From f301a6c67c924219da785232fc5faa2fbfdc4c81 Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sat, 7 Dec 2024 21:13:22 +0900 Subject: [PATCH 01/15] =?UTF-8?q?chore:=20Blackjack=20=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +++++++++++++- src/main/kotlin/blackjack/Main.kt | 4 ++-- .../{BlackJackGame.kt => BlackjackGame.kt} | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) rename src/main/kotlin/blackjack/controller/{BlackJackGame.kt => BlackjackGame.kt} (98%) diff --git a/README.md b/README.md index dd6e2bb9a..282da85b4 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,16 @@ - [x] 덱에서 카드를 하나 꺼낸다. - [x] 유저는 카드 목록을 보유한다. - [x] 카드목록에서 점수를 계산한다. -- [x] 카드를 덱에서 하나 뽑는다. \ No newline at end of file +- [x] 카드를 덱에서 하나 뽑는다. + +## Step3 - 블랙잭(딜러) + +### Step2 - 리뷰반영사항 +- [ ] BlackJackGame::game table 이 users 와 deck 을 상태로서 관리하도록 수정 +- [ ] BlackJackGame::start 메서드 분리 +- [ ] BlackJackGame::allCardUsersCard 한번에 card 출력하도록 수정 +- [ ] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합 +- [ ] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합 +- [ ] BlackJackGame::게임진행책임 분리 +- [ ] BlackJackGame::receive 를 hit 로 메서드명 수정 +- [ ] Rank::enum 클래스로 수정 \ No newline at end of file diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt index 865246b0a..bad06fe69 100644 --- a/src/main/kotlin/blackjack/Main.kt +++ b/src/main/kotlin/blackjack/Main.kt @@ -1,10 +1,10 @@ package blackjack -import blackjack.controller.BlackJackGame +import blackjack.controller.BlackjackGame import blackjack.domain.GameTable import blackjack.view.InputView import blackjack.view.ResultView fun main() { - BlackJackGame(GameTable, InputView, ResultView).start() + BlackjackGame(GameTable, InputView, ResultView).start() } diff --git a/src/main/kotlin/blackjack/controller/BlackJackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt similarity index 98% rename from src/main/kotlin/blackjack/controller/BlackJackGame.kt rename to src/main/kotlin/blackjack/controller/BlackjackGame.kt index 3595c1016..9d7bb60e9 100644 --- a/src/main/kotlin/blackjack/controller/BlackJackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -6,7 +6,7 @@ import blackjack.domain.User import blackjack.view.InputView import blackjack.view.ResultView -class BlackJackGame( +class BlackjackGame( private val gameTable: GameTable, private val inputView: InputView, private val resultView: ResultView, From 6658a35bf48ee6aecaf7b89e3ca881980fd41651 Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sat, 7 Dec 2024 21:22:10 +0900 Subject: [PATCH 02/15] =?UTF-8?q?refactor:=20game=20table=20=EC=9D=B4=20us?= =?UTF-8?q?ers=20=EC=99=80=20deck=20=EC=9D=84=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=A1=9C=EC=84=9C=20=EA=B4=80=EB=A6=AC=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/main/kotlin/blackjack/Main.kt | 3 +-- .../kotlin/blackjack/controller/BlackjackGame.kt | 14 +++++++------- src/main/kotlin/blackjack/domain/GameTable.kt | 16 +++++++++------- src/main/kotlin/blackjack/view/ResultView.kt | 2 +- .../kotlin/blackjack/domain/GameTableTest.kt | 8 ++------ .../kotlin/blackjack/fixtures/UserFixtures.kt | 8 ++++++++ 7 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 src/test/kotlin/blackjack/fixtures/UserFixtures.kt diff --git a/README.md b/README.md index 282da85b4..f31920a62 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ ## Step3 - 블랙잭(딜러) ### Step2 - 리뷰반영사항 -- [ ] BlackJackGame::game table 이 users 와 deck 을 상태로서 관리하도록 수정 +- [x] BlackJackGame::game table 이 users 와 deck 을 상태로서 관리하도록 수정 - [ ] BlackJackGame::start 메서드 분리 - [ ] BlackJackGame::allCardUsersCard 한번에 card 출력하도록 수정 - [ ] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합 diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt index bad06fe69..6a4dee803 100644 --- a/src/main/kotlin/blackjack/Main.kt +++ b/src/main/kotlin/blackjack/Main.kt @@ -1,10 +1,9 @@ package blackjack import blackjack.controller.BlackjackGame -import blackjack.domain.GameTable import blackjack.view.InputView import blackjack.view.ResultView fun main() { - BlackjackGame(GameTable, InputView, ResultView).start() + BlackjackGame(InputView, ResultView).start() } diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index 9d7bb60e9..a26b0879c 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -7,16 +7,12 @@ import blackjack.view.InputView import blackjack.view.ResultView class BlackjackGame( - private val gameTable: GameTable, private val inputView: InputView, private val resultView: ResultView, ) { fun start() { - val names = inputView.inputNames() - val deck = Deck.create() - val users = names.map { User.create(name = it) } - - val initCardReceivedUsers = gameTable.dealInitCard(users, deck) + val gameTable = GameTable(getUsers(), Deck.create()) + val initCardReceivedUsers = gameTable.dealInitCard() println() resultView.printInitCardReceive(initCardReceivedUsers) @@ -33,7 +29,7 @@ class BlackjackGame( resultView.printAskReceiveMoreCard(currentUser) val moreCard = inputView.inputReceiveMoreCard() if (moreCard) { - currentUser = currentUser.receiveCard(deck.draw()) + currentUser = currentUser.receiveCard(Deck.create().draw()) resultView.printUserCards(user = currentUser, printScore = false) } else { break @@ -47,4 +43,8 @@ class BlackjackGame( resultView.printUserCards(user = user, printScore = true) } } + + private fun getUsers(): List { + return inputView.inputNames().map { User.create(name = it) } + } } diff --git a/src/main/kotlin/blackjack/domain/GameTable.kt b/src/main/kotlin/blackjack/domain/GameTable.kt index c56f577b2..2472a5aa1 100644 --- a/src/main/kotlin/blackjack/domain/GameTable.kt +++ b/src/main/kotlin/blackjack/domain/GameTable.kt @@ -1,16 +1,18 @@ package blackjack.domain -object GameTable { - const val INIT_CARD_DRAW_COUNT = 2 - - fun dealInitCard( - users: List, - deck: Deck, - ): List { +class GameTable( + private val users: List, + private val deck: Deck, +) { + fun dealInitCard(): List { return users.map { user -> (1..INIT_CARD_DRAW_COUNT).fold(user) { acc, _ -> acc.receiveCard(deck.draw()) } } } + + companion object { + const val INIT_CARD_DRAW_COUNT = 2 + } } diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt index 56430ad2c..a3957194a 100644 --- a/src/main/kotlin/blackjack/view/ResultView.kt +++ b/src/main/kotlin/blackjack/view/ResultView.kt @@ -1,6 +1,6 @@ package blackjack.view -import blackjack.domain.GameTable.INIT_CARD_DRAW_COUNT +import blackjack.domain.GameTable.Companion.INIT_CARD_DRAW_COUNT import blackjack.domain.User object ResultView { diff --git a/src/test/kotlin/blackjack/domain/GameTableTest.kt b/src/test/kotlin/blackjack/domain/GameTableTest.kt index 44d328492..b18c6c070 100644 --- a/src/test/kotlin/blackjack/domain/GameTableTest.kt +++ b/src/test/kotlin/blackjack/domain/GameTableTest.kt @@ -1,16 +1,12 @@ package blackjack.domain +import blackjack.fixtures.createUsers import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe class GameTableTest : StringSpec({ "최초 딜 시 카드를 2장 나누어준다" { - val users = - listOf( - User("홍길동", Cards(emptyList())), - User("홍길덩", Cards(emptyList())), - ) - val initCardReceivedUsers = GameTable.dealInitCard(users, Deck.create()) + val initCardReceivedUsers = GameTable(createUsers(), Deck.create()).dealInitCard() initCardReceivedUsers.forEach { it.cards.values.size shouldBe 2 diff --git a/src/test/kotlin/blackjack/fixtures/UserFixtures.kt b/src/test/kotlin/blackjack/fixtures/UserFixtures.kt new file mode 100644 index 000000000..296f051b4 --- /dev/null +++ b/src/test/kotlin/blackjack/fixtures/UserFixtures.kt @@ -0,0 +1,8 @@ +package blackjack.fixtures + +import blackjack.domain.Cards +import blackjack.domain.User + +fun createUsers(names: List = listOf("홍길동", "홍길덩")): List { + return names.map { User(it, Cards(emptyList())) } +} From fd73ec17f7861a1e746137e6293756877eb84009 Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sat, 7 Dec 2024 21:32:39 +0900 Subject: [PATCH 03/15] =?UTF-8?q?refactor:=20start=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../blackjack/controller/BlackjackGame.kt | 26 ++++++++++++++----- src/main/kotlin/blackjack/view/ResultView.kt | 1 + 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f31920a62..eab01e204 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ ### Step2 - 리뷰반영사항 - [x] BlackJackGame::game table 이 users 와 deck 을 상태로서 관리하도록 수정 -- [ ] BlackJackGame::start 메서드 분리 +- [x] BlackJackGame::start 메서드 분리 - [ ] BlackJackGame::allCardUsersCard 한번에 card 출력하도록 수정 - [ ] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합 - [ ] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합 diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index a26b0879c..0d853101a 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -12,12 +12,27 @@ class BlackjackGame( ) { fun start() { val gameTable = GameTable(getUsers(), Deck.create()) - val initCardReceivedUsers = gameTable.dealInitCard() + val allCardReceivedUsers = playGame(gameTable) + printGameResult(allCardReceivedUsers) + } - println() + private fun getUsers(): List { + return inputView.inputNames().map { User.create(name = it) } + } + + private fun playGame(gameTable: GameTable): List { + val initCardReceivedUsers = setUpInitCard(gameTable) + return processTurn(initCardReceivedUsers) + } + + private fun setUpInitCard(gameTable: GameTable): List { + val initCardReceivedUsers = gameTable.dealInitCard() resultView.printInitCardReceive(initCardReceivedUsers) initCardReceivedUsers.forEach { resultView.printUserCards(user = it, printScore = false) } + return initCardReceivedUsers + } + private fun processTurn(initCardReceivedUsers: List): List { val allCardReceivedUsers = initCardReceivedUsers.map { user -> var currentUser = user @@ -37,14 +52,13 @@ class BlackjackGame( } currentUser } + return allCardReceivedUsers + } + private fun printGameResult(allCardReceivedUsers: List) { println() allCardReceivedUsers.forEach { user -> resultView.printUserCards(user = user, printScore = true) } } - - private fun getUsers(): List { - return inputView.inputNames().map { User.create(name = it) } - } } diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt index a3957194a..3d03aeb92 100644 --- a/src/main/kotlin/blackjack/view/ResultView.kt +++ b/src/main/kotlin/blackjack/view/ResultView.kt @@ -5,6 +5,7 @@ import blackjack.domain.User object ResultView { fun printInitCardReceive(users: List) { + println() println("${users.joinToString(", ") { it.name }}에게 ${INIT_CARD_DRAW_COUNT}장의 카드를 나누었습니다.") } From dee9976d5987cbbb1012f814d6b72f79ccb7df80 Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sat, 7 Dec 2024 22:42:51 +0900 Subject: [PATCH 04/15] =?UTF-8?q?refactor:=20=EC=9C=A0=EC=A0=80=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=EC=9D=84=20=EB=B0=9B=EC=95=84=20card=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../kotlin/blackjack/controller/BlackjackGame.kt | 11 +++++------ src/main/kotlin/blackjack/view/ResultView.kt | 14 ++++++++++++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index eab01e204..ce14f5963 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ ### Step2 - 리뷰반영사항 - [x] BlackJackGame::game table 이 users 와 deck 을 상태로서 관리하도록 수정 - [x] BlackJackGame::start 메서드 분리 -- [ ] BlackJackGame::allCardUsersCard 한번에 card 출력하도록 수정 +- [x] BlackJackGame::유저목록을 받아 card 출력하도록 수정 - [ ] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합 - [ ] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합 - [ ] BlackJackGame::게임진행책임 분리 diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index 0d853101a..631fad2a9 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -27,8 +27,9 @@ class BlackjackGame( private fun setUpInitCard(gameTable: GameTable): List { val initCardReceivedUsers = gameTable.dealInitCard() + resultView.linebreak() resultView.printInitCardReceive(initCardReceivedUsers) - initCardReceivedUsers.forEach { resultView.printUserCards(user = it, printScore = false) } + resultView.printUsersCard(users = initCardReceivedUsers, printScore = false) return initCardReceivedUsers } @@ -45,7 +46,7 @@ class BlackjackGame( val moreCard = inputView.inputReceiveMoreCard() if (moreCard) { currentUser = currentUser.receiveCard(Deck.create().draw()) - resultView.printUserCards(user = currentUser, printScore = false) + resultView.printUserCard(user = currentUser, printScore = false) } else { break } @@ -56,9 +57,7 @@ class BlackjackGame( } private fun printGameResult(allCardReceivedUsers: List) { - println() - allCardReceivedUsers.forEach { user -> - resultView.printUserCards(user = user, printScore = true) - } + resultView.linebreak() + resultView.printUsersCard(users = allCardReceivedUsers, printScore = true) } } diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt index 3d03aeb92..418c8f524 100644 --- a/src/main/kotlin/blackjack/view/ResultView.kt +++ b/src/main/kotlin/blackjack/view/ResultView.kt @@ -5,11 +5,17 @@ import blackjack.domain.User object ResultView { fun printInitCardReceive(users: List) { - println() println("${users.joinToString(", ") { it.name }}에게 ${INIT_CARD_DRAW_COUNT}장의 카드를 나누었습니다.") } - fun printUserCards( + fun printUsersCard( + users: List, + printScore: Boolean, + ) { + users.forEach { printUserCard(user = it, printScore = printScore) } + } + + fun printUserCard( user: User, printScore: Boolean, ) { @@ -25,4 +31,8 @@ object ResultView { fun printCanNotReceivedCard() { println("더 이상 카드를 받을 수 없습니다") } + + fun linebreak() { + println() + } } From 97d81cd8c132b73a3c9124e9594ee3bd6b972aa7 Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sat, 7 Dec 2024 22:46:53 +0900 Subject: [PATCH 05/15] =?UTF-8?q?refactor:=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=ED=9E=88=ED=8A=B8=20=EC=97=AC=EB=B6=80=ED=99=95=EC=9D=B8?= =?UTF-8?q?=EC=8B=9C=20=EC=B6=9C=EB=A0=A5=EA=B3=BC=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EC=9D=84=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/main/kotlin/blackjack/controller/BlackjackGame.kt | 11 +++++------ src/main/kotlin/blackjack/view/InputView.kt | 5 ++++- src/main/kotlin/blackjack/view/ResultView.kt | 4 ---- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ce14f5963..d9e486497 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ - [x] BlackJackGame::game table 이 users 와 deck 을 상태로서 관리하도록 수정 - [x] BlackJackGame::start 메서드 분리 - [x] BlackJackGame::유저목록을 받아 card 출력하도록 수정 -- [ ] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합 +- [x] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합 - [ ] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합 - [ ] BlackJackGame::게임진행책임 분리 - [ ] BlackJackGame::receive 를 hit 로 메서드명 수정 diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index 631fad2a9..6dcbbbb36 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -33,17 +33,16 @@ class BlackjackGame( return initCardReceivedUsers } - private fun processTurn(initCardReceivedUsers: List): List { + private fun processTurn(users: List): List { val allCardReceivedUsers = - initCardReceivedUsers.map { user -> + users.map { user -> var currentUser = user while (true) { if (!currentUser.canReceiveCard()) { resultView.printCanNotReceivedCard() break } - resultView.printAskReceiveMoreCard(currentUser) - val moreCard = inputView.inputReceiveMoreCard() + val moreCard = inputView.inputReceiveMoreCard(currentUser) if (moreCard) { currentUser = currentUser.receiveCard(Deck.create().draw()) resultView.printUserCard(user = currentUser, printScore = false) @@ -56,8 +55,8 @@ class BlackjackGame( return allCardReceivedUsers } - private fun printGameResult(allCardReceivedUsers: List) { + private fun printGameResult(users: List) { resultView.linebreak() - resultView.printUsersCard(users = allCardReceivedUsers, printScore = true) + resultView.printUsersCard(users = users, printScore = true) } } diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt index 12bbe7f90..ffb2e76de 100644 --- a/src/main/kotlin/blackjack/view/InputView.kt +++ b/src/main/kotlin/blackjack/view/InputView.kt @@ -1,5 +1,7 @@ package blackjack.view +import blackjack.domain.User + object InputView { fun inputNames(): List { println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)") @@ -10,7 +12,8 @@ object InputView { return names } - fun inputReceiveMoreCard(): Boolean { + fun inputReceiveMoreCard(user: User): Boolean { + println("${user.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") return when (readlnOrNull()) { "y" -> true "n" -> false diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt index 418c8f524..a04bd5c23 100644 --- a/src/main/kotlin/blackjack/view/ResultView.kt +++ b/src/main/kotlin/blackjack/view/ResultView.kt @@ -24,10 +24,6 @@ object ResultView { println("${user.name}카드: $cards ${if (printScore) scoreText else ""}") } - fun printAskReceiveMoreCard(user: User) { - println("${user.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") - } - fun printCanNotReceivedCard() { println("더 이상 카드를 받을 수 없습니다") } From ed857a003f8607429b6fc70242c590f6026f3bcc Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sat, 7 Dec 2024 22:52:00 +0900 Subject: [PATCH 06/15] =?UTF-8?q?refactor:=20while=20=EB=AC=B8=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=20if=20=EC=A0=88=EC=9D=84=20while=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../blackjack/controller/BlackjackGame.kt | 19 +++++-------------- src/main/kotlin/blackjack/view/ResultView.kt | 4 ---- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index d9e486497..bac88f06e 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ - [x] BlackJackGame::start 메서드 분리 - [x] BlackJackGame::유저목록을 받아 card 출력하도록 수정 - [x] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합 -- [ ] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합 +- [x] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합 - [ ] BlackJackGame::게임진행책임 분리 - [ ] BlackJackGame::receive 를 hit 로 메서드명 수정 - [ ] Rank::enum 클래스로 수정 \ No newline at end of file diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index 6dcbbbb36..b8824786a 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -12,8 +12,8 @@ class BlackjackGame( ) { fun start() { val gameTable = GameTable(getUsers(), Deck.create()) - val allCardReceivedUsers = playGame(gameTable) - printGameResult(allCardReceivedUsers) + val users = playGame(gameTable) + printGameResult(users) } private fun getUsers(): List { @@ -37,18 +37,9 @@ class BlackjackGame( val allCardReceivedUsers = users.map { user -> var currentUser = user - while (true) { - if (!currentUser.canReceiveCard()) { - resultView.printCanNotReceivedCard() - break - } - val moreCard = inputView.inputReceiveMoreCard(currentUser) - if (moreCard) { - currentUser = currentUser.receiveCard(Deck.create().draw()) - resultView.printUserCard(user = currentUser, printScore = false) - } else { - break - } + while (currentUser.canReceiveCard() && inputView.inputReceiveMoreCard(currentUser)) { + currentUser = currentUser.receiveCard(Deck.create().draw()) + resultView.printUserCard(user = currentUser, printScore = false) } currentUser } diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt index a04bd5c23..5ce495ec4 100644 --- a/src/main/kotlin/blackjack/view/ResultView.kt +++ b/src/main/kotlin/blackjack/view/ResultView.kt @@ -24,10 +24,6 @@ object ResultView { println("${user.name}카드: $cards ${if (printScore) scoreText else ""}") } - fun printCanNotReceivedCard() { - println("더 이상 카드를 받을 수 없습니다") - } - fun linebreak() { println() } From 2c50b72993267c9431515ed61d905e34e2c05ede Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sat, 7 Dec 2024 23:00:12 +0900 Subject: [PATCH 07/15] =?UTF-8?q?refactor:=20=EA=B2=8C=EC=9E=84=EC=A7=84?= =?UTF-8?q?=ED=96=89=EC=B1=85=EC=9E=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../blackjack/controller/BlackjackGame.kt | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index bac88f06e..099882aac 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,6 @@ - [x] BlackJackGame::유저목록을 받아 card 출력하도록 수정 - [x] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합 - [x] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합 -- [ ] BlackJackGame::게임진행책임 분리 +- [x] BlackJackGame::게임진행책임 분리 - [ ] BlackJackGame::receive 를 hit 로 메서드명 수정 - [ ] Rank::enum 클래스로 수정 \ No newline at end of file diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index b8824786a..27aba4fcb 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -34,16 +34,17 @@ class BlackjackGame( } private fun processTurn(users: List): List { - val allCardReceivedUsers = - users.map { user -> - var currentUser = user - while (currentUser.canReceiveCard() && inputView.inputReceiveMoreCard(currentUser)) { - currentUser = currentUser.receiveCard(Deck.create().draw()) - resultView.printUserCard(user = currentUser, printScore = false) - } - currentUser - } - return allCardReceivedUsers + return users.map { turn(it) } + } + + private fun turn(user: User): User { + return if (user.canReceiveCard() && inputView.inputReceiveMoreCard(user)) { + val updatedUser = user.receiveCard(Deck.create().draw()) + resultView.printUserCard(user = updatedUser, printScore = false) + turn(updatedUser) + } else { + user + } } private fun printGameResult(users: List) { From 37b72e7cab904fb579aa87ae68e657da08046507 Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sat, 7 Dec 2024 23:03:27 +0900 Subject: [PATCH 08/15] =?UTF-8?q?refactor:=20receive=20=EB=A5=BC=20hit=20?= =?UTF-8?q?=EB=A1=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=AA=85=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../kotlin/blackjack/controller/BlackjackGame.kt | 8 ++++---- src/main/kotlin/blackjack/domain/GameTable.kt | 2 +- src/main/kotlin/blackjack/domain/User.kt | 4 ++-- src/main/kotlin/blackjack/view/InputView.kt | 2 +- src/test/kotlin/blackjack/domain/UserTest.kt | 16 ++++++++-------- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 099882aac..b1362c70f 100644 --- a/README.md +++ b/README.md @@ -31,5 +31,5 @@ - [x] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합 - [x] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합 - [x] BlackJackGame::게임진행책임 분리 -- [ ] BlackJackGame::receive 를 hit 로 메서드명 수정 +- [x] BlackJackGame::receive 를 hit 로 메서드명 수정 - [ ] Rank::enum 클래스로 수정 \ No newline at end of file diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index 27aba4fcb..fe59e6828 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -38,10 +38,10 @@ class BlackjackGame( } private fun turn(user: User): User { - return if (user.canReceiveCard() && inputView.inputReceiveMoreCard(user)) { - val updatedUser = user.receiveCard(Deck.create().draw()) - resultView.printUserCard(user = updatedUser, printScore = false) - turn(updatedUser) + return if (user.canHit() && inputView.inputHit(user)) { + val hitUser = user.hit(Deck.create().draw()) + resultView.printUserCard(user = hitUser, printScore = false) + turn(hitUser) } else { user } diff --git a/src/main/kotlin/blackjack/domain/GameTable.kt b/src/main/kotlin/blackjack/domain/GameTable.kt index 2472a5aa1..5120c4c87 100644 --- a/src/main/kotlin/blackjack/domain/GameTable.kt +++ b/src/main/kotlin/blackjack/domain/GameTable.kt @@ -7,7 +7,7 @@ class GameTable( fun dealInitCard(): List { return users.map { user -> (1..INIT_CARD_DRAW_COUNT).fold(user) { acc, _ -> - acc.receiveCard(deck.draw()) + acc.hit(deck.draw()) } } } diff --git a/src/main/kotlin/blackjack/domain/User.kt b/src/main/kotlin/blackjack/domain/User.kt index ceab91f53..627740cf2 100644 --- a/src/main/kotlin/blackjack/domain/User.kt +++ b/src/main/kotlin/blackjack/domain/User.kt @@ -4,11 +4,11 @@ data class User( val name: String, val cards: Cards, ) { - fun canReceiveCard(): Boolean { + fun canHit(): Boolean { return cards.isScoreLowerThanLimit() } - fun receiveCard(card: Card): User { + fun hit(card: Card): User { return this.copy(cards = cards.add(card)) } diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt index ffb2e76de..6dca95595 100644 --- a/src/main/kotlin/blackjack/view/InputView.kt +++ b/src/main/kotlin/blackjack/view/InputView.kt @@ -12,7 +12,7 @@ object InputView { return names } - fun inputReceiveMoreCard(user: User): Boolean { + fun inputHit(user: User): Boolean { println("${user.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") return when (readlnOrNull()) { "y" -> true diff --git a/src/test/kotlin/blackjack/domain/UserTest.kt b/src/test/kotlin/blackjack/domain/UserTest.kt index ce7ef2cfe..d97bac5e3 100644 --- a/src/test/kotlin/blackjack/domain/UserTest.kt +++ b/src/test/kotlin/blackjack/domain/UserTest.kt @@ -20,7 +20,7 @@ class UserTest : StringSpec({ ).forAll { ranks -> val cards = Cards(ranks.map { createCard(it) }) - User("홍길동", cards).canReceiveCard() shouldBe true + User("홍길동", cards).canHit() shouldBe true } } @@ -36,7 +36,7 @@ class UserTest : StringSpec({ val cards = Cards(ranks.map { createCard(it) }) cards.score shouldBe score - User("홍길동", cards).canReceiveCard() shouldBe false + User("홍길동", cards).canHit() shouldBe false } } @@ -44,19 +44,19 @@ class UserTest : StringSpec({ val cards = Cards(emptyList()) val user = User("홍길동", cards) - .receiveCard(createCard("A")) - .receiveCard(createCard("K")) + .hit(createCard("A")) + .hit(createCard("K")) - user.canReceiveCard() shouldBe false + user.canHit() shouldBe false } "유저는 카드 2장을 받은 후 점수 합이 21점 미만인 경우 카드를 더 받을 수 있다" { val cards = Cards(emptyList()) val user = User("홍길동", cards) - .receiveCard(createCard("10")) - .receiveCard(createCard("5")) + .hit(createCard("10")) + .hit(createCard("5")) - user.canReceiveCard() shouldBe true + user.canHit() shouldBe true } }) From f6331c97e0849c15a4dab7799ddf5d99dbd36bee Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sun, 8 Dec 2024 00:26:42 +0900 Subject: [PATCH 09/15] =?UTF-8?q?refactor:=20rank=EB=A5=BC=20enum=20class?= =?UTF-8?q?=EB=A1=9C=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/Card.kt | 4 +- src/main/kotlin/blackjack/domain/Rank.kt | 44 ++++++++----------- src/test/kotlin/blackjack/domain/CardTest.kt | 6 ++- src/test/kotlin/blackjack/domain/RankTest.kt | 16 +++---- .../kotlin/blackjack/fixtures/CardFixture.kt | 2 +- 5 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/main/kotlin/blackjack/domain/Card.kt b/src/main/kotlin/blackjack/domain/Card.kt index c648d9a78..ebf4f9567 100644 --- a/src/main/kotlin/blackjack/domain/Card.kt +++ b/src/main/kotlin/blackjack/domain/Card.kt @@ -1,6 +1,6 @@ package blackjack.domain -import blackjack.domain.Rank.Companion.ACE +import blackjack.domain.Rank.ACE data class Card( val rank: Rank, @@ -14,6 +14,6 @@ data class Card( companion object { val ALL: List = - Suit.entries.flatMap { suit -> Rank.ALL.map { rank -> Card(rank, suit) } } + Suit.entries.flatMap { suit -> Rank.entries.map { rank -> Card(rank, suit) } } } } diff --git a/src/main/kotlin/blackjack/domain/Rank.kt b/src/main/kotlin/blackjack/domain/Rank.kt index 67049a95b..b65d2a669 100644 --- a/src/main/kotlin/blackjack/domain/Rank.kt +++ b/src/main/kotlin/blackjack/domain/Rank.kt @@ -1,31 +1,25 @@ package blackjack.domain -@JvmInline -value class Rank(val value: String) { - init { - require(value in RANK_VALUES) { "랭크는 $RANK_VALUES 에 포함되어야 합니다" } - } - - val score: Int - get() = - when (value) { - in NUMBER_VALUES -> value.toInt() - in FACE_VALUES -> FACE_SCORE - ACE_VALUE -> DEFAULT_ACE_SCORE - else -> throw IllegalArgumentException("유효하지 않은 랭크 값입니다") - } +enum class Rank(val value: String, val score: Int) { + ACE("A", 11), + TWO("2", 2), + THREE("3", 3), + FOUR("4", 4), + FIVE("5", 5), + SIX("6", 6), + SEVEN("7", 7), + EIGHT("8", 8), + NINE("9", 9), + TEN("10", 10), + JACK("J", 10), + QUEEN("Q", 10), + KING("K", 10), + ; companion object { - private const val FACE_SCORE = 10 - private const val DEFAULT_ACE_SCORE = 11 - - private const val ACE_VALUE = "A" - private val NUMBER_VALUES = listOf("2", "3", "4", "5", "6", "7", "8", "9", "10") - private val FACE_VALUES = listOf("J", "Q", "K") - - private val RANK_VALUES = NUMBER_VALUES + FACE_VALUES + ACE_VALUE - - val ACE = Rank(ACE_VALUE) - val ALL = RANK_VALUES.map { Rank(it) } + fun from(value: String): Rank { + return entries.firstOrNull { it.value == value } + ?: throw IllegalArgumentException("유효하지 않은 랭크 값입니다: $value") + } } } diff --git a/src/test/kotlin/blackjack/domain/CardTest.kt b/src/test/kotlin/blackjack/domain/CardTest.kt index 0a22b88f9..d5164dcbb 100644 --- a/src/test/kotlin/blackjack/domain/CardTest.kt +++ b/src/test/kotlin/blackjack/domain/CardTest.kt @@ -1,18 +1,20 @@ package blackjack.domain +import blackjack.domain.Rank.ACE +import blackjack.domain.Rank.THREE import blackjack.domain.Suit.SPADE import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe class CardTest : StringSpec({ "카드는 에이스로 만들어진다면 에이스 카드이다" { - val card = Card(Rank("A"), SPADE) + val card = Card(ACE, SPADE) card.isAce() shouldBe true } "카드는 스페이드로 만들어진다면 에이스 카드가 아니다" { - val card = Card(Rank("3"), SPADE) + val card = Card(THREE, SPADE) card.isAce() shouldBe false } diff --git a/src/test/kotlin/blackjack/domain/RankTest.kt b/src/test/kotlin/blackjack/domain/RankTest.kt index 350438fd0..dbe94e535 100644 --- a/src/test/kotlin/blackjack/domain/RankTest.kt +++ b/src/test/kotlin/blackjack/domain/RankTest.kt @@ -6,25 +6,25 @@ import io.kotest.inspectors.forAll import io.kotest.matchers.shouldBe class RankTest : StringSpec({ - "카드의 랭크가 2~10,J,Q,K,A가 아닌 경우 예외 발생한다" { - listOf("0", "1", "-1", "B", "D").forAll { invalidRank -> - shouldThrow { Rank(invalidRank) } + "유효하지 않은 랭크값을 통해 랭크 생성시 예외 발생한다" { + listOf("0", "1", "-1", "B", "D").forAll { invalidRankValue -> + shouldThrow { Rank.from(invalidRankValue) } } } - "랭크가 숫자이면 점수는 숫자의 값으로 계산된다" { + "랭크가 숫자이면 점수는 숫자의 값이다" { listOf("2", "3", "4", "5", "6").forAll { number -> - Rank(number).score shouldBe number.toInt() + Rank.from(number).score shouldBe number.toInt() } } - "랭크가 J,Q,K 이면 점수는 10점으로 계산된다" { + "랭크가 J,Q,K 이면 점수는 10점이다" { listOf("J", "Q", "K").forAll { face -> - Rank(face).score shouldBe 10 + Rank.from(face).score shouldBe 10 } } "랭크가 에이스이면 기본점수는 11점이다" { - Rank("A").score shouldBe 11 + Rank.from("A").score shouldBe 11 } }) diff --git a/src/test/kotlin/blackjack/fixtures/CardFixture.kt b/src/test/kotlin/blackjack/fixtures/CardFixture.kt index 69494ab58..d4c2c25cf 100644 --- a/src/test/kotlin/blackjack/fixtures/CardFixture.kt +++ b/src/test/kotlin/blackjack/fixtures/CardFixture.kt @@ -9,5 +9,5 @@ fun createCard( rank: String, suit: Suit = SPADE, ): Card { - return Card(Rank(rank), suit) + return Card(Rank.from(rank), suit) } From a95919b9e3c02cf7a6d9557f4840b8686c397473 Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sun, 8 Dec 2024 17:32:36 +0900 Subject: [PATCH 10/15] =?UTF-8?q?feat:=20=EB=94=9C=EB=9F=AC=EB=8A=94=20?= =?UTF-8?q?=EC=B2=98=EC=9D=8C=EB=B0=9B=EB=8A=94=202=EC=9E=A5=EC=9D=98=20?= =?UTF-8?q?=ED=95=A9=EA=B3=84=EA=B0=80=2017=EB=AF=B8=EB=A7=8C=EC=9D=B4?= =?UTF-8?q?=EB=A9=B4=20=EB=B0=98=EB=93=9C=EC=8B=9C=201=EC=9E=A5=EC=9D=98?= =?UTF-8?q?=20=EC=B9=B4=EB=93=9C=EB=A5=BC=20=EC=B6=94=EA=B0=80=EB=A1=9C=20?= =?UTF-8?q?=EB=B0=9B=EB=8A=94=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +- .../blackjack/controller/BlackjackGame.kt | 76 +++++++++++++------ src/main/kotlin/blackjack/domain/Cards.kt | 4 +- src/main/kotlin/blackjack/domain/GameTable.kt | 12 ++- .../kotlin/blackjack/domain/Participant.kt | 49 ++++++++++++ src/main/kotlin/blackjack/domain/User.kt | 20 ----- src/main/kotlin/blackjack/view/InputView.kt | 11 ++- src/main/kotlin/blackjack/view/ResultView.kt | 29 ++++--- .../kotlin/blackjack/domain/DealerTest.kt | 19 +++++ .../domain/{UserTest.kt => PlayerTest.kt} | 19 +++-- .../kotlin/blackjack/fixtures/UserFixtures.kt | 6 +- 11 files changed, 174 insertions(+), 78 deletions(-) create mode 100644 src/main/kotlin/blackjack/domain/Participant.kt delete mode 100644 src/main/kotlin/blackjack/domain/User.kt create mode 100644 src/test/kotlin/blackjack/domain/DealerTest.kt rename src/test/kotlin/blackjack/domain/{UserTest.kt => PlayerTest.kt} (82%) diff --git a/README.md b/README.md index b1362c70f..948762578 100644 --- a/README.md +++ b/README.md @@ -32,4 +32,9 @@ - [x] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합 - [x] BlackJackGame::게임진행책임 분리 - [x] BlackJackGame::receive 를 hit 로 메서드명 수정 -- [ ] Rank::enum 클래스로 수정 \ No newline at end of file +- [x] Rank::enum 클래스로 수정 + +### 기능 구현사항 +- [x] 딜러는 처음받는 2장의 합계가 16이하면 반드시 1장의 카드를 추가로 받는다 +- [ ] 딜러가 21을 초과하면 남은 플레이어들은 패에 상관없이 승리한다 +- [ ] 게임 완료 후 각 플레이어별로 승패를 출력한다 \ No newline at end of file diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index fe59e6828..4ecf885b6 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -1,8 +1,10 @@ package blackjack.controller +import blackjack.domain.Dealer import blackjack.domain.Deck import blackjack.domain.GameTable -import blackjack.domain.User +import blackjack.domain.Participant +import blackjack.domain.Player import blackjack.view.InputView import blackjack.view.ResultView @@ -11,44 +13,70 @@ class BlackjackGame( private val resultView: ResultView, ) { fun start() { - val gameTable = GameTable(getUsers(), Deck.create()) - val users = playGame(gameTable) - printGameResult(users) + val gameTable = GameTable(getParticipants(), Deck.create()) + val participants = playGame(gameTable) + printGameResult(participants) } - private fun getUsers(): List { - return inputView.inputNames().map { User.create(name = it) } + private fun getParticipants(): List { + return buildList { + add(Dealer.create()) + addAll(inputView.inputNames().map { Player.create(name = it) }) + } } - private fun playGame(gameTable: GameTable): List { - val initCardReceivedUsers = setUpInitCard(gameTable) - return processTurn(initCardReceivedUsers) + private fun playGame(gameTable: GameTable): List { + val participants = setUpInitCard(gameTable) + val (players, dealer) = Participant.separate(participants) + val gamedPlayers = playersTurn(players, gameTable) + resultView.linebreak() + val gamedDealer = dealerTurn(dealer, gameTable) + return gamedPlayers + gamedDealer } - private fun setUpInitCard(gameTable: GameTable): List { - val initCardReceivedUsers = gameTable.dealInitCard() + private fun setUpInitCard(gameTable: GameTable): List { + val participants = gameTable.dealInitCard() resultView.linebreak() - resultView.printInitCardReceive(initCardReceivedUsers) - resultView.printUsersCard(users = initCardReceivedUsers, printScore = false) - return initCardReceivedUsers + resultView.printInitCardReceive(participants) + resultView.printParticipantsCard(participants = participants, printScore = false) + resultView.linebreak() + return participants } - private fun processTurn(users: List): List { - return users.map { turn(it) } + private fun playersTurn( + participants: List, + gameTable: GameTable, + ): List { + return participants.map { playerTurn(it, gameTable) } + } + + private fun playerTurn( + player: Participant, + gameTable: GameTable, + ): Participant { + return if (player.canHit() && inputView.inputHit(player)) { + val hitPlayer = gameTable.hit(player) + resultView.printParticipantCard(participant = player, printScore = false) + playerTurn(hitPlayer, gameTable) + } else { + player + } } - private fun turn(user: User): User { - return if (user.canHit() && inputView.inputHit(user)) { - val hitUser = user.hit(Deck.create().draw()) - resultView.printUserCard(user = hitUser, printScore = false) - turn(hitUser) + private fun dealerTurn( + dealer: Participant, + gameTable: GameTable, + ): Participant { + return if (dealer.canHit()) { + resultView.printDealerHit() + dealerTurn(gameTable.hit(dealer), gameTable) } else { - user + dealer } } - private fun printGameResult(users: List) { + private fun printGameResult(participants: List) { resultView.linebreak() - resultView.printUsersCard(users = users, printScore = true) + resultView.printParticipantsCard(participants = participants, printScore = true) } } diff --git a/src/main/kotlin/blackjack/domain/Cards.kt b/src/main/kotlin/blackjack/domain/Cards.kt index 248a97f8b..e8721d905 100644 --- a/src/main/kotlin/blackjack/domain/Cards.kt +++ b/src/main/kotlin/blackjack/domain/Cards.kt @@ -4,8 +4,8 @@ data class Cards(val values: List) { val score: Int get() = calculateScore() - fun isScoreLowerThanLimit(): Boolean { - return score < BLACKJACK_SCORE_LIMIT + fun scoreLowerThan(limit: Int): Boolean { + return score < limit } fun add(card: Card): Cards { diff --git a/src/main/kotlin/blackjack/domain/GameTable.kt b/src/main/kotlin/blackjack/domain/GameTable.kt index 5120c4c87..c8c2c11d4 100644 --- a/src/main/kotlin/blackjack/domain/GameTable.kt +++ b/src/main/kotlin/blackjack/domain/GameTable.kt @@ -1,17 +1,21 @@ package blackjack.domain class GameTable( - private val users: List, + private val participants: List, private val deck: Deck, ) { - fun dealInitCard(): List { - return users.map { user -> - (1..INIT_CARD_DRAW_COUNT).fold(user) { acc, _ -> + fun dealInitCard(): List { + return participants.map { participant -> + (1..INIT_CARD_DRAW_COUNT).fold(participant) { acc, _ -> acc.hit(deck.draw()) } } } + fun hit(participant: Participant): Participant { + return participant.hit(deck.draw()) + } + companion object { const val INIT_CARD_DRAW_COUNT = 2 } diff --git a/src/main/kotlin/blackjack/domain/Participant.kt b/src/main/kotlin/blackjack/domain/Participant.kt new file mode 100644 index 000000000..7a7cbd913 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Participant.kt @@ -0,0 +1,49 @@ +package blackjack.domain + +sealed class Participant(val name: String, val cards: Cards) { + abstract fun canHit(): Boolean + + abstract fun hit(card: Card): Participant + + companion object { + fun separate(participants: List): Pair, Dealer> { + return participants.filterIsInstance() to participants.first { it is Dealer } as Dealer + } + } +} + +class Player(name: String, cards: Cards) : Participant(name, cards) { + override fun canHit(): Boolean { + return cards.scoreLowerThan(PLAYER_SCORE_LIMIT) + } + + override fun hit(card: Card): Player { + return Player(this.name, cards.add(card)) + } + + companion object { + private const val PLAYER_SCORE_LIMIT = 21 + + fun create(name: String): Player { + return Player(name, Cards(emptyList())) + } + } +} + +class Dealer(cards: Cards) : Participant("딜러", cards) { + override fun canHit(): Boolean { + return cards.scoreLowerThan(DEALER_SCORE_LIMIT) + } + + override fun hit(card: Card): Dealer { + return Dealer(cards.add(card)) + } + + companion object { + private const val DEALER_SCORE_LIMIT = 17 + + fun create(): Dealer { + return Dealer(Cards(emptyList())) + } + } +} diff --git a/src/main/kotlin/blackjack/domain/User.kt b/src/main/kotlin/blackjack/domain/User.kt deleted file mode 100644 index 627740cf2..000000000 --- a/src/main/kotlin/blackjack/domain/User.kt +++ /dev/null @@ -1,20 +0,0 @@ -package blackjack.domain - -data class User( - val name: String, - val cards: Cards, -) { - fun canHit(): Boolean { - return cards.isScoreLowerThanLimit() - } - - fun hit(card: Card): User { - return this.copy(cards = cards.add(card)) - } - - companion object { - fun create(name: String): User { - return User(name, Cards(emptyList())) - } - } -} diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt index 6dca95595..ec7feab94 100644 --- a/src/main/kotlin/blackjack/view/InputView.kt +++ b/src/main/kotlin/blackjack/view/InputView.kt @@ -1,6 +1,7 @@ package blackjack.view -import blackjack.domain.User +import blackjack.domain.Dealer +import blackjack.domain.Participant object InputView { fun inputNames(): List { @@ -12,8 +13,12 @@ object InputView { return names } - fun inputHit(user: User): Boolean { - println("${user.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") + fun inputHit(participant: Participant): Boolean { + if (participant is Dealer) { + return true + } + + println("${participant.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") return when (readlnOrNull()) { "y" -> true "n" -> false diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt index 5ce495ec4..93ec4260b 100644 --- a/src/main/kotlin/blackjack/view/ResultView.kt +++ b/src/main/kotlin/blackjack/view/ResultView.kt @@ -1,27 +1,34 @@ package blackjack.view import blackjack.domain.GameTable.Companion.INIT_CARD_DRAW_COUNT -import blackjack.domain.User +import blackjack.domain.Participant object ResultView { - fun printInitCardReceive(users: List) { - println("${users.joinToString(", ") { it.name }}에게 ${INIT_CARD_DRAW_COUNT}장의 카드를 나누었습니다.") + fun printInitCardReceive(participants: List) { + val players = Participant.separate(participants).first + println("딜러와 ${players.joinToString(", ") { it.name }}에게 ${INIT_CARD_DRAW_COUNT}장의 카드를 나누었습니다.") } - fun printUsersCard( - users: List, + fun printParticipantsCard( + participants: List, printScore: Boolean, ) { - users.forEach { printUserCard(user = it, printScore = printScore) } + val (players, dealer) = Participant.separate(participants) + printParticipantCard(participant = dealer, printScore = printScore) + players.forEach { printParticipantCard(participant = it, printScore = printScore) } } - fun printUserCard( - user: User, + fun printParticipantCard( + participant: Participant, printScore: Boolean, ) { - val cards = user.cards.values.joinToString(", ") { "${it.rank.value}${it.suit.description}" } - val scoreText = "- 결과: ${user.cards.score}" - println("${user.name}카드: $cards ${if (printScore) scoreText else ""}") + val cards = participant.cards.values.joinToString(", ") { "${it.rank.value}${it.suit.description}" } + val scoreText = "- 결과: ${participant.cards.score}" + println("${participant.name} 카드: $cards ${if (printScore) scoreText else ""}") + } + + fun printDealerHit() { + println("딜러는 16이하라 한장의 카드를 더 받았습니다.") } fun linebreak() { diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt new file mode 100644 index 000000000..c915c586d --- /dev/null +++ b/src/test/kotlin/blackjack/domain/DealerTest.kt @@ -0,0 +1,19 @@ +package blackjack.domain + +import blackjack.fixtures.createCard +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe + +class DealerTest : StringSpec({ + "딜러는 카드를 가진다" { + val dealer = Dealer.create() + dealer.cards shouldNotBe null + } + + "딜러는 첫 2장의 합이 16이하면 반드시 카드를 받아야 한다" { + val dealer = Dealer(Cards(listOf(createCard("10"), createCard("6")))) + + dealer.canHit() shouldBe true + } +}) diff --git a/src/test/kotlin/blackjack/domain/UserTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt similarity index 82% rename from src/test/kotlin/blackjack/domain/UserTest.kt rename to src/test/kotlin/blackjack/domain/PlayerTest.kt index d97bac5e3..102144a3f 100644 --- a/src/test/kotlin/blackjack/domain/UserTest.kt +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -8,8 +8,7 @@ import io.kotest.data.row import io.kotest.data.table import io.kotest.matchers.shouldBe -class UserTest : StringSpec({ - +class PlayerTest : StringSpec({ "유저는 카드목록의 점수합이 21점 미만일 경우 카드를 더 받을 수 있다" { table( headers("ranks"), @@ -20,7 +19,7 @@ class UserTest : StringSpec({ ).forAll { ranks -> val cards = Cards(ranks.map { createCard(it) }) - User("홍길동", cards).canHit() shouldBe true + Player("홍길동", cards).canHit() shouldBe true } } @@ -36,27 +35,27 @@ class UserTest : StringSpec({ val cards = Cards(ranks.map { createCard(it) }) cards.score shouldBe score - User("홍길동", cards).canHit() shouldBe false + Player("홍길동", cards).canHit() shouldBe false } } "유저는 카드 2장을 받은 후 점수 합이 21점인 경우 카드를 더 받지 못한다" { val cards = Cards(emptyList()) - val user = - User("홍길동", cards) + val player = + Player("홍길동", cards) .hit(createCard("A")) .hit(createCard("K")) - user.canHit() shouldBe false + player.canHit() shouldBe false } "유저는 카드 2장을 받은 후 점수 합이 21점 미만인 경우 카드를 더 받을 수 있다" { val cards = Cards(emptyList()) - val user = - User("홍길동", cards) + val player = + Player("홍길동", cards) .hit(createCard("10")) .hit(createCard("5")) - user.canHit() shouldBe true + player.canHit() shouldBe true } }) diff --git a/src/test/kotlin/blackjack/fixtures/UserFixtures.kt b/src/test/kotlin/blackjack/fixtures/UserFixtures.kt index 296f051b4..aaee55f35 100644 --- a/src/test/kotlin/blackjack/fixtures/UserFixtures.kt +++ b/src/test/kotlin/blackjack/fixtures/UserFixtures.kt @@ -1,8 +1,8 @@ package blackjack.fixtures import blackjack.domain.Cards -import blackjack.domain.User +import blackjack.domain.Player -fun createUsers(names: List = listOf("홍길동", "홍길덩")): List { - return names.map { User(it, Cards(emptyList())) } +fun createUsers(names: List = listOf("홍길동", "홍길덩")): List { + return names.map { Player(it, Cards(emptyList())) } } From 6f38f2ba2bdb4322e8300af0c8c1eceece458cae Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sun, 8 Dec 2024 17:44:24 +0900 Subject: [PATCH 11/15] =?UTF-8?q?refactor:=20=EA=B2=8C=EC=9E=84=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=83=81=ED=83=9C=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=B0=B8=EA=B0=80=EC=9E=90=20=EB=AA=A9=EB=A1=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../blackjack/controller/BlackjackGame.kt | 18 +++++++++--------- src/main/kotlin/blackjack/domain/GameTable.kt | 3 +-- .../kotlin/blackjack/domain/GameTableTest.kt | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 948762578..bf4eeeef6 100644 --- a/README.md +++ b/README.md @@ -36,5 +36,5 @@ ### 기능 구현사항 - [x] 딜러는 처음받는 2장의 합계가 16이하면 반드시 1장의 카드를 추가로 받는다 -- [ ] 딜러가 21을 초과하면 남은 플레이어들은 패에 상관없이 승리한다 +- [x] 딜러가 21을 초과하면 남은 플레이어들은 패에 상관없이 승리한다 - [ ] 게임 완료 후 각 플레이어별로 승패를 출력한다 \ No newline at end of file diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index 4ecf885b6..489178ddb 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -13,18 +13,11 @@ class BlackjackGame( private val resultView: ResultView, ) { fun start() { - val gameTable = GameTable(getParticipants(), Deck.create()) + val gameTable = GameTable(Deck.create()) val participants = playGame(gameTable) printGameResult(participants) } - private fun getParticipants(): List { - return buildList { - add(Dealer.create()) - addAll(inputView.inputNames().map { Player.create(name = it) }) - } - } - private fun playGame(gameTable: GameTable): List { val participants = setUpInitCard(gameTable) val (players, dealer) = Participant.separate(participants) @@ -35,7 +28,7 @@ class BlackjackGame( } private fun setUpInitCard(gameTable: GameTable): List { - val participants = gameTable.dealInitCard() + val participants = gameTable.dealInitCard(getParticipants()) resultView.linebreak() resultView.printInitCardReceive(participants) resultView.printParticipantsCard(participants = participants, printScore = false) @@ -79,4 +72,11 @@ class BlackjackGame( resultView.linebreak() resultView.printParticipantsCard(participants = participants, printScore = true) } + + private fun getParticipants(): List { + return buildList { + add(Dealer.create()) + addAll(inputView.inputNames().map { Player.create(name = it) }) + } + } } diff --git a/src/main/kotlin/blackjack/domain/GameTable.kt b/src/main/kotlin/blackjack/domain/GameTable.kt index c8c2c11d4..c03850275 100644 --- a/src/main/kotlin/blackjack/domain/GameTable.kt +++ b/src/main/kotlin/blackjack/domain/GameTable.kt @@ -1,10 +1,9 @@ package blackjack.domain class GameTable( - private val participants: List, private val deck: Deck, ) { - fun dealInitCard(): List { + fun dealInitCard(participants: List): List { return participants.map { participant -> (1..INIT_CARD_DRAW_COUNT).fold(participant) { acc, _ -> acc.hit(deck.draw()) diff --git a/src/test/kotlin/blackjack/domain/GameTableTest.kt b/src/test/kotlin/blackjack/domain/GameTableTest.kt index b18c6c070..f0fb528de 100644 --- a/src/test/kotlin/blackjack/domain/GameTableTest.kt +++ b/src/test/kotlin/blackjack/domain/GameTableTest.kt @@ -6,7 +6,7 @@ import io.kotest.matchers.shouldBe class GameTableTest : StringSpec({ "최초 딜 시 카드를 2장 나누어준다" { - val initCardReceivedUsers = GameTable(createUsers(), Deck.create()).dealInitCard() + val initCardReceivedUsers = GameTable(Deck.create()).dealInitCard(createUsers()) initCardReceivedUsers.forEach { it.cards.values.size shouldBe 2 From 7cff569d5065c511ef2b146001f8b4d771c8e409 Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sun, 8 Dec 2024 19:23:57 +0900 Subject: [PATCH 12/15] =?UTF-8?q?feat:=20=EB=94=9C=EB=9F=AC=EA=B0=80=2021?= =?UTF-8?q?=EC=9D=84=20=EC=B4=88=EA=B3=BC=ED=95=98=EB=A9=B4=20=EB=82=A8?= =?UTF-8?q?=EC=9D=80=20=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4=EB=93=A4?= =?UTF-8?q?=EC=9D=80=20=ED=8C=A8=EC=97=90=20=EC=83=81=EA=B4=80=EC=97=86?= =?UTF-8?q?=EC=9D=B4=20=EC=8A=B9=EB=A6=AC=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/blackjack/domain/Card.kt | 8 +- src/main/kotlin/blackjack/domain/Cards.kt | 17 +++- .../kotlin/blackjack/domain/MatchResult.kt | 7 ++ .../kotlin/blackjack/domain/Participant.kt | 12 +++ src/test/kotlin/blackjack/domain/CardTest.kt | 4 +- .../kotlin/blackjack/domain/GameTableTest.kt | 6 +- .../kotlin/blackjack/domain/PlayerTest.kt | 79 ++++++++++++++++++- .../kotlin/blackjack/fixtures/UserFixtures.kt | 2 +- 8 files changed, 120 insertions(+), 15 deletions(-) create mode 100644 src/main/kotlin/blackjack/domain/MatchResult.kt diff --git a/src/main/kotlin/blackjack/domain/Card.kt b/src/main/kotlin/blackjack/domain/Card.kt index ebf4f9567..3b1cb0e9d 100644 --- a/src/main/kotlin/blackjack/domain/Card.kt +++ b/src/main/kotlin/blackjack/domain/Card.kt @@ -6,11 +6,11 @@ data class Card( val rank: Rank, val suit: Suit, ) { - val score = rank.score + val score: Int + get() = rank.score - fun isAce(): Boolean { - return rank == ACE - } + val isAce: Boolean + get() = rank == ACE companion object { val ALL: List = diff --git a/src/main/kotlin/blackjack/domain/Cards.kt b/src/main/kotlin/blackjack/domain/Cards.kt index e8721d905..aabe71254 100644 --- a/src/main/kotlin/blackjack/domain/Cards.kt +++ b/src/main/kotlin/blackjack/domain/Cards.kt @@ -1,20 +1,35 @@ package blackjack.domain +import blackjack.domain.MatchResult.DRAW +import blackjack.domain.MatchResult.LOSS +import blackjack.domain.MatchResult.WIN + data class Cards(val values: List) { val score: Int get() = calculateScore() + val isBust: Boolean + get() = calculateScore() > BLACKJACK_SCORE_LIMIT + fun scoreLowerThan(limit: Int): Boolean { return score < limit } + fun compareScore(other: Cards): MatchResult { + return when { + score > other.score -> WIN + score < other.score -> LOSS + else -> DRAW + } + } + fun add(card: Card): Cards { return Cards(values + card) } private fun calculateScore(): Int { val totalScore = values.sumOf { it.score } - var aceCount = values.count { it.isAce() } + var aceCount = values.count { it.isAce } var adjustedScore = totalScore while (adjustedScore > BLACKJACK_SCORE_LIMIT && aceCount > 0) { diff --git a/src/main/kotlin/blackjack/domain/MatchResult.kt b/src/main/kotlin/blackjack/domain/MatchResult.kt new file mode 100644 index 000000000..f0c2fe5cb --- /dev/null +++ b/src/main/kotlin/blackjack/domain/MatchResult.kt @@ -0,0 +1,7 @@ +package blackjack.domain + +enum class MatchResult { + WIN, + LOSS, + DRAW, +} diff --git a/src/main/kotlin/blackjack/domain/Participant.kt b/src/main/kotlin/blackjack/domain/Participant.kt index 7a7cbd913..db92afca2 100644 --- a/src/main/kotlin/blackjack/domain/Participant.kt +++ b/src/main/kotlin/blackjack/domain/Participant.kt @@ -1,5 +1,7 @@ package blackjack.domain +import blackjack.domain.MatchResult.WIN + sealed class Participant(val name: String, val cards: Cards) { abstract fun canHit(): Boolean @@ -21,6 +23,13 @@ class Player(name: String, cards: Cards) : Participant(name, cards) { return Player(this.name, cards.add(card)) } + fun compareScore(dealer: Dealer): MatchResult { + if (dealer.isBust) { + return WIN + } + return cards.compareScore(dealer.cards) + } + companion object { private const val PLAYER_SCORE_LIMIT = 21 @@ -31,6 +40,9 @@ class Player(name: String, cards: Cards) : Participant(name, cards) { } class Dealer(cards: Cards) : Participant("딜러", cards) { + val isBust: Boolean + get() = cards.isBust + override fun canHit(): Boolean { return cards.scoreLowerThan(DEALER_SCORE_LIMIT) } diff --git a/src/test/kotlin/blackjack/domain/CardTest.kt b/src/test/kotlin/blackjack/domain/CardTest.kt index d5164dcbb..727410f08 100644 --- a/src/test/kotlin/blackjack/domain/CardTest.kt +++ b/src/test/kotlin/blackjack/domain/CardTest.kt @@ -10,12 +10,12 @@ class CardTest : StringSpec({ "카드는 에이스로 만들어진다면 에이스 카드이다" { val card = Card(ACE, SPADE) - card.isAce() shouldBe true + card.isAce shouldBe true } "카드는 스페이드로 만들어진다면 에이스 카드가 아니다" { val card = Card(THREE, SPADE) - card.isAce() shouldBe false + card.isAce shouldBe false } }) diff --git a/src/test/kotlin/blackjack/domain/GameTableTest.kt b/src/test/kotlin/blackjack/domain/GameTableTest.kt index f0fb528de..8b0517a3b 100644 --- a/src/test/kotlin/blackjack/domain/GameTableTest.kt +++ b/src/test/kotlin/blackjack/domain/GameTableTest.kt @@ -1,14 +1,14 @@ package blackjack.domain -import blackjack.fixtures.createUsers +import blackjack.fixtures.createPlayers import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe class GameTableTest : StringSpec({ "최초 딜 시 카드를 2장 나누어준다" { - val initCardReceivedUsers = GameTable(Deck.create()).dealInitCard(createUsers()) + val players = GameTable(Deck.create()).dealInitCard(createPlayers()) - initCardReceivedUsers.forEach { + players.forEach { it.cards.values.size shouldBe 2 } } diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt index 102144a3f..aa5db26b9 100644 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -1,15 +1,23 @@ package blackjack.domain +import blackjack.domain.MatchResult.DRAW +import blackjack.domain.MatchResult.LOSS +import blackjack.domain.MatchResult.WIN +import blackjack.domain.Suit.CLUB +import blackjack.domain.Suit.DIAMOND +import blackjack.domain.Suit.HEART +import blackjack.domain.Suit.SPADE import blackjack.fixtures.createCard import io.kotest.core.spec.style.StringSpec import io.kotest.data.forAll import io.kotest.data.headers import io.kotest.data.row import io.kotest.data.table +import io.kotest.inspectors.forAll import io.kotest.matchers.shouldBe class PlayerTest : StringSpec({ - "유저는 카드목록의 점수합이 21점 미만일 경우 카드를 더 받을 수 있다" { + "플레이어는 카드목록의 점수합이 21점 미만일 경우 카드를 더 받을 수 있다" { table( headers("ranks"), row(listOf("2", "3", "4")), @@ -23,7 +31,7 @@ class PlayerTest : StringSpec({ } } - "유저는 카드목록의 점수합이 21점 이상할 경우 카드를 더 받을 수 없다" { + "플레이어는 카드목록의 점수합이 21점 이상할 경우 카드를 더 받을 수 없다" { table( headers("ranks", "score"), row(listOf("J", "Q", "K"), 30), @@ -39,7 +47,7 @@ class PlayerTest : StringSpec({ } } - "유저는 카드 2장을 받은 후 점수 합이 21점인 경우 카드를 더 받지 못한다" { + "플레이어는 카드 2장을 받은 후 점수 합이 21점인 경우 카드를 더 받지 못한다" { val cards = Cards(emptyList()) val player = Player("홍길동", cards) @@ -49,7 +57,7 @@ class PlayerTest : StringSpec({ player.canHit() shouldBe false } - "유저는 카드 2장을 받은 후 점수 합이 21점 미만인 경우 카드를 더 받을 수 있다" { + "플레이어는 카드 2장을 받은 후 점수 합이 21점 미만인 경우 카드를 더 받을 수 있다" { val cards = Cards(emptyList()) val player = Player("홍길동", cards) @@ -58,4 +66,67 @@ class PlayerTest : StringSpec({ player.canHit() shouldBe true } + + "플레이어가 딜러보다 점수가 높다면 승리한다" { + val cards = Cards(emptyList()) + val player = + Player("홍길동", cards) + .hit(createCard("10")) + .hit(createCard("5")) + val dealer = Dealer.create().hit(createCard("10")) + + player.compareScore(dealer) shouldBe WIN + } + + "플레이어가 딜러보다 점수가 낮다면 패배한다" { + val cards = Cards(emptyList()) + val player = + Player("홍길동", cards) + .hit(createCard("5")) + val dealer = + Dealer.create() + .hit(createCard("10")) + + player.compareScore(dealer) shouldBe LOSS + } + + "플레이어가 딜러보와 점수가 같다면 무승부다" { + val cards = Cards(emptyList()) + val player = + Player("홍길동", cards) + .hit(createCard("5")) + val dealer = + Dealer.create() + .hit(createCard("5")) + + player.compareScore(dealer) shouldBe DRAW + } + + "플레이어의 패에 상관없이 딜러의 점수가 21점이 넘는다면 승리한다" { + val cards = Cards(emptyList()) + val biggerScorePlayer = + Player("김큰점수", cards) + .hit(createCard("K", SPADE)) + .hit(createCard("Q", SPADE)) + .hit(createCard("J", SPADE)) + val smallerScorePlayer = + Player("박작은점수", cards) + .hit(createCard("K", HEART)) + .hit(createCard("Q", HEART)) + .hit(createCard("J", HEART)) + val sameScorePlayer = + Player("최같은점수", cards) + .hit(createCard("10", CLUB)) + .hit(createCard("10", CLUB)) + .hit(createCard("3", CLUB)) + val dealer = + Dealer.create() + .hit(createCard("10", DIAMOND)) + .hit(createCard("10", DIAMOND)) + .hit(createCard("3", DIAMOND)) + + listOf(biggerScorePlayer, smallerScorePlayer, sameScorePlayer).forAll { player -> + player.compareScore(dealer) shouldBe WIN + } + } }) diff --git a/src/test/kotlin/blackjack/fixtures/UserFixtures.kt b/src/test/kotlin/blackjack/fixtures/UserFixtures.kt index aaee55f35..12edefc85 100644 --- a/src/test/kotlin/blackjack/fixtures/UserFixtures.kt +++ b/src/test/kotlin/blackjack/fixtures/UserFixtures.kt @@ -3,6 +3,6 @@ package blackjack.fixtures import blackjack.domain.Cards import blackjack.domain.Player -fun createUsers(names: List = listOf("홍길동", "홍길덩")): List { +fun createPlayers(names: List = listOf("홍길동", "홍길덩")): List { return names.map { Player(it, Cards(emptyList())) } } From 823396e2647320c5f7256629644a1d9338c2bf5d Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sun, 8 Dec 2024 19:47:33 +0900 Subject: [PATCH 13/15] =?UTF-8?q?refactor:=20=EC=9E=AC=EA=B7=80=ED=95=A8?= =?UTF-8?q?=EC=88=98=EC=97=90=20tailrec=20=ED=82=A4=EC=9B=8C=EB=93=9C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/controller/BlackjackGame.kt | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index 489178ddb..3329b8844 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -43,29 +43,27 @@ class BlackjackGame( return participants.map { playerTurn(it, gameTable) } } - private fun playerTurn( + private tailrec fun playerTurn( player: Participant, gameTable: GameTable, ): Participant { - return if (player.canHit() && inputView.inputHit(player)) { - val hitPlayer = gameTable.hit(player) - resultView.printParticipantCard(participant = player, printScore = false) - playerTurn(hitPlayer, gameTable) - } else { - player + if (!player.canHit() || !inputView.inputHit(player)) { + return player } + val hitPlayer = gameTable.hit(player) + resultView.printParticipantCard(participant = player, printScore = false) + return playerTurn(hitPlayer, gameTable) } - private fun dealerTurn( + private tailrec fun dealerTurn( dealer: Participant, gameTable: GameTable, ): Participant { - return if (dealer.canHit()) { - resultView.printDealerHit() - dealerTurn(gameTable.hit(dealer), gameTable) - } else { - dealer + if (!dealer.canHit()) { + return dealer } + resultView.printDealerHit() + return dealerTurn(gameTable.hit(dealer), gameTable) } private fun printGameResult(participants: List) { From c0412975c03e07173654c496f5a5f0bbb7d71ad3 Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Sun, 8 Dec 2024 21:45:20 +0900 Subject: [PATCH 14/15] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=ED=9B=84=20=EA=B0=81=20=ED=94=8C=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=EB=B3=84=EB=A1=9C=20=EC=8A=B9=ED=8C=A8=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../blackjack/controller/BlackjackGame.kt | 23 ++++++++++------ .../kotlin/blackjack/domain/GameResult.kt | 27 +++++++++++++++++++ src/main/kotlin/blackjack/domain/GameTable.kt | 2 +- .../kotlin/blackjack/domain/MatchResult.kt | 8 +++--- .../kotlin/blackjack/domain/Participant.kt | 14 +++++----- .../blackjack/domain/dto/DealerGameResult.kt | 7 +++++ .../blackjack/domain/dto/PlayerGameResult.kt | 9 +++++++ src/main/kotlin/blackjack/view/ResultView.kt | 15 +++++++++++ 9 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 src/main/kotlin/blackjack/domain/GameResult.kt create mode 100644 src/main/kotlin/blackjack/domain/dto/DealerGameResult.kt create mode 100644 src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt diff --git a/README.md b/README.md index bf4eeeef6..3c69bf851 100644 --- a/README.md +++ b/README.md @@ -37,4 +37,4 @@ ### 기능 구현사항 - [x] 딜러는 처음받는 2장의 합계가 16이하면 반드시 1장의 카드를 추가로 받는다 - [x] 딜러가 21을 초과하면 남은 플레이어들은 패에 상관없이 승리한다 -- [ ] 게임 완료 후 각 플레이어별로 승패를 출력한다 \ No newline at end of file +- [x] 게임 완료 후 각 플레이어별로 승패를 출력한다 \ No newline at end of file diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index 3329b8844..6a2eb75db 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -2,19 +2,21 @@ package blackjack.controller import blackjack.domain.Dealer import blackjack.domain.Deck +import blackjack.domain.GameResult import blackjack.domain.GameTable import blackjack.domain.Participant import blackjack.domain.Player import blackjack.view.InputView import blackjack.view.ResultView -class BlackjackGame( +data class BlackjackGame( private val inputView: InputView, private val resultView: ResultView, ) { fun start() { val gameTable = GameTable(Deck.create()) val participants = playGame(gameTable) + printCard(participants) printGameResult(participants) } @@ -36,6 +38,13 @@ class BlackjackGame( return participants } + private fun getParticipants(): List { + return buildList { + add(Dealer.create()) + addAll(inputView.inputNames().map { Player.create(name = it) }) + } + } + private fun playersTurn( participants: List, gameTable: GameTable, @@ -51,7 +60,7 @@ class BlackjackGame( return player } val hitPlayer = gameTable.hit(player) - resultView.printParticipantCard(participant = player, printScore = false) + resultView.printParticipantCard(participant = hitPlayer, printScore = false) return playerTurn(hitPlayer, gameTable) } @@ -66,15 +75,13 @@ class BlackjackGame( return dealerTurn(gameTable.hit(dealer), gameTable) } - private fun printGameResult(participants: List) { + private fun printCard(participants: List) { resultView.linebreak() resultView.printParticipantsCard(participants = participants, printScore = true) } - private fun getParticipants(): List { - return buildList { - add(Dealer.create()) - addAll(inputView.inputNames().map { Player.create(name = it) }) - } + private fun printGameResult(participants: List) { + resultView.linebreak() + resultView.printGameResult(GameResult.from(participants)) } } diff --git a/src/main/kotlin/blackjack/domain/GameResult.kt b/src/main/kotlin/blackjack/domain/GameResult.kt new file mode 100644 index 000000000..5cfbb1cfb --- /dev/null +++ b/src/main/kotlin/blackjack/domain/GameResult.kt @@ -0,0 +1,27 @@ +package blackjack.domain + +import blackjack.domain.MatchResult.DRAW +import blackjack.domain.MatchResult.LOSS +import blackjack.domain.MatchResult.WIN +import blackjack.domain.dto.DealerGameResult +import blackjack.domain.dto.PlayerGameResult + +data class GameResult( + val dealerGameResult: DealerGameResult, + val playerGameResults: List, +) { + companion object { + fun from(participants: List): GameResult { + val (players, dealer) = Participant.separate(participants) + val playerGameResults = players.map { player -> PlayerGameResult(player, player.compareScore(dealer)) } + return GameResult( + DealerGameResult( + winCount = playerGameResults.count { it.result == LOSS }, + lossCount = playerGameResults.count { it.result == WIN }, + drawCount = playerGameResults.count { it.result == DRAW }, + ), + playerGameResults, + ) + } + } +} diff --git a/src/main/kotlin/blackjack/domain/GameTable.kt b/src/main/kotlin/blackjack/domain/GameTable.kt index c03850275..9fda80d74 100644 --- a/src/main/kotlin/blackjack/domain/GameTable.kt +++ b/src/main/kotlin/blackjack/domain/GameTable.kt @@ -1,6 +1,6 @@ package blackjack.domain -class GameTable( +data class GameTable( private val deck: Deck, ) { fun dealInitCard(participants: List): List { diff --git a/src/main/kotlin/blackjack/domain/MatchResult.kt b/src/main/kotlin/blackjack/domain/MatchResult.kt index f0c2fe5cb..610434564 100644 --- a/src/main/kotlin/blackjack/domain/MatchResult.kt +++ b/src/main/kotlin/blackjack/domain/MatchResult.kt @@ -1,7 +1,7 @@ package blackjack.domain -enum class MatchResult { - WIN, - LOSS, - DRAW, +enum class MatchResult(val description: String) { + WIN("승"), + LOSS("패"), + DRAW("무"), } diff --git a/src/main/kotlin/blackjack/domain/Participant.kt b/src/main/kotlin/blackjack/domain/Participant.kt index db92afca2..fccfc02a7 100644 --- a/src/main/kotlin/blackjack/domain/Participant.kt +++ b/src/main/kotlin/blackjack/domain/Participant.kt @@ -1,8 +1,12 @@ package blackjack.domain +import blackjack.domain.MatchResult.LOSS import blackjack.domain.MatchResult.WIN sealed class Participant(val name: String, val cards: Cards) { + val isBust: Boolean + get() = cards.isBust + abstract fun canHit(): Boolean abstract fun hit(card: Card): Participant @@ -24,10 +28,11 @@ class Player(name: String, cards: Cards) : Participant(name, cards) { } fun compareScore(dealer: Dealer): MatchResult { - if (dealer.isBust) { - return WIN + return when { + dealer.isBust -> WIN + this.isBust -> LOSS + else -> cards.compareScore(dealer.cards) } - return cards.compareScore(dealer.cards) } companion object { @@ -40,9 +45,6 @@ class Player(name: String, cards: Cards) : Participant(name, cards) { } class Dealer(cards: Cards) : Participant("딜러", cards) { - val isBust: Boolean - get() = cards.isBust - override fun canHit(): Boolean { return cards.scoreLowerThan(DEALER_SCORE_LIMIT) } diff --git a/src/main/kotlin/blackjack/domain/dto/DealerGameResult.kt b/src/main/kotlin/blackjack/domain/dto/DealerGameResult.kt new file mode 100644 index 000000000..7a3b7b1b2 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/dto/DealerGameResult.kt @@ -0,0 +1,7 @@ +package blackjack.domain.dto + +data class DealerGameResult( + val winCount: Int, + val lossCount: Int, + val drawCount: Int, +) diff --git a/src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt b/src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt new file mode 100644 index 000000000..4651bc2ce --- /dev/null +++ b/src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt @@ -0,0 +1,9 @@ +package blackjack.domain.dto + +import blackjack.domain.MatchResult +import blackjack.domain.Player + +data class PlayerGameResult( + val player: Player, + val result: MatchResult, +) diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt index 93ec4260b..463c306f0 100644 --- a/src/main/kotlin/blackjack/view/ResultView.kt +++ b/src/main/kotlin/blackjack/view/ResultView.kt @@ -1,5 +1,6 @@ package blackjack.view +import blackjack.domain.GameResult import blackjack.domain.GameTable.Companion.INIT_CARD_DRAW_COUNT import blackjack.domain.Participant @@ -31,6 +32,20 @@ object ResultView { println("딜러는 16이하라 한장의 카드를 더 받았습니다.") } + fun printGameResult(gameResult: GameResult) { + val winMessage = + if (gameResult.dealerGameResult.winCount > 0) "${gameResult.dealerGameResult.winCount}승" else "" + val lossMessage = + if (gameResult.dealerGameResult.lossCount > 0) "${gameResult.dealerGameResult.lossCount}패" else "" + val drawMessage = + if (gameResult.dealerGameResult.drawCount > 0) "${gameResult.dealerGameResult.drawCount}무" else "" + println("## 최종 승패") + println("딜러: $winMessage $lossMessage $drawMessage") + gameResult.playerGameResults.forEach { + println("${it.player.name} ${it.result.description}") + } + } + fun linebreak() { println() } From bca0a3068252003f17f9f649ea59b71af6562781 Mon Sep 17 00:00:00 2001 From: Duhan Mo Date: Wed, 11 Dec 2024 18:51:34 +0900 Subject: [PATCH 15/15] =?UTF-8?q?fix:=20=EC=B6=9C=EB=A0=A5=EA=B5=AC?= =?UTF-8?q?=EB=AC=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +- src/main/kotlin/blackjack/Main.kt | 4 +- .../blackjack/controller/BlackjackGame.kt | 92 +++--------- .../kotlin/blackjack/controller/GameTable.kt | 46 ++++++ src/main/kotlin/blackjack/domain/Cards.kt | 46 ------ src/main/kotlin/blackjack/domain/Deck.kt | 14 -- .../kotlin/blackjack/domain/GameResult.kt | 27 ---- src/main/kotlin/blackjack/domain/GameTable.kt | 21 --- .../kotlin/blackjack/domain/Participant.kt | 63 -------- .../blackjack/domain/{ => card}/Card.kt | 4 +- src/main/kotlin/blackjack/domain/card/Deck.kt | 8 + src/main/kotlin/blackjack/domain/card/Hand.kt | 30 ++++ .../blackjack/domain/{ => card}/Rank.kt | 7 +- .../blackjack/domain/{ => card}/Suit.kt | 2 +- .../blackjack/domain/dto/PlayerGameResult.kt | 9 -- .../blackjack/domain/game/GameResult.kt | 31 ++++ .../domain/{ => game}/MatchResult.kt | 4 +- .../domain/{ => game}/dto/DealerGameResult.kt | 4 +- .../domain/game/dto/PlayerGameResult.kt | 9 ++ .../kotlin/blackjack/domain/player/Dealer.kt | 16 ++ .../kotlin/blackjack/domain/player/Player.kt | 42 ++++++ src/main/kotlin/blackjack/view/InputView.kt | 11 +- src/main/kotlin/blackjack/view/ResultView.kt | 63 ++++---- src/test/kotlin/blackjack/domain/CardTest.kt | 21 --- .../kotlin/blackjack/domain/DealerTest.kt | 19 --- src/test/kotlin/blackjack/domain/DeckTest.kt | 21 --- .../kotlin/blackjack/domain/GameTableTest.kt | 15 -- .../kotlin/blackjack/domain/PlayerTest.kt | 132 ----------------- .../kotlin/blackjack/domain/card/DeckTest.kt | 30 ++++ .../domain/{CardsTest.kt => card/HandTest.kt} | 16 +- .../blackjack/domain/{ => card}/RankTest.kt | 2 +- .../blackjack/domain/game/GameResultTest.kt | 66 +++++++++ .../blackjack/domain/player/DealerTest.kt | 42 ++++++ .../blackjack/domain/player/PlayerTest.kt | 140 ++++++++++++++++++ .../kotlin/blackjack/fixtures/CardFixture.kt | 8 +- .../kotlin/blackjack/fixtures/UserFixtures.kt | 8 - 36 files changed, 548 insertions(+), 531 deletions(-) create mode 100644 src/main/kotlin/blackjack/controller/GameTable.kt delete mode 100644 src/main/kotlin/blackjack/domain/Cards.kt delete mode 100644 src/main/kotlin/blackjack/domain/Deck.kt delete mode 100644 src/main/kotlin/blackjack/domain/GameResult.kt delete mode 100644 src/main/kotlin/blackjack/domain/GameTable.kt delete mode 100644 src/main/kotlin/blackjack/domain/Participant.kt rename src/main/kotlin/blackjack/domain/{ => card}/Card.kt (82%) create mode 100644 src/main/kotlin/blackjack/domain/card/Deck.kt create mode 100644 src/main/kotlin/blackjack/domain/card/Hand.kt rename src/main/kotlin/blackjack/domain/{ => card}/Rank.kt (76%) rename src/main/kotlin/blackjack/domain/{ => card}/Suit.kt (82%) delete mode 100644 src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt create mode 100644 src/main/kotlin/blackjack/domain/game/GameResult.kt rename src/main/kotlin/blackjack/domain/{ => game}/MatchResult.kt (64%) rename src/main/kotlin/blackjack/domain/{ => game}/dto/DealerGameResult.kt (57%) create mode 100644 src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt create mode 100644 src/main/kotlin/blackjack/domain/player/Dealer.kt create mode 100644 src/main/kotlin/blackjack/domain/player/Player.kt delete mode 100644 src/test/kotlin/blackjack/domain/CardTest.kt delete mode 100644 src/test/kotlin/blackjack/domain/DealerTest.kt delete mode 100644 src/test/kotlin/blackjack/domain/DeckTest.kt delete mode 100644 src/test/kotlin/blackjack/domain/GameTableTest.kt delete mode 100644 src/test/kotlin/blackjack/domain/PlayerTest.kt create mode 100644 src/test/kotlin/blackjack/domain/card/DeckTest.kt rename src/test/kotlin/blackjack/domain/{CardsTest.kt => card/HandTest.kt} (54%) rename src/test/kotlin/blackjack/domain/{ => card}/RankTest.kt (96%) create mode 100644 src/test/kotlin/blackjack/domain/game/GameResultTest.kt create mode 100644 src/test/kotlin/blackjack/domain/player/DealerTest.kt create mode 100644 src/test/kotlin/blackjack/domain/player/PlayerTest.kt delete mode 100644 src/test/kotlin/blackjack/fixtures/UserFixtures.kt diff --git a/README.md b/README.md index 3c69bf851..7817229dc 100644 --- a/README.md +++ b/README.md @@ -37,4 +37,8 @@ ### 기능 구현사항 - [x] 딜러는 처음받는 2장의 합계가 16이하면 반드시 1장의 카드를 추가로 받는다 - [x] 딜러가 21을 초과하면 남은 플레이어들은 패에 상관없이 승리한다 -- [x] 게임 완료 후 각 플레이어별로 승패를 출력한다 \ No newline at end of file +- [x] 게임 완료 후 각 플레이어별로 승패를 출력한다 +- [x] data class , 일반 클래스 설정 일관성 유지 +- [x] 도메인 패키지 분리 +- [x] 객체 상태를 객체가 관리 +- [x] 게임 관련 로직 분리 \ No newline at end of file diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt index 6a4dee803..c857254f0 100644 --- a/src/main/kotlin/blackjack/Main.kt +++ b/src/main/kotlin/blackjack/Main.kt @@ -1,9 +1,7 @@ package blackjack import blackjack.controller.BlackjackGame -import blackjack.view.InputView -import blackjack.view.ResultView fun main() { - BlackjackGame(InputView, ResultView).start() + BlackjackGame.start() } diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt index 6a2eb75db..c090e01c1 100644 --- a/src/main/kotlin/blackjack/controller/BlackjackGame.kt +++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt @@ -1,87 +1,35 @@ package blackjack.controller -import blackjack.domain.Dealer -import blackjack.domain.Deck -import blackjack.domain.GameResult -import blackjack.domain.GameTable -import blackjack.domain.Participant -import blackjack.domain.Player +import blackjack.domain.card.Deck +import blackjack.domain.player.Dealer +import blackjack.domain.player.Player import blackjack.view.InputView import blackjack.view.ResultView -data class BlackjackGame( - private val inputView: InputView, - private val resultView: ResultView, -) { +object BlackjackGame { fun start() { - val gameTable = GameTable(Deck.create()) - val participants = playGame(gameTable) - printCard(participants) - printGameResult(participants) + val gameTable = setUp() + initDeal(gameTable) + turnStart(gameTable) + ResultView.printAfterTurn(gameTable) } - private fun playGame(gameTable: GameTable): List { - val participants = setUpInitCard(gameTable) - val (players, dealer) = Participant.separate(participants) - val gamedPlayers = playersTurn(players, gameTable) - resultView.linebreak() - val gamedDealer = dealerTurn(dealer, gameTable) - return gamedPlayers + gamedDealer + private fun setUp(): GameTable { + val gameTable = GameTable(Deck(), Dealer(), getPlayers()) + ResultView.linebreak() + return gameTable } - private fun setUpInitCard(gameTable: GameTable): List { - val participants = gameTable.dealInitCard(getParticipants()) - resultView.linebreak() - resultView.printInitCardReceive(participants) - resultView.printParticipantsCard(participants = participants, printScore = false) - resultView.linebreak() - return participants - } - - private fun getParticipants(): List { - return buildList { - add(Dealer.create()) - addAll(inputView.inputNames().map { Player.create(name = it) }) - } - } - - private fun playersTurn( - participants: List, - gameTable: GameTable, - ): List { - return participants.map { playerTurn(it, gameTable) } - } - - private tailrec fun playerTurn( - player: Participant, - gameTable: GameTable, - ): Participant { - if (!player.canHit() || !inputView.inputHit(player)) { - return player - } - val hitPlayer = gameTable.hit(player) - resultView.printParticipantCard(participant = hitPlayer, printScore = false) - return playerTurn(hitPlayer, gameTable) - } - - private tailrec fun dealerTurn( - dealer: Participant, - gameTable: GameTable, - ): Participant { - if (!dealer.canHit()) { - return dealer - } - resultView.printDealerHit() - return dealerTurn(gameTable.hit(dealer), gameTable) - } + private fun getPlayers(): List = InputView.inputNames().map { Player(it) } - private fun printCard(participants: List) { - resultView.linebreak() - resultView.printParticipantsCard(participants = participants, printScore = true) + private fun initDeal(gameTable: GameTable) { + gameTable.dealInitCard() + ResultView.printDealInitCard(gameTable) } - private fun printGameResult(participants: List) { - resultView.linebreak() - resultView.printGameResult(GameResult.from(participants)) + private fun turnStart(gameTable: GameTable) { + gameTable.playersTurn() + ResultView.linebreak() + gameTable.dealerTurn() } } diff --git a/src/main/kotlin/blackjack/controller/GameTable.kt b/src/main/kotlin/blackjack/controller/GameTable.kt new file mode 100644 index 000000000..4fa31a914 --- /dev/null +++ b/src/main/kotlin/blackjack/controller/GameTable.kt @@ -0,0 +1,46 @@ +package blackjack.controller + +import blackjack.domain.card.Deck +import blackjack.domain.game.GameResult +import blackjack.domain.player.Dealer +import blackjack.domain.player.Player +import blackjack.view.InputView +import blackjack.view.ResultView + +class GameTable( + val deck: Deck, + val dealer: Dealer, + val players: List, +) { + fun dealInitCard() = + repeat(INIT_CARD_DRAW_COUNT) { + dealer.hit(deck.draw()) + players.forEach { it.hit(deck.draw()) } + } + + fun playersTurn() = players.forEach { playerTurn(it) } + + fun dealerTurn() { + if (!dealer.canHit()) { + return + } + ResultView.printDealerHit() + dealer.hit(deck.draw()) + dealerTurn() + } + + fun getGameResult(): GameResult = GameResult.from(dealer, players) + + private fun playerTurn(player: Player) { + if (!player.canHit() || !InputView.inputHit(player)) { + return + } + player.hit(deck.draw()) + ResultView.printPlayerCard(player) + playerTurn(player) + } + + companion object { + const val INIT_CARD_DRAW_COUNT = 2 + } +} diff --git a/src/main/kotlin/blackjack/domain/Cards.kt b/src/main/kotlin/blackjack/domain/Cards.kt deleted file mode 100644 index aabe71254..000000000 --- a/src/main/kotlin/blackjack/domain/Cards.kt +++ /dev/null @@ -1,46 +0,0 @@ -package blackjack.domain - -import blackjack.domain.MatchResult.DRAW -import blackjack.domain.MatchResult.LOSS -import blackjack.domain.MatchResult.WIN - -data class Cards(val values: List) { - val score: Int - get() = calculateScore() - - val isBust: Boolean - get() = calculateScore() > BLACKJACK_SCORE_LIMIT - - fun scoreLowerThan(limit: Int): Boolean { - return score < limit - } - - fun compareScore(other: Cards): MatchResult { - return when { - score > other.score -> WIN - score < other.score -> LOSS - else -> DRAW - } - } - - fun add(card: Card): Cards { - return Cards(values + card) - } - - private fun calculateScore(): Int { - val totalScore = values.sumOf { it.score } - var aceCount = values.count { it.isAce } - - var adjustedScore = totalScore - while (adjustedScore > BLACKJACK_SCORE_LIMIT && aceCount > 0) { - adjustedScore -= ACE_SCORE_DIFFERENCE - aceCount-- - } - return adjustedScore - } - - companion object { - private const val BLACKJACK_SCORE_LIMIT = 21 - private const val ACE_SCORE_DIFFERENCE = 10 - } -} diff --git a/src/main/kotlin/blackjack/domain/Deck.kt b/src/main/kotlin/blackjack/domain/Deck.kt deleted file mode 100644 index d6b33e27c..000000000 --- a/src/main/kotlin/blackjack/domain/Deck.kt +++ /dev/null @@ -1,14 +0,0 @@ -package blackjack.domain - -data class Deck(private val cards: MutableList) { - fun draw(): Card { - check(cards.isNotEmpty()) { "카드가 모두 소진되었습니다" } - return cards.removeFirst() - } - - companion object { - fun create(): Deck { - return Deck(Card.ALL.shuffled().toMutableList()) - } - } -} diff --git a/src/main/kotlin/blackjack/domain/GameResult.kt b/src/main/kotlin/blackjack/domain/GameResult.kt deleted file mode 100644 index 5cfbb1cfb..000000000 --- a/src/main/kotlin/blackjack/domain/GameResult.kt +++ /dev/null @@ -1,27 +0,0 @@ -package blackjack.domain - -import blackjack.domain.MatchResult.DRAW -import blackjack.domain.MatchResult.LOSS -import blackjack.domain.MatchResult.WIN -import blackjack.domain.dto.DealerGameResult -import blackjack.domain.dto.PlayerGameResult - -data class GameResult( - val dealerGameResult: DealerGameResult, - val playerGameResults: List, -) { - companion object { - fun from(participants: List): GameResult { - val (players, dealer) = Participant.separate(participants) - val playerGameResults = players.map { player -> PlayerGameResult(player, player.compareScore(dealer)) } - return GameResult( - DealerGameResult( - winCount = playerGameResults.count { it.result == LOSS }, - lossCount = playerGameResults.count { it.result == WIN }, - drawCount = playerGameResults.count { it.result == DRAW }, - ), - playerGameResults, - ) - } - } -} diff --git a/src/main/kotlin/blackjack/domain/GameTable.kt b/src/main/kotlin/blackjack/domain/GameTable.kt deleted file mode 100644 index 9fda80d74..000000000 --- a/src/main/kotlin/blackjack/domain/GameTable.kt +++ /dev/null @@ -1,21 +0,0 @@ -package blackjack.domain - -data class GameTable( - private val deck: Deck, -) { - fun dealInitCard(participants: List): List { - return participants.map { participant -> - (1..INIT_CARD_DRAW_COUNT).fold(participant) { acc, _ -> - acc.hit(deck.draw()) - } - } - } - - fun hit(participant: Participant): Participant { - return participant.hit(deck.draw()) - } - - companion object { - const val INIT_CARD_DRAW_COUNT = 2 - } -} diff --git a/src/main/kotlin/blackjack/domain/Participant.kt b/src/main/kotlin/blackjack/domain/Participant.kt deleted file mode 100644 index fccfc02a7..000000000 --- a/src/main/kotlin/blackjack/domain/Participant.kt +++ /dev/null @@ -1,63 +0,0 @@ -package blackjack.domain - -import blackjack.domain.MatchResult.LOSS -import blackjack.domain.MatchResult.WIN - -sealed class Participant(val name: String, val cards: Cards) { - val isBust: Boolean - get() = cards.isBust - - abstract fun canHit(): Boolean - - abstract fun hit(card: Card): Participant - - companion object { - fun separate(participants: List): Pair, Dealer> { - return participants.filterIsInstance() to participants.first { it is Dealer } as Dealer - } - } -} - -class Player(name: String, cards: Cards) : Participant(name, cards) { - override fun canHit(): Boolean { - return cards.scoreLowerThan(PLAYER_SCORE_LIMIT) - } - - override fun hit(card: Card): Player { - return Player(this.name, cards.add(card)) - } - - fun compareScore(dealer: Dealer): MatchResult { - return when { - dealer.isBust -> WIN - this.isBust -> LOSS - else -> cards.compareScore(dealer.cards) - } - } - - companion object { - private const val PLAYER_SCORE_LIMIT = 21 - - fun create(name: String): Player { - return Player(name, Cards(emptyList())) - } - } -} - -class Dealer(cards: Cards) : Participant("딜러", cards) { - override fun canHit(): Boolean { - return cards.scoreLowerThan(DEALER_SCORE_LIMIT) - } - - override fun hit(card: Card): Dealer { - return Dealer(cards.add(card)) - } - - companion object { - private const val DEALER_SCORE_LIMIT = 17 - - fun create(): Dealer { - return Dealer(Cards(emptyList())) - } - } -} diff --git a/src/main/kotlin/blackjack/domain/Card.kt b/src/main/kotlin/blackjack/domain/card/Card.kt similarity index 82% rename from src/main/kotlin/blackjack/domain/Card.kt rename to src/main/kotlin/blackjack/domain/card/Card.kt index 3b1cb0e9d..49e38e89d 100644 --- a/src/main/kotlin/blackjack/domain/Card.kt +++ b/src/main/kotlin/blackjack/domain/card/Card.kt @@ -1,6 +1,6 @@ -package blackjack.domain +package blackjack.domain.card -import blackjack.domain.Rank.ACE +import blackjack.domain.card.Rank.ACE data class Card( val rank: Rank, diff --git a/src/main/kotlin/blackjack/domain/card/Deck.kt b/src/main/kotlin/blackjack/domain/card/Deck.kt new file mode 100644 index 000000000..6f20fd3ce --- /dev/null +++ b/src/main/kotlin/blackjack/domain/card/Deck.kt @@ -0,0 +1,8 @@ +package blackjack.domain.card + +class Deck(private val cards: MutableList = Card.ALL.shuffled().toMutableList()) { + fun draw(): Card { + check(cards.isNotEmpty()) { "카드가 모두 소진되었습니다" } + return cards.removeFirst() + } +} diff --git a/src/main/kotlin/blackjack/domain/card/Hand.kt b/src/main/kotlin/blackjack/domain/card/Hand.kt new file mode 100644 index 000000000..bebed6e7d --- /dev/null +++ b/src/main/kotlin/blackjack/domain/card/Hand.kt @@ -0,0 +1,30 @@ +package blackjack.domain.card + +class Hand(private val _cards: MutableList = mutableListOf()) { + val cards: List + get() = _cards.toList() + + val score: Int + get() = calculateScore() + + fun add(card: Card) { + _cards.add(card) + } + + private fun calculateScore(): Int { + val totalScore = cards.sumOf { it.score } + var aceCount = cards.count { it.isAce } + + var adjustedScore = totalScore + while (adjustedScore > BLACKJACK_SCORE_LIMIT && aceCount > 0) { + adjustedScore -= ACE_SCORE_DIFFERENCE + aceCount-- + } + return adjustedScore + } + + companion object { + private const val BLACKJACK_SCORE_LIMIT = 21 + private const val ACE_SCORE_DIFFERENCE = 10 + } +} diff --git a/src/main/kotlin/blackjack/domain/Rank.kt b/src/main/kotlin/blackjack/domain/card/Rank.kt similarity index 76% rename from src/main/kotlin/blackjack/domain/Rank.kt rename to src/main/kotlin/blackjack/domain/card/Rank.kt index b65d2a669..7b7e1ad82 100644 --- a/src/main/kotlin/blackjack/domain/Rank.kt +++ b/src/main/kotlin/blackjack/domain/card/Rank.kt @@ -1,4 +1,4 @@ -package blackjack.domain +package blackjack.domain.card enum class Rank(val value: String, val score: Int) { ACE("A", 11), @@ -17,9 +17,8 @@ enum class Rank(val value: String, val score: Int) { ; companion object { - fun from(value: String): Rank { - return entries.firstOrNull { it.value == value } + fun from(value: String): Rank = + entries.firstOrNull { it.value == value } ?: throw IllegalArgumentException("유효하지 않은 랭크 값입니다: $value") - } } } diff --git a/src/main/kotlin/blackjack/domain/Suit.kt b/src/main/kotlin/blackjack/domain/card/Suit.kt similarity index 82% rename from src/main/kotlin/blackjack/domain/Suit.kt rename to src/main/kotlin/blackjack/domain/card/Suit.kt index bc2d994b1..fd563f625 100644 --- a/src/main/kotlin/blackjack/domain/Suit.kt +++ b/src/main/kotlin/blackjack/domain/card/Suit.kt @@ -1,4 +1,4 @@ -package blackjack.domain +package blackjack.domain.card enum class Suit(val description: String) { SPADE("스페이드"), diff --git a/src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt b/src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt deleted file mode 100644 index 4651bc2ce..000000000 --- a/src/main/kotlin/blackjack/domain/dto/PlayerGameResult.kt +++ /dev/null @@ -1,9 +0,0 @@ -package blackjack.domain.dto - -import blackjack.domain.MatchResult -import blackjack.domain.Player - -data class PlayerGameResult( - val player: Player, - val result: MatchResult, -) diff --git a/src/main/kotlin/blackjack/domain/game/GameResult.kt b/src/main/kotlin/blackjack/domain/game/GameResult.kt new file mode 100644 index 000000000..cf7856903 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/game/GameResult.kt @@ -0,0 +1,31 @@ +package blackjack.domain.game + +import blackjack.domain.game.MatchResult.DRAW +import blackjack.domain.game.MatchResult.LOSE +import blackjack.domain.game.MatchResult.WIN +import blackjack.domain.game.dto.DealerGameResult +import blackjack.domain.game.dto.PlayerGameResult +import blackjack.domain.player.Dealer +import blackjack.domain.player.Player + +data class GameResult( + val dealerGameResult: DealerGameResult, + val playerGameResults: List, +) { + companion object { + fun from( + dealer: Dealer, + players: List, + ): GameResult { + val playerGameResults = players.map { player -> PlayerGameResult(player, player.matchHand(dealer)) } + return GameResult( + DealerGameResult( + winCount = playerGameResults.count { it.result == LOSE }, + loseCount = playerGameResults.count { it.result == WIN }, + drawCount = playerGameResults.count { it.result == DRAW }, + ), + playerGameResults, + ) + } + } +} diff --git a/src/main/kotlin/blackjack/domain/MatchResult.kt b/src/main/kotlin/blackjack/domain/game/MatchResult.kt similarity index 64% rename from src/main/kotlin/blackjack/domain/MatchResult.kt rename to src/main/kotlin/blackjack/domain/game/MatchResult.kt index 610434564..fd60f033b 100644 --- a/src/main/kotlin/blackjack/domain/MatchResult.kt +++ b/src/main/kotlin/blackjack/domain/game/MatchResult.kt @@ -1,7 +1,7 @@ -package blackjack.domain +package blackjack.domain.game enum class MatchResult(val description: String) { WIN("승"), - LOSS("패"), + LOSE("패"), DRAW("무"), } diff --git a/src/main/kotlin/blackjack/domain/dto/DealerGameResult.kt b/src/main/kotlin/blackjack/domain/game/dto/DealerGameResult.kt similarity index 57% rename from src/main/kotlin/blackjack/domain/dto/DealerGameResult.kt rename to src/main/kotlin/blackjack/domain/game/dto/DealerGameResult.kt index 7a3b7b1b2..aa160e028 100644 --- a/src/main/kotlin/blackjack/domain/dto/DealerGameResult.kt +++ b/src/main/kotlin/blackjack/domain/game/dto/DealerGameResult.kt @@ -1,7 +1,7 @@ -package blackjack.domain.dto +package blackjack.domain.game.dto data class DealerGameResult( val winCount: Int, - val lossCount: Int, + val loseCount: Int, val drawCount: Int, ) diff --git a/src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt b/src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt new file mode 100644 index 000000000..40e60dfc9 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt @@ -0,0 +1,9 @@ +package blackjack.domain.game.dto + +import blackjack.domain.game.MatchResult +import blackjack.domain.player.Player + +data class PlayerGameResult( + val player: Player, + val result: MatchResult, +) diff --git a/src/main/kotlin/blackjack/domain/player/Dealer.kt b/src/main/kotlin/blackjack/domain/player/Dealer.kt new file mode 100644 index 000000000..c8d958b08 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/player/Dealer.kt @@ -0,0 +1,16 @@ +package blackjack.domain.player + +import blackjack.domain.card.Card + +class Dealer : Player( + DEALER_NAME, +) { + override fun canHit(): Boolean = hand.score < DEALER_SCORE_LIMIT + + override fun hit(card: Card) = hand.add(card) + + companion object { + private const val DEALER_NAME = "딜러" + private const val DEALER_SCORE_LIMIT = 17 + } +} diff --git a/src/main/kotlin/blackjack/domain/player/Player.kt b/src/main/kotlin/blackjack/domain/player/Player.kt new file mode 100644 index 000000000..ff465d03c --- /dev/null +++ b/src/main/kotlin/blackjack/domain/player/Player.kt @@ -0,0 +1,42 @@ +package blackjack.domain.player + +import blackjack.domain.card.Card +import blackjack.domain.card.Hand +import blackjack.domain.game.MatchResult +import blackjack.domain.game.MatchResult.DRAW +import blackjack.domain.game.MatchResult.LOSE +import blackjack.domain.game.MatchResult.WIN + +open class Player( + val name: String, + val hand: Hand = Hand(), +) { + open fun canHit(): Boolean = hand.score < PLAYER_SCORE_LIMIT + + open fun hit(card: Card) = hand.add(card) + + fun matchHand(other: Player): MatchResult = + when { + other.isBust() -> WIN + this.isBust() -> LOSE + other.isBlackjack() && this.isBlackjack() -> DRAW + other.isBlackjack() -> LOSE + this.isBlackjack() -> WIN + else -> compareScore(other) + } + + private fun compareScore(other: Player): MatchResult = + when { + this.hand.score > other.hand.score -> WIN + this.hand.score < other.hand.score -> LOSE + else -> DRAW + } + + private fun isBust(): Boolean = hand.score > PLAYER_SCORE_LIMIT + + private fun isBlackjack(): Boolean = hand.score == PLAYER_SCORE_LIMIT + + companion object { + private const val PLAYER_SCORE_LIMIT = 21 + } +} diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt index ec7feab94..79b34c263 100644 --- a/src/main/kotlin/blackjack/view/InputView.kt +++ b/src/main/kotlin/blackjack/view/InputView.kt @@ -1,7 +1,6 @@ package blackjack.view -import blackjack.domain.Dealer -import blackjack.domain.Participant +import blackjack.domain.player.Player object InputView { fun inputNames(): List { @@ -13,12 +12,8 @@ object InputView { return names } - fun inputHit(participant: Participant): Boolean { - if (participant is Dealer) { - return true - } - - println("${participant.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") + fun inputHit(player: Player): Boolean { + println("${player.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") return when (readlnOrNull()) { "y" -> true "n" -> false diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt index 463c306f0..80ec6a4d4 100644 --- a/src/main/kotlin/blackjack/view/ResultView.kt +++ b/src/main/kotlin/blackjack/view/ResultView.kt @@ -1,52 +1,57 @@ package blackjack.view -import blackjack.domain.GameResult -import blackjack.domain.GameTable.Companion.INIT_CARD_DRAW_COUNT -import blackjack.domain.Participant +import blackjack.controller.GameTable +import blackjack.controller.GameTable.Companion.INIT_CARD_DRAW_COUNT +import blackjack.domain.player.Player object ResultView { - fun printInitCardReceive(participants: List) { - val players = Participant.separate(participants).first - println("딜러와 ${players.joinToString(", ") { it.name }}에게 ${INIT_CARD_DRAW_COUNT}장의 카드를 나누었습니다.") + fun printDealerHit() = println("딜러는 16이하라 한장의 카드를 더 받았습니다.") + + fun printDealInitCard(gameTable: GameTable) { + println("딜러와 ${gameTable.players.joinToString(", ") { it.name }}에게 ${INIT_CARD_DRAW_COUNT}장의 카드를 나누었습니다.") + printPlayerCard(gameTable.dealer) + printPlayersCard(gameTable.players) + linebreak() } - fun printParticipantsCard( - participants: List, - printScore: Boolean, - ) { - val (players, dealer) = Participant.separate(participants) - printParticipantCard(participant = dealer, printScore = printScore) - players.forEach { printParticipantCard(participant = it, printScore = printScore) } + fun printAfterTurn(gameTable: GameTable) { + printFinalHand(gameTable) + printGameResult(gameTable) } - fun printParticipantCard( - participant: Participant, - printScore: Boolean, + fun printPlayerCard( + player: Player, + printScore: Boolean = false, ) { - val cards = participant.cards.values.joinToString(", ") { "${it.rank.value}${it.suit.description}" } - val scoreText = "- 결과: ${participant.cards.score}" - println("${participant.name} 카드: $cards ${if (printScore) scoreText else ""}") + val cards = player.hand.cards.joinToString(", ") { "${it.rank.value}${it.suit.description}" } + val scoreText = "- 결과: ${player.hand.score}" + println("${player.name} 카드: $cards ${if (printScore) scoreText else ""}") } - fun printDealerHit() { - println("딜러는 16이하라 한장의 카드를 더 받았습니다.") + fun linebreak() = println() + + private fun printFinalHand(gameTable: GameTable) { + linebreak() + printPlayerCard(gameTable.dealer, printScore = true) + printPlayersCard(gameTable.players, printScore = true) } - fun printGameResult(gameResult: GameResult) { + private fun printGameResult(gameTable: GameTable) { + linebreak() + val gameResult = gameTable.getGameResult() val winMessage = if (gameResult.dealerGameResult.winCount > 0) "${gameResult.dealerGameResult.winCount}승" else "" val lossMessage = - if (gameResult.dealerGameResult.lossCount > 0) "${gameResult.dealerGameResult.lossCount}패" else "" + if (gameResult.dealerGameResult.loseCount > 0) "${gameResult.dealerGameResult.loseCount}패" else "" val drawMessage = if (gameResult.dealerGameResult.drawCount > 0) "${gameResult.dealerGameResult.drawCount}무" else "" println("## 최종 승패") println("딜러: $winMessage $lossMessage $drawMessage") - gameResult.playerGameResults.forEach { - println("${it.player.name} ${it.result.description}") - } + gameResult.playerGameResults.forEach { println("${it.player.name}: ${it.result.description}") } } - fun linebreak() { - println() - } + private fun printPlayersCard( + players: List, + printScore: Boolean = false, + ) = players.forEach { printPlayerCard(it, printScore) } } diff --git a/src/test/kotlin/blackjack/domain/CardTest.kt b/src/test/kotlin/blackjack/domain/CardTest.kt deleted file mode 100644 index 727410f08..000000000 --- a/src/test/kotlin/blackjack/domain/CardTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package blackjack.domain - -import blackjack.domain.Rank.ACE -import blackjack.domain.Rank.THREE -import blackjack.domain.Suit.SPADE -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe - -class CardTest : StringSpec({ - "카드는 에이스로 만들어진다면 에이스 카드이다" { - val card = Card(ACE, SPADE) - - card.isAce shouldBe true - } - - "카드는 스페이드로 만들어진다면 에이스 카드가 아니다" { - val card = Card(THREE, SPADE) - - card.isAce shouldBe false - } -}) diff --git a/src/test/kotlin/blackjack/domain/DealerTest.kt b/src/test/kotlin/blackjack/domain/DealerTest.kt deleted file mode 100644 index c915c586d..000000000 --- a/src/test/kotlin/blackjack/domain/DealerTest.kt +++ /dev/null @@ -1,19 +0,0 @@ -package blackjack.domain - -import blackjack.fixtures.createCard -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe - -class DealerTest : StringSpec({ - "딜러는 카드를 가진다" { - val dealer = Dealer.create() - dealer.cards shouldNotBe null - } - - "딜러는 첫 2장의 합이 16이하면 반드시 카드를 받아야 한다" { - val dealer = Dealer(Cards(listOf(createCard("10"), createCard("6")))) - - dealer.canHit() shouldBe true - } -}) diff --git a/src/test/kotlin/blackjack/domain/DeckTest.kt b/src/test/kotlin/blackjack/domain/DeckTest.kt deleted file mode 100644 index bbcd5afbc..000000000 --- a/src/test/kotlin/blackjack/domain/DeckTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package blackjack.domain - -import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldNotBe - -class DeckTest : StringSpec({ - "덱에서 카드를 하나 꺼낸다" { - val deck = Deck.create() - val card = deck.draw() - - card shouldNotBe null - } - - "덱에 카드가 없을 때 카드를 꺼내면 예외 발생한다" { - val deck = Deck.create() - repeat(52) { deck.draw() } - - shouldThrow { deck.draw() } - } -}) diff --git a/src/test/kotlin/blackjack/domain/GameTableTest.kt b/src/test/kotlin/blackjack/domain/GameTableTest.kt deleted file mode 100644 index 8b0517a3b..000000000 --- a/src/test/kotlin/blackjack/domain/GameTableTest.kt +++ /dev/null @@ -1,15 +0,0 @@ -package blackjack.domain - -import blackjack.fixtures.createPlayers -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe - -class GameTableTest : StringSpec({ - "최초 딜 시 카드를 2장 나누어준다" { - val players = GameTable(Deck.create()).dealInitCard(createPlayers()) - - players.forEach { - it.cards.values.size shouldBe 2 - } - } -}) diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt deleted file mode 100644 index aa5db26b9..000000000 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ /dev/null @@ -1,132 +0,0 @@ -package blackjack.domain - -import blackjack.domain.MatchResult.DRAW -import blackjack.domain.MatchResult.LOSS -import blackjack.domain.MatchResult.WIN -import blackjack.domain.Suit.CLUB -import blackjack.domain.Suit.DIAMOND -import blackjack.domain.Suit.HEART -import blackjack.domain.Suit.SPADE -import blackjack.fixtures.createCard -import io.kotest.core.spec.style.StringSpec -import io.kotest.data.forAll -import io.kotest.data.headers -import io.kotest.data.row -import io.kotest.data.table -import io.kotest.inspectors.forAll -import io.kotest.matchers.shouldBe - -class PlayerTest : StringSpec({ - "플레이어는 카드목록의 점수합이 21점 미만일 경우 카드를 더 받을 수 있다" { - table( - headers("ranks"), - row(listOf("2", "3", "4")), - row(listOf("4", "5", "10")), - row(listOf("K", "Q")), - row(listOf("5", "5", "4", "3", "2")), - ).forAll { ranks -> - val cards = Cards(ranks.map { createCard(it) }) - - Player("홍길동", cards).canHit() shouldBe true - } - } - - "플레이어는 카드목록의 점수합이 21점 이상할 경우 카드를 더 받을 수 없다" { - table( - headers("ranks", "score"), - row(listOf("J", "Q", "K"), 30), - row(listOf("A", "K"), 21), - row(listOf("Q", "10", "A"), 21), - row(listOf("10", "9", "2"), 21), - row(listOf("10", "10", "3"), 23), - ).forAll { ranks, score -> - val cards = Cards(ranks.map { createCard(it) }) - - cards.score shouldBe score - Player("홍길동", cards).canHit() shouldBe false - } - } - - "플레이어는 카드 2장을 받은 후 점수 합이 21점인 경우 카드를 더 받지 못한다" { - val cards = Cards(emptyList()) - val player = - Player("홍길동", cards) - .hit(createCard("A")) - .hit(createCard("K")) - - player.canHit() shouldBe false - } - - "플레이어는 카드 2장을 받은 후 점수 합이 21점 미만인 경우 카드를 더 받을 수 있다" { - val cards = Cards(emptyList()) - val player = - Player("홍길동", cards) - .hit(createCard("10")) - .hit(createCard("5")) - - player.canHit() shouldBe true - } - - "플레이어가 딜러보다 점수가 높다면 승리한다" { - val cards = Cards(emptyList()) - val player = - Player("홍길동", cards) - .hit(createCard("10")) - .hit(createCard("5")) - val dealer = Dealer.create().hit(createCard("10")) - - player.compareScore(dealer) shouldBe WIN - } - - "플레이어가 딜러보다 점수가 낮다면 패배한다" { - val cards = Cards(emptyList()) - val player = - Player("홍길동", cards) - .hit(createCard("5")) - val dealer = - Dealer.create() - .hit(createCard("10")) - - player.compareScore(dealer) shouldBe LOSS - } - - "플레이어가 딜러보와 점수가 같다면 무승부다" { - val cards = Cards(emptyList()) - val player = - Player("홍길동", cards) - .hit(createCard("5")) - val dealer = - Dealer.create() - .hit(createCard("5")) - - player.compareScore(dealer) shouldBe DRAW - } - - "플레이어의 패에 상관없이 딜러의 점수가 21점이 넘는다면 승리한다" { - val cards = Cards(emptyList()) - val biggerScorePlayer = - Player("김큰점수", cards) - .hit(createCard("K", SPADE)) - .hit(createCard("Q", SPADE)) - .hit(createCard("J", SPADE)) - val smallerScorePlayer = - Player("박작은점수", cards) - .hit(createCard("K", HEART)) - .hit(createCard("Q", HEART)) - .hit(createCard("J", HEART)) - val sameScorePlayer = - Player("최같은점수", cards) - .hit(createCard("10", CLUB)) - .hit(createCard("10", CLUB)) - .hit(createCard("3", CLUB)) - val dealer = - Dealer.create() - .hit(createCard("10", DIAMOND)) - .hit(createCard("10", DIAMOND)) - .hit(createCard("3", DIAMOND)) - - listOf(biggerScorePlayer, smallerScorePlayer, sameScorePlayer).forAll { player -> - player.compareScore(dealer) shouldBe WIN - } - } -}) diff --git a/src/test/kotlin/blackjack/domain/card/DeckTest.kt b/src/test/kotlin/blackjack/domain/card/DeckTest.kt new file mode 100644 index 000000000..b82dcc0d4 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/card/DeckTest.kt @@ -0,0 +1,30 @@ +package blackjack.domain.card + +import blackjack.domain.card.Rank.ACE +import blackjack.fixtures.createCard +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe + +class DeckTest : BehaviorSpec({ + Given("덱에 카드가 존재하는 경우") { + val deck = Deck(mutableListOf(createCard("A"))) + + When("드로우하면") { + Then("카드를 반환한다") { + deck.draw().rank shouldBe ACE + } + } + } + Given("덱에 카드가 없는 경우") { + val deck = Deck(mutableListOf()) + + When("드로우하면") { + Then("예외 발생한다") { + shouldThrow { + deck.draw() + } + } + } + } +}) diff --git a/src/test/kotlin/blackjack/domain/CardsTest.kt b/src/test/kotlin/blackjack/domain/card/HandTest.kt similarity index 54% rename from src/test/kotlin/blackjack/domain/CardsTest.kt rename to src/test/kotlin/blackjack/domain/card/HandTest.kt index 280d5844d..e53a937f6 100644 --- a/src/test/kotlin/blackjack/domain/CardsTest.kt +++ b/src/test/kotlin/blackjack/domain/card/HandTest.kt @@ -1,15 +1,15 @@ -package blackjack.domain +package blackjack.domain.card import blackjack.fixtures.createCard -import io.kotest.core.spec.style.StringSpec +import io.kotest.core.spec.style.BehaviorSpec import io.kotest.data.forAll import io.kotest.data.headers import io.kotest.data.row import io.kotest.data.table import io.kotest.matchers.shouldBe -class CardsTest : StringSpec({ - "카드목록의 점수합이 21을 초과할 경우 에이스는 1점으로 보정된다" { +class HandTest : BehaviorSpec({ + Given("카드목록의 점수 합이 21점을 초과하는 경우") { table( headers("ranks", "expected"), row(listOf("A", "2", "10"), 13), @@ -18,9 +18,13 @@ class CardsTest : StringSpec({ row(listOf("A", "A", "A"), 13), row(listOf("A", "3", "9"), 13), ).forAll { ranks, expected -> - val cards = Cards(ranks.map { createCard(it) }) + val hand = Hand(ranks.map { createCard(it) }.toMutableList()) - cards.score shouldBe expected + When("점수 계산하면") { + Then("에이스는 1점으로 보정된다") { + hand.score shouldBe expected + } + } } } }) diff --git a/src/test/kotlin/blackjack/domain/RankTest.kt b/src/test/kotlin/blackjack/domain/card/RankTest.kt similarity index 96% rename from src/test/kotlin/blackjack/domain/RankTest.kt rename to src/test/kotlin/blackjack/domain/card/RankTest.kt index dbe94e535..0d099ad8f 100644 --- a/src/test/kotlin/blackjack/domain/RankTest.kt +++ b/src/test/kotlin/blackjack/domain/card/RankTest.kt @@ -1,4 +1,4 @@ -package blackjack.domain +package blackjack.domain.card import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec diff --git a/src/test/kotlin/blackjack/domain/game/GameResultTest.kt b/src/test/kotlin/blackjack/domain/game/GameResultTest.kt new file mode 100644 index 000000000..0864293be --- /dev/null +++ b/src/test/kotlin/blackjack/domain/game/GameResultTest.kt @@ -0,0 +1,66 @@ +package blackjack.domain.game + +import blackjack.domain.card.Suit.DIAMOND +import blackjack.domain.card.Suit.HEART +import blackjack.domain.card.Suit.SPADE +import blackjack.domain.player.Dealer +import blackjack.domain.player.Player +import blackjack.fixtures.createCard +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe + +class GameResultTest : BehaviorSpec({ + Given("플레이어가 이긴 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("3", SPADE)) + player1.hit(createCard("4", HEART)) + player2.hit(createCard("5", DIAMOND)) + + When("게임결과를 생성하면") { + val actual = GameResult.from(dealer, listOf(player1, player2)) + + Then("딜러의 패배 카운트가 증가한다") { + actual.dealerGameResult.loseCount shouldBe 2 + } + } + } + + Given("플레이어가 진 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("10", SPADE)) + player1.hit(createCard("4", HEART)) + player2.hit(createCard("5", DIAMOND)) + + When("게임결과를 생성하면") { + val actual = GameResult.from(dealer, listOf(player1, player2)) + + Then("딜러의 승리 카운트가 증가한다") { + actual.dealerGameResult.winCount shouldBe 2 + } + } + } + + Given("플레이어가 딜러와 무승부인 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("10", SPADE)) + player1.hit(createCard("10", HEART)) + player2.hit(createCard("10", DIAMOND)) + + When("게임결과를 생성하면") { + val actual = GameResult.from(dealer, listOf(player1, player2)) + + Then("딜러의 무승부 카운트가 증가한다") { + actual.dealerGameResult.drawCount shouldBe 2 + } + } + } +}) diff --git a/src/test/kotlin/blackjack/domain/player/DealerTest.kt b/src/test/kotlin/blackjack/domain/player/DealerTest.kt new file mode 100644 index 000000000..7f4b9cb6e --- /dev/null +++ b/src/test/kotlin/blackjack/domain/player/DealerTest.kt @@ -0,0 +1,42 @@ +package blackjack.domain.player + +import blackjack.fixtures.createCard +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe + +class DealerTest : BehaviorSpec({ + Given("딜러 히트하는 경우") { + val dealer = Dealer() + + When("히트하면") { + dealer.hit(createCard("J")) + + Then("카드를 추가로 갖는다") { + dealer.hand.cards.count() shouldBe 1 + } + } + } + + Given("딜러 핸드가 17점 이상인 경우") { + val dealer = Dealer() + dealer.hit(createCard("J")) + dealer.hit(createCard("Q")) + + When("히트 가능 여부 질의하면") { + Then("결과 false 이다") { + dealer.canHit() shouldBe false + } + } + } + + Given("딜러 핸드가 17점 미만인 경우") { + val dealer = Dealer() + dealer.hit(createCard("J")) + + When("히트 가능 여부 질의하면") { + Then("결과는 true 이다") { + dealer.canHit() shouldBe true + } + } + } +}) diff --git a/src/test/kotlin/blackjack/domain/player/PlayerTest.kt b/src/test/kotlin/blackjack/domain/player/PlayerTest.kt new file mode 100644 index 000000000..6ddb5c533 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/player/PlayerTest.kt @@ -0,0 +1,140 @@ +package blackjack.domain.player + +import blackjack.domain.card.Hand +import blackjack.domain.card.Suit.DIAMOND +import blackjack.domain.card.Suit.HEART +import blackjack.domain.card.Suit.SPADE +import blackjack.domain.game.MatchResult.DRAW +import blackjack.domain.game.MatchResult.LOSE +import blackjack.domain.game.MatchResult.WIN +import blackjack.fixtures.createCard +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.data.forAll +import io.kotest.data.headers +import io.kotest.data.row +import io.kotest.data.table +import io.kotest.inspectors.forAll +import io.kotest.matchers.shouldBe + +class PlayerTest : BehaviorSpec({ + Given("플레이어 핸드가 21점 미만인 경우") { + table( + headers("ranks"), + row(listOf("2", "3", "4")), + row(listOf("4", "5", "10")), + row(listOf("K", "Q")), + row(listOf("5", "5", "4", "3", "2")), + ).forAll { ranks -> + val player = Player("홍길동", Hand(ranks.map { createCard(it) }.toMutableList())) + + When("히트 가능 여부 질의하면") { + Then("결과는 true 이다") { + player.canHit() shouldBe true + } + } + } + } + + Given("플레이어 핸드가 21점 이상인 경우") { + table( + headers("ranks"), + row(listOf("J", "Q", "K")), + row(listOf("A", "K")), + row(listOf("Q", "10", "A")), + row(listOf("10", "9", "2")), + row(listOf("10", "10", "3")), + ).forAll { ranks -> + val player = Player("홍길동", Hand(ranks.map { createCard(it) }.toMutableList())) + + When("히트 가능 여부 질의하면") { + Then("결과는 false 이다") { + player.canHit() shouldBe false + } + } + } + } + + Given("플레이어가 딜러보다 점수가 높은 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("3")) + + player1.hit(createCard("4")) + player2.hit(createCard("5")) + + When("게임 결과 비교하면") { + Then("플레이어가 승리한다") { + listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe WIN } + } + } + } + + Given("플레이어가 딜러보다 점수가 낮은 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("10")) + + player1.hit(createCard("4")) + player2.hit(createCard("5")) + + When("게임 결과 비교하면") { + Then("플레이어가 승리한다") { + listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe LOSE } + } + } + } + + Given("플레이어가 딜러보다 점수가 동일한 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("10", SPADE)) + + player1.hit(createCard("10", HEART)) + player2.hit(createCard("10", DIAMOND)) + + When("게임 결과 비교하면") { + Then("플레이어가 승리한다") { + listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe DRAW } + } + } + } + + Given("딜러가 버스트인 경우") { + val dealer = Dealer() + val player1 = Player("유저1") + val player2 = Player("유저2") + + dealer.hit(createCard("K", SPADE)) + dealer.hit(createCard("Q", SPADE)) + dealer.hit(createCard("J", SPADE)) + + player1.hit(createCard("7", HEART)) + player2.hit(createCard("8", DIAMOND)) + + When("게임 결과 비교하면") { + Then("모든 플레이어가 승리한다") { + listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe WIN } + } + } + + When("게임 결과 비교하면 플레이어 핸드가 21점을 초과해도") { + player1.hit(createCard("K", HEART)) + player1.hit(createCard("Q", HEART)) + player1.hit(createCard("J", HEART)) + + player2.hit(createCard("K", DIAMOND)) + player2.hit(createCard("Q", DIAMOND)) + player2.hit(createCard("J", DIAMOND)) + + Then("모든 플레이어가 승리한다") { + listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe WIN } + } + } + } +}) diff --git a/src/test/kotlin/blackjack/fixtures/CardFixture.kt b/src/test/kotlin/blackjack/fixtures/CardFixture.kt index d4c2c25cf..caf9c481b 100644 --- a/src/test/kotlin/blackjack/fixtures/CardFixture.kt +++ b/src/test/kotlin/blackjack/fixtures/CardFixture.kt @@ -1,9 +1,9 @@ package blackjack.fixtures -import blackjack.domain.Card -import blackjack.domain.Rank -import blackjack.domain.Suit -import blackjack.domain.Suit.SPADE +import blackjack.domain.card.Card +import blackjack.domain.card.Rank +import blackjack.domain.card.Suit +import blackjack.domain.card.Suit.SPADE fun createCard( rank: String, diff --git a/src/test/kotlin/blackjack/fixtures/UserFixtures.kt b/src/test/kotlin/blackjack/fixtures/UserFixtures.kt deleted file mode 100644 index 12edefc85..000000000 --- a/src/test/kotlin/blackjack/fixtures/UserFixtures.kt +++ /dev/null @@ -1,8 +0,0 @@ -package blackjack.fixtures - -import blackjack.domain.Cards -import blackjack.domain.Player - -fun createPlayers(names: List = listOf("홍길동", "홍길덩")): List { - return names.map { Player(it, Cards(emptyList())) } -}