From bca0a3068252003f17f9f649ea59b71af6562781 Mon Sep 17 00:00:00 2001 From: Duhan Mo <duhandv@gmail.com> Date: Wed, 11 Dec 2024 18:51:34 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=B6=9C=EB=A0=A5=EA=B5=AC=EB=AC=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +- src/main/kotlin/blackjack/Main.kt | 4 +- .../blackjack/controller/BlackjackGame.kt | 92 +++--------- .../kotlin/blackjack/controller/GameTable.kt | 46 ++++++ src/main/kotlin/blackjack/domain/Cards.kt | 46 ------ src/main/kotlin/blackjack/domain/Deck.kt | 14 -- .../kotlin/blackjack/domain/GameResult.kt | 27 ---- src/main/kotlin/blackjack/domain/GameTable.kt | 21 --- .../kotlin/blackjack/domain/Participant.kt | 63 -------- .../blackjack/domain/{ => card}/Card.kt | 4 +- src/main/kotlin/blackjack/domain/card/Deck.kt | 8 + src/main/kotlin/blackjack/domain/card/Hand.kt | 30 ++++ .../blackjack/domain/{ => card}/Rank.kt | 7 +- .../blackjack/domain/{ => card}/Suit.kt | 2 +- .../blackjack/domain/dto/PlayerGameResult.kt | 9 -- .../blackjack/domain/game/GameResult.kt | 31 ++++ .../domain/{ => game}/MatchResult.kt | 4 +- .../domain/{ => game}/dto/DealerGameResult.kt | 4 +- .../domain/game/dto/PlayerGameResult.kt | 9 ++ .../kotlin/blackjack/domain/player/Dealer.kt | 16 ++ .../kotlin/blackjack/domain/player/Player.kt | 42 ++++++ src/main/kotlin/blackjack/view/InputView.kt | 11 +- src/main/kotlin/blackjack/view/ResultView.kt | 63 ++++---- src/test/kotlin/blackjack/domain/CardTest.kt | 21 --- .../kotlin/blackjack/domain/DealerTest.kt | 19 --- src/test/kotlin/blackjack/domain/DeckTest.kt | 21 --- .../kotlin/blackjack/domain/GameTableTest.kt | 15 -- .../kotlin/blackjack/domain/PlayerTest.kt | 132 ----------------- .../kotlin/blackjack/domain/card/DeckTest.kt | 30 ++++ .../domain/{CardsTest.kt => card/HandTest.kt} | 16 +- .../blackjack/domain/{ => card}/RankTest.kt | 2 +- .../blackjack/domain/game/GameResultTest.kt | 66 +++++++++ .../blackjack/domain/player/DealerTest.kt | 42 ++++++ .../blackjack/domain/player/PlayerTest.kt | 140 ++++++++++++++++++ .../kotlin/blackjack/fixtures/CardFixture.kt | 8 +- .../kotlin/blackjack/fixtures/UserFixtures.kt | 8 - 36 files changed, 548 insertions(+), 531 deletions(-) create mode 100644 src/main/kotlin/blackjack/controller/GameTable.kt delete mode 100644 src/main/kotlin/blackjack/domain/Cards.kt delete mode 100644 src/main/kotlin/blackjack/domain/Deck.kt delete mode 100644 src/main/kotlin/blackjack/domain/GameResult.kt delete mode 100644 src/main/kotlin/blackjack/domain/GameTable.kt delete mode 100644 src/main/kotlin/blackjack/domain/Participant.kt rename src/main/kotlin/blackjack/domain/{ => card}/Card.kt (82%) create mode 100644 src/main/kotlin/blackjack/domain/card/Deck.kt create mode 100644 src/main/kotlin/blackjack/domain/card/Hand.kt rename src/main/kotlin/blackjack/domain/{ => card}/Rank.kt (76%) rename src/main/kotlin/blackjack/domain/{ => card}/Suit.kt (82%) delete mode 100644 src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt create mode 100644 src/main/kotlin/blackjack/domain/game/GameResult.kt rename src/main/kotlin/blackjack/domain/{ => game}/MatchResult.kt (64%) rename src/main/kotlin/blackjack/domain/{ => game}/dto/DealerGameResult.kt (57%) create mode 100644 src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt create mode 100644 src/main/kotlin/blackjack/domain/player/Dealer.kt create mode 100644 src/main/kotlin/blackjack/domain/player/Player.kt delete mode 100644 src/test/kotlin/blackjack/domain/CardTest.kt delete mode 100644 src/test/kotlin/blackjack/domain/DealerTest.kt delete mode 100644 src/test/kotlin/blackjack/domain/DeckTest.kt delete mode 100644 src/test/kotlin/blackjack/domain/GameTableTest.kt delete mode 100644 src/test/kotlin/blackjack/domain/PlayerTest.kt create mode 100644 src/test/kotlin/blackjack/domain/card/DeckTest.kt rename src/test/kotlin/blackjack/domain/{CardsTest.kt => card/HandTest.kt} (54%) rename src/test/kotlin/blackjack/domain/{ => card}/RankTest.kt (96%) create mode 100644 src/test/kotlin/blackjack/domain/game/GameResultTest.kt create mode 100644 src/test/kotlin/blackjack/domain/player/DealerTest.kt create mode 100644 src/test/kotlin/blackjack/domain/player/PlayerTest.kt delete mode 100644 src/test/kotlin/blackjack/fixtures/UserFixtures.kt diff --git a/README.md b/README.md index 3c69bf851..7817229dc 100644 --- a/README.md +++ b/README.md @@ -37,4 +37,8 @@ ### 기능 구현사항 - [x] 딜러는 처음받는 2장의 합계가 16이하면 반드시 1장의 카드를 추가로 받는다 - [x] 딜러가 21을 초과하면 남은 플레이어들은 패에 상관없이 승리한다 -- [x] 게임 완료 후 각 플레이어별로 승패를 출력한다 \ No newline at end of file +- [x] 게임 완료 후 각 플레이어별로 승패를 출력한다 +- [x] data class , 일반 클래스 설정 일관성 유지 +- [x] 도메인 패키지 분리 +- [x] 객체 상태를 객체가 관리 +- [x] 게임 관련 로직 분리 \ No newline at end of file diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt index 6a4dee803..c857254f0 100644 --- a/src/main/kotlin/blackjack/Main.kt +++ b/src/main/kotlin/blackjack/Main.kt @@ -1,9 +1,7 @@ package blackjack import blackjack.controller.BlackjackGame -import blackjack.view.InputView -import blackjack.view.ResultView fun main() { - BlackjackGame(InputView, ResultView).start() + BlackjackGame.start() } diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index 6a2eb75db..c090e01c1 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -1,87 +1,35 @@ package blackjack.controller -import blackjack.domain.Dealer -import blackjack.domain.Deck -import blackjack.domain.GameResult -import blackjack.domain.GameTable -import blackjack.domain.Participant -import blackjack.domain.Player +import blackjack.domain.card.Deck +import blackjack.domain.player.Dealer +import blackjack.domain.player.Player import blackjack.view.InputView import blackjack.view.ResultView -data class BlackjackGame( - private val inputView: InputView, - private val resultView: ResultView, -) { +object BlackjackGame { fun start() { - val gameTable = GameTable(Deck.create()) - val participants = playGame(gameTable) - printCard(participants) - printGameResult(participants) + val gameTable = setUp() + initDeal(gameTable) + turnStart(gameTable) + ResultView.printAfterTurn(gameTable) } - private fun playGame(gameTable: GameTable): List<Participant> { - val participants = setUpInitCard(gameTable) - val (players, dealer) = Participant.separate(participants) - val gamedPlayers = playersTurn(players, gameTable) - resultView.linebreak() - val gamedDealer = dealerTurn(dealer, gameTable) - return gamedPlayers + gamedDealer + private fun setUp(): GameTable { + val gameTable = GameTable(Deck(), Dealer(), getPlayers()) + ResultView.linebreak() + return gameTable } - private fun setUpInitCard(gameTable: GameTable): List<Participant> { - val participants = gameTable.dealInitCard(getParticipants()) - resultView.linebreak() - resultView.printInitCardReceive(participants) - resultView.printParticipantsCard(participants = participants, printScore = false) - resultView.linebreak() - return participants - } - - private fun getParticipants(): List<Participant> { - return buildList { - add(Dealer.create()) - addAll(inputView.inputNames().map { Player.create(name = it) }) - } - } - - private fun playersTurn( - participants: List<Participant>, - gameTable: GameTable, - ): List<Participant> { - return participants.map { playerTurn(it, gameTable) } - } - - private tailrec fun playerTurn( - player: Participant, - gameTable: GameTable, - ): Participant { - if (!player.canHit() || !inputView.inputHit(player)) { - return player - } - val hitPlayer = gameTable.hit(player) - resultView.printParticipantCard(participant = hitPlayer, printScore = false) - return playerTurn(hitPlayer, gameTable) - } - - private tailrec fun dealerTurn( - dealer: Participant, - gameTable: GameTable, - ): Participant { - if (!dealer.canHit()) { - return dealer - } - resultView.printDealerHit() - return dealerTurn(gameTable.hit(dealer), gameTable) - } + private fun getPlayers(): List<Player> = InputView.inputNames().map { Player(it) } - private fun printCard(participants: List<Participant>) { - resultView.linebreak() - resultView.printParticipantsCard(participants = participants, printScore = true) + private fun initDeal(gameTable: GameTable) { + gameTable.dealInitCard() + ResultView.printDealInitCard(gameTable) } - private fun printGameResult(participants: List<Participant>) { - resultView.linebreak() - resultView.printGameResult(GameResult.from(participants)) + private fun turnStart(gameTable: GameTable) { + gameTable.playersTurn() + ResultView.linebreak() + gameTable.dealerTurn() } } diff --git a/src/main/kotlin/blackjack/controller/GameTable.kt b/src/main/kotlin/blackjack/controller/GameTable.kt new file mode 100644 index 000000000..4fa31a914 --- /dev/null +++ b/src/main/kotlin/blackjack/controller/GameTable.kt @@ -0,0 +1,46 @@ +package blackjack.controller + +import blackjack.domain.card.Deck +import blackjack.domain.game.GameResult +import blackjack.domain.player.Dealer +import blackjack.domain.player.Player +import blackjack.view.InputView +import blackjack.view.ResultView + +class GameTable( + val deck: Deck, + val dealer: Dealer, + val players: List<Player>, +) { + fun dealInitCard() = + repeat(INIT_CARD_DRAW_COUNT) { + dealer.hit(deck.draw()) + players.forEach { it.hit(deck.draw()) } + } + + fun playersTurn() = players.forEach { playerTurn(it) } + + fun dealerTurn() { + if (!dealer.canHit()) { + return + } + ResultView.printDealerHit() + dealer.hit(deck.draw()) + dealerTurn() + } + + fun getGameResult(): GameResult = GameResult.from(dealer, players) + + private fun playerTurn(player: Player) { + if (!player.canHit() || !InputView.inputHit(player)) { + return + } + player.hit(deck.draw()) + ResultView.printPlayerCard(player) + playerTurn(player) + } + + companion object { + const val INIT_CARD_DRAW_COUNT = 2 + } +} diff --git a/src/main/kotlin/blackjack/domain/Cards.kt b/src/main/kotlin/blackjack/domain/Cards.kt deleted file mode 100644 index aabe71254..000000000 --- a/src/main/kotlin/blackjack/domain/Cards.kt +++ /dev/null @@ -1,46 +0,0 @@ -package blackjack.domain - -import blackjack.domain.MatchResult.DRAW -import blackjack.domain.MatchResult.LOSS -import blackjack.domain.MatchResult.WIN - -data class Cards(val values: List<Card>) { - val score: Int - get() = calculateScore() - - val isBust: Boolean - get() = calculateScore() > BLACKJACK_SCORE_LIMIT - - fun scoreLowerThan(limit: Int): Boolean { - return score < limit - } - - fun compareScore(other: Cards): MatchResult { - return when { - score > other.score -> WIN - score < other.score -> LOSS - else -> DRAW - } - } - - fun add(card: Card): Cards { - return Cards(values + card) - } - - private fun calculateScore(): Int { - val totalScore = values.sumOf { it.score } - var aceCount = values.count { it.isAce } - - var adjustedScore = totalScore - while (adjustedScore > BLACKJACK_SCORE_LIMIT && aceCount > 0) { - adjustedScore -= ACE_SCORE_DIFFERENCE - aceCount-- - } - return adjustedScore - } - - companion object { - private const val BLACKJACK_SCORE_LIMIT = 21 - private const val ACE_SCORE_DIFFERENCE = 10 - } -} diff --git a/src/main/kotlin/blackjack/domain/Deck.kt b/src/main/kotlin/blackjack/domain/Deck.kt deleted file mode 100644 index d6b33e27c..000000000 --- a/src/main/kotlin/blackjack/domain/Deck.kt +++ /dev/null @@ -1,14 +0,0 @@ -package blackjack.domain - -data class Deck(private val cards: MutableList<Card>) { - fun draw(): Card { - check(cards.isNotEmpty()) { "카드가 모두 소진되었습니다" } - return cards.removeFirst() - } - - companion object { - fun create(): Deck { - return Deck(Card.ALL.shuffled().toMutableList()) - } - } -} diff --git a/src/main/kotlin/blackjack/domain/GameResult.kt b/src/main/kotlin/blackjack/domain/GameResult.kt deleted file mode 100644 index 5cfbb1cfb..000000000 --- a/src/main/kotlin/blackjack/domain/GameResult.kt +++ /dev/null @@ -1,27 +0,0 @@ -package blackjack.domain - -import blackjack.domain.MatchResult.DRAW -import blackjack.domain.MatchResult.LOSS -import blackjack.domain.MatchResult.WIN -import blackjack.domain.dto.DealerGameResult -import blackjack.domain.dto.PlayerGameResult - -data class GameResult( - val dealerGameResult: DealerGameResult, - val playerGameResults: List<PlayerGameResult>, -) { - companion object { - fun from(participants: List<Participant>): GameResult { - val (players, dealer) = Participant.separate(participants) - val playerGameResults = players.map { player -> PlayerGameResult(player, player.compareScore(dealer)) } - return GameResult( - DealerGameResult( - winCount = playerGameResults.count { it.result == LOSS }, - lossCount = playerGameResults.count { it.result == WIN }, - drawCount = playerGameResults.count { it.result == DRAW }, - ), - playerGameResults, - ) - } - } -} diff --git a/src/main/kotlin/blackjack/domain/GameTable.kt b/src/main/kotlin/blackjack/domain/GameTable.kt deleted file mode 100644 index 9fda80d74..000000000 --- a/src/main/kotlin/blackjack/domain/GameTable.kt +++ /dev/null @@ -1,21 +0,0 @@ -package blackjack.domain - -data class GameTable( - private val deck: Deck, -) { - fun dealInitCard(participants: List<Participant>): List<Participant> { - return participants.map { participant -> - (1..INIT_CARD_DRAW_COUNT).fold(participant) { acc, _ -> - acc.hit(deck.draw()) - } - } - } - - fun hit(participant: Participant): Participant { - return participant.hit(deck.draw()) - } - - companion object { - const val INIT_CARD_DRAW_COUNT = 2 - } -} diff --git a/src/main/kotlin/blackjack/domain/Participant.kt b/src/main/kotlin/blackjack/domain/Participant.kt deleted file mode 100644 index fccfc02a7..000000000 --- a/src/main/kotlin/blackjack/domain/Participant.kt +++ /dev/null @@ -1,63 +0,0 @@ -package blackjack.domain - -import blackjack.domain.MatchResult.LOSS -import blackjack.domain.MatchResult.WIN - -sealed class Participant(val name: String, val cards: Cards) { - val isBust: Boolean - get() = cards.isBust - - abstract fun canHit(): Boolean - - abstract fun hit(card: Card): Participant - - companion object { - fun separate(participants: List<Participant>): Pair<List<Player>, Dealer> { - return participants.filterIsInstance<Player>() to participants.first { it is Dealer } as Dealer - } - } -} - -class Player(name: String, cards: Cards) : Participant(name, cards) { - override fun canHit(): Boolean { - return cards.scoreLowerThan(PLAYER_SCORE_LIMIT) - } - - override fun hit(card: Card): Player { - return Player(this.name, cards.add(card)) - } - - fun compareScore(dealer: Dealer): MatchResult { - return when { - dealer.isBust -> WIN - this.isBust -> LOSS - else -> cards.compareScore(dealer.cards) - } - } - - companion object { - private const val PLAYER_SCORE_LIMIT = 21 - - fun create(name: String): Player { - return Player(name, Cards(emptyList())) - } - } -} - -class Dealer(cards: Cards) : Participant("딜러", cards) { - override fun canHit(): Boolean { - return cards.scoreLowerThan(DEALER_SCORE_LIMIT) - } - - override fun hit(card: Card): Dealer { - return Dealer(cards.add(card)) - } - - companion object { - private const val DEALER_SCORE_LIMIT = 17 - - fun create(): Dealer { - return Dealer(Cards(emptyList())) - } - } -} diff --git a/src/main/kotlin/blackjack/domain/Card.kt b/src/main/kotlin/blackjack/domain/card/Card.kt similarity index 82% rename from src/main/kotlin/blackjack/domain/Card.kt rename to src/main/kotlin/blackjack/domain/card/Card.kt index 3b1cb0e9d..49e38e89d 100644 --- a/src/main/kotlin/blackjack/domain/Card.kt +++ b/src/main/kotlin/blackjack/domain/card/Card.kt @@ -1,6 +1,6 @@ -package blackjack.domain +package blackjack.domain.card -import blackjack.domain.Rank.ACE +import blackjack.domain.card.Rank.ACE data class Card( val rank: Rank, diff --git a/src/main/kotlin/blackjack/domain/card/Deck.kt b/src/main/kotlin/blackjack/domain/card/Deck.kt new file mode 100644 index 000000000..6f20fd3ce --- /dev/null +++ b/src/main/kotlin/blackjack/domain/card/Deck.kt @@ -0,0 +1,8 @@ +package blackjack.domain.card + +class Deck(private val cards: MutableList<Card> = Card.ALL.shuffled().toMutableList()) { + fun draw(): Card { + check(cards.isNotEmpty()) { "카드가 모두 소진되었습니다" } + return cards.removeFirst() + } +} diff --git a/src/main/kotlin/blackjack/domain/card/Hand.kt b/src/main/kotlin/blackjack/domain/card/Hand.kt new file mode 100644 index 000000000..bebed6e7d --- /dev/null +++ b/src/main/kotlin/blackjack/domain/card/Hand.kt @@ -0,0 +1,30 @@ +package blackjack.domain.card + +class Hand(private val _cards: MutableList<Card> = mutableListOf()) { + val cards: List<Card> + get() = _cards.toList() + + val score: Int + get() = calculateScore() + + fun add(card: Card) { + _cards.add(card) + } + + private fun calculateScore(): Int { + val totalScore = cards.sumOf { it.score } + var aceCount = cards.count { it.isAce } + + var adjustedScore = totalScore + while (adjustedScore > BLACKJACK_SCORE_LIMIT && aceCount > 0) { + adjustedScore -= ACE_SCORE_DIFFERENCE + aceCount-- + } + return adjustedScore + } + + companion object { + private const val BLACKJACK_SCORE_LIMIT = 21 + private const val ACE_SCORE_DIFFERENCE = 10 + } +} diff --git a/src/main/kotlin/blackjack/domain/Rank.kt b/src/main/kotlin/blackjack/domain/card/Rank.kt similarity index 76% rename from src/main/kotlin/blackjack/domain/Rank.kt rename to src/main/kotlin/blackjack/domain/card/Rank.kt index b65d2a669..7b7e1ad82 100644 --- a/src/main/kotlin/blackjack/domain/Rank.kt +++ b/src/main/kotlin/blackjack/domain/card/Rank.kt @@ -1,4 +1,4 @@ -package blackjack.domain +package blackjack.domain.card enum class Rank(val value: String, val score: Int) { ACE("A", 11), @@ -17,9 +17,8 @@ enum class Rank(val value: String, val score: Int) { ; companion object { - fun from(value: String): Rank { - return entries.firstOrNull { it.value == value } + fun from(value: String): Rank = + entries.firstOrNull { it.value == value } ?: throw IllegalArgumentException("유효하지 않은 랭크 값입니다: $value") - } } } diff --git a/src/main/kotlin/blackjack/domain/Suit.kt b/src/main/kotlin/blackjack/domain/card/Suit.kt similarity index 82% rename from src/main/kotlin/blackjack/domain/Suit.kt rename to src/main/kotlin/blackjack/domain/card/Suit.kt index bc2d994b1..fd563f625 100644 --- a/src/main/kotlin/blackjack/domain/Suit.kt +++ b/src/main/kotlin/blackjack/domain/card/Suit.kt @@ -1,4 +1,4 @@ -package blackjack.domain +package blackjack.domain.card enum class Suit(val description: String) { SPADE("스페이드"), diff --git a/src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt b/src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt deleted file mode 100644 index 4651bc2ce..000000000 --- a/src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt +++ /dev/null @@ -1,9 +0,0 @@ -package blackjack.domain.dto - -import blackjack.domain.MatchResult -import blackjack.domain.Player - -data class PlayerGameResult( - val player: Player, - val result: MatchResult, -) diff --git a/src/main/kotlin/blackjack/domain/game/GameResult.kt b/src/main/kotlin/blackjack/domain/game/GameResult.kt new file mode 100644 index 000000000..cf7856903 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/game/GameResult.kt @@ -0,0 +1,31 @@ +package blackjack.domain.game + +import blackjack.domain.game.MatchResult.DRAW +import blackjack.domain.game.MatchResult.LOSE +import blackjack.domain.game.MatchResult.WIN +import blackjack.domain.game.dto.DealerGameResult +import blackjack.domain.game.dto.PlayerGameResult +import blackjack.domain.player.Dealer +import blackjack.domain.player.Player + +data class GameResult( + val dealerGameResult: DealerGameResult, + val playerGameResults: List<PlayerGameResult>, +) { + companion object { + fun from( + dealer: Dealer, + players: List<Player>, + ): GameResult { + val playerGameResults = players.map { player -> PlayerGameResult(player, player.matchHand(dealer)) } + return GameResult( + DealerGameResult( + winCount = playerGameResults.count { it.result == LOSE }, + loseCount = playerGameResults.count { it.result == WIN }, + drawCount = playerGameResults.count { it.result == DRAW }, + ), + playerGameResults, + ) + } + } +} diff --git a/src/main/kotlin/blackjack/domain/MatchResult.kt b/src/main/kotlin/blackjack/domain/game/MatchResult.kt similarity index 64% rename from src/main/kotlin/blackjack/domain/MatchResult.kt rename to src/main/kotlin/blackjack/domain/game/MatchResult.kt index 610434564..fd60f033b 100644 --- a/src/main/kotlin/blackjack/domain/MatchResult.kt +++ b/src/main/kotlin/blackjack/domain/game/MatchResult.kt @@ -1,7 +1,7 @@ -package blackjack.domain +package blackjack.domain.game enum class MatchResult(val description: String) { WIN("승"), - LOSS("패"), + LOSE("패"), DRAW("무"), } diff --git a/src/main/kotlin/blackjack/domain/dto/DealerGameResult.kt b/src/main/kotlin/blackjack/domain/game/dto/DealerGameResult.kt similarity index 57% rename from src/main/kotlin/blackjack/domain/dto/DealerGameResult.kt rename to src/main/kotlin/blackjack/domain/game/dto/DealerGameResult.kt index 7a3b7b1b2..aa160e028 100644 --- a/src/main/kotlin/blackjack/domain/dto/DealerGameResult.kt +++ b/src/main/kotlin/blackjack/domain/game/dto/DealerGameResult.kt @@ -1,7 +1,7 @@ -package blackjack.domain.dto +package blackjack.domain.game.dto data class DealerGameResult( val winCount: Int, - val lossCount: Int, + val loseCount: Int, val drawCount: Int, ) diff --git a/src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt b/src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt new file mode 100644 index 000000000..40e60dfc9 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt @@ -0,0 +1,9 @@ +package blackjack.domain.game.dto + +import blackjack.domain.game.MatchResult +import blackjack.domain.player.Player + +data class PlayerGameResult( + val player: Player, + val result: MatchResult, +) diff --git a/src/main/kotlin/blackjack/domain/player/Dealer.kt b/src/main/kotlin/blackjack/domain/player/Dealer.kt new file mode 100644 index 000000000..c8d958b08 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/player/Dealer.kt @@ -0,0 +1,16 @@ +package blackjack.domain.player + +import blackjack.domain.card.Card + +class Dealer : Player( + DEALER_NAME, +) { + override fun canHit(): Boolean = hand.score < DEALER_SCORE_LIMIT + + override fun hit(card: Card) = hand.add(card) + + companion object { + private const val DEALER_NAME = "딜러" + private const val DEALER_SCORE_LIMIT = 17 + } +} diff --git a/src/main/kotlin/blackjack/domain/player/Player.kt b/src/main/kotlin/blackjack/domain/player/Player.kt new file mode 100644 index 000000000..ff465d03c --- /dev/null +++ b/src/main/kotlin/blackjack/domain/player/Player.kt @@ -0,0 +1,42 @@ +package blackjack.domain.player + +import blackjack.domain.card.Card +import blackjack.domain.card.Hand +import blackjack.domain.game.MatchResult +import blackjack.domain.game.MatchResult.DRAW +import blackjack.domain.game.MatchResult.LOSE +import blackjack.domain.game.MatchResult.WIN + +open class Player( + val name: String, + val hand: Hand = Hand(), +) { + open fun canHit(): Boolean = hand.score < PLAYER_SCORE_LIMIT + + open fun hit(card: Card) = hand.add(card) + + fun matchHand(other: Player): MatchResult = + when { + other.isBust() -> WIN + this.isBust() -> LOSE + other.isBlackjack() && this.isBlackjack() -> DRAW + other.isBlackjack() -> LOSE + this.isBlackjack() -> WIN + else -> compareScore(other) + } + + private fun compareScore(other: Player): MatchResult = + when { + this.hand.score > other.hand.score -> WIN + this.hand.score < other.hand.score -> LOSE + else -> DRAW + } + + private fun isBust(): Boolean = hand.score > PLAYER_SCORE_LIMIT + + private fun isBlackjack(): Boolean = hand.score == PLAYER_SCORE_LIMIT + + companion object { + private const val PLAYER_SCORE_LIMIT = 21 + } +} diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt index ec7feab94..79b34c263 100644 --- a/src/main/kotlin/blackjack/view/InputView.kt +++ b/src/main/kotlin/blackjack/view/InputView.kt @@ -1,7 +1,6 @@ package blackjack.view -import blackjack.domain.Dealer -import blackjack.domain.Participant +import blackjack.domain.player.Player object InputView { fun inputNames(): List<String> { @@ -13,12 +12,8 @@ object InputView { return names } - fun inputHit(participant: Participant): Boolean { - if (participant is Dealer) { - return true - } - - println("${participant.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") + fun inputHit(player: Player): Boolean { + println("${player.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") return when (readlnOrNull()) { "y" -> true "n" -> false diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt index 463c306f0..80ec6a4d4 100644 --- a/src/main/kotlin/blackjack/view/ResultView.kt +++ b/src/main/kotlin/blackjack/view/ResultView.kt @@ -1,52 +1,57 @@ package blackjack.view -import blackjack.domain.GameResult -import blackjack.domain.GameTable.Companion.INIT_CARD_DRAW_COUNT -import blackjack.domain.Participant +import blackjack.controller.GameTable +import blackjack.controller.GameTable.Companion.INIT_CARD_DRAW_COUNT +import blackjack.domain.player.Player object ResultView { - fun printInitCardReceive(participants: List<Participant>) { - val players = Participant.separate(participants).first - println("딜러와 ${players.joinToString(", ") { it.name }}에게 ${INIT_CARD_DRAW_COUNT}장의 카드를 나누었습니다.") + fun printDealerHit() = println("딜러는 16이하라 한장의 카드를 더 받았습니다.") + + fun printDealInitCard(gameTable: GameTable) { + println("딜러와 ${gameTable.players.joinToString(", ") { it.name }}에게 ${INIT_CARD_DRAW_COUNT}장의 카드를 나누었습니다.") + printPlayerCard(gameTable.dealer) + printPlayersCard(gameTable.players) + linebreak() } - fun printParticipantsCard( - participants: List<Participant>, - printScore: Boolean, - ) { - val (players, dealer) = Participant.separate(participants) - printParticipantCard(participant = dealer, printScore = printScore) - players.forEach { printParticipantCard(participant = it, printScore = printScore) } + fun printAfterTurn(gameTable: GameTable) { + printFinalHand(gameTable) + printGameResult(gameTable) } - fun printParticipantCard( - participant: Participant, - printScore: Boolean, + fun printPlayerCard( + player: Player, + printScore: Boolean = false, ) { - val cards = participant.cards.values.joinToString(", ") { "${it.rank.value}${it.suit.description}" } - val scoreText = "- 결과: ${participant.cards.score}" - println("${participant.name} 카드: $cards ${if (printScore) scoreText else ""}") + val cards = player.hand.cards.joinToString(", ") { "${it.rank.value}${it.suit.description}" } + val scoreText = "- 결과: ${player.hand.score}" + println("${player.name} 카드: $cards ${if (printScore) scoreText else ""}") } - fun printDealerHit() { - println("딜러는 16이하라 한장의 카드를 더 받았습니다.") + fun linebreak() = println() + + private fun printFinalHand(gameTable: GameTable) { + linebreak() + printPlayerCard(gameTable.dealer, printScore = true) + printPlayersCard(gameTable.players, printScore = true) } - fun printGameResult(gameResult: GameResult) { + private fun printGameResult(gameTable: GameTable) { + linebreak() + val gameResult = gameTable.getGameResult() val winMessage = if (gameResult.dealerGameResult.winCount > 0) "${gameResult.dealerGameResult.winCount}승" else "" val lossMessage = - if (gameResult.dealerGameResult.lossCount > 0) "${gameResult.dealerGameResult.lossCount}패" else "" + if (gameResult.dealerGameResult.loseCount > 0) "${gameResult.dealerGameResult.loseCount}패" else "" val drawMessage = if (gameResult.dealerGameResult.drawCount > 0) "${gameResult.dealerGameResult.drawCount}무" else "" println("## 최종 승패") println("딜러: $winMessage $lossMessage $drawMessage") - gameResult.playerGameResults.forEach { - println("${it.player.name} ${it.result.description}") - } + gameResult.playerGameResults.forEach { println("${it.player.name}: ${it.result.description}") } } - fun linebreak() { - println() - } + private fun printPlayersCard( + players: List<Player>, + printScore: Boolean = false, + ) = players.forEach { printPlayerCard(it, printScore) } } diff --git a/src/test/kotlin/blackjack/domain/CardTest.kt b/src/test/kotlin/blackjack/domain/CardTest.kt deleted file mode 100644 index 727410f08..000000000 --- a/src/test/kotlin/blackjack/domain/CardTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package blackjack.domain - -import blackjack.domain.Rank.ACE -import blackjack.domain.Rank.THREE -import blackjack.domain.Suit.SPADE -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe - -class CardTest : StringSpec({ - "카드는 에이스로 만들어진다면 에이스 카드이다" { - val card = Card(ACE, SPADE) - - card.isAce shouldBe true - } - - "카드는 스페이드로 만들어진다면 에이스 카드가 아니다" { - val card = Card(THREE, SPADE) - - card.isAce shouldBe false - } -}) diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt deleted file mode 100644 index c915c586d..000000000 --- a/src/test/kotlin/blackjack/domain/DealerTest.kt +++ /dev/null @@ -1,19 +0,0 @@ -package blackjack.domain - -import blackjack.fixtures.createCard -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe - -class DealerTest : StringSpec({ - "딜러는 카드를 가진다" { - val dealer = Dealer.create() - dealer.cards shouldNotBe null - } - - "딜러는 첫 2장의 합이 16이하면 반드시 카드를 받아야 한다" { - val dealer = Dealer(Cards(listOf(createCard("10"), createCard("6")))) - - dealer.canHit() shouldBe true - } -}) diff --git a/src/test/kotlin/blackjack/domain/DeckTest.kt b/src/test/kotlin/blackjack/domain/DeckTest.kt deleted file mode 100644 index bbcd5afbc..000000000 --- a/src/test/kotlin/blackjack/domain/DeckTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package blackjack.domain - -import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldNotBe - -class DeckTest : StringSpec({ - "덱에서 카드를 하나 꺼낸다" { - val deck = Deck.create() - val card = deck.draw() - - card shouldNotBe null - } - - "덱에 카드가 없을 때 카드를 꺼내면 예외 발생한다" { - val deck = Deck.create() - repeat(52) { deck.draw() } - - shouldThrow<IllegalStateException> { deck.draw() } - } -}) diff --git a/src/test/kotlin/blackjack/domain/GameTableTest.kt b/src/test/kotlin/blackjack/domain/GameTableTest.kt deleted file mode 100644 index 8b0517a3b..000000000 --- a/src/test/kotlin/blackjack/domain/GameTableTest.kt +++ /dev/null @@ -1,15 +0,0 @@ -package blackjack.domain - -import blackjack.fixtures.createPlayers -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe - -class GameTableTest : StringSpec({ - "최초 딜 시 카드를 2장 나누어준다" { - val players = GameTable(Deck.create()).dealInitCard(createPlayers()) - - players.forEach { - it.cards.values.size shouldBe 2 - } - } -}) diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt deleted file mode 100644 index aa5db26b9..000000000 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ /dev/null @@ -1,132 +0,0 @@ -package blackjack.domain - -import blackjack.domain.MatchResult.DRAW -import blackjack.domain.MatchResult.LOSS -import blackjack.domain.MatchResult.WIN -import blackjack.domain.Suit.CLUB -import blackjack.domain.Suit.DIAMOND -import blackjack.domain.Suit.HEART -import blackjack.domain.Suit.SPADE -import blackjack.fixtures.createCard -import io.kotest.core.spec.style.StringSpec -import io.kotest.data.forAll -import io.kotest.data.headers -import io.kotest.data.row -import io.kotest.data.table -import io.kotest.inspectors.forAll -import io.kotest.matchers.shouldBe - -class PlayerTest : StringSpec({ - "플레이어는 카드목록의 점수합이 21점 미만일 경우 카드를 더 받을 수 있다" { - table( - headers("ranks"), - row(listOf("2", "3", "4")), - row(listOf("4", "5", "10")), - row(listOf("K", "Q")), - row(listOf("5", "5", "4", "3", "2")), - ).forAll { ranks -> - val cards = Cards(ranks.map { createCard(it) }) - - Player("홍길동", cards).canHit() shouldBe true - } - } - - "플레이어는 카드목록의 점수합이 21점 이상할 경우 카드를 더 받을 수 없다" { - table( - headers("ranks", "score"), - row(listOf("J", "Q", "K"), 30), - row(listOf("A", "K"), 21), - row(listOf("Q", "10", "A"), 21), - row(listOf("10", "9", "2"), 21), - row(listOf("10", "10", "3"), 23), - ).forAll { ranks, score -> - val cards = Cards(ranks.map { createCard(it) }) - - cards.score shouldBe score - Player("홍길동", cards).canHit() shouldBe false - } - } - - "플레이어는 카드 2장을 받은 후 점수 합이 21점인 경우 카드를 더 받지 못한다" { - val cards = Cards(emptyList()) - val player = - Player("홍길동", cards) - .hit(createCard("A")) - .hit(createCard("K")) - - player.canHit() shouldBe false - } - - "플레이어는 카드 2장을 받은 후 점수 합이 21점 미만인 경우 카드를 더 받을 수 있다" { - val cards = Cards(emptyList()) - val player = - Player("홍길동", cards) - .hit(createCard("10")) - .hit(createCard("5")) - - player.canHit() shouldBe true - } - - "플레이어가 딜러보다 점수가 높다면 승리한다" { - val cards = Cards(emptyList()) - val player = - Player("홍길동", cards) - .hit(createCard("10")) - .hit(createCard("5")) - val dealer = Dealer.create().hit(createCard("10")) - - player.compareScore(dealer) shouldBe WIN - } - - "플레이어가 딜러보다 점수가 낮다면 패배한다" { - val cards = Cards(emptyList()) - val player = - Player("홍길동", cards) - .hit(createCard("5")) - val dealer = - Dealer.create() - .hit(createCard("10")) - - player.compareScore(dealer) shouldBe LOSS - } - - "플레이어가 딜러보와 점수가 같다면 무승부다" { - val cards = Cards(emptyList()) - val player = - Player("홍길동", cards) - .hit(createCard("5")) - val dealer = - Dealer.create() - .hit(createCard("5")) - - player.compareScore(dealer) shouldBe DRAW - } - - "플레이어의 패에 상관없이 딜러의 점수가 21점이 넘는다면 승리한다" { - val cards = Cards(emptyList()) - val biggerScorePlayer = - Player("김큰점수", cards) - .hit(createCard("K", SPADE)) - .hit(createCard("Q", SPADE)) - .hit(createCard("J", SPADE)) - val smallerScorePlayer = - Player("박작은점수", cards) - .hit(createCard("K", HEART)) - .hit(createCard("Q", HEART)) - .hit(createCard("J", HEART)) - val sameScorePlayer = - Player("최같은점수", cards) - .hit(createCard("10", CLUB)) - .hit(createCard("10", CLUB)) - .hit(createCard("3", CLUB)) - val dealer = - Dealer.create() - .hit(createCard("10", DIAMOND)) - .hit(createCard("10", DIAMOND)) - .hit(createCard("3", DIAMOND)) - - listOf(biggerScorePlayer, smallerScorePlayer, sameScorePlayer).forAll { player -> - player.compareScore(dealer) shouldBe WIN - } - } -}) diff --git a/src/test/kotlin/blackjack/domain/card/DeckTest.kt b/src/test/kotlin/blackjack/domain/card/DeckTest.kt new file mode 100644 index 000000000..b82dcc0d4 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/card/DeckTest.kt @@ -0,0 +1,30 @@ +package blackjack.domain.card + +import blackjack.domain.card.Rank.ACE +import blackjack.fixtures.createCard +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe + +class DeckTest : BehaviorSpec({ + Given("덱에 카드가 존재하는 경우") { + val deck = Deck(mutableListOf(createCard("A"))) + + When("드로우하면") { + Then("카드를 반환한다") { + deck.draw().rank shouldBe ACE + } + } + } + Given("덱에 카드가 없는 경우") { + val deck = Deck(mutableListOf()) + + When("드로우하면") { + Then("예외 발생한다") { + shouldThrow<IllegalStateException> { + deck.draw() + } + } + } + } +}) diff --git a/src/test/kotlin/blackjack/domain/CardsTest.kt b/src/test/kotlin/blackjack/domain/card/HandTest.kt similarity index 54% rename from src/test/kotlin/blackjack/domain/CardsTest.kt rename to src/test/kotlin/blackjack/domain/card/HandTest.kt index 280d5844d..e53a937f6 100644 --- a/src/test/kotlin/blackjack/domain/CardsTest.kt +++ b/src/test/kotlin/blackjack/domain/card/HandTest.kt @@ -1,15 +1,15 @@ -package blackjack.domain +package blackjack.domain.card import blackjack.fixtures.createCard -import io.kotest.core.spec.style.StringSpec +import io.kotest.core.spec.style.BehaviorSpec import io.kotest.data.forAll import io.kotest.data.headers import io.kotest.data.row import io.kotest.data.table import io.kotest.matchers.shouldBe -class CardsTest : StringSpec({ - "카드목록의 점수합이 21을 초과할 경우 에이스는 1점으로 보정된다" { +class HandTest : BehaviorSpec({ + Given("카드목록의 점수 합이 21점을 초과하는 경우") { table( headers("ranks", "expected"), row(listOf("A", "2", "10"), 13), @@ -18,9 +18,13 @@ class CardsTest : StringSpec({ row(listOf("A", "A", "A"), 13), row(listOf("A", "3", "9"), 13), ).forAll { ranks, expected -> - val cards = Cards(ranks.map { createCard(it) }) + val hand = Hand(ranks.map { createCard(it) }.toMutableList()) - cards.score shouldBe expected + When("점수 계산하면") { + Then("에이스는 1점으로 보정된다") { + hand.score shouldBe expected + } + } } } }) diff --git a/src/test/kotlin/blackjack/domain/RankTest.kt b/src/test/kotlin/blackjack/domain/card/RankTest.kt similarity index 96% rename from src/test/kotlin/blackjack/domain/RankTest.kt rename to src/test/kotlin/blackjack/domain/card/RankTest.kt index dbe94e535..0d099ad8f 100644 --- a/src/test/kotlin/blackjack/domain/RankTest.kt +++ b/src/test/kotlin/blackjack/domain/card/RankTest.kt @@ -1,4 +1,4 @@ -package blackjack.domain +package blackjack.domain.card import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec diff --git a/src/test/kotlin/blackjack/domain/game/GameResultTest.kt b/src/test/kotlin/blackjack/domain/game/GameResultTest.kt new file mode 100644 index 000000000..0864293be --- /dev/null +++ b/src/test/kotlin/blackjack/domain/game/GameResultTest.kt @@ -0,0 +1,66 @@ +package blackjack.domain.game + +import blackjack.domain.card.Suit.DIAMOND +import blackjack.domain.card.Suit.HEART +import blackjack.domain.card.Suit.SPADE +import blackjack.domain.player.Dealer +import blackjack.domain.player.Player +import blackjack.fixtures.createCard +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe + +class GameResultTest : BehaviorSpec({ + Given("플레이어가 이긴 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("3", SPADE)) + player1.hit(createCard("4", HEART)) + player2.hit(createCard("5", DIAMOND)) + + When("게임결과를 생성하면") { + val actual = GameResult.from(dealer, listOf(player1, player2)) + + Then("딜러의 패배 카운트가 증가한다") { + actual.dealerGameResult.loseCount shouldBe 2 + } + } + } + + Given("플레이어가 진 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("10", SPADE)) + player1.hit(createCard("4", HEART)) + player2.hit(createCard("5", DIAMOND)) + + When("게임결과를 생성하면") { + val actual = GameResult.from(dealer, listOf(player1, player2)) + + Then("딜러의 승리 카운트가 증가한다") { + actual.dealerGameResult.winCount shouldBe 2 + } + } + } + + Given("플레이어가 딜러와 무승부인 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("10", SPADE)) + player1.hit(createCard("10", HEART)) + player2.hit(createCard("10", DIAMOND)) + + When("게임결과를 생성하면") { + val actual = GameResult.from(dealer, listOf(player1, player2)) + + Then("딜러의 무승부 카운트가 증가한다") { + actual.dealerGameResult.drawCount shouldBe 2 + } + } + } +}) diff --git a/src/test/kotlin/blackjack/domain/player/DealerTest.kt b/src/test/kotlin/blackjack/domain/player/DealerTest.kt new file mode 100644 index 000000000..7f4b9cb6e --- /dev/null +++ b/src/test/kotlin/blackjack/domain/player/DealerTest.kt @@ -0,0 +1,42 @@ +package blackjack.domain.player + +import blackjack.fixtures.createCard +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe + +class DealerTest : BehaviorSpec({ + Given("딜러 히트하는 경우") { + val dealer = Dealer() + + When("히트하면") { + dealer.hit(createCard("J")) + + Then("카드를 추가로 갖는다") { + dealer.hand.cards.count() shouldBe 1 + } + } + } + + Given("딜러 핸드가 17점 이상인 경우") { + val dealer = Dealer() + dealer.hit(createCard("J")) + dealer.hit(createCard("Q")) + + When("히트 가능 여부 질의하면") { + Then("결과 false 이다") { + dealer.canHit() shouldBe false + } + } + } + + Given("딜러 핸드가 17점 미만인 경우") { + val dealer = Dealer() + dealer.hit(createCard("J")) + + When("히트 가능 여부 질의하면") { + Then("결과는 true 이다") { + dealer.canHit() shouldBe true + } + } + } +}) diff --git a/src/test/kotlin/blackjack/domain/player/PlayerTest.kt b/src/test/kotlin/blackjack/domain/player/PlayerTest.kt new file mode 100644 index 000000000..6ddb5c533 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/player/PlayerTest.kt @@ -0,0 +1,140 @@ +package blackjack.domain.player + +import blackjack.domain.card.Hand +import blackjack.domain.card.Suit.DIAMOND +import blackjack.domain.card.Suit.HEART +import blackjack.domain.card.Suit.SPADE +import blackjack.domain.game.MatchResult.DRAW +import blackjack.domain.game.MatchResult.LOSE +import blackjack.domain.game.MatchResult.WIN +import blackjack.fixtures.createCard +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.data.forAll +import io.kotest.data.headers +import io.kotest.data.row +import io.kotest.data.table +import io.kotest.inspectors.forAll +import io.kotest.matchers.shouldBe + +class PlayerTest : BehaviorSpec({ + Given("플레이어 핸드가 21점 미만인 경우") { + table( + headers("ranks"), + row(listOf("2", "3", "4")), + row(listOf("4", "5", "10")), + row(listOf("K", "Q")), + row(listOf("5", "5", "4", "3", "2")), + ).forAll { ranks -> + val player = Player("홍길동", Hand(ranks.map { createCard(it) }.toMutableList())) + + When("히트 가능 여부 질의하면") { + Then("결과는 true 이다") { + player.canHit() shouldBe true + } + } + } + } + + Given("플레이어 핸드가 21점 이상인 경우") { + table( + headers("ranks"), + row(listOf("J", "Q", "K")), + row(listOf("A", "K")), + row(listOf("Q", "10", "A")), + row(listOf("10", "9", "2")), + row(listOf("10", "10", "3")), + ).forAll { ranks -> + val player = Player("홍길동", Hand(ranks.map { createCard(it) }.toMutableList())) + + When("히트 가능 여부 질의하면") { + Then("결과는 false 이다") { + player.canHit() shouldBe false + } + } + } + } + + Given("플레이어가 딜러보다 점수가 높은 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("3")) + + player1.hit(createCard("4")) + player2.hit(createCard("5")) + + When("게임 결과 비교하면") { + Then("플레이어가 승리한다") { + listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe WIN } + } + } + } + + Given("플레이어가 딜러보다 점수가 낮은 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("10")) + + player1.hit(createCard("4")) + player2.hit(createCard("5")) + + When("게임 결과 비교하면") { + Then("플레이어가 승리한다") { + listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe LOSE } + } + } + } + + Given("플레이어가 딜러보다 점수가 동일한 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("10", SPADE)) + + player1.hit(createCard("10", HEART)) + player2.hit(createCard("10", DIAMOND)) + + When("게임 결과 비교하면") { + Then("플레이어가 승리한다") { + listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe DRAW } + } + } + } + + Given("딜러가 버스트인 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("K", SPADE)) + dealer.hit(createCard("Q", SPADE)) + dealer.hit(createCard("J", SPADE)) + + player1.hit(createCard("7", HEART)) + player2.hit(createCard("8", DIAMOND)) + + When("게임 결과 비교하면") { + Then("모든 플레이어가 승리한다") { + listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe WIN } + } + } + + When("게임 결과 비교하면 플레이어 핸드가 21점을 초과해도") { + player1.hit(createCard("K", HEART)) + player1.hit(createCard("Q", HEART)) + player1.hit(createCard("J", HEART)) + + player2.hit(createCard("K", DIAMOND)) + player2.hit(createCard("Q", DIAMOND)) + player2.hit(createCard("J", DIAMOND)) + + Then("모든 플레이어가 승리한다") { + listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe WIN } + } + } + } +}) diff --git a/src/test/kotlin/blackjack/fixtures/CardFixture.kt b/src/test/kotlin/blackjack/fixtures/CardFixture.kt index d4c2c25cf..caf9c481b 100644 --- a/src/test/kotlin/blackjack/fixtures/CardFixture.kt +++ b/src/test/kotlin/blackjack/fixtures/CardFixture.kt @@ -1,9 +1,9 @@ package blackjack.fixtures -import blackjack.domain.Card -import blackjack.domain.Rank -import blackjack.domain.Suit -import blackjack.domain.Suit.SPADE +import blackjack.domain.card.Card +import blackjack.domain.card.Rank +import blackjack.domain.card.Suit +import blackjack.domain.card.Suit.SPADE fun createCard( rank: String, diff --git a/src/test/kotlin/blackjack/fixtures/UserFixtures.kt b/src/test/kotlin/blackjack/fixtures/UserFixtures.kt deleted file mode 100644 index 12edefc85..000000000 --- a/src/test/kotlin/blackjack/fixtures/UserFixtures.kt +++ /dev/null @@ -1,8 +0,0 @@ -package blackjack.fixtures - -import blackjack.domain.Cards -import blackjack.domain.Player - -fun createPlayers(names: List<String> = listOf("홍길동", "홍길덩")): List<Player> { - return names.map { Player(it, Cards(emptyList())) } -}