diff --git a/README.md b/README.md index e1c7c927d8..e340798cac 100644 --- a/README.md +++ b/README.md @@ -1 +1,38 @@ -# kotlin-blackjack \ No newline at end of file +# kotlin-blackjack +### 기능 요구사항 +- 블랙잭 게임을 변형한 프로그램을 구현한다. 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다. + +- 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다. +- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. + +### 실행 결과 +``` +게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리) +pobi,jason + +pobi, jason에게 2장의 카드를 나누었습니다. +pobi카드: 2하트, 8스페이드 +jason카드: 7클로버, K스페이드 + +pobi는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +y +pobi카드: 2하트, 8스페이드, A클로버 +pobi는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +n +jason은 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +n +jason카드: 7클로버, K스페이드 + +pobi카드: 2하트, 8스페이드, A클로버 - 결과: 21 +jason카드: 7클로버, K스페이드 - 결과: 17 +``` + +### 기능목록 +- card + - card shape, card number +- player + - name, card list + - cardSum() +- players + - player list +- 입출력 \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index e78e729567..43c17967be 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ repositories { dependencies { testImplementation("org.junit.jupiter", "junit-jupiter", "5.8.2") testImplementation("org.assertj", "assertj-core", "3.22.0") - testImplementation("io.kotest", "kotest-runner-junit5", "5.2.3") + testImplementation("io.kotest", "kotest-runner-junit5", "5.5.0") } tasks { diff --git a/src/main/kotlin/blackjack/BlackjackApplication.kt b/src/main/kotlin/blackjack/BlackjackApplication.kt new file mode 100644 index 0000000000..6abae48a09 --- /dev/null +++ b/src/main/kotlin/blackjack/BlackjackApplication.kt @@ -0,0 +1,26 @@ +package blackjack + +import blackjack.domain.Players +import blackjack.view.InputConsoleView +import blackjack.view.OutputConsoleView + +object BlackjackApplication { + private val inputConsoleView = InputConsoleView() + private val outputConsoleView = OutputConsoleView() + + @JvmStatic + fun main(args: Array) { + val namesOfPlayers = inputConsoleView.namesOfPlayers() + val players = Players(namesOfPlayers) + + outputConsoleView.printInitCardMsg(players) + players.players.map { player -> + while (player.cardSum() < 21 && inputConsoleView.wannaGetNextCard(player)) { + players.getCard(player) + outputConsoleView.printCards(player) + } + } + + outputConsoleView.printResult(players) + } +} diff --git a/src/main/kotlin/blackjack/domain/Card.kt b/src/main/kotlin/blackjack/domain/Card.kt new file mode 100644 index 0000000000..1a7aaad0fb --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Card.kt @@ -0,0 +1,3 @@ +package blackjack.domain + +data class Card(val shape: CardShape, val number: CardNumber) diff --git a/src/main/kotlin/blackjack/domain/CardNumber.kt b/src/main/kotlin/blackjack/domain/CardNumber.kt new file mode 100644 index 0000000000..fc4e4d2a78 --- /dev/null +++ b/src/main/kotlin/blackjack/domain/CardNumber.kt @@ -0,0 +1,16 @@ +package blackjack.domain + +enum class CardNumber(val value: Number, val display: String) { + ACE(1, "A"), + TWO(2, "2"), + THREE(3, "3"), + FOUR(4, "4"), + FIVE(5, "5"), + SIX(6, "6"), + SEVEN(7, "7"), + EIGHT(8, "8"), + NINE(9, "9"), + JACK(10, "J"), + QUEEN(10, "Q"), + KING(10, "K"), +} diff --git a/src/main/kotlin/blackjack/domain/CardShape.kt b/src/main/kotlin/blackjack/domain/CardShape.kt new file mode 100644 index 0000000000..1ddaa709ef --- /dev/null +++ b/src/main/kotlin/blackjack/domain/CardShape.kt @@ -0,0 +1,8 @@ +package blackjack.domain + +enum class CardShape(val display: String) { + SPADE("스페이드"), + DIAMOND("다이아"), + HEART("하트"), + CLOVA("클로버"), +} diff --git a/src/main/kotlin/blackjack/domain/Player.kt b/src/main/kotlin/blackjack/domain/Player.kt new file mode 100644 index 0000000000..0fcc4d1feb --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Player.kt @@ -0,0 +1,17 @@ +package blackjack.domain + +class Player(val name: String, initCards: List) { + val cards: MutableList = initCards.toMutableList() + + fun addCard(card: Card) { + cards.add(card) + } + + fun cardSum(): Int { + val sum = cards.sumOf { it.number.value.toInt() } + if (cards.map { it.number }.contains(CardNumber.ACE) && sum <= 11) { + return sum + 10 + } + return sum + } +} diff --git a/src/main/kotlin/blackjack/domain/Players.kt b/src/main/kotlin/blackjack/domain/Players.kt new file mode 100644 index 0000000000..201f50fd8a --- /dev/null +++ b/src/main/kotlin/blackjack/domain/Players.kt @@ -0,0 +1,33 @@ +package blackjack.domain + +class Players(names: List) { + private val cards = mutableListOf() + val players = names.map { Player(it, arrayListOf(randomCard(), randomCard())) } + + init { + require(names.size == names.toSet().size) + } + + fun getPlayer(name: String): Player { + return players.find { it.name == name } ?: throw IllegalArgumentException("no such player") + } + + fun getCard(player: Player) { + player.addCard(randomCard()) + } + + private fun randomCard(): Card { + val cardShape = CardShape.values().toList().shuffled()[0] + val cardNumber = CardNumber.values().toList().shuffled()[0] + val card = Card(cardShape, cardNumber) + if (hasCard(card)) { + return randomCard() + } + cards.add(card) + return card + } + + private fun hasCard(card: Card): Boolean { + return cards.contains(card) + } +} diff --git a/src/main/kotlin/blackjack/view/InputConsoleView.kt b/src/main/kotlin/blackjack/view/InputConsoleView.kt new file mode 100644 index 0000000000..2b55767c2c --- /dev/null +++ b/src/main/kotlin/blackjack/view/InputConsoleView.kt @@ -0,0 +1,15 @@ +package blackjack.view + +import blackjack.domain.Player + +class InputConsoleView { + fun namesOfPlayers(): List { + println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)") + return readln().split(",") + } + + fun wannaGetNextCard(player: Player): Boolean { + println("${player.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") + return readln() == "y" + } +} diff --git a/src/main/kotlin/blackjack/view/OutputConsoleView.kt b/src/main/kotlin/blackjack/view/OutputConsoleView.kt new file mode 100644 index 0000000000..c1496f2904 --- /dev/null +++ b/src/main/kotlin/blackjack/view/OutputConsoleView.kt @@ -0,0 +1,30 @@ +package blackjack.view + +import blackjack.domain.Card +import blackjack.domain.Player +import blackjack.domain.Players + +class OutputConsoleView { + fun printInitCardMsg(players: Players) { + println("${players.players.map { it.name }.joinToString(",")} 에게 2장의 카드를 나누었습니다.") + players.players.map { printCards(it) } + println() + } + + fun printResult(players: Players) { + println() + players.players.map { println("${cardsToString(it)} - 결과: ${it.cardSum()}") } + } + + fun printCards(player: Player) { + println(cardsToString(player)) + } + + private fun cardsToString(player: Player): String { + return "${player.name}카드: ${player.cards.joinToString(", ") { cardToString(it) }}" + } + + private fun cardToString(card: Card): String { + return "${card.number.display}${card.shape.display}" + } +} diff --git a/src/main/kotlin/learning/LanguageBuilder.kt b/src/main/kotlin/learning/LanguageBuilder.kt index bdc90f7f68..08d848b653 100644 --- a/src/main/kotlin/learning/LanguageBuilder.kt +++ b/src/main/kotlin/learning/LanguageBuilder.kt @@ -1,9 +1,13 @@ package learning class LanguageBuilder { - val languages = mutableListOf() + private val languages = mutableListOf() infix fun String.level(level: Int) { languages.add(Language(this, level)) } + + fun build(): MutableList { + return languages + } } diff --git a/src/main/kotlin/learning/PersonBuilder.kt b/src/main/kotlin/learning/PersonBuilder.kt index 29041cd73d..6ce9e7acce 100644 --- a/src/main/kotlin/learning/PersonBuilder.kt +++ b/src/main/kotlin/learning/PersonBuilder.kt @@ -3,8 +3,8 @@ package learning class PersonBuilder { private lateinit var name: String private var company: String? = null - private val skills = mutableListOf() - private val languages = mutableListOf() + private var skills = mutableListOf() + private var languages = mutableListOf() fun name(value: String) { name = value @@ -15,13 +15,11 @@ class PersonBuilder { } fun skills(block: SkillsBuilder.() -> Unit) { - val skillsBuilder = SkillsBuilder().apply(block) - skills.addAll(skillsBuilder.skills) + this.skills = SkillsBuilder().apply(block).build() } fun languages(block: LanguageBuilder.() -> Unit) { - val languageBuilder = LanguageBuilder().apply(block) - languages.addAll(languageBuilder.languages) + this.languages = LanguageBuilder().apply(block).build() } fun build(): Person { diff --git a/src/main/kotlin/learning/SkillsBuilder.kt b/src/main/kotlin/learning/SkillsBuilder.kt index ec780d3655..67e5e58bf1 100644 --- a/src/main/kotlin/learning/SkillsBuilder.kt +++ b/src/main/kotlin/learning/SkillsBuilder.kt @@ -1,7 +1,7 @@ package learning class SkillsBuilder { - val skills = mutableListOf() + private val skills = mutableListOf() fun soft(value: String) { skills.add(Skill(SkillType.SOFT, value)) @@ -10,4 +10,8 @@ class SkillsBuilder { fun hard(value: String) { skills.add(Skill(SkillType.HARD, value)) } + + fun build(): MutableList { + return skills + } } diff --git a/src/test/kotlin/blackjack/domain/CardTest.kt b/src/test/kotlin/blackjack/domain/CardTest.kt new file mode 100644 index 0000000000..188b919176 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/CardTest.kt @@ -0,0 +1,10 @@ +package blackjack.domain + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class CardTest : StringSpec({ + "Card 생성 성공" { + Card(CardShape.CLOVA, CardNumber.JACK) shouldBe Card(CardShape.CLOVA, CardNumber.JACK) + } +}) diff --git a/src/test/kotlin/blackjack/domain/PlayerTest.kt b/src/test/kotlin/blackjack/domain/PlayerTest.kt new file mode 100644 index 0000000000..c69d8cbd9f --- /dev/null +++ b/src/test/kotlin/blackjack/domain/PlayerTest.kt @@ -0,0 +1,48 @@ +package blackjack.domain + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class PlayerTest : StringSpec({ + val initCards = listOf(Card(CardShape.CLOVA, CardNumber.TWO), Card(CardShape.HEART, CardNumber.THREE)) + var edge = Player("edge", initCards) + + beforeEach { + edge = Player("edge", initCards) + } + + "player 생성 성공" { + edge.name shouldBe "edge" + } + + "player 카드 확인하기" { + edge.cards shouldBe initCards + } + + "player 카드 합산하기" { + edge.cardSum() shouldBe 5 + } + + "player에 카드 추가하기" { + val addCard = Card(CardShape.DIAMOND, CardNumber.JACK) + edge.addCard(addCard) + + edge.cardSum() shouldBe 15 + val list = initCards.toMutableList() + list.add(addCard) + edge.cards shouldBe list + } + + "합이 11보다 작거나 같으면 ace를 11로 계산하기" { + edge.addCard(Card(CardShape.DIAMOND, CardNumber.ACE)) + + edge.cardSum() shouldBe 16 + } + + "합이 11보다 크면 ace를 1로 계산하기" { + edge.addCard(Card(CardShape.DIAMOND, CardNumber.ACE)) + edge.addCard(Card(CardShape.DIAMOND, CardNumber.JACK)) + + edge.cardSum() shouldBe 16 + } +}) diff --git a/src/test/kotlin/blackjack/domain/PlayersTest.kt b/src/test/kotlin/blackjack/domain/PlayersTest.kt new file mode 100644 index 0000000000..e9c2c593c3 --- /dev/null +++ b/src/test/kotlin/blackjack/domain/PlayersTest.kt @@ -0,0 +1,18 @@ +package blackjack.domain + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class PlayersTest : StringSpec({ + "players 생성 성공" { + val players = Players(listOf("edge", "test")) + players.getPlayer("edge").name shouldBe "edge" + } + + "player 이름이 중복되면 에러" { + shouldThrow { + Players(listOf("edge", "edge")) + } + } +}) diff --git a/src/test/kotlin/learning/DslTest.kt b/src/test/kotlin/learning/DslTest.kt index e5e89ebf6c..751900abfc 100644 --- a/src/test/kotlin/learning/DslTest.kt +++ b/src/test/kotlin/learning/DslTest.kt @@ -1,5 +1,6 @@ package learning +import io.kotest.matchers.collections.shouldBeIn import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test @@ -53,14 +54,11 @@ class DslTest { soft("Good communication skills") hard("Kotlin") } - languages { - "Korean" level 5 - "English" level 3 - } } - person.languages.languages[0] shouldBe Language("Korean", 5) - person.languages.languages[1] shouldBe Language("English", 3) + Skill(SkillType.SOFT, "A passion for problem solving").shouldBeIn(person.skills.skills) + Skill(SkillType.SOFT, "Good communication skills").shouldBeIn(person.skills.skills) + Skill(SkillType.HARD, "Kotlin").shouldBeIn(person.skills.skills) } @Test @@ -73,11 +71,14 @@ class DslTest { soft("Good communication skills") hard("Kotlin") } + languages { + "Korean" level 5 + "English" level 3 + } } - person.skills.skills[0] shouldBe Skill(SkillType.SOFT, "A passion for problem solving") - person.skills.skills[1] shouldBe Skill(SkillType.SOFT, "Good communication skills") - person.skills.skills[2] shouldBe Skill(SkillType.HARD, "Kotlin") + Language("Korean", 5).shouldBeIn(person.languages.languages) + Language("English", 3).shouldBeIn(person.languages.languages) } }