From 2ec40083c624d872a738da60b4c58a65b113707d Mon Sep 17 00:00:00 2001 From: hyotaek-jang Date: Tue, 28 Nov 2023 08:59:30 +0900 Subject: [PATCH 01/10] docs: add README.md --- src/main/kotlin/blackjack/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/kotlin/blackjack/README.md diff --git a/src/main/kotlin/blackjack/README.md b/src/main/kotlin/blackjack/README.md new file mode 100644 index 0000000000..033613e19b --- /dev/null +++ b/src/main/kotlin/blackjack/README.md @@ -0,0 +1,16 @@ +# kotlin-blackjack + +## 기능 요구사항 + +- [ ] 카드의 합이 21을 넘지 않으면서 21에 가장 가까운 사람이 승리한다. +- [ ] 카드의 종류는 스페이드, 다이아몬드, 하트, 클로버의 4가지로 한다. + - [ ] 숫자는 각각 1에서 10까지 주어진다. + - [ ] A는 1 또는 11로 계산할 수 있다. + - [ ] J, Q, K는 각각 10으로 계산한다. +- [ ] 게임에 참여할 사람의 이름은 쉼표(,)를 기준으로 구분한다. +- [ ] 게임을 시작하면 각 플레이어는 각자 두 장의 카드를 지급 받는다. + - [ ] 받은 카드를 출력한다. + - [ ] 카드 숫자를 모두 합쳐 21을 초과하지 않으면 카드를 더 받을 수 있다. + - [ ] 한 사용자의 받기가 끝나면 다음 사용자로 넘어간다. + - [ ] 예는 y, 아니오는 n을 입력한다. + - [ ] 카드를 받을 때마다 현재 갖고 있는 카드를 출력한다. From 571bc6bc3ca3514da99736e09beb98f6ba75b05d Mon Sep 17 00:00:00 2001 From: hyotaek-jang Date: Tue, 28 Nov 2023 15:36:54 +0900 Subject: [PATCH 02/10] feat: add Rank --- src/main/kotlin/blackjack/README.md | 2 +- src/main/kotlin/blackjack/domain/Rank.kt | 26 ++++++++++++++++++++ src/test/kotlin/blackjack/domain/RankTest.kt | 18 ++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/blackjack/domain/Rank.kt create mode 100644 src/test/kotlin/blackjack/domain/RankTest.kt diff --git a/src/main/kotlin/blackjack/README.md b/src/main/kotlin/blackjack/README.md index 033613e19b..6e78fe02a8 100644 --- a/src/main/kotlin/blackjack/README.md +++ b/src/main/kotlin/blackjack/README.md @@ -4,7 +4,7 @@ - [ ] 카드의 합이 21을 넘지 않으면서 21에 가장 가까운 사람이 승리한다. - [ ] 카드의 종류는 스페이드, 다이아몬드, 하트, 클로버의 4가지로 한다. - - [ ] 숫자는 각각 1에서 10까지 주어진다. + - [x] 숫자는 각각 1에서 10까지의 점수를 갖는다. - [ ] A는 1 또는 11로 계산할 수 있다. - [ ] J, Q, K는 각각 10으로 계산한다. - [ ] 게임에 참여할 사람의 이름은 쉼표(,)를 기준으로 구분한다. diff --git a/src/main/kotlin/blackjack/domain/Rank.kt b/src/main/kotlin/blackjack/domain/Rank.kt new file mode 100644 index 0000000000..aa9bbd103b --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Rank.kt @@ -0,0 +1,26 @@ +package blackjack.domain + + +enum class Rank(val score: Int) { + ACE(1), + TWO(2), + THREE(3), + FOUR(4), + FIVE(5), + SIX(6), + SEVEN(7), + EIGHT(8), + NINE(9), + TEN(10), + J(10), + Q(10), + K(10); + + companion object { + private val RANK_SET = values().associateBy { it.score } + + fun getRankSet(): Map { + return RANK_SET + } + } +} diff --git a/src/test/kotlin/blackjack/domain/RankTest.kt b/src/test/kotlin/blackjack/domain/RankTest.kt new file mode 100644 index 0000000000..2ec2194df4 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/RankTest.kt @@ -0,0 +1,18 @@ +package blackjack.domain + +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class RankTest { + + @Test + fun `Rank는 1부터 10까지의 점수를 갖는다`() { + // given, when + val rankList = Rank.values().map { it.score }.sorted() + + // then + assertThat(rankList.first()).isEqualTo(1) + assertThat(rankList.last()).isEqualTo(10) + } +} From 935d9099af8086bfd3f67ff397cb11e5646ce6cc Mon Sep 17 00:00:00 2001 From: hyotaek-jang Date: Tue, 28 Nov 2023 15:38:30 +0900 Subject: [PATCH 03/10] feat: add Suit --- src/main/kotlin/blackjack/README.md | 4 ++-- src/main/kotlin/blackjack/domain/Suit.kt | 13 +++++++++++++ src/test/kotlin/blackjack/domain/SuitTest.kt | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/blackjack/domain/Suit.kt create mode 100644 src/test/kotlin/blackjack/domain/SuitTest.kt diff --git a/src/main/kotlin/blackjack/README.md b/src/main/kotlin/blackjack/README.md index 6e78fe02a8..1ac5caf084 100644 --- a/src/main/kotlin/blackjack/README.md +++ b/src/main/kotlin/blackjack/README.md @@ -3,10 +3,10 @@ ## 기능 요구사항 - [ ] 카드의 합이 21을 넘지 않으면서 21에 가장 가까운 사람이 승리한다. -- [ ] 카드의 종류는 스페이드, 다이아몬드, 하트, 클로버의 4가지로 한다. +- [x] 카드의 종류는 스페이드, 다이아몬드, 하트, 클로버의 4가지로 한다. - [x] 숫자는 각각 1에서 10까지의 점수를 갖는다. - [ ] A는 1 또는 11로 계산할 수 있다. - - [ ] J, Q, K는 각각 10으로 계산한다. + - [x] J, Q, K는 각각 10으로 계산한다. - [ ] 게임에 참여할 사람의 이름은 쉼표(,)를 기준으로 구분한다. - [ ] 게임을 시작하면 각 플레이어는 각자 두 장의 카드를 지급 받는다. - [ ] 받은 카드를 출력한다. diff --git a/src/main/kotlin/blackjack/domain/Suit.kt b/src/main/kotlin/blackjack/domain/Suit.kt new file mode 100644 index 0000000000..6c9062af2d --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Suit.kt @@ -0,0 +1,13 @@ +package blackjack.domain + +enum class Suit(val value: String) { + SPADE("스페이드"), DIAMOND("다이아몬드"), HEART("하트"), CLUB("클로버"); + + companion object { + private val SUIT_SET = values().associateBy { it.value } + + fun getSuitSet(): Map { + return SUIT_SET + } + } +} diff --git a/src/test/kotlin/blackjack/domain/SuitTest.kt b/src/test/kotlin/blackjack/domain/SuitTest.kt new file mode 100644 index 0000000000..30cdce464e --- /dev/null +++ b/src/test/kotlin/blackjack/domain/SuitTest.kt @@ -0,0 +1,20 @@ +package blackjack.domain + +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class SuitTest { + @Test + fun `Suit는 4가지 종류의 무늬(스페이드, 다이아몬드, 하트, 클로버)를 갖는다`() { + // given, when + val suitSet = Suit.getSuitSet() + + // then + assertThat(suitSet.size).isEqualTo(4) + assertThat(suitSet["스페이드"]).isEqualTo(Suit.SPADE) + assertThat(suitSet["다이아몬드"]).isEqualTo(Suit.DIAMOND) + assertThat(suitSet["하트"]).isEqualTo(Suit.HEART) + assertThat(suitSet["클로버"]).isEqualTo(Suit.CLUB) + } +} From 8e35001d3a7c28cbec2c33d4e0f9fd8b131f47f1 Mon Sep 17 00:00:00 2001 From: hyotaek-jang Date: Tue, 28 Nov 2023 15:48:56 +0900 Subject: [PATCH 04/10] feat: add inputView --- src/main/kotlin/blackjack/README.md | 4 ++-- src/main/kotlin/blackjack/view/InputView.kt | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/blackjack/view/InputView.kt diff --git a/src/main/kotlin/blackjack/README.md b/src/main/kotlin/blackjack/README.md index 1ac5caf084..bb9e3894ff 100644 --- a/src/main/kotlin/blackjack/README.md +++ b/src/main/kotlin/blackjack/README.md @@ -8,9 +8,9 @@ - [ ] A는 1 또는 11로 계산할 수 있다. - [x] J, Q, K는 각각 10으로 계산한다. - [ ] 게임에 참여할 사람의 이름은 쉼표(,)를 기준으로 구분한다. -- [ ] 게임을 시작하면 각 플레이어는 각자 두 장의 카드를 지급 받는다. +- [x] 게임을 시작하면 각 플레이어는 각자 두 장의 카드를 지급 받는다. - [ ] 받은 카드를 출력한다. - [ ] 카드 숫자를 모두 합쳐 21을 초과하지 않으면 카드를 더 받을 수 있다. - [ ] 한 사용자의 받기가 끝나면 다음 사용자로 넘어간다. - - [ ] 예는 y, 아니오는 n을 입력한다. + - [x] 예는 y, 아니오는 n을 입력한다. - [ ] 카드를 받을 때마다 현재 갖고 있는 카드를 출력한다. diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt new file mode 100644 index 0000000000..acf3d43746 --- /dev/null +++ b/src/main/kotlin/blackjack/view/InputView.kt @@ -0,0 +1,16 @@ +package blackjack.view + +object InputView { + private const val DELIMITER = "," + private val RESPONSE_MAP = mapOf("y" to true, "n" to false) + + fun inputNames(): List { + println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)") + return readln().split(DELIMITER) + } + + fun inputHitOrStand(name: String): Boolean { + println("${name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") + return RESPONSE_MAP[readln()] ?: throw IllegalArgumentException("잘못된 입력입니다.") + } +} From 36eee72396b1cc7460f1d3f7d63f474b25ea2ddb Mon Sep 17 00:00:00 2001 From: hyotaek-jang Date: Tue, 28 Nov 2023 15:56:23 +0900 Subject: [PATCH 05/10] feat: add OutputView --- src/main/kotlin/blackjack/README.md | 6 ++++-- src/main/kotlin/blackjack/view/OutputView.kt | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/blackjack/view/OutputView.kt diff --git a/src/main/kotlin/blackjack/README.md b/src/main/kotlin/blackjack/README.md index bb9e3894ff..9704eeead6 100644 --- a/src/main/kotlin/blackjack/README.md +++ b/src/main/kotlin/blackjack/README.md @@ -6,11 +6,13 @@ - [x] 카드의 종류는 스페이드, 다이아몬드, 하트, 클로버의 4가지로 한다. - [x] 숫자는 각각 1에서 10까지의 점수를 갖는다. - [ ] A는 1 또는 11로 계산할 수 있다. + - [ ] A를 11로 계산했을 때 21을 초과하면 1로 계산한다. - [x] J, Q, K는 각각 10으로 계산한다. - [ ] 게임에 참여할 사람의 이름은 쉼표(,)를 기준으로 구분한다. - [x] 게임을 시작하면 각 플레이어는 각자 두 장의 카드를 지급 받는다. - - [ ] 받은 카드를 출력한다. + - [x] 받은 카드를 출력한다. - [ ] 카드 숫자를 모두 합쳐 21을 초과하지 않으면 카드를 더 받을 수 있다. - [ ] 한 사용자의 받기가 끝나면 다음 사용자로 넘어간다. - [x] 예는 y, 아니오는 n을 입력한다. - - [ ] 카드를 받을 때마다 현재 갖고 있는 카드를 출력한다. + - [x] 카드를 받을 때마다 현재 갖고 있는 카드를 출력한다. +- [ ] 더이상 카드를 받지 않는다면 각 플레이어는 각자가 가진 카드의 합을 계산한다. diff --git a/src/main/kotlin/blackjack/view/OutputView.kt b/src/main/kotlin/blackjack/view/OutputView.kt new file mode 100644 index 0000000000..f1c6926a19 --- /dev/null +++ b/src/main/kotlin/blackjack/view/OutputView.kt @@ -0,0 +1,16 @@ +package blackjack.view + +import blackjack.domain.Card +import blackjack.domain.Player + +object OutputView { + private const val DELIMITER = "," + + fun printCards(name: String, cards: List) { + println("${name}카드: ${cards.joinToString(DELIMITER)}") + } + + fun printPlayerScore(player: Player) { + println("${player.name}카드 : ${player.cards.joinToString(DELIMITER)} - 결과: ${player.score()}") + } +} From 7e8a0a72e5367bd98ba51bc80a01386d90b18311 Mon Sep 17 00:00:00 2001 From: hyotaek-jang Date: Tue, 28 Nov 2023 15:57:22 +0900 Subject: [PATCH 06/10] refactor: apply lint --- src/main/kotlin/blackjack/domain/Rank.kt | 1 - src/main/kotlin/dsl/domain/PersonBuilder.kt | 1 - src/main/kotlin/dsl/domain/Skills.kt | 3 +-- src/test/kotlin/blackjack/domain/RankTest.kt | 1 - src/test/kotlin/blackjack/domain/SuitTest.kt | 1 - 5 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/kotlin/blackjack/domain/Rank.kt b/src/main/kotlin/blackjack/domain/Rank.kt index aa9bbd103b..08bc82d5cf 100644 --- a/src/main/kotlin/blackjack/domain/Rank.kt +++ b/src/main/kotlin/blackjack/domain/Rank.kt @@ -1,6 +1,5 @@ package blackjack.domain - enum class Rank(val score: Int) { ACE(1), TWO(2), diff --git a/src/main/kotlin/dsl/domain/PersonBuilder.kt b/src/main/kotlin/dsl/domain/PersonBuilder.kt index 6c58394f54..55e4271f26 100644 --- a/src/main/kotlin/dsl/domain/PersonBuilder.kt +++ b/src/main/kotlin/dsl/domain/PersonBuilder.kt @@ -26,4 +26,3 @@ class PersonBuilder { return Person(name, company, skills, languages) } } - diff --git a/src/main/kotlin/dsl/domain/Skills.kt b/src/main/kotlin/dsl/domain/Skills.kt index 0d2a5b0edc..142e88b80c 100644 --- a/src/main/kotlin/dsl/domain/Skills.kt +++ b/src/main/kotlin/dsl/domain/Skills.kt @@ -1,4 +1,3 @@ package dsl.domain -class Skills(val soft: List, val hard: List) { -} +class Skills(val soft: List, val hard: List) diff --git a/src/test/kotlin/blackjack/domain/RankTest.kt b/src/test/kotlin/blackjack/domain/RankTest.kt index 2ec2194df4..8edf07ceb6 100644 --- a/src/test/kotlin/blackjack/domain/RankTest.kt +++ b/src/test/kotlin/blackjack/domain/RankTest.kt @@ -1,6 +1,5 @@ package blackjack.domain -import org.assertj.core.api.Assertions import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/blackjack/domain/SuitTest.kt b/src/test/kotlin/blackjack/domain/SuitTest.kt index 30cdce464e..0d3174252a 100644 --- a/src/test/kotlin/blackjack/domain/SuitTest.kt +++ b/src/test/kotlin/blackjack/domain/SuitTest.kt @@ -1,6 +1,5 @@ package blackjack.domain -import org.assertj.core.api.Assertions import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test From 046405307025a3e5e7033212b1cb2f6422302aa7 Mon Sep 17 00:00:00 2001 From: hyotaek-jang Date: Wed, 29 Nov 2023 14:45:24 +0900 Subject: [PATCH 07/10] feat: add Card, Deck, Player --- .../kotlin/blackjack/BlackJackController.kt | 16 ++++++++ src/main/kotlin/blackjack/README.md | 2 +- src/main/kotlin/blackjack/domain/Card.kt | 11 ++++++ src/main/kotlin/blackjack/domain/Deck.kt | 29 ++++++++++++++ src/main/kotlin/blackjack/domain/Player.kt | 18 +++++++++ src/main/kotlin/blackjack/domain/Rank.kt | 32 ++++++++-------- src/main/kotlin/blackjack/view/OutputView.kt | 7 +++- src/test/kotlin/blackjack/domain/DeckTest.kt | 28 ++++++++++++++ .../kotlin/blackjack/domain/PlayerTest.kt | 38 +++++++++++++++++++ 9 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 src/main/kotlin/blackjack/BlackJackController.kt create mode 100644 src/main/kotlin/blackjack/domain/Card.kt create mode 100644 src/main/kotlin/blackjack/domain/Deck.kt create mode 100644 src/main/kotlin/blackjack/domain/Player.kt create mode 100644 src/test/kotlin/blackjack/domain/DeckTest.kt create mode 100644 src/test/kotlin/blackjack/domain/PlayerTest.kt diff --git a/src/main/kotlin/blackjack/BlackJackController.kt b/src/main/kotlin/blackjack/BlackJackController.kt new file mode 100644 index 0000000000..ede8daab37 --- /dev/null +++ b/src/main/kotlin/blackjack/BlackJackController.kt @@ -0,0 +1,16 @@ +package blackjack + +import blackjack.domain.Deck +import blackjack.domain.Player +import blackjack.view.InputView +import blackjack.view.OutputView + +fun main() { + val deck = Deck.create() + val players = InputView.inputNames().map { Player(it) } + + players.forEach() { + it.addCard(deck.draw(2)) + } + OutputView.printDrawTwoCards(players) +} diff --git a/src/main/kotlin/blackjack/README.md b/src/main/kotlin/blackjack/README.md index 9704eeead6..ba4831e42c 100644 --- a/src/main/kotlin/blackjack/README.md +++ b/src/main/kotlin/blackjack/README.md @@ -8,7 +8,7 @@ - [ ] A는 1 또는 11로 계산할 수 있다. - [ ] A를 11로 계산했을 때 21을 초과하면 1로 계산한다. - [x] J, Q, K는 각각 10으로 계산한다. -- [ ] 게임에 참여할 사람의 이름은 쉼표(,)를 기준으로 구분한다. +- [x] 게임에 참여할 사람의 이름은 쉼표(,)를 기준으로 구분한다. - [x] 게임을 시작하면 각 플레이어는 각자 두 장의 카드를 지급 받는다. - [x] 받은 카드를 출력한다. - [ ] 카드 숫자를 모두 합쳐 21을 초과하지 않으면 카드를 더 받을 수 있다. diff --git a/src/main/kotlin/blackjack/domain/Card.kt b/src/main/kotlin/blackjack/domain/Card.kt new file mode 100644 index 0000000000..adeb80cfc8 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Card.kt @@ -0,0 +1,11 @@ +package blackjack.domain + +class Card(private val suit: Suit, private val rank: Rank) { + fun score(): Int { + return rank.score + } + + override fun toString(): String { + return "${rank.outputName}${suit.value}" + } +} diff --git a/src/main/kotlin/blackjack/domain/Deck.kt b/src/main/kotlin/blackjack/domain/Deck.kt new file mode 100644 index 0000000000..1aaa65fd9a --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Deck.kt @@ -0,0 +1,29 @@ +package blackjack.domain + +import java.util.Stack + +class Deck(private val cards: Stack) { + fun draw(): Card { + require(cards.isNotEmpty()) { "카드가 없습니다." } + return cards.pop() + } + + fun draw(count: Int): List { + require(cards.size >= count) { "카드가 없습니다." } + return (1..count).map { cards.pop() } + } + + companion object { + fun create(): Deck { + val cards: List = Rank.getRankSet().values + .flatMap { rank -> Suit.getSuitSet().values.map { suit -> Card(suit, rank) } } + .shuffled() + + return Deck( + Stack().apply { + addAll(cards) + } + ) + } + } +} diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt new file mode 100644 index 0000000000..9e5f20f3bc --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -0,0 +1,18 @@ +package blackjack.domain + +class Player(val name: String, private val _cards: MutableList = mutableListOf()) { + val cards: List + get() = _cards + + fun score(): Int { + return _cards.sumOf { it.score() } + } + + fun addCard(newCard: Card) { + _cards.add(newCard) + } + + fun addCard(newCards: List) { + _cards.addAll(newCards) + } +} diff --git a/src/main/kotlin/blackjack/domain/Rank.kt b/src/main/kotlin/blackjack/domain/Rank.kt index 08bc82d5cf..a06e783ff4 100644 --- a/src/main/kotlin/blackjack/domain/Rank.kt +++ b/src/main/kotlin/blackjack/domain/Rank.kt @@ -1,24 +1,24 @@ package blackjack.domain -enum class Rank(val score: Int) { - ACE(1), - TWO(2), - THREE(3), - FOUR(4), - FIVE(5), - SIX(6), - SEVEN(7), - EIGHT(8), - NINE(9), - TEN(10), - J(10), - Q(10), - K(10); +enum class Rank(val outputName: String, val score: Int) { + ACE("A", 1), + 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), + J("J", 10), + Q("Q", 10), + K("K", 10); companion object { - private val RANK_SET = values().associateBy { it.score } + private val RANK_SET = values().associateBy { it.outputName } - fun getRankSet(): Map { + fun getRankSet(): Map { return RANK_SET } } diff --git a/src/main/kotlin/blackjack/view/OutputView.kt b/src/main/kotlin/blackjack/view/OutputView.kt index f1c6926a19..ba54e8060e 100644 --- a/src/main/kotlin/blackjack/view/OutputView.kt +++ b/src/main/kotlin/blackjack/view/OutputView.kt @@ -4,12 +4,17 @@ import blackjack.domain.Card import blackjack.domain.Player object OutputView { - private const val DELIMITER = "," + private const val DELIMITER = ", " fun printCards(name: String, cards: List) { println("${name}카드: ${cards.joinToString(DELIMITER)}") } + fun printDrawTwoCards(players: List) { + println("${players.joinToString(DELIMITER) { it.name }}에게 2장의 카드를 나누었습니다.") + players.forEach { printCards(it.name, it.cards) } + } + fun printPlayerScore(player: Player) { println("${player.name}카드 : ${player.cards.joinToString(DELIMITER)} - 결과: ${player.score()}") } diff --git a/src/test/kotlin/blackjack/domain/DeckTest.kt b/src/test/kotlin/blackjack/domain/DeckTest.kt new file mode 100644 index 0000000000..01fd4707e5 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/DeckTest.kt @@ -0,0 +1,28 @@ +package blackjack.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.lang.IllegalArgumentException + +internal class DeckTest { + @Test + fun `Deck은 52장의 카드를 갖는다 따라서 그 이상의 카드를 draw하면 오류가 발생한다`() { + // given, when + val deck = Deck.create() + val deckSize = 52 + + // then + assertThrows { deck.draw(deckSize + 1) } + } + + @Test + fun `Deck은 52장의 카드를 갖는다`() { + // given, when + val deck = Deck.create() + val deckSize = 52 + + // then + assertThat(deck.draw(deckSize).size).isEqualTo(deckSize) + } +} diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt new file mode 100644 index 0000000000..5cfe3a6750 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -0,0 +1,38 @@ +package blackjack.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PlayerTest { + + @Test + fun `단건 카드 추가`() { + // given + val player = Player("test") + val card = Card(Suit.SPADE, Rank.ACE) + + // when + player.addCard(card) + + // then + assertThat(player.cards).contains(card) + } + + @Test + fun `여러 카드 추가`() { + // given + val player = Player("test") + val cards = listOf( + Card(Suit.SPADE, Rank.ACE), + Card(Suit.DIAMOND, Rank.TWO), + Card(Suit.HEART, Rank.THREE), + Card(Suit.CLUB, Rank.FOUR) + ) + + // when + player.addCard(cards) + + // then + assertThat(player.cards).containsAll(cards) + } +} From 57cc773360d1c0d0e718d4ef4a04d432072a3478 Mon Sep 17 00:00:00 2001 From: hyotaek-jang Date: Wed, 29 Nov 2023 15:04:29 +0900 Subject: [PATCH 08/10] feat: add calculate score --- .../kotlin/blackjack/BlackJackController.kt | 9 +++++ src/main/kotlin/blackjack/README.md | 11 +++--- src/main/kotlin/blackjack/domain/Card.kt | 4 +++ src/main/kotlin/blackjack/domain/Player.kt | 19 +++++++++- src/main/kotlin/blackjack/view/InputView.kt | 2 +- src/main/kotlin/blackjack/view/OutputView.kt | 7 +++- .../kotlin/blackjack/domain/PlayerTest.kt | 36 +++++++++++++++++++ 7 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/blackjack/BlackJackController.kt b/src/main/kotlin/blackjack/BlackJackController.kt index ede8daab37..0b15f83562 100644 --- a/src/main/kotlin/blackjack/BlackJackController.kt +++ b/src/main/kotlin/blackjack/BlackJackController.kt @@ -13,4 +13,13 @@ fun main() { it.addCard(deck.draw(2)) } OutputView.printDrawTwoCards(players) + + players.forEach { + while (it.canDraw() && InputView.inputHitOrStand(it.name)) { + it.addCard(deck.draw()) + OutputView.printCards(it.name, it.cards) + } + } + + OutputView.printPlayersScore(players) } diff --git a/src/main/kotlin/blackjack/README.md b/src/main/kotlin/blackjack/README.md index ba4831e42c..edbded6b63 100644 --- a/src/main/kotlin/blackjack/README.md +++ b/src/main/kotlin/blackjack/README.md @@ -2,17 +2,16 @@ ## 기능 요구사항 -- [ ] 카드의 합이 21을 넘지 않으면서 21에 가장 가까운 사람이 승리한다. - [x] 카드의 종류는 스페이드, 다이아몬드, 하트, 클로버의 4가지로 한다. - [x] 숫자는 각각 1에서 10까지의 점수를 갖는다. - - [ ] A는 1 또는 11로 계산할 수 있다. - - [ ] A를 11로 계산했을 때 21을 초과하면 1로 계산한다. + - [x] A는 1 또는 11로 계산할 수 있다. + - [x] A를 11로 계산했을 때 21을 초과하면 1로 계산한다. - [x] J, Q, K는 각각 10으로 계산한다. - [x] 게임에 참여할 사람의 이름은 쉼표(,)를 기준으로 구분한다. - [x] 게임을 시작하면 각 플레이어는 각자 두 장의 카드를 지급 받는다. - [x] 받은 카드를 출력한다. - - [ ] 카드 숫자를 모두 합쳐 21을 초과하지 않으면 카드를 더 받을 수 있다. - - [ ] 한 사용자의 받기가 끝나면 다음 사용자로 넘어간다. + - [x] 카드 숫자를 모두 합쳐 21을 초과하지 않으면 카드를 더 받을 수 있다. + - [x] 한 사용자의 받기가 끝나면 다음 사용자로 넘어간다. - [x] 예는 y, 아니오는 n을 입력한다. - [x] 카드를 받을 때마다 현재 갖고 있는 카드를 출력한다. -- [ ] 더이상 카드를 받지 않는다면 각 플레이어는 각자가 가진 카드의 합을 계산한다. +- [x] 더이상 카드를 받지 않는다면 각 플레이어는 각자가 가진 카드의 합을 계산한다. diff --git a/src/main/kotlin/blackjack/domain/Card.kt b/src/main/kotlin/blackjack/domain/Card.kt index adeb80cfc8..c18a3a595c 100644 --- a/src/main/kotlin/blackjack/domain/Card.kt +++ b/src/main/kotlin/blackjack/domain/Card.kt @@ -5,6 +5,10 @@ class Card(private val suit: Suit, private val rank: Rank) { return rank.score } + fun isAce(): Boolean { + return rank == Rank.ACE + } + override fun toString(): String { return "${rank.outputName}${suit.value}" } diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt index 9e5f20f3bc..307f59c9fc 100644 --- a/src/main/kotlin/blackjack/domain/Player.kt +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -5,7 +5,11 @@ class Player(val name: String, private val _cards: MutableList = mutableLi get() = _cards fun score(): Int { - return _cards.sumOf { it.score() } + var score = _cards.sumOf { it.score() } + if (hasAce() && score + ADDITIONAL_ACE_SCORE <= MAX_SCORE) { + score += ADDITIONAL_ACE_SCORE + } + return score } fun addCard(newCard: Card) { @@ -15,4 +19,17 @@ class Player(val name: String, private val _cards: MutableList = mutableLi fun addCard(newCards: List) { _cards.addAll(newCards) } + + fun canDraw(): Boolean { + return score() < MAX_SCORE + } + + private fun hasAce(): Boolean { + return _cards.any(Card::isAce) + } + + companion object { + private const val MAX_SCORE = 21 + private const val ADDITIONAL_ACE_SCORE = 10 + } } diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt index acf3d43746..5b45f136ac 100644 --- a/src/main/kotlin/blackjack/view/InputView.kt +++ b/src/main/kotlin/blackjack/view/InputView.kt @@ -10,7 +10,7 @@ object InputView { } fun inputHitOrStand(name: String): Boolean { - println("${name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") + println("\n${name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") return RESPONSE_MAP[readln()] ?: throw IllegalArgumentException("잘못된 입력입니다.") } } diff --git a/src/main/kotlin/blackjack/view/OutputView.kt b/src/main/kotlin/blackjack/view/OutputView.kt index ba54e8060e..13da17c7e5 100644 --- a/src/main/kotlin/blackjack/view/OutputView.kt +++ b/src/main/kotlin/blackjack/view/OutputView.kt @@ -11,10 +11,15 @@ object OutputView { } fun printDrawTwoCards(players: List) { - println("${players.joinToString(DELIMITER) { it.name }}에게 2장의 카드를 나누었습니다.") + println("\n${players.joinToString(DELIMITER) { it.name }}에게 2장의 카드를 나누었습니다.") players.forEach { printCards(it.name, it.cards) } } + fun printPlayersScore(players: List) { + println() + players.forEach { printPlayerScore(it) } + } + fun printPlayerScore(player: Player) { println("${player.name}카드 : ${player.cards.joinToString(DELIMITER)} - 결과: ${player.score()}") } diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt index 5cfe3a6750..789f15664c 100644 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -35,4 +35,40 @@ internal class PlayerTest { // then assertThat(player.cards).containsAll(cards) } + + @Test + fun `카드 점수 계산 - 에이스를 제외하고 카드 점수의 총합이 11점 이상이면 에이스는 1점이 된다`() { + // given + val player = Player("test") + val cards = listOf( + Card(Suit.SPADE, Rank.ACE), + Card(Suit.DIAMOND, Rank.NINE), + Card(Suit.HEART, Rank.TWO) + ) + player.addCard(cards) + + // when + val score = player.score() + + // then + assertThat(score).isEqualTo(12) + } + + @Test + fun `카드 점수 계산 - 에이스를 제외하고 카드 점수의 총합이 10점 이하면 에이스는 11점이 된다`() { + // given + val player = Player("test") + val cards = listOf( + Card(Suit.SPADE, Rank.ACE), + Card(Suit.DIAMOND, Rank.SIX), + Card(Suit.HEART, Rank.FOUR) + ) + player.addCard(cards) + + // when + val score = player.score() + + // then + assertThat(score).isEqualTo(21) + } } From 670cef87235bc87d6f19c7690a7ca15340404423 Mon Sep 17 00:00:00 2001 From: hyotaek-jang Date: Wed, 29 Nov 2023 15:27:23 +0900 Subject: [PATCH 09/10] refactor: refactor Player domain --- .../kotlin/blackjack/domain/BlackJackGame.kt | 14 +++++ src/main/kotlin/blackjack/domain/CardList.kt | 18 ++++++ src/main/kotlin/blackjack/domain/Player.kt | 29 ++------- src/main/kotlin/blackjack/view/OutputView.kt | 13 ++-- .../blackjack/domain/BlackJackGameTest.kt | 42 +++++++++++++ .../kotlin/blackjack/domain/CardListTest.kt | 36 +++++++++++ .../kotlin/blackjack/domain/PlayerTest.kt | 59 ++----------------- 7 files changed, 128 insertions(+), 83 deletions(-) create mode 100644 src/main/kotlin/blackjack/domain/BlackJackGame.kt create mode 100644 src/main/kotlin/blackjack/domain/CardList.kt create mode 100644 src/test/kotlin/blackjack/domain/BlackJackGameTest.kt create mode 100644 src/test/kotlin/blackjack/domain/CardListTest.kt diff --git a/src/main/kotlin/blackjack/domain/BlackJackGame.kt b/src/main/kotlin/blackjack/domain/BlackJackGame.kt new file mode 100644 index 0000000000..a83576cb85 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/BlackJackGame.kt @@ -0,0 +1,14 @@ +package blackjack.domain + +object BlackJackGame { + const val MAX_SCORE = 21 + private const val ADDITIONAL_ACE_SCORE = 10 + + fun score(cards: CardList): Int { + var score = cards.cards.sumOf { it.score() } + if (cards.hasAce() && score + ADDITIONAL_ACE_SCORE <= MAX_SCORE) { + score += ADDITIONAL_ACE_SCORE + } + return score + } +} diff --git a/src/main/kotlin/blackjack/domain/CardList.kt b/src/main/kotlin/blackjack/domain/CardList.kt new file mode 100644 index 0000000000..680a919f13 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/CardList.kt @@ -0,0 +1,18 @@ +package blackjack.domain + +class CardList(private val _cards: MutableList = mutableListOf()) { + val cards: List + get() = _cards + + fun addCard(newCard: Card) { + _cards.add(newCard) + } + + fun addCard(newCards: List) { + _cards.addAll(newCards) + } + + fun hasAce(): Boolean { + return _cards.any(Card::isAce) + } +} diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt index 307f59c9fc..e2c1b2b36c 100644 --- a/src/main/kotlin/blackjack/domain/Player.kt +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -1,35 +1,18 @@ package blackjack.domain -class Player(val name: String, private val _cards: MutableList = mutableListOf()) { - val cards: List - get() = _cards - - fun score(): Int { - var score = _cards.sumOf { it.score() } - if (hasAce() && score + ADDITIONAL_ACE_SCORE <= MAX_SCORE) { - score += ADDITIONAL_ACE_SCORE - } - return score - } +import blackjack.domain.BlackJackGame.MAX_SCORE +import blackjack.domain.BlackJackGame.score +class Player(val name: String, val cards: CardList = CardList()) { fun addCard(newCard: Card) { - _cards.add(newCard) + cards.addCard(newCard) } fun addCard(newCards: List) { - _cards.addAll(newCards) + cards.addCard(newCards) } fun canDraw(): Boolean { - return score() < MAX_SCORE - } - - private fun hasAce(): Boolean { - return _cards.any(Card::isAce) - } - - companion object { - private const val MAX_SCORE = 21 - private const val ADDITIONAL_ACE_SCORE = 10 + return score(cards) < MAX_SCORE } } diff --git a/src/main/kotlin/blackjack/view/OutputView.kt b/src/main/kotlin/blackjack/view/OutputView.kt index 13da17c7e5..95474e1c47 100644 --- a/src/main/kotlin/blackjack/view/OutputView.kt +++ b/src/main/kotlin/blackjack/view/OutputView.kt @@ -1,13 +1,14 @@ package blackjack.view -import blackjack.domain.Card +import blackjack.domain.BlackJackGame +import blackjack.domain.CardList import blackjack.domain.Player object OutputView { private const val DELIMITER = ", " - fun printCards(name: String, cards: List) { - println("${name}카드: ${cards.joinToString(DELIMITER)}") + fun printCards(name: String, cards: CardList) { + println("${name}카드: ${cards.cards.joinToString(DELIMITER)}") } fun printDrawTwoCards(players: List) { @@ -17,10 +18,10 @@ object OutputView { fun printPlayersScore(players: List) { println() - players.forEach { printPlayerScore(it) } + players.forEach { printPlayerScore(it, it.cards) } } - fun printPlayerScore(player: Player) { - println("${player.name}카드 : ${player.cards.joinToString(DELIMITER)} - 결과: ${player.score()}") + private fun printPlayerScore(player: Player, cards: CardList) { + println("${player.name}카드 : ${cards.cards.joinToString(DELIMITER)} - 결과: ${BlackJackGame.score(cards)}") } } diff --git a/src/test/kotlin/blackjack/domain/BlackJackGameTest.kt b/src/test/kotlin/blackjack/domain/BlackJackGameTest.kt new file mode 100644 index 0000000000..2f49c0a818 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/BlackJackGameTest.kt @@ -0,0 +1,42 @@ +package blackjack.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BlackJackGameTest { + @Test + fun `카드 점수 계산 - 에이스를 제외하고 카드 점수의 총합이 11점 이상이면 에이스는 1점이 된다`() { + // given + val cardList = CardList() + val cards = listOf( + Card(Suit.SPADE, Rank.ACE), + Card(Suit.DIAMOND, Rank.NINE), + Card(Suit.HEART, Rank.TWO) + ) + cardList.addCard(cards) + + // when + val score = BlackJackGame.score(cardList) + + // then + assertThat(score).isEqualTo(12) + } + + @Test + fun `카드 점수 계산 - 에이스를 제외하고 카드 점수의 총합이 10점 이하면 에이스는 11점이 된다`() { + // given + val cardList = CardList() + val cards = listOf( + Card(Suit.SPADE, Rank.ACE), + Card(Suit.DIAMOND, Rank.SIX), + Card(Suit.HEART, Rank.FOUR) + ) + cardList.addCard(cards) + + // when + val score = BlackJackGame.score(cardList) + + // then + assertThat(score).isEqualTo(21) + } +} diff --git a/src/test/kotlin/blackjack/domain/CardListTest.kt b/src/test/kotlin/blackjack/domain/CardListTest.kt new file mode 100644 index 0000000000..8cb76e7f8a --- /dev/null +++ b/src/test/kotlin/blackjack/domain/CardListTest.kt @@ -0,0 +1,36 @@ +package blackjack.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CardListTest { + @Test + fun `단건 카드 추가`() { + // given + val cardList = CardList() + val cards = listOf(Card(Suit.SPADE, Rank.ACE)) + + // when + cardList.addCard(cards) + + // then + assertThat(cardList.cards).containsAll(cards) + } + + @Test + fun `여러 카드 추가`() { + // given + val cardList = CardList() + val cards = listOf( + Card(Suit.SPADE, Rank.ACE), + Card(Suit.DIAMOND, Rank.NINE), + Card(Suit.HEART, Rank.TWO) + ) + + // when + cardList.addCard(cards) + + // then + assertThat(cardList.cards).containsAll(cards) + } +} diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt index 789f15664c..b5465482a4 100644 --- a/src/test/kotlin/blackjack/domain/PlayerTest.kt +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -4,71 +4,22 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test internal class PlayerTest { - - @Test - fun `단건 카드 추가`() { - // given - val player = Player("test") - val card = Card(Suit.SPADE, Rank.ACE) - - // when - player.addCard(card) - - // then - assertThat(player.cards).contains(card) - } - - @Test - fun `여러 카드 추가`() { - // given - val player = Player("test") - val cards = listOf( - Card(Suit.SPADE, Rank.ACE), - Card(Suit.DIAMOND, Rank.TWO), - Card(Suit.HEART, Rank.THREE), - Card(Suit.CLUB, Rank.FOUR) - ) - - // when - player.addCard(cards) - - // then - assertThat(player.cards).containsAll(cards) - } - @Test - fun `카드 점수 계산 - 에이스를 제외하고 카드 점수의 총합이 11점 이상이면 에이스는 1점이 된다`() { + fun `21점을 넘지 않으면 플레이어는 게임을 계속할 수 있다`() { // given val player = Player("test") + val cardList = CardList() val cards = listOf( Card(Suit.SPADE, Rank.ACE), Card(Suit.DIAMOND, Rank.NINE), Card(Suit.HEART, Rank.TWO) ) - player.addCard(cards) - - // when - val score = player.score() - - // then - assertThat(score).isEqualTo(12) - } - - @Test - fun `카드 점수 계산 - 에이스를 제외하고 카드 점수의 총합이 10점 이하면 에이스는 11점이 된다`() { - // given - val player = Player("test") - val cards = listOf( - Card(Suit.SPADE, Rank.ACE), - Card(Suit.DIAMOND, Rank.SIX), - Card(Suit.HEART, Rank.FOUR) - ) - player.addCard(cards) + cardList.addCard(cards) // when - val score = player.score() + val isContinue = player.canDraw() // then - assertThat(score).isEqualTo(21) + assertThat(isContinue).isTrue() } } From 0f6ee4a7babfe46a26c01662e7d706e8f536064e Mon Sep 17 00:00:00 2001 From: hyotaek-jang Date: Wed, 29 Nov 2023 15:38:05 +0900 Subject: [PATCH 10/10] refactor: refactor BlackJackController --- .../kotlin/blackjack/BlackJackController.kt | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/blackjack/BlackJackController.kt b/src/main/kotlin/blackjack/BlackJackController.kt index 0b15f83562..f6ad51076c 100644 --- a/src/main/kotlin/blackjack/BlackJackController.kt +++ b/src/main/kotlin/blackjack/BlackJackController.kt @@ -1,25 +1,39 @@ package blackjack +import blackjack.BlackJackController.initPlayer +import blackjack.BlackJackController.initSetting +import blackjack.BlackJackController.progressGame import blackjack.domain.Deck import blackjack.domain.Player import blackjack.view.InputView import blackjack.view.OutputView -fun main() { - val deck = Deck.create() - val players = InputView.inputNames().map { Player(it) } +object BlackJackController { + fun initPlayer(names: List): List { + return names.map { Player(it) } + } - players.forEach() { - it.addCard(deck.draw(2)) + fun initSetting(players: List, deck: Deck) { + players.forEach { + it.addCard(deck.draw(2)) + } } - OutputView.printDrawTwoCards(players) - players.forEach { - while (it.canDraw() && InputView.inputHitOrStand(it.name)) { - it.addCard(deck.draw()) - OutputView.printCards(it.name, it.cards) + fun progressGame(player: Player, deck: Deck) { + while (player.canDraw() && InputView.inputHitOrStand(player.name)) { + player.addCard(deck.draw()) + OutputView.printCards(player.name, player.cards) } } +} + +fun main() { + val deck = Deck.create() + val players = initPlayer(InputView.inputNames()) + + initSetting(players, deck) + OutputView.printDrawTwoCards(players) + players.forEach { progressGame(it, deck) } OutputView.printPlayersScore(players) }