Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Step2: 블랙잭 구현 #667

Open
wants to merge 12 commits into
base: baek0318
Choose a base branch
from
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);
}
baek0318 marked this conversation as resolved.
Show resolved Hide resolved
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

baek0318 marked this conversation as resolved.
Show resolved Hide resolved
}
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)
baek0318 marked this conversation as resolved.
Show resolved Hide resolved
}

private fun checkCardCount() {
if (cards.size == 0) {
throw IllegalStateException("카드가 없습니다.")
}
baek0318 marked this conversation as resolved.
Show resolved Hide resolved
}

}
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 }
baek0318 marked this conversation as resolved.
Show resolved Hide resolved
}
}
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())
}
baek0318 marked this conversation as resolved.
Show resolved Hide resolved
}


}

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