Skip to content
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# kotlin-blackjack
# kotlin-blackjack

## 요구사항
- 카드의 숫자 계산은 카드 숫자를 기본으로 하며
- Ace는 1 또는 11로 계산할 수 있으며
- King, Queen, Jack은 각각 10으로 계산한다.
- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며
- 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다.
- 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다.
Comment on lines +3 to +9

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p1; 프로그래밍 요구사항이 몇 가지 지켜지지 않은 부분들이 있습니다.

  • ACE가 21을 넘지 않는 경우 16이 아닌 7로 계산되는 점
  • 21을 초과했음에도 카드를 계속 받겠냐는 문구
  • 각 카드 뭉치의 suit(다이아, 스페이스, 하트, 클로버)가 나누어 있지 않는 점

다음 요구사항을 확인하고, 다시 PR 요청 부탁드리겠습니다. 💪🏽

image

19 changes: 19 additions & 0 deletions src/main/kotlin/blackjack/Card.kt
Original file line number Diff line number Diff line change
@@ -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);
}
6 changes: 6 additions & 0 deletions src/main/kotlin/blackjack/CardDeck.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package blackjack

interface CardDeck {
fun drawCard(): Card

}
14 changes: 14 additions & 0 deletions src/main/kotlin/blackjack/InputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package blackjack

fun oneMoreCardInput(): Boolean {
val input = readlnOrNull() ?: throw IllegalArgumentException("올바른 입력을 해주세요")
return when (input) {
"y" -> true
"n" -> false
else -> throw IllegalArgumentException("올바른 입력을 해주세요")
}
Comment on lines +5 to +9

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3; view의 영역이라도 적절한 객체로 관리하면 어떨까요?

}

fun namesInput(): List<String> {
return readlnOrNull()?.split(",") ?: throw IllegalArgumentException("이름을 입력해주세요.")
}
18 changes: 18 additions & 0 deletions src/main/kotlin/blackjack/ListCardDeck.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package blackjack

class ListCardDeck(
private val cards: MutableList<Card>
): CardDeck {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3; 클래스 정렬이 되어 있지 않은 거 같아요.


override fun drawCard(): Card {
checkCardCount()
return cards.removeAt(cards.size - 1)
}

private fun checkCardCount() {
if (cards.size == 0) {
throw IllegalStateException("카드가 없습니다.")
}
}

}
47 changes: 47 additions & 0 deletions src/main/kotlin/blackjack/Main.kt
Original file line number Diff line number Diff line change
@@ -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<Player>, 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<Player>, 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
}
Comment on lines +3 to +47

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p1; 메인에 작성해 주신 내용들은 비즈니스 로직 성격이 짙은 거 같아요.
메인에서는 오케스트레이션 역할 정도만 하고, 각 도메인에게 적절한 메시지를 던지면 어떨까요?

27 changes: 27 additions & 0 deletions src/main/kotlin/blackjack/OutputView.kt
Original file line number Diff line number Diff line change
@@ -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<Player>) {
players.forEach {
println("${it.name}카드: ${it.cards.joinToString(separator = ",") { card -> card.name }}-결과: ${it.stop()}")
}
}

fun oneMoreCardOutput(name: String) {
println("${name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)")
}
19 changes: 19 additions & 0 deletions src/main/kotlin/blackjack/Player.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package blackjack

class Player(
val name: String,
) {
private val _cards: MutableList<Card> = mutableListOf()

val cards: List<Card>
get() = _cards.toList()

fun go(card: Card) {
_cards.add(card)
}

fun stop(): Int {
return ScoreCalculator.calc(cards)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2; stop이라는 메서드에서 계산까지 해주는 형태네요. 플레이어가 ScoreCalculator를 알아야 할지 고민해 보면 좋을 거 같아요.

}

}
7 changes: 7 additions & 0 deletions src/main/kotlin/blackjack/RandomCardDeck.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package blackjack

class RandomCardDeck: CardDeck {
override fun drawCard(): Card {
return Card.values().random()
}
}
Comment on lines +3 to +7

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p1; 카드덱은 정해져 있고, 그 과정에서 랜덤하게 나와야 할 거 같은데 이 구조라면 A(다이아), A(다이아), A(다이아)... 처럼 동일한 카드와 형태를 받을 수도 있을 거 같아요.

8 changes: 8 additions & 0 deletions src/main/kotlin/blackjack/ScoreCalculator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package blackjack

object ScoreCalculator {

fun calc(cards: List<Card>): Int {
return cards.map { it.score }.reduce { acc, i -> acc + i }
}
}
5 changes: 5 additions & 0 deletions src/main/kotlin/dsl/Dsl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package dsl

@DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class Dsl()
162 changes: 65 additions & 97 deletions src/main/kotlin/dsl/Resume.kt
Original file line number Diff line number Diff line change
@@ -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<String> = mutableListOf(),
val hardSkills: MutableList<String> = mutableListOf()
data class Skills(
val softSkills: List<String>,
val hardSkills: List<String>
) {

fun soft(skill: String) {
softSkills.add(skill)
}

fun hard(skill: String) {
hardSkills.add(skill)
class Builder(
private val softSkills: MutableList<String> = mutableListOf(),
private val hardSkills: MutableList<String> = 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<String, Int> = mutableMapOf()
data class Languages(
val languages: Map<String, Int>
) {
class Builder(
private val languages: MutableMap<String, Int> = 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()
}
16 changes: 16 additions & 0 deletions src/test/kotlin/blackjack/CardDeckTest.kt
Original file line number Diff line number Diff line change
@@ -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
}

})
Loading