diff --git a/README.md b/README.md index 4e5d37809..f4abde6ac 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,19 @@ # kotlin-blackjack + +## Step 3 +### Functional Requirements +Implement a program based on a simplified version of Blackjack. In this game, the player or dealer with a total closest to 21—without going over—wins. + +- Card values follow standard Blackjack rules: + - Number cards are counted by their face value. + - Face cards (King, Queen, Jack) are each worth 10. + - Aces can be worth either 1 or 11. +- Each player starts with two cards. +- Players may draw additional cards as long as their total remains 21 or less. +- The dealer must draw a card if their total is 16 or less, and must stand on 17 or more. +- If the dealer busts (goes over 21), all remaining players automatically win. +- After the game ends, display the result (win/loss) for each player. + ## Step 2 ### Functional Requirements - Card values follow standard Blackjack rules: @@ -8,10 +23,35 @@ - At the start of the game, each player receives two cards. - Players can choose to draw additional cards as long as their total does not exceed 21. -### Player +### Participant - [x] Have hand -- [x] Have name - [x] Add card to Hand +- [x] Open Card for first + +### Dealer +- [x] Implement Participant +- [x] Add card to Hand + +### Player +- [x] Implement Participant +- [x] Have name + +### FirstTurn +- [x] Draw cards +- [x] When the sum is 21 return Blackjack +- [x] WHen the sum is less than 21 return Hit + +### Hit +- [x] Draw Card +- [x] When the sum is over than 21 return Bust +- [x] When the sum is less than 21 return Hit +- [x] return stay + +### Blackjack + +### Bust + +### Stay ### Hand - [x] Have cards as a list @@ -21,6 +61,7 @@ - [x] Return size - [x] Have to have at least two cards - [x] Return is bust +- [x] Return is blackjack ### PlayingCard - [x] Has suit and denomination @@ -45,4 +86,9 @@ ### OutputView - [x] Display player's cards - [x] Display player's cards and total score -- [x] Display first turn \ No newline at end of file +- [x] Display first turn +- [x] Display final results + +### WinningResult +- [x] Calculate winners between two participants +- [x] Return the result of participant \ No newline at end of file diff --git a/src/main/kotlin/Casino.kt b/src/main/kotlin/Casino.kt index 438b403c3..27a4f0062 100644 --- a/src/main/kotlin/Casino.kt +++ b/src/main/kotlin/Casino.kt @@ -1,3 +1,10 @@ +import card.Deck +import card.PlayingCard +import participant.Dealer +import participant.Participant +import participant.Player +import state.Bust +import state.FirstTurn import view.InputView import view.OutputView @@ -8,22 +15,37 @@ class Casino( fun run() { val deck = Deck(PlayingCard.ALL.shuffled()) val names = inputView.getPlayerNames() - val players = names.map { Player(it, Hand(deck.drawCard(2))) } - outputView.printFirstTurn(players) + val players = names.map { Player(it, FirstTurn(Hand(emptyList()))) } + val dealer = Dealer(state = FirstTurn(Hand(emptyList()))) - players.forEach { turn(it, deck) } - players.forEach { outputView.printScore(it) } + val participants: List = players + dealer + repeat(2) { participants.forEach { it.drawCard(deck.drawOne()) } } + + outputView.printFirstTurn(participants) + + participants.forEach { turn(it, deck) } + participants.forEach { outputView.printScore(it) } + + val winningResult = WinningResult(dealer) + participants.forEach { + val result = winningResult.versus(it) + outputView.printResult(it, result) + } } private fun turn( - player: Player, + participant: Participant, deck: Deck, ) { while (true) { - val response = inputView.getResponse(player.name) - if (!response || player.hand.isBust()) return - player.drawCard(deck.drawCard(1).first()) - outputView.printPlayerCards(player) + val response = inputView.getResponse(participant) + if (!response || !participant.wantDraw()) { + participant.stay() + return + } + participant.drawCard(deck.drawOne()) + outputView.printPlayerCards(participant) + if (participant.state is Bust) return } } } diff --git a/src/main/kotlin/GambleResult.kt b/src/main/kotlin/GambleResult.kt new file mode 100644 index 000000000..fda0fd974 --- /dev/null +++ b/src/main/kotlin/GambleResult.kt @@ -0,0 +1,23 @@ +data class GambleResult( + val win: Int = 0, + val lose: Int = 0, + val draw: Int = 0, +) { + operator fun plus(other: GameResult): GambleResult { + return when (other) { + GameResult.WIN -> copy(win = win + 1) + GameResult.LOSE -> copy(lose = lose + 1) + GameResult.DRAW -> copy(draw = draw + 1) + } + } + + companion object { + fun from(value: GameResult): GambleResult { + return when (value) { + GameResult.WIN -> GambleResult(win = 1) + GameResult.LOSE -> GambleResult(lose = 1) + GameResult.DRAW -> GambleResult(draw = 1) + } + } + } +} diff --git a/src/main/kotlin/GameResult.kt b/src/main/kotlin/GameResult.kt new file mode 100644 index 000000000..a46b52ba5 --- /dev/null +++ b/src/main/kotlin/GameResult.kt @@ -0,0 +1,16 @@ +enum class GameResult { + WIN, + LOSE, + DRAW, + ; + + companion object { + fun getApposite(result: GameResult): GameResult { + return when (result) { + WIN -> LOSE + DRAW -> DRAW + LOSE -> WIN + } + } + } +} diff --git a/src/main/kotlin/Hand.kt b/src/main/kotlin/Hand.kt index 779edf1c9..e849e48e8 100644 --- a/src/main/kotlin/Hand.kt +++ b/src/main/kotlin/Hand.kt @@ -1,8 +1,7 @@ -class Hand(cards: List) { - init { - require(cards.size >= MINIMUM_SIZE) { ERROR_MINIMUM_SIZE } - } +import card.Denomination +import card.PlayingCard +class Hand(cards: List) { private val _cards: MutableList = cards.toMutableList() val cards: List get() = _cards.toList() @@ -24,10 +23,13 @@ class Hand(cards: List) { return score() > MAX_SCORE } + fun isBlackjack(): Boolean { + return size == BLACKJACK_SIZE && score() == MAX_SCORE + } + companion object { private const val BONUS = 10 - private const val MINIMUM_SIZE = 2 private const val MAX_SCORE = 21 - private const val ERROR_MINIMUM_SIZE = "Have to have at least two cards" + private const val BLACKJACK_SIZE = 2 } } diff --git a/src/main/kotlin/Player.kt b/src/main/kotlin/Player.kt deleted file mode 100644 index 429682a16..000000000 --- a/src/main/kotlin/Player.kt +++ /dev/null @@ -1,5 +0,0 @@ -class Player(val name: String, val hand: Hand) { - fun drawCard(card: PlayingCard) { - hand.add(card) - } -} diff --git a/src/main/kotlin/WinningResult.kt b/src/main/kotlin/WinningResult.kt new file mode 100644 index 000000000..3e9dcb79d --- /dev/null +++ b/src/main/kotlin/WinningResult.kt @@ -0,0 +1,47 @@ +import GameResult.Companion.getApposite +import GameResult.DRAW +import GameResult.LOSE +import GameResult.WIN +import participant.Participant +import state.Blackjack +import state.Bust +import state.Stay + +class WinningResult(private val participant: Participant) { + private val participantScore: MutableMap = + mutableMapOf( + WIN to 0, + LOSE to 0, + DRAW to 0, + ) + + fun versus(player: Participant): GambleResult { + if (participant.name == player.name) { + return GambleResult( + win = participantScore[WIN] ?: 0, + lose = participantScore[WIN] ?: 0, + draw = participantScore[WIN] ?: 0, + ) + } + val result = compare(player) + participantScore[result] = participantScore.getValue(result) + 1 + val playerResult = getApposite(result) + return GambleResult.from(playerResult) + } + + private fun compare(player: Participant): GameResult { + return when (participant.state) { + is Bust -> if (player.state is Bust) WIN else LOSE + is Blackjack -> if (player.state is Blackjack) DRAW else WIN + is Stay -> + when { + player.state is Bust -> WIN + participant.score() < player.score() -> LOSE + participant.score() == player.score() -> DRAW + else -> WIN + } + + else -> throw IllegalStateException() + } + } +} diff --git a/src/main/kotlin/Deck.kt b/src/main/kotlin/card/Deck.kt similarity index 61% rename from src/main/kotlin/Deck.kt rename to src/main/kotlin/card/Deck.kt index 44499020c..a01f6aebb 100644 --- a/src/main/kotlin/Deck.kt +++ b/src/main/kotlin/card/Deck.kt @@ -1,9 +1,11 @@ +package card + class Deck(cards: List) { private val _cards = cards.toMutableList() val cards: List get() = _cards.toList() - fun drawCard(count: Int): List { - return List(count) { _cards.removeFirst() } + fun drawOne(): PlayingCard { + return _cards.removeFirst() } } diff --git a/src/main/kotlin/Denomination.kt b/src/main/kotlin/card/Denomination.kt similarity index 93% rename from src/main/kotlin/Denomination.kt rename to src/main/kotlin/card/Denomination.kt index 2b31666db..145060c75 100644 --- a/src/main/kotlin/Denomination.kt +++ b/src/main/kotlin/card/Denomination.kt @@ -1,3 +1,5 @@ +package card + enum class Denomination(val score: Int) { ACE(1), TWO(2), diff --git a/src/main/kotlin/PlayingCard.kt b/src/main/kotlin/card/PlayingCard.kt similarity index 97% rename from src/main/kotlin/PlayingCard.kt rename to src/main/kotlin/card/PlayingCard.kt index 2a8c8b870..eb1d76f68 100644 --- a/src/main/kotlin/PlayingCard.kt +++ b/src/main/kotlin/card/PlayingCard.kt @@ -1,3 +1,5 @@ +package card + data class PlayingCard private constructor(val suit: Suit, val denomination: Denomination) { companion object { val ALL = diff --git a/src/main/kotlin/Suit.kt b/src/main/kotlin/card/Suit.kt similarity index 82% rename from src/main/kotlin/Suit.kt rename to src/main/kotlin/card/Suit.kt index 6886db30f..c1327794f 100644 --- a/src/main/kotlin/Suit.kt +++ b/src/main/kotlin/card/Suit.kt @@ -1,3 +1,5 @@ +package card + enum class Suit { CLUB, DIAMOND, diff --git a/src/main/kotlin/participant/Dealer.kt b/src/main/kotlin/participant/Dealer.kt new file mode 100644 index 000000000..a908c2579 --- /dev/null +++ b/src/main/kotlin/participant/Dealer.kt @@ -0,0 +1,14 @@ +package participant + +import card.PlayingCard +import state.State + +class Dealer(name: String = "Dealer", override var state: State) : Participant(name) { + override fun showCardFirst(): List { + return listOf(state.cards.first()) + } + + override fun wantDraw(): Boolean { + return score() < 17 + } +} diff --git a/src/main/kotlin/participant/Participant.kt b/src/main/kotlin/participant/Participant.kt new file mode 100644 index 000000000..b6401996d --- /dev/null +++ b/src/main/kotlin/participant/Participant.kt @@ -0,0 +1,24 @@ +package participant + +import card.PlayingCard +import state.State + +abstract class Participant(val name: String) { + abstract var state: State + + abstract fun showCardFirst(): List + + fun score(): Int { + return state.hand.score() + } + + fun stay() { + state = state.stay() + } + + fun drawCard(card: PlayingCard) { + state = state.drawCard(card) + } + + abstract fun wantDraw(): Boolean +} diff --git a/src/main/kotlin/participant/Player.kt b/src/main/kotlin/participant/Player.kt new file mode 100644 index 000000000..557cd4c89 --- /dev/null +++ b/src/main/kotlin/participant/Player.kt @@ -0,0 +1,14 @@ +package participant + +import card.PlayingCard +import state.State + +class Player(name: String, override var state: State) : Participant(name) { + override fun showCardFirst(): List { + return state.cards + } + + override fun wantDraw(): Boolean { + return true + } +} diff --git a/src/main/kotlin/state/Blackjack.kt b/src/main/kotlin/state/Blackjack.kt new file mode 100644 index 000000000..056f6ba47 --- /dev/null +++ b/src/main/kotlin/state/Blackjack.kt @@ -0,0 +1,5 @@ +package state + +import Hand + +class Blackjack(hand: Hand) : Finished(hand) diff --git a/src/main/kotlin/state/Bust.kt b/src/main/kotlin/state/Bust.kt new file mode 100644 index 000000000..f68750c02 --- /dev/null +++ b/src/main/kotlin/state/Bust.kt @@ -0,0 +1,5 @@ +package state + +import Hand + +class Bust(hand: Hand) : Finished(hand) diff --git a/src/main/kotlin/state/Finished.kt b/src/main/kotlin/state/Finished.kt new file mode 100644 index 000000000..57960faf8 --- /dev/null +++ b/src/main/kotlin/state/Finished.kt @@ -0,0 +1,14 @@ +package state + +import Hand +import card.PlayingCard + +abstract class Finished(override val hand: Hand) : State { + override fun drawCard(card: PlayingCard): State { + throw IllegalStateException() + } + + override fun stay(): State { + return this + } +} diff --git a/src/main/kotlin/state/FirstTurn.kt b/src/main/kotlin/state/FirstTurn.kt new file mode 100644 index 000000000..7a779abc3 --- /dev/null +++ b/src/main/kotlin/state/FirstTurn.kt @@ -0,0 +1,20 @@ +package state + +import Hand +import card.PlayingCard + +class FirstTurn(override val hand: Hand) : State { + override fun drawCard(card: PlayingCard): State { + hand.add(card) + + if (hand.size == 2) { + if (hand.isBlackjack()) return Blackjack(hand) + return Hit(hand) + } + return FirstTurn(hand) + } + + override fun stay(): State { + throw IllegalStateException() + } +} diff --git a/src/main/kotlin/state/Hit.kt b/src/main/kotlin/state/Hit.kt new file mode 100644 index 000000000..67cf4d030 --- /dev/null +++ b/src/main/kotlin/state/Hit.kt @@ -0,0 +1,20 @@ +package state + +import Hand +import card.PlayingCard + +class Hit(override val hand: Hand) : State { + override fun drawCard(card: PlayingCard): State { + hand.add(card) + + return if (hand.isBust()) { + return Bust(hand) + } else { + Hit(hand) + } + } + + override fun stay(): State { + return Stay(hand) + } +} diff --git a/src/main/kotlin/state/State.kt b/src/main/kotlin/state/State.kt new file mode 100644 index 000000000..80623d060 --- /dev/null +++ b/src/main/kotlin/state/State.kt @@ -0,0 +1,15 @@ +package state + +import Hand +import card.PlayingCard + +interface State { + val hand: Hand + + val cards: List + get() = hand.cards + + fun drawCard(cards: PlayingCard): State + + fun stay(): State +} diff --git a/src/main/kotlin/state/Stay.kt b/src/main/kotlin/state/Stay.kt new file mode 100644 index 000000000..c5b830bd4 --- /dev/null +++ b/src/main/kotlin/state/Stay.kt @@ -0,0 +1,5 @@ +package state + +import Hand + +class Stay(hand: Hand) : Finished(hand) diff --git a/src/main/kotlin/view/InputView.kt b/src/main/kotlin/view/InputView.kt index a5cfaccf9..a558908e7 100644 --- a/src/main/kotlin/view/InputView.kt +++ b/src/main/kotlin/view/InputView.kt @@ -1,17 +1,26 @@ package view +import participant.Dealer +import participant.Participant +import participant.Player + class InputView { fun getPlayerNames(): List { println(GUIDE_ENTER_PLAYER_NAMES) return readln().split(",").map { it.trim() } } - fun getResponse(name: String): Boolean { - println(GUIDE_ENTER_RESPONSE.format(name)) - val response = readln().trim() - if (response == "y") return true - if (response == "n") return false - return getResponse(name) + fun getResponse(participant: Participant): Boolean { + when (participant) { + is Dealer -> return true + is Player -> { + println(GUIDE_ENTER_RESPONSE.format(participant.name)) + val response = readln().trim() + if (response == "y") return true + if (response == "n") return false + } + } + return getResponse(participant) } companion object { diff --git a/src/main/kotlin/view/OutputView.kt b/src/main/kotlin/view/OutputView.kt index 1688f6392..3dce9b417 100644 --- a/src/main/kotlin/view/OutputView.kt +++ b/src/main/kotlin/view/OutputView.kt @@ -1,29 +1,59 @@ package view +import GambleResult +import GameResult +import GameResult.DRAW +import GameResult.LOSE +import GameResult.WIN import Hand -import Player -import PlayingCard -import Suit +import card.PlayingCard +import card.Suit +import participant.Dealer +import participant.Participant +import participant.Player class OutputView { - fun printScore(player: Player) { - print(MESSAGE_PLAYER_CARD.format(player.name, player.hand.toDisplay())) - println(MESSAGE_SCORE.format(player.hand.score())) + fun printResult( + participant: Participant, + result: GambleResult, + ) { + println(MESSAGE_FINAL_RESULT) + println(MESSAGE_RESULT.format(participant.name) + result.toDisplay()) } - fun printPlayerCards(player: Player) { - println(MESSAGE_PLAYER_CARD.format(player.name, player.hand.toDisplay())) + fun printScore(participant: Participant) { + print(MESSAGE_PLAYER_CARD.format(participant.name, participant.state.hand.toDisplay())) + println(MESSAGE_SCORE.format(participant.score())) } - fun printFirstTurn(players: List) { - println(MESSAGE_DEALING_CARDS.format(players.map { it.name }.joinToString())) - players.forEach { printPlayerCards(it) } + fun printPlayerCards(participant: Participant) { + when (participant) { + is Dealer -> { + println(MESSAGE_DRAW_DEALER) + } + is Player -> { + println(MESSAGE_PLAYER_CARD.format(participant.name, participant.showCardFirst().toDisplay())) + } + } + } + + fun printFirstTurn(participants: List) { + println(MESSAGE_DEALING_CARDS.format(participants.joinToString { it.name })) + participants.forEach { printPlayerCards(it) } + } + + private fun List.toDisplay(): String { + return this.map { it.toDisplay() }.toString() } private fun Hand.toDisplay(): String { return this.cards.map { it.toDisplay() }.toString() } + private fun Map.toDisplay(): String { + return MESSAGE_RESULTS.format(this[WIN], this[LOSE], this[DRAW]) + } + private fun PlayingCard.toDisplay(): String { return this.denomination.toString() + this.suit.toEmoji() } @@ -37,9 +67,23 @@ class OutputView { } } + fun GambleResult.toDisplay(): String { + val parts = mutableListOf() + + if (win > 0) parts += "$win Win" + if (lose > 0) parts += "$lose Lose" + if (draw > 0) parts += "$draw Draw" + + return parts.joinToString(" ") + } + companion object { private const val MESSAGE_DEALING_CARDS = "Dealing two cards to %s" private const val MESSAGE_PLAYER_CARD = "%s's cards: %s" private const val MESSAGE_SCORE = "– Total: %d" + private const val MESSAGE_DRAW_DEALER = "Dealer draws one more card due to having 16 or less." + private const val MESSAGE_FINAL_RESULT = "## Final Results" + private const val MESSAGE_RESULT = "%s: " + private const val MESSAGE_RESULTS = "%d Win %d Lose %d Draw" } } diff --git a/src/test/kotlin/CardsFixture.kt b/src/test/kotlin/CardsFixture.kt index 7d9ea695f..7b52b941b 100644 --- a/src/test/kotlin/CardsFixture.kt +++ b/src/test/kotlin/CardsFixture.kt @@ -1,3 +1,7 @@ +import card.Denomination +import card.PlayingCard +import card.Suit + val CLUB_SEVEN: PlayingCard = PlayingCard.of(Suit.CLUB, Denomination.SEVEN) val CLUB_THREE: PlayingCard = PlayingCard.of(Suit.CLUB, Denomination.THREE) val CLUB_TWO: PlayingCard = PlayingCard.of(Suit.CLUB, Denomination.TWO) diff --git a/src/test/kotlin/DeckTest.kt b/src/test/kotlin/DeckTest.kt index 817b86a0d..991fcab8d 100644 --- a/src/test/kotlin/DeckTest.kt +++ b/src/test/kotlin/DeckTest.kt @@ -1,3 +1,4 @@ +import card.Deck import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll @@ -9,11 +10,11 @@ class DeckTest { val deck = Deck(listOf(CLUB_SEVEN, CLUB_THREE, CLUB_KING)) // when - val actual = deck.drawCard(1) + val actual = deck.drawOne() // then assertAll( - { assertThat(actual.size).isEqualTo(1) }, + { assertThat(actual).isEqualTo(CLUB_SEVEN) }, { assertThat(deck.cards.size).isEqualTo(2) }, ) } diff --git a/src/test/kotlin/HandTest.kt b/src/test/kotlin/HandTest.kt index fd0802288..3574a8db7 100644 --- a/src/test/kotlin/HandTest.kt +++ b/src/test/kotlin/HandTest.kt @@ -1,16 +1,8 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.api.assertThrows class HandTest { - @Test - fun `Throw exception when hand has cards less than 2`() { - assertThrows { - Hand(listOf(CLUB_SEVEN)) - } - } - @Test fun `Create Hand when hand has cards more than 2`() { assertDoesNotThrow { @@ -130,4 +122,18 @@ class HandTest { // then assertThat(actual).isEqualTo(expected) } + + @Test + fun `Return blackjack when the card size is 2 and sum is 21`() { + // given + val cards = listOf(CLUB_KING, CLUB_ACE) + val hand = Hand(cards) + val expected = true + + // when + val actual = hand.isBlackjack() + + // then + assertThat(actual).isEqualTo(expected) + } } diff --git a/src/test/kotlin/PlayerTest.kt b/src/test/kotlin/PlayerTest.kt deleted file mode 100644 index 2e6d0768b..000000000 --- a/src/test/kotlin/PlayerTest.kt +++ /dev/null @@ -1,18 +0,0 @@ -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - -class PlayerTest { - @Test - fun `Add card to hand`() { - // given - val hand = Hand(listOf(CLUB_SEVEN, CLUB_SEVEN)) - val player = Player("krrong", hand) - val expected = 3 - - // when - player.drawCard(CLUB_TWO) - - // then - assertThat(player.hand.size).isEqualTo(expected) - } -} diff --git a/src/test/kotlin/WinningResultTest.kt b/src/test/kotlin/WinningResultTest.kt new file mode 100644 index 000000000..c7def4a70 --- /dev/null +++ b/src/test/kotlin/WinningResultTest.kt @@ -0,0 +1,151 @@ +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import participant.Dealer +import participant.Player +import state.Blackjack +import state.Bust +import state.Stay + +class WinningResultTest { + private lateinit var winningResult: WinningResult + private lateinit var dealer: Dealer + private lateinit var player: Player + + @Test + fun `when the dealer is bust, the player is bust then player lose`() { + // given + val hand = Hand(emptyList()) + dealer = Dealer(state = Bust(hand)) + player = Player(name = "seokjin", state = Bust(hand)) + winningResult = WinningResult(dealer) + + // when + val result = winningResult.versus(player) + + // then + assertThat(result.lose).isEqualTo(1) + } + + @Test + fun `when the dealer is bust, the player is not bust then player win`() { + // given + val hand = Hand(emptyList()) + dealer = Dealer(state = Bust(hand)) + player = Player(name = "seokjin", state = Stay(hand)) + winningResult = WinningResult(dealer) + + // when + val result = winningResult.versus(player) + + // then + assertThat(result.win).isEqualTo(1) + } + + @Test + fun `when the dealer is Stay, the player is bust then player lose`() { + // given + val hand = Hand(emptyList()) + dealer = Dealer(state = Stay(hand)) + player = Player(name = "seokjin", state = Bust(hand)) + winningResult = WinningResult(dealer) + + // when + val result = winningResult.versus(player) + + // then + assertThat(result.lose).isEqualTo(1) + } + + @Test + fun `when the dealer is Stay, the player is stay and dealer's score is high then player lose`() { + // given + val dealerHand = Hand(listOf(CLUB_SEVEN, CLUB_SEVEN)) + val playerHand = Hand(listOf(CLUB_THREE, CLUB_THREE)) + dealer = Dealer(state = Stay(dealerHand)) + player = Player(name = "seokjin", state = Stay(playerHand)) + winningResult = WinningResult(dealer) + + // when + val result = winningResult.versus(player) + + // then + assertThat(result.lose).isEqualTo(1) + } + + @Test + fun `when the dealer is Stay, the player is stay and dealer's score is low then player lose`() { + // given + val dealerHand = Hand(listOf(CLUB_THREE, CLUB_THREE)) + val playerHand = Hand(listOf(CLUB_SEVEN, CLUB_SEVEN)) + dealer = Dealer(state = Stay(dealerHand)) + player = Player(name = "seokjin", state = Stay(playerHand)) + winningResult = WinningResult(dealer) + + // when + val result = winningResult.versus(player) + + // then + assertThat(result.win).isEqualTo(1) + } + + @Test + fun `when the dealer is Stay, the player is stay and scores are same then draw`() { + // given + val dealerHand = Hand(listOf(CLUB_THREE, CLUB_THREE)) + val playerHand = Hand(listOf(CLUB_THREE, CLUB_THREE)) + dealer = Dealer(state = Stay(dealerHand)) + player = Player(name = "seokjin", state = Stay(playerHand)) + winningResult = WinningResult(dealer) + + // when + val result = winningResult.versus(player) + + // then + assertThat(result.draw).isEqualTo(1) + } + + @Test + fun `when the dealer is Blackjack, the player is Bust then player lose`() { + // given + val hand = Hand(emptyList()) + dealer = Dealer(state = Blackjack(hand)) + player = Player(name = "seokjin", state = Bust(hand)) + winningResult = WinningResult(dealer) + + // when + val result = winningResult.versus(player) + + // then + assertThat(result.lose).isEqualTo(1) + } + + @Test + fun `when the dealer is Blackjack, the player is Blackjack then draw`() { + // given + val hand = Hand(emptyList()) + dealer = Dealer(state = Blackjack(hand)) + player = Player(name = "seokjin", state = Blackjack(hand)) + winningResult = WinningResult(dealer) + + // when + val result = winningResult.versus(player) + + // then + assertThat(result.draw).isEqualTo(1) + } + + @Test + fun `when the dealer is Blackjack, the player is Stay then player lose`() { + // given + val hand = Hand(emptyList()) + dealer = Dealer(state = Blackjack(hand)) + player = Player(name = "seokjin", state = Stay(hand)) + winningResult = WinningResult(dealer) + + // when + val result = winningResult.versus(player) + + // then + assertThat(result.lose).isEqualTo(1) + } +} diff --git a/src/test/kotlin/participant/DealerTest.kt b/src/test/kotlin/participant/DealerTest.kt new file mode 100644 index 000000000..76abdbe6e --- /dev/null +++ b/src/test/kotlin/participant/DealerTest.kt @@ -0,0 +1,24 @@ +package participant + +import CLUB_SEVEN +import Hand +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test +import state.Hit + +class DealerTest { + @Test + fun `Return dealers first cards`() { + // given + val hand = Hand(listOf(CLUB_SEVEN, CLUB_SEVEN)) + val hit = Hit(hand) + val dealer = Dealer(state = hit) + val expected = listOf(CLUB_SEVEN) + + // when + val actual = dealer.showCardFirst() + + // then + Assertions.assertThat(actual).isEqualTo(expected) + } +} diff --git a/src/test/kotlin/participant/PlayerTest.kt b/src/test/kotlin/participant/PlayerTest.kt new file mode 100644 index 000000000..87b43ab4d --- /dev/null +++ b/src/test/kotlin/participant/PlayerTest.kt @@ -0,0 +1,40 @@ +package participant + +import CLUB_SEVEN +import CLUB_TWO +import Hand +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import state.Hit + +class PlayerTest { + @Test + fun `Add card to hand`() { + // given + val hand = Hand(listOf(CLUB_SEVEN, CLUB_SEVEN)) + val hit = Hit(hand) + val player = Player("krrong", hit) + val expected = 3 + + // when + player.drawCard(CLUB_TWO) + + // then + assertThat(player.state.hand.size).isEqualTo(expected) + } + + @Test + fun `Return players cards`() { + // given + val hand = Hand(listOf(CLUB_SEVEN, CLUB_SEVEN)) + val hit = Hit(hand) + val player = Player("krrong", hit) + val expected = listOf(CLUB_SEVEN, CLUB_SEVEN) + + // when + val actual = player.showCardFirst() + + // then + assertThat(actual).isEqualTo(expected) + } +} diff --git a/src/test/kotlin/state/FirstTurnTest.kt b/src/test/kotlin/state/FirstTurnTest.kt new file mode 100644 index 000000000..101719ca2 --- /dev/null +++ b/src/test/kotlin/state/FirstTurnTest.kt @@ -0,0 +1,41 @@ +package state + +import CLUB_ACE +import CLUB_KING +import Hand +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class FirstTurnTest { + @Test + fun `When draw two cards with 21 sum then return Blackjack`() { + // given + val hand = Hand(emptyList()) + val firstTurn = FirstTurn(hand) + + // when + val actual = + firstTurn + .drawCard(CLUB_KING) + .drawCard(CLUB_ACE) + + // then + assertThat(actual).isInstanceOf(Blackjack::class.java) + } + + @Test + fun `When draw two cards less than 21 sum then return Hit`() { + // given + val hand = Hand(emptyList()) + val firstTurn = FirstTurn(hand) + + // when + val actual = + firstTurn + .drawCard(CLUB_KING) + .drawCard(CLUB_KING) + + // then + assertThat(actual).isInstanceOf(Hit::class.java) + } +} diff --git a/src/test/kotlin/state/HitTest.kt b/src/test/kotlin/state/HitTest.kt new file mode 100644 index 000000000..be0c2cb9a --- /dev/null +++ b/src/test/kotlin/state/HitTest.kt @@ -0,0 +1,62 @@ +package state + +import CLUB_ACE +import CLUB_KING +import CLUB_TWO +import Hand +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class HitTest { + @Test + fun `Return Bust when the sum is over than 21`() { + // given + val hand = Hand(listOf(CLUB_KING, CLUB_KING)) + val hit = Hit(hand) + + // when + val actual = hit.drawCard(CLUB_KING) + + // then + assertThat(actual).isInstanceOf(Bust::class.java) + } + + @Test + fun `Return Hit when the sum is less than 21`() { + // given + val hand = Hand(listOf(CLUB_TWO, CLUB_TWO)) + val hit = Hit(hand) + + // when + val actual = hit.drawCard(CLUB_TWO) + + // then + assertThat(actual).isInstanceOf(Hit::class.java) + } + + @Test + fun `Return Hit when the sum is 21`() { + // given + val hand = Hand(listOf(CLUB_KING, CLUB_KING)) + val hit = Hit(hand) + + // when + val actual = hit.drawCard(CLUB_ACE) + + // then + assertThat(actual).isInstanceOf(Hit::class.java) + } + + @Test + fun `Return Hit when stay`() { + // given + val hand = Hand(listOf(CLUB_KING, CLUB_KING)) + val hit = Hit(hand) + + // when + val actual = hit.stay() + + // then + assertThat(actual).isInstanceOf(Stay::class.java) + } +}