Skip to content

Instantly share code, notes, and snippets.

@dwijnand
Created May 5, 2014 23:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dwijnand/b1b4ce445c27e0556abb to your computer and use it in GitHub Desktop.
Save dwijnand/b1b4ce445c27e0556abb to your computer and use it in GitHub Desktop.
Blackjack basic strategy
object Suit extends Enumeration {
type Suit = Value
val Spades, Hearts, Diamonds, Clubs = Value // Bridge ordering
}
import Suit._
case class Card(rank: Int, suit: Suit)
abstract sealed class Command
case object StandCmd extends Command
case object HitCmd extends Command
case object DoubleOrHitCmd extends Command
case object DoubleOrStandCmd extends Command
case object SplitCmd extends Command
case object SurrenderCmd extends Command
/**
* Reference: http://en.wikipedia.org/wiki/Blackjack#Basic_strategy
*/
object BasicStrategy {
/*
* Depends on the following rule-set:
* - 4 to 8 decks
* - Dealer stands on soft 17
* - Double on any 2 cards
* - Double after split allowed
* - Only original bets lost on dealer blackjack
* - Late surrender
*/
private val SS = StandCmd
private val HH = HitCmd
private val Dh = DoubleOrHitCmd
private val Ds = DoubleOrStandCmd
private val SP = SplitCmd
private val SU = SurrenderCmd
private val HARD_TOTALS = List(
// 2 3 4 5 6 7 8 9 10 A
List(HH, HH, HH, HH, HH, HH, HH, HH, HH, HH), // 5
List(HH, HH, HH, HH, HH, HH, HH, HH, HH, HH), // 6
List(HH, HH, HH, HH, HH, HH, HH, HH, HH, HH), // 7
List(HH, HH, HH, HH, HH, HH, HH, HH, HH, HH), // 8
List(HH, Dh, Dh, Dh, Dh, HH, HH, HH, HH, HH), // 9
List(Dh, Dh, Dh, Dh, Dh, Dh, Dh, Dh, HH, HH), // 10
List(Dh, Dh, Dh, Dh, Dh, Dh, Dh, Dh, Dh, HH), // 11
List(HH, HH, SS, SS, HH, HH, HH, HH, HH, HH), // 12
List(SS, SS, SS, SS, HH, HH, HH, HH, HH, HH), // 13
List(SS, SS, SS, SS, HH, HH, HH, HH, HH, HH), // 14
List(SS, SS, SS, SS, HH, HH, HH, HH, SU, HH), // 15
List(SS, SS, SS, SS, HH, HH, HH, SU, SU, SU), // 16
List(SS, SS, SS, SS, SS, SS, SS, SS, SS, SS), // 17
List(SS, SS, SS, SS, SS, SS, SS, SS, SS, SS), // 18
List(SS, SS, SS, SS, SS, SS, SS, SS, SS, SS), // 19
List(SS, SS, SS, SS, SS, SS, SS, SS, SS, SS) // 20
)
private val SOFT_TOTALS = List(
// 2 3 4 5 6 7 8 9 10 A
List(HH, HH, HH, Dh, Dh, HH, HH, HH, HH, HH), // A,2
List(HH, HH, HH, Dh, Dh, HH, HH, HH, HH, HH), // A,3
List(HH, HH, Dh, Dh, Dh, HH, HH, HH, HH, HH), // A,4
List(HH, HH, Dh, Dh, Dh, HH, HH, HH, HH, HH), // A,5
List(HH, Dh, Dh, Dh, Dh, HH, HH, HH, HH, HH), // A,6
List(SS, Ds, Ds, Ds, Ds, SS, SS, HH, HH, HH), // A,7
List(SS, SS, SS, SS, SS, SS, SS, SS, SS, SS), // A,8
List(SS, SS, SS, SS, SS, SS, SS, SS, SS, SS) // A,9
)
private val PAIRS = List(
// 2 3 4 5 6 7 8 9 10 A
List(SP, SP, SP, SP, SP, SP, HH, HH, HH, HH), // 2, 2
List(SP, SP, SP, SP, SP, SP, HH, HH, HH, HH), // 3, 3
List(HH, HH, HH, SP, SP, HH, HH, HH, HH, HH), // 4, 4
List(Dh, Dh, Dh, Dh, Dh, Dh, Dh, Dh, HH, HH), // 5, 5
List(SP, SP, SP, SP, SP, Dh, Dh, Dh, HH, HH), // 6, 6
List(SP, SP, SP, SP, SP, SP, Dh, Dh, HH, HH), // 7, 7
List(SP, SP, SP, SP, SP, SP, SP, SP, SP, SP), // 8, 8
List(SP, SP, SP, SP, SP, SS, SP, SP, SS, SS), // 9, 9
List(SS, SS, SS, SS, SS, SS, SS, SS, SS, SS), // 10,10
List(SP, SP, SP, SP, SP, SP, SP, SP, SP, SP) // A, A
)
def getDecision(cards: Seq[Card], dealerCard: Card) = {
if (cards.size == 2 && cards(0).rank == cards(1).rank) {
getPairsDecision(cards, dealerCard)
}
val softTotal = getSoftTotal(cards)
if (softTotal == 21) {
StandCmd
} else if (softTotal < 21) {
getSoftTotalDecision(cards, dealerCard)
} else {
getHardTotalDecision(cards, dealerCard)
}
}
private def getHardTotalDecision(cards: Seq[Card], dealerCard: Card) = {
val hardTotalIndex = getHardTotal(cards) - 5
HARD_TOTALS(hardTotalIndex)(wrapAce(dealerCard.rank))
}
private def getSoftTotalDecision(cards: Seq[Card], dealerCard: Card) = {
val otherCard = cards.find(_.rank != 1).get
val softTotalIndex = otherCard.rank - 2
SOFT_TOTALS(softTotalIndex)(wrapAce(dealerCard.rank))
}
private def getPairsDecision(cards: Seq[Card], dealerCard: Card) = {
PAIRS(wrapAce(cards.head.rank))(wrapAce(dealerCard.rank))
}
private def getSoftTotal(cards: Seq[Card]) = getCardTotal(cards, 11)
private def getHardTotal(cards: Seq[Card]) = getCardTotal(cards, 1)
private def getCardTotal(cards: Seq[Card], aceValue: Int) = {
cards.map(_.rank match {
case 1 => aceValue
case 11 => 10
case 12 => 10
case 13 => 10
case r => r
}).sum
}
private def wrapAce(rank: Int) = (rank - 2 + 10) % 10
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment