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

Step3: 블랙잭(딜러) #553

Open
wants to merge 20 commits into
base: shlee4290
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
12c86aa
refactor(step2): infix function 활용
peter-shlee Jul 8, 2023
5ce4251
refactor(step2): Players 클래스를 domain으로 옮기고 입출력 관련 코드와 분리
peter-shlee Jul 8, 2023
520ee4a
refactor(step2): Game이 players를 들고있도록 수정
peter-shlee Jul 8, 2023
19cace4
refactor(step2): Game -> Turn으로 이름 변경 및 게임 진행 방식 수정
peter-shlee Jul 8, 2023
f60f2ee
refactor(step2): Turn과 InitialTurn 분리
peter-shlee Jul 8, 2023
ca87c66
feat(step3): TODO 작성
peter-shlee Jul 8, 2023
a519b5d
feat(step3): 게임 시작 시 딜러에게 카드 2장 지급, 1장 오픈 & 게임 진행 시 딜러 카드 합계가 16 이하이면…
peter-shlee Jul 8, 2023
99009b4
feat(step3): 딜러 21 초과하면 플레이어 승리 & 플레이어 별 승패 출력
peter-shlee Jul 8, 2023
99535ec
refactor(step3): 불필요한 메서드 제거
peter-shlee Jul 8, 2023
d911e3b
refactor(step3): Player, Dealer가 state 기반으로 동작하도록 변경
peter-shlee Jul 8, 2023
50d4b40
refactor(step3): Turn이 state 기반으로 동작하도록 변경
peter-shlee Jul 8, 2023
8abb360
feat(step3): InitialTurn 카드 나눠주는 순서 변경
peter-shlee Jul 8, 2023
ca809df
feat(step3): 카드 나눠주는 순서 블랙잭 룰에 맞춰 수정
peter-shlee Jul 8, 2023
5238474
refactor(step3): player hit 여부 결정 시 콜백 이용하도록 변경
peter-shlee Jul 9, 2023
77cdaf6
refactor(step3): DealerState, PlayerState를 State로 합침
peter-shlee Jul 9, 2023
8d90951
refactor(step3): 승패 결정을 Dealer가 하도록 수정
peter-shlee Jul 9, 2023
73cebac
refactor(step3): Players 위임 메서드 추가
peter-shlee Jul 9, 2023
4e4022e
refactor(step3): Dealer.onHit을 생성자 주입 받을 수 있게 변경
peter-shlee Jul 9, 2023
bc6a7e4
refactor(step3): 점수 상수를 모아놓은 클래스 추가
peter-shlee Jul 9, 2023
52788be
refactor(step3): Score에서 점수 계산 수행
peter-shlee Jul 9, 2023
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
15 changes: 15 additions & 0 deletions README.md

Choose a reason for hiding this comment

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

모든 플레이어의 턴이 종료된 후에 딜러가 카드를 뽑아야 할 것 같습니다.
image

Copy link
Author

Choose a reason for hiding this comment

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

블랙잭 룰을 잘못 이해하고 있었네요.. 감사합니다.

Choose a reason for hiding this comment

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

딜러의 카드는 마지막 단계에서만 오픈되어야 할 것 같아요.
image

Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,18 @@

* 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다.
* 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다.

## step3 - 딜러

### TO DO

- [X] 게임 시작 시 딜러에게 카드 2장 지급, 1장 오픈
- [X] 게임 진행 시 딜러 카드 합계가 16 이하이면 1장 추가로 받기
- [X] 딜러 21 초과하면 플레이어 승리
- [X] 플레이어 별 승패 출력

### 기능 요구 사항

* 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다.
* 딜러가 21을 초과하면 그 시점까지 남아 있던 플레이어들은 가지고 있는 패에 상관 없이 승리한다.
* 게임을 완료한 후 각 플레이어별로 승패를 출력한다.
41 changes: 29 additions & 12 deletions src/main/kotlin/controller/BlackJackController.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
package controller

import domain.Game
import domain.Player
import domain.dealer.Dealer
import domain.player.Player
import domain.player.Players
import domain.turn.Game
import domain.turn.InitialTurn
import presentation.InputView
import presentation.ResultView

fun main() {
val game = Game(
InputView.getPlayerNames()
.map { Player(it) }
)
game.start()
ResultView.printInitialState(game.players)
val playerNames = InputView.getPlayerNames()
val players = Players(playerNames.map { Player(it) })
val dealer = Dealer()

val game = Game(InitialTurn, dealer, players)
game.proceed()
ResultView.printInitialState(dealer, players.list)
dealer.addOnHitCallback {
ResultView.printDealerReceiveCardMessage()
}

while (true) {
val playerReceiveMoreCard = Players(game.playersCanReceiveMoreCard())
if (playerReceiveMoreCard.noMorePlayer()) break
game.playersCanTakeNextTurn().list.filterNot {
InputView.askReceiveCard(it.name)
}.forEach {
it.stay()
}

game.proceed()

playerReceiveMoreCard.hit(game)
if (game.isFinish) {
game.result?.let {
ResultView.printResult(it)
}
break
}

ResultView.printResult(game.players)
ResultView.printResult(dealer, players.list)
}
}
24 changes: 0 additions & 24 deletions src/main/kotlin/controller/Players.kt

This file was deleted.

24 changes: 0 additions & 24 deletions src/main/kotlin/domain/Game.kt

This file was deleted.

19 changes: 0 additions & 19 deletions src/main/kotlin/domain/Player.kt

This file was deleted.

11 changes: 11 additions & 0 deletions src/main/kotlin/domain/Result.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package domain

import domain.player.Players

data class Result(
val winners: Players,
val losers: Players,
) {
val numOfWinner = winners.list.size
val numOfLoser = losers.list.size
}
8 changes: 3 additions & 5 deletions src/main/kotlin/domain/card/Cards.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ class Cards(cards: List<Card> = emptyList()) {
sumWithoutAce + it
}

if (sumOfAllCases.contains(BLACKJACK_POINT)) {
return BLACKJACK_POINT
}

return sumOfAllCases.min()
return sumOfAllCases.filter {
it <= BLACKJACK_POINT
}.maxOrNull() ?: sumOfAllCases.min()
}

private fun doesNotHaveAce(): Boolean {
Expand Down
41 changes: 41 additions & 0 deletions src/main/kotlin/domain/dealer/Dealer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package domain.dealer

import domain.card.CardDeck
import domain.card.Cards

class Dealer(initialState: DealerState = DealerState.Hit(Cards())) {

private var state: DealerState = initialState
private var onHit: (() -> Unit)? = null

val cards: Cards
get() = state.cards

val canReceiveMoreCard: Boolean
get() = state.canReceiveMoreCard

val isBlackJack: Boolean
get() = state is DealerState.BlackJack

val isBust: Boolean
get() = state is DealerState.Bust

val result: Int
get() = state.cards.result()

fun hit(cardDeck: CardDeck) {
val capturedState = state
if (capturedState is DealerState.Hit) {
state = capturedState.hit(cardDeck.pop())
onHit?.invoke()
}
}

fun addOnHitCallback(callback: (() -> Unit)) {
onHit = callback
}

companion object {
const val DEALER_MAX_POINT = 16
}
}
41 changes: 41 additions & 0 deletions src/main/kotlin/domain/dealer/DealerState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package domain.dealer

import domain.card.Card
import domain.card.Cards

sealed interface DealerState {
val cards: Cards

val canReceiveMoreCard: Boolean

class Hit(override val cards: Cards) : DealerState {
fun hit(card: Card): DealerState {
cards.add(card)
return DealerState(cards)
}

override val canReceiveMoreCard: Boolean = cards.result() <= Dealer.DEALER_MAX_POINT
}

class Bust(override val cards: Cards) : DealerState {
override val canReceiveMoreCard: Boolean = false
}

class Stay(override val cards: Cards) : DealerState {
override val canReceiveMoreCard: Boolean = false
}

class BlackJack(override val cards: Cards) : DealerState {
override val canReceiveMoreCard: Boolean = false
}
}

fun DealerState(cards: Cards): DealerState {
val result = cards.result()
return when {
result > Cards.BLACKJACK_POINT -> DealerState.Bust(cards)
result == Cards.BLACKJACK_POINT -> DealerState.BlackJack(cards)
result > Dealer.DEALER_MAX_POINT -> DealerState.Stay(cards)
else -> DealerState.Hit(cards)
}
}
35 changes: 35 additions & 0 deletions src/main/kotlin/domain/player/Player.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package domain.player

import domain.card.Card
import domain.card.Cards

open class Player(val name: String, initialState: PlayerState = PlayerState.Hit(Cards())) {

private var state: PlayerState = initialState

val cards: Cards
get() = state.cards

val result: Int
get() = state.cards.result()

val isBust: Boolean
get() = state is PlayerState.Bust

val isHit: Boolean
get() = state is PlayerState.Hit

open val canReceiveMoreCard: Boolean
get() = state.canReceiveMoreCard

fun hit(card: Card) {
val capturedState = state
if (capturedState is PlayerState.Hit) {
state = capturedState.hit(card)
}
}

fun stay() {
state = PlayerState.Stay(state.cards)
}
}
40 changes: 40 additions & 0 deletions src/main/kotlin/domain/player/PlayerState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package domain.player

import domain.card.Card
import domain.card.Cards

sealed interface PlayerState {
val cards: Cards

val canReceiveMoreCard: Boolean

class Hit(override val cards: Cards) : PlayerState {
fun hit(card: Card): PlayerState {
cards.add(card)
return PlayerState(cards)
}

override val canReceiveMoreCard: Boolean = true
}

class Bust(override val cards: Cards) : PlayerState {
override val canReceiveMoreCard: Boolean = false
}

class Stay(override val cards: Cards) : PlayerState {
override val canReceiveMoreCard: Boolean = false
}

class BlackJack(override val cards: Cards) : PlayerState {
override val canReceiveMoreCard: Boolean = false
}
}

fun PlayerState(cards: Cards): PlayerState {
val result = cards.result()
return when {
result == Cards.BLACKJACK_POINT -> PlayerState.BlackJack(cards)
result < Cards.BLACKJACK_POINT -> PlayerState.Hit(cards)
else -> PlayerState.Bust(cards)
}
}
24 changes: 24 additions & 0 deletions src/main/kotlin/domain/player/Players.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package domain.player

import domain.card.CardDeck

data class Players(val list: List<Player>) {

fun hit(cardDeck: CardDeck) {
list.forEach {
it.hit(cardDeck.pop())
}
}

fun playersCanReceiveMoreCard(): Players {
return Players(
list.filter {
it.canReceiveMoreCard
}
)
}

fun noMoreHit(): Boolean {
return list.none { it.isHit }
}
}
Loading