Skip to content
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
- 카드를 더 줄 때마다 해당 player가 갖고있는 카드를 출력한다.
- 모든 플레이어가 카드를 더 이상 받지 않으면 결과를 출력한다.
- 결과는 플레이어별로 갖고 있는 카드의 목록과 점수를 출력한다.
- 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다.
- 딜러가 21을 초과하면 그 시점까지 남아 있던 플레이어들은 가지고 있는 패에 상관 없이 승리한다.
- 게임을 완료한 후 각 플레이어별로 승패를 출력한다.

## 테스트 구현 목록
- [x] Test 1 : Player 객체에 이름을 부여할 수 있다.
Expand All @@ -32,4 +35,7 @@
- view라서 테스트 미구현
- [x] Test 4 : Player별 점수를 계산할 수 있다.
- card의 리스트를 감싼 Cards 객체에게 점수 계산을 위임
- [x] Test 5 : 딜러의 점수가 17점 이상이면 추가로 카드를 받을 수 없다.
- Dealer가 Player를 상속하도록 하고 addCard 로직을 바꿔서 구현
-

45 changes: 30 additions & 15 deletions src/main/kotlin/blackjack/BlackJackGame.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package blackjack

import blackjack.domain.Dealer
import blackjack.domain.Distributor
import blackjack.domain.Player
import blackjack.domain.Players
import blackjack.domain.card.CardDeck
Expand All @@ -11,34 +12,48 @@ class BlackJackGame {

fun start() {
val names = InputView.inputNameOfPlayer()
val players = Players(names)
val players = Players(names.map { Player(it) })
DisplayView.dealOutCards(players)

val dealer = Dealer(CardDeck())
dealer.dealOutCards(players)
DisplayView.cardsOfPlayers(players)
val distributor = Distributor(CardDeck())
val dealer = Dealer()

dealOutAdditionalCards(dealer, players)
DisplayView.result(players)
distributor.dealOutCards(dealer, players)
DisplayView.cardsOfPlayers(dealer, players)

dealOutAdditionalCards(distributor, players)
dealOutAdditionalCard(distributor, dealer)
DisplayView.finalScore(dealer, players)

GameResultCalculator.setResult(dealer, players)
DisplayView.result(dealer, players)
}

private fun dealOutAdditionalCards(dealer: Dealer, players: Players) {
players.players.forEach {
dealOutAdditionalCard(dealer, it)
private fun dealOutAdditionalCards(distributor: Distributor, players: Players) {
players.forEach {
dealOutAdditionalCard(distributor, it)
}
}

private fun dealOutAdditionalCard(dealer: Dealer, player: Player) {
private fun dealOutAdditionalCard(distributor: Distributor, player: Player) {
DisplayView.dealOutAdditionalCard(player)
if (InputView.inputAdditionalCard() == "y") {
dealer.dealOutCard(player)
takeAnotherCard(dealer, player)
distributor.dealOutCard(player)
takeAnotherCard(distributor, player)
}
}

private fun dealOutAdditionalCard(distributor: Distributor, dealer: Dealer) {
val received = dealer.isReceivableNewCard()
if (received) {
distributor.dealOutCard(dealer)
}
DisplayView.dealOutAdditionalCard(received)
}

private fun takeAnotherCard(dealer: Dealer, player: Player) {
if (player.getScore() >= MAX_SCORE) {
dealOutAdditionalCard(dealer, player)
private fun takeAnotherCard(distributor: Distributor, player: Player) {
if (player.isReceivableNewCard()) {
dealOutAdditionalCard(distributor, player)
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/main/kotlin/blackjack/GameResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package blackjack

enum class GameResult(val description: String) {
WIN("승"),
LOSE("패")
}
18 changes: 18 additions & 0 deletions src/main/kotlin/blackjack/GameResultCalculator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package blackjack

import blackjack.domain.Dealer
import blackjack.domain.Players

object GameResultCalculator {
// 딜러와 각 플레이어의 점수를 비교해서 승패를 판별하고 기록함
// 딜러가 21을 초과하면 그 시점까지 남아 있던 플레이어들은 가지고 있는 패에 상관 없이 승리한다
fun setResult(dealer: Dealer, players: Players) {
val scoreOfDealer = dealer.getScore()
val isDealerLose = scoreOfDealer > BlackJackGame.MAX_SCORE
players.forEach {
val scoreOfPlayer = it.getScore()
it.setGameResult(isDealerLose || scoreOfPlayer > scoreOfDealer)
dealer.setGameResult(!isDealerLose && scoreOfDealer > scoreOfPlayer)
}
}
}
43 changes: 19 additions & 24 deletions src/main/kotlin/blackjack/domain/Dealer.kt
Original file line number Diff line number Diff line change
@@ -1,39 +1,34 @@
package blackjack.domain

import blackjack.GameResult
import blackjack.domain.card.Card
import blackjack.domain.card.CardDeck
import kotlin.random.Random
import blackjack.domain.card.Cards

class Dealer(
private val cardDeck: CardDeck
) {
class Dealer(name: String, cards: Cards = Cards()) : Player(name, cards) {
private var isFinished = false
val gameResults = mutableListOf<GameResult>()

/**
* 플레이어들에게 2장씩 카드를 분배함
*/
fun dealOutCards(players: Players) {
repeat(DEAL_OUT_CARD_AMOUNT){
players.players.forEach{ dealOutCard(it) }
constructor(cards: Cards = Cards()) : this(DEALER_DISPLAY_NAME, cards)

// 17점이 넘으면 호출되어도 더 이상 카드를 추가하지 않는다
override fun addCard(card: Card) {
if (isReceivableNewCard()) {
super.addCard(card)
} else {
isFinished = true
}
}

/**
* 플레이어들에게 1장씩 카드를 분배함
*/
fun dealOutCard(player: Player) {
player.cards.addCard(peekCard())
override fun setGameResult(win: Boolean) {
gameResults.add(if (win) GameResult.WIN else GameResult.LOSE)
}

/**
* 카드덱에서 랜덤한 카드를 1장 꺼냄
*/
private fun peekCard(): Card {
val random = Random.Default
val randomIndex = random.nextInt(cardDeck.cards.size)
return cardDeck.peekCard(randomIndex)
override fun isReceivableNewCard(): Boolean {
return !isFinished && getScore() < LIMIT_SCORE
}

companion object {
const val DEAL_OUT_CARD_AMOUNT = 2
private const val DEALER_DISPLAY_NAME = "딜러"
private const val LIMIT_SCORE = 17
}
}
40 changes: 40 additions & 0 deletions src/main/kotlin/blackjack/domain/Distributor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package blackjack.domain

import blackjack.domain.card.Card
import blackjack.domain.card.CardDeck
import kotlin.random.Random

class Distributor(

Choose a reason for hiding this comment

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

CardDeck에 테스트를 작성해주시긴 했지만 이 클래스를 호출한 뒤 dealer나 players에 카드가 정상적으로 분배 되었는지 등을 테스트해볼 수 있을 것 같습니다.

private val cardDeck: CardDeck
) {

/**
* 딜러와 플레이어들에게 2장씩 카드를 분배함
*/
fun dealOutCards(dealer: Dealer, players: Players) {
repeat(DEAL_OUT_CARD_AMOUNT) {
dealOutCard(dealer)
players.forEach { dealOutCard(it) }
}
}

/**
* 플레이어들에게 1장씩 카드를 분배함
*/
fun dealOutCard(player: Player) {
player.addCard(peekCard())
}

/**
* 카드덱에서 랜덤한 카드를 1장 꺼냄
*/
private fun peekCard(): Card {
val random = Random.Default
val randomIndex = random.nextInt(cardDeck.cards.size)
return cardDeck.peekCard(randomIndex)
}

companion object {
const val DEAL_OUT_CARD_AMOUNT = 2
}
}
18 changes: 17 additions & 1 deletion src/main/kotlin/blackjack/domain/Player.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
package blackjack.domain

import blackjack.BlackJackGame
import blackjack.GameResult
import blackjack.domain.card.Card
import blackjack.domain.card.Cards

data class Player(
open class Player(
val name: String,
val cards: Cards = Cards()
) {
lateinit var gameResult: GameResult

override fun toString(): String {
return name
}

open fun addCard(card: Card) {
cards.addCard(card)
}

fun getScore(): Int {
return cards.getScore()
}

open fun isReceivableNewCard(): Boolean {
return getScore() < BlackJackGame.MAX_SCORE
}

open fun setGameResult(win: Boolean) {
gameResult = if (win) GameResult.WIN else GameResult.LOSE
}
}
6 changes: 1 addition & 5 deletions src/main/kotlin/blackjack/domain/Players.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
package blackjack.domain

data class Players(
val names: List<String>
) {
val players: List<Player> = names.map { Player(it) }
}
data class Players(private val players: List<Player>) : List<Player> by players
9 changes: 3 additions & 6 deletions src/main/kotlin/blackjack/domain/card/Cards.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package blackjack.domain.card

import blackjack.BlackJackGame

class Cards(
private val cards: MutableList<Card> = mutableListOf()
) {
class Cards(private val cards: MutableList<Card> = mutableListOf()) : MutableList<Card> by cards {
fun addCard(card: Card) {
cards += card
}
Expand All @@ -19,12 +17,11 @@ class Cards(
}

private fun isAceAvailable(score: Int): Boolean {
return cards.any { it.isAce() }
&& (score + CardNumber.ACE_ADDITIONAL_SCORE) <= BlackJackGame.MAX_SCORE
return cards.any { it.isAce() } &&
(score + CardNumber.ACE_ADDITIONAL_SCORE) <= BlackJackGame.MAX_SCORE
}

override fun toString(): String {
return cards.joinToString()
}

}
39 changes: 32 additions & 7 deletions src/main/kotlin/blackjack/view/DisplayView.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package blackjack.view

import blackjack.GameResult
import blackjack.domain.Dealer
import blackjack.domain.Player
import blackjack.domain.Players

object DisplayView {

fun dealOutCards(players: Players) {
val playersName = players.players.joinToString()
println("${playersName}에게 2장의 카드 나누었습니다.")
val playersName = players.joinToString()
println("딜러와 ${playersName}에게 2장의 카드 나누었습니다.")
}

fun cardsOfPlayers(players: Players) {
players.players.forEach {
fun cardsOfPlayers(dealer: Dealer, players: Players) {
cardsOfDealer(dealer)
players.forEach {
cardsOfPlayer(it)
}
}

fun cardsOfDealer(dealer: Dealer) {
println("${dealer.name}: ${dealer.cards}")
}

fun cardsOfPlayer(player: Player) {
println("${player.name}카드: ${player.cards}")
}
Expand All @@ -24,10 +31,28 @@ object DisplayView {
println("${player}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)")
}

fun result(players: Players) {
players.players.forEach {
println("${it.name}카드: ${it.cards} - 결과: ${it.getScore()}")
fun dealOutAdditionalCard(received: Boolean) {
if (received) {
println("딜러는 16이하라 한장의 카드를 더 받았습니다.")
} else {
println("딜러는 17이상이라 카드를 더 받지 않았습니다.")
}
}

fun finalScore(dealer: Dealer, players: Players) {
printFinalScore(dealer)
players.forEach { printFinalScore(it) }
}

fun result(dealer: Dealer, players: Players) {
println("\n## 최종 승패")
val dealerWinCnt = dealer.gameResults.count { it == GameResult.WIN }
val dealerLoseCnt = dealer.gameResults.count { it == GameResult.LOSE }
println("딜러: ${dealerWinCnt}승 ${dealerLoseCnt}패")
players.forEach { println("${it.name}: ${it.gameResult.description}") }
}

private fun printFinalScore(player: Player) {
println("${player.name}카드: ${player.cards} - 결과: ${player.getScore()}")
}
}
Loading