UNPKG

blackjack-strategy

Version:

provides a suggested action for a player based on Basic Strategy

251 lines (230 loc) 10.5 kB
/* * MIT License * Copyright (c) 2016 Garrett Vargas * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ module.exports = { // Special-case overrides based on exact composition of cards GetExactCompositionOverride: function (playerCards, dealerCard, handValue, handCount, dealerCheckedBlackjack, options) { // If exactComposition isn't set, no override if (options.strategyComplexity != "exactComposition") { return null; } // Now look at strategies based on game options if ((options.numberOfDecks == 2) && (!options.hitSoft17)) { return TwoDeckStandSoft17(playerCards, dealerCard, handValue, handCount, dealerCheckedBlackjack, options); } }, // Should you surrender based on the exact composition of your cards? // return value of true means you should surrender (based on the exact cards in your hand) // return value of false means you should NOT surrender // return value of null means there is no opinion based on exact composition (continue processing via normal rules) GetSurrenderOverride : function (playerCards, dealerCard, handValue, handCount, options) { var shouldSurrender = null; if (options.surrender == "early") { // Don't surrender 10/4 or 5/9 in single deck, or 4/10 in double deck against dealer 10 if ((handValue.total == 14) && (dealerCard == 10)) { if ((options.numberOfDecks == 1) && ((playerCards[0] == 4) || (playerCards[0] == 5) || (playerCards[0] == 9) || (playerCards[0] == 10))) { shouldSurrender = false; } if ((options.numberOfDecks == 2) && ((playerCards[0] == 4) || (playerCards[0] == 10))) { shouldSurrender = false; } } } // Late surrender against an Ace when dealer hits on soft 17 else if ((options.hitSoft17) && (dealerCard == 1)) { // Single or double deck 8/7 doesn't surrender if ((handValue.total == 15) && (options.numberOfDecks <= 2) && ((playerCards[0] == 8) || (playerCards[0] == 7))) { shouldSurrender = false; } else if ((handValue.total == 17) && (options.numberOfDecks == 1) && ((playerCards[0] == 10) || (playerCards[0] == 7))) { shouldSurrender = true; } } // Late surrender against an Ace, dealer doesn't hit soft 17 else if (dealerCard == 1) { // If it is a 9/7 single deck, we don't surrender if ((handValue.total == 16) && (options.numberOfDecks == 1) && ((playerCards[0] == 9) || (playerCards[0] == 7))) { shouldSurrender = false; } } // Late surrender against a non-Ace else { // 8/7 against 10 doesn't surrender if ((handValue.total == 15) && (dealerCard == 10) && ((playerCards[0] == 8) || (playerCards[0] == 7))) { shouldSurrender = false; } } return shouldSurrender; } }; /* * Internal functions */ function FindExceptionInList(playerCards, dealerCard, overrides) { // First create a sorted version of this list var sortedHand = playerCards.slice(0).sort((a, b) => (a-b)); var action = null; // Now you can compare for (var i = 0; i < overrides.length; i++) { if ((dealerCard == overrides[i].dealer) && (sortedHand.length == overrides[i].hand.length)) { for (var card = 0; card < sortedHand.length; card++) { if (sortedHand[card] != overrides[i].hand[card]) { // Not a match break; } } // If we got all the way through, it's a match! if (card == sortedHand.length) { // We did it! action = overrides[i].action; break; } } } return action; } // Taken from http://wizardofodds.com/games/blackjack/appendix/3b/ function TwoDeckStandSoft17(playerCards, dealerCard, handValue, handCount, dealerCheckedBlackjack, options) { var result; const overrides = [ {hand:[4,6,6], dealer: 10, action: "hit"}, {hand:[3,6,7], dealer: 10, action: "hit"}, {hand:[2,6,8], dealer: 10, action: "hit"}, {hand:[1,6,9], dealer: 10, action: "hit"}, {hand:[3,3,10], dealer: 10, action: "hit"}, {hand:[2,2,6,6], dealer: 10, action: "hit"}, {hand:[1,3,6,6], dealer: 10, action: "hit"}, {hand:[1,2,6,7], dealer: 10, action: "hit"}, {hand:[1,1,6,8], dealer: 10, action: "hit"}, {hand:[2,2,2,10], dealer: 10, action: "hit"}, {hand:[1,2,3,10], dealer: 10, action: "hit"}, {hand:[4,4,4,4], dealer: 9, action: "stand"}, {hand:[3,4,4,5], dealer: 9, action: "stand"}, {hand:[3,3,5,5], dealer: 9, action: "stand"}, {hand:[2,4,5,5], dealer: 9, action: "stand"}, {hand:[1,5,5,5], dealer: 9, action: "stand"}, {hand:[2,2,3,3,6], dealer: 10, action: "hit"}, {hand:[1,1,1,6,7], dealer: 10, action: "hit"}, {hand:[1,1,2,2,10], dealer: 10, action: "hit"}, {hand:[1,1,2,6,6], dealer: 10, action: "hit"}, {hand:[1,3,4,4,4], dealer: 9, action: "stand"}, {hand:[2,2,4,4,4], dealer: 9, action: "stand"}, {hand:[2,3,3,4,4], dealer: 9, action: "stand"}, {hand:[3,3,3,3,4], dealer: 9, action: "stand"}, {hand:[1,2,3,5,5], dealer: 9, action: "stand"}, {hand:[1,2,4,4,5], dealer: 9, action: "stand"}, {hand:[1,3,3,4,5], dealer: 9, action: "stand"}, {hand:[2,2,2,5,5], dealer: 9, action: "stand"}, {hand:[2,2,3,4,5], dealer: 9, action: "stand"}, {hand:[2,3,3,3,5], dealer: 9, action: "stand"}, {hand:[1,1,1,1,4,4], dealer: 3, action: "stand"}, {hand:[1,1,2,4,4,4], dealer: 7, action: "stand"}, {hand:[1,1,3,3,4,4], dealer: 7, action: "stand"}, {hand:[1,2,2,3,4,4], dealer: 7, action: "stand"}, {hand:[1,2,3,3,3,4], dealer: 7, action: "stand"}, {hand:[1,3,3,3,3,3], dealer: 7, action: "stand"}, {hand:[2,2,2,3,3,4], dealer: 7, action: "stand"}, {hand:[2,2,3,3,3,3], dealer: 7, action: "stand"}, {hand:[1,3,3,3,3,3], dealer: 8, action: "stand"}, {hand:[2,2,3,3,3,3], dealer: 8, action: "stand"}, {hand:[1,3,3,3,3,3], dealer: 9, action: "stand"}, {hand:[2,2,3,3,3,3], dealer: 9, action: "stand"}, {hand:[1,1,2,4,4,4], dealer: 9, action: "stand"}, {hand:[1,1,3,3,4,4], dealer: 9, action: "stand"}, {hand:[1,2,2,3,4,4], dealer: 9, action: "stand"}, {hand:[1,2,3,3,3,4], dealer: 9, action: "stand"}, {hand:[2,2,2,2,4,4], dealer: 9, action: "stand"}, {hand:[2,2,2,3,3,4], dealer: 9, action: "stand"}, {hand:[1,1,1,3,5,5], dealer: 9, action: "stand"}, {hand:[1,1,1,4,4,5], dealer: 9, action: "stand"}, {hand:[1,1,2,2,5,5], dealer: 9, action: "stand"}, {hand:[1,1,2,3,4,5], dealer: 9, action: "stand"}, {hand:[1,1,3,3,3,5], dealer: 9, action: "stand"}, {hand:[1,2,2,2,4,5], dealer: 9, action: "stand"}, {hand:[1,2,2,3,3,5], dealer: 9, action: "stand"}, {hand:[2,2,2,2,3,5], dealer: 9, action: "stand"}, {hand:[1,1,1,1,6,6], dealer: 10, action: "hit"}, {hand:[1,2,2,2,3,6], dealer: 10, action: "hit"}, {hand:[2,2,2,2,2,6], dealer: 10, action: "hit"}, ]; // Special case if there are two cards if (playerCards.length == 2) { if ((handValue.total == 11) && (dealerCard == 1) && ((playerCards[0] == 2) || (playerCards[0] == 3) || (playerCards[0] == 8) || (playerCards[0] == 9))) { return "double"; } else if ((handValue.total == 12) && (dealerCard == 4) && ((playerCards[0] == 2) || (playerCards[0] == 10))) { return "hit"; } } else // 3 or more cards { // Ace, Ace, 6 hits against dealer Ace if ((playerCards.length == 3) && (handValue.total == 18) && handValue.soft && (dealerCard == 1) && ((playerCards[0] == 6) || (playerCards[1] == 6) || (playerCards[2] == 6))) { return "hit"; } else if ((playerCards.length == 6) || (handValue.total == 16)) { result = FindExceptionInList(playerCards, dealerCard, overrides); if (result) { return result; } } // Three or more cards, 16 vs 10 stands and soft 18 vs Ace stands (except as noted in table which was already checked) if ((handValue.total == 16) && (dealerCard == 10)) { return "stand"; } if ((handValue.total == 18) && (handValue.soft) && (dealerCard == 1)) { return "stand"; } } return null; }