diff --git a/README.md b/README.md index e1c7c927d8..714e7f8a50 100644 --- a/README.md +++ b/README.md @@ -1 +1,54 @@ -# kotlin-blackjack \ No newline at end of file +# kotlin-blackjack + +## 기능 요구 사항 + +블랙잭 게임을 변형한 프로그램을 구현한다. 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다. + +- 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다. +- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. + +## 기능 목록 + +### 플레이어 + +- [O] 플레이어의 초기 상태는 START이다. +- [O] 플레이어가 카드 받는 것을 끝내면 상태는 STAND가 된다. +- [O] 플레이어는 받은 카드를 저장한다. +- [O] 플레이어는 상태를 업데이트 한다. + - [O] 카드의 합이 20 이하 라면 PLAYING + - [O] 카드의 합이 21을 이상 이라면 BUST + - [O] 카드를 받지 않는다 하면 STAND + - [O] 첫 2장의 카드 합이 21이라면 BLACK_JACK + +### 플레이어 그룹 + +- [O] 플레이어는 최소 2명 이상 26명 이하여야 한다. + +### 플레이어 펙토리 +- [O] 문자열을 받아서 플레이어 리스트 생성. +- [O] 공백은 플레어어로 생성하지 않는다. + +### 트럼프 카드(플레잉 카드) + +- [O] 카드의 포인트를 반환한다. +- [O] 카드의 무늬 명을 반환한다. +- [O] 카드의 번호를 반환한다. + +### 게임 덱 +- [O] 카드를 순차적으로 반환한다. + +### 플레이어 덱 +- [O] 카드를 추가할 수 있다. +- [O] 점수를 반환한다. + - [O] A는 블랙잭에 유리하게 점수를 반영한다. + +### inputView + +- [O] 플레이어를 입력받는다. + - [O] 플레이어가 입력되지 않으면 예외를 던진다. + +### output + +- [O] 게임 시작 시 초기 카드 출력 +- [O] 플레이어가 가지고 있는 카드를 출력 +- [O] 플레이어 카드 계산값을 출력 diff --git a/src/main/kotlin/BlackjackGame.kt b/src/main/kotlin/BlackjackGame.kt new file mode 100644 index 0000000000..9d96d4de75 --- /dev/null +++ b/src/main/kotlin/BlackjackGame.kt @@ -0,0 +1,60 @@ +import card.deck.CardDeck +import player.Player +import player.PlayerGroup +import player.Status +import view.InputViewInterface +import view.OutputViewInterface + +class BlackjackGame( + private val cardDeck: CardDeck, + val playerGroup: PlayerGroup, + private val inputView: InputViewInterface, + private val outputView: OutputViewInterface, +) { + + init { + for (player in playerGroup.playerList) { + settingCard(player) + } + } + + fun start() { + for (player in playerGroup.playerList) { + gamePlay(player) + } + } + + private fun gamePlay(player: Player) { + while (player.status == Status.PLAYING) { + val response = inputView.askForHit(player.name) + handleForResponse(response, player) + outputView.showPlayingCard(player) + } + } + + private fun handleForResponse(response: String, player: Player) { + when (response.uppercase()) { + TEXT_ANSWER_YES -> { + player.saveCard(cardDeck.getCardWithIncrease()) + } + TEXT_ANSWER_NO -> { + player.playDone() + } + else -> { + println(TEXT_RETRY_INPUT) + } + } + } + + private fun settingCard(player: Player) { + repeat(2) { + player.saveCard(cardDeck.getCardWithIncrease()) + } + } + + companion object { + private const val TEXT_RETRY_INPUT = "Y 혹은 N만 입력 가능합니다." + private const val TEXT_ANSWER_YES = "Y" + private const val TEXT_ANSWER_NO = "N" + } +} diff --git a/src/main/kotlin/BlackjackMain.kt b/src/main/kotlin/BlackjackMain.kt new file mode 100644 index 0000000000..13fc36b4c8 --- /dev/null +++ b/src/main/kotlin/BlackjackMain.kt @@ -0,0 +1,21 @@ +import card.CardPack +import card.deck.CardDeck +import player.PlayerFactory +import player.PlayerGroup +import view.InputView +import view.OutputView + +fun main() { + + val playerNames = InputView.inputPlayerName() + val playerGroup = PlayerGroup(PlayerFactory.createPlayerList(playerNames)) + val cardDeck = CardDeck(CardPack.cards.toMutableList()) + + val game = BlackjackGame(cardDeck = cardDeck, playerGroup = playerGroup, InputView, OutputView) + + OutputView.showGameStart(game.playerGroup) + + game.start() + + OutputView.showGameEnd(game.playerGroup) +} diff --git a/src/main/kotlin/card/CardPack.kt b/src/main/kotlin/card/CardPack.kt new file mode 100644 index 0000000000..8ce92082c0 --- /dev/null +++ b/src/main/kotlin/card/CardPack.kt @@ -0,0 +1,9 @@ +package card + +object CardPack { + val cards: List = CardRank.values().flatMap { rank -> + Suit.values().map { suit -> + PlayingCard.of(suit = suit, cardRank = rank) + } + } +} diff --git a/src/main/kotlin/card/CardRank.kt b/src/main/kotlin/card/CardRank.kt new file mode 100644 index 0000000000..1248e17401 --- /dev/null +++ b/src/main/kotlin/card/CardRank.kt @@ -0,0 +1,17 @@ +package card + +enum class CardRank(val point: Int, val symbol: String) { + ACE(1, "A"), + TWO(2, "2"), + TREE(3, "3"), + FOUR(4, "4"), + FIVE(5, "5"), + SIX(6, "6"), + SEVEN(7, "7"), + EIGHT(8, "8"), + NINE(9, "9"), + TEN(10, "10"), + JACK(10, "J"), + QUEEN(10, "Q"), + KING(10, "K"); +} diff --git a/src/main/kotlin/card/PlayingCard.kt b/src/main/kotlin/card/PlayingCard.kt new file mode 100644 index 0000000000..51bc0e1fc9 --- /dev/null +++ b/src/main/kotlin/card/PlayingCard.kt @@ -0,0 +1,35 @@ +package card + +data class PlayingCard(val suit: Suit, val cardRank: CardRank) { + fun getPoint(): Int { + return cardRank.point + } + + fun getSuitName(): String { + return suit.koName + } + + override fun toString(): String { + return "${cardRank.symbol}${suit.koName}" + } + + companion object { + private val CARDS: MutableMap = mutableMapOf() + + fun of(suit: Suit, cardRank: CardRank): PlayingCard { + return CARDS[toKey(suit, cardRank)] ?: throw NoSuchElementException() + } + + private fun toKey(suit: Suit, cardRank: CardRank): String { + return suit.name + cardRank.name + } + + init { + for (suit in Suit.values()) { + for (cardRank in CardRank.values()) { + CARDS[toKey(suit, cardRank)] = PlayingCard(suit, cardRank) + } + } + } + } +} diff --git a/src/main/kotlin/card/Suit.kt b/src/main/kotlin/card/Suit.kt new file mode 100644 index 0000000000..06dbde7394 --- /dev/null +++ b/src/main/kotlin/card/Suit.kt @@ -0,0 +1,7 @@ +package card +enum class Suit(val koName: String) { + SPADE("스페이드"), + HEART("하트"), + DIAMOND("다이아몬드"), + CLUB("클러버"); +} diff --git a/src/main/kotlin/card/deck/CardDeck.kt b/src/main/kotlin/card/deck/CardDeck.kt new file mode 100644 index 0000000000..46aea755be --- /dev/null +++ b/src/main/kotlin/card/deck/CardDeck.kt @@ -0,0 +1,26 @@ +package card.deck + +import card.PlayingCard + +class CardDeck(private val cardList: MutableList) { + + init { + cardList.shuffle() + } + + private var index = 0 + + fun getCardWithIncrease(): PlayingCard { + if (isMaxIndexOfCard()) resetCard() + return cardList[index++] + } + + private fun isMaxIndexOfCard(): Boolean { + return index == cardList.size + } + + private fun resetCard() { + cardList.shuffle() + index = 0 + } +} diff --git a/src/main/kotlin/card/deck/Hands.kt b/src/main/kotlin/card/deck/Hands.kt new file mode 100644 index 0000000000..92a42daa80 --- /dev/null +++ b/src/main/kotlin/card/deck/Hands.kt @@ -0,0 +1,50 @@ +package card.deck + +import card.CardRank +import card.PlayingCard + +class Hands(val cardList: MutableList = mutableListOf()) { + + fun addCard(playingCard: PlayingCard) { + cardList.add(playingCard) + } + + fun getResultPoint(): Int { + var point = cardList.sumOf { it.getPoint() } + + if (isContainAce()) { + point += addAcePoint(point) + } + + return point + } + + fun cardDeckSize() = cardList.size + + private fun isContainAce(): Boolean { + return cardList.any { it.cardRank == CardRank.ACE } + } + + private fun addAcePoint(point: Int): Int { + return if (point <= 11) { + ADD_ACE_POINT + } else { + ADD_ACE_POINT_NONE + } + } + + companion object { + fun create() = Hands(mutableListOf()) + private const val ADD_ACE_POINT = 10 + private const val ADD_ACE_POINT_NONE = 0 + } + + override fun toString(): String { + val sb = StringBuilder() + for (card in cardList) { + sb.append("$card, ") + } + sb.deleteCharAt(sb.lastIndex - 1) + return sb.toString() + } +} diff --git a/src/main/kotlin/player/Player.kt b/src/main/kotlin/player/Player.kt new file mode 100644 index 0000000000..80e2dfaf37 --- /dev/null +++ b/src/main/kotlin/player/Player.kt @@ -0,0 +1,52 @@ +package player + +import card.PlayingCard +import card.deck.Hands + +class Player( + val name: String, + status: Status = Status.START, + hands: Hands = Hands(), +) { + + var status = status + private set + + var playerDeck = hands + private set + + fun playDone() { + this.status = Status.STAND + } + + fun saveCard(card: PlayingCard) { + playerDeck.addCard(card) + updateStatus() + } + + fun updateStatus() { + val totalPoint = playerDeck.getResultPoint() + + if (totalPoint > BLACKJACK_NUMBER) { + this.status = Status.BUST + } else if (isBlackJack()) { + this.status = Status.BLACK_JACK + } else { + this.status = Status.PLAYING + } + } + + fun getResultPoint(): Int { + return playerDeck.getResultPoint() + } + + private fun isBlackJack(): Boolean { + val totalPoint = playerDeck.getResultPoint() + return playerDeck.cardDeckSize() == BLACKJACK_CARD_COUNT && totalPoint == BLACKJACK_NUMBER + } + + companion object { + private const val BLACKJACK_CARD_COUNT = 2 + private const val BLACKJACK_NUMBER = 21 + } +} diff --git a/src/main/kotlin/player/PlayerFactory.kt b/src/main/kotlin/player/PlayerFactory.kt new file mode 100644 index 0000000000..2e348012ba --- /dev/null +++ b/src/main/kotlin/player/PlayerFactory.kt @@ -0,0 +1,19 @@ +package player + +object PlayerFactory { + + private const val DELIMITERS = ',' + + fun createPlayerList(playerNames: String): List { + val playerNameList = splitInputData(playerNames) + val playerList = mutableListOf() + playerNameList.forEach { name -> + playerList.add(Player(name)) + } + return playerList + } + + private fun splitInputData(inputData: String): List { + return inputData.split(DELIMITERS).map { it.trim() }.filter { it.isNotBlank() } + } +} diff --git a/src/main/kotlin/player/PlayerGroup.kt b/src/main/kotlin/player/PlayerGroup.kt new file mode 100644 index 0000000000..eb7e58f104 --- /dev/null +++ b/src/main/kotlin/player/PlayerGroup.kt @@ -0,0 +1,17 @@ +package player + +class PlayerGroup(val playerList: List) { + + init { + validatePlayer() + } + + private fun validatePlayer() { + require(playerList.size in MIN_PLAYER_CNT..MAM_PLAYER_CNT) { "참여 가능한 플레이어의 범위를 넘어섰습니다." } + } + + companion object { + private const val MIN_PLAYER_CNT = 2 + private const val MAM_PLAYER_CNT = 26 + } +} diff --git a/src/main/kotlin/player/Status.kt b/src/main/kotlin/player/Status.kt new file mode 100644 index 0000000000..a5f8ae38fc --- /dev/null +++ b/src/main/kotlin/player/Status.kt @@ -0,0 +1,5 @@ +package player + +enum class Status { + START, PLAYING, STAND, BLACK_JACK, BUST +} diff --git a/src/main/kotlin/view/InputView.kt b/src/main/kotlin/view/InputView.kt new file mode 100644 index 0000000000..c56b148029 --- /dev/null +++ b/src/main/kotlin/view/InputView.kt @@ -0,0 +1,32 @@ +package view + +object InputView : InputViewInterface { + + private const val TEXT_INPUT_PROMPT = "게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)" + private const val TEXT_HIT_PROMPT_FORMAT = "%s는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)" + private const val ERR_MSG_MINIMUM_PLAYER_COUNT = "출전할 플레이어는 2명 이상이어야 합니다." + + fun inputPlayerName(): String { + println(TEXT_INPUT_PROMPT) + val inputData = readln() + validatePlayerNameInputData(inputData) + return inputData + } + + override fun askForHit(playerName: String): String { + println(TEXT_HIT_PROMPT_FORMAT.format(playerName)) + return readln() + } + + private fun validatePlayerNameInputData(inputData: String) { + validateZeroPlayerCnt(inputData) + } + + private fun validateZeroPlayerCnt(inputData: String) { + val nameList = inputData.split(',') + .map { it.trim() } + .filter { it.isNotBlank() } + + require(nameList.isNotEmpty()) { ERR_MSG_MINIMUM_PLAYER_COUNT } + } +} diff --git a/src/main/kotlin/view/InputViewInterface.kt b/src/main/kotlin/view/InputViewInterface.kt new file mode 100644 index 0000000000..b3704a9875 --- /dev/null +++ b/src/main/kotlin/view/InputViewInterface.kt @@ -0,0 +1,5 @@ +package view + +interface InputViewInterface { + fun askForHit(playerName: String): String +} diff --git a/src/main/kotlin/view/OutputView.kt b/src/main/kotlin/view/OutputView.kt new file mode 100644 index 0000000000..b52a7b8159 --- /dev/null +++ b/src/main/kotlin/view/OutputView.kt @@ -0,0 +1,33 @@ +package view + +import player.Player +import player.PlayerGroup + +object OutputView : OutputViewInterface { + + fun showGameStart(playerGroup: PlayerGroup) { + val sb = StringBuilder() + playerGroup.playerList.forEach { + sb.append("${it.name}, ") + } + sb.deleteCharAt(sb.lastIndex - 1) + sb.append("에게 2장의 나누었습니다.") + println(sb.toString()) + playerGroup.playerList.forEach { showPlayingCard(it) } + println() + } + + override fun showPlayingCard(player: Player) { + println("${player.name}카드: ${player.playerDeck}") + } + + fun showGameEnd(playerGroup: PlayerGroup) { + println() + playerGroup.playerList.forEach { showPlayerCardWithPoint(it) } + } + + private fun showPlayerCardWithPoint(player: Player) { + val totalPoint = player.getResultPoint() + println("${player.name}카드: ${player.playerDeck} - 결과: $totalPoint") + } +} diff --git a/src/main/kotlin/view/OutputViewInterface.kt b/src/main/kotlin/view/OutputViewInterface.kt new file mode 100644 index 0000000000..d6d0713a82 --- /dev/null +++ b/src/main/kotlin/view/OutputViewInterface.kt @@ -0,0 +1,7 @@ +package view + +import player.Player + +interface OutputViewInterface { + fun showPlayingCard(player: Player) +} diff --git a/src/test/kotlin/DslTest.kt b/src/test/kotlin/DslTest.kt index 17b51ca1b1..5b0c198fa5 100644 --- a/src/test/kotlin/DslTest.kt +++ b/src/test/kotlin/DslTest.kt @@ -1,5 +1,3 @@ -import io.kotest.matchers.shouldBe -import org.intellij.lang.annotations.Language import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource @@ -31,44 +29,24 @@ class DslTest { val person = introduce { name(name) company("블루버드") - softSkill("A passion for problem solving") - softSkill("Good communication skills") - hardSkill("Kotlin") - language("Korean" to 5) - language("English" to 3) + skills( + introduceSkill { + softSkills("A passion for problem solving") + softSkills("Good communication skills") + hardSkills("Kotlin") + } + ) + language( + introduceLanguage { + language("Korean" to 5) + language("English" to 3) + } + ) } - person.name shouldBe name - person.company shouldBe "블루버드" - person.skills shouldBe Skills( - listOf("A passion for problem solving", "Good communication skills"), - listOf("Kotlin") - ) println(person) } - @Test - fun company() { - val person = introduce { - name("홍길동") - company("활빈당") - } - person.name shouldBe "홍길동" - person.company shouldBe "활빈당" - } - - @Test - fun skills() { - val skills = Skills( - softSkill = listOf("A passion for problem solving", "Good communication skills"), - hardSkill = listOf("Kotlin") - ) - val softExpect = listOf("A passion for problem solving", "Good communication skills") - val hardExpect = listOf("Kotlin") - skills.softSkill shouldBe softExpect - skills.hardSkill shouldBe hardExpect - } - @Test fun pair() { val pair1 = Pair(1, "one") @@ -79,6 +57,14 @@ class DslTest { private infix fun Int.of(s: String): Pair = Pair(this, s) } +fun introduceLanguage(block: LanguagesBuilder.() -> Unit): Languages { + return LanguagesBuilder().apply(block).build() +} + +fun introduceSkill(block: SkillsBuilder.() -> Unit): Skills { + return SkillsBuilder().apply(block).build() +} + fun introduce(block: PersonBuilder.() -> Unit): Person { return PersonBuilder().apply(block).build() } @@ -86,8 +72,8 @@ fun introduce(block: PersonBuilder.() -> Unit): Person { class PersonBuilder { private lateinit var name: String private var company: String? = null - private val skills = SkillsBuilder() - private val languages = LanguagesBuilder() + private lateinit var skills: Skills + private lateinit var languages: Languages fun name(value: String) { name = value } @@ -96,20 +82,16 @@ class PersonBuilder { company = value } - fun softSkill(value: String) { - skills.softSkills(value) - } - - fun hardSkill(value: String) { - skills.hardSkills(value) + fun skills(skill: Skills) { + skills = skill } - fun language(language: Pair) { - languages.addLanguages(language) + fun language(language: Languages) { + languages = language } fun build(): Person { - return Person(name, company, skills.build(), languages.build()) + return Person(name, company, skills, languages) } } diff --git a/src/test/kotlin/blackJack/card/PlayingCardTest.kt b/src/test/kotlin/blackJack/card/PlayingCardTest.kt new file mode 100644 index 0000000000..4b20449110 --- /dev/null +++ b/src/test/kotlin/blackJack/card/PlayingCardTest.kt @@ -0,0 +1,34 @@ +package blackJack.card + +import card.CardRank +import card.PlayingCard +import card.Suit +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class PlayingCardTest { + + @Test + fun `트럼프 카드를 생성하고, 해당 카드의 포인트를 요청할 때, 포인트를 반환한다`() { + // given : 트럼프 카드를 생성한다. + val playingCard = PlayingCard(Suit.SPADE, CardRank.TREE) + + // when : 해당 카드의 포인트를 요청한다. + val actual = playingCard.getPoint() + + // then : 카드별 포인트를 반환한다 + assertThat(actual).isEqualTo(3) + } + + @Test + fun `트럼프 카드를 만들고, 무늬 명칭을 요청할때, 무늬 명을 반환한다`() { + // given : 카드를 만들고 + val playingCard = PlayingCard(Suit.SPADE, CardRank.TREE) + + // when : 무늬 명칭을 요청하면 + val actual = playingCard.getSuitName() + + // then : 무늬 명을 반환한다. + assertThat(actual).isEqualTo("스페이드") + } +} diff --git a/src/test/kotlin/blackJack/card/deck/CardDeckTest.kt b/src/test/kotlin/blackJack/card/deck/CardDeckTest.kt new file mode 100644 index 0000000000..0b2fda7b99 --- /dev/null +++ b/src/test/kotlin/blackJack/card/deck/CardDeckTest.kt @@ -0,0 +1,30 @@ +package blackJack.card.deck + +import card.CardPack +import card.CardRank +import card.PlayingCard +import card.Suit +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class CardDeckTest { + + @Test + fun `GameDeck을 생성하고, 카드 반환 요청을 했을 떄, 카드 반환과 카드 인덱스가 증가한다`() { + // given : Cards를 생성한다. + + val gameDeck = FakeGameDeck(CardPack.cards.toMutableList()) + + // when : 카드 한장 반환을 요청한다. + val actual = gameDeck.getCardWithIncrease() + + // then : 첫번째 카드를 반환하고 카드 인덱스가 1 증가한다. + assertThat(actual).isEqualTo(PlayingCard(Suit.SPADE, CardRank.ACE)) + + // when : 카드 한장 반환을 요청한다. + val actual2 = gameDeck.getCardWithIncrease() + + // then : 두번째 카드를 반환하고 카드 인덱스가 1 증가한다. + assertThat(actual2).isEqualTo(PlayingCard(Suit.HEART, CardRank.ACE)) + } +} diff --git a/src/test/kotlin/blackJack/card/deck/FakeGameDeck.kt b/src/test/kotlin/blackJack/card/deck/FakeGameDeck.kt new file mode 100644 index 0000000000..aea5e56591 --- /dev/null +++ b/src/test/kotlin/blackJack/card/deck/FakeGameDeck.kt @@ -0,0 +1,21 @@ +package blackJack.card.deck + +import card.PlayingCard + +class FakeGameDeck(private val cardList: MutableList) { + + private var index = 0 + + fun getCardWithIncrease(): PlayingCard { + if (isMaxIndexOfCard()) resetCard() + return cardList[index++] + } + + private fun isMaxIndexOfCard(): Boolean { + return index == cardList.size + } + + private fun resetCard() { + index = 0 + } +} diff --git a/src/test/kotlin/blackJack/card/deck/HandsTest.kt b/src/test/kotlin/blackJack/card/deck/HandsTest.kt new file mode 100644 index 0000000000..430c548688 --- /dev/null +++ b/src/test/kotlin/blackJack/card/deck/HandsTest.kt @@ -0,0 +1,52 @@ +package blackJack.card.deck + +import card.CardPack +import card.CardRank +import card.PlayingCard +import card.Suit +import card.deck.Hands +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class HandsTest { + + @Test + fun `플레이어 카드 추가`() { + val hands = Hands.create() + + hands.addCard(CardPack.cards[0]) + + val actual = hands.cardList[0] + + assertThat(actual).isEqualTo(PlayingCard(suit = Suit.SPADE, cardRank = CardRank.ACE)) + } + + @Test + fun `카드 점수 반환(ACE == 1)`() { + // given : 플레이어 덱을 생성하고 카드를 추가한다. + val hands = Hands() + hands.addCard(PlayingCard(Suit.DIAMOND, CardRank.ACE)) + hands.addCard(PlayingCard(Suit.DIAMOND, CardRank.TEN)) + hands.addCard(PlayingCard(Suit.DIAMOND, CardRank.TEN)) + + // when : 점수 계산 요청 + val actual: Int = hands.getResultPoint() + + // then : + assertThat(actual).isEqualTo(21) + } + + @Test + fun `카드 점수 반환(ACE == 11)`() { + // given : 플레이어 덱을 생성하고 카드를 추가한다. + val hands = Hands() + hands.addCard(PlayingCard(Suit.DIAMOND, CardRank.ACE)) + hands.addCard(PlayingCard(Suit.DIAMOND, CardRank.TEN)) + + // when : 점수 계산 요청 + val actual: Int = hands.getResultPoint() + + // then : + assertThat(actual).isEqualTo(21) + } +} diff --git a/src/test/kotlin/blackJack/player/PlayerFactoryTest.kt b/src/test/kotlin/blackJack/player/PlayerFactoryTest.kt new file mode 100644 index 0000000000..bde64ebb6f --- /dev/null +++ b/src/test/kotlin/blackJack/player/PlayerFactoryTest.kt @@ -0,0 +1,34 @@ +package blackJack.player + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import player.PlayerFactory + +class PlayerFactoryTest { + + @Test + fun `문자열을 받고, 플레이어 생성을 요청할 때, 플레이어 리스트를 반환한다`() { + // given : ,로 구분된 문자열을 받는다. + val playerNames = "oyj, kth, og" + + // when : 플레이어 생성을 요청한다. + val actual = PlayerFactory.createPlayerList(playerNames).size + + // then : List를 생성한다. + val expect = 3 + assertThat(actual).isEqualTo(expect) + } + + @Test + fun `공백이 포한된 문자열을 받고, 플레이어 생성을 요청할 때, 공백은 제외된 플레이어만 생성된다`() { + // given : ,로 구분된 문자열을 받는다. + val playerNames = "oyj, kth, " + + // when : 플레이어 생성을 요청한다. + val actual = PlayerFactory.createPlayerList(playerNames).size + + // then : List를 생성한다. + val expect = 2 + assertThat(actual).isEqualTo(expect) + } +} diff --git a/src/test/kotlin/blackJack/player/PlayerGroupTest.kt b/src/test/kotlin/blackJack/player/PlayerGroupTest.kt new file mode 100644 index 0000000000..1891fb0337 --- /dev/null +++ b/src/test/kotlin/blackJack/player/PlayerGroupTest.kt @@ -0,0 +1,24 @@ +package blackJack.player + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import player.Player +import player.PlayerGroup + +class PlayerGroupTest { + @Test + fun `플레이어 그룹을 만든다, 플레이어가 2명 이상 26명 이하의 범위를 넘어설 때, 예외를 던진다`() { + // given : 카드팩과 범위를 넘어서는 플레이어 리스트가 생성된다. + + val singlePlayerList = listOf(Player("OYJ")) + val overPlayerList = List(27) { i -> Player("OYJ_$i") } + + // when : 게임에 플레이어를 주입한다 + val actualSingle = runCatching { PlayerGroup(singlePlayerList) }.exceptionOrNull() + val actualOver = runCatching { PlayerGroup(overPlayerList) }.exceptionOrNull() + + // then : 예외를 던진다. + assertThat(actualSingle).isInstanceOf(IllegalArgumentException::class.java) + assertThat(actualOver).isInstanceOf(IllegalArgumentException::class.java) + } +} diff --git a/src/test/kotlin/blackJack/player/PlayerTest.kt b/src/test/kotlin/blackJack/player/PlayerTest.kt new file mode 100644 index 0000000000..f37531119b --- /dev/null +++ b/src/test/kotlin/blackJack/player/PlayerTest.kt @@ -0,0 +1,113 @@ +package blackJack.player + +import card.CardPack +import card.CardRank +import card.PlayingCard +import card.Suit +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import player.Player +import player.Status + +class PlayerTest { + + @Test + fun `플레이어와 카드팩이 생성되고, 플레이어가 카드를 받을 때, 받은 카드는 저장한다`() { + // given : 플레이어와 카드팩을 생성한다. + val player = Player("OYJ") + val card = CardPack.cards[0] + + // when : 플레이어가 카드를 받는다. + player.saveCard(card) + val actual = player.playerDeck.cardList[0] + + // then : + assertThat(actual).isEqualTo(card) + } + + @Test + fun `,플에이어가 생성될 때 ,초기 상태는 플레이중 이다 `() { + // given : + + // when : 플레이어가 생성될 때 + val player = Player("OYJ") + val actual = player.status + + // then : 초기 상태는 PLAYING이다. + val expect = Status.START + assertThat(actual).isEqualTo(expect) + } + + @Test + fun `플에이어가 생성되고, 카드 받기가 종료될 때, 플에이어 상태는 STAND가 된다`() { + // given : 플레이어가 생성된다. + val player = Player("OYJ") + + // when : 플레이어가 카드 받기를 멈춘다. + player.playDone() + val actual = player.status + + // then : 플레이어의 상태는 STAND이다. + val expect = Status.STAND + assertThat(actual).isEqualTo(expect) + } + + @Test + fun `플레이어가 카드를 받고, 플레이어 보유 카드 합이 20이하 일 때, 플에이어 상테는 PLAYING로 업데이트 된다`() { + // given : 플레이어 보유 카드 합 20 이하(8) + val player = Player("OYJ") + player.saveCard(PlayingCard(Suit.DIAMOND, CardRank.TREE)) + player.saveCard(PlayingCard(Suit.DIAMOND, CardRank.FIVE)) + + // when : 플레이어 상태 업데이트 + player.updateStatus() + val actual = player.status + + // then : 플레이어 상태는 PLAYING이다. + assertThat(actual).isEqualTo(Status.PLAYING) + } + + @Test + fun `플레이어가 카드를 받고, 플레이어 보유 카드 합이 21이상 일 때, 플에이어 상테는 BUST로 업데이트 된다`() { + // given : 플레이어 보유 카드 합 21 이상(28) + val player = Player("OYJ") + player.saveCard(PlayingCard(Suit.DIAMOND, CardRank.EIGHT)) + player.saveCard(PlayingCard(Suit.DIAMOND, CardRank.KING)) + player.saveCard(PlayingCard(Suit.DIAMOND, CardRank.JACK)) + + // when : 플레이어 상태 업데이트 + player.updateStatus() + val actual = player.status + + // then : 플레이어 상태는 PLAYING이다. + assertThat(actual).isEqualTo(Status.BUST) + } + + @Test + fun `플레이어가 초기 2장의 카드를 받고, 플레이어 보유 가드 합이 21 일 때, 플에이어 상테는 BLACK_JACK으로 업데이트 된다`() { + // given : 플레이어 보유 카드 2장 && 합 21 + val player = Player("OYJ") + player.saveCard(PlayingCard(Suit.DIAMOND, CardRank.ACE)) + player.saveCard(PlayingCard(Suit.DIAMOND, CardRank.KING)) + + // when : 플레이어 상태 업데이트 + player.updateStatus() + val actual = player.status + + // then : 플레이어 상태는 PLAYING이다. + assertThat(actual).isEqualTo(Status.BLACK_JACK) + } + + @Test + fun `, 플레이어가 플레이를 멈춘다고 할때, 플에이어의 상태는 STAND로 업데이트 된다`() { + // given : 플레이어 보유 카드 합 21 이상(28) + val player = Player("OYJ") + + // when : 플레이어 상태 업데이트 + player.playDone() + val actual = player.status + + // then : 플레이어 상태는 PLAYING이다. + assertThat(actual).isEqualTo(Status.STAND) + } +} diff --git a/src/test/kotlin/study/LanguagesBuilder.kt b/src/test/kotlin/study/LanguagesBuilder.kt index 294acc16fc..e22ee9a186 100644 --- a/src/test/kotlin/study/LanguagesBuilder.kt +++ b/src/test/kotlin/study/LanguagesBuilder.kt @@ -5,7 +5,7 @@ class LanguagesBuilder { val languages: List> get() = _languages - fun addLanguages(language: Pair) { + fun language(language: Pair) { _languages.add(language) } diff --git a/src/test/kotlin/study/Skills.kt b/src/test/kotlin/study/Skills.kt index 22186485aa..f862da29ae 100644 --- a/src/test/kotlin/study/Skills.kt +++ b/src/test/kotlin/study/Skills.kt @@ -1,3 +1,3 @@ package study -data class Skills (val softSkill: List, val hardSkill: List) +data class Skills(val softSkill: List, val hardSkill: List)