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 블랙잭(딜러) #785

Open
wants to merge 15 commits into
base: duhanmo
Choose a base branch
from
Open
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
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -20,4 +20,25 @@
- [x] 덱에서 카드를 하나 꺼낸다.
- [x] 유저는 카드 목록을 보유한다.
- [x] 카드목록에서 점수를 계산한다.
- [x] 카드를 덱에서 하나 뽑는다.
- [x] 카드를 덱에서 하나 뽑는다.

## Step3 - 블랙잭(딜러)

### Step2 - 리뷰반영사항
- [x] BlackJackGame::game table 이 users 와 deck 을 상태로서 관리하도록 수정
- [x] BlackJackGame::start 메서드 분리
- [x] BlackJackGame::유저목록을 받아 card 출력하도록 수정
- [x] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합
- [x] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합
- [x] BlackJackGame::게임진행책임 분리
- [x] BlackJackGame::receive 를 hit 로 메서드명 수정
- [x] Rank::enum 클래스로 수정

### 기능 구현사항
- [x] 딜러는 처음받는 2장의 합계가 16이하면 반드시 1장의 카드를 추가로 받는다
- [x] 딜러가 21을 초과하면 남은 플레이어들은 패에 상관없이 승리한다
- [x] 게임 완료 후 각 플레이어별로 승패를 출력한다
- [x] data class , 일반 클래스 설정 일관성 유지
- [x] 도메인 패키지 분리
- [x] 객체 상태를 객체가 관리
- [x] 게임 관련 로직 분리
7 changes: 2 additions & 5 deletions src/main/kotlin/blackjack/Main.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package blackjack

import blackjack.controller.BlackJackGame
import blackjack.domain.GameTable
import blackjack.view.InputView
import blackjack.view.ResultView
import blackjack.controller.BlackjackGame

fun main() {
BlackJackGame(GameTable, InputView, ResultView).start()
BlackjackGame.start()
}
50 changes: 0 additions & 50 deletions src/main/kotlin/blackjack/controller/BlackJackGame.kt

This file was deleted.

35 changes: 35 additions & 0 deletions src/main/kotlin/blackjack/controller/BlackjackGame.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package blackjack.controller

import blackjack.domain.card.Deck
import blackjack.domain.player.Dealer
import blackjack.domain.player.Player
import blackjack.view.InputView
import blackjack.view.ResultView

object BlackjackGame {
fun start() {
val gameTable = setUp()
initDeal(gameTable)
turnStart(gameTable)
ResultView.printAfterTurn(gameTable)
}

private fun setUp(): GameTable {
val gameTable = GameTable(Deck(), Dealer(), getPlayers())
ResultView.linebreak()
return gameTable
}

private fun getPlayers(): List<Player> = InputView.inputNames().map { Player(it) }

private fun initDeal(gameTable: GameTable) {
gameTable.dealInitCard()
ResultView.printDealInitCard(gameTable)
}

private fun turnStart(gameTable: GameTable) {
gameTable.playersTurn()
ResultView.linebreak()
gameTable.dealerTurn()
}

Choose a reason for hiding this comment

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

BlackjackGame은 Controller역할을 하고 있어요,
하지만 BlackjackGame이 너무 비대하진 않을까요?
너무 많은 로직이 있는거 같아요
책임을 분리해보아요!

Copy link
Author

Choose a reason for hiding this comment

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

네! 말씀대로 GameTable을 service로직처럼 상위패키지(controller)로 추출하여 로직을 분담하도록했어요🙂

}
46 changes: 46 additions & 0 deletions src/main/kotlin/blackjack/controller/GameTable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package blackjack.controller

import blackjack.domain.card.Deck
import blackjack.domain.game.GameResult
import blackjack.domain.player.Dealer
import blackjack.domain.player.Player
import blackjack.view.InputView
import blackjack.view.ResultView
Comment on lines +7 to +8

Choose a reason for hiding this comment

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

BlackjackGame , GameTable은 어떤 책임이 있을까요?
BlackjackGame은 Controller역할을 하고 있다면, GameTable은 전반적인 블랙잭 게임을 관리하고 있는거같아요
GameTable은 비즈니스로직 영역은 아닐까요?
GameTable에서 View의 의존도를 제거해보면 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

GameTable에서 게임을 진행하며 (turn) 카드상태를 출력해줘야 하는 요구사항을 만족시키느라 View에 대한 의존성이 들어가게되었는데요,
고민을 해보아도 출력에 대한 의존을 어떻게 끊을지 마땅한 방법이 떠오르질 않아요 🥲

혹시 생각나시는 방법이 있으시다면 조언 부탁드려도 될까요 남재님!?🙇‍♂️


class GameTable(
val deck: Deck,
val dealer: Dealer,
val players: List<Player>,
) {
fun dealInitCard() =
repeat(INIT_CARD_DRAW_COUNT) {
dealer.hit(deck.draw())
players.forEach { it.hit(deck.draw()) }
}

fun playersTurn() = players.forEach { playerTurn(it) }

fun dealerTurn() {
if (!dealer.canHit()) {
return
}
ResultView.printDealerHit()
dealer.hit(deck.draw())
dealerTurn()
}

fun getGameResult(): GameResult = GameResult.from(dealer, players)

Choose a reason for hiding this comment

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

get으로 접근하기보다 함수의 결과값으로 전달해보면 어떨까요?
val gameResult = gameTable.playGame( ...

Copy link
Author

Choose a reason for hiding this comment

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

넵! 해당방법으로 수정하도록 할게요 🙂


private fun playerTurn(player: Player) {
if (!player.canHit() || !InputView.inputHit(player)) {

Choose a reason for hiding this comment

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

InputView에 대한 의존성을 어떻게 끊을수 있을까요?
여러 방법있겠지만,
간단하게 람다를 전달하여 분리하는 방식도 있을거같아요 :)

Copy link
Author

@DuhanMo DuhanMo Dec 14, 2024

Choose a reason for hiding this comment

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

제가 제일 어렵게 느껴지는 부분인데🥲
람다를 이용해서 InputView에 대한 의존성을 끊도록 수정해보겠습니다!

return
}
player.hit(deck.draw())
ResultView.printPlayerCard(player)
playerTurn(player)
}

companion object {
const val INIT_CARD_DRAW_COUNT = 2
}
}
19 changes: 0 additions & 19 deletions src/main/kotlin/blackjack/domain/Card.kt

This file was deleted.

14 changes: 0 additions & 14 deletions src/main/kotlin/blackjack/domain/Deck.kt

This file was deleted.

16 changes: 0 additions & 16 deletions src/main/kotlin/blackjack/domain/GameTable.kt

This file was deleted.

31 changes: 0 additions & 31 deletions src/main/kotlin/blackjack/domain/Rank.kt

This file was deleted.

20 changes: 0 additions & 20 deletions src/main/kotlin/blackjack/domain/User.kt

This file was deleted.

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

import blackjack.domain.card.Rank.ACE

data class Card(
val rank: Rank,
val suit: Suit,
) {
val score: Int
get() = rank.score

val isAce: Boolean
get() = rank == ACE

companion object {
val ALL: List<Card> =
Suit.entries.flatMap { suit -> Rank.entries.map { rank -> Card(rank, suit) } }
}
}
8 changes: 8 additions & 0 deletions src/main/kotlin/blackjack/domain/card/Deck.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package blackjack.domain.card

class Deck(private val cards: MutableList<Card> = Card.ALL.shuffled().toMutableList()) {
fun draw(): Card {
check(cards.isNotEmpty()) { "카드가 모두 소진되었습니다" }
return cards.removeFirst()
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package blackjack.domain
package blackjack.domain.card

class Hand(private val _cards: MutableList<Card> = mutableListOf()) {
val cards: List<Card>
get() = _cards.toList()

data class Cards(val values: List<Card>) {
val score: Int
get() = calculateScore()

fun isScoreLowerThanLimit(): Boolean {
return score < BLACKJACK_SCORE_LIMIT
}

fun add(card: Card): Cards {
return Cards(values + card)
fun add(card: Card) {
_cards.add(card)
}

private fun calculateScore(): Int {
val totalScore = values.sumOf { it.score }
var aceCount = values.count { it.isAce() }
val totalScore = cards.sumOf { it.score }
var aceCount = cards.count { it.isAce }

var adjustedScore = totalScore
while (adjustedScore > BLACKJACK_SCORE_LIMIT && aceCount > 0) {
24 changes: 24 additions & 0 deletions src/main/kotlin/blackjack/domain/card/Rank.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package blackjack.domain.card

enum class Rank(val value: String, val score: Int) {
ACE("A", 11),
TWO("2", 2),
THREE("3", 3),
FOUR("4", 4),
FIVE("5", 5),
SIX("6", 6),
SEVEN("7", 7),
EIGHT("8", 8),
NINE("9", 9),
TEN("10", 10),
JACK("J", 10),
QUEEN("Q", 10),
KING("K", 10),
;

companion object {
fun from(value: String): Rank =
entries.firstOrNull { it.value == value }
?: throw IllegalArgumentException("유효하지 않은 랭크 값입니다: $value")
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package blackjack.domain
package blackjack.domain.card

enum class Suit(val description: String) {
SPADE("스페이드"),
Loading