Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
52 changes: 49 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
# kotlin-blackjack

## Step 3
### Functional Requirements
Implement a program based on a simplified version of Blackjack. In this game, the player or dealer with a total closest to 21—without going over—wins.

- Card values follow standard Blackjack rules:
- Number cards are counted by their face value.
- Face cards (King, Queen, Jack) are each worth 10.
- Aces can be worth either 1 or 11.
- Each player starts with two cards.
- Players may draw additional cards as long as their total remains 21 or less.
- The dealer must draw a card if their total is 16 or less, and must stand on 17 or more.
- If the dealer busts (goes over 21), all remaining players automatically win.
- After the game ends, display the result (win/loss) for each player.

## Step 2
### Functional Requirements
- Card values follow standard Blackjack rules:
Expand All @@ -8,10 +23,35 @@
- At the start of the game, each player receives two cards.
- Players can choose to draw additional cards as long as their total does not exceed 21.

### Player
### Participant
- [x] Have hand
- [x] Have name
- [x] Add card to Hand
- [x] Open Card for first

### Dealer
- [x] Implement Participant
- [x] Add card to Hand

### Player
- [x] Implement Participant
- [x] Have name

### FirstTurn
- [x] Draw cards
- [x] When the sum is 21 return Blackjack
- [x] WHen the sum is less than 21 return Hit

### Hit
- [x] Draw Card
- [x] When the sum is over than 21 return Bust
- [x] When the sum is less than 21 return Hit
- [x] return stay

### Blackjack

### Bust

### Stay

### Hand
- [x] Have cards as a list
Expand All @@ -21,6 +61,7 @@
- [x] Return size
- [x] Have to have at least two cards
- [x] Return is bust
- [x] Return is blackjack

### PlayingCard
- [x] Has suit and denomination
Expand All @@ -45,4 +86,9 @@
### OutputView
- [x] Display player's cards
- [x] Display player's cards and total score
- [x] Display first turn
- [x] Display first turn
- [x] Display final results

### WinningResult
- [x] Calculate winners between two participants
- [x] Return the result of participant
40 changes: 31 additions & 9 deletions src/main/kotlin/Casino.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import card.Deck
import card.PlayingCard
import participant.Dealer
import participant.Participant
import participant.Player
import state.Bust
import state.FirstTurn
import view.InputView
import view.OutputView

Expand All @@ -8,22 +15,37 @@ class Casino(
fun run() {
val deck = Deck(PlayingCard.ALL.shuffled())
val names = inputView.getPlayerNames()
val players = names.map { Player(it, Hand(deck.drawCard(2))) }
outputView.printFirstTurn(players)
val players = names.map { Player(it, FirstTurn(Hand(emptyList()))) }
val dealer = Dealer(state = FirstTurn(Hand(emptyList())))

players.forEach { turn(it, deck) }
players.forEach { outputView.printScore(it) }
val participants: List<Participant> = players + dealer
repeat(2) { participants.forEach { it.drawCard(deck.drawOne()) } }

outputView.printFirstTurn(participants)

participants.forEach { turn(it, deck) }
participants.forEach { outputView.printScore(it) }

val winningResult = WinningResult(dealer)
participants.forEach {
val result = winningResult.versus(it)
outputView.printResult(it, result)
}
}

private fun turn(
player: Player,
participant: Participant,
deck: Deck,
) {
while (true) {
val response = inputView.getResponse(player.name)
if (!response || player.hand.isBust()) return
player.drawCard(deck.drawCard(1).first())
outputView.printPlayerCards(player)
val response = inputView.getResponse(participant)
if (!response || !participant.wantDraw()) {
participant.stay()
return
}
participant.drawCard(deck.drawOne())
outputView.printPlayerCards(participant)
if (participant.state is Bust) return
}
}
}
23 changes: 23 additions & 0 deletions src/main/kotlin/GambleResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
data class GambleResult(
val win: Int = 0,
val lose: Int = 0,
val draw: Int = 0,
) {
operator fun plus(other: GameResult): GambleResult {
return when (other) {
GameResult.WIN -> copy(win = win + 1)
GameResult.LOSE -> copy(lose = lose + 1)
GameResult.DRAW -> copy(draw = draw + 1)
}
}

companion object {
fun from(value: GameResult): GambleResult {
return when (value) {
GameResult.WIN -> GambleResult(win = 1)
GameResult.LOSE -> GambleResult(lose = 1)
GameResult.DRAW -> GambleResult(draw = 1)
}
}
}
}
16 changes: 16 additions & 0 deletions src/main/kotlin/GameResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
enum class GameResult {
WIN,
LOSE,
DRAW,
;

companion object {
fun getApposite(result: GameResult): GameResult {
return when (result) {
WIN -> LOSE
DRAW -> DRAW
LOSE -> WIN
}
}
}
}
14 changes: 8 additions & 6 deletions src/main/kotlin/Hand.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
class Hand(cards: List<PlayingCard>) {
init {
require(cards.size >= MINIMUM_SIZE) { ERROR_MINIMUM_SIZE }
}
import card.Denomination
import card.PlayingCard

class Hand(cards: List<PlayingCard>) {
private val _cards: MutableList<PlayingCard> = cards.toMutableList()
val cards: List<PlayingCard>
get() = _cards.toList()
Expand All @@ -24,10 +23,13 @@ class Hand(cards: List<PlayingCard>) {
return score() > MAX_SCORE
}

fun isBlackjack(): Boolean {
return size == BLACKJACK_SIZE && score() == MAX_SCORE
}

companion object {
private const val BONUS = 10
private const val MINIMUM_SIZE = 2
private const val MAX_SCORE = 21
private const val ERROR_MINIMUM_SIZE = "Have to have at least two cards"
private const val BLACKJACK_SIZE = 2
}
}
5 changes: 0 additions & 5 deletions src/main/kotlin/Player.kt

This file was deleted.

47 changes: 47 additions & 0 deletions src/main/kotlin/WinningResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import GameResult.Companion.getApposite
import GameResult.DRAW
import GameResult.LOSE
import GameResult.WIN
import participant.Participant
import state.Blackjack
import state.Bust
import state.Stay

class WinningResult(private val participant: Participant) {
private val participantScore: MutableMap<GameResult, Int> =
mutableMapOf(
WIN to 0,
LOSE to 0,
DRAW to 0,
)

fun versus(player: Participant): GambleResult {
if (participant.name == player.name) {
return GambleResult(
win = participantScore[WIN] ?: 0,
lose = participantScore[WIN] ?: 0,
draw = participantScore[WIN] ?: 0,
)
}
val result = compare(player)
participantScore[result] = participantScore.getValue(result) + 1
val playerResult = getApposite(result)
return GambleResult.from(playerResult)
}

private fun compare(player: Participant): GameResult {
return when (participant.state) {
is Bust -> if (player.state is Bust) WIN else LOSE
is Blackjack -> if (player.state is Blackjack) DRAW else WIN
is Stay ->
when {
player.state is Bust -> WIN
participant.score() < player.score() -> LOSE
participant.score() == player.score() -> DRAW
else -> WIN
}

else -> throw IllegalStateException()
}
}
}
6 changes: 4 additions & 2 deletions src/main/kotlin/Deck.kt → src/main/kotlin/card/Deck.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package card

class Deck(cards: List<PlayingCard>) {
private val _cards = cards.toMutableList()
val cards: List<PlayingCard>
get() = _cards.toList()

fun drawCard(count: Int): List<PlayingCard> {
return List(count) { _cards.removeFirst() }
fun drawOne(): PlayingCard {
return _cards.removeFirst()
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package card

enum class Denomination(val score: Int) {
ACE(1),
TWO(2),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package card

data class PlayingCard private constructor(val suit: Suit, val denomination: Denomination) {
companion object {
val ALL =
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/Suit.kt → src/main/kotlin/card/Suit.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package card

enum class Suit {
CLUB,
DIAMOND,
Expand Down
14 changes: 14 additions & 0 deletions src/main/kotlin/participant/Dealer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package participant

import card.PlayingCard
import state.State

class Dealer(name: String = "Dealer", override var state: State) : Participant(name) {
override fun showCardFirst(): List<PlayingCard> {
return listOf(state.cards.first())
}

override fun wantDraw(): Boolean {
return score() < 17
}
}
24 changes: 24 additions & 0 deletions src/main/kotlin/participant/Participant.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package participant

import card.PlayingCard
import state.State

abstract class Participant(val name: String) {
abstract var state: State

abstract fun showCardFirst(): List<PlayingCard>

fun score(): Int {
return state.hand.score()
}

fun stay() {
state = state.stay()
}

fun drawCard(card: PlayingCard) {
state = state.drawCard(card)
}

abstract fun wantDraw(): Boolean
}
14 changes: 14 additions & 0 deletions src/main/kotlin/participant/Player.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package participant

import card.PlayingCard
import state.State

class Player(name: String, override var state: State) : Participant(name) {
override fun showCardFirst(): List<PlayingCard> {
return state.cards
}

override fun wantDraw(): Boolean {
return true
}
}
5 changes: 5 additions & 0 deletions src/main/kotlin/state/Blackjack.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package state

import Hand

class Blackjack(hand: Hand) : Finished(hand)
5 changes: 5 additions & 0 deletions src/main/kotlin/state/Bust.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package state

import Hand

class Bust(hand: Hand) : Finished(hand)
14 changes: 14 additions & 0 deletions src/main/kotlin/state/Finished.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package state

import Hand
import card.PlayingCard

abstract class Finished(override val hand: Hand) : State {
override fun drawCard(card: PlayingCard): State {
throw IllegalStateException()
}

override fun stay(): State {
return this
}
}
20 changes: 20 additions & 0 deletions src/main/kotlin/state/FirstTurn.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package state

import Hand
import card.PlayingCard

class FirstTurn(override val hand: Hand) : State {
override fun drawCard(card: PlayingCard): State {
hand.add(card)

if (hand.size == 2) {
if (hand.isBlackjack()) return Blackjack(hand)
return Hit(hand)
}
return FirstTurn(hand)
}

override fun stay(): State {
throw IllegalStateException()
}
}
Loading