diff --git a/README.md b/README.md index e1c7c927d8..db1c54f214 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ -# kotlin-blackjack \ No newline at end of file +# kotlin-blackjack + +## 요구사항 +- 카드의 숫자 계산은 카드 숫자를 기본으로 하며 + - Ace는 1 또는 11로 계산할 수 있으며 + - King, Queen, Jack은 각각 10으로 계산한다. +- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며 + - 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. + - 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. \ No newline at end of file diff --git a/src/main/kotlin/blackjack/Card.kt b/src/main/kotlin/blackjack/Card.kt new file mode 100644 index 0000000000..c14ea567f8 --- /dev/null +++ b/src/main/kotlin/blackjack/Card.kt @@ -0,0 +1,19 @@ +package blackjack + +enum class Card( + val score: Int +) { + ACE(1), + TWO(2), + THREE(3), + FOUR(4), + FIVE(5), + SIX(6), + SEVEN(7), + EIGHT(8), + NINE(9), + TEN(10), + JACK(10), + QUEEN(10), + KING(10); +} \ No newline at end of file diff --git a/src/main/kotlin/blackjack/CardDeck.kt b/src/main/kotlin/blackjack/CardDeck.kt new file mode 100644 index 0000000000..6d791a7b3c --- /dev/null +++ b/src/main/kotlin/blackjack/CardDeck.kt @@ -0,0 +1,6 @@ +package blackjack + +interface CardDeck { + fun drawCard(): Card + +} \ No newline at end of file diff --git a/src/main/kotlin/blackjack/InputView.kt b/src/main/kotlin/blackjack/InputView.kt new file mode 100644 index 0000000000..c6d5964ed4 --- /dev/null +++ b/src/main/kotlin/blackjack/InputView.kt @@ -0,0 +1,14 @@ +package blackjack + +fun oneMoreCardInput(): Boolean { + val input = readlnOrNull() ?: throw IllegalArgumentException("올바른 입력을 해주세요") + return when (input) { + "y" -> true + "n" -> false + else -> throw IllegalArgumentException("올바른 입력을 해주세요") + } +} + +fun namesInput(): List { + return readlnOrNull()?.split(",") ?: throw IllegalArgumentException("이름을 입력해주세요.") +} \ No newline at end of file diff --git a/src/main/kotlin/blackjack/ListCardDeck.kt b/src/main/kotlin/blackjack/ListCardDeck.kt new file mode 100644 index 0000000000..42abafa144 --- /dev/null +++ b/src/main/kotlin/blackjack/ListCardDeck.kt @@ -0,0 +1,18 @@ +package blackjack + +class ListCardDeck( + private val cards: MutableList +): CardDeck { + + override fun drawCard(): Card { + checkCardCount() + return cards.removeAt(cards.size - 1) + } + + private fun checkCardCount() { + if (cards.size == 0) { + throw IllegalStateException("카드가 없습니다.") + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt new file mode 100644 index 0000000000..a63e57753f --- /dev/null +++ b/src/main/kotlin/blackjack/Main.kt @@ -0,0 +1,47 @@ +package blackjack + +fun main() { + startOutput() + val names = namesInput() + val players = names.map { Player(it) } + val deck = RandomCardDeck() + + giveCardsOutput(players.joinToString(separator = ",") { it.name }) + initPlayer(players, deck) + + playGame(players, deck) + + resultOutput(players) +} + +fun initPlayer(players: List, deck: CardDeck) { + players.forEach { + it.go(deck.drawCard()) + it.go(deck.drawCard()) + whatCardsOutput(it.name, it.cards.joinToString(separator = ",") { card -> card.name }) + } +} + +fun playGame(players: List, deck: CardDeck) { + players.forEach { + playerGoOrStop(it, deck) + } +} + +private fun playerGoOrStop(player: Player, deck: CardDeck) { + while (true) { + if (isGo(player, deck)) break + } +} + +private fun isGo(player: Player, deck: CardDeck): Boolean { + oneMoreCardOutput(player.name) + val isGo = oneMoreCardInput() + if (isGo) { + player.go(deck.drawCard()) + drawResultOutput(player.name, player.cards.joinToString(separator = ",") { card -> card.name }) + } else { + return true + } + return false +} \ No newline at end of file diff --git a/src/main/kotlin/blackjack/OutputView.kt b/src/main/kotlin/blackjack/OutputView.kt new file mode 100644 index 0000000000..6cd867266d --- /dev/null +++ b/src/main/kotlin/blackjack/OutputView.kt @@ -0,0 +1,27 @@ +package blackjack + +fun startOutput() { + println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)") +} + +fun giveCardsOutput(name: String) { + println("${name}에게 카드를 2장 나누었습니다.") +} + +fun whatCardsOutput(name: String, cardsName: String) { + println("${name}카드: $cardsName") +} + +fun drawResultOutput(name: String, cardsName: String) { + println("${name}카드: $cardsName") +} + +fun resultOutput(players: List) { + players.forEach { + println("${it.name}카드: ${it.cards.joinToString(separator = ",") { card -> card.name }}-결과: ${it.stop()}") + } +} + +fun oneMoreCardOutput(name: String) { + println("${name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)") +} \ No newline at end of file diff --git a/src/main/kotlin/blackjack/Player.kt b/src/main/kotlin/blackjack/Player.kt new file mode 100644 index 0000000000..60f0ad4477 --- /dev/null +++ b/src/main/kotlin/blackjack/Player.kt @@ -0,0 +1,19 @@ +package blackjack + +class Player( + val name: String, +) { + private val _cards: MutableList = mutableListOf() + + val cards: List + get() = _cards.toList() + + fun go(card: Card) { + _cards.add(card) + } + + fun stop(): Int { + return ScoreCalculator.calc(cards) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/blackjack/RandomCardDeck.kt b/src/main/kotlin/blackjack/RandomCardDeck.kt new file mode 100644 index 0000000000..8778bf9689 --- /dev/null +++ b/src/main/kotlin/blackjack/RandomCardDeck.kt @@ -0,0 +1,7 @@ +package blackjack + +class RandomCardDeck: CardDeck { + override fun drawCard(): Card { + return Card.values().random() + } +} \ No newline at end of file diff --git a/src/main/kotlin/blackjack/ScoreCalculator.kt b/src/main/kotlin/blackjack/ScoreCalculator.kt new file mode 100644 index 0000000000..f672fed0a1 --- /dev/null +++ b/src/main/kotlin/blackjack/ScoreCalculator.kt @@ -0,0 +1,8 @@ +package blackjack + +object ScoreCalculator { + + fun calc(cards: List): Int { + return cards.map { it.score }.reduce { acc, i -> acc + i } + } +} \ No newline at end of file diff --git a/src/main/kotlin/dsl/Dsl.kt b/src/main/kotlin/dsl/Dsl.kt new file mode 100644 index 0000000000..cf9109251b --- /dev/null +++ b/src/main/kotlin/dsl/Dsl.kt @@ -0,0 +1,5 @@ +package dsl + +@DslMarker +@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) +annotation class Dsl() diff --git a/src/main/kotlin/dsl/Resume.kt b/src/main/kotlin/dsl/Resume.kt index e46b870dee..40b966bd03 100644 --- a/src/main/kotlin/dsl/Resume.kt +++ b/src/main/kotlin/dsl/Resume.kt @@ -1,114 +1,82 @@ package dsl -class Resume( - var name: String = "", - var company: String = "", - var skills: Skill = Skill(), - var languages: Language = Language() +data class Resume( + val name: String, + val company: String, + val skills: Skills, + val languages: Languages ) { - - fun name(name: String) { - this.name = name - } - - fun company(companyName: String) { - this.company = companyName - } - - fun skills(block: Skill.() -> Unit) { - skills.block() - } - - fun languages(block: Language.() -> Unit) { - languages.block() - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Resume - - if (name != other.name) return false - if (company != other.company) return false - if (skills != other.skills) return false - if (languages != other.languages) return false - - return true - } - - override fun hashCode(): Int { - var result = name.hashCode() - result = 31 * result + company.hashCode() - result = 31 * result + skills.hashCode() - result = 31 * result + languages.hashCode() - return result + class Builder( + private var name: String = "", + private var company: String = "", + private var skills: Skills.Builder = Skills.Builder(), + private var languages: Languages.Builder = Languages.Builder() + ) { + + fun name(name: String) { + this.name = name + } + + fun company(companyName: String) { + this.company = companyName + } + + fun skills(block: (@Dsl Skills.Builder).() -> Unit) { + skills.block() + } + + fun languages(block: (@Dsl Languages.Builder).() -> Unit) { + languages.block() + } + + internal fun build(): Resume { + return Resume(name, company, skills.build(), languages.build()) + } } - - } -class Skill( - val softSkills: MutableList = mutableListOf(), - val hardSkills: MutableList = mutableListOf() +data class Skills( + val softSkills: List, + val hardSkills: List ) { - - fun soft(skill: String) { - softSkills.add(skill) - } - - fun hard(skill: String) { - hardSkills.add(skill) + class Builder( + private val softSkills: MutableList = mutableListOf(), + private val hardSkills: MutableList = mutableListOf() + ) { + + fun soft(skill: String) { + softSkills.add(skill) + } + + fun hard(skill: String) { + hardSkills.add(skill) + } + + internal fun build(): Skills { + return Skills(softSkills, hardSkills) + } } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Skill - - if (softSkills != other.softSkills) return false - if (hardSkills != other.hardSkills) return false - - return true - } - - override fun hashCode(): Int { - var result = softSkills.hashCode() - result = 31 * result + hardSkills.hashCode() - return result - } - - } -class Language( - val languages: MutableMap = mutableMapOf() +data class Languages( + val languages: Map ) { + class Builder( + private val languages: MutableMap = mutableMapOf() + ) { - infix fun String.level(level: Int) { - languages[this] = level - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false + infix fun String.level(level: Int) { + languages[this] = level + } - other as Language - - return languages == other.languages + internal fun build(): Languages { + return Languages(languages) + } } - - override fun hashCode(): Int { - return languages.hashCode() - } - - } - -fun introduce(block: Resume.() -> Unit): Resume { - val resume = Resume() - resume.block() - return resume +fun introduce(block: (@Dsl Resume.Builder).() -> Unit): Resume { + val builder = Resume.Builder() + builder.block() + return builder.build() } diff --git a/src/test/kotlin/blackjack/CardDeckTest.kt b/src/test/kotlin/blackjack/CardDeckTest.kt new file mode 100644 index 0000000000..09234659f7 --- /dev/null +++ b/src/test/kotlin/blackjack/CardDeckTest.kt @@ -0,0 +1,16 @@ +package blackjack + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import java.util.* +import kotlin.collections.ArrayList + +class CardDeckTest: StringSpec({ + + "카드를 뽑을 수 있다" { + val cardDeck = ListCardDeck(mutableListOf(Card.TWO, Card.THREE)) + val card = cardDeck.drawCard() + card shouldBe Card.THREE + } + +}) \ No newline at end of file diff --git a/src/test/kotlin/blackjack/PlayerTest.kt b/src/test/kotlin/blackjack/PlayerTest.kt new file mode 100644 index 0000000000..2bd728e6fe --- /dev/null +++ b/src/test/kotlin/blackjack/PlayerTest.kt @@ -0,0 +1,28 @@ +package blackjack + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class PlayerTest: StringSpec({ + + "player는 go를 한다면 카드를 받을 수 있어야 한다" { + val player = Player("test") + player.go(Card.TWO) + player.go(Card.THREE) + + player.cards shouldBe listOf(Card.TWO, Card.THREE) + } + + "player는 stop을 한다면 자신의 점수를 계산해서 줘야한다" { + val player = Player("test") + player.go(Card.TWO) + player.go(Card.THREE) + + player.stop() shouldBe 5 + } + + "player는 이름을 가질 수 있다" { + val player = Player("edward") + player.name shouldBe "edward" + } +}) diff --git a/src/test/kotlin/blackjack/ScoreCalculatorTest.kt b/src/test/kotlin/blackjack/ScoreCalculatorTest.kt new file mode 100644 index 0000000000..ac28de8188 --- /dev/null +++ b/src/test/kotlin/blackjack/ScoreCalculatorTest.kt @@ -0,0 +1,12 @@ +package blackjack + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class ScoreCalculatorTest: StringSpec({ + + "카드 점수에 맞게 최종 점수가 계산 되어야 한다" { + ScoreCalculator.calc(listOf(Card.TWO, Card.THREE)) shouldBe 5 + } + +}) \ No newline at end of file diff --git a/src/test/kotlin/dsl/ResumeTest.kt b/src/test/kotlin/dsl/ResumeTest.kt index fdc26060c9..e0b5496019 100644 --- a/src/test/kotlin/dsl/ResumeTest.kt +++ b/src/test/kotlin/dsl/ResumeTest.kt @@ -3,7 +3,7 @@ package dsl import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe -class ResumeTest:StringSpec({ +class ResumeTest : StringSpec({ "이력서 dsl 생성 테스트" { val result = introduce { @@ -22,16 +22,16 @@ class ResumeTest:StringSpec({ result shouldBe Resume( name = "박재성", company = "우아한형제들", - skills = Skill( - softSkills = mutableListOf("A passion for problem solving", "Good communication skills"), - hardSkills = mutableListOf("Kotlin") + skills = Skills( + softSkills = listOf("A passion for problem solving", "Good communication skills"), + hardSkills = listOf("Kotlin") ), - languages = Language( - languages = mutableMapOf( + languages = Languages( + languages = mapOf( "Korean" to 5, "English" to 3 ) ) ) } -}) \ No newline at end of file +})