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

3단계 - 블랙잭(딜러) #566

Open
wants to merge 9 commits into
base: jonghoonok
Choose a base branch
from
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