Created
August 30, 2019 03:50
-
-
Save Experiment5X/d4470cdbb4b1413a3cd0884def1e07dd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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