Skip to content

Instantly share code, notes, and snippets.

@Experiment5X
Created August 30, 2019 03:50
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 Experiment5X/d4470cdbb4b1413a3cd0884def1e07dd to your computer and use it in GitHub Desktop.
Save Experiment5X/d4470cdbb4b1413a3cd0884def1e07dd to your computer and use it in GitHub Desktop.
import scala.util.Random
import scala.io.StdIn
object BlackJack extends App {
case class InvalidCardValueException(value: Int) extends Exception(s"Invalid card value of $value")
sealed abstract class Suit
case object Spades extends Suit
case object Clubs extends Suit
case object Hearts extends Suit
case object Diamonds extends Suit
case class Card(suit: Suit, value: Int) {
if (!(1 to 13 contains value))
throw InvalidCardValueException(value)
override def toString: String = s"$valueCommonName of $suit"
def blackJackValue: Int = value match {
case x if 1 to 10 contains x => x
case x if 11 to 13 contains x => 10
case x => throw InvalidCardValueException(x)
}
private def valueCommonName: String = value match {
case 1 => "Ace"
case x if 2 to 10 contains x => x.toString
case 11 => "Jack"
case 12 => "Queen"
case 13 => "King"
case x => throw InvalidCardValueException(x)
}
}
case class Deck(cards: Seq[Card]) {
def drawCard: (Card, Deck) =
(cards.head, Deck(cards.tail))
}
object Deck {
val suits = Seq(Spades, Clubs, Hearts, Diamonds)
/**
* Create a new deck with all 52 cards shuffled
*/
def fullShuffled: Deck = {
val cards = for {
suit <- suits
value <- 1 to 13
} yield Card(suit, value)
val shuffledCards = Random.shuffle(cards)
Deck(shuffledCards)
}
}
object Player {
val DEALER_NAME = "Dealer"
def dealer =
Player(DEALER_NAME)
}
case class Player(name: String, cards: Seq[Card] = Seq(), playerStopped: Boolean = false) {
def totalScore: Int =
cards.foldLeft(0)(_ + _.blackJackValue)
def isOver: Boolean =
totalScore > 21
def finished: Boolean =
isOver || playerStopped
def isDealer: Boolean =
name == Player.DEALER_NAME
override def toString: String = {
val cardLinePrefix = "\n\t- "
val cardsStr = cards.mkString(cardLinePrefix)
s"$name : $totalScore$cardLinePrefix$cardsStr"
}
}
case class GameResult(player: Player, dealer: Player, dealerWon: Boolean) {
override def toString: String = {
val winnerName =
if (dealerWon)
dealer.name
else
player.name
s"$winnerName won. ${player.name}: - ${player.totalScore} Dealer: ${dealer.totalScore}"
}
}
/**
* Play the game and determine who wins
* @param players All of the players in the game
* @return The winner
*/
def playGame(players: Seq[Player]): Seq[GameResult] = {
def givePlayerACard(player: Player, deck: Deck): (Player, Deck) = {
val (topCard, newDeck) = deck.drawCard
val newPlayerCards = player.cards :+ topCard
val newPlayer = Player(player.name, newPlayerCards)
(newPlayer, newDeck)
}
def givePlayer2Cards(player: Player, deck: Deck): (Player, Deck) = {
val (newPlayer1, newDeck1) = givePlayerACard(player, deck)
val (newPlayer2, newDeck2) = givePlayerACard(newPlayer1, newDeck1)
(newPlayer2, newDeck2)
}
def askPlayerForCard(player: Player, deck: Deck): (Player, Deck) = {
val playerDone =
if (player.isDealer) {
player.totalScore > 15
} else {
println(player)
println(s"Do you want a card?")
!StdIn.readBoolean()
}
if (playerDone)
(Player(player.name, player.cards, playerDone), deck)
else
givePlayerACard(player, deck)
}
def determineWinners(players: Seq[Player]): Seq[GameResult] = {
def beatDealer(dealer: Player, player: Player): Boolean =
if (dealer.isOver && !player.isOver)
true
else if (!dealer.isOver && player.isOver)
false
else if (dealer.isOver && player.isOver)
false
else
dealer.totalScore < player.totalScore
val dealers = players.filter(_.isDealer)
val dealer: Player = dealers.head
val nonDealers = players.diff(dealers)
nonDealers.map {
player =>
val dealerWon = !beatDealer(player, dealer)
GameResult(player, dealer, !dealerWon)
}
}
def playGameHelper(players: Seq[Player], deck: Deck): Seq[GameResult] = {
// if there are still players who are not finished, continue playing with them
if (players.exists(!_.finished)) {
val currentPlayer = players.head
val (nextRoundPlayer, nextRoundDeck) =
if (currentPlayer.cards.isEmpty)
givePlayer2Cards(currentPlayer, deck)
else if (!currentPlayer.finished) {
askPlayerForCard(currentPlayer, deck)
} else {
(currentPlayer, deck)
}
val nextRoundPlayers = players.tail :+ nextRoundPlayer
playGameHelper(nextRoundPlayers, nextRoundDeck)
} else {
determineWinners(players)
}
}
val playersWithDealer = players :+ Player.dealer
playGameHelper(playersWithDealer, Deck.fullShuffled)
}
val players = Seq(
Player("Adam"),
)
val results = playGame(players)
println("Results:")
results.foreach(println(_))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment