diff --git a/README.md b/README.md index a07979f34..1eabc1ef6 100644 --- a/README.md +++ b/README.md @@ -19,4 +19,21 @@ - 기능 요구사항 바탕으로 테스트코드 작성 - 테스트코드 컴파일 되도록 코드 수정 - 게임의 주체를 나타내는 Player 객체 정의 -- 블랙잭 카드를 표현할 수 있는 객체 정의 \ No newline at end of file +- 블랙잭 카드를 표현할 수 있는 객체 정의 + + +- 3단계 목록이 날라갔네 .. + +### 4단계 구현 기능 목록 + +- [x] 플레이어는 게임을 시작할 때 베팅 금액을 정해야 한다. +- 베팅 관련 규칙 추가 + - [x] 카드를 추가로 뽑아 21을 초과할 경우 베팅 금액을 모두 잃게 된다. + - [x] 처음 두 장의 카드 합이 21일 경우 블랙잭이 되면 베팅 금액의 1.5 배를 딜러에게 받는다. + - [x] 딜러와 플레이어가 모두 동시에 블랙잭인 경우 플레이어는 베팅한 금액을 돌려받는다. + - [x] 딜러가 21을 초과하면 그 시점까지 남아 있던 플레이어들은 가지고 있는 패에 상관 없이 승리해 베팅 금액을 받는다. + +### 4단계 프로그래밍 요구사항 +- 모든 엔티티를 작게 유지한다. +- 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다. +- 딜러와 플레이어에서 발생하는 중복 코드를 제거해야 한다. diff --git a/src/main/kotlin/blackjack/controller/BlackJackController.kt b/src/main/kotlin/blackjack/controller/BlackJackController.kt index ddd49102c..e45641baf 100644 --- a/src/main/kotlin/blackjack/controller/BlackJackController.kt +++ b/src/main/kotlin/blackjack/controller/BlackJackController.kt @@ -22,6 +22,7 @@ object BlackJackController { val players = dealer.initPlayers( fetchPlayerNames = { InputView.readPlayerNames() }, + getBettingAmount = { name -> InputView.readBettingAmount(name) }, onPlayerInit = { names -> ResultView.printPlayerInitMessage(names) ResultView.printDealerWithCard(dealer.getCardForInitialDisplay()) @@ -59,8 +60,7 @@ object BlackJackController { players.onEach { player -> ResultView.printFinalScoresForPlayer(player) } - - val result = BlackJackResultManager(dealer, players).getResult() - ResultView.printFinalWinLose(result) + val result = BlackJackResultManager.getResult(dealer, players) + ResultView.printFinalProfit(result) } } diff --git a/src/main/kotlin/blackjack/domain/BetMoney.kt b/src/main/kotlin/blackjack/domain/BetMoney.kt new file mode 100644 index 000000000..876edb863 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/BetMoney.kt @@ -0,0 +1,34 @@ +package blackjack.domain + +import blackjack.domain.GameResult.* +import java.math.BigDecimal +import java.math.RoundingMode + +@JvmInline +value class BetMoney(private val amount: BigDecimal) { + fun getAmount(gameResult: GameResult): BigDecimal { + return when (gameResult) { + BLACK_JACK -> getAmountOnBlackJack() + WIN -> getOriginalBetAmount() + PUSH -> getOriginalBetAmount() + LOSE -> getAmountOnLose() + BUST -> getAmountOnBust() + } + } + + private fun getOriginalBetAmount(): BigDecimal { + return amount + } + + private fun getAmountOnBlackJack(): BigDecimal { + return amount.multiply((1.5).toBigDecimal()).setScale(0, RoundingMode.DOWN) + } + + private fun getAmountOnBust(): BigDecimal { + return -(amount) + } + + private fun getAmountOnLose(): BigDecimal { + return -(amount) + } +} diff --git a/src/main/kotlin/blackjack/domain/BlackJackResultManager.kt b/src/main/kotlin/blackjack/domain/BlackJackResultManager.kt index d9485b7e3..005747360 100644 --- a/src/main/kotlin/blackjack/domain/BlackJackResultManager.kt +++ b/src/main/kotlin/blackjack/domain/BlackJackResultManager.kt @@ -1,33 +1,27 @@ package blackjack.domain -class BlackJackResultManager( - private val dealer: Dealer, - private val players: Players, - private val playerResultCalculator: PlayerResultCalculator = PlayerResultCalculator(), -) { - fun getResult(): BlackJackResult { - val dealerScore = dealer.cardsSum - val playersWinLose = - players.value.associateWith { player -> - playerResultCalculator.calculate(dealerScore, player.cardsSum) - } +import java.math.BigDecimal - val dealerWinCount = playersWinLose.count { it.value == PlayerResult.LOSE } - val dealerLoseCount = playersWinLose.count { it.value == PlayerResult.WIN } - return BlackJackResult(dealerWinCount, dealerLoseCount, PlayerToResultMap(playersWinLose)) +object BlackJackResultManager { + fun getResult( + dealer: Dealer, + players: Players, + ): BlackJackResult { + val playersProfits = + players.getPlayersToProfitMoney( + dealer.isBlackJackInitially, + dealer.cardsSum, + ) + return BlackJackResult(playersProfits) } } data class BlackJackResult( - val dealerWinCount: Int, - val dealerLoseCount: Int, - val playerToResultMap: PlayerToResultMap, -) - -@JvmInline -value class PlayerToResultMap(val value: Map) + val playerToProfit: PlayerToProfitMoney, +) { + val dealerProfitMoney: ProfitMoney get() = ProfitMoney().apply { set(-playerToProfit.getAllProfitSum) } +} -enum class PlayerResult { - WIN, - LOSE, +data class PlayerToProfitMoney(val value: Map) { + val getAllProfitSum: BigDecimal get() = value.values.sumOf { it.getCurrentProfit() } } diff --git a/src/main/kotlin/blackjack/domain/Card.kt b/src/main/kotlin/blackjack/domain/Card.kt index 2fcdd9c83..fbcb6880c 100644 --- a/src/main/kotlin/blackjack/domain/Card.kt +++ b/src/main/kotlin/blackjack/domain/Card.kt @@ -3,14 +3,6 @@ package blackjack.domain data class Card(val rank: Rank, val suit: Suit) { private fun isAce() = rank == Rank.ACE - fun isOverMaxSum(currentCardSum: Int): Boolean { - return if (isAce()) { - currentCardSum + ACE_VALUE_ONE > MAX_SUM && currentCardSum + this.rank.value > MAX_SUM - } else { - currentCardSum + this.rank.value > MAX_SUM - } - } - companion object { const val ACE_VALUE_ONE = 1 const val MAX_SUM = 21 diff --git a/src/main/kotlin/blackjack/domain/Dealer.kt b/src/main/kotlin/blackjack/domain/Dealer.kt index f46cbcb20..2357cb68d 100644 --- a/src/main/kotlin/blackjack/domain/Dealer.kt +++ b/src/main/kotlin/blackjack/domain/Dealer.kt @@ -1,24 +1,37 @@ package blackjack.domain +import java.math.BigDecimal + class Dealer( private val drawCard: () -> Card, ) : Participant(drawCard = drawCard) { fun initPlayers( fetchPlayerNames: () -> List, + getBettingAmount: (String) -> BigDecimal, onPlayerInit: (List) -> Unit, ): Players { val names = fetchPlayerNames() - val players = names.map { name -> Player(name = name, drawCard = drawCard) } + val nameAndBets = names.associateWith(getBettingAmount) + val players = + Players( + nameAndBets.map { (name, bet) -> + Player( + name = name, + betMoney = BetMoney(bet), + drawCard = drawCard, + ) + }, + ) onPlayerInit(names) - return Players(value = players) + return players } fun drawOneMoreCardIfNeeded(onDrawCard: () -> Unit) { - addCardIfAvailable(requireCard = { drawCard() }, onDrawCard = onDrawCard) + addCardIfAvailable(requireCard = drawCard, onDrawCard = onDrawCard) } override fun isAddCardEnabled(): Boolean { - return cardsSum <= 16 + return cardsSum <= DEALER_DRAW_ONE_MORE_CARD_THRESHOLD } fun getCardForInitialDisplay(): Card { @@ -28,5 +41,6 @@ class Dealer( companion object { private const val DEALER_CARD_COUNT = 2 + private const val DEALER_DRAW_ONE_MORE_CARD_THRESHOLD = 16 } } diff --git a/src/main/kotlin/blackjack/domain/GameResult.kt b/src/main/kotlin/blackjack/domain/GameResult.kt new file mode 100644 index 000000000..de5eabb79 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/GameResult.kt @@ -0,0 +1,38 @@ +package blackjack.domain + +enum class GameResult { + WIN, + BUST, + LOSE, + PUSH, + BLACK_JACK, + ; + + companion object { + fun getGameResultsWith( + isPlayerBlackJackInitially: Boolean, + isDealerBlackJackInitially: Boolean, + dealerCardSum: Int, + playerCardSum: Int, + ): GameResult { + return when { + isPlayerBlackJackInitially && isDealerBlackJackInitially.not() -> BLACK_JACK + isPlayerBlackJackInitially && isDealerBlackJackInitially -> PUSH + else -> fromScores(dealerCardSum, playerCardSum) + } + } + + private fun fromScores( + dealerScore: Int, + playerScore: Int, + ): GameResult { + return when { + dealerScore > Card.MAX_SUM -> WIN // Dealer bust + playerScore > Card.MAX_SUM -> BUST // Player bust + dealerScore > playerScore -> LOSE + playerScore > dealerScore -> WIN + else -> PUSH + } + } + } +} diff --git a/src/main/kotlin/blackjack/domain/Participant.kt b/src/main/kotlin/blackjack/domain/Participant.kt index 28ccc8c98..9ff1f98d1 100644 --- a/src/main/kotlin/blackjack/domain/Participant.kt +++ b/src/main/kotlin/blackjack/domain/Participant.kt @@ -2,14 +2,15 @@ package blackjack.domain abstract class Participant( private val drawCard: () -> Card, - initialCardCount: Int = 2, ) { private val _cards = mutableListOf() val cards: Cards = Cards(_cards) val cardsSum: Int get() = cards.sumValues() + val isBlackJackInitially: Boolean init { - repeat(initialCardCount) { addCard(drawCard()) } + repeat(INITIAL_CARD_COUNT) { addCard(drawCard()) } + isBlackJackInitially = cardsSum == Card.MAX_SUM } private fun addCard(card: Card) { @@ -29,4 +30,8 @@ abstract class Participant( } abstract fun isAddCardEnabled(): Boolean + + companion object { + private const val INITIAL_CARD_COUNT = 2 + } } diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt index 251acfb3f..78d75896e 100644 --- a/src/main/kotlin/blackjack/domain/Player.kt +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -2,24 +2,35 @@ package blackjack.domain class Player( val name: String, + private val betMoney: BetMoney, private val drawCard: () -> Card, ) : Participant(drawCard = drawCard) { - private lateinit var currentCard: Card + private val profitMoney: ProfitMoney = ProfitMoney() fun play( isDrawCard: (String) -> Boolean, onDrawCard: () -> Unit, onExitPlay: () -> Unit, ) { - while (isDrawCard(name)) { - currentCard = drawCard() - val isCardAdded = addCardIfAvailable(requireCard = { currentCard }, onDrawCard = onDrawCard) - if (isCardAdded.not()) break + var shouldContinue = shouldContinueDrawing(isDrawCard) + while (shouldContinue) { + val isCardAdded = addCardIfAvailable(requireCard = drawCard, onDrawCard = onDrawCard) + shouldContinue = isCardAdded && shouldContinueDrawing(isDrawCard) } onExitPlay() } + fun getProfitMoney(gameResult: GameResult): ProfitMoney { + val betMoneyAmount = betMoney.getAmount(gameResult) + profitMoney.set(betMoneyAmount) + return profitMoney + } + + private fun shouldContinueDrawing(isDrawCard: (String) -> Boolean): Boolean { + return isDrawCard(name) + } + override fun isAddCardEnabled(): Boolean { - return currentCard.isOverMaxSum(cardsSum).not() + return cardsSum < Card.MAX_SUM } } diff --git a/src/main/kotlin/blackjack/domain/PlayerResultCalculator.kt b/src/main/kotlin/blackjack/domain/PlayerResultCalculator.kt deleted file mode 100644 index 90b08aae2..000000000 --- a/src/main/kotlin/blackjack/domain/PlayerResultCalculator.kt +++ /dev/null @@ -1,15 +0,0 @@ -package blackjack.domain - -class PlayerResultCalculator { - fun calculate( - dealerScore: Int, - playerScore: Int, - ): PlayerResult { - return when { - dealerScore > 21 -> PlayerResult.WIN - playerScore > 21 -> PlayerResult.LOSE - playerScore > dealerScore -> PlayerResult.WIN - else -> PlayerResult.LOSE - } - } -} diff --git a/src/main/kotlin/blackjack/domain/Players.kt b/src/main/kotlin/blackjack/domain/Players.kt index 0929e6fad..b12d6b553 100644 --- a/src/main/kotlin/blackjack/domain/Players.kt +++ b/src/main/kotlin/blackjack/domain/Players.kt @@ -2,12 +2,12 @@ package blackjack.domain data class Players(val value: List) { fun onEachPreparePlay(action: (Player) -> Unit): Players { - value.forEach { action(it) } + onEach(action) return this } fun onEachStartPlay(action: (Player) -> Unit): Players { - value.forEach { action(it) } + onEach(action) return this } @@ -15,4 +15,22 @@ data class Players(val value: List) { value.forEach { action(it) } return this } + + fun getPlayersToProfitMoney( + isDealerBlackJack: Boolean, + dealerCardSum: Int, + ): PlayerToProfitMoney { + return PlayerToProfitMoney( + value.associateWith { player -> + val gameResult = + GameResult.getGameResultsWith( + isPlayerBlackJackInitially = player.isBlackJackInitially, + isDealerBlackJackInitially = isDealerBlackJack, + dealerCardSum = dealerCardSum, + playerCardSum = player.cardsSum, + ) + player.getProfitMoney(gameResult) + }, + ) + } } diff --git a/src/main/kotlin/blackjack/domain/ProfitMoney.kt b/src/main/kotlin/blackjack/domain/ProfitMoney.kt new file mode 100644 index 000000000..7078da6c6 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/ProfitMoney.kt @@ -0,0 +1,15 @@ +package blackjack.domain + +import java.math.BigDecimal + +class ProfitMoney { + private var current: BigDecimal = BigDecimal.ZERO + + fun getCurrentProfit(): BigDecimal { + return current + } + + fun set(amount: BigDecimal) { + current = amount + } +} diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt index 178ba2149..fedb2af3e 100644 --- a/src/main/kotlin/blackjack/view/InputView.kt +++ b/src/main/kotlin/blackjack/view/InputView.kt @@ -1,5 +1,7 @@ package blackjack.view +import java.math.BigDecimal + object InputView { fun readPlayerNames(): List { println("\n* 게임에 참여할 사람의 이름을 임력하세요 (쉼표 기준으로 분리)") @@ -10,6 +12,13 @@ object InputView { return names } + fun readBettingAmount(name: String): BigDecimal { + println("\n* $name 의 베팅 금액은?") + val amount = + readlnOrNull()?.trim()?.toBigDecimalOrNull() ?: throw IllegalStateException("Invalid Betting amount") + return amount + } + fun readIsDrawMore(name: String): Boolean { println("\n* $name 는 한 장의 카드를 더 받겠습니까? (예는 y, 아니오는 n)") val yesOrNo = readlnOrNull()?.trim() ?: "n" diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt index 2036071d7..e4947ff6e 100644 --- a/src/main/kotlin/blackjack/view/ResultView.kt +++ b/src/main/kotlin/blackjack/view/ResultView.kt @@ -4,7 +4,6 @@ import blackjack.domain.BlackJackResult import blackjack.domain.Card import blackjack.domain.Dealer import blackjack.domain.Player -import blackjack.domain.PlayerResult object ResultView { fun printPlayerInitMessage(names: List) { @@ -41,12 +40,11 @@ object ResultView { return "${player.name} 카드: ${player.cards.value.map { "${it.rank.rankName}${it.suit.koreanName}" }}" } - fun printFinalWinLose(result: BlackJackResult) { - println("\n### 최종 승패") - println("딜러 ${result.dealerWinCount}승 ${result.dealerLoseCount}패") - result.playerToResultMap.value.forEach { (player, result) -> - val winOrLose = if (result == PlayerResult.WIN) "승" else "패" - println("${player.name} : $winOrLose") + fun printFinalProfit(result: BlackJackResult) { + println("\n### 최종 수익") + println("딜러 : ${result.dealerProfitMoney.getCurrentProfit()}") + result.playerToProfit.value.forEach { (player, profit) -> + println("${player.name} : ${profit.getCurrentProfit()}") } } } diff --git a/src/test/kotlin/blackjack/domain/CardDeckTest.kt b/src/test/kotlin/blackjack/domain/CardDeckTest.kt index b8078c544..57108bbfc 100644 --- a/src/test/kotlin/blackjack/domain/CardDeckTest.kt +++ b/src/test/kotlin/blackjack/domain/CardDeckTest.kt @@ -3,11 +3,13 @@ package blackjack.domain import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test class CardDeckTest { @Test - fun `{given} 초기 Deck 상태 {when} draw() {then} 카드 리스트 사이즈 1 감소`() { + @DisplayName("{given} 초기 Deck 상태 {when} draw() {then} 카드 리스트 사이즈 1 감소") + fun `should decrease card list size by 1 when draw() is called on an initial deck`() { // Given val deck = CardDeck() val initialSize = Rank.entries.size * Suit.entries.size @@ -22,7 +24,8 @@ class CardDeckTest { } @Test - fun `{given} Empty Deck {when} draw() {then} IllegalStateException 발생`() { + @DisplayName("{given} Empty Deck {when} draw() {then} IllegalStateException 발생") + fun `should throw IllegalStateException when draw() is called on an empty deck`() { // Given val deck = CardDeck( diff --git a/src/test/kotlin/blackjack/domain/CardTest.kt b/src/test/kotlin/blackjack/domain/CardTest.kt index d25a38e3e..47e6f2348 100644 --- a/src/test/kotlin/blackjack/domain/CardTest.kt +++ b/src/test/kotlin/blackjack/domain/CardTest.kt @@ -1,28 +1,16 @@ package blackjack.domain import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource class CardTest { @ParameterizedTest - @MethodSource("provideDataForIsOverMaxSumTest") - fun `카드의 합이 21을 넘는지 여부를 확인`( - card: Card, - currentSum: Int, - expectedResult: Boolean, - ) { - // When - val result = card.isOverMaxSum(currentSum) - - // Then - assertThat(result).isEqualTo(expectedResult) - } - - @ParameterizedTest + @DisplayName("임의의 카드 리스트가 주어졌을 때 카드의 합이 룰과 일치하는지 확인") @MethodSource("provideDataForSumValuesTest") - fun `임의의 카드 리스트가 주어졌을 때 카드의 합이 룰과 일치하는지 확인`( + fun `should verify if the sum of a given card list matches the rule`( cards: Cards, expectedSum: Int, ) { @@ -34,14 +22,6 @@ class CardTest { } companion object { - @JvmStatic - fun provideDataForIsOverMaxSumTest(): List { - return listOf( - Arguments.of(Card(Rank.ACE, Suit.HEARTS), 20, false), - Arguments.of(Card(Rank.ACE, Suit.HEARTS), 21, true), - ) - } - @JvmStatic fun provideDataForSumValuesTest(): List { return listOf( diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt index f50733681..00f6a85a0 100644 --- a/src/test/kotlin/blackjack/domain/DealerTest.kt +++ b/src/test/kotlin/blackjack/domain/DealerTest.kt @@ -1,11 +1,14 @@ package blackjack.domain import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import java.math.BigDecimal class DealerTest { @Test - fun `{given} 플레이어들의 이름 {when} initPlayers {then} 각 플에이어들을 초기화 한다`() { + @DisplayName("플레이어들의 이름으로 Player 들을 초기화") + fun `should initialize players with given names`() { // given val playerNames = listOf("Sara", "John", "Levi") val dealer = Dealer(drawCard = { Card(Rank.TWO, Suit.HEARTS) }) @@ -14,6 +17,7 @@ class DealerTest { val players = dealer.initPlayers( fetchPlayerNames = { playerNames }, + getBettingAmount = { BigDecimal.ZERO }, onPlayerInit = {}, ) @@ -24,7 +28,8 @@ class DealerTest { } @Test - fun `{given} 딜러의 카드 합이 16 이하일 때 {when & then} 카드를 한개 더 뽑아 총 3장 됨`() { + @DisplayName("딜러의 카드 합이 16 이하일 때 한 장 더 뽑기") + fun `should draw one more card when dealer's card sum is 16 or less`() { // given val dealer = Dealer(drawCard = { Card(Rank.TWO, Suit.HEARTS) }) assertThat(dealer.isAddCardEnabled()).isTrue() @@ -37,7 +42,8 @@ class DealerTest { } @Test - fun `{given} 딜러 생성 {when} getCardForInitialDisplay() {then} 갖고 있는 첫번째 카드를 반환`() { + @DisplayName("딜러가 가진 카드 중 하나만 꺼내기") + fun `should return the first card when dealer retrieves one card for initial display`() { // given val firstCard = Card(Rank.TWO, Suit.HEARTS) val secondCard = Card(Rank.TWO, Suit.HEARTS) diff --git a/src/test/kotlin/blackjack/domain/GameResultTest.kt b/src/test/kotlin/blackjack/domain/GameResultTest.kt new file mode 100644 index 000000000..282829295 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/GameResultTest.kt @@ -0,0 +1,44 @@ +package blackjack.domain + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +class GameResultTest { + @DisplayName("딜러와 플레이어 점수가 주어졌을 때 게임 결과를 알맞게 반환한다") + @ParameterizedTest + @MethodSource("gameResultProvider") + fun testGetGameResultsWith( + isPlayerBlackJackInitially: Boolean, + isDealerBlackJackInitially: Boolean, + dealerCardSum: Int, + playerCardSum: Int, + expected: GameResult, + ) { + val result = + GameResult.getGameResultsWith( + isPlayerBlackJackInitially, + isDealerBlackJackInitially, + dealerCardSum, + playerCardSum, + ) + assertEquals(expected, result) + } + + companion object { + @JvmStatic + fun gameResultProvider(): List { + return listOf( + Arguments.of(true, false, 20, 21, GameResult.BLACK_JACK), + Arguments.of(true, true, 21, 21, GameResult.PUSH), + Arguments.of(false, false, 22, 18, GameResult.WIN), + Arguments.of(false, false, 20, 22, GameResult.BUST), + Arguments.of(false, false, 20, 19, GameResult.LOSE), + Arguments.of(false, false, 19, 20, GameResult.WIN), + Arguments.of(false, false, 20, 20, GameResult.PUSH), + ) + } + } +} diff --git a/src/test/kotlin/blackjack/domain/PlayerResultCalculatorTest.kt b/src/test/kotlin/blackjack/domain/PlayerResultCalculatorTest.kt deleted file mode 100644 index 096787863..000000000 --- a/src/test/kotlin/blackjack/domain/PlayerResultCalculatorTest.kt +++ /dev/null @@ -1,32 +0,0 @@ -package blackjack.domain - -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test - -class PlayerResultCalculatorTest { - private val calculator = PlayerResultCalculator() - - @Test - fun `{given} 딜러 = Bust 이면 {when & then} 플레이어 승리`() { - val result = calculator.calculate(dealerScore = 22, playerScore = 20) - assertEquals(PlayerResult.WIN, result) - } - - @Test - fun `{given} 플레이어 = Bust 이면 {when & then} 딜러 승리`() { - val result = calculator.calculate(dealerScore = 20, playerScore = 22) - assertEquals(PlayerResult.LOSE, result) - } - - @Test - fun `{given} 딜러, 플레이어 둘 다 Bust 아닐때 {when} 플레이어 점수 높으면 {then} 플레이어가 승리`() { - val result = calculator.calculate(dealerScore = 18, playerScore = 19) - assertEquals(PlayerResult.WIN, result) - } - - @Test - fun `{given}딜러, 플레이어 둘 다 Bust 아닐때 {when} 딜러 점수 높으면 {then} 딜러가 승리`() { - val result = calculator.calculate(dealerScore = 19, playerScore = 18) - assertEquals(PlayerResult.LOSE, result) - } -} diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt index 0fbefc81f..00e442efd 100644 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -1,27 +1,41 @@ package blackjack.domain import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import java.math.BigDecimal class PlayerTest { @Test - fun `Player 가 계속해서 Rank SEVEN 만 뽑으면, 최대 3번 뽑을 수 있고 카드 합은 21이다`() { - val fakeDeck = Deck { Card(Rank.SEVEN, Suit.HEARTS) } // King 만 리턴한다 - val player = Player("sara", drawCard = { fakeDeck.draw() }) + @DisplayName("Player 가 계속해서 Rank SEVEN 만 뽑으면, 최대 3번 뽑을 수 있고 카드 합은 21이다") + fun `player can draw up to 3 cards if all are Rank SEVEN, with a total sum of 21`() { + val fakeDeck = Deck { Card(Rank.SEVEN, Suit.HEARTS) } + val player = + Player( + name = "sara", + betMoney = BetMoney(BigDecimal.ZERO), + drawCard = { fakeDeck.draw() }, + ) player.play( - // 계속해서 King 만 뽑을때 isDrawCard = { true }, onDrawCard = {}, onExitPlay = {}, ) - assertThat(player.cards.value.size).isEqualTo(3) // 최대 3개를 뽑을 수 있다 + assertThat(player.cards.value.size).isEqualTo(3) assertThat(player.cardsSum).isEqualTo(21) } @Test - fun `Player 가 카드를 뽑지 않는다면 기본으로 주어진 카드만 갖는다`() { + @DisplayName("Player 가 카드를 뽑지 않는다면 기본으로 주어진 카드만 갖는다") + fun `player keeps the default cards if no additional cards are drawn`() { val fakeDeck = Deck { Card(Rank.TWO, Suit.HEARTS) } // 기본으로 주어질 카드 - val player = Player("sara", drawCard = { fakeDeck.draw() }) + val player = + Player( + name = "sara", + betMoney = BetMoney(BigDecimal.ZERO), + drawCard = { fakeDeck.draw() }, + ) player.play( // 카드를 추가로 뽑지 않는다 isDrawCard = { false }, @@ -31,4 +45,108 @@ class PlayerTest { assertThat(player.cards.value.size).isEqualTo(2) assertThat(player.cardsSum).isEqualTo(4) } + + @Test + @DisplayName("BUST 되었다면 플레이어의 베팅 금액을 모두 잃는다") + fun `player loses all money on bust`() { + // Given + val player = initFakePlayer() + + // When + val profit = player.getProfitMoney(GameResult.BUST).getCurrentProfit() + + // Then + assertEquals(BigDecimal(-10000), profit) + } + + @Test + @DisplayName("WIN 이면 플레이어의 베팅 금액을 그대로 가져간다") + fun `player wins and keeps the bet amount`() { + // Given + val player = initFakePlayer() + + // When + val profit = player.getProfitMoney(GameResult.WIN).getCurrentProfit() + + // Then + assertEquals(BigDecimal(10000), profit) + } + + @Test + @DisplayName("LOSE 이면 플레이어의 베팅 금액을 모두 잃는다") + fun `player loses all money on lose`() { + // Given + val player = initFakePlayer() + + // When + val profit = player.getProfitMoney(GameResult.LOSE).getCurrentProfit() + + // Then + assertEquals(BigDecimal(-10000), profit) + } + + @Test + @DisplayName("PUSH 이면 플레이어의 베팅 금액은 변동되지 않는다") + fun `player's profit remains the same on push`() { + // Given + val player = initFakePlayer() + + // When + val profit = player.getProfitMoney(GameResult.PUSH).getCurrentProfit() + + // Then + assertEquals(BigDecimal(10000), profit) + } + + @Test + @DisplayName("최초 할당된 2장의 카드 합이 21 이라면 블랙잭이다") + fun `if player's initial two cards sum is 21, it is blackjack`() { + val player = initBlackJackPlayer() + assertThat(player.cardsSum == Card.MAX_SUM).isTrue() + assertThat(player.isBlackJackInitially).isTrue() + } + + @Test + @DisplayName("플레이어에 할당된 최초 2장이 블랙잭이면, 베팅금액의 1.5배를 받는다") + fun `player's profit multiplies by 150 percent on blackjack`() { + // Given + val player = initBlackJackPlayer() + + // When + val profit = player.getProfitMoney(GameResult.BLACK_JACK).getCurrentProfit() + + // Then + assertEquals(BigDecimal(15000), profit) + } + + private fun initFakePlayer(): Player { + val betAmount = BetMoney(BigDecimal(10000)) + val fakeDeck = Deck { Card(Rank.KING, Suit.HEARTS) } + val player = + Player( + name = "Pobi", + betMoney = betAmount, + drawCard = { fakeDeck.draw() }, + ) + return player + } + + private fun initBlackJackPlayer(): Player { + val betAmount = BetMoney(BigDecimal(10000)) + val cards = + ArrayDeque( + listOf( + Card(Rank.ACE, Suit.HEARTS), + Card(Rank.KING, Suit.HEARTS), + ), + ) + val fakeDeck = Deck { cards.removeFirst() } + val player = + Player( + name = "Pobi", + betMoney = betAmount, + drawCard = { fakeDeck.draw() }, + ) + return player + } }