diff --git a/README.md b/README.md index 0818d6a9e..651e90821 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,11 @@ - [x] 딜러는 17 이상일 경우 카드를 받을 수 없다. - [x] 딜러의 패가 21이 넘어가면 유저들의 패와 상관없이 유저들이 승리한다. - [x] 결과를 출력한다. + +## step4 요구사항 + +- [ ] 플레이어는 게임 시작 시 베팅 금액을 입력한다. +- [ ] 플레이어는 bust가 되면 베팅 금액을 모두 잃게 된다. +- [ ] 딜러가 bust가 되면 플레이어들은 베팅 금액을 받는다. +- [ ] 블랙잭일 경우 베팅 금액의 1.5배를 받는다. +- [ ] 딜러와 플레이어 모두 블랙잭일 경우 베팅한 금액만 돌려받는다. diff --git a/src/main/kotlin/blackjack/BlackJackGame.kt b/src/main/kotlin/blackjack/BlackJackGame.kt index 3d019d732..76a4c4e2f 100644 --- a/src/main/kotlin/blackjack/BlackJackGame.kt +++ b/src/main/kotlin/blackjack/BlackJackGame.kt @@ -5,7 +5,9 @@ import blackjack.domain.Deck import blackjack.domain.Player import blackjack.domain.PlayerGameResult import blackjack.domain.PlayerStatus +import blackjack.domain.PlayerWinLoseResult import blackjack.domain.Players +import blackjack.view.PlayerInfo class BlackJackGame private constructor( val dealer: Dealer, @@ -40,7 +42,14 @@ class BlackJackGame private constructor( } fun getGameResult(): GameResult { - val playerGameResults = players.members.map { PlayerGameResult(it.name, it.compareWithDealer(dealer)) } + val playerGameResults = + players.members.map { + PlayerGameResult( + it.name, + it.betAmount, + PlayerWinLoseResult.compareResult(dealer, it), + ) + } val dealerResult = DealerResult(playerGameResults) return GameResult(dealerResult, playerGameResults) } @@ -62,19 +71,19 @@ class BlackJackGame private constructor( private const val DEFAULT_CARD_COUNT = 2 fun createGame( - playerNames: List, + playerInfos: List, deck: Deck, ): BlackJackGame { val dealer = createDealer(deck) - val players = createPlayers(playerNames, deck) + val players = createPlayers(playerInfos, deck) return BlackJackGame(dealer, players, deck) } private fun createPlayers( - playerNames: List, + playerInfos: List, deck: Deck, ): Players { - val players = Players.from(playerNames) + val players = Players.from(playerInfos) players.drawDefaultCards(deck, DEFAULT_CARD_COUNT) return players } diff --git a/src/main/kotlin/blackjack/DealerResult.kt b/src/main/kotlin/blackjack/DealerResult.kt index 9c3b91298..4b8884594 100644 --- a/src/main/kotlin/blackjack/DealerResult.kt +++ b/src/main/kotlin/blackjack/DealerResult.kt @@ -1,23 +1,15 @@ package blackjack +import blackjack.domain.CalculateEarnAmount import blackjack.domain.PlayerGameResult -import blackjack.domain.PlayerWinLoseResult -class DealerResult(playerGameResults: List) { - var winCount: Int = 0 - private set - var loseCount: Int = 0 - private set - var pushCount: Int = 0 - private set +class DealerResult(playerGameResults: List) : CalculateEarnAmount { + private var earnAmount: Int = + playerGameResults + .map { it.getEarnAmount() } + .fold(0) { total, amount -> total + amount }.toInt() - init { - playerGameResults.forEach { - when (it.result) { - PlayerWinLoseResult.WIN -> loseCount++ - PlayerWinLoseResult.LOSE -> winCount++ - PlayerWinLoseResult.PUSH -> pushCount++ - } - } + override fun getEarnAmount(): Int { + return -earnAmount } } diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt index 578951ad3..b6aa55049 100644 --- a/src/main/kotlin/blackjack/Main.kt +++ b/src/main/kotlin/blackjack/Main.kt @@ -8,9 +8,8 @@ import blackjack.view.PlayerCommands fun main() { // 1. 유저명 입력 및 유저 생성 val deck = RandomDeck() - val userNames = InputView.getUserNames() - val game = BlackJackGame.createGame(userNames, deck) - + val playerInfos = InputView.getPlayerInfos() + val game = BlackJackGame.createGame(playerInfos, deck) OutputView.printCurrentStatus(game) // 2-1. 블랙잭 여부 확인 @@ -36,7 +35,7 @@ private fun playBlackJack(game: BlackJackGame) { private fun handlePlayerCommand(game: BlackJackGame) { val player = game.currentPlayer - val command = InputView.getUserCommand(player) + val command = InputView.getPlayerCommand(player) when (command) { PlayerCommands.HIT -> game.hit(player) PlayerCommands.STAY -> game.stay(player) diff --git a/src/main/kotlin/blackjack/domain/BetAmount.kt b/src/main/kotlin/blackjack/domain/BetAmount.kt new file mode 100644 index 000000000..219d5fc6a --- /dev/null +++ b/src/main/kotlin/blackjack/domain/BetAmount.kt @@ -0,0 +1,11 @@ +package blackjack.domain + +data class BetAmount(val amount: Double) { + init { + require(amount > ZERO) { "베팅 금액은 0원 이상이어야 합니다." } + } + + companion object { + private const val ZERO = 0 + } +} diff --git a/src/main/kotlin/blackjack/domain/CalculateEarnAmount.kt b/src/main/kotlin/blackjack/domain/CalculateEarnAmount.kt new file mode 100644 index 000000000..46e3955d1 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/CalculateEarnAmount.kt @@ -0,0 +1,5 @@ +package blackjack.domain + +fun interface CalculateEarnAmount { + fun getEarnAmount(): Int +} diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt index 28f593c08..7bb104493 100644 --- a/src/main/kotlin/blackjack/domain/Player.kt +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -2,6 +2,7 @@ package blackjack.domain class Player( val name: String, + val betAmount: BetAmount, val hand: PlayerCards = PlayerCards(), ) { var status: PlayerStatus = PlayerStatus.HIT @@ -29,18 +30,6 @@ class Player( return status == PlayerStatus.BLACKJACK } - fun compareWithDealer(dealer: Dealer): PlayerWinLoseResult { - return when { - dealer.isBlackJack() -> PlayerWinLoseResult.LOSE - dealer.isBust() -> PlayerWinLoseResult.WIN - isBust() -> PlayerWinLoseResult.LOSE - isBlackJack() && !dealer.isBlackJack() -> PlayerWinLoseResult.WIN - dealer.getCardSum() > this.hand.calculateCardsMaxSum() -> PlayerWinLoseResult.LOSE - dealer.getCardSum() == this.hand.calculateCardsMaxSum() -> PlayerWinLoseResult.PUSH - else -> PlayerWinLoseResult.WIN - } - } - private fun validateName(name: String) { require(name.isNotBlank()) { "유저의 이름은 공백일 수 없습니다." } } diff --git a/src/main/kotlin/blackjack/domain/PlayerGameResult.kt b/src/main/kotlin/blackjack/domain/PlayerGameResult.kt index 368678e2e..0afd2f7ea 100644 --- a/src/main/kotlin/blackjack/domain/PlayerGameResult.kt +++ b/src/main/kotlin/blackjack/domain/PlayerGameResult.kt @@ -1,6 +1,13 @@ package blackjack.domain +import kotlin.math.roundToInt + data class PlayerGameResult( val name: String, + val betAmount: BetAmount, val result: PlayerWinLoseResult, -) +) : CalculateEarnAmount { + override fun getEarnAmount(): Int { + return (betAmount.amount * result.odds).roundToInt() + } +} diff --git a/src/main/kotlin/blackjack/domain/PlayerWinLoseResult.kt b/src/main/kotlin/blackjack/domain/PlayerWinLoseResult.kt index 536fb1136..e35db5f3a 100644 --- a/src/main/kotlin/blackjack/domain/PlayerWinLoseResult.kt +++ b/src/main/kotlin/blackjack/domain/PlayerWinLoseResult.kt @@ -1,7 +1,25 @@ package blackjack.domain -enum class PlayerWinLoseResult { - WIN, - LOSE, - PUSH, +enum class PlayerWinLoseResult(val odds: Double) { + WIN(1.0), + LOSE(-1.0), + PUSH(0.0), + BLACKJACK(1.5), ; + + companion object { + fun compareResult( + dealer: Dealer, + player: Player, + ): PlayerWinLoseResult { + return when { + dealer.isBlackJack() -> LOSE + dealer.isBust() -> WIN + player.isBust() -> LOSE + player.isBlackJack() && !dealer.isBlackJack() -> BLACKJACK + dealer.getCardSum() > player.hand.calculateCardsMaxSum() -> LOSE + dealer.getCardSum() == player.hand.calculateCardsMaxSum() -> PUSH + else -> WIN + } + } + } } diff --git a/src/main/kotlin/blackjack/domain/Players.kt b/src/main/kotlin/blackjack/domain/Players.kt index 667f404f3..0e11e845c 100644 --- a/src/main/kotlin/blackjack/domain/Players.kt +++ b/src/main/kotlin/blackjack/domain/Players.kt @@ -1,5 +1,7 @@ package blackjack.domain +import blackjack.view.PlayerInfo + class Players private constructor( val members: List, ) { @@ -25,8 +27,8 @@ class Players private constructor( } companion object { - fun from(names: List): Players { - return Players(names.map { Player(it) }) + fun from(names: List): Players { + return Players(names.map { Player(it.name, BetAmount(it.amount)) }) } } } diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt index 3ed39d8c5..e9eb73cd9 100644 --- a/src/main/kotlin/blackjack/view/InputView.kt +++ b/src/main/kotlin/blackjack/view/InputView.kt @@ -5,13 +5,23 @@ import blackjack.domain.Player object InputView { private const val SEPARATOR = "," - fun getUserCommand(player: Player): PlayerCommands { + fun getPlayerCommand(player: Player): PlayerCommands { println("${player.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") return PlayerCommands.findByCommand(readln()) } - fun getUserNames(): List { + fun getPlayerInfos(): List { + val playerNames = getPlayerNames() + return playerNames.map { PlayerInfo(it, getBetAmountInput(it)) } + } + + private fun getPlayerNames(): List { println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)") return readln().split(SEPARATOR) } + + private fun getBetAmountInput(playerName: String): Double { + println("${playerName}의 배팅 금액은?") + return readln().toDouble() + } } diff --git a/src/main/kotlin/blackjack/view/OutputView.kt b/src/main/kotlin/blackjack/view/OutputView.kt index 08f53e2f7..49ed32724 100644 --- a/src/main/kotlin/blackjack/view/OutputView.kt +++ b/src/main/kotlin/blackjack/view/OutputView.kt @@ -6,7 +6,6 @@ import blackjack.domain.CardMark import blackjack.domain.Cards import blackjack.domain.Dealer import blackjack.domain.Player -import blackjack.domain.PlayerWinLoseResult import blackjack.domain.Players object OutputView { @@ -47,16 +46,8 @@ object OutputView { val playerGameResults = gameResult.playerGameResults println() - println("딜러: ${dealerResult.winCount}승 ${dealerResult.pushCount}무 ${dealerResult.loseCount}패") - playerGameResults.forEach { println("${it.name}: ${convertResultToMessage(it.result)}") } - } - - private fun convertResultToMessage(result: PlayerWinLoseResult): String { - return when (result) { - PlayerWinLoseResult.WIN -> "승" - PlayerWinLoseResult.LOSE -> "패" - PlayerWinLoseResult.PUSH -> "무" - } + println("딜러: ${dealerResult.getEarnAmount()}") + playerGameResults.forEach { println("${it.name}: ${it.getEarnAmount()}") } } private fun printDealerResult(dealer: Dealer) { diff --git a/src/main/kotlin/blackjack/view/PlayerInfo.kt b/src/main/kotlin/blackjack/view/PlayerInfo.kt new file mode 100644 index 000000000..ddd1accad --- /dev/null +++ b/src/main/kotlin/blackjack/view/PlayerInfo.kt @@ -0,0 +1,6 @@ +package blackjack.view + +data class PlayerInfo( + val name: String, + val amount: Double, +) diff --git a/src/test/kotlin/blackjack/BlackJackGameTest.kt b/src/test/kotlin/blackjack/BlackJackGameTest.kt index 160bcceea..9232cc641 100644 --- a/src/test/kotlin/blackjack/BlackJackGameTest.kt +++ b/src/test/kotlin/blackjack/BlackJackGameTest.kt @@ -9,14 +9,14 @@ import io.kotest.matchers.types.shouldBeInstanceOf class BlackJackGameTest : StringSpec({ "게임에 참여한 유저들의 정보와 한명의 딜러의 정보를 가지고 있다." { - val game = BlackJackGame.createGame(createPlayers(2), RandomDeck()) + val game = BlackJackGame.createGame(createPlayerInfos(2), RandomDeck()) game.players.members.size shouldBe 2 game.dealer.shouldBeInstanceOf() } "게임이 시작되면 각 유저들은 2개의 패를 지급받는다." { - val game = BlackJackGame.createGame(createPlayers(2), RandomDeck()) + val game = BlackJackGame.createGame(createPlayerInfos(2), RandomDeck()) game.players.members.forEach { it.hand.cards.size() shouldBe 2 @@ -24,13 +24,13 @@ class BlackJackGameTest : StringSpec({ } "유저들에게 최초 지급된 패가 21일 경우 블랙잭을 선언한다." { - val game = BlackJackGame.createGame(createPlayers(2), aceDeck()) + val game = BlackJackGame.createGame(createPlayerInfos(2), aceDeck()) game.checkBlackJack() shouldBe true } "유저에게 패를 한장 지급할 수 있다." { - val game = BlackJackGame.createGame(createPlayers(2), RandomDeck()) + val game = BlackJackGame.createGame(createPlayerInfos(2), RandomDeck()) val currentPlayer = game.currentPlayer game.hit(currentPlayer) @@ -38,7 +38,7 @@ class BlackJackGameTest : StringSpec({ } "유저가 더이상 패를 지급하지 못하도록 상태를 변경할 수 있다." { - val game = BlackJackGame.createGame(createPlayers(2), RandomDeck()) + val game = BlackJackGame.createGame(createPlayerInfos(2), RandomDeck()) val currentPlayer = game.currentPlayer game.stay(currentPlayer) @@ -47,7 +47,7 @@ class BlackJackGameTest : StringSpec({ } "딜러의 패가 16이하일 경우 한장을 뽑도록 한다." { - val game = BlackJackGame.createGame(createPlayers(2), basicDeck()) + val game = BlackJackGame.createGame(createPlayerInfos(2), basicDeck()) game.handleDealerChance() diff --git a/src/test/kotlin/blackjack/HelperFunction.kt b/src/test/kotlin/blackjack/HelperFunction.kt index f5af14260..489c2b3c5 100644 --- a/src/test/kotlin/blackjack/HelperFunction.kt +++ b/src/test/kotlin/blackjack/HelperFunction.kt @@ -4,6 +4,7 @@ import blackjack.domain.Card import blackjack.domain.CardMark import blackjack.domain.CardNumber import blackjack.domain.Deck +import blackjack.view.PlayerInfo fun createAceCard(): Card { return Card(CardNumber.ACE, CardMark.HEART) @@ -16,8 +17,8 @@ fun createBasicCard( return Card(number, mark) } -fun createPlayers(number: Int): List { - return (1..number).map { "name$it" } +fun createPlayerInfos(number: Int): List { + return (1..number).map { PlayerInfo("name$it", 1000.0) } } private val CACHED_CARDS = diff --git a/src/test/kotlin/blackjack/PlayerGameResultTest.kt b/src/test/kotlin/blackjack/PlayerGameResultTest.kt new file mode 100644 index 000000000..b190f5190 --- /dev/null +++ b/src/test/kotlin/blackjack/PlayerGameResultTest.kt @@ -0,0 +1,21 @@ +package blackjack + +import blackjack.domain.BetAmount +import blackjack.domain.PlayerGameResult +import blackjack.domain.PlayerWinLoseResult +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import kotlin.math.roundToInt + +class PlayerGameResultTest : StringSpec({ + "최종 수익을 계산한다." { + // given + val result = PlayerGameResult("player1", BetAmount(10000.0), PlayerWinLoseResult.WIN) + + // when + val earnAmount = result.getEarnAmount() + + // then + earnAmount shouldBe (10000.0 * 1.0).roundToInt() + } +}) diff --git a/src/test/kotlin/blackjack/PlayerTest.kt b/src/test/kotlin/blackjack/PlayerTest.kt index e3065b28a..acf65f952 100644 --- a/src/test/kotlin/blackjack/PlayerTest.kt +++ b/src/test/kotlin/blackjack/PlayerTest.kt @@ -1,5 +1,6 @@ package blackjack +import blackjack.domain.BetAmount import blackjack.domain.CardMark import blackjack.domain.CardNumber import blackjack.domain.Player @@ -10,13 +11,13 @@ import io.kotest.matchers.shouldBe class PlayerTest : StringSpec({ "유저의 이름이 공백일 경우 예외를 반환한다." { shouldThrow { - Player("") - Player(" ") + Player("", BetAmount(1000.0)) + Player(" ", BetAmount(1000.0)) } } "유저는 한장의 카드를 패에 추가할 수 있다." { - val player = Player("user1") + val player = Player("user1", BetAmount(1000.0)) player.addCard(createAceCard()) @@ -24,7 +25,7 @@ class PlayerTest : StringSpec({ } "유저의 패가 21을 넘게 되면 버스트가 된다." { - val player = Player("user1") + val player = Player("user1", BetAmount(1000.0)) val listOfCards = listOf( createBasicCard(CardNumber.TEN, CardMark.HEART), @@ -36,4 +37,11 @@ class PlayerTest : StringSpec({ player.isBust() shouldBe true } + + "유저의 베팅 금액이 0원 이하일 경우 예외를 반환한다." { + shouldThrow { + Player("player", BetAmount(0.0)) + Player("player", BetAmount(-1000.0)) + } + } })