-
Notifications
You must be signed in to change notification settings - Fork 313
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Refactor: Skills, Language의 builder를 사용하여 가독성 향상 * Docs: 기능 목록 초기 구성 * Refactor: Skills, language 구현 변경 * Feat: 카드 번호, 카드 무늬 유효성 검증 구현 Docs: 딜러 역할 삭제, 딜러 역할 분배 * Feat: 카드 포인트 반환 기능 구현, 카드 무늬 명 반환 기능 구현 Refactor: Suit, CardNumber / class -> Enum으로 변경, 카드 무늬, 카드 번호 검증 로직 제거 * Feat: 카드 클래스 생성 및 카드 구성 데이터 캐싱 chore: 카드 관련 클래스 패키지 생성 및 이동 * Feat: 카드팩 클래스 생성 및 캐싱 구현 * Docs: 블랙젝 게임 기능 추가 (카드팩 기능 이동), 플레이어 기능 목록 변경 Refactor: 카드 번호 '10' 추가, 관련 테스트 수정 * Docs: 블랙젝 게임 기능 목록 추가. Refactor: 카드팩 object -> class 변경, 사이드 이팩트 수정 * Feat: 블랙젝 게임 클래스 생성, 카드팩 검증 로직 구현(52장, 중복X) * Docs: 카드인텍스 기능 목록 추가. Feat: 카드 인덱스 증가, 현재 인덱스 반환 기능 구현 * Docs: 카드 인텍스 검증 기능 목록 추가 Feat: 카드 인덱스 검증 로직 구현 * Feat: 카드 배포 구현 * Feat: 플레이어 상태 추가, 플레이어 상태 추가 및 변경 구현 * Feat: 플레이어 카드 저장 기능 구현 * Refactor: CardNumber 명칭 추가, class PlayingCard @OverRide toString(), 카드 셔플 기능 역할 변경 (블렉젝 게임 -> 카드팩 ) * Refactor: 블랙젝 게임 디폴트 파라미터 추가 - playerList: List<Player> Feat: 추가된 사용자 검증 로직 추가. * Docs: 블랙젝 게임 기능 추가, 블랙젝 계산기 추가. * Feat: 카드 초기 세팅 기능 추가(게임 시작시 카드 2장 받음) Refactor: 사용자 이름 추가. style: CardNumber 컨벤션 수정 * Refactor: CardIndex -> GameIndex로 역할 범위 확장. Index 값 생성자 파라미터 -> Backing Property 변경 * Refactor: 참여 가능 플레이어 조건 변경, 카드팩 검증 로직 역할 변경(BlackjackGame -> CardPack), 첫 카드 배포 인덱스 변경(플레이어 별 2장은 기본) * Chore: 프로덕트 클래스, 테스트 클래스 패키지 구조 동기화 * Feat: 블랙젝 계산기 추가(승리에 유리한 계산 결과 반환) Refactor: PlayingCard의 Ace계산 로직 제거 * Refactor: CardNumber파라미터 접근 제한 변경 private -> publid * Docs: 기능 목록 체크 * Refactor: GameIndex의 index 변수 명 변경 -> cardIndex * Feat: 게임 인덱스, 플레이어 인덱스 추가 및 증가 기능 구현 Refactor: 게임 인덱스의 파라미터 maxIndex ->. maxCardIndex로 변경 * Feat: 플레이어 카드 저장 함수 추가 Docs: 블랙젝 게임 기능 목록 추가. * Feat: 플레이어 상태 업데이트 기능 구현 * Feat: PlayerGroup 일급 컬렉션 추가, 블랙젝 게임 역할 이동(-> PlayerGroup) - 참여 인원 검증 - 플레이어 턴 넘김 * Refactor: 카드 반환 기능 CardPack으로 이동 * Refactor: 카드 배포 역할 변경(BlackjackGame -> CardPack), 게임 진행 로직 변경 * Refactor: 불필요한 코드 제거 및 수정 * Refactor - 클래스명 변경 CardNumber -> CardRank - CardRank프로퍼티명 일부 변경 cardName -> symbol * Feat: CardDeck생성(카드 캐싱) * Refactor: - GameIndex, CardPack 제거, - 사이드 이팩트 수정 Feat: - Class Cards 추가 (PlayingCard기반 일급 컬랙션) - CardPack의 카드 저장 역할 마이그래이션 CardPack -> Cards * Refactor: - 카드덱 인터페이스화(게임 덱, 플레이어 덱) - 테스트 코드 사이드 이팩트 수정 * Feat: 플레이어 카드 점수 계산 기능 추가. * Refactor: - 플레이어 상태 추가 - 플레이어 상태 업데이트 조건문 추가. - BlackJackCalculator삭제 * chore - PlayingCard class -> data class - class Player 불필요한 함수 제거 - class Player 변수 변경 cardList -> playerDeck * Refactor: 플레이어 상태 업데이트 로직 변경 * chore: GameDeckTest 주석 추가. * Feat: Input, Output View 구현 * Feat: InputData to PlayerList 를 위한 플래이어 팩토리 구현 chore: 플레이어 수 검증 예외 메시지 수정, InputView 매직 스트링 제거 및 함수 네이밍 변경 * Docs: README.md 변경 Refactor: BlackjackGameTest 삭제 * Refactor: 게임 덱 셔플 기능 추가, 생성자 public으로 변경, 플레이어 덱 override toString() * Refactor: 블랙잭 게임 로직 재구현 * Refactor: 플레이어 관련 불필요한 함수 제거 및 매직 넘버 제거 * Feat: 메인 함수 구현 * style: 코드 컨벤션 정리 (feat: Gradle) * Refactor: CardDeck interface 삭제, 플레이어 생성자 추가, 카드팩 재사용, 게임덱 리셋 기능 구현 등 리뷰 구현 * Chore: 클래스 명 변경 GameDeck -> CardDeck * Refactor: ViewInterfacd생성 및 BlackjackGame 생성자 추가. * BlackjackGame 생성자로 ViewInterFace 추가.
- Loading branch information
Showing
27 changed files
with
787 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,54 @@ | ||
# kotlin-blackjack | ||
# 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] 플레이어 카드 계산값을 출력 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package card | ||
|
||
object CardPack { | ||
val cards: List<PlayingCard> = CardRank.values().flatMap { rank -> | ||
Suit.values().map { suit -> | ||
PlayingCard.of(suit = suit, cardRank = rank) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<String, PlayingCard> = 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) | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package card | ||
enum class Suit(val koName: String) { | ||
SPADE("스페이드"), | ||
HEART("하트"), | ||
DIAMOND("다이아몬드"), | ||
CLUB("클러버"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package card.deck | ||
|
||
import card.PlayingCard | ||
|
||
class CardDeck(private val cardList: MutableList<PlayingCard>) { | ||
|
||
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package card.deck | ||
|
||
import card.CardRank | ||
import card.PlayingCard | ||
|
||
class Hands(val cardList: MutableList<PlayingCard> = 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() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package player | ||
|
||
object PlayerFactory { | ||
|
||
private const val DELIMITERS = ',' | ||
|
||
fun createPlayerList(playerNames: String): List<Player> { | ||
val playerNameList = splitInputData(playerNames) | ||
val playerList = mutableListOf<Player>() | ||
playerNameList.forEach { name -> | ||
playerList.add(Player(name)) | ||
} | ||
return playerList | ||
} | ||
|
||
private fun splitInputData(inputData: String): List<String> { | ||
return inputData.split(DELIMITERS).map { it.trim() }.filter { it.isNotBlank() } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package player | ||
|
||
class PlayerGroup(val playerList: List<Player>) { | ||
|
||
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package player | ||
|
||
enum class Status { | ||
START, PLAYING, STAND, BLACK_JACK, BUST | ||
} |
Oops, something went wrong.