From 7632dbd6f615f947a2a416840d103b7ab49e3870 Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Mon, 27 Nov 2023 13:58:27 +0900 Subject: [PATCH 01/15] =?UTF-8?q?fix:=20=EC=B9=B4=EB=93=9C=EB=8D=B1=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/Main.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt index 1d0ff97615..3fc3dd7c7f 100644 --- a/src/main/kotlin/blackjack/Main.kt +++ b/src/main/kotlin/blackjack/Main.kt @@ -7,7 +7,8 @@ import blackjack.view.OutputView fun main() { val names = InputView.inputNames() - val players = names.map { Player(it, ShuffledCardDeck()) } + val cardDeck = ShuffledCardDeck() + val players = names.map { Player(it, cardDeck) } OutputView.printPlayersCards(players) players.forEach { obtainCard(it) } From 46dc4e3f31fdc6f2e941f1596136c91da87fe148 Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 12:43:53 +0900 Subject: [PATCH 02/15] =?UTF-8?q?docs:=20=EB=94=9C=EB=9F=AC=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=20=EC=82=AC=ED=95=AD=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Readme.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index e78fb2f0cf..eea457de8a 100644 --- a/Readme.md +++ b/Readme.md @@ -2,6 +2,18 @@ ## 플레이어 * 플레이어는 이름을 가지고 있다 + * 블랙잭 시작 시 각 플레이어는 카드 2장을 받는다 + * 발급 받은 카드를 항상 공개한다 + * 21이 넘지 않을 때 까지 카드를 받을 수 있다 + +## 딜러 + * 처음 받은 2장이 16이하면 17이상이 될 때까지 카드를 받는다 + * 발급 받은 카드 중 한장만 공개한다 + * 마지막에 카드를 모두 공개한다 + +## 블랙잭 딜러 승패 계산 + * 딜러가 21이 넘으면 플레이어는 모두 승리한다 + * 딜러가 21이 넘지 않으면 21에 가까운 플레이어가 승리한다 ## 블랙잭 카드 * 숫자(1 ~ 10, Jack, Queen, King)와 문양(클로버, 스페이드, 하트, 다이아)으로 구성 @@ -9,7 +21,3 @@ ## 블랙잭 카드 계산 * Ace는 1 또는 11로 계산한다 * King, Queen, Jack은 각각 10으로 계산한다 - -## 블랙잭 드로우 룰 - * 블랙잭 시작 시 각 플레이어는 카드 2장을 받는다 - * 21이 넘지 않을 때 까지 카드를 받을 수 있다 From ee1e71dda8320225f6e073a99f042eb511c82857 Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 12:51:22 +0900 Subject: [PATCH 03/15] =?UTF-8?q?test:=20=EC=83=88=EB=A1=AD=EA=B2=8C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC=ED=95=9C=20=ED=94=8C=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EC=B6=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/blackjack/domain/PlayerTest.kt | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt index 3856e2a2b6..66c1c42bbe 100644 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -14,18 +14,22 @@ class PlayerTest { ) assertThat(player.name).isEqualTo("pobi") - assertThat(player.sumOfCards()).isEqualTo(18) + assertThat(player.hands).containsExactlyInAnyOrder( + Card.diamond(Number.EIGHT), Card.heart(Number.TEN) + ) } @Test - fun `플레이어 카드를 획득할 수 있다`() { + fun `21 미만이면 플레이어는 카드를 획득할 수 있다`() { val player = Player( - "pobi", cardDeck(Card.diamond(Number.TWO), Card.heart(Number.THREE), Card.spade(Number.ACE)) + "pobi", cardDeck(Card.diamond(Number.QUEEN), Card.heart(Number.JACK), Card.spade(Number.ACE)) ) player.obtain() - assertThat(player.sumOfCards()).isEqualTo(16) + assertThat(player.hands).containsExactlyInAnyOrder( + Card.diamond(Number.QUEEN), Card.heart(Number.JACK), Card.spade(Number.ACE) + ) } @Test @@ -38,15 +42,20 @@ class PlayerTest { player.obtain() } assertThat(player.isObtainable()).isFalse + assertThat(player.hands).containsExactlyInAnyOrder( + Card.diamond(Number.ACE), Card.heart(Number.JACK) + ) } @Test - fun `21 미만이면 플레이어는 카드를 획득할 수 있다`() { + fun `플레이어는 발급 받은 카드의 총합을 계산한다`() { val player = Player( - "pobi", cardDeck(Card.diamond(Number.QUEEN), Card.heart(Number.JACK)) + "pobi", cardDeck(Card.diamond(Number.EIGHT), Card.heart(Number.TEN)) ) - assertThat(player.isObtainable()).isTrue() + val actual = player.sumOfCards() + + assertThat(actual).isEqualTo(18) } private fun cardDeck(vararg cards: Card): CardDeck { From 604658b904573a511728bed0802301b727cc2a2b Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 12:56:27 +0900 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20=EB=94=9C=EB=9F=AC=20=EA=B3=B5?= =?UTF-8?q?=EA=B0=9C=20=EC=B9=B4=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/domain/Dealer.kt | 6 ++++++ .../kotlin/blackjack/domain/DealerTest.kt | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/main/kotlin/blackjack/domain/Dealer.kt create mode 100644 src/test/kotlin/blackjack/domain/DealerTest.kt diff --git a/src/main/kotlin/blackjack/domain/Dealer.kt b/src/main/kotlin/blackjack/domain/Dealer.kt new file mode 100644 index 0000000000..ad5c9cd8ee --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Dealer.kt @@ -0,0 +1,6 @@ +package blackjack.domain + +class Dealer(cardDeck: CardDeck) { + private val cards = Cards(cardDeck.next(), cardDeck.next()) + val openedCards = cards.values[0] +} diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt new file mode 100644 index 0000000000..41ed03d044 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/DealerTest.kt @@ -0,0 +1,21 @@ +package blackjack.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class DealerTest { + + @Test + fun `딜러는 발급 받은 첫 카드만 공개한다`() { + val dealer = Dealer(cardDeck(Card.diamond(Number.TEN), Card.heart(Number.FIVE))) + + val actual = dealer.openedCards + + assertThat(actual).isEqualTo(Card.diamond(Number.TEN)) + } + + private fun cardDeck(vararg cards: Card): CardDeck { + val iterator = cards.iterator() + return CardDeck { iterator.next() } + } +} From 1921a1e5af933e5c896f064a5e95c6ede7534445 Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 13:05:43 +0900 Subject: [PATCH 05/15] =?UTF-8?q?feat:=20=EB=94=9C=EB=9F=AC=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EB=B0=9C=EA=B8=89=20=EC=97=AC=EB=B6=80=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/domain/Dealer.kt | 13 +++++++++++- .../kotlin/blackjack/domain/DealerTest.kt | 21 +++++++++++++++++++ .../kotlin/blackjack/domain/PlayerTest.kt | 2 +- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/blackjack/domain/Dealer.kt b/src/main/kotlin/blackjack/domain/Dealer.kt index ad5c9cd8ee..f054e8aa41 100644 --- a/src/main/kotlin/blackjack/domain/Dealer.kt +++ b/src/main/kotlin/blackjack/domain/Dealer.kt @@ -1,6 +1,17 @@ package blackjack.domain -class Dealer(cardDeck: CardDeck) { +class Dealer(private val cardDeck: CardDeck) { private val cards = Cards(cardDeck.next(), cardDeck.next()) + val openedCards = cards.values[0] + val hands + get() = cards.values + + fun obtain() { + cards.add(cardDeck.next()) + } + + fun isObtainable(): Boolean { + return cards.sum() < 17 + } } diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt index 41ed03d044..e979dc36ce 100644 --- a/src/test/kotlin/blackjack/domain/DealerTest.kt +++ b/src/test/kotlin/blackjack/domain/DealerTest.kt @@ -2,6 +2,8 @@ package blackjack.domain import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.CsvSource class DealerTest { @@ -14,6 +16,25 @@ class DealerTest { assertThat(actual).isEqualTo(Card.diamond(Number.TEN)) } + @Test + fun `딜러는 16점 이하인 경우 카드를 발급 받을 수 있다`() { + val dealer = Dealer(cardDeck(Card.diamond(Number.TEN), Card.heart(Number.SIX), Card.spade(Number.ACE))) + + dealer.obtain() + + assertThat(dealer.hands).containsExactlyInAnyOrder( + Card.diamond(Number.TEN), Card.heart(Number.SIX), Card.spade(Number.ACE) + ) + } + + @ParameterizedTest + @CsvSource(value = ["TEN, SIX, true", "TEN, SEVEN, false"]) + fun `딜러는 카드 발급 여부를 확인할 수 있다`(num1: Number, num2: Number, expect: Boolean) { + val dealer = Dealer(cardDeck(Card.diamond(num1), Card.heart(num2))) + + assertThat(dealer.isObtainable()).isEqualTo(expect) + } + private fun cardDeck(vararg cards: Card): CardDeck { val iterator = cards.iterator() return CardDeck { iterator.next() } diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt index 66c1c42bbe..72a7601f11 100644 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -38,10 +38,10 @@ class PlayerTest { "pobi", cardDeck(Card.diamond(Number.ACE), Card.heart(Number.JACK), Card.spade(Number.ACE)) ) + assertThat(player.isObtainable()).isFalse assertThrows { player.obtain() } - assertThat(player.isObtainable()).isFalse assertThat(player.hands).containsExactlyInAnyOrder( Card.diamond(Number.ACE), Card.heart(Number.JACK) ) From f33540712d95b4a86bd5f36f56126645217a5bed Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 13:09:36 +0900 Subject: [PATCH 06/15] =?UTF-8?q?feat:=20=EB=94=9C=EB=9F=AC=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EB=B0=9C=EA=B8=89=20=EB=B6=88=EA=B0=80=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/domain/Dealer.kt | 2 +- src/test/kotlin/blackjack/domain/DealerTest.kt | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/blackjack/domain/Dealer.kt b/src/main/kotlin/blackjack/domain/Dealer.kt index f054e8aa41..4bb05ea978 100644 --- a/src/main/kotlin/blackjack/domain/Dealer.kt +++ b/src/main/kotlin/blackjack/domain/Dealer.kt @@ -2,12 +2,12 @@ package blackjack.domain class Dealer(private val cardDeck: CardDeck) { private val cards = Cards(cardDeck.next(), cardDeck.next()) - val openedCards = cards.values[0] val hands get() = cards.values fun obtain() { + require(isObtainable()) { "딜러는 카드를 발급받을 수 없습니다." } cards.add(cardDeck.next()) } diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt index e979dc36ce..1756fc41e3 100644 --- a/src/test/kotlin/blackjack/domain/DealerTest.kt +++ b/src/test/kotlin/blackjack/domain/DealerTest.kt @@ -2,6 +2,7 @@ package blackjack.domain import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource @@ -27,6 +28,16 @@ class DealerTest { ) } + @Test + fun `딜러는 17점 이상인 경우 카드를 발급 받을 수 없다`() { + val dealer = Dealer(cardDeck(Card.diamond(Number.TEN), Card.heart(Number.SEVEN), Card.spade(Number.ACE))) + + assertThrows { dealer.obtain() } + assertThat(dealer.hands).containsExactlyInAnyOrder( + Card.diamond(Number.TEN), Card.heart(Number.SEVEN) + ) + } + @ParameterizedTest @CsvSource(value = ["TEN, SIX, true", "TEN, SEVEN, false"]) fun `딜러는 카드 발급 여부를 확인할 수 있다`(num1: Number, num2: Number, expect: Boolean) { From a45d6fefd3376103b04cd2e5e955f7f63a33a2cb Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 20:11:35 +0900 Subject: [PATCH 07/15] =?UTF-8?q?feat:=20=EB=94=9C=EB=9F=AC=20=ED=94=8C?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=20=EC=8A=B9=ED=8C=A8=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/blackjack/domain/CompareResult.kt | 9 +++ src/main/kotlin/blackjack/domain/Dealer.kt | 17 ++++++ .../kotlin/blackjack/domain/DealerTest.kt | 56 +++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 src/main/kotlin/blackjack/domain/CompareResult.kt diff --git a/src/main/kotlin/blackjack/domain/CompareResult.kt b/src/main/kotlin/blackjack/domain/CompareResult.kt new file mode 100644 index 0000000000..1a14dc60d0 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/CompareResult.kt @@ -0,0 +1,9 @@ +package blackjack.domain + +enum class CompareResult { + DEALER_LOSE, + DRAW, + DEALER_WIN + ; + +} diff --git a/src/main/kotlin/blackjack/domain/Dealer.kt b/src/main/kotlin/blackjack/domain/Dealer.kt index 4bb05ea978..48ca2e30cc 100644 --- a/src/main/kotlin/blackjack/domain/Dealer.kt +++ b/src/main/kotlin/blackjack/domain/Dealer.kt @@ -14,4 +14,21 @@ class Dealer(private val cardDeck: CardDeck) { fun isObtainable(): Boolean { return cards.sum() < 17 } + + fun compareWith(player: Player): CompareResult { + if (cards.sum() > 21) { + return CompareResult.DEALER_LOSE + } + if (player.sumOfCards() > 21) { + return CompareResult.DEALER_WIN + } + + if (cards.sum() == player.sumOfCards()) { + return CompareResult.DRAW + } + if (cards.sum() < player.sumOfCards()) { + return CompareResult.DEALER_LOSE + } + return CompareResult.DEALER_WIN + } } diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt index 1756fc41e3..286516a9bb 100644 --- a/src/test/kotlin/blackjack/domain/DealerTest.kt +++ b/src/test/kotlin/blackjack/domain/DealerTest.kt @@ -46,6 +46,62 @@ class DealerTest { assertThat(dealer.isObtainable()).isEqualTo(expect) } + @ParameterizedTest + @CsvSource(value = ["TEN, JACK, TWO", "ACE, FIVE, FIVE", "ACE, FOUR, FIVE"]) + fun `딜러가 21이 넘으면 플레이어는 항상 승리한다`(num1: Number, num2: Number, num3: Number) { + // arrange + val dealer = Dealer( + cardDeck(Card.diamond(Number.TEN), Card.heart(Number.SIX), Card.spade(Number.SIX)) + ).apply { obtain() } + val player = Player( + "player", + cardDeck(Card.spade(num1), Card.clover(num2), Card.heart(num3)) + ).apply { obtain() } + + // act + val actual = dealer.compareWith(player) + + // assert + assertThat(actual).isEqualTo(CompareResult.DEALER_LOSE) + } + + @ParameterizedTest + @CsvSource(value = ["TEN, TEN, DRAW", "TEN, ACE, DEALER_LOSE", "TEN, NINE, DEALER_WIN"]) + fun `딜러와 플레이어가 21이하면 21에 더 가까운 플레이어가 승리한다`(num1: Number, num2: Number, expect: CompareResult) { + // arrange + val dealer = Dealer( + cardDeck(Card.diamond(Number.TEN), Card.heart(Number.TEN)) + ) + val player = Player( + "player", + cardDeck(Card.spade(num1), Card.clover(num2)) + ) + + // act + val actual = dealer.compareWith(player) + + // assert + assertThat(actual).isEqualTo(expect) + } + + @Test + fun `딜러는 21이하이고 플레이어가 21을 넘으면 딜러가 승리한다`() { + // arrange + val dealer = Dealer( + cardDeck(Card.diamond(Number.TEN), Card.heart(Number.ACE)) + ) + val player = Player( + "player", + cardDeck(Card.spade(Number.TEN), Card.clover(Number.JACK), Card.heart(Number.TWO)) + ).apply { obtain() } + + // act + val actual = dealer.compareWith(player) + + // assert + assertThat(actual).isEqualTo(CompareResult.DEALER_WIN) + } + private fun cardDeck(vararg cards: Card): CardDeck { val iterator = cards.iterator() return CardDeck { iterator.next() } From cfdfb7d0ccc6e4ab5c3f35cd607148ecf9c3cf42 Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 20:48:46 +0900 Subject: [PATCH 08/15] =?UTF-8?q?refactor:=20Participant=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/domain/Cards.kt | 12 ++------ src/main/kotlin/blackjack/domain/Dealer.kt | 29 +++++++++---------- .../kotlin/blackjack/domain/Participant.kt | 27 +++++++++++++++++ src/main/kotlin/blackjack/domain/Player.kt | 27 +++++------------ .../kotlin/blackjack/domain/DealerTest.kt | 3 +- .../kotlin/blackjack/domain/PlayerTest.kt | 6 ++++ 6 files changed, 59 insertions(+), 45 deletions(-) create mode 100644 src/main/kotlin/blackjack/domain/Participant.kt diff --git a/src/main/kotlin/blackjack/domain/Cards.kt b/src/main/kotlin/blackjack/domain/Cards.kt index cf43faccda..f8631e8d1c 100644 --- a/src/main/kotlin/blackjack/domain/Cards.kt +++ b/src/main/kotlin/blackjack/domain/Cards.kt @@ -1,5 +1,7 @@ package blackjack.domain +const val BLACKJACK_SCORE = 21 + class Cards ( cards: List, ) { @@ -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() @@ -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 - } } diff --git a/src/main/kotlin/blackjack/domain/Dealer.kt b/src/main/kotlin/blackjack/domain/Dealer.kt index 48ca2e30cc..73cad2e708 100644 --- a/src/main/kotlin/blackjack/domain/Dealer.kt +++ b/src/main/kotlin/blackjack/domain/Dealer.kt @@ -1,32 +1,29 @@ package blackjack.domain -class Dealer(private val cardDeck: CardDeck) { - private val cards = Cards(cardDeck.next(), cardDeck.next()) - val openedCards = cards.values[0] - val hands - get() = cards.values +class Dealer( + cardDeck: CardDeck +): Participant("딜러", cardDeck) { + override val openedCards = listOf(hands.first()) - fun obtain() { - require(isObtainable()) { "딜러는 카드를 발급받을 수 없습니다." } - cards.add(cardDeck.next()) - } - - fun isObtainable(): Boolean { - return cards.sum() < 17 + override fun isObtainable(): Boolean { + return sumOfCards() < 17 } fun compareWith(player: Player): CompareResult { - if (cards.sum() > 21) { + if (isMoreThanBlackjack()) { return CompareResult.DEALER_LOSE } - if (player.sumOfCards() > 21) { + if (player.isMoreThanBlackjack()) { return CompareResult.DEALER_WIN } + return compareBySumOfCards(player) + } - if (cards.sum() == player.sumOfCards()) { + private fun compareBySumOfCards(player: Player): CompareResult { + if (sumOfCards() == player.sumOfCards()) { return CompareResult.DRAW } - if (cards.sum() < player.sumOfCards()) { + if (sumOfCards() < player.sumOfCards()) { return CompareResult.DEALER_LOSE } return CompareResult.DEALER_WIN diff --git a/src/main/kotlin/blackjack/domain/Participant.kt b/src/main/kotlin/blackjack/domain/Participant.kt new file mode 100644 index 0000000000..813c2a6c07 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Participant.kt @@ -0,0 +1,27 @@ +package blackjack.domain + +abstract class Participant( + val name: String, + private val cardDeck: CardDeck, +) { + private val cards = Cards(cardDeck.next(), cardDeck.next()) + abstract val openedCards: List + + val hands + get() = cards.values + + 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 +} diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt index 5e271dd386..4c7042b7c4 100644 --- a/src/main/kotlin/blackjack/domain/Player.kt +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -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() +open class Player ( + name: String, + cardDeck: CardDeck, +) : Participant(name, cardDeck) { + override val openedCards = hands.subList(0, 2) + + override fun isObtainable(): Boolean { + return sumOfCards() < BLACKJACK_SCORE } } diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt index 286516a9bb..02e4c518e1 100644 --- a/src/test/kotlin/blackjack/domain/DealerTest.kt +++ b/src/test/kotlin/blackjack/domain/DealerTest.kt @@ -14,7 +14,7 @@ class DealerTest { val actual = dealer.openedCards - assertThat(actual).isEqualTo(Card.diamond(Number.TEN)) + assertThat(actual).containsExactly(Card.diamond(Number.TEN)) } @Test @@ -23,6 +23,7 @@ class DealerTest { dealer.obtain() + assertThat(dealer.openedCards).containsExactly(Card.diamond(Number.TEN)) assertThat(dealer.hands).containsExactlyInAnyOrder( Card.diamond(Number.TEN), Card.heart(Number.SIX), Card.spade(Number.ACE) ) diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt index 72a7601f11..f6c09f7f6a 100644 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -14,6 +14,9 @@ class PlayerTest { ) assertThat(player.name).isEqualTo("pobi") + assertThat(player.openedCards).containsExactlyInAnyOrder( + Card.diamond(Number.EIGHT), Card.heart(Number.TEN) + ) assertThat(player.hands).containsExactlyInAnyOrder( Card.diamond(Number.EIGHT), Card.heart(Number.TEN) ) @@ -27,6 +30,9 @@ class PlayerTest { player.obtain() + assertThat(player.openedCards).containsExactlyInAnyOrder( + Card.diamond(Number.QUEEN), Card.heart(Number.JACK) + ) assertThat(player.hands).containsExactlyInAnyOrder( Card.diamond(Number.QUEEN), Card.heart(Number.JACK), Card.spade(Number.ACE) ) From 21c71a2709825c7ea8cab23c09374fd33e18aa14 Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 21:20:41 +0900 Subject: [PATCH 09/15] =?UTF-8?q?feat:=20=EB=94=9C=EB=9F=AC=20=EB=B8=94?= =?UTF-8?q?=EB=9E=99=EC=9E=AD=20=EC=B0=B8=EA=B0=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/Main.kt | 41 +++++++++++--- src/main/kotlin/blackjack/domain/Dealer.kt | 6 +- src/main/kotlin/blackjack/view/OutputView.kt | 55 +++++++++++++++---- .../kotlin/blackjack/domain/DealerTest.kt | 43 ++++++++++++++- 4 files changed, 121 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt index 3fc3dd7c7f..e4342e67c7 100644 --- a/src/main/kotlin/blackjack/Main.kt +++ b/src/main/kotlin/blackjack/Main.kt @@ -1,27 +1,50 @@ package blackjack +import blackjack.domain.Dealer import blackjack.domain.ShuffledCardDeck import blackjack.domain.Player import blackjack.view.InputView import blackjack.view.OutputView fun main() { - val names = InputView.inputNames() val cardDeck = ShuffledCardDeck() - val players = names.map { Player(it, cardDeck) } - OutputView.printPlayersCards(players) - players.forEach { obtainCard(it) } - OutputView.printPlayerResult(players) + val dealer = Dealer(cardDeck) + val players = createPlayers(cardDeck) + val participants = listOf(dealer) + players + + OutputView.printParticipantOpenedCards(participants) + obtainCards(players, dealer) + OutputView.printParticipantHands(participants) + + val compareResults = dealer.compareWith(*players.toTypedArray()) + OutputView.printCompareResults(compareResults) +} + +private fun createPlayers(cardDeck: ShuffledCardDeck): List { + val names = InputView.inputNames() + return names.map { Player(it, cardDeck) } } -private fun obtainCard(player: Player) { - while (isObtainCard(player)) { +private fun obtainCards(players: List, dealer: Dealer) { + players.forEach { obtainPlayerCard(it) } + obtainDealerCard(dealer) +} + +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() + } +} diff --git a/src/main/kotlin/blackjack/domain/Dealer.kt b/src/main/kotlin/blackjack/domain/Dealer.kt index 73cad2e708..39d57cb971 100644 --- a/src/main/kotlin/blackjack/domain/Dealer.kt +++ b/src/main/kotlin/blackjack/domain/Dealer.kt @@ -9,7 +9,11 @@ class Dealer( return sumOfCards() < 17 } - fun compareWith(player: Player): CompareResult { + fun compareWith(vararg players: Player): Map { + return players.associate { it.name to compareWith(it) } + } + + private fun compareWith(player: Player): CompareResult { if (isMoreThanBlackjack()) { return CompareResult.DEALER_LOSE } diff --git a/src/main/kotlin/blackjack/view/OutputView.kt b/src/main/kotlin/blackjack/view/OutputView.kt index 08abeb5f78..9050423d6d 100644 --- a/src/main/kotlin/blackjack/view/OutputView.kt +++ b/src/main/kotlin/blackjack/view/OutputView.kt @@ -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) { + fun printParticipantOpenedCards(players: List) { 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) { + val cardsText = cards.joinToString(", ") { cardText(it) } + println("${name} 카드 : $cardsText") } private fun cardText(card: Card): String { @@ -41,10 +43,41 @@ object OutputView { } } - fun printPlayerResult(players: List) { - 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) { + for (participant in participants) { + val hands = participant.hands.joinToString(", ") { cardText(it) } + println("${participant.name} 카드 : $hands - 결과: ${participant.sumOfCards()}") + } + } + + fun printCompareResults(compareResults: Map) { + println("## 최종 승패") + printDealerResult(compareResults) + printPlayerResult(compareResults) + } + + private fun printDealerResult(compareResults: Map) { + 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) { + 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 -> "패" } } } diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt index 02e4c518e1..629de7c19d 100644 --- a/src/test/kotlin/blackjack/domain/DealerTest.kt +++ b/src/test/kotlin/blackjack/domain/DealerTest.kt @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource +import kotlin.math.exp class DealerTest { @@ -63,7 +64,8 @@ class DealerTest { val actual = dealer.compareWith(player) // assert - assertThat(actual).isEqualTo(CompareResult.DEALER_LOSE) + assertThat(actual) + .isEqualTo(mapOf("player" to CompareResult.DEALER_LOSE)) } @ParameterizedTest @@ -82,7 +84,8 @@ class DealerTest { val actual = dealer.compareWith(player) // assert - assertThat(actual).isEqualTo(expect) + assertThat(actual) + .isEqualTo(mapOf("player" to expect)) } @Test @@ -100,7 +103,41 @@ class DealerTest { val actual = dealer.compareWith(player) // assert - assertThat(actual).isEqualTo(CompareResult.DEALER_WIN) + assertThat(actual) + .isEqualTo(mapOf("player" to CompareResult.DEALER_WIN)) + } + + @Test + fun `딜러는 여러 플레이어의 승패 결과를 수집하여 반환한다`() { + // arrange + val dealer = Dealer( + cardDeck(Card.diamond(Number.TEN), Card.diamond(Number.JACK)) + ) + val player1 = Player( + "player1", + cardDeck(Card.spade(Number.TEN), Card.spade(Number.ACE)) + ) + val player2 = Player( + "player2", + cardDeck(Card.clover(Number.TEN), Card.clover(Number.NINE)) + ) + val player3 = Player( + "player3", + cardDeck(Card.heart(Number.TEN), Card.heart(Number.JACK)) + ) + + // act + val actual = dealer.compareWith(player1, player2, player3) + + // assert + assertThat(actual) + .isEqualTo( + mapOf( + "player1" to CompareResult.DEALER_LOSE, + "player2" to CompareResult.DEALER_WIN, + "player3" to CompareResult.DRAW, + ) + ) } private fun cardDeck(vararg cards: Card): CardDeck { From 77f3671ee39e3b1a886be81ef93fe014c867edff Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 21:44:15 +0900 Subject: [PATCH 10/15] =?UTF-8?q?refactor:=20Main=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/Main.kt | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt index e4342e67c7..d0d7b1d2db 100644 --- a/src/main/kotlin/blackjack/Main.kt +++ b/src/main/kotlin/blackjack/Main.kt @@ -8,17 +8,16 @@ import blackjack.view.OutputView fun main() { val cardDeck = ShuffledCardDeck() + val (dealer, players) = createParticipants(cardDeck) + obtainCards(players, dealer) + compareBetweenDealerAndPlayers(dealer, players) +} +private fun createParticipants(cardDeck: ShuffledCardDeck): Pair> { val dealer = Dealer(cardDeck) val players = createPlayers(cardDeck) - val participants = listOf(dealer) + players - - OutputView.printParticipantOpenedCards(participants) - obtainCards(players, dealer) - OutputView.printParticipantHands(participants) - - val compareResults = dealer.compareWith(*players.toTypedArray()) - OutputView.printCompareResults(compareResults) + OutputView.printParticipantOpenedCards(listOf(dealer) + players) + return Pair(dealer, players) } private fun createPlayers(cardDeck: ShuffledCardDeck): List { @@ -48,3 +47,9 @@ private fun obtainDealerCard(dealer: Dealer) { OutputView.printObtainDealerCard() } } + +private fun compareBetweenDealerAndPlayers(dealer: Dealer, players: List) { + val compareResults = dealer.compareWith(*players.toTypedArray()) + OutputView.printParticipantHands(listOf(dealer) + players) + OutputView.printCompareResults(compareResults) +} From b37b6c33e292880f2ed928af77d7a302bfa044c2 Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 21:46:45 +0900 Subject: [PATCH 11/15] =?UTF-8?q?refactor:=20ShuffledCardDeck=20=EB=9E=9C?= =?UTF-8?q?=EB=8D=A4=20=EC=B9=B4=EB=93=9C=20=EC=83=9D=EC=84=B1=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/domain/ShuffledCardDeck.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/kotlin/blackjack/domain/ShuffledCardDeck.kt b/src/main/kotlin/blackjack/domain/ShuffledCardDeck.kt index 37799618e9..59d31ec6ec 100644 --- a/src/main/kotlin/blackjack/domain/ShuffledCardDeck.kt +++ b/src/main/kotlin/blackjack/domain/ShuffledCardDeck.kt @@ -11,8 +11,7 @@ class ShuffledCardDeck: CardDeck { } private fun deck(): Iterator { - return (0 until 4) - .flatMap { cards() } + return cards() .shuffled() .iterator() } From adb91b3d27c6d3299ca116f04abb0acbf3c2fb46 Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Tue, 28 Nov 2023 21:57:40 +0900 Subject: [PATCH 12/15] =?UTF-8?q?refactor:=20Player=20open=20=ED=82=A4?= =?UTF-8?q?=EC=9B=8C=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/domain/Player.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt index 4c7042b7c4..5358a73992 100644 --- a/src/main/kotlin/blackjack/domain/Player.kt +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -1,6 +1,6 @@ package blackjack.domain -open class Player ( +class Player ( name: String, cardDeck: CardDeck, ) : Participant(name, cardDeck) { From 1fac2fd43d1c965b8b1ee16842fa7d2e2668adc1 Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Fri, 29 Dec 2023 16:52:30 +0900 Subject: [PATCH 13/15] =?UTF-8?q?refactor:=20BlackJack=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/Main.kt | 56 +++---- src/main/kotlin/blackjack/domain/BlackJack.kt | 43 ++++++ src/main/kotlin/blackjack/view/OutputView.kt | 9 +- .../kotlin/blackjack/domain/BlackjackTest.kt | 138 ++++++++++++++++++ 4 files changed, 207 insertions(+), 39 deletions(-) create mode 100644 src/main/kotlin/blackjack/domain/BlackJack.kt create mode 100644 src/test/kotlin/blackjack/domain/BlackjackTest.kt diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt index d0d7b1d2db..fe525902e6 100644 --- a/src/main/kotlin/blackjack/Main.kt +++ b/src/main/kotlin/blackjack/Main.kt @@ -1,55 +1,41 @@ package blackjack -import blackjack.domain.Dealer +import blackjack.domain.BlackJack import blackjack.domain.ShuffledCardDeck -import blackjack.domain.Player import blackjack.view.InputView import blackjack.view.OutputView fun main() { val cardDeck = ShuffledCardDeck() - val (dealer, players) = createParticipants(cardDeck) - obtainCards(players, dealer) - compareBetweenDealerAndPlayers(dealer, players) -} - -private fun createParticipants(cardDeck: ShuffledCardDeck): Pair> { - val dealer = Dealer(cardDeck) - val players = createPlayers(cardDeck) - OutputView.printParticipantOpenedCards(listOf(dealer) + players) - return Pair(dealer, players) -} + val playerNames = InputView.inputNames() + val blackjack = BlackJack(cardDeck, *playerNames.toTypedArray()) -private fun createPlayers(cardDeck: ShuffledCardDeck): List { - val names = InputView.inputNames() - return names.map { Player(it, cardDeck) } + OutputView.printParticipantOpenedCards(blackjack.openCardsOfParticipant()) + obtainCards(playerNames, blackjack) + OutputView.printCompareResults(blackjack.compareResults()) } -private fun obtainCards(players: List, dealer: Dealer) { - players.forEach { obtainPlayerCard(it) } - obtainDealerCard(dealer) +private fun obtainCards(playerNames: List, blackjack: BlackJack) { + obtainCardsForPlayers(playerNames, blackjack) + obtainCardsForDealer(blackjack) + OutputView.printParticipantHands(blackjack.participants()) } -private fun obtainPlayerCard(player: Player) { - while (isPlayerObtainCard(player)) { - player.obtain() - OutputView.printParticipantCards(player.name, player.hands) +private fun obtainCardsForDealer(blackjack: BlackJack) { + while (blackjack.isDealerObtainable()) { + blackjack.obtainDealerCard() + OutputView.printObtainDealerCard() } } -private fun isPlayerObtainCard(player: Player): Boolean { - return player.isObtainable() && InputView.inputIsObtainCard(player.name) +private fun obtainCardsForPlayers(playerNames: List, blackjack: BlackJack) { + playerNames.forEach { obtainCardsForPlayer(it, blackjack) } } -private fun obtainDealerCard(dealer: Dealer) { - while(dealer.isObtainable()) { - dealer.obtain() - OutputView.printObtainDealerCard() +private fun obtainCardsForPlayer(name: String, blackjack: BlackJack) { + val wantToTake = { InputView.inputIsObtainCard(name) } + while (blackjack.isPlayerObtainable(name, wantToTake)) { + val cards = blackjack.obtainPlayerCard(name) + OutputView.printParticipantCards(name, cards) } } - -private fun compareBetweenDealerAndPlayers(dealer: Dealer, players: List) { - val compareResults = dealer.compareWith(*players.toTypedArray()) - OutputView.printParticipantHands(listOf(dealer) + players) - OutputView.printCompareResults(compareResults) -} diff --git a/src/main/kotlin/blackjack/domain/BlackJack.kt b/src/main/kotlin/blackjack/domain/BlackJack.kt new file mode 100644 index 0000000000..09ae72b640 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/BlackJack.kt @@ -0,0 +1,43 @@ +package blackjack.domain + +import java.util.function.Supplier + +class BlackJack( + private val cardDeck: CardDeck, + vararg names: String +) { + private val dealer = Dealer(cardDeck) + private val players = names.map { Player(it, cardDeck) } + + fun openCardsOfParticipant(): Map> { + return participants().associate { it.name to it.openedCards } + } + + fun isDealerObtainable(): Boolean { + return dealer.isObtainable() + } + + fun obtainDealerCard(): List { + dealer.obtain() + return dealer.hands + } + + fun isPlayerObtainable(name: String, wantToTake: Supplier): Boolean { + val player = players.first { it.name == name } + return player.isObtainable() && wantToTake.get() + } + + fun obtainPlayerCard(name: String): List { + val player = players.first { it.name == name } + player.obtain() + return player.hands + } + + fun compareResults(): Map { + return dealer.compareWith(*players.toTypedArray()) + } + + fun participants(): List { + return players + listOf(dealer) + } +} diff --git a/src/main/kotlin/blackjack/view/OutputView.kt b/src/main/kotlin/blackjack/view/OutputView.kt index 9050423d6d..5706ede258 100644 --- a/src/main/kotlin/blackjack/view/OutputView.kt +++ b/src/main/kotlin/blackjack/view/OutputView.kt @@ -7,11 +7,12 @@ import blackjack.domain.Participant import blackjack.domain.Shape object OutputView { - fun printParticipantOpenedCards(players: List) { - val names = players.joinToString(separator = ", ") { it.name } + + fun printParticipantOpenedCards(openCards: Map>) { + val names = openCards.keys.joinToString(separator = ", ") println("${names}에게 2장의 카드를 나누었습니다") - players.forEach { - printParticipantCards(it.name, it.openedCards) + openCards.entries.forEach { + printParticipantCards(it.key, it.value) } } diff --git a/src/test/kotlin/blackjack/domain/BlackjackTest.kt b/src/test/kotlin/blackjack/domain/BlackjackTest.kt new file mode 100644 index 0000000000..ff79aadd04 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/BlackjackTest.kt @@ -0,0 +1,138 @@ +package blackjack.domain + +import io.kotest.assertions.fail +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +class BlackjackTest { + + @Test + fun `블랙잭 오픈 카드 공개`() { + val dealerInitCards = listOf(Card.diamond(Number.NINE), Card.diamond(Number.JACK)) + val jasonInitCards = listOf(Card.clover(Number.EIGHT), Card.clover(Number.TEN)) + val pobiInitCards = listOf(Card.heart(Number.ACE), Card.heart(Number.QUEEN)) + val cardDeck = cardDeck(dealerInitCards + jasonInitCards + pobiInitCards) + val blackjack = BlackJack(cardDeck, "jason", "pobi") + + val actual = blackjack.openCardsOfParticipant() + + assertThat(actual) + .containsEntry("딜러", listOf(Card.diamond(Number.NINE))) + .containsEntry("jason", listOf(Card.clover(Number.EIGHT), Card.clover(Number.TEN))) + .containsEntry("pobi", listOf(Card.heart(Number.ACE), Card.heart(Number.QUEEN))) + } + + @Test + fun `딜러 카드 얻기`() { + val dealerInitCards = listOf(Card.diamond(Number.FIVE), Card.diamond(Number.JACK)) + val jasonInitCards = listOf(Card.clover(Number.EIGHT), Card.clover(Number.TEN)) + val dealerAdditionCards = listOf(Card.diamond(Number.TWO)) + val cardDeck = cardDeck( + dealerInitCards + jasonInitCards + dealerAdditionCards + ) + val blackjack = BlackJack(cardDeck, "jason") + + val actual = blackjack.obtainDealerCard() + + assertThat(actual) + .isEqualTo(listOf(Card.diamond(Number.FIVE), Card.diamond(Number.JACK), Card.diamond(Number.TWO))) + } + + @ParameterizedTest + @MethodSource("dealerInitCardsForCheckObtainable") + fun `딜러 카드 획득 여부 확인`(dealerInitCards: List, expected: Boolean) { + val jasonInitCards = listOf(Card.clover(Number.EIGHT), Card.clover(Number.TEN)) + val cardDeck = cardDeck( + dealerInitCards + jasonInitCards + ) + val blackjack = BlackJack(cardDeck, "jason") + + val actual = blackjack.isDealerObtainable() + + assertThat(actual).isEqualTo(expected) + } + + @ParameterizedTest + @MethodSource("playerInitCardsForCheckObtainable") + fun `플레이어 카드 획득 여부 확인`(playerInitCards: List, expected: Boolean) { + val dealerInitCards = listOf(Card.clover(Number.EIGHT), Card.clover(Number.TEN)) + val cardDeck = cardDeck( + dealerInitCards + playerInitCards + ) + val blackjack = BlackJack(cardDeck, "jason") + + val actual = blackjack.isPlayerObtainable("jason") { true } + + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `플레이어가 카드를 획득할 수 없으면 획득 여부도 묻지 않는다`() { + val dealerInitCards = listOf(Card.clover(Number.EIGHT), Card.clover(Number.TEN)) + val playerInitCards = listOf(Card.diamond(Number.ACE), Card.diamond(Number.JACK)) + val cardDeck = cardDeck( + dealerInitCards + playerInitCards + ) + val blackjack = BlackJack(cardDeck, "jason") + + blackjack.isPlayerObtainable("jason") { fail("획득 여부를 묻지 않아야 합니다.") } + } + + @Test + fun `플레이어 카드 얻기`() { + val dealerInitCards = listOf(Card.diamond(Number.NINE), Card.diamond(Number.JACK)) + val jasonInitCards = listOf(Card.clover(Number.EIGHT), Card.clover(Number.TEN)) + val jasonAdditionCards = listOf(Card.clover(Number.TWO)) + val cardDeck = cardDeck( + dealerInitCards + jasonInitCards + jasonAdditionCards + ) + val blackjack = BlackJack(cardDeck, "jason") + + val actual = blackjack.obtainPlayerCard("jason") + + assertThat(actual) + .isEqualTo(listOf(Card.clover(Number.EIGHT), Card.clover(Number.TEN), Card.clover(Number.TWO))) + } + + @Test + fun `블랙잭 결과 확인`() { + val dealerInitCards = listOf(Card.diamond(Number.NINE), Card.diamond(Number.JACK)) + val jasonInitCards = listOf(Card.clover(Number.EIGHT), Card.clover(Number.TEN)) + val pobiInitCards = listOf(Card.heart(Number.ACE), Card.heart(Number.QUEEN)) + val cardDeck = cardDeck(dealerInitCards + jasonInitCards + pobiInitCards) + val blackjack = BlackJack(cardDeck, "jason", "pobi") + + val actual = blackjack.compareResults() + + assertThat(actual) + .containsEntry("jason", CompareResult.DEALER_WIN) + .containsEntry("pobi", CompareResult.DEALER_LOSE) + } + + + private fun cardDeck(cards: List): CardDeck { + val iterator = cards.iterator() + return CardDeck { iterator.next() } + } + + companion object { + @JvmStatic + fun dealerInitCardsForCheckObtainable(): List { + return listOf( + Arguments.of(listOf(Card.diamond(Number.SIX), Card.diamond(Number.JACK)), true), + Arguments.of(listOf(Card.diamond(Number.SEVEN), Card.diamond(Number.JACK)), false) + ) + } + + @JvmStatic + fun playerInitCardsForCheckObtainable(): List { + return listOf( + Arguments.of(listOf(Card.diamond(Number.QUEEN), Card.diamond(Number.JACK)), true), + Arguments.of(listOf(Card.diamond(Number.ACE), Card.diamond(Number.JACK)), false) + ) + } + } +} From cd65ff5270e8ecac9f01a0d0c53a365100cda3aa Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Fri, 29 Dec 2023 16:57:13 +0900 Subject: [PATCH 14/15] =?UTF-8?q?refactor:=20openCards=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/domain/BlackJack.kt | 2 +- src/main/kotlin/blackjack/domain/Dealer.kt | 6 ++++-- src/main/kotlin/blackjack/domain/Participant.kt | 2 +- src/main/kotlin/blackjack/domain/Player.kt | 6 ++++-- src/test/kotlin/blackjack/domain/DealerTest.kt | 4 ++-- src/test/kotlin/blackjack/domain/PlayerTest.kt | 4 ++-- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/blackjack/domain/BlackJack.kt b/src/main/kotlin/blackjack/domain/BlackJack.kt index 09ae72b640..ef258b5fef 100644 --- a/src/main/kotlin/blackjack/domain/BlackJack.kt +++ b/src/main/kotlin/blackjack/domain/BlackJack.kt @@ -10,7 +10,7 @@ class BlackJack( private val players = names.map { Player(it, cardDeck) } fun openCardsOfParticipant(): Map> { - return participants().associate { it.name to it.openedCards } + return participants().associate { it.name to it.openCards() } } fun isDealerObtainable(): Boolean { diff --git a/src/main/kotlin/blackjack/domain/Dealer.kt b/src/main/kotlin/blackjack/domain/Dealer.kt index 39d57cb971..c63c4e68fc 100644 --- a/src/main/kotlin/blackjack/domain/Dealer.kt +++ b/src/main/kotlin/blackjack/domain/Dealer.kt @@ -3,12 +3,14 @@ package blackjack.domain class Dealer( cardDeck: CardDeck ): Participant("딜러", cardDeck) { - override val openedCards = listOf(hands.first()) - override fun isObtainable(): Boolean { return sumOfCards() < 17 } + override fun openCards(): List { + return listOf(hands.first()) + } + fun compareWith(vararg players: Player): Map { return players.associate { it.name to compareWith(it) } } diff --git a/src/main/kotlin/blackjack/domain/Participant.kt b/src/main/kotlin/blackjack/domain/Participant.kt index 813c2a6c07..d6bc40fde2 100644 --- a/src/main/kotlin/blackjack/domain/Participant.kt +++ b/src/main/kotlin/blackjack/domain/Participant.kt @@ -5,7 +5,6 @@ abstract class Participant( private val cardDeck: CardDeck, ) { private val cards = Cards(cardDeck.next(), cardDeck.next()) - abstract val openedCards: List val hands get() = cards.values @@ -24,4 +23,5 @@ abstract class Participant( } abstract fun isObtainable(): Boolean + abstract fun openCards(): List } diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt index 5358a73992..cee9cddf9f 100644 --- a/src/main/kotlin/blackjack/domain/Player.kt +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -4,9 +4,11 @@ class Player ( name: String, cardDeck: CardDeck, ) : Participant(name, cardDeck) { - override val openedCards = hands.subList(0, 2) - override fun isObtainable(): Boolean { return sumOfCards() < BLACKJACK_SCORE } + + override fun openCards(): List { + return hands.subList(0, 2) + } } diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt index 629de7c19d..9a2ec119d9 100644 --- a/src/test/kotlin/blackjack/domain/DealerTest.kt +++ b/src/test/kotlin/blackjack/domain/DealerTest.kt @@ -13,7 +13,7 @@ class DealerTest { fun `딜러는 발급 받은 첫 카드만 공개한다`() { val dealer = Dealer(cardDeck(Card.diamond(Number.TEN), Card.heart(Number.FIVE))) - val actual = dealer.openedCards + val actual = dealer.openCards() assertThat(actual).containsExactly(Card.diamond(Number.TEN)) } @@ -24,7 +24,7 @@ class DealerTest { dealer.obtain() - assertThat(dealer.openedCards).containsExactly(Card.diamond(Number.TEN)) + assertThat(dealer.openCards()).containsExactly(Card.diamond(Number.TEN)) assertThat(dealer.hands).containsExactlyInAnyOrder( Card.diamond(Number.TEN), Card.heart(Number.SIX), Card.spade(Number.ACE) ) diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt index f6c09f7f6a..ee065ba1dd 100644 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -14,7 +14,7 @@ class PlayerTest { ) assertThat(player.name).isEqualTo("pobi") - assertThat(player.openedCards).containsExactlyInAnyOrder( + assertThat(player.openCards()).containsExactlyInAnyOrder( Card.diamond(Number.EIGHT), Card.heart(Number.TEN) ) assertThat(player.hands).containsExactlyInAnyOrder( @@ -30,7 +30,7 @@ class PlayerTest { player.obtain() - assertThat(player.openedCards).containsExactlyInAnyOrder( + assertThat(player.openCards()).containsExactlyInAnyOrder( Card.diamond(Number.QUEEN), Card.heart(Number.JACK) ) assertThat(player.hands).containsExactlyInAnyOrder( From 8c995acd8f401510e3edb830720c8e85a048cc12 Mon Sep 17 00:00:00 2001 From: "verus.j" Date: Fri, 29 Dec 2023 17:47:50 +0900 Subject: [PATCH 15/15] =?UTF-8?q?refactor:=20=EC=B0=B8=EA=B0=80=EC=9E=90?= =?UTF-8?q?=EC=97=90=EA=B2=8C=20Card=20=EC=A0=84=EB=8B=AC=ED=95=B4?= =?UTF-8?q?=EC=A3=BC=EB=8A=94=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/domain/BlackJack.kt | 8 ++-- src/main/kotlin/blackjack/domain/Dealer.kt | 5 +-- .../kotlin/blackjack/domain/Participant.kt | 9 ++--- src/main/kotlin/blackjack/domain/Player.kt | 8 ++-- .../kotlin/blackjack/domain/DealerTest.kt | 39 +++++++++---------- .../kotlin/blackjack/domain/PlayerTest.kt | 12 +++--- 6 files changed, 40 insertions(+), 41 deletions(-) diff --git a/src/main/kotlin/blackjack/domain/BlackJack.kt b/src/main/kotlin/blackjack/domain/BlackJack.kt index ef258b5fef..c796052f85 100644 --- a/src/main/kotlin/blackjack/domain/BlackJack.kt +++ b/src/main/kotlin/blackjack/domain/BlackJack.kt @@ -6,8 +6,8 @@ class BlackJack( private val cardDeck: CardDeck, vararg names: String ) { - private val dealer = Dealer(cardDeck) - private val players = names.map { Player(it, cardDeck) } + private val dealer = Dealer(cardDeck.next(), cardDeck.next()) + private val players = names.map { Player(it, cardDeck.next(), cardDeck.next()) } fun openCardsOfParticipant(): Map> { return participants().associate { it.name to it.openCards() } @@ -18,7 +18,7 @@ class BlackJack( } fun obtainDealerCard(): List { - dealer.obtain() + dealer.obtain(cardDeck.next()) return dealer.hands } @@ -29,7 +29,7 @@ class BlackJack( fun obtainPlayerCard(name: String): List { val player = players.first { it.name == name } - player.obtain() + player.obtain(cardDeck.next()) return player.hands } diff --git a/src/main/kotlin/blackjack/domain/Dealer.kt b/src/main/kotlin/blackjack/domain/Dealer.kt index c63c4e68fc..9b42cface7 100644 --- a/src/main/kotlin/blackjack/domain/Dealer.kt +++ b/src/main/kotlin/blackjack/domain/Dealer.kt @@ -1,8 +1,7 @@ package blackjack.domain -class Dealer( - cardDeck: CardDeck -): Participant("딜러", cardDeck) { +class Dealer(card1: Card, card2: Card): Participant("딜러", card1, card2) { + override fun isObtainable(): Boolean { return sumOfCards() < 17 } diff --git a/src/main/kotlin/blackjack/domain/Participant.kt b/src/main/kotlin/blackjack/domain/Participant.kt index d6bc40fde2..84bd4525c0 100644 --- a/src/main/kotlin/blackjack/domain/Participant.kt +++ b/src/main/kotlin/blackjack/domain/Participant.kt @@ -1,17 +1,16 @@ package blackjack.domain abstract class Participant( - val name: String, - private val cardDeck: CardDeck, + val name: String, card1: Card, card2: Card, ) { - private val cards = Cards(cardDeck.next(), cardDeck.next()) + private val cards = Cards(card1, card2) val hands get() = cards.values - fun obtain() { + fun obtain(card: Card) { require(isObtainable()) { "카드를 획득할 수 없습니다." } - cards.add(cardDeck.next()) + cards.add(card) } fun sumOfCards(): Int { diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt index cee9cddf9f..e8077bd13d 100644 --- a/src/main/kotlin/blackjack/domain/Player.kt +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -1,9 +1,11 @@ package blackjack.domain -class Player ( +class Player( name: String, - cardDeck: CardDeck, -) : Participant(name, cardDeck) { + card1: Card, + card2: Card, +) : Participant(name, card1, card2) { + override fun isObtainable(): Boolean { return sumOfCards() < BLACKJACK_SCORE } diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt index 9a2ec119d9..9c61bf62e3 100644 --- a/src/test/kotlin/blackjack/domain/DealerTest.kt +++ b/src/test/kotlin/blackjack/domain/DealerTest.kt @@ -5,13 +5,12 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource -import kotlin.math.exp class DealerTest { @Test fun `딜러는 발급 받은 첫 카드만 공개한다`() { - val dealer = Dealer(cardDeck(Card.diamond(Number.TEN), Card.heart(Number.FIVE))) + val dealer = Dealer(Card.diamond(Number.TEN), Card.heart(Number.FIVE)) val actual = dealer.openCards() @@ -20,9 +19,9 @@ class DealerTest { @Test fun `딜러는 16점 이하인 경우 카드를 발급 받을 수 있다`() { - val dealer = Dealer(cardDeck(Card.diamond(Number.TEN), Card.heart(Number.SIX), Card.spade(Number.ACE))) + val dealer = Dealer(Card.diamond(Number.TEN), Card.heart(Number.SIX)) - dealer.obtain() + dealer.obtain(Card.spade(Number.ACE)) assertThat(dealer.openCards()).containsExactly(Card.diamond(Number.TEN)) assertThat(dealer.hands).containsExactlyInAnyOrder( @@ -32,9 +31,9 @@ class DealerTest { @Test fun `딜러는 17점 이상인 경우 카드를 발급 받을 수 없다`() { - val dealer = Dealer(cardDeck(Card.diamond(Number.TEN), Card.heart(Number.SEVEN), Card.spade(Number.ACE))) + val dealer = Dealer(Card.diamond(Number.TEN), Card.heart(Number.SEVEN)) - assertThrows { dealer.obtain() } + assertThrows { dealer.obtain(Card.spade(Number.ACE)) } assertThat(dealer.hands).containsExactlyInAnyOrder( Card.diamond(Number.TEN), Card.heart(Number.SEVEN) ) @@ -43,7 +42,7 @@ class DealerTest { @ParameterizedTest @CsvSource(value = ["TEN, SIX, true", "TEN, SEVEN, false"]) fun `딜러는 카드 발급 여부를 확인할 수 있다`(num1: Number, num2: Number, expect: Boolean) { - val dealer = Dealer(cardDeck(Card.diamond(num1), Card.heart(num2))) + val dealer = Dealer(Card.diamond(num1), Card.heart(num2)) assertThat(dealer.isObtainable()).isEqualTo(expect) } @@ -53,12 +52,12 @@ class DealerTest { fun `딜러가 21이 넘으면 플레이어는 항상 승리한다`(num1: Number, num2: Number, num3: Number) { // arrange val dealer = Dealer( - cardDeck(Card.diamond(Number.TEN), Card.heart(Number.SIX), Card.spade(Number.SIX)) - ).apply { obtain() } + Card.diamond(Number.TEN), Card.heart(Number.SIX) + ).apply { obtain(Card.spade(Number.SIX)) } val player = Player( "player", - cardDeck(Card.spade(num1), Card.clover(num2), Card.heart(num3)) - ).apply { obtain() } + Card.spade(num1), Card.clover(num2) + ).apply { obtain(Card.heart(num3)) } // act val actual = dealer.compareWith(player) @@ -73,11 +72,11 @@ class DealerTest { fun `딜러와 플레이어가 21이하면 21에 더 가까운 플레이어가 승리한다`(num1: Number, num2: Number, expect: CompareResult) { // arrange val dealer = Dealer( - cardDeck(Card.diamond(Number.TEN), Card.heart(Number.TEN)) + Card.diamond(Number.TEN), Card.heart(Number.TEN) ) val player = Player( "player", - cardDeck(Card.spade(num1), Card.clover(num2)) + Card.spade(num1), Card.clover(num2) ) // act @@ -92,12 +91,12 @@ class DealerTest { fun `딜러는 21이하이고 플레이어가 21을 넘으면 딜러가 승리한다`() { // arrange val dealer = Dealer( - cardDeck(Card.diamond(Number.TEN), Card.heart(Number.ACE)) + Card.diamond(Number.TEN), Card.heart(Number.ACE) ) val player = Player( "player", - cardDeck(Card.spade(Number.TEN), Card.clover(Number.JACK), Card.heart(Number.TWO)) - ).apply { obtain() } + Card.spade(Number.TEN), Card.clover(Number.JACK) + ).apply { obtain(Card.heart(Number.TWO)) } // act val actual = dealer.compareWith(player) @@ -111,19 +110,19 @@ class DealerTest { fun `딜러는 여러 플레이어의 승패 결과를 수집하여 반환한다`() { // arrange val dealer = Dealer( - cardDeck(Card.diamond(Number.TEN), Card.diamond(Number.JACK)) + Card.diamond(Number.TEN), Card.diamond(Number.JACK) ) val player1 = Player( "player1", - cardDeck(Card.spade(Number.TEN), Card.spade(Number.ACE)) + Card.spade(Number.TEN), Card.spade(Number.ACE) ) val player2 = Player( "player2", - cardDeck(Card.clover(Number.TEN), Card.clover(Number.NINE)) + Card.clover(Number.TEN), Card.clover(Number.NINE) ) val player3 = Player( "player3", - cardDeck(Card.heart(Number.TEN), Card.heart(Number.JACK)) + Card.heart(Number.TEN), Card.heart(Number.JACK) ) // act diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt index ee065ba1dd..793f189ab2 100644 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -10,7 +10,7 @@ class PlayerTest { @Test fun `플레이어는 이름과 2장의 카드를 처음에 가진다`() { val player = Player( - "pobi", cardDeck(Card.diamond(Number.EIGHT), Card.heart(Number.TEN)) + "pobi", Card.diamond(Number.EIGHT), Card.heart(Number.TEN) ) assertThat(player.name).isEqualTo("pobi") @@ -25,10 +25,10 @@ class PlayerTest { @Test fun `21 미만이면 플레이어는 카드를 획득할 수 있다`() { val player = Player( - "pobi", cardDeck(Card.diamond(Number.QUEEN), Card.heart(Number.JACK), Card.spade(Number.ACE)) + "pobi", Card.diamond(Number.QUEEN), Card.heart(Number.JACK) ) - player.obtain() + player.obtain(Card.spade(Number.ACE)) assertThat(player.openCards()).containsExactlyInAnyOrder( Card.diamond(Number.QUEEN), Card.heart(Number.JACK) @@ -41,12 +41,12 @@ class PlayerTest { @Test fun `21 이상이면 플레이어는 카드를 획득할 수 없다`() { val player = Player( - "pobi", cardDeck(Card.diamond(Number.ACE), Card.heart(Number.JACK), Card.spade(Number.ACE)) + "pobi", Card.diamond(Number.ACE), Card.heart(Number.JACK) ) assertThat(player.isObtainable()).isFalse assertThrows { - player.obtain() + player.obtain(Card.spade(Number.ACE)) } assertThat(player.hands).containsExactlyInAnyOrder( Card.diamond(Number.ACE), Card.heart(Number.JACK) @@ -56,7 +56,7 @@ class PlayerTest { @Test fun `플레이어는 발급 받은 카드의 총합을 계산한다`() { val player = Player( - "pobi", cardDeck(Card.diamond(Number.EIGHT), Card.heart(Number.TEN)) + "pobi", Card.diamond(Number.EIGHT), Card.heart(Number.TEN) ) val actual = player.sumOfCards()