Skip to content

[Step4] 블랙잭(베팅) #797

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: kimihiqq
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@
## Step3
- [x] 딜러 카드 관리
- [x] 게임 진행 로직(딜러)

## Step4
- [x] 게임 진행 로직(베팅)
12 changes: 9 additions & 3 deletions src/main/kotlin/blackjack/app/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import blackjack.view.ResultView

fun main() {
val playerNames = InputView.getPlayerNames()
val players = Players(playerNames.map { Player(it) })
val players =
Players(
playerNames.map { name ->
val bettingAmount = InputView.getBettingAmount(name)
Player(name, bettingAmount)
},
)
val dealer = Dealer()
val participants = GameParticipants(dealer, players)
val deck = Deck()
Expand All @@ -31,6 +37,6 @@ fun main() {
game.dealerPlayTurn()
ResultView.showDealerCards(dealer.getCards())

val gameResult = game.calculateResults()
ResultView.showFinalResults(gameResult)
val profits = game.calculateFinalResults()
ResultView.showFinalProfits(profits)
}
35 changes: 33 additions & 2 deletions src/main/kotlin/blackjack/domain/Game.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ class Game(
private val deck: Deck,
private val participants: GameParticipants,
) {
companion object {
private const val BUST_THRESHOLD = 21
private const val BLACKJACK_MULTIPLIER = 1.5
}

fun start() {
participants.distributeInitialCards(deck)
}
Expand All @@ -16,7 +21,33 @@ class Game(
participants.dealer.playTurn(deck)
}

fun calculateResults(): GameResult {
return GameResultCalculator.calculate(participants.dealer, participants.players)
fun calculateFinalResults(): Map<String, Int> {
val dealerBlackjack = participants.dealer.isBlackjack()
val dealerScore = participants.dealer.getTotalValue()

val playerResults =
participants.players.getPlayers()
.associate { player ->
player.name to calculatePlayerResult(player, dealerScore, dealerBlackjack)
}

val dealerProfit = playerResults.values.sum() * -1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앞에 -(마이너스) 를 달아서 간단하게 표현해도 좋을 것 같아요 🙂

return playerResults + ("Dealer" to dealerProfit)
Comment on lines +28 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

player result 객체를 만들어보는건 어떨까요? 🙂

}

private fun calculatePlayerResult(
player: Player,
dealerScore: Int,
dealerBlackjack: Boolean,
): Int {
val playerBlackjack = player.isBlackjack()
return when {
player.getTotalValue() > BUST_THRESHOLD -> -player.getBettingAmount()
dealerScore > BUST_THRESHOLD -> player.getBettingAmount()
dealerBlackjack && playerBlackjack -> 0
playerBlackjack -> (player.getBettingAmount() * BLACKJACK_MULTIPLIER).toInt()
player.getTotalValue() > dealerScore -> player.getBettingAmount()
else -> -player.getBettingAmount()
}
Comment on lines +38 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Score 라는 객체를 만들어 일부 혹은 전체 기능의 일을 위임해보면 어떨까요?

}
}
9 changes: 9 additions & 0 deletions src/main/kotlin/blackjack/domain/Participant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ open class Participant {
fun getCards(): List<Card> {
return cards.asList()
}

fun isBlackjack(): Boolean {
return getCards().size == INITIAL_DRAW_COUNT && getTotalValue() == BLACKJACK
}

companion object {
private const val BLACKJACK = 21
private const val INITIAL_DRAW_COUNT = 2
}
}
9 changes: 8 additions & 1 deletion src/main/kotlin/blackjack/domain/Player.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
package blackjack.domain

class Player(val name: String) : Participant()
class Player(
val name: String,
private var bettingAmount: Int,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 변수의 값은 바뀌지 않아보여요 !

) : Participant() {
fun getBettingAmount(): Int {
return bettingAmount
}
Comment on lines +7 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코틀린은 외부에서 사용할 게터는 자동으로 정의해주기 때문에 필요없어보여요 😀

}
5 changes: 5 additions & 0 deletions src/main/kotlin/blackjack/view/InputView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ object InputView {
return readlnOrNull()?.split(",")?.map { it.trim() } ?: emptyList()
}

fun getBettingAmount(playerName: String): Int {
println("$playerName 의 배팅 금액은?")
return readlnOrNull()?.toIntOrNull() ?: 0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

베팅 금액이 없으면 게임을 시작하지 못하게 해야 하지 않을까요? 🧐

}

fun askToHit(playerName: String): Boolean {
println("$playerName 은 한 장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)")
return readlnOrNull()?.lowercase() == "y"
Expand Down
23 changes: 8 additions & 15 deletions src/main/kotlin/blackjack/view/ResultView.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package blackjack.view

import blackjack.domain.Card
import blackjack.domain.GameResult
import blackjack.domain.PlayerResultDTO

object ResultView {
Expand All @@ -12,28 +11,22 @@ object ResultView {
}
}

fun showDealerInitialCard(firstCard: Card) {
println("딜러: ${firstCard.rank}${firstCard.suit}")
fun showDealerInitialCard(card: Card) {
println("딜러: ${card.rank}${card.suit}")
}

fun showPlayerCards(playerResult: PlayerResultDTO) {
println("${playerResult.name}카드: ${playerResult.cards}")
}

fun showDealerCards(dealerCards: List<Card>) {
val cardDetails = dealerCards.joinToString { "${it.rank}${it.suit}" }
println("딜러 카드: $cardDetails")
fun showDealerCards(cards: List<Card>) {
println("딜러 카드: ${cards.joinToString { "${it.rank}${it.suit}" }}")
}

fun showFinalResults(gameResult: GameResult) {
println("\n## 최종 승패")

val (wins, losses) = gameResult.getDealerWinLoss()
println("딜러: ${wins}승 ${losses}패")

gameResult.playerResults.forEach { (playerName, result) ->
val outcome = if (result == GameResult.Result.WIN) "승" else "패"
println("$playerName: $outcome")
fun showFinalProfits(profits: Map<String, Int>) {
println("\n## 최종 수익")
profits.forEach { (name, profit) ->
println("$name: $profit")
}
}
}
8 changes: 4 additions & 4 deletions src/test/kotlin/blackjack/domain/GameResultCalculatorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ class GameResultCalculatorTest : StringSpec({
val players =
Players(
listOf(
Player("pobi").apply {
Player("pobi", 1000).apply {
addCard(Card(Rank.FIVE, Suit.CLUB))
addCard(Card(Rank.SIX, Suit.SPADE))
},
Player("jason").apply {
Player("jason", 1000).apply {
addCard(Card(Rank.SEVEN, Suit.DIAMOND))
addCard(Card(Rank.FOUR, Suit.CLUB))
},
Expand All @@ -44,11 +44,11 @@ class GameResultCalculatorTest : StringSpec({
val players =
Players(
listOf(
Player("pobi").apply {
Player("pobi", 1000).apply {
addCard(Card(Rank.NINE, Suit.CLUB))
addCard(Card(Rank.SEVEN, Suit.SPADE))
},
Player("jason").apply {
Player("jason", 1000).apply {
addCard(Card(Rank.EIGHT, Suit.DIAMOND))
addCard(Card(Rank.SIX, Suit.CLUB))
},
Expand Down
28 changes: 12 additions & 16 deletions src/test/kotlin/blackjack/domain/GameTest.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package blackjack.domain

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.shouldBe

class GameTest : StringSpec({

"Game should distribute initial cards to dealer and players" {
val dealer = Dealer()
val players = Players(listOf(Player("pobi"), Player("jason")))
val players = Players(listOf(Player("pobi", 10000), Player("jason", 20000)))
val participants = GameParticipants(dealer, players)
val deck = Deck()
val game = Game(deck, participants)
Expand All @@ -30,7 +30,7 @@ class GameTest : StringSpec({
val players =
Players(
listOf(
Player("pobi").apply {
Player("pobi", 10000).apply {
addCard(Card(Rank.FIVE, Suit.SPADE))
addCard(Card(Rank.THREE, Suit.HEART))
},
Expand All @@ -40,12 +40,10 @@ class GameTest : StringSpec({
val deck = Deck()
val game = Game(deck, participants)

val result = game.calculateResults()
val results = game.calculateFinalResults()

result.dealerScore shouldBe dealer.getTotalValue()
result.playerResults.shouldContainExactly(
GameResult.PlayerGameResult("pobi", GameResult.Result.WIN),
)
results["pobi"] shouldBe 10000
results["Dealer"] shouldBe -10000
}

"Game should handle player wins and losses" {
Expand All @@ -57,11 +55,11 @@ class GameTest : StringSpec({
val players =
Players(
listOf(
Player("pobi").apply {
Player("pobi", 15000).apply {
addCard(Card(Rank.TEN, Suit.SPADE))
addCard(Card(Rank.SEVEN, Suit.HEART))
},
Player("jason").apply {
Player("jason", 20000).apply {
addCard(Card(Rank.EIGHT, Suit.DIAMOND))
addCard(Card(Rank.FOUR, Suit.HEART))
},
Expand All @@ -71,12 +69,10 @@ class GameTest : StringSpec({
val deck = Deck()
val game = Game(deck, participants)

val result = game.calculateResults()
val results = game.calculateFinalResults()

result.dealerScore shouldBe dealer.getTotalValue()
result.playerResults.shouldContainExactly(
GameResult.PlayerGameResult("pobi", GameResult.Result.WIN),
GameResult.PlayerGameResult("jason", GameResult.Result.LOSE),
)
results["pobi"] shouldBe 15000
results["jason"] shouldBe -20000
results["Dealer"] shouldBe 5000
}
})
8 changes: 4 additions & 4 deletions src/test/kotlin/blackjack/domain/PlayerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import io.kotest.matchers.shouldBe

class PlayerTest : StringSpec({
"Player should have a name and manage cards through Cards" {
val player = Player("pobi")
val player = Player("pobi", 1000)
player.name shouldBe "pobi"
player.getCards().isEmpty() shouldBe true
}

"Player should add cards to their collection" {
val player = Player("pobi")
val player = Player("pobi", 1000)
val card = Card(Rank.ACE, Suit.HEART)
player.addCard(card)

Expand All @@ -20,15 +20,15 @@ class PlayerTest : StringSpec({
}

"Player should calculate the total value of their cards correctly" {
val player = Player("pobi")
val player = Player("pobi", 1000)
player.addCard(Card(Rank.ACE, Suit.HEART))
player.addCard(Card(Rank.KING, Suit.CLUB))

player.getTotalValue() shouldBe 21
}

"Player should consider Ace as 1 if 11 causes the total to exceed 21" {
val player = Player("pobi")
val player = Player("pobi", 1000)
player.addCard(Card(Rank.ACE, Suit.HEART))
player.addCard(Card(Rank.KING, Suit.CLUB))
player.addCard(Card(Rank.TWO, Suit.DIAMOND))
Expand Down
6 changes: 3 additions & 3 deletions src/test/kotlin/blackjack/domain/PlayersTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.kotest.matchers.shouldBe

class PlayersTest : StringSpec({
"Players should distribute two cards to each player" {
val players = Players(listOf(Player("pobi"), Player("jason")))
val players = Players(listOf(Player("pobi", 1000), Player("jason", 1000)))
Comment on lines 7 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

players 도 getter 가 있는데, 코틀린 기본 게터를 사용해보는게 어떨까요?

val deck = Deck()

players.distributeInitialCards(deck)
Expand All @@ -17,7 +17,7 @@ class PlayersTest : StringSpec({
}

"Players should allow a specific player to draw a card" {
val players = Players(listOf(Player("pobi"), Player("jason")))
val players = Players(listOf(Player("pobi", 1000), Player("jason", 1000)))
val deck = Deck()

players.distributeInitialCards(deck)
Expand All @@ -28,7 +28,7 @@ class PlayersTest : StringSpec({
}

"Players should throw exception if a non-existent player tries to hit" {
val players = Players(listOf(Player("pobi"), Player("jason")))
val players = Players(listOf(Player("pobi", 1000), Player("jason", 1000)))
val deck = Deck()

shouldThrow<NoSuchElementException> {
Expand Down