Skip to content
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

step4: 블랙잭(베팅) #700

Open
wants to merge 1 commit into
base: yoonnyeong
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,10 @@
- [X] 딜러와 player의 점수를 각자 비교해서 21에 더 가까운 사람이 승, 나머지 사람은 패이다.
- [X] 플레이어는 승, 패, 무 셋 중 한가지 값을 가진다

# 4단계 - 블랙잭(베팅)
- [X] 게임을 시작할 때 베팅금액을 정한다.
- [X] 플레이어는 베팅 금액을 가지고 있다.
- [X] 플레이어가 21점 초과(버스트 상태)가 되면 베팅 금액을 전부 잃는다.
- [X] 딜러가 21 이상이 되면 플레이어는 베팅 금액을 그대로 돌려받는다.
- [X] 처음 받은 두장의 카드의 총합이 21이면 베팅 금액의 1.5배를 돌려받는다.
- [X] 최종 승패를 가지고 수익을 계산해서 최종 수익을 출력한다.
9 changes: 5 additions & 4 deletions src/main/kotlin/blackjack/BlackJackMain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ package blackjack

import blackjack.domain.Dealer
import blackjack.domain.Deck
import blackjack.domain.GameResult
import blackjack.domain.Players
import blackjack.view.InputView
import blackjack.view.ResultView

fun main() {
val deck = Deck.init()
val players = InputView.getPlayers()
for (player in players) {
InputView.getPlayersBettingAmount(player)
}
val dealer = Dealer()
val playersObject = Players(players)

Expand All @@ -18,17 +20,16 @@ fun main() {
dealer.getFirstDealCards(deck.firstDraw())
ResultView.showPlayerCards(dealer)

playersObject.getFirstTwoCards(deck.firstDraw()) { player ->
playersObject.getFirstTwoCards(deck) { player ->
ResultView.showPlayerCards(player)
}

for (player in players) {
while (InputView.askPlayer(player.name) && !player.isBusted) {
while (!player.isBusted && InputView.askPlayer(player.name)) {
Copy link

Choose a reason for hiding this comment

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

player.hit(deck.draw())
ResultView.showPlayerCards(player)
}
}

ResultView.showPlayerResult(dealer.getCard(deck), dealer, players)
ResultView.displayGameResult(GameResult(dealer, players))
}
9 changes: 5 additions & 4 deletions src/main/kotlin/blackjack/domain/BlackjackParticipant.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package blackjack.domain

abstract class BlackjackParticipant(val name: String) {
abstract class BlackjackParticipant(val name: String, var bettingAmount: Int = 0) {
Copy link

Choose a reason for hiding this comment

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

bettingAmount도 외부에서 변경할 수 없도록 변경해주세요.

private var _cards = Cards(mutableListOf())

var profit = 0.0
Copy link

Choose a reason for hiding this comment

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

외부에서 값을 바꿀수 없도록 가시성을 제한해주세요.

val isBusted get() = getScore() > Score.BLACKJACK

val isBlackjack get() = this.getScore() == Score.BLACKJACK
val cards: Cards
get() = _cards

Expand All @@ -13,7 +13,8 @@ abstract class BlackjackParticipant(val name: String) {
}

fun getFirstDealCards(twoCards: List<Card>) {
twoCards.forEach { cards.addCard(it) }
twoCards.forEach { _cards.addCard(it) }

}

fun hit(card: Card) {
Expand Down
11 changes: 9 additions & 2 deletions src/main/kotlin/blackjack/domain/Dealer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ class Dealer() : BlackjackParticipant(DEALER_NAME) {
return count
}

fun calculateProfit(playerProfit: Int) {
profit -= playerProfit
}

override val canHit: Boolean
get() = getScore() < DEALER_TARGET_SCORE
get() = getScore() < DEALER_MIN_SCORE && !isBusted

fun compare(player: Player): MatchResult {
if (player.isBlackjack && player.cards.cards.size == 2) {
return MatchResult.BLACKJACK_WIN
}
if (this.isBusted) return MatchResult.WIN
if (player.isBusted) return MatchResult.LOSS
val playerScore = player.getScore()
Expand All @@ -26,6 +33,6 @@ class Dealer() : BlackjackParticipant(DEALER_NAME) {

companion object {
private const val DEALER_NAME = "딜러"
private const val DEALER_TARGET_SCORE = 17
private const val DEALER_MIN_SCORE = 17
}
}
17 changes: 14 additions & 3 deletions src/main/kotlin/blackjack/domain/GameResult.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package blackjack.domain

class GameResult(private val dealer: Dealer, private val players: List<Player>) {
fun getMatchCount(resultMap: List<Map<Player, MatchResult>>): List<Map<MatchResult, Int>> =
resultMap.map { playerMap -> playerMap.values.groupingBy { it }.eachCount() }

fun getResultMap(): List<Map<Player, MatchResult>> {
var resultMap: MutableList<Map<Player, MatchResult>> = mutableListOf()
for (player in players) {
Expand All @@ -12,4 +9,18 @@ class GameResult(private val dealer: Dealer, private val players: List<Player>)
}
return resultMap
}

fun setProfit(): List<Player> {
val resultMapList = getResultMap()

for (resultMap in resultMapList) {
for ((player, matchResult) in resultMap) {
val profit = matchResult.rate * player.bettingAmount
Copy link

Choose a reason for hiding this comment

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

프로퍼티를 get해서 사용하지 말고 메세지를 주고받도록 해주시면 좋을것 같습니다.

player.profit = profit
dealer.calculateProfit(profit.toInt())
}
}
Comment on lines +16 to +22
Copy link

Choose a reason for hiding this comment

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

depth가 2가 넘는것 같습니다.

객체의 값을 외부에서 set할수 없도록 하고 메세지를 던지도록 변경해주시면 좋을것 같습니다.


return players
}
}
9 changes: 5 additions & 4 deletions src/main/kotlin/blackjack/domain/MatchResult.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package blackjack.domain

enum class MatchResult(val text: String) {
WIN("승"),
LOSS("패"),
TIE("무")
enum class MatchResult(val text: String, val rate: Double) {
BLACKJACK_WIN("승", 1.5),
WIN("승", 1.0),
LOSS("패", -1.0),
TIE("무", 1.0)
}
5 changes: 3 additions & 2 deletions src/main/kotlin/blackjack/domain/Player.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package blackjack.domain

class Player(name: String) : BlackjackParticipant(name) {
class Player(name: String, bettingAmount: Int) : BlackjackParticipant(name, bettingAmount) {
constructor(name: String) : this(name, bettingAmount = 0)

override val canHit: Boolean = (getScore() < Score.BLACKJACK)
override val canHit: Boolean = (getScore() < Score.BLACKJACK && !isBusted)
}
3 changes: 2 additions & 1 deletion src/main/kotlin/blackjack/domain/Players.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package blackjack.domain

class Players(private val players: List<Player>) {

fun getFirstTwoCards(cards: List<Card>, callback: (Player) -> Unit) {
fun getFirstTwoCards(cardDeck: Deck, callback: (Player) -> Unit) {
for (player in players) {
val cards = cardDeck.firstDraw()
player.getFirstDealCards(cards)
callback(player)
}
Expand Down
8 changes: 8 additions & 0 deletions src/main/kotlin/blackjack/domain/Score.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ class Score(private val denominations: List<Denomination>) {
var score = denominations.sumOf { it.score }
var numOfAce = denominations.count { it == Denomination.ACE }

if (denominations.size == 2 && numOfAce == 1 && denominations.containsScoreTen()) {
return BLACKJACK
}

while (numOfAce > 0 && score > BLACKJACK) {
score -= Denomination.ACE.let { it.score - it.optionScore }
numOfAce -= 1
}
return score
}

private fun List<Denomination>.containsScoreTen(): Boolean {
return any { it.score == 10 }
}

companion object {
const val BLACKJACK = 21
}
Expand Down
6 changes: 6 additions & 0 deletions src/main/kotlin/blackjack/view/InputView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ object InputView {
return readln().replace(" ", "").split(",").map { Player(it) }
}

fun getPlayersBettingAmount(player: Player) {
println("${player.name} 의 배팅 금액은?")
val amount = readln().toInt()
player.bettingAmount = amount
}

fun askPlayer(playerName: String): Boolean {
println("\n${playerName}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)")
val answer = readln().trim()
Expand Down
26 changes: 11 additions & 15 deletions src/main/kotlin/blackjack/view/ResultView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package blackjack.view
import blackjack.domain.BlackjackParticipant
import blackjack.domain.Dealer
import blackjack.domain.GameResult
import blackjack.domain.MatchResult
import blackjack.domain.Player

object ResultView {
Expand All @@ -21,27 +20,24 @@ object ResultView {
for (player in players) {
println("${player.name}카드: ${player.cards.cards} - 결과: ${player.getScore()}")
}
displayGameProfit(GameResult(dealer, players), dealer)
}

fun displayGameResult(gameResult: GameResult) {
println("\n## 최종 승패")
val resultMap = gameResult.getResultMap()
displayDealerResult(gameResult.getMatchCount(resultMap))
for (resultMapEntry in resultMap) {
println("${resultMapEntry.keys.first().name}: ${resultMapEntry.values.first().text}")
private fun displayGameProfit(gameResult: GameResult, dealer: Dealer) {
println("\n## 최종 수익")
val players = gameResult.setProfit()
showProfit(dealer)
for (player in players) {
println("${player.name} : ${player.profit.toInt()}")
}
}

private fun showProfit(blackjackParticipant: BlackjackParticipant) {
println("${blackjackParticipant.name}: ${blackjackParticipant.profit.toInt()}")
}

private fun showDealerDrawCount(count: Int) {
if (count == 0) return
println("딜러는 16점이하라 ${count}장의 카드를 더 받았습니다.\n")
}

private fun displayDealerResult(matches: List<Map<MatchResult, Int>>) {
val loss = matches.sumOf { it.getOrDefault(MatchResult.WIN, 0) }
val win = matches.sumOf { it.getOrDefault(MatchResult.LOSS, 0) }
val tie = matches.sumOf { it.getOrDefault(MatchResult.TIE, 0) }

println("딜러: ${win}승 ${loss}패 ${tie}무")
}
}
16 changes: 4 additions & 12 deletions src/test/kotlin/blackjack/domain/DealerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,26 @@ class DealerTest {
@Test
fun `딜러는 16점 이하이면 카드를 받아야한다`() {
val dealer = Dealer()
val cards = Cards(
dealer.getFirstDealCards(
mutableListOf(
Card(Denomination.TWO, Suit.CLUBS),
Card(Denomination.JACK, Suit.HEARTS),
)
)

for (card in cards.cards) {
dealer.hit(card)
}

dealer.canHit shouldBe true
}

@Test
fun `딜러는 17점 이상이면 카드를 받을 수 없다`() {
val dealer = Dealer()
val cards = Cards(
mutableListOf(
dealer.getFirstDealCards(
listOf(
Card(Denomination.JACK, Suit.CLUBS),
Card(Denomination.JACK, Suit.HEARTS),
Card(Denomination.JACK, Suit.HEARTS)
)
)

for (card in cards.cards) {
dealer.hit(card)
}

dealer.canHit shouldBe false
}
}
72 changes: 72 additions & 0 deletions src/test/kotlin/blackjack/domain/GameResultTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,76 @@ class GameResultTest {

resultMap.first()[player1] shouldBe MatchResult.WIN
}

@Test
fun `플레이어가 버스트 상태가 되면 가진 돈을 전부 잃는다`() {
val dealer = Dealer()
val player1 = Player("player1", 10000)
dealer.getFirstDealCards(
mutableListOf(
Card(Denomination.SEVEN, Suit.CLUBS),
Card(Denomination.JACK, Suit.CLUBS),
)
)
player1.getFirstDealCards(
mutableListOf(
Card(Denomination.KING, Suit.CLUBS),
Card(Denomination.KING, Suit.CLUBS),
Card(Denomination.KING, Suit.CLUBS)
)
)

val gameResult = GameResult(dealer, listOf(player1))
gameResult.setProfit()

player1.profit shouldBe -10000
}

@Test
fun `딜러가 버스트 상태가 되면 플레이어의 점수와 상관없이 베팅 금액을 돌려받는다`() {
val dealer = Dealer()
val player1 = Player("player1", 10000)
dealer.getFirstDealCards(
mutableListOf(
Card(Denomination.KING, Suit.CLUBS),
Card(Denomination.KING, Suit.CLUBS),
Card(Denomination.KING, Suit.CLUBS)
)
)
player1.getFirstDealCards(
mutableListOf(
Card(Denomination.KING, Suit.CLUBS),
Card(Denomination.KING, Suit.CLUBS),
Card(Denomination.KING, Suit.CLUBS)
)
)

val gameResult = GameResult(dealer, listOf(player1))
gameResult.setProfit()

player1.profit shouldBe 10000
}

@Test
fun `처음받은 카드 2장의 합이 21이면 베팅 금액의 1,5배를 돌려받는다`() {
val dealer = Dealer()
val player1 = Player("player1", 10000)
dealer.getFirstDealCards(
mutableListOf(
Card(Denomination.TWO, Suit.CLUBS),
Card(Denomination.THREE, Suit.CLUBS)
)
)
player1.getFirstDealCards(
mutableListOf(
Card(Denomination.KING, Suit.CLUBS),
Card(Denomination.ACE, Suit.CLUBS)
)
)

val gameResult = GameResult(dealer, listOf(player1))
val players = gameResult.setProfit()

players.get(0).profit.toInt() shouldBe 15000
}
}
7 changes: 7 additions & 0 deletions src/test/kotlin/blackjack/domain/PlayerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,11 @@ class PlayerTest {

player.getScore() shouldBe 17
}

@Test
fun `player 베팅금액을 정할 수 있다`() {
val player = Player("test", 10000)

player.bettingAmount shouldBe 10000
}
}