-
Notifications
You must be signed in to change notification settings - Fork 320
Step3 블랙잭(딜러) #785
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
base: duhanmo
Are you sure you want to change the base?
Step3 블랙잭(딜러) #785
Changes from 14 commits
f301a6c
6658a35
fd73ec1
dee9976
97d81cd
ed857a0
2c50b72
37b72e7
f6331c9
a95919b
6f38f2b
7cff569
823396e
c041297
bca0a30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,9 @@ | ||
| package blackjack | ||
|
|
||
| import blackjack.controller.BlackJackGame | ||
| import blackjack.domain.GameTable | ||
| import blackjack.controller.BlackjackGame | ||
| import blackjack.view.InputView | ||
| import blackjack.view.ResultView | ||
|
|
||
| fun main() { | ||
| BlackJackGame(GameTable, InputView, ResultView).start() | ||
| BlackjackGame(InputView, ResultView).start() | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| package blackjack.controller | ||
|
|
||
| import blackjack.domain.Dealer | ||
| import blackjack.domain.Deck | ||
| import blackjack.domain.GameResult | ||
| import blackjack.domain.GameTable | ||
| import blackjack.domain.Participant | ||
| import blackjack.domain.Player | ||
| import blackjack.view.InputView | ||
| import blackjack.view.ResultView | ||
|
|
||
| data class BlackjackGame( | ||
| private val inputView: InputView, | ||
| private val resultView: ResultView, | ||
| ) { | ||
| fun start() { | ||
| val gameTable = GameTable(Deck.create()) | ||
| val participants = playGame(gameTable) | ||
| printCard(participants) | ||
| printGameResult(participants) | ||
| } | ||
|
|
||
| private fun playGame(gameTable: GameTable): List<Participant> { | ||
| val participants = setUpInitCard(gameTable) | ||
| val (players, dealer) = Participant.separate(participants) | ||
|
||
| val gamedPlayers = playersTurn(players, gameTable) | ||
| resultView.linebreak() | ||
| val gamedDealer = dealerTurn(dealer, gameTable) | ||
| return gamedPlayers + gamedDealer | ||
| } | ||
|
|
||
| private fun setUpInitCard(gameTable: GameTable): List<Participant> { | ||
| val participants = gameTable.dealInitCard(getParticipants()) | ||
| resultView.linebreak() | ||
| resultView.printInitCardReceive(participants) | ||
| resultView.printParticipantsCard(participants = participants, printScore = false) | ||
| resultView.linebreak() | ||
|
||
| return participants | ||
| } | ||
|
|
||
| private fun getParticipants(): List<Participant> { | ||
| return buildList { | ||
| add(Dealer.create()) | ||
| addAll(inputView.inputNames().map { Player.create(name = it) }) | ||
| } | ||
| } | ||
|
|
||
| private fun playersTurn( | ||
| participants: List<Participant>, | ||
| gameTable: GameTable, | ||
| ): List<Participant> { | ||
| return participants.map { playerTurn(it, gameTable) } | ||
| } | ||
|
|
||
| private tailrec fun playerTurn( | ||
| player: Participant, | ||
| gameTable: GameTable, | ||
| ): Participant { | ||
| if (!player.canHit() || !inputView.inputHit(player)) { | ||
| return player | ||
| } | ||
| val hitPlayer = gameTable.hit(player) | ||
| resultView.printParticipantCard(participant = hitPlayer, printScore = false) | ||
| return playerTurn(hitPlayer, gameTable) | ||
| } | ||
|
|
||
| private tailrec fun dealerTurn( | ||
| dealer: Participant, | ||
| gameTable: GameTable, | ||
| ): Participant { | ||
| if (!dealer.canHit()) { | ||
| return dealer | ||
| } | ||
| resultView.printDealerHit() | ||
| return dealerTurn(gameTable.hit(dealer), gameTable) | ||
| } | ||
|
|
||
| private fun printCard(participants: List<Participant>) { | ||
| resultView.linebreak() | ||
| resultView.printParticipantsCard(participants = participants, printScore = true) | ||
| } | ||
|
|
||
| private fun printGameResult(participants: List<Participant>) { | ||
| resultView.linebreak() | ||
| resultView.printGameResult(GameResult.from(participants)) | ||
|
||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BlackjackGame은 Controller역할을 하고 있어요,
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네! 말씀대로 GameTable을 service로직처럼 상위패키지(controller)로 추출하여 로직을 분담하도록했어요🙂 |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,19 @@ | ||
| package blackjack.domain | ||
|
|
||
| import blackjack.domain.Rank.Companion.ACE | ||
| import blackjack.domain.Rank.ACE | ||
|
|
||
| data class Card( | ||
| val rank: Rank, | ||
| val suit: Suit, | ||
| ) { | ||
| val score = rank.score | ||
| val score: Int | ||
| get() = rank.score | ||
|
|
||
| fun isAce(): Boolean { | ||
| return rank == ACE | ||
| } | ||
| val isAce: Boolean | ||
| get() = rank == ACE | ||
|
|
||
| companion object { | ||
| val ALL: List<Card> = | ||
| Suit.entries.flatMap { suit -> Rank.ALL.map { rank -> Card(rank, suit) } } | ||
| Suit.entries.flatMap { suit -> Rank.entries.map { rank -> Card(rank, suit) } } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,26 @@ | ||
| package blackjack.domain | ||
|
|
||
| import blackjack.domain.MatchResult.DRAW | ||
| import blackjack.domain.MatchResult.LOSS | ||
| import blackjack.domain.MatchResult.WIN | ||
|
|
||
| data class Cards(val values: List<Card>) { | ||
| val score: Int | ||
| get() = calculateScore() | ||
|
|
||
| fun isScoreLowerThanLimit(): Boolean { | ||
| return score < BLACKJACK_SCORE_LIMIT | ||
| val isBust: Boolean | ||
| get() = calculateScore() > BLACKJACK_SCORE_LIMIT | ||
|
|
||
| fun scoreLowerThan(limit: Int): Boolean { | ||
| return score < limit | ||
| } | ||
|
|
||
| fun compareScore(other: Cards): MatchResult { | ||
| return when { | ||
| score > other.score -> WIN | ||
| score < other.score -> LOSS | ||
| else -> DRAW | ||
| } | ||
|
||
| } | ||
|
|
||
| fun add(card: Card): Cards { | ||
|
|
@@ -14,7 +29,7 @@ data class Cards(val values: List<Card>) { | |
|
|
||
| private fun calculateScore(): Int { | ||
| val totalScore = values.sumOf { it.score } | ||
| var aceCount = values.count { it.isAce() } | ||
| var aceCount = values.count { it.isAce } | ||
|
|
||
| var adjustedScore = totalScore | ||
| while (adjustedScore > BLACKJACK_SCORE_LIMIT && aceCount > 0) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package blackjack.domain | ||
|
|
||
| import blackjack.domain.MatchResult.DRAW | ||
| import blackjack.domain.MatchResult.LOSS | ||
| import blackjack.domain.MatchResult.WIN | ||
| import blackjack.domain.dto.DealerGameResult | ||
| import blackjack.domain.dto.PlayerGameResult | ||
|
|
||
| data class GameResult( | ||
| val dealerGameResult: DealerGameResult, | ||
| val playerGameResults: List<PlayerGameResult>, | ||
| ) { | ||
| companion object { | ||
| fun from(participants: List<Participant>): GameResult { | ||
| val (players, dealer) = Participant.separate(participants) | ||
| val playerGameResults = players.map { player -> PlayerGameResult(player, player.compareScore(dealer)) } | ||
| return GameResult( | ||
| DealerGameResult( | ||
| winCount = playerGameResults.count { it.result == LOSS }, | ||
| lossCount = playerGameResults.count { it.result == WIN }, | ||
| drawCount = playerGameResults.count { it.result == DRAW }, | ||
| ), | ||
| playerGameResults, | ||
| ) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,21 @@ | ||
| package blackjack.domain | ||
|
|
||
| object GameTable { | ||
| const val INIT_CARD_DRAW_COUNT = 2 | ||
|
|
||
| fun dealInitCard( | ||
| users: List<User>, | ||
| deck: Deck, | ||
| ): List<User> { | ||
| return users.map { user -> | ||
| (1..INIT_CARD_DRAW_COUNT).fold(user) { acc, _ -> | ||
| acc.receiveCard(deck.draw()) | ||
| data class GameTable( | ||
| private val deck: Deck, | ||
| ) { | ||
|
||
| fun dealInitCard(participants: List<Participant>): List<Participant> { | ||
| return participants.map { participant -> | ||
| (1..INIT_CARD_DRAW_COUNT).fold(participant) { acc, _ -> | ||
| acc.hit(deck.draw()) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fun hit(participant: Participant): Participant { | ||
| return participant.hit(deck.draw()) | ||
| } | ||
|
|
||
| companion object { | ||
| const val INIT_CARD_DRAW_COUNT = 2 | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package blackjack.domain | ||
|
|
||
| enum class MatchResult(val description: String) { | ||
| WIN("승"), | ||
| LOSS("패"), | ||
| DRAW("무"), | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package blackjack.domain | ||
|
|
||
| import blackjack.domain.MatchResult.LOSS | ||
| import blackjack.domain.MatchResult.WIN | ||
|
|
||
| sealed class Participant(val name: String, val cards: Cards) { | ||
|
||
| val isBust: Boolean | ||
| get() = cards.isBust | ||
|
|
||
| abstract fun canHit(): Boolean | ||
|
|
||
| abstract fun hit(card: Card): Participant | ||
|
|
||
| companion object { | ||
| fun separate(participants: List<Participant>): Pair<List<Player>, Dealer> { | ||
| return participants.filterIsInstance<Player>() to participants.first { it is Dealer } as Dealer | ||
| } | ||
| } | ||
| } | ||
|
|
||
| class Player(name: String, cards: Cards) : Participant(name, cards) { | ||
| override fun canHit(): Boolean { | ||
| return cards.scoreLowerThan(PLAYER_SCORE_LIMIT) | ||
| } | ||
|
|
||
| override fun hit(card: Card): Player { | ||
| return Player(this.name, cards.add(card)) | ||
| } | ||
|
|
||
| fun compareScore(dealer: Dealer): MatchResult { | ||
| return when { | ||
| dealer.isBust -> WIN | ||
| this.isBust -> LOSS | ||
| else -> cards.compareScore(dealer.cards) | ||
| } | ||
| } | ||
|
||
|
|
||
| companion object { | ||
| private const val PLAYER_SCORE_LIMIT = 21 | ||
|
|
||
| fun create(name: String): Player { | ||
| return Player(name, Cards(emptyList())) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| class Dealer(cards: Cards) : Participant("딜러", cards) { | ||
| override fun canHit(): Boolean { | ||
| return cards.scoreLowerThan(DEALER_SCORE_LIMIT) | ||
| } | ||
|
|
||
| override fun hit(card: Card): Dealer { | ||
| return Dealer(cards.add(card)) | ||
| } | ||
|
|
||
| companion object { | ||
| private const val DEALER_SCORE_LIMIT = 17 | ||
|
|
||
| fun create(): Dealer { | ||
| return Dealer(Cards(emptyList())) | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BlackjackGame을 data class 로 정의한 이유가있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일관성을 맞추기 위해 특별한 경우가 아니면 data class 로 선언을 했는데요,
좀더 의미를 가지고 정의를 하도록 할게요🙂
--
추가 반영하며 data class와 일반 class를 나눈 근거는
객체가 직접 자신의 상태를 변경하며 관리하는 클래스는 일반 class,
내부 로직이 없으며 데이터로서의 역할을 하는 클래스는 data class로 선언하였어요!