Skip to content

Commit

Permalink
Add autoDeclineInsurance option
Browse files Browse the repository at this point in the history
  • Loading branch information
mhluska committed Jan 4, 2021
1 parent d1a7ec9 commit 69c0722
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 38 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const settings = {
checkDeviations: false,
// Can be one of 'default', 'pairs', 'uncommon', 'illustrious18'.
gameMode: 'default',
autoDeclineInsurance: false,
};

const game = new BlackjackEngine(settings);
Expand Down
50 changes: 33 additions & 17 deletions src/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const SETTINGS_DEFAULTS = {
// Can be one of 'default', 'pairs', 'uncommon', 'illustrious18'. If the mode
// is set to 'illustrious18', `checkDeviations` will be forced to true.
gameMode: 'default',
autoDeclineInsurance: false,
};

export default class Game extends EventEmitter {
Expand Down Expand Up @@ -65,23 +66,7 @@ export default class Game extends EventEmitter {
this._setHandWinner({ winner: 'dealer' });
}

// Dealer peeks at the hole card if the upcard is ace to ask insurance.
if (this.dealer.upcard.value === 11) {
let input;
while (!input?.includes('insurance')) {
input = await this._getPlayerInsuranceInput();
}

this._validateInput(input);

if (this.dealer.holeCard.value === 10) {
this._setHandWinner({ winner: 'dealer' });

// TODO: Make insurance amount configurable. Currently uses half the
// bet size as insurance to recover full bet amount.
this.player.addChips(betAmount);
}
}
await this._handleInsurance(betAmount);

for (let hand of this.player.hands) {
if (this.state.handWinner[hand.id]) {
Expand Down Expand Up @@ -140,6 +125,37 @@ export default class Game extends EventEmitter {
}
}

async _handleInsurance(betAmount) {
if (this.dealer.upcard.value !== 11) {
return;
}

let input;

if (this.settings.autoDeclineInsurance) {
input = 'no-insurance';
} else {
while (!input?.includes('insurance')) {
input = await this._getPlayerInsuranceInput();
}

this._validateInput(input);
}

// Dealer peeks at the hole card if the upcard is ace to ask insurance.
if (this.dealer.holeCard.value !== 10) {
return;
}

this._setHandWinner({ winner: 'dealer' });

// TODO: Make insurance amount configurable. Currently uses half the
// bet size as insurance to recover full bet amount.
if (input === 'buy-insurance') {
this.player.addChips(betAmount);
}
}

_chainEmitChange(object) {
object.on('change', (name, value) => this.emit('change', name, value));
return object;
Expand Down
87 changes: 66 additions & 21 deletions test/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,56 @@ import assert from 'assert';
import Game from '../src/game.js';
import Card from '../src/card.js';

describe('Game', function () {
let game;
function setupGame(options = {}) {
const defaultOptions = {
gameOptions: { animationDelay: 0 },
resolvePlayerInput: true,
cards: '',
};

before(function () {
game = new Game({ animationDelay: 0 });
const gameOptions = Object.assign(
{},
defaultOptions.gameOptions,
options.gameOptions
);

Object.assign(defaultOptions, options);

sinon
.stub(game.playerInputReader, 'readInput')
.callsFake(({ keypress }) => {
return Promise.resolve(
const game = new Game(gameOptions);

const readInputCallback = defaultOptions.resolvePlayerInput
? ({ keypress }) =>
Promise.resolve(
{
'game-result': true,
'waiting-for-move': 'hit',
'ask-insurance': 'no-insurance',
}[game.state.step]
);
});
)
: () => new Promise(() => {});

const hand = game.player.hands[0];
const length = game.shoe.cards.length;

defaultOptions.cards.split('').forEach((cardRank, index) => {
game.shoe.cards[length - index - 1] = new Card(
'hearts',
// If the input is `?`, the rank is irrelevant. We arbitrarily pick `2`.
cardRank === '?' ? '2' : cardRank,
game.shoe
);
});

after(function () {
game.playerInputReader.readInput.restore();
sinon.stub(game.playerInputReader, 'readInput').callsFake(readInputCallback);

return game;
}

describe('Game', function () {
let game;

before(function () {
game = setupGame();
});

describe('#step()', function () {
Expand Down Expand Up @@ -51,19 +80,16 @@ describe('Game', function () {

context('when the player bets and wins', function () {
let playerBalanceBefore;
const betAmount = 100;

before(async function () {
playerBalanceBefore = game.player.balance;
const betAmount = 100;

const game = setupGame({
// Force a winning hand for the player (Blackjack with A-J).
const hand = game.player.hands[0];
const length = game.shoe.cards.length;
cards: 'A?J?',
});

game.shoe.cards[length - 1] = new Card('hearts', 'A', hand);
game.shoe.cards[length - 2] = new Card('hearts', '2', hand);
game.shoe.cards[length - 3] = new Card('hearts', 'J', hand);
game.shoe.cards[length - 4] = new Card('hearts', '3', hand);
before(async function () {
playerBalanceBefore = game.player.balance;

await game.step({ betAmount });
});
Expand All @@ -72,5 +98,24 @@ describe('Game', function () {
assert.equal(game.player.balance, playerBalanceBefore + betAmount);
});
});

context('when autoDeclineInsurance is enabled', function () {
let game;

before(function () {
game = setupGame({
gameOptions: { autoDeclineInsurance: true },
resolvePlayerInput: false,
// Force a hand that prompts for insurance (dealer Ace).
cards: '?A',
});

game.step();
});

it('should not pause for player input', function () {
assert.notEqual(game.state.step, 'ask-insurance');
});
});
});
});

0 comments on commit 69c0722

Please sign in to comment.