From fefa3de2bad98571a96f74a3a2876b60358c0282 Mon Sep 17 00:00:00 2001 From: wook Date: Mon, 16 Dec 2024 17:40:07 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat=20:=20=EB=B8=94=EB=9E=99=EC=9E=AD=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EB=8F=84=EB=A9=94=EC=9D=B8=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 --- README.md | 53 +++++++++++++- src/test/kotlin/blackjack/BlackJackTest.kt | 81 ++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/test/kotlin/blackjack/BlackJackTest.kt diff --git a/README.md b/README.md index b7ca2177d..5c729caf7 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,55 @@ introduce { "English" level 3 } } -``` \ No newline at end of file +``` + +### Step2: 블랙잭 + +##### `규칙` +- 블랙잭 게임을 변형한 프로그램을 구현한다. +- 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다. +- 카드의 숫자 계산은 카드 숫자를 기본, +- 예외로 Ace는 1 또는 11로 계산할 수 있으며, +- King, Queen, Jack은 각각 10으로 계산한다. +- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, +- 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. +- 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. + +##### `실행 결과` +```text +게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리) +pobi,jason + +pobi, jason에게 2장의 나누었습니다. +pobi카드: 2하트, 8스페이드 +jason카드: 7클로버, K스페이드 + +pobi는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +y +pobi카드: 2하트, 8스페이드, A클로버 +pobi는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +n +jason은 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +n +jason카드: 7클로버, K스페이드 + +pobi카드: 2하트, 8스페이드, A클로버 - 결과: 21 +jason카드: 7클로버, K스페이드 - 결과: 17 +``` + +##### `요구사항 정리` +- 카드 + - [ ] 카드는 각각 총 13개의 랭크와, 4개의 무늬(mark or suits)가 있다 + - [x] rank : 2, 3, 4, 5, 6, 7, 8, 9, 10, A(Ace), K(King), Q(Queen), J(Jack) + - [x] suits : spade(♠️), heart(🖤), diamond(♦️), club(♣️) + - [x] rank 는 포인트를 가진다 + - [x] 숫자 : 각 숫자, Ace : 1 또는 11, King, Queen, Jack : 10 + - [x] 총 52개의 카드가 무작위 순서로 생성 할 수 있다 + - [x] 카드들의 포인트를 더할 수 있다 +- 플레이어 + - [ ] 게임에 참여하는 사람을 직접 입력 받는다 + - [ ] 첫번째 라운드는 랜덤한 두개의 카드를 지급 받는다 + - [ ] 다음 라운드부터 카드를 더 받을지 선택 할 수 있다 +- 승리자 + - [ ] 카드들의 점수들을 비교하여 21점에 근접한 사람이 승리한다 + \ No newline at end of file diff --git a/src/test/kotlin/blackjack/BlackJackTest.kt b/src/test/kotlin/blackjack/BlackJackTest.kt new file mode 100644 index 000000000..c43ed5e54 --- /dev/null +++ b/src/test/kotlin/blackjack/BlackJackTest.kt @@ -0,0 +1,81 @@ +package blackjack + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class BlackJackTest { + @Test + fun `에이스가 아닌 카드의 포인트를 합산 할 수 있다`() { + val card1 = Card(rank = Ranks.TWO, suits = Suits.SPADE) + val card2 = Card(rank = Ranks.SIX, suits = Suits.HEART) + val cards = Cards(listOf(card1, card2)) + cards.calculatePoints() shouldBe 8 + } + + @Test + fun `포인트를 합산 할때 21보다 크지 않다면 에이스 카드는 11점으로 계산한다`() { + val card1 = Card(rank = Ranks.ACE, suits = Suits.SPADE) + val card2 = Card(rank = Ranks.TEN, suits = Suits.HEART) + val cards = Cards(listOf(card1, card2)) + cards.calculatePoints() shouldBe 21 + } + + @Test + fun `포인트를 합산할때 21점을 초과하면 에이스 카드는 1점으로 계산한다`() { + val card1 = Card(rank = Ranks.ACE, suits = Suits.SPADE) + val card2 = Card(rank = Ranks.EIGHT, suits = Suits.HEART) + val card3 = Card(rank = Ranks.TEN, suits = Suits.HEART) + val cards = Cards(listOf(card1, card2, card3)) + cards.calculatePoints() shouldBe 19 + } + + @Test + fun `전체 카드의 수는 52이다`() { + Cards.create().size shouldBe 52 + } +} + +data class Card(val rank: Ranks, val suits: Suits) + +enum class Ranks(val points: List) { + ACE(listOf(1, 11)), + TWO(listOf(2)), + THREE(listOf(3)), + FOUR(listOf(4)), + FIVE(listOf(5)), + SIX(listOf(6)), + SEVEN(listOf(7)), + EIGHT(listOf(8)), + NINE(listOf(9)), + TEN(listOf(10)), + JACK(listOf(10)), + QUEEN(listOf(10)), + KING(listOf(10)),; +} + +enum class Suits { + SPADE, + HEART, + DIAMOND, + CLUB, +} + +class Cards(private val cards: List) : Collection by cards { + fun calculatePoints(): Int { + val basePoints = cards.sumOf { it.rank.points[0] } + val hasAce = cards.any { it.rank == Ranks.ACE } + if (hasAce && basePoints + 10 <= 21) { + return basePoints + 10 + } + return basePoints + } + + companion object { + fun create(): Cards { + val allCards = Suits.entries.flatMap { suit -> + Ranks.entries.map { rank -> Card(rank, suit) } + } + return Cards(allCards.shuffled()) + } + } +} \ No newline at end of file From bd1e7bee369bc0be8a70e8f893a5b072c52f5e71 Mon Sep 17 00:00:00 2001 From: wook Date: Mon, 16 Dec 2024 23:34:18 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat=20:=20Cards=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EB=B6=84=EB=A6=AC,=20Player=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++- src/test/kotlin/blackjack/BlackJackTest.kt | 106 ++++++++++++++++++--- 2 files changed, 110 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5c729caf7..115fed189 100644 --- a/README.md +++ b/README.md @@ -64,9 +64,21 @@ jason카드: 7클로버, K스페이드 - 결과: 17 - [x] 총 52개의 카드가 무작위 순서로 생성 할 수 있다 - [x] 카드들의 포인트를 더할 수 있다 - 플레이어 - - [ ] 게임에 참여하는 사람을 직접 입력 받는다 - - [ ] 첫번째 라운드는 랜덤한 두개의 카드를 지급 받는다 - - [ ] 다음 라운드부터 카드를 더 받을지 선택 할 수 있다 + - [x] 플레이어는 카드를 추가 할 수 있다 + - [x] 플레이어의 카드의 포인트를 합산 할 수 있다 + - [x] 카드 드로우를 중지하면 더이상 카드를 받을 수 없다 +- 컨트롤러 + - [ ] 게임을 시작하면 게임에 참여할 사람들 입력 요청한다 + - [ ] 게임을 시작하면 무작위 전체 카드를 생성한다 + - [ ] 게임 참여자들에게 2장씩 카드를 지급한다 + - [ ] 모든 참여자가 카드 추가 지급을 거절하면 게임을 종료하고 합산결과 출력 요청한다 - 승리자 - [ ] 카드들의 점수들을 비교하여 21점에 근접한 사람이 승리한다 +- InputView + - [ ] 게임에 참여하는 사람을 직접 입력 받는다 + - [ ] 카드를 더 받을지 입력받는다 +- OutputView + - [ ] 게임 참여자들에게 처음 지급된 카드 리스트를 출력한다 + - [ ] 카드를 더 받으면 지급된 카드와 함께 전체 카드 리스트를 출력한다 + - [ ] 모든 사용자의 카드 리스트와 포인트 합산 결과를 출력한다 \ No newline at end of file diff --git a/src/test/kotlin/blackjack/BlackJackTest.kt b/src/test/kotlin/blackjack/BlackJackTest.kt index c43ed5e54..ce6882660 100644 --- a/src/test/kotlin/blackjack/BlackJackTest.kt +++ b/src/test/kotlin/blackjack/BlackJackTest.kt @@ -1,23 +1,25 @@ package blackjack +import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test +import java.util.* class BlackJackTest { @Test fun `에이스가 아닌 카드의 포인트를 합산 할 수 있다`() { val card1 = Card(rank = Ranks.TWO, suits = Suits.SPADE) val card2 = Card(rank = Ranks.SIX, suits = Suits.HEART) - val cards = Cards(listOf(card1, card2)) - cards.calculatePoints() shouldBe 8 + val userCards = UserCards(mutableListOf(card1, card2)) + userCards.calculatePoints() shouldBe 8 } @Test fun `포인트를 합산 할때 21보다 크지 않다면 에이스 카드는 11점으로 계산한다`() { val card1 = Card(rank = Ranks.ACE, suits = Suits.SPADE) val card2 = Card(rank = Ranks.TEN, suits = Suits.HEART) - val cards = Cards(listOf(card1, card2)) - cards.calculatePoints() shouldBe 21 + val userCards = UserCards(mutableListOf(card1, card2)) + userCards.calculatePoints() shouldBe 21 } @Test @@ -25,13 +27,56 @@ class BlackJackTest { val card1 = Card(rank = Ranks.ACE, suits = Suits.SPADE) val card2 = Card(rank = Ranks.EIGHT, suits = Suits.HEART) val card3 = Card(rank = Ranks.TEN, suits = Suits.HEART) - val cards = Cards(listOf(card1, card2, card3)) - cards.calculatePoints() shouldBe 19 + val userCards = UserCards(mutableListOf(card1, card2, card3)) + userCards.calculatePoints() shouldBe 19 } @Test fun `전체 카드의 수는 52이다`() { - Cards.create().size shouldBe 52 + val gameCards = GameCards.create() + gameCards.size() shouldBe 52 + } + + @Test + fun `게임 카드를 모두 드로우 하면 전체 덱의 사이즈는 0이다`() { + val gameCards = GameCards.create() + repeat(gameCards.size()) { + gameCards.drawCard() + } + gameCards.size() shouldBe 0 + } + + @Test + fun `플레이어는 드로우 된 카드를 가질 수 있다`() { + val gameCards = GameCards.create() + val card = gameCards.drawCard() + val player = Player("lee") + player.receiveCard(Card(rank = Ranks.TEN, suits = Suits.CLUB)) + player.receiveCard(Card(rank = Ranks.TWO, suits = Suits.DIAMOND)) + player.receiveCard(Card(rank = Ranks.FIVE, suits = Suits.HEART)) + player.cardSize() shouldBe 3 + } + + @Test + fun `플레이어의 카드 점수를 합산 할 수 있다`() { + val gameCards = GameCards.create() + val card = gameCards.drawCard() + val player = Player("lee") + player.receiveCard(Card(rank = Ranks.TEN, suits = Suits.CLUB)) + player.receiveCard(Card(rank = Ranks.TWO, suits = Suits.DIAMOND)) + player.receiveCard(Card(rank = Ranks.FIVE, suits = Suits.HEART)) + player.calculateCardPoints() shouldBe 17 + } + + @Test + fun `카드 드로우 중지 요청하면 플레이어는 카드를 받을수 없다`() { + val player = Player("lee") + player.stopCardDraw() + shouldThrow { + player.receiveCard(Card(rank = Ranks.SIX, suits = Suits.CLUB)) + }.also { + it.message shouldBe "카드를 받을 수 없습니다" + } } } @@ -60,7 +105,7 @@ enum class Suits { CLUB, } -class Cards(private val cards: List) : Collection by cards { +class UserCards(private val cards: MutableList) : Collection by cards { fun calculatePoints(): Int { val basePoints = cards.sumOf { it.rank.points[0] } val hasAce = cards.any { it.rank == Ranks.ACE } @@ -70,12 +115,51 @@ class Cards(private val cards: List) : Collection by cards { return basePoints } + fun addCard(card: Card) { + cards.add(card) + } +} + +class GameCards private constructor(private val deck: Queue) { + + fun drawCard(): Card { + return deck.poll() + } + + fun size(): Int { + return deck.size + } + companion object { - fun create(): Cards { + fun create(): GameCards { val allCards = Suits.entries.flatMap { suit -> Ranks.entries.map { rank -> Card(rank, suit) } } - return Cards(allCards.shuffled()) + return GameCards(LinkedList(allCards.shuffled())) } } -} \ No newline at end of file +} + +class Player(val name: String) { + private var userCards = UserCards(mutableListOf()) + private var isDrawContinue: Boolean = true + + fun receiveCard(card: Card) { + require(isDrawContinue) { "카드를 받을 수 없습니다" } + userCards.addCard(card) + } + + fun cardSize(): Int { + return userCards.size + } + + fun calculateCardPoints(): Int { + return userCards.calculatePoints() + } + + fun stopCardDraw() { + isDrawContinue = false + } +} + + From 768e53dc6e2130a9e2b1c06ee8dba33b76c2be4a Mon Sep 17 00:00:00 2001 From: wook Date: Mon, 16 Dec 2024 23:50:38 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat=20:=20Player=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/blackjack/BlackJackTest.kt | 28 ++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/blackjack/BlackJackTest.kt b/src/test/kotlin/blackjack/BlackJackTest.kt index ce6882660..0e456b6d9 100644 --- a/src/test/kotlin/blackjack/BlackJackTest.kt +++ b/src/test/kotlin/blackjack/BlackJackTest.kt @@ -78,6 +78,20 @@ class BlackJackTest { it.message shouldBe "카드를 받을 수 없습니다" } } + + @Test + fun `게임에 참여할 사람들의 이름을 입력 받을 수 있다`() { + val players = Players.create("kim,lee,hong") + players.size shouldBe 3 + } +} + +object UserInput { + fun enterParticipatingPlayers(): Players { + println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)") + val playerNames = readln() + return Players.create(playerNames) + } } data class Card(val rank: Ranks, val suits: Suits) @@ -140,9 +154,8 @@ class GameCards private constructor(private val deck: Queue) { } } -class Player(val name: String) { +class Player(val name: String, private var isDrawContinue: Boolean = true) { private var userCards = UserCards(mutableListOf()) - private var isDrawContinue: Boolean = true fun receiveCard(card: Card) { require(isDrawContinue) { "카드를 받을 수 없습니다" } @@ -162,4 +175,15 @@ class Player(val name: String) { } } +data class Players(private val players: List) : Collection by players { + companion object { + private const val DELIMITER = "," + + fun create(playerNames: String): Players { + val players = playerNames.split(DELIMITER).map { playerName -> Player(playerName, true) } + return Players(players) + } + } +} + From fa0faebc0d5292845dfe361ef82b87e844ba7e1c Mon Sep 17 00:00:00 2001 From: wook Date: Tue, 17 Dec 2024 00:13:39 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat=20:=20=EA=B2=8C=EC=9E=84=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=91=20=EC=B0=B8=EC=97=AC=EC=9E=90=EB=93=A4=EC=97=90?= =?UTF-8?q?=EA=B2=8C=20=EC=B9=B4=EB=93=9C=202=EC=9E=A5=EC=94=A9=20?= =?UTF-8?q?=EC=A7=80=EA=B8=89=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++--- src/test/kotlin/blackjack/BlackJackTest.kt | 35 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 115fed189..aee84d9d2 100644 --- a/README.md +++ b/README.md @@ -67,15 +67,16 @@ jason카드: 7클로버, K스페이드 - 결과: 17 - [x] 플레이어는 카드를 추가 할 수 있다 - [x] 플레이어의 카드의 포인트를 합산 할 수 있다 - [x] 카드 드로우를 중지하면 더이상 카드를 받을 수 없다 + - [x] 쉼표로 구분된 이름 문자열을 전달하면 플레이어를 생성 할 수 있다 - 컨트롤러 - - [ ] 게임을 시작하면 게임에 참여할 사람들 입력 요청한다 - - [ ] 게임을 시작하면 무작위 전체 카드를 생성한다 - - [ ] 게임 참여자들에게 2장씩 카드를 지급한다 + - [x] 게임을 시작하면 게임에 참여할 사람들 입력 요청한다 + - [x] 게임을 시작하면 무작위 전체 카드를 생성한다 + - [x] 게임 참여자들에게 2장씩 카드를 지급한다 - [ ] 모든 참여자가 카드 추가 지급을 거절하면 게임을 종료하고 합산결과 출력 요청한다 - 승리자 - [ ] 카드들의 점수들을 비교하여 21점에 근접한 사람이 승리한다 - InputView - - [ ] 게임에 참여하는 사람을 직접 입력 받는다 + - [x] 게임에 참여하는 사람을 직접 입력 받는다 - [ ] 카드를 더 받을지 입력받는다 - OutputView - [ ] 게임 참여자들에게 처음 지급된 카드 리스트를 출력한다 diff --git a/src/test/kotlin/blackjack/BlackJackTest.kt b/src/test/kotlin/blackjack/BlackJackTest.kt index 0e456b6d9..09b0c6438 100644 --- a/src/test/kotlin/blackjack/BlackJackTest.kt +++ b/src/test/kotlin/blackjack/BlackJackTest.kt @@ -84,6 +84,41 @@ class BlackJackTest { val players = Players.create("kim,lee,hong") players.size shouldBe 3 } + + @Test + fun `게임을 시작하면 각 플레이어들은 2장씩 카드를 지급 받는다`() { + val players = Players.create("kim,lee,hong") + val gameCards = GameCards.create() + val game = BlackJackGame(players, gameCards) + game.startGame() + players.size shouldBe 3 + players.sumOf { player -> player.cardSize() } shouldBe 6 + players.forEach { + it.cardSize() shouldBe 2 + } + } +} + +class BlackJackGame( + private val players: Players, + private val gameCards: GameCards +) { + fun startGame() { + repeat(FIRST_ROUND_HAND_SIZE) { + giveOutCards() + } + } + + private fun giveOutCards() { + players.forEach { players -> + players.receiveCard(gameCards.drawCard()) + } + } + + companion object { + private const val FIRST_ROUND_HAND_SIZE = 2 + } + } object UserInput { From 784bc4da5cbad74ecb808d88b720687e0453e78e Mon Sep 17 00:00:00 2001 From: wook Date: Tue, 17 Dec 2024 01:23:29 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat=20:=20InputView,=20OutputView=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- src/test/kotlin/blackjack/BlackJackTest.kt | 122 ++++++++++++++------- 2 files changed, 85 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index aee84d9d2..ea2bce590 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ jason카드: 7클로버, K스페이드 - 결과: 17 - [x] 게임에 참여하는 사람을 직접 입력 받는다 - [ ] 카드를 더 받을지 입력받는다 - OutputView - - [ ] 게임 참여자들에게 처음 지급된 카드 리스트를 출력한다 + - [x] 게임 참여자들에게 처음 지급된 카드 리스트를 출력한다 + - [x] 모든 사용자의 카드 리스트와 포인트 합산 결과를 출력한다 - [ ] 카드를 더 받으면 지급된 카드와 함께 전체 카드 리스트를 출력한다 - - [ ] 모든 사용자의 카드 리스트와 포인트 합산 결과를 출력한다 \ No newline at end of file diff --git a/src/test/kotlin/blackjack/BlackJackTest.kt b/src/test/kotlin/blackjack/BlackJackTest.kt index 09b0c6438..ecb0f20bc 100644 --- a/src/test/kotlin/blackjack/BlackJackTest.kt +++ b/src/test/kotlin/blackjack/BlackJackTest.kt @@ -3,7 +3,8 @@ package blackjack import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test -import java.util.* +import java.util.LinkedList +import java.util.Queue class BlackJackTest { @Test @@ -48,19 +49,17 @@ class BlackJackTest { @Test fun `플레이어는 드로우 된 카드를 가질 수 있다`() { - val gameCards = GameCards.create() - val card = gameCards.drawCard() val player = Player("lee") - player.receiveCard(Card(rank = Ranks.TEN, suits = Suits.CLUB)) - player.receiveCard(Card(rank = Ranks.TWO, suits = Suits.DIAMOND)) - player.receiveCard(Card(rank = Ranks.FIVE, suits = Suits.HEART)) - player.cardSize() shouldBe 3 + val gameCards = GameCards.create() + val card1 = gameCards.drawCard() + player.receiveCard(card1) + val card2 = gameCards.drawCard() + player.receiveCard(card2) + player.cardSize() shouldBe 2 } @Test fun `플레이어의 카드 점수를 합산 할 수 있다`() { - val gameCards = GameCards.create() - val card = gameCards.drawCard() val player = Player("lee") player.receiveCard(Card(rank = Ranks.TEN, suits = Suits.CLUB)) player.receiveCard(Card(rank = Ranks.TWO, suits = Suits.DIAMOND)) @@ -83,6 +82,7 @@ class BlackJackTest { fun `게임에 참여할 사람들의 이름을 입력 받을 수 있다`() { val players = Players.create("kim,lee,hong") players.size shouldBe 3 + players.getPlayerNames() shouldBe listOf("kim", "lee", "hong") } @Test @@ -101,7 +101,7 @@ class BlackJackTest { class BlackJackGame( private val players: Players, - private val gameCards: GameCards + private val gameCards: GameCards, ) { fun startGame() { repeat(FIRST_ROUND_HAND_SIZE) { @@ -118,10 +118,9 @@ class BlackJackGame( companion object { private const val FIRST_ROUND_HAND_SIZE = 2 } - } -object UserInput { +object InputView { fun enterParticipatingPlayers(): Players { println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)") val playerNames = readln() @@ -129,29 +128,59 @@ object UserInput { } } +object OutputView { + private const val PLAYER_NAME_DELIMITER = ", " + private const val BLANK_PREFIX_MESSAGE = "" + private const val POST_MESSAGE = "에게 2장의 나누었습니다." + private const val RESULT_EXPRESSION = " - 결과: " + private const val NAME_POSTFIX_EXPRESSION = ": " + + fun printFirstAllPlayersCards(players: Players) { + println() + val result = + players.getPlayerNames().joinToString( + PLAYER_NAME_DELIMITER, + BLANK_PREFIX_MESSAGE, + POST_MESSAGE, + ) + println(result) + players.forEach { player -> + print(player.name + NAME_POSTFIX_EXPRESSION) + print(player.findAllCardsNames().joinToString(PLAYER_NAME_DELIMITER)) + print(RESULT_EXPRESSION) + println(player.calculateCardPoints()) + } + } +} + data class Card(val rank: Ranks, val suits: Suits) -enum class Ranks(val points: List) { - ACE(listOf(1, 11)), - TWO(listOf(2)), - THREE(listOf(3)), - FOUR(listOf(4)), - FIVE(listOf(5)), - SIX(listOf(6)), - SEVEN(listOf(7)), - EIGHT(listOf(8)), - NINE(listOf(9)), - TEN(listOf(10)), - JACK(listOf(10)), - QUEEN(listOf(10)), - KING(listOf(10)),; +enum class Ranks( + val keyword: String, + val points: List, +) { + ACE("A", listOf(1, 11)), + TWO("2", listOf(2)), + THREE("3", listOf(3)), + FOUR("4", listOf(4)), + FIVE("5", listOf(5)), + SIX("6", listOf(6)), + SEVEN("7", listOf(7)), + EIGHT("8", listOf(8)), + NINE("9", listOf(9)), + TEN("10", listOf(10)), + JACK("J", listOf(10)), + QUEEN("Q", listOf(10)), + KING("K", listOf(10)), } -enum class Suits { - SPADE, - HEART, - DIAMOND, - CLUB, +enum class Suits( + val koreanName: String, +) { + SPADE("스페이드"), + HEART("하트"), + DIAMOND("다이어"), + CLUB("클로버"), } class UserCards(private val cards: MutableList) : Collection by cards { @@ -170,7 +199,6 @@ class UserCards(private val cards: MutableList) : Collection by card } class GameCards private constructor(private val deck: Queue) { - fun drawCard(): Card { return deck.poll() } @@ -181,9 +209,10 @@ class GameCards private constructor(private val deck: Queue) { companion object { fun create(): GameCards { - val allCards = Suits.entries.flatMap { suit -> - Ranks.entries.map { rank -> Card(rank, suit) } - } + val allCards = + Suits.entries.flatMap { suit -> + Ranks.entries.map { rank -> Card(rank, suit) } + } return GameCards(LinkedList(allCards.shuffled())) } } @@ -194,7 +223,7 @@ class Player(val name: String, private var isDrawContinue: Boolean = true) { fun receiveCard(card: Card) { require(isDrawContinue) { "카드를 받을 수 없습니다" } - userCards.addCard(card) + userCards.addCard(card) } fun cardSize(): Int { @@ -208,17 +237,32 @@ class Player(val name: String, private var isDrawContinue: Boolean = true) { fun stopCardDraw() { isDrawContinue = false } + + fun findAllCardsNames(): List { + return userCards.map { card -> + card.rank.keyword + card.suits.koreanName + } + } } -data class Players(private val players: List) : Collection by players { +data class Players(val players: List) : Collection by players { + fun getPlayerNames(): List { + return players.map { it.name } + } + companion object { private const val DELIMITER = "," fun create(playerNames: String): Players { - val players = playerNames.split(DELIMITER).map { playerName -> Player(playerName, true) } + val players = playerNames.split(DELIMITER).map { playerName -> Player(playerName) } return Players(players) } } } - +fun main() { + val players = InputView.enterParticipatingPlayers() + val blackJackGame = BlackJackGame(players, GameCards.create()) + blackJackGame.startGame() + OutputView.printFirstAllPlayersCards(players) +} From a8bd02e20aba99cdb7a7bc0c6756b7cb57a4df72 Mon Sep 17 00:00:00 2001 From: wook Date: Tue, 17 Dec 2024 03:14:42 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat=20:=20=EC=B9=B4=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=A7=80=EA=B8=89=20=EC=97=AC=EB=B6=80=20=ED=8C=90?= =?UTF-8?q?=EB=8B=A8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +-- src/test/kotlin/blackjack/BlackJackTest.kt | 76 ++++++++++++++++++++-- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ea2bce590..5395cd4fb 100644 --- a/README.md +++ b/README.md @@ -72,14 +72,12 @@ jason카드: 7클로버, K스페이드 - 결과: 17 - [x] 게임을 시작하면 게임에 참여할 사람들 입력 요청한다 - [x] 게임을 시작하면 무작위 전체 카드를 생성한다 - [x] 게임 참여자들에게 2장씩 카드를 지급한다 - - [ ] 모든 참여자가 카드 추가 지급을 거절하면 게임을 종료하고 합산결과 출력 요청한다 -- 승리자 - - [ ] 카드들의 점수들을 비교하여 21점에 근접한 사람이 승리한다 + - [x] 모든 참여자가 카드 추가 지급을 거절하면 게임을 종료하고 합산결과 출력 요청한다 - InputView - [x] 게임에 참여하는 사람을 직접 입력 받는다 - - [ ] 카드를 더 받을지 입력받는다 + - [x] 카드를 더 받을지 입력받는다 - OutputView - [x] 게임 참여자들에게 처음 지급된 카드 리스트를 출력한다 - [x] 모든 사용자의 카드 리스트와 포인트 합산 결과를 출력한다 - - [ ] 카드를 더 받으면 지급된 카드와 함께 전체 카드 리스트를 출력한다 + - [x] 카드를 더 받으면 지급된 카드와 함께 전체 카드 리스트를 출력한다 \ No newline at end of file diff --git a/src/test/kotlin/blackjack/BlackJackTest.kt b/src/test/kotlin/blackjack/BlackJackTest.kt index ecb0f20bc..a375b4429 100644 --- a/src/test/kotlin/blackjack/BlackJackTest.kt +++ b/src/test/kotlin/blackjack/BlackJackTest.kt @@ -115,17 +115,48 @@ class BlackJackGame( } } + fun handlePlayerDraw(player: Player, gameCards: GameCards) { + val previousCardCount = player.cardSize() + InputView.enterIsContinueDrawCard(player) + if (player.isDrawContinue) { + player.receiveCard(gameCards.drawCard()) + OutputView.printPlayerCard(player) + handlePlayerDraw(player, gameCards) // 재귀 호출 + } + + if (!player.isDrawContinue && player.cardSize() == previousCardCount) { + OutputView.printPlayerCard(player) + } + } + companion object { private const val FIRST_ROUND_HAND_SIZE = 2 } } object InputView { + private const val CONTINUE_OR_STOP_MESSAGE = "는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)" + fun enterParticipatingPlayers(): Players { println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)") val playerNames = readln() return Players.create(playerNames) } + + fun enterIsContinueDrawCard(player: Player) { + val userInput = getIsContinueDraw(player) + when (userInput.lowercase()) { + "n" -> player.stopCardDraw() + "y" -> player.continueCardDraw() + else -> println("잘못된 입력입니다. 다시 입력해주세요.") + } + } + + fun getIsContinueDraw(player: Player): String { + println() + println(player.name + CONTINUE_OR_STOP_MESSAGE) + return readln() + } } object OutputView { @@ -133,7 +164,7 @@ object OutputView { private const val BLANK_PREFIX_MESSAGE = "" private const val POST_MESSAGE = "에게 2장의 나누었습니다." private const val RESULT_EXPRESSION = " - 결과: " - private const val NAME_POSTFIX_EXPRESSION = ": " + private const val NAME_POSTFIX_EXPRESSION = "카드: " fun printFirstAllPlayersCards(players: Players) { println() @@ -145,12 +176,34 @@ object OutputView { ) println(result) players.forEach { player -> - print(player.name + NAME_POSTFIX_EXPRESSION) - print(player.findAllCardsNames().joinToString(PLAYER_NAME_DELIMITER)) - print(RESULT_EXPRESSION) - println(player.calculateCardPoints()) + printPlayerAllCards(player) + println() } } + + fun printFinalResults(players: Players) { + println() + println() + println("게임 종료!") + players.forEach { player -> + printPlayerAllCards(player) + resultExpression(player) + } + } + + fun printPlayerCard(player: Player) { + printPlayerAllCards(player) + } + + private fun printPlayerAllCards(player: Player) { + print(player.name + NAME_POSTFIX_EXPRESSION) + print(player.findAllCardsNames().joinToString(PLAYER_NAME_DELIMITER)) + } + + private fun resultExpression(player: Player) { + print(RESULT_EXPRESSION) + println(player.calculateCardPoints()) + } } data class Card(val rank: Ranks, val suits: Suits) @@ -218,7 +271,7 @@ class GameCards private constructor(private val deck: Queue) { } } -class Player(val name: String, private var isDrawContinue: Boolean = true) { +class Player(val name: String, var isDrawContinue: Boolean = true) { private var userCards = UserCards(mutableListOf()) fun receiveCard(card: Card) { @@ -238,6 +291,10 @@ class Player(val name: String, private var isDrawContinue: Boolean = true) { isDrawContinue = false } + fun continueCardDraw() { + isDrawContinue = true + } + fun findAllCardsNames(): List { return userCards.map { card -> card.rank.keyword + card.suits.koreanName @@ -262,7 +319,12 @@ data class Players(val players: List) : Collection by players { fun main() { val players = InputView.enterParticipatingPlayers() - val blackJackGame = BlackJackGame(players, GameCards.create()) + val gameCards = GameCards.create() + val blackJackGame = BlackJackGame(players, gameCards) blackJackGame.startGame() OutputView.printFirstAllPlayersCards(players) + players.forEach { player -> + blackJackGame.handlePlayerDraw(player, gameCards) + } + OutputView.printFinalResults(players) } From bfb1ce2cc905eb03ea675853ad3ed92112bbfda4 Mon Sep 17 00:00:00 2001 From: wook Date: Tue, 17 Dec 2024 03:24:04 +0900 Subject: [PATCH 7/7] =?UTF-8?q?refactor=20:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/blackjack/BlackJackApplcation.kt | 18 ++ src/main/kotlin/blackjack/UserCards.kt | 19 ++ .../kotlin/blackjack/domain/BlackJackGame.kt | 42 +++ src/main/kotlin/blackjack/domain/Card.kt | 3 + src/main/kotlin/blackjack/domain/GameCards.kt | 24 ++ src/main/kotlin/blackjack/domain/Player.kt | 34 +++ src/main/kotlin/blackjack/domain/Players.kt | 16 ++ src/main/kotlin/blackjack/domain/Ranks.kt | 20 ++ src/main/kotlin/blackjack/domain/Suits.kt | 10 + src/main/kotlin/blackjack/view/InputView.kt | 29 +++ src/main/kotlin/blackjack/view/OutputView.kt | 51 ++++ src/test/kotlin/blackjack/BlackJackTest.kt | 239 +----------------- 12 files changed, 273 insertions(+), 232 deletions(-) create mode 100644 src/main/kotlin/blackjack/BlackJackApplcation.kt create mode 100644 src/main/kotlin/blackjack/UserCards.kt create mode 100644 src/main/kotlin/blackjack/domain/BlackJackGame.kt create mode 100644 src/main/kotlin/blackjack/domain/Card.kt create mode 100644 src/main/kotlin/blackjack/domain/GameCards.kt create mode 100644 src/main/kotlin/blackjack/domain/Player.kt create mode 100644 src/main/kotlin/blackjack/domain/Players.kt create mode 100644 src/main/kotlin/blackjack/domain/Ranks.kt create mode 100644 src/main/kotlin/blackjack/domain/Suits.kt create mode 100644 src/main/kotlin/blackjack/view/InputView.kt create mode 100644 src/main/kotlin/blackjack/view/OutputView.kt diff --git a/src/main/kotlin/blackjack/BlackJackApplcation.kt b/src/main/kotlin/blackjack/BlackJackApplcation.kt new file mode 100644 index 000000000..e6f78396f --- /dev/null +++ b/src/main/kotlin/blackjack/BlackJackApplcation.kt @@ -0,0 +1,18 @@ +package blackjack + +import blackjack.domain.BlackJackGame +import blackjack.domain.GameCards +import blackjack.view.InputView +import blackjack.view.OutputView + +fun main() { + val players = InputView.enterParticipatingPlayers() + val gameCards = GameCards.create() + val blackJackGame = BlackJackGame(players, gameCards) + blackJackGame.startGame() + OutputView.printFirstAllPlayersCards(players) + players.forEach { player -> + blackJackGame.handlePlayerDraw(player, gameCards) + } + OutputView.printFinalResults(players) +} diff --git a/src/main/kotlin/blackjack/UserCards.kt b/src/main/kotlin/blackjack/UserCards.kt new file mode 100644 index 000000000..31ffc45fd --- /dev/null +++ b/src/main/kotlin/blackjack/UserCards.kt @@ -0,0 +1,19 @@ +package blackjack + +import blackjack.domain.Card +import blackjack.domain.Ranks + +class UserCards(private val cards: MutableList) : Collection by cards { + fun calculatePoints(): Int { + val basePoints = cards.sumOf { it.rank.points[0] } + val hasAce = cards.any { it.rank == Ranks.ACE } + if (hasAce && basePoints + 10 <= 21) { + return basePoints + 10 + } + return basePoints + } + + fun addCard(card: Card) { + cards.add(card) + } +} diff --git a/src/main/kotlin/blackjack/domain/BlackJackGame.kt b/src/main/kotlin/blackjack/domain/BlackJackGame.kt new file mode 100644 index 000000000..5f9bfd9cd --- /dev/null +++ b/src/main/kotlin/blackjack/domain/BlackJackGame.kt @@ -0,0 +1,42 @@ +package blackjack.domain + +import blackjack.view.InputView +import blackjack.view.OutputView + +class BlackJackGame( + private val players: Players, + private val gameCards: GameCards, +) { + fun startGame() { + repeat(FIRST_ROUND_HAND_SIZE) { + giveOutCards() + } + } + + private fun giveOutCards() { + players.forEach { players -> + players.receiveCard(gameCards.drawCard()) + } + } + + fun handlePlayerDraw( + player: Player, + gameCards: GameCards, + ) { + val previousCardCount = player.cardSize() + InputView.enterIsContinueDrawCard(player) + if (player.isDrawContinue) { + player.receiveCard(gameCards.drawCard()) + OutputView.printPlayerCard(player) + handlePlayerDraw(player, gameCards) // 재귀 호출 + } + + if (!player.isDrawContinue && player.cardSize() == previousCardCount) { + OutputView.printPlayerCard(player) + } + } + + companion object { + private const val FIRST_ROUND_HAND_SIZE = 2 + } +} diff --git a/src/main/kotlin/blackjack/domain/Card.kt b/src/main/kotlin/blackjack/domain/Card.kt new file mode 100644 index 000000000..d5632d54a --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Card.kt @@ -0,0 +1,3 @@ +package blackjack.domain + +data class Card(val rank: Ranks, val suits: Suits) diff --git a/src/main/kotlin/blackjack/domain/GameCards.kt b/src/main/kotlin/blackjack/domain/GameCards.kt new file mode 100644 index 000000000..853b36e17 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/GameCards.kt @@ -0,0 +1,24 @@ +package blackjack.domain + +import java.util.LinkedList +import java.util.Queue + +class GameCards private constructor(private val deck: Queue) { + fun drawCard(): Card { + return deck.poll() + } + + fun size(): Int { + return deck.size + } + + companion object { + fun create(): GameCards { + val allCards = + Suits.entries.flatMap { suit -> + Ranks.entries.map { rank -> Card(rank, suit) } + } + return GameCards(LinkedList(allCards.shuffled())) + } + } +} diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt new file mode 100644 index 000000000..4de3482d9 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -0,0 +1,34 @@ +package blackjack.domain + +import blackjack.UserCards + +class Player(val name: String, var isDrawContinue: Boolean = true) { + private var userCards = UserCards(mutableListOf()) + + fun receiveCard(card: Card) { + require(isDrawContinue) { "카드를 받을 수 없습니다" } + userCards.addCard(card) + } + + fun cardSize(): Int { + return userCards.size + } + + fun calculateCardPoints(): Int { + return userCards.calculatePoints() + } + + fun stopCardDraw() { + isDrawContinue = false + } + + fun continueCardDraw() { + isDrawContinue = true + } + + fun findAllCardsNames(): List { + return userCards.map { card -> + card.rank.keyword + card.suits.koreanName + } + } +} diff --git a/src/main/kotlin/blackjack/domain/Players.kt b/src/main/kotlin/blackjack/domain/Players.kt new file mode 100644 index 000000000..bc4f6412a --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Players.kt @@ -0,0 +1,16 @@ +package blackjack.domain + +data class Players(val players: List) : Collection by players { + fun getPlayerNames(): List { + return players.map { it.name } + } + + companion object { + private const val DELIMITER = "," + + fun create(playerNames: String): Players { + val players = playerNames.split(DELIMITER).map { playerName -> Player(playerName) } + return Players(players) + } + } +} diff --git a/src/main/kotlin/blackjack/domain/Ranks.kt b/src/main/kotlin/blackjack/domain/Ranks.kt new file mode 100644 index 000000000..e8018be27 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Ranks.kt @@ -0,0 +1,20 @@ +package blackjack.domain + +enum class Ranks( + val keyword: String, + val points: List, +) { + ACE("A", listOf(1, 11)), + TWO("2", listOf(2)), + THREE("3", listOf(3)), + FOUR("4", listOf(4)), + FIVE("5", listOf(5)), + SIX("6", listOf(6)), + SEVEN("7", listOf(7)), + EIGHT("8", listOf(8)), + NINE("9", listOf(9)), + TEN("10", listOf(10)), + JACK("J", listOf(10)), + QUEEN("Q", listOf(10)), + KING("K", listOf(10)), +} diff --git a/src/main/kotlin/blackjack/domain/Suits.kt b/src/main/kotlin/blackjack/domain/Suits.kt new file mode 100644 index 000000000..d1fba6583 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Suits.kt @@ -0,0 +1,10 @@ +package blackjack.domain + +enum class Suits( + val koreanName: String, +) { + SPADE("스페이드"), + HEART("하트"), + DIAMOND("다이어"), + CLUB("클로버"), +} diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt new file mode 100644 index 000000000..8d5d95c72 --- /dev/null +++ b/src/main/kotlin/blackjack/view/InputView.kt @@ -0,0 +1,29 @@ +package blackjack.view + +import blackjack.domain.Player +import blackjack.domain.Players + +object InputView { + private const val CONTINUE_OR_STOP_MESSAGE = "는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)" + + fun enterParticipatingPlayers(): Players { + println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)") + val playerNames = readln() + return Players.create(playerNames) + } + + fun enterIsContinueDrawCard(player: Player) { + val userInput = getIsContinueDraw(player) + when (userInput.lowercase()) { + "n" -> player.stopCardDraw() + "y" -> player.continueCardDraw() + else -> println("잘못된 입력입니다. 다시 입력해주세요.") + } + } + + fun getIsContinueDraw(player: Player): String { + println() + println(player.name + CONTINUE_OR_STOP_MESSAGE) + return readln() + } +} diff --git a/src/main/kotlin/blackjack/view/OutputView.kt b/src/main/kotlin/blackjack/view/OutputView.kt new file mode 100644 index 000000000..93a5bf1a6 --- /dev/null +++ b/src/main/kotlin/blackjack/view/OutputView.kt @@ -0,0 +1,51 @@ +package blackjack.view + +import blackjack.domain.Player +import blackjack.domain.Players + +object OutputView { + private const val PLAYER_NAME_DELIMITER = ", " + private const val BLANK_PREFIX_MESSAGE = "" + private const val POST_MESSAGE = "에게 2장의 나누었습니다." + private const val RESULT_EXPRESSION = " - 결과: " + private const val NAME_POSTFIX_EXPRESSION = "카드: " + + fun printFirstAllPlayersCards(players: Players) { + println() + val result = + players.getPlayerNames().joinToString( + PLAYER_NAME_DELIMITER, + BLANK_PREFIX_MESSAGE, + POST_MESSAGE, + ) + println(result) + players.forEach { player -> + printPlayerAllCards(player) + println() + } + } + + fun printFinalResults(players: Players) { + println() + println() + println("게임 종료!") + players.forEach { player -> + printPlayerAllCards(player) + resultExpression(player) + } + } + + fun printPlayerCard(player: Player) { + printPlayerAllCards(player) + } + + private fun printPlayerAllCards(player: Player) { + print(player.name + NAME_POSTFIX_EXPRESSION) + print(player.findAllCardsNames().joinToString(PLAYER_NAME_DELIMITER)) + } + + private fun resultExpression(player: Player) { + print(RESULT_EXPRESSION) + println(player.calculateCardPoints()) + } +} diff --git a/src/test/kotlin/blackjack/BlackJackTest.kt b/src/test/kotlin/blackjack/BlackJackTest.kt index a375b4429..7c7a4dcb1 100644 --- a/src/test/kotlin/blackjack/BlackJackTest.kt +++ b/src/test/kotlin/blackjack/BlackJackTest.kt @@ -1,10 +1,15 @@ package blackjack +import blackjack.domain.BlackJackGame +import blackjack.domain.Card +import blackjack.domain.GameCards +import blackjack.domain.Player +import blackjack.domain.Players +import blackjack.domain.Ranks +import blackjack.domain.Suits import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test -import java.util.LinkedList -import java.util.Queue class BlackJackTest { @Test @@ -98,233 +103,3 @@ class BlackJackTest { } } } - -class BlackJackGame( - private val players: Players, - private val gameCards: GameCards, -) { - fun startGame() { - repeat(FIRST_ROUND_HAND_SIZE) { - giveOutCards() - } - } - - private fun giveOutCards() { - players.forEach { players -> - players.receiveCard(gameCards.drawCard()) - } - } - - fun handlePlayerDraw(player: Player, gameCards: GameCards) { - val previousCardCount = player.cardSize() - InputView.enterIsContinueDrawCard(player) - if (player.isDrawContinue) { - player.receiveCard(gameCards.drawCard()) - OutputView.printPlayerCard(player) - handlePlayerDraw(player, gameCards) // 재귀 호출 - } - - if (!player.isDrawContinue && player.cardSize() == previousCardCount) { - OutputView.printPlayerCard(player) - } - } - - companion object { - private const val FIRST_ROUND_HAND_SIZE = 2 - } -} - -object InputView { - private const val CONTINUE_OR_STOP_MESSAGE = "는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)" - - fun enterParticipatingPlayers(): Players { - println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)") - val playerNames = readln() - return Players.create(playerNames) - } - - fun enterIsContinueDrawCard(player: Player) { - val userInput = getIsContinueDraw(player) - when (userInput.lowercase()) { - "n" -> player.stopCardDraw() - "y" -> player.continueCardDraw() - else -> println("잘못된 입력입니다. 다시 입력해주세요.") - } - } - - fun getIsContinueDraw(player: Player): String { - println() - println(player.name + CONTINUE_OR_STOP_MESSAGE) - return readln() - } -} - -object OutputView { - private const val PLAYER_NAME_DELIMITER = ", " - private const val BLANK_PREFIX_MESSAGE = "" - private const val POST_MESSAGE = "에게 2장의 나누었습니다." - private const val RESULT_EXPRESSION = " - 결과: " - private const val NAME_POSTFIX_EXPRESSION = "카드: " - - fun printFirstAllPlayersCards(players: Players) { - println() - val result = - players.getPlayerNames().joinToString( - PLAYER_NAME_DELIMITER, - BLANK_PREFIX_MESSAGE, - POST_MESSAGE, - ) - println(result) - players.forEach { player -> - printPlayerAllCards(player) - println() - } - } - - fun printFinalResults(players: Players) { - println() - println() - println("게임 종료!") - players.forEach { player -> - printPlayerAllCards(player) - resultExpression(player) - } - } - - fun printPlayerCard(player: Player) { - printPlayerAllCards(player) - } - - private fun printPlayerAllCards(player: Player) { - print(player.name + NAME_POSTFIX_EXPRESSION) - print(player.findAllCardsNames().joinToString(PLAYER_NAME_DELIMITER)) - } - - private fun resultExpression(player: Player) { - print(RESULT_EXPRESSION) - println(player.calculateCardPoints()) - } -} - -data class Card(val rank: Ranks, val suits: Suits) - -enum class Ranks( - val keyword: String, - val points: List, -) { - ACE("A", listOf(1, 11)), - TWO("2", listOf(2)), - THREE("3", listOf(3)), - FOUR("4", listOf(4)), - FIVE("5", listOf(5)), - SIX("6", listOf(6)), - SEVEN("7", listOf(7)), - EIGHT("8", listOf(8)), - NINE("9", listOf(9)), - TEN("10", listOf(10)), - JACK("J", listOf(10)), - QUEEN("Q", listOf(10)), - KING("K", listOf(10)), -} - -enum class Suits( - val koreanName: String, -) { - SPADE("스페이드"), - HEART("하트"), - DIAMOND("다이어"), - CLUB("클로버"), -} - -class UserCards(private val cards: MutableList) : Collection by cards { - fun calculatePoints(): Int { - val basePoints = cards.sumOf { it.rank.points[0] } - val hasAce = cards.any { it.rank == Ranks.ACE } - if (hasAce && basePoints + 10 <= 21) { - return basePoints + 10 - } - return basePoints - } - - fun addCard(card: Card) { - cards.add(card) - } -} - -class GameCards private constructor(private val deck: Queue) { - fun drawCard(): Card { - return deck.poll() - } - - fun size(): Int { - return deck.size - } - - companion object { - fun create(): GameCards { - val allCards = - Suits.entries.flatMap { suit -> - Ranks.entries.map { rank -> Card(rank, suit) } - } - return GameCards(LinkedList(allCards.shuffled())) - } - } -} - -class Player(val name: String, var isDrawContinue: Boolean = true) { - private var userCards = UserCards(mutableListOf()) - - fun receiveCard(card: Card) { - require(isDrawContinue) { "카드를 받을 수 없습니다" } - userCards.addCard(card) - } - - fun cardSize(): Int { - return userCards.size - } - - fun calculateCardPoints(): Int { - return userCards.calculatePoints() - } - - fun stopCardDraw() { - isDrawContinue = false - } - - fun continueCardDraw() { - isDrawContinue = true - } - - fun findAllCardsNames(): List { - return userCards.map { card -> - card.rank.keyword + card.suits.koreanName - } - } -} - -data class Players(val players: List) : Collection by players { - fun getPlayerNames(): List { - return players.map { it.name } - } - - companion object { - private const val DELIMITER = "," - - fun create(playerNames: String): Players { - val players = playerNames.split(DELIMITER).map { playerName -> Player(playerName) } - return Players(players) - } - } -} - -fun main() { - val players = InputView.enterParticipatingPlayers() - val gameCards = GameCards.create() - val blackJackGame = BlackJackGame(players, gameCards) - blackJackGame.startGame() - OutputView.printFirstAllPlayersCards(players) - players.forEach { player -> - blackJackGame.handlePlayerDraw(player, gameCards) - } - OutputView.printFinalResults(players) -}