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

[블랙잭 1단계] 뭉치 미션 제출합니다. #122

Open
wants to merge 66 commits into
base: m6z1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
d9f4ea4
docs: 기능 목록 작성
wondroid-world Mar 4, 2025
c9e3b8a
docs: 기능 요구 사항 추가
m6z1 Mar 4, 2025
2df5152
feat: 카드 모양 객체 구현
m6z1 Mar 4, 2025
7ceb108
feat: 카드 객체 구현
m6z1 Mar 4, 2025
d3d99c2
docs: 기능 요구 사항 추가
m6z1 Mar 4, 2025
5d8b925
feat: 카드 끗수를 숫자로 변환하는 로직 구현
m6z1 Mar 4, 2025
6d8e4d1
feat: 플레이어 행동 객체 구현
m6z1 Mar 4, 2025
e902fdf
Merge branch 'step1' of https://github.com/wondroid-world/kotlin-blac…
wondroid-world Mar 5, 2025
7fe01d4
docs: 플레이어 상태 기능 목록 수정
wondroid-world Mar 5, 2025
61ed0d5
feat: 플레이어 객체 구현
wondroid-world Mar 5, 2025
9c24b94
feat: 플레이어 카드 추가 기능 구현
wondroid-world Mar 5, 2025
90a4195
docs: 플레이어 기능 구현 추가
wondroid-world Mar 5, 2025
5b79da7
feat: 플레이어 이름만 가질 경우, 비어있는 카드 리스트를 가짐
wondroid-world Mar 5, 2025
47dc2ec
feat: 게임 결과 상태 객체 구현
m6z1 Mar 5, 2025
3557ebc
docs: 게임 결과 기능 목록 수정
m6z1 Mar 5, 2025
48d8577
feat: 기준 점수와 비교 점수를 비교하여 기준 점수 기준으로 게임 결과 반환 로직 구현
m6z1 Mar 5, 2025
48e2386
feat: 딜러 객체 구현
m6z1 Mar 5, 2025
ea6300e
docs: 딜러 이름 기능 목록 추가
m6z1 Mar 5, 2025
e55f6e2
feat: 딜러 이름의 초기값은 '딜러'
m6z1 Mar 5, 2025
a4a29b4
docs: 딜러 카드 리스트 기능 목록 추가
m6z1 Mar 5, 2025
a0ccfcc
feat: 딜러 카드 리스트 초기값 구현
m6z1 Mar 5, 2025
8147bce
feat: 딜러 카드 추가 기능 구현
m6z1 Mar 5, 2025
bbce347
docs: 카드들 상태 기능 목록 수정
wondroid-world Mar 5, 2025
fd9af80
feat: 게임 진행 중 카드들의 상태 구현
wondroid-world Mar 5, 2025
827acb6
docs: 끗수 기능 목록 추가
wondroid-world Mar 5, 2025
16be104
feat: 끗수 객체 추가
wondroid-world Mar 5, 2025
e44a767
docs: 카드 덱 기능 목록 추가
wondroid-world Mar 6, 2025
9100d77
feat: 카드 생성기 객체 구현
wondroid-world Mar 6, 2025
fa8bedd
docs: 카드 덱 기능 목록 수정
wondroid-world Mar 6, 2025
1a91b51
feat: 카드 덱 객체 구현
wondroid-world Mar 6, 2025
7088c4e
docs: 카드들 기능 목록 수정
wondroid-world Mar 6, 2025
8a55fdc
feat: 카드들 객체 구현
wondroid-world Mar 6, 2025
520e9b9
refactor: 카드 생성자 끗수를 끗수 타입으로 변경
wondroid-world Mar 6, 2025
1325edb
feat: 끗수 숫자 추가
wondroid-world Mar 6, 2025
a90b02a
refactor: ktlint 수정
wondroid-world Mar 6, 2025
43bca8f
refactor: 카드의 함수 네이밍 변경
wondroid-world Mar 6, 2025
aeebccf
docs: 플레이어 이름 검증 로직 추가
wondroid-world Mar 6, 2025
6ff398c
refactor: ktlint 수정
wondroid-world Mar 6, 2025
929540a
feat: 딜러 이름을 가지도록 수정
wondroid-world Mar 6, 2025
9b3d0e5
docs: 카드들 합 기능 목록 수정
wondroid-world Mar 6, 2025
fa7d4c4
feat: 카드 점수 계산 로직 구현
m6z1 Mar 6, 2025
d2bb180
refactor: 플레이어와 딜러의 카드들 타입 변경 및 카드 추가 로직 수정
m6z1 Mar 6, 2025
eb8b486
docs: 플레이어 행동 기능 목록 수정
m6z1 Mar 6, 2025
d395b56
feat: 딜러 점수에 따른 플레이어 행동 반환 로직 구현
m6z1 Mar 6, 2025
bb1979e
feat: 카드 상태 반환 로직 구현
m6z1 Mar 6, 2025
701086a
feat: 플레이어 카드 입출력 로직 구현
m6z1 Mar 6, 2025
18f3c58
feat: dsl 실습 구현
m6z1 Mar 6, 2025
5aa7ecd
docs: DSL 노트 정리 수정
m6z1 Mar 6, 2025
feda5e3
feat: 입출력 구현
m6z1 Mar 7, 2025
5390252
feat: 카드들의 블랙잭, 버스트 반환 로직 추가
m6z1 Mar 7, 2025
14d5ccb
feat: 딜러 결과 반환 및 업데이트 로직 추가
m6z1 Mar 7, 2025
dd2cd76
feat: 게임 결과의 반댓값 반환 로직 구현
m6z1 Mar 7, 2025
60ff224
feat: 플레이어의 블랙잭 여부 및 결과 업데이트 로직 추가
m6z1 Mar 7, 2025
b4a8fbc
feat: 플레이어 이름 검증 로직 추가
m6z1 Mar 7, 2025
2cce28d
feat: 블랙잭 로직 구현
m6z1 Mar 7, 2025
a60f022
feat: 딜러의 점수 분기 로직 추가
wondroid-world Mar 7, 2025
3ff8699
feat: 플레이어 이름 검증 로직 추가
wondroid-world Mar 7, 2025
196df71
refactor: 컨틀롤러 내부의 함수 순서 변경
m6z1 Mar 7, 2025
05980db
refactor: 사용하지 않는 생성자 제거
m6z1 Mar 7, 2025
9024c63
refactor: 카드들의 파라미터 네이밍 변경
m6z1 Mar 7, 2025
09916bb
test: 카드들 테스트 추가
m6z1 Mar 7, 2025
69e1097
refactor: 플레이어에서 bust, blackjack 판단하도록 수정
m6z1 Mar 7, 2025
e1779e9
test: 카드 테스트 추가
m6z1 Mar 7, 2025
87b91af
test: 딜러 테스트 추가
m6z1 Mar 7, 2025
08cba24
test: 게임 결과 테스트 추가
m6z1 Mar 7, 2025
70c7164
test: 플레이어 테스트 추가
m6z1 Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,63 @@
# kotlin-blackjack
# kotlin-blackjack

- 카드들 상태
- [X] 게임 진행 중 카드들의 상태는 Blackjack, Bust, None이 있다.
- [X] 카드들의 점수와 처음 턴 여부를 받아서 카드들의 상태를 반환한다.
- Blackjack는 처음 받은 카드의 합이 21이다.
- Bust는 카드의 합이 21초과이다.
- None은 카드의 합이 21미만이다.
- 게임 결과
- [X] 게임 결과는 Push, Win, Lose가 있고, 한국어 게임 결과를 가진다.
- [X] Push는 기준 점수가 비교 점수와 같다.
- [X] Win는 기준 점수가 비교 점수보다 높다.
- [X] Lose는 기준 점수가 비교 점수보다 낮다.
- 플레이어
- [X] 플레이어는 이름과 카드 리스트를 가진다.
- [X] 플레이어는 카드를 추가로 받을 수 있다.
- [X] 플레이어가 이름만 가질 경우, 가진 카드 리스트는 비어있다.
- 플레이어 이름 검증 로직
- [X] 플레이어의 이름은 1자 이상 5자 이내이다.
- [X] 플레이어 이름은 공백일 수 없다.
- [X] 플레이어 이름은 딜러가 될 수 없다.
- [X] 플레이어 이름은 중복될 수 없다.
- 카드 모양
- [X] 카드 모양은 클로버, 하트, 다이아몬드, 스페이드를 가진다.
- [X] 각 모양에 대해 한글 이름을 가진다.
- 카드
- [X] Card는 모양과 denomination(끗수)를 가진다.
- [X] Card의 끗수가 J, Q, K이면 숫자는 10이다.
- [X] Card의 끗수가 2~10 사이의 숫자이면 숫자 그대로 가진다.
- [X] 위의 끗수가 아닐 경우엔 숫자는 0이다.
- 카드들
- [X] 카드들은 생성자로 카드 리스트를 가진다.
- [X] Ace 카드가 0개 일때, 카드들의 총합을 반환한다.
- [X] Ace 카드가 1개이고 카드의 합이 11미만 일때, Ace카드를 11로 판단한다.
- [X] Ace 카드가 1개이고 카드의 합이 11이상 일때, Ace카드를 1로 판단한다.
- [X] Ace 카드가 2개이고 카드의 합이 10미만 일때, Ace카드의 합을 12로 판단한다.
- Ace의 카드 2장 중 1장은 11, 1장은 1로 판단한다.
- [X] Ace 카드가 2개이고 카드의 합이 10이상 일때, Ace카드의 합을 2로 판단한다.
- Ace의 카드 2장 다 1로 판단한다.
- [X] Ace 카드가 3개이고 카드의 합이 9미만 일때, Ace카드의 합을 13로 판단한다.
- Ace의 카드 3장 중 1장은 11, 2장은 1로 판단한다.
- [X] Ace 카드가 3개이고 카드의 합이 9이상 일때, Ace카드의 합을 3으로 판단한다.
- Ace의 카드 3장 다 1로 판단한다.
- [X] Ace 카드가 4개이고 카드의 합이 8미만 일때, Ace카드의 합을 14로 판단한다.
- Ace의 카드 4장 중 1장은 11, 3장은 1로 판단한다.
- [X] Ace 카드가 4개이고 카드의 합이 8이상 일때, Ace카드를 합을 4으로 판단한다.
- Ace의 카드 4장 다 1로 판단한다.
- [X] 카드들에 카드를 추가할 수 있다.
- 플레이어 행동
- [X] 응답은 Y, N로만 받을 수 있다.
- [X] 플레이 행동은 hit과 stay로 나뉜다.
- [X] 딜러 카드의 합이 16 이하일 경우 hit을 반환하고, 아닐 경우엔 stay를 반환한다.
- 딜러
- [X] 딜러는 이름과 카드들을 가진다.
- [X] 딜러는 이름이 없을 경우, 딜러라는 이름을 가진다.
- [X] 딜러의 초기 카드 리스트는 비어있다.
- [X] 딜러는 카드를 추가로 받을 수 있다.
- 카드 생성기
- [X] 무작위로 섞은 카드 번들을 생성한다.
- 카드 덱
- [X] 카드 덱은 첫번째 순서의 카드부터 차례대로 카드를 반환한다.
- 끗수
- [X] 끗수는 제목과 숫자를 가진다.
Empty file removed src/main/kotlin/.gitkeep
Empty file.
10 changes: 10 additions & 0 deletions src/main/kotlin/blackjack/Application.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package blackjack

import blackjack.controller.BlackjackController
import blackjack.view.InputView
import blackjack.view.OutputView

fun main() {
val blackjackController = BlackjackController(InputView(), OutputView())
blackjackController.run()
}
170 changes: 170 additions & 0 deletions src/main/kotlin/blackjack/controller/BlackjackController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package blackjack.controller

import blackjack.model.CardDeck
import blackjack.model.CardsMaker
import blackjack.model.CardsStatus
import blackjack.model.Dealer
import blackjack.model.GameResult
import blackjack.model.Player
import blackjack.model.PlayerBehavior
import blackjack.model.Players
import blackjack.view.InputView
import blackjack.view.OutputView

class BlackjackController(

Choose a reason for hiding this comment

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

딜러가 ~하면, 플레이어가 ~한 경우의 분기가 굉장히 많다보니

혹시 이런 경우의 테스트가 어떤 것이 있을까요?
보통은 단위 테스트면 충분하다고 생각하고, 전제조건을 초기값으로 제공해서 테스트하는 함수들이 지금도 보여서요!

Choose a reason for hiding this comment

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

별개로 지금도 컨트롤러가 하고있는게 많아보이긴하는데... 우선 다른 것에 집중해서 수정해보고, 그 이후에 컨트롤러를 살펴보죠!

private val inputView: InputView,
private val outputView: OutputView,
) {
private val cardDeck = CardDeck(CardsMaker.CARDS)

fun run() {
outputView.printStartMessage()
val players: Players = inputView.readPlayers()
val dealer = Dealer()

getCards(players, dealer)
playGames(players, dealer)
}

private fun getCards(
players: Players,
dealer: Dealer,
) {
repeat(2) {
getCardsToPlayer(players.value)
getCardsToDealer(dealer)
}
outputView.printPlayersCards(dealer, players.value)
}

private fun getCardsToPlayer(players: List<Player>) {
players.forEach { player ->
val card = cardDeck.pickCard()
player.appendCard(card)
}
}

private fun getCardsToDealer(dealer: Dealer) {
val card = cardDeck.pickCard()
dealer.appendCard(card)
}

private fun playGames(
players: Players,
dealer: Dealer,
) {
if (dealer.isBlackjack(true)) {
val blackjackPlayers: List<Player> = players.findBlackjackPlayer()
updateGameResult(players, dealer)
outputView.printDealerBlackjackMessage(dealer, blackjackPlayers)
displayResult(players, dealer)
return
}
players.value.forEach { player ->
playGame(player, dealer)
}
displayResult(players, dealer)
}

private fun updateGameResult(
players: Players,
dealer: Dealer,
) {
players.value.forEach { player ->
if (isAllPlayerBlackjack(player, dealer)) return@forEach
player.updateResult(GameResult.WIN)
dealer.updateResult(player.cards.calculateScore())
}
}

private fun isAllPlayerBlackjack(
player: Player,
dealer: Dealer,
): Boolean {
if (player.isBlackjack(true)) {
player.updateResult(GameResult.PUSH)
dealer.updateResult(player.cards.calculateScore())
return true
}
return false
}

private fun playGame(
player: Player,
dealer: Dealer,
) {
if (isPlayerBlackjack(player, dealer)) return
executePlayerGameLogic(player)
executeDealerGameLogic(dealer)
val dealerResult: GameResult = dealer.updateResult(dealer.cards.calculateScore())
player.updateResult(dealerResult)
}

private fun isPlayerBlackjack(
player: Player,
dealer: Dealer,
): Boolean {
if (player.isBlackjack(true)) {
val dealerResult: GameResult = dealer.updateResult(CardsStatus.BLACKJACK_SCORE)
player.updateResult(dealerResult)
return true
}
return false
}

private fun executePlayerGameLogic(player: Player) {
while (!player.isBust()) {
outputView.printPlayerBehaviorGuide(player)
val playerBehavior: PlayerBehavior = inputView.readPlayerBehavior()

if (executePlayerBehavior(playerBehavior, player)) break
}
}

private fun executePlayerBehavior(
playerBehavior: PlayerBehavior,
player: Player,
): Boolean {
when (playerBehavior) {
PlayerBehavior.HIT -> {
player.appendCard(cardDeck.pickCard())
outputView.printPlayerCard(player)
if (isPlayerBust(player)) return true
}

PlayerBehavior.STAY -> return true
}
return false
}

private fun isPlayerBust(player: Player): Boolean {
if (player.isBust()) {
outputView.printBust(player)
return true
}
return false
}

private fun executeDealerGameLogic(dealer: Dealer) {
while (dealer.isHit()) {
dealer.appendCard(cardDeck.pickCard())
outputView.printDealerGettingCard()
if (isDealerBust(dealer)) break
}
}

private fun isDealerBust(dealer: Dealer): Boolean {
if (dealer.isBust()) {
outputView.printBust(dealer)
return true
}
return false
}

private fun displayResult(
players: Players,
dealer: Dealer,
) {
outputView.printResult(dealer, players)
}
}
10 changes: 10 additions & 0 deletions src/main/kotlin/blackjack/model/Card.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package blackjack.model

data class Card(
val shape: CardShape,
val denomination: Denomination,
) {
fun isDenominationAce(card: Card): Boolean = card.denomination == Denomination.ACE

fun combine(): String = denomination.title + shape.koreanName
Copy link
Author

Choose a reason for hiding this comment

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

combine이란 함수가 String 을 반환하는데, 다시 생각해보니 view에서 결합하여 출력을 하는 게 더 적절할 것이라는 생각이 들었습니다.

A다이아몬드에서 다이아몬드A로 출력 요구 사항이 변경될 경우, model이 아닌 view에서 변경을 해야 하지 않나?
하지만, 반대로 생각하면 card에서 메시지를 받아 반환 할 수 있는 것은 아닐까? 라는 생각이 들었습니다...

고민의 끝에 현재 코드는 card 에서 직접 제작해서 제출하고 있습니다.

Choose a reason for hiding this comment

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

combine 함수의 로직은 뷰의 관심사가 맞습니다.
그럼 이렇게 생각해보면 어떨까요?

  1. 이 도메인 로직을 여러 플랫폼에서 사용한다
  2. combine() 함수로 뷰에 출력할 String값을 받는다.
  3. A플랫폼에서는 A다이아몬드, B플랫폼에서는 B다이아몬드 라고 출력해야한다.

뷰의 요구사항이 바뀌면 뷰를 수정하는 것이 자연스럽지 않을까요?

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

class CardDeck(
private val cards: List<Card>,
) {
private var index = 0

fun pickCard(): Card = cards[index++]
}
10 changes: 10 additions & 0 deletions src/main/kotlin/blackjack/model/CardShape.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package blackjack.model

enum class CardShape(
val koreanName: String,
) {
DIAMOND("다이아몬드"),
SPADE("스페이드"),
HEART("하트"),
CLOVER("클로버"),
}
33 changes: 33 additions & 0 deletions src/main/kotlin/blackjack/model/Cards.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package blackjack.model

class Cards(
value: List<Card>,
Copy link
Author

Choose a reason for hiding this comment

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

기존의 cards의 생성자 네이밍은 cards 였습니다.
일급컬렉션을 만들어 외부에서 사용하게 되면 , List<Card>에 접근하기 위해서는
cards.cards.~~로 사용을 했어야 했는데, 미관상으로도 그렇고 카드들의 카드들이라는 이름이 어색하다고 느꼈습니다.
이에 value라는 네이밍을 사용했는데, 조금 추상적인가? 라는 생각도 들었습니다.
둘리는 어떻게 생각하시는지 궁금합니다..!!

Choose a reason for hiding this comment

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

취향차이라고 생각하는데, 저는 cards.cards를 더 많이 사용하긴 합니다. 정답은 없어요!

) {
private val _value: MutableList<Card> = value.toMutableList()
val value: List<Card> get() = _value.map { card -> card.copy() }

Choose a reason for hiding this comment

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

방어적 복사 👍


var size: Int = this.value.size

Choose a reason for hiding this comment

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

이 변수는 테스트를 위함일까요? 🤔

private set

fun add(card: Card) {
_value.add(card)
}

fun isBlackjack(firstTurn: Boolean): Boolean = CardsStatus.from(calculateScore(), firstTurn) == CardsStatus.BLACKJACK

fun isBust(): Boolean = CardsStatus.from(calculateScore()) == CardsStatus.BUST
Comment on lines +16 to +18

Choose a reason for hiding this comment

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

상태를 활용하겠다면, Cards가 상태를 가지고, 카드가 추가될 때마다 상태를 업데이트하는 것이 자연스럽지 않을까요?


fun calculateScore(): Int {
val aceCount: Int = value.count { card -> card.isDenominationAce(card) }
val score: Int = value.sumOf { card -> card.denomination.number }
return when (aceCount) {
1 -> if (score < 11) score + 11 else score + 1
2 -> if (score < 10) score + 11 + 1 else score + aceCount * 1
3 -> if (score < 9) score + 11 + 1 + 1 else score + aceCount * 1
4 -> if (score < 8) score + 11 + 1 + 1 + 1 else score + aceCount * 1
else -> score
}
}
Comment on lines +20 to +30

Choose a reason for hiding this comment

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

이 계산 로직이 이렇게 길지 않아도 해결할 수 있을 것 같아요. 🤔


fun getCardsInfomation(): List<String> = value.map { it.combine() }
}
14 changes: 14 additions & 0 deletions src/main/kotlin/blackjack/model/CardsMaker.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package blackjack.model

object CardsMaker {
private val DENOMINATIONS: List<Denomination> = Denomination.entries
private val SHAPES: List<CardShape> = CardShape.entries

val CARDS: List<Card> =
DENOMINATIONS
.flatMap { denomination ->
SHAPES.map { shape ->
Card(shape, denomination)
}
}.shuffled()
Comment on lines +4 to +13

Choose a reason for hiding this comment

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

Kotlin에서 이렇게 전부 대문자로 이름을 짓는 경우는 어떤 경우일까요?

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

enum class CardsStatus {
BLACKJACK,
BUST,
NONE,
;

companion object {
const val BLACKJACK_SCORE = 21

fun from(
cardsScore: Int,
firstTurn: Boolean = false,
): CardsStatus {
if (firstTurn && cardsScore == 21) return BLACKJACK
if (cardsScore > 21) return BUST
return NONE
}
}
}
25 changes: 25 additions & 0 deletions src/main/kotlin/blackjack/model/Dealer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package blackjack.model

class Dealer(
private val dealerName: String = "딜러",
override val cards: Cards = Cards(mutableListOf()),
) : Player(dealerName) {
private var _results: MutableMap<GameResult, Int> = mutableMapOf()
val results: Map<GameResult, Int> get() = _results.toMap()

override fun appendCard(card: Card) {
cards.add(card)
}

fun isHit(): Boolean {
val dealerScore = cards.calculateScore()
return PlayerBehavior.from(dealerScore) == PlayerBehavior.HIT
}

fun updateResult(playerScore: Int): GameResult {
val dealerScore: Int = cards.calculateScore()
val result: GameResult = GameResult.of(dealerScore, playerScore)
_results[result] = _results.getOrDefault(result, 0) + 1
return result
}
}
20 changes: 20 additions & 0 deletions src/main/kotlin/blackjack/model/Denomination.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package blackjack.model

enum class Denomination(
val title: String,
val number: Int,
) {
ACE("A", 0),
TWO("2", 2),
THREE("3", 3),
FOUR("4", 4),
FIVE("5", 5),
SIX("6", 6),
SEVEN("7", 7),
EIGHT("8", 8),
NINE("9", 9),
TEN("10", 10),
JACK("J", 10),
QUEEN("Q", 10),
KING("K", 10),
}
Loading