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

Open
wants to merge 15 commits into
base: wilgur513
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 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
16 changes: 12 additions & 4 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@

## 플레이어
* 플레이어는 이름을 가지고 있다
* 블랙잭 시작 시 각 플레이어는 카드 2장을 받는다
* 발급 받은 카드를 항상 공개한다
* 21이 넘지 않을 때 까지 카드를 받을 수 있다

## 딜러
* 처음 받은 2장이 16이하면 17이상이 될 때까지 카드를 받는다
* 발급 받은 카드 중 한장만 공개한다
* 마지막에 카드를 모두 공개한다

## 블랙잭 딜러 승패 계산
* 딜러가 21이 넘으면 플레이어는 모두 승리한다
* 딜러가 21이 넘지 않으면 21에 가까운 플레이어가 승리한다

## 블랙잭 카드
* 숫자(1 ~ 10, Jack, Queen, King)와 문양(클로버, 스페이드, 하트, 다이아)으로 구성

## 블랙잭 카드 계산
* Ace는 1 또는 11로 계산한다
* King, Queen, Jack은 각각 10으로 계산한다

## 블랙잭 드로우 룰
* 블랙잭 시작 시 각 플레이어는 카드 2장을 받는다
* 21이 넘지 않을 때 까지 카드를 받을 수 있다
45 changes: 37 additions & 8 deletions src/main/kotlin/blackjack/Main.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,55 @@
package blackjack

import blackjack.domain.Dealer
import blackjack.domain.ShuffledCardDeck
import blackjack.domain.Player
import blackjack.view.InputView
import blackjack.view.OutputView

fun main() {
Copy link

Choose a reason for hiding this comment

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

main 함수가 너무 많은 책임을 가지고 있는것 같습니다.
블랙잭 게임을 진행하기 위한 클래스를 정의해보는건 어떨까요 ?

Copy link
Author

Choose a reason for hiding this comment

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

Blackjack 클래스 추출했습니다.

val cardDeck = ShuffledCardDeck()
val (dealer, players) = createParticipants(cardDeck)
obtainCards(players, dealer)
compareBetweenDealerAndPlayers(dealer, players)
}

private fun createParticipants(cardDeck: ShuffledCardDeck): Pair<Dealer, List<Player>> {
val dealer = Dealer(cardDeck)
val players = createPlayers(cardDeck)
OutputView.printParticipantOpenedCards(listOf(dealer) + players)
return Pair(dealer, players)
}

private fun createPlayers(cardDeck: ShuffledCardDeck): List<Player> {
val names = InputView.inputNames()
val players = names.map { Player(it, ShuffledCardDeck()) }
return names.map { Player(it, cardDeck) }
}

OutputView.printPlayersCards(players)
players.forEach { obtainCard(it) }
OutputView.printPlayerResult(players)
private fun obtainCards(players: List<Player>, dealer: Dealer) {
players.forEach { obtainPlayerCard(it) }
obtainDealerCard(dealer)
}

private fun obtainCard(player: Player) {
while (isObtainCard(player)) {
private fun obtainPlayerCard(player: Player) {
while (isPlayerObtainCard(player)) {
player.obtain()
OutputView.printPlayerCards(player)
OutputView.printParticipantCards(player.name, player.hands)
}
}

private fun isObtainCard(player: Player): Boolean {
private fun isPlayerObtainCard(player: Player): Boolean {
return player.isObtainable() && InputView.inputIsObtainCard(player.name)
}

private fun obtainDealerCard(dealer: Dealer) {
while(dealer.isObtainable()) {
dealer.obtain()
OutputView.printObtainDealerCard()
}
}

private fun compareBetweenDealerAndPlayers(dealer: Dealer, players: List<Player>) {
val compareResults = dealer.compareWith(*players.toTypedArray())
OutputView.printParticipantHands(listOf(dealer) + players)
OutputView.printCompareResults(compareResults)
}
12 changes: 3 additions & 9 deletions src/main/kotlin/blackjack/domain/Cards.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package blackjack.domain

const val BLACKJACK_SCORE = 21

class Cards (
cards: List<Card>,
) {
Expand All @@ -10,7 +12,7 @@ class Cards (
constructor(vararg cards: Card): this(cards.toList())

fun sum(): Int {
if (BLACKJACK < sumOfMaximum()) {
if (BLACKJACK_SCORE < sumOfMaximum()) {
return sumOfMinimum()
}
return sumOfMaximum()
Expand All @@ -32,12 +34,4 @@ class Cards (
fun add(card: Card) {
cards.add(card)
}

fun isLessThanBlackjack(): Boolean {
return sum() < BLACKJACK
}

companion object {
private const val BLACKJACK = 21
}
}
9 changes: 9 additions & 0 deletions src/main/kotlin/blackjack/domain/CompareResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package blackjack.domain

enum class CompareResult {
DEALER_LOSE,
DRAW,
DEALER_WIN
;

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

class Dealer(
cardDeck: CardDeck
): Participant("딜러", cardDeck) {
override val openedCards = listOf(hands.first())

override fun isObtainable(): Boolean {
return sumOfCards() < 17
}

fun compareWith(vararg players: Player): Map<String, CompareResult> {
return players.associate { it.name to compareWith(it) }
}

private fun compareWith(player: Player): CompareResult {
if (isMoreThanBlackjack()) {
return CompareResult.DEALER_LOSE
}
if (player.isMoreThanBlackjack()) {
return CompareResult.DEALER_WIN
}
return compareBySumOfCards(player)
}

private fun compareBySumOfCards(player: Player): CompareResult {
if (sumOfCards() == player.sumOfCards()) {
return CompareResult.DRAW
}
if (sumOfCards() < player.sumOfCards()) {
return CompareResult.DEALER_LOSE
}
return CompareResult.DEALER_WIN
}
}
27 changes: 27 additions & 0 deletions src/main/kotlin/blackjack/domain/Participant.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package blackjack.domain

abstract class Participant(
Copy link

Choose a reason for hiding this comment

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

Participant 정의 👍

val name: String,
private val cardDeck: CardDeck,
Copy link

Choose a reason for hiding this comment

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

참가자 클래스가 카드덱을 들고있는 모델링이 적합한지 고민해보시면 좋을것 같아요.
블랙잭 참가자는 카드덱을 가지고 있다.

Copy link
Author

Choose a reason for hiding this comment

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

Card를 받도록 수정했습니다

) {
private val cards = Cards(cardDeck.next(), cardDeck.next())
abstract val openedCards: List<Card>
Copy link

Choose a reason for hiding this comment

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

openedCards 를 상태값으로 가지는것도 좋지만 반드시 가지고 있어야 하는것은 아닐것 같아요.
딜러와 참가자간의 오픈 카드룰을 정의하여 함수로 제공하는건 어떨까요 ?


val hands
get() = cards.values
Comment on lines +8 to +9
Copy link

Choose a reason for hiding this comment

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

hands 추가 👍


fun obtain() {
require(isObtainable()) { "카드를 획득할 수 없습니다." }
cards.add(cardDeck.next())
}

fun sumOfCards(): Int {
return cards.sum()
}

fun isMoreThanBlackjack(): Boolean {
return sumOfCards() > BLACKJACK_SCORE
}

abstract fun isObtainable(): Boolean
}
27 changes: 8 additions & 19 deletions src/main/kotlin/blackjack/domain/Player.kt
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
package blackjack.domain

class Player(
val name: String,
private val cardDeck: CardDeck,
) {
private val cards = Cards(cardDeck.next(), cardDeck.next())
val hands
get() = cards.values

fun obtain() {
require(isObtainable()) { "카드를 획득할 수 없습니다." }
cards.add(cardDeck.next())
}

fun sumOfCards(): Int {
return cards.sum()
}

fun isObtainable(): Boolean {
return cards.isLessThanBlackjack()
class Player (
name: String,
cardDeck: CardDeck,
) : Participant(name, cardDeck) {
override val openedCards = hands.subList(0, 2)

override fun isObtainable(): Boolean {
return sumOfCards() < BLACKJACK_SCORE
Comment on lines +9 to +10
Copy link

Choose a reason for hiding this comment

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

플레이어는 더이상 카드를 받지 않지 않는 선언을 할수 있습니다.
input의 값으로 넘기기 보단 플레이어의 상태값을 추가해보는것을 도전해보세요 😄

}
}
3 changes: 1 addition & 2 deletions src/main/kotlin/blackjack/domain/ShuffledCardDeck.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ class ShuffledCardDeck: CardDeck {
}

private fun deck(): Iterator<Card> {
return (0 until 4)
.flatMap { cards() }
return cards()
.shuffled()
.iterator()
}
Expand Down
55 changes: 44 additions & 11 deletions src/main/kotlin/blackjack/view/OutputView.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package blackjack.view

import blackjack.domain.Card
import blackjack.domain.CompareResult
import blackjack.domain.Number
import blackjack.domain.Player
import blackjack.domain.Participant
import blackjack.domain.Shape

object OutputView {

fun printPlayersCards(players: List<Player>) {
fun printParticipantOpenedCards(players: List<Participant>) {
val names = players.joinToString(separator = ", ") { it.name }
println("${names}에게 2장의 카드를 나누었습니다")
players.forEach { printPlayerCards(it) }
players.forEach {
printParticipantCards(it.name, it.openedCards)
}
}

fun printPlayerCards(player: Player) {
val hands = player.hands.joinToString(", ") { cardText(it) }
println("${player.name} 카드 : $hands")
fun printParticipantCards(name: String, cards: List<Card>) {
val cardsText = cards.joinToString(", ") { cardText(it) }
println("${name} 카드 : $cardsText")
}

private fun cardText(card: Card): String {
Expand All @@ -41,10 +43,41 @@ object OutputView {
}
}

fun printPlayerResult(players: List<Player>) {
for (player in players) {
val hands = player.hands.joinToString(", ") { cardText(it) }
println("${player.name} 카드 : $hands - 결과: ${player.sumOfCards()}")
fun printObtainDealerCard() {
println("딜러는 16이하라 한장의 카드를 더 받았습니다.")
}

fun printParticipantHands(participants: List<Participant>) {
for (participant in participants) {
val hands = participant.hands.joinToString(", ") { cardText(it) }
println("${participant.name} 카드 : $hands - 결과: ${participant.sumOfCards()}")
}
}

fun printCompareResults(compareResults: Map<String, CompareResult>) {
println("## 최종 승패")
printDealerResult(compareResults)
printPlayerResult(compareResults)
}

private fun printDealerResult(compareResults: Map<String, CompareResult>) {
val dealerWinCount = compareResults.values.count { it == CompareResult.DEALER_WIN }
val dealerDrawCount = compareResults.values.count { it == CompareResult.DRAW }
val dealerLoseCount = compareResults.values.count { it == CompareResult.DEALER_LOSE }
println("딜러 : ${dealerWinCount}승 ${dealerDrawCount}무 ${dealerLoseCount}패")
}

private fun printPlayerResult(compareResults: Map<String, CompareResult>) {
compareResults.forEach { (name, result) ->
println("${name} ${playerResultText(result)}")
}
}

private fun playerResultText(result: CompareResult): String {
return when(result) {
CompareResult.DEALER_LOSE -> "승"
CompareResult.DRAW -> "무"
CompareResult.DEALER_WIN -> "패"
}
}
}
Loading