From 59eb007a751854a5f52653cac7331ea73a4c1900 Mon Sep 17 00:00:00 2001 From: Jorge Molero Date: Mon, 20 May 2019 16:00:12 +0200 Subject: [PATCH 1/2] Allow even money with insurance --- src/engine.js | 12 +++-- src/game.js | 15 ++++-- test/game.spec.js | 124 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 140 insertions(+), 11 deletions(-) diff --git a/src/engine.js b/src/engine.js index 09d05e4..5843ee8 100644 --- a/src/engine.js +++ b/src/engine.js @@ -108,7 +108,7 @@ export const getHandInfo = (playerCards: Array, dealerCards: Array, const isClosed = hasBusted || hasBlackjack || handValue.hi === 21 const canDoubleDown = !isClosed && true const canSplit = playerCards.length > 1 && playerCards[ 0 ].value === playerCards[ 1 ].value && !isClosed - const canInsure = dealerCards[ 0 ].value === 1 && !isClosed + const canInsure = dealerCards[0].value === 1 && playerCards.length === 2 return { cards: playerCards, playerValue: handValue, @@ -140,7 +140,7 @@ export const getHandInfoAfterDeal = (playerCards: Array, dealerCards: Arra } return { ...hand, - close: hand.playerHasBlackjack + close: hand.playerHasBlackjack && !availableActions.insurance } } @@ -289,7 +289,7 @@ export const isActionAllowed = (actionName: string, stage: string): boolean => { } } -export const getPrize = (playerHand: Hand, dealerCards: Array): number => { +export const getPrize = (playerHand: Hand, dealerCards: Array, tookInsurance: boolean): number => { const { close = false, playerHasSurrendered = true, @@ -309,7 +309,7 @@ export const getPrize = (playerHand: Hand, dealerCards: Array): number => if (playerHasSurrendered) { return bet / 2 } - if (playerHasBlackjack && !dealerHasBlackjack) { + if (playerHasBlackjack && (!dealerHasBlackjack || (!dealerHasBlackjack && tookInsurance))) { return bet + (bet * 1.5) } const dealerHasBusted = higherValidDealerValue > 21 @@ -330,7 +330,9 @@ export const getPrizes = ({ history, handInfo: { left, right }, dealerCards }: { memo += x.value return memo }, 0) - const wonOnRight = getPrize(right, dealerCards) + const insuranceAction = history.find(x => x.type === TYPES.INSURANCE) + const tookInsurance = insuranceAction && insuranceAction.payload.bet > 0 + const wonOnRight = getPrize(right, dealerCards, tookInsurance) const wonOnLeft = getPrize(left, dealerCards) return { finalBet: finalBet, diff --git a/src/game.js b/src/game.js index ce0f9fc..7eacbc2 100644 --- a/src/game.js +++ b/src/game.js @@ -202,8 +202,12 @@ export default class Game { hits: hits + 1 }) - if (right.playerHasBlackjack) { - // purpose of the game archived !!! + if ( + right.playerHasBlackjack && + (!right.availableActions.insurance || + (right.availableActions.insurance && dealerValue.lo !== 1)) + ) { + // purpose of the game achieved !!! this._dispatch(actions.showdown()) break } @@ -214,7 +218,7 @@ export default class Game { } // else // in this case, the game must continue in "player-turn-right" - // waiting for the insurance action + // waiting for the insurance (including even money) action } break } @@ -227,7 +231,8 @@ export default class Game { const insurancePrize = (isFirstCardAce && dealerHasBlackjack && insuranceValue > 0 && bet > 0) ? insuranceValue * 3 : 0 const right = this.enforceRules(engine.getHandInfoAfterInsurance(handInfo.right.cards, dealerCards)) right.bet = initialBet - right.close = dealerHasBlackjack + const tookEvenMoney = insuranceValue > 0 && handInfo.right.playerHasBlackjack + right.close = dealerHasBlackjack || tookEvenMoney const historyItem = appendEpoch({ ...action, payload: { bet: insuranceValue || 0 } @@ -244,7 +249,7 @@ export default class Game { } } }) - if (dealerHasBlackjack) { + if (dealerHasBlackjack || tookEvenMoney) { this._dispatch(actions.showdown()) } break diff --git a/test/game.spec.js b/test/game.spec.js index 7a6ed38..77ffaec 100644 --- a/test/game.spec.js +++ b/test/game.spec.js @@ -303,6 +303,128 @@ describe('Game flow', function () { assert.equal(wonOnLeft, 20, 'won 20 on left') assert.equal(wonOnRight, 20, 'won 20 on right') }) + describe('# even money dealer BJ', () => { + const test = { + cards: '♦10 ♥1 ♠10 ♠1' + } + it(`INSURANCE ON: should deal ${ + test.cards + }, insure YES, and finish`, () => { + const testActions = ['restore', 'deal', 'insuranceYes'] + const rules = { insurance: true } + const state = executeFlow( + rules, + test.cards, + testActions.map(x => functions[x]) + ) + const { + finalBet, + wonOnRight, + handInfo: { right }, + sideBetsInfo: { + insurance: { win } + } + } = state + assert.equal( + state.stage, + 'done', + 'blackjack but insurance is ON and first card is ♥1' + ) + assert.equal(finalBet, 15, 'bet 10 and insurance 1') + assert.equal(right.close, true, 'right hand should be close') + assert.equal( + win, + 5 * 3, + 'insurance pays 2 to 1 when dealer has bj + insurance value' + ) + assert.equal( + wonOnRight, + 10, + "right has prize because it's a push (even money)" + ) + }) + it(`INSURANCE ON: should deal ${ + test.cards + }, insure YES, and finish`, () => { + const testActions = ['restore', 'deal20', 'insuranceYes'] + const rules = { insurance: true } + const state = executeFlow( + rules, + test.cards, + testActions.map(x => functions[x]) + ) + const { + finalBet, + wonOnRight, + handInfo: { right }, + sideBetsInfo: { + insurance: { risk, win } + } + } = state + assert.equal( + state.stage, + 'done', + 'blackjack but insurance is ON and first card is ♥1' + ) + assert.equal(finalBet, 20 + 10, 'bet 20 and insurance 10') + assert.equal(right.close, true, 'right hand should be close') + assert.equal( + risk, + 10, + 'insurance pays 2 to 1 when dealer has bj + insurance value' + ) + assert.equal( + win, + 30, + 'insurance pays 2 to 1 when dealer has bj + insurance value' + ) + assert.equal( + wonOnRight, + 20, + "right has prize because it's a push (even money)" + ) + }) + }) + describe('# even money dealer no BJ', () => { + it(`INSURANCE ON: prevent amount injection`, () => { + const testActions = ['restore', 'deal', 'insuranceInjectAmount', 'standR'] + const rules = { insurance: true } + const state = executeFlow( + rules, + '♦5 ♥1 ♠10 ♠1', + testActions.map(x => functions[x]) + ) + const bet = 10 + const maxInsuranceAmount = bet / 2 + const { + finalBet, + wonOnRight, + handInfo: { right }, + sideBetsInfo: { + insurance: { risk, win } + } + } = state + assert.equal( + state.stage, + 'done', + 'blackjack but insurance is ON and first card is ♥1' + ) + assert.equal(right.playerValue.hi, 21, 'player value must be 4') + assert.equal( + finalBet, + bet + maxInsuranceAmount, + `bet ${bet} and max insurance ${maxInsuranceAmount}` + ) + assert.equal(right.close, true, 'right hand should be close') + assert.equal(risk, 5, 'insurance risk value is 5') + assert.equal(win, 0, 'insurance win value is 0') + assert.equal( + wonOnRight, + bet + bet * 1.5, + "right has prize because it's a player blackjack (even money)" + ) + }) + }) }) describe('Must Stand on 17', function () { @@ -468,4 +590,4 @@ describe('No matter how many aces ... soft hands do not busts', () => { assert.equal(playerHasBusted, false, 'Player should be 12 not 22') assert.equal(wonOnRight, 0, 'player lose. dealer has 21, player 12 or 22') }) -}) \ No newline at end of file +}) From cd9278f16e5bc95a11f6250025ec647d66f90c4e Mon Sep 17 00:00:00 2001 From: Jorge Molero Date: Wed, 22 May 2019 16:25:44 +0200 Subject: [PATCH 2/2] Add evenMoneyInsurance as a rule --- src/game.js | 7 +++--- src/presets.js | 2 ++ src/types/index.js | 1 + test/game.spec.js | 61 ++++++++++++++++++---------------------------- types/index.d.ts | 1 + 5 files changed, 32 insertions(+), 40 deletions(-) diff --git a/src/game.js b/src/game.js index 7eacbc2..cecd9ec 100644 --- a/src/game.js +++ b/src/game.js @@ -157,7 +157,7 @@ export default class Game { switch (action.type) { case TYPES.DEAL: { const { bet, sideBets } = action.payload - const { rules: { insurance }, availableBets, history, hits } = this.state + const { rules: { insurance, evenMoneyInsurance }, availableBets, history, hits } = this.state const playerCards = this.state.deck.splice(this.state.deck.length - 2, 2) const dealerCards = this.state.deck.splice(this.state.deck.length - 1, 1) const dealerHoleCard = this.state.deck.splice(this.state.deck.length - 1, 1)[ 0 ] @@ -205,7 +205,8 @@ export default class Game { if ( right.playerHasBlackjack && (!right.availableActions.insurance || - (right.availableActions.insurance && dealerValue.lo !== 1)) + (right.availableActions.insurance && + (!evenMoneyInsurance || dealerValue.lo !== 1))) ) { // purpose of the game achieved !!! this._dispatch(actions.showdown()) @@ -218,7 +219,7 @@ export default class Game { } // else // in this case, the game must continue in "player-turn-right" - // waiting for the insurance (including even money) action + // waiting for the insurance action, including even money if enabled } break } diff --git a/src/presets.js b/src/presets.js index 31f7a01..98677d0 100644 --- a/src/presets.js +++ b/src/presets.js @@ -39,6 +39,7 @@ export const getRules = ({ doubleAfterSplit = true, surrender = true, insurance = true, + evenMoneyInsurance = false, showdownAfterAceSplit = true }: Rule) => { return { @@ -49,6 +50,7 @@ export const getRules = ({ doubleAfterSplit: doubleAfterSplit, surrender: surrender, insurance: insurance, + evenMoneyInsurance: evenMoneyInsurance, showdownAfterAceSplit: showdownAfterAceSplit } } diff --git a/src/types/index.js b/src/types/index.js index 6cf009c..7972482 100644 --- a/src/types/index.js +++ b/src/types/index.js @@ -56,6 +56,7 @@ export type Rule = { split: boolean, surrender: boolean, insurance: boolean, + evenMoneyInsurance: boolean, showdownAfterAceSplit: boolean } diff --git a/test/game.spec.js b/test/game.spec.js index 77ffaec..540ae55 100644 --- a/test/game.spec.js +++ b/test/game.spec.js @@ -307,11 +307,11 @@ describe('Game flow', function () { const test = { cards: '♦10 ♥1 ♠10 ♠1' } - it(`INSURANCE ON: should deal ${ + it(`INSURANCE ON AND EVEN_MONEY ON ${ test.cards }, insure YES, and finish`, () => { const testActions = ['restore', 'deal', 'insuranceYes'] - const rules = { insurance: true } + const rules = { insurance: true, evenMoneyInsurance: true } const state = executeFlow( rules, test.cards, @@ -330,7 +330,7 @@ describe('Game flow', function () { 'done', 'blackjack but insurance is ON and first card is ♥1' ) - assert.equal(finalBet, 15, 'bet 10 and insurance 1') + assert.equal(finalBet, 15, 'bet 10 and insurance 5') assert.equal(right.close, true, 'right hand should be close') assert.equal( win, @@ -343,52 +343,26 @@ describe('Game flow', function () { "right has prize because it's a push (even money)" ) }) - it(`INSURANCE ON: should deal ${ + it(`INSURANCE ON AND EVEN_MONEY OFF ${ test.cards }, insure YES, and finish`, () => { - const testActions = ['restore', 'deal20', 'insuranceYes'] - const rules = { insurance: true } + const testActions = ['restore', 'deal'] + const rules = { insurance: true, evenMoneyInsurance: false } const state = executeFlow( rules, test.cards, testActions.map(x => functions[x]) ) const { - finalBet, - wonOnRight, - handInfo: { right }, - sideBetsInfo: { - insurance: { risk, win } - } + sideBetsInfo: { insurance } } = state - assert.equal( - state.stage, - 'done', - 'blackjack but insurance is ON and first card is ♥1' - ) - assert.equal(finalBet, 20 + 10, 'bet 20 and insurance 10') - assert.equal(right.close, true, 'right hand should be close') - assert.equal( - risk, - 10, - 'insurance pays 2 to 1 when dealer has bj + insurance value' - ) - assert.equal( - win, - 30, - 'insurance pays 2 to 1 when dealer has bj + insurance value' - ) - assert.equal( - wonOnRight, - 20, - "right has prize because it's a push (even money)" - ) + assert.equal(insurance, undefined, 'insurance not offered') }) }) describe('# even money dealer no BJ', () => { - it(`INSURANCE ON: prevent amount injection`, () => { - const testActions = ['restore', 'deal', 'insuranceInjectAmount', 'standR'] - const rules = { insurance: true } + it(`INSURANCE ON AND EVEN_MONEY ON`, () => { + const testActions = ['restore', 'deal', 'insuranceYes'] + const rules = { insurance: true, evenMoneyInsurance: true } const state = executeFlow( rules, '♦5 ♥1 ♠10 ♠1', @@ -424,6 +398,19 @@ describe('Game flow', function () { "right has prize because it's a player blackjack (even money)" ) }) + it(`INSURANCE ON AND EVEN_MONEY OFF`, () => { + const testActions = ['restore', 'deal'] + const rules = { insurance: true, evenMoneyInsurance: false } + const state = executeFlow( + rules, + '♦5 ♥1 ♠10 ♠1', + testActions.map(x => functions[x]) + ) + const { + sideBetsInfo: { insurance } + } = state + assert.equal(insurance, undefined, 'insurance not offered') + }) }) }) diff --git a/types/index.d.ts b/types/index.d.ts index e211589..1b390a3 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -91,6 +91,7 @@ declare module 'engine-blackjack' { split: boolean; surrender: boolean; insurance: boolean; + evenMoneyInsurance: boolean; showdownAfterAceSplit: boolean; }