blackjack

package module
v1.3.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2025 License: MIT Imports: 5 Imported by: 1

README

Blackjack Game

A comprehensive console-based blackjack game written in Go, featuring a dealer, multiple players, and a multi-deck shoe.

Features

  • Multiple Players: Support for one or more players with individual chip counts and betting
  • Professional Dealer: AI dealer that follows standard blackjack rules (hits on soft 17)
  • Multi-Deck Shoe: Configurable shoe with multiple decks, automatic shuffling, and cut card placement
  • Standard Blackjack Rules:
    • Blackjack pays 3:2
    • Dealer hits on soft 17
    • Double down on any two cards
    • Split pairs (up to 4 hands per player)
    • Standard hand evaluation with proper Ace handling
  • Interactive Gameplay: Console-based interface with clear prompts and game state display
  • Betting System: Chip-based betting with win/loss tracking

Game Components

🃏 Hand
  • Tracks cards and calculates blackjack values
  • Handles Ace as 1 or 11 automatically
  • Detects blackjack, busts, and soft hands
  • Provides both visible and hidden display modes
👤 Player
  • Manages multiple hands (for splits), chips, and bets
  • Supports hit, stand, double down, and split actions
  • Tracks active/inactive status during rounds
  • Handles win/loss payouts
🎯 Dealer
  • Follows standard blackjack dealer rules
  • Automatically hits on 16 or less, stands on 17 or more
  • Hits on soft 17 (configurable house rule)
  • Manages hole card display
🎴 Shoe
  • Multi-deck shoe with configurable deck count (default: 6 decks)
  • Automatic shuffling with cut card placement
  • Tracks penetration percentage
  • Reshuffles when cut card is reached
🎮 Game Engine
  • Orchestrates complete blackjack rounds
  • Handles betting, dealing, player actions, and payouts
  • Tracks game statistics and round progression
  • Manages game state and player turns
💰 Chip Management

The game uses an abstracted chip management system through the ChipManager interface, allowing for flexible chip handling:

  • Interface-Based Design: Players use a ChipManager interface for all chip operations
  • Default Implementation: DefaultChipManager provides standard integer-based chip management
  • Extensible: Custom chip managers can be implemented for advanced features like:
    • Transaction logging
    • Different currencies or chip types
    • External banking systems
    • Audit trails and analytics
Creating Players
// Using default chip manager (recommended for most cases)
player := NewPlayer("Alice", 1000)

// Using custom chip manager
customManager := &MyCustomChipManager{...}
player := NewPlayerWithChipManager("Bob", customManager)
Adding Players to Game
// Create a new game
game := New(6) // 6-deck shoe

// Add player with default chip manager
game.AddPlayer("Alice", 1000)

// Add player with custom chip manager
customManager := &MyCustomChipManager{...}
game.AddPlayerWithChipManager("Bob", 500, customManager)

How to Play

  1. Setup: Add players with starting chip amounts
  2. Betting: Each player places their bet for the round
  3. Dealing: Two cards dealt to each player and dealer (dealer's second card is face down)
  4. Player Actions: Each player can:
    • Hit: Take another card
    • Stand: Keep current hand
    • Double Down: Double the bet and take exactly one more card (if eligible)
    • Split: If dealt a pair, split into two separate hands with separate bets
    • Surrender: Forfeit the hand and get half the bet back (if eligible)
  5. Dealer Play: Dealer reveals hole card and hits according to rules
  6. Payouts: Winners are paid according to standard blackjack payouts

Running the Game

# Build the game
go build

# Run the game
./blackjack

Game Rules

  • Blackjack: 21 with first two cards (pays 3:2)
  • Bust: Hand value over 21 (automatic loss)
  • Dealer Rules: Must hit on 16 or less, stand on 17 or more
  • Soft 17: Dealer hits on soft 17 (Ace + 6)
  • Double Down: Available on any two cards if you have sufficient chips
  • Split: Available when dealt a pair (two cards of same rank)
    • Each split hand gets a separate bet equal to the original bet
    • Split hands cannot achieve "natural" blackjack (still pays 1:1)
    • Can continue to hit, stand, or double down on each split hand
    • Maximum of 4 hands per player (up to 3 splits from the original hand)
  • Surrender: Available only when a hand has exactly 2 cards and hasn't been acted upon
    • Player forfeits the hand and receives half their bet back
    • Hand is automatically considered "stood" and no further actions are possible
    • Can be used on split hands if they meet the surrender conditions
  • Winning: Beat dealer without busting, or dealer busts

Dependencies

  • github.com/rbrabson/cards: Provides card, deck, and shoe functionality

File Structure

  • cmd/blackjack/main.go: Main game loop and user interface
  • game.go: Core game logic and round management
  • hand.go: Hand representation and value calculation
  • player.go: Player management and actions
  • dealer.go: Dealer logic and rules
  • shoe.go: Multi-deck shoe with blackjack-specific features

Example Gameplay

🃏 Welcome to Blackjack! 🃏
========================

Enter player name (or 'done' to start): Alice
Enter starting chips: 1000
Added Alice with 1000 chips.

Enter player name (or 'done' to start): Bob
Enter starting chips: 500
Added Bob with 500 chips.

Enter player name (or 'done' to start): done

🎲 Starting Round 1 🎲
===================

Alice (Chips: 1000), place your bet: 50
Alice bet 50 chips.

Bob (Chips: 500), place your bet: 25
Bob bet 25 chips.

📋 Initial Cards:
Dealer: [Hidden, 7♠] (Visible Value: 7)

Alice (Chips: 950, Bet: 50, active): [K♥, 5♦] (Value: 15)
Bob (Chips: 475, Bet: 25, active): [A♠, Q♣] (Value: 21)

🎯 Bob has blackjack!

🎮 Alice's turn:
Alice: [K♥, 5♦] (Value: 15)
Choose action: (h)it, (s)tand, (d)ouble down: h
Drew: [K♥, 5♦, 8♣] (Value: 23)
💥 Alice busted!

🎯 Dealer's turn:
Revealing hole card...
Dealer: [6♥, 7♠] (Value: 13)
Dealer: [6♥, 7♠, 9♦] (Value: 22)

🏁 Final Results:
Dealer: [6♥, 7♠, 9♦] (Value: 22)

Alice (Chips: 950, Bet: 50, inactive): [K♥, 5♦, 8♣] (Value: 23)
Bob (Chips: 475, Bet: 25, inactive): [A♠, Q♣] (Value: 21)

💰 Round Results:
================
Alice: Dealer Wins
  Chips: 950
Bob: Player Blackjack!
  Chips: 512

Play another round? (y/n):

Surrender Example

🎮 Alice's turn:
Alice: [10♠, 6♥] (Value: 16)
What would you like to do? [h(i)t, (s)tand, s(p)lit, (d)ouble, s(u)rrender]: u
Alice surrendered and received 25 chips back.

💰 Round Results:
================
Alice: Player Surrendered (received half bet back)
  Chips: 975

Split Example

🎮 Charlie's turn:
Charlie: [8♠, 8♥] (Value: 16)
Choose action: (h)it, (s)tand, (d)ouble down, s(p)lit: p
Hand split! You now have 2 hands.
Current hand: [8♠, 3♦] (Value: 11)

Charlie - Hand 1 of 2: [8♠, 3♦] (Value: 11) (Split)
Choose action: (h)it, (s)tand, (d)ouble down: h
Drew: [8♠, 3♦, 7♣] (Value: 18) (Split)
Standing on hand.

Charlie - Hand 2 of 2: [8♥, K♠] (Value: 18) (Split)
Choose action: (h)it, (s)tand: s
Standing on hand.

✅ Charlie finished all hands.

💰 Round Results:
================
Charlie:
  Hand 1: Player Wins
  Hand 2: Player Wins
  Final Chips: 1100

Enjoy playing blackjack! 🎉

Documentation

Index

Constants

View Source
const (
	CutCardPenetration = 0.75 // CutCardPenetration is the fraction of the shoe dealt before reshuffling
	NumCardsInDeck     = 52   // NumCardsInDeck is the number of cards in a standard deck
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Action

type Action struct {
	Type      ActionType  `json:"type"`
	Card      *cards.Card `json:"card,omitempty"` // Card involved (for deal/hit)
	Timestamp time.Time   `json:"timestamp"`
	Details   string      `json:"details,omitempty"` // Additional details about the action
}

Action represents an action taken on a hand

type ActionType

type ActionType string

ActionType represents the type of action taken on a hand

const (
	ActionDeal      ActionType = "deal"
	ActionHit       ActionType = "hit"
	ActionStand     ActionType = "stand"
	ActionDouble    ActionType = "double"
	ActionSplit     ActionType = "split"
	ActionSurrender ActionType = "surrender"
)

type ChipManager

type ChipManager interface {
	GetChips() int                  // GetChips returns the current chip count
	SetChips(amount int)            // SetChips sets the chip count to the specified amount
	AddChips(amount int)            // AddChips adds the specified amount to the chip count
	DeductChips(amount int) error   // DeductChips removes the specified amount from the chip count
	HasEnoughChips(amount int) bool // HasEnoughChips returns true if there are enough chips for the specified amount
}

ChipManager interface defines the operations for managing player chips

type Dealer

type Dealer struct {
	// contains filtered or unexported fields
}

Dealer represents the blackjack dealer

func NewDealer

func NewDealer() *Dealer

NewDealer creates a new dealer

func (*Dealer) ClearHand

func (d *Dealer) ClearHand()

ClearHand clears the dealer's hand for a new round

func (*Dealer) DealCard

func (d *Dealer) DealCard(card cards.Card)

DealCard adds a card to the dealer's hand as part of the initial deal

func (*Dealer) Hand

func (d *Dealer) Hand() *Hand

Hand returns the dealer's hand

func (*Dealer) HasBlackjack

func (d *Dealer) HasBlackjack() bool

HasBlackjack returns true if dealer has blackjack

func (*Dealer) Hit

func (d *Dealer) Hit(card cards.Card)

Hit adds a card to the dealer's hand

func (*Dealer) IsBusted

func (d *Dealer) IsBusted() bool

IsBusted returns true if dealer is busted

func (*Dealer) RevealHoleCard

func (d *Dealer) RevealHoleCard() string

RevealHoleCard shows the dealer's full hand

func (*Dealer) ShouldHit

func (d *Dealer) ShouldHit() bool

ShouldHit returns true if the dealer should hit according to standard blackjack rules Dealer hits on 16 or less, stands on 17 or more (including soft 17)

func (*Dealer) ShowFirstCard

func (d *Dealer) ShowFirstCard() cards.Card

ShowFirstCard returns the dealer's first card (face up)

func (*Dealer) Stand

func (d *Dealer) Stand()

Stand marks the dealer as standing

func (*Dealer) String

func (d *Dealer) String() string

String returns a string representation of the dealer with both cards showing

func (*Dealer) StringHidden

func (d *Dealer) StringHidden() string

StringHidden returns a string representation of the dealer with hole card hidden

func (*Dealer) Value

func (d *Dealer) Value() int

Value returns the dealer's hand value

type DefaultChipManager

type DefaultChipManager struct {
	// contains filtered or unexported fields
}

DefaultChipManager implements ChipManager with simple integer-based chip management

func NewDefaultChipManager

func NewDefaultChipManager(initialChips int) *DefaultChipManager

NewDefaultChipManager creates a new default chip manager with the given initial amount

func (*DefaultChipManager) AddChips

func (c *DefaultChipManager) AddChips(amount int)

AddChips adds the specified amount to the chip count

func (*DefaultChipManager) DeductChips

func (c *DefaultChipManager) DeductChips(amount int) error

DeductChips removes the specified amount from the chip count

func (*DefaultChipManager) GetChips

func (c *DefaultChipManager) GetChips() int

GetChips returns the current chip count

func (*DefaultChipManager) HasEnoughChips

func (c *DefaultChipManager) HasEnoughChips(amount int) bool

HasEnoughChips returns true if there are enough chips for the specified amount

func (*DefaultChipManager) SetChips

func (c *DefaultChipManager) SetChips(amount int)

SetChips sets the chip count to the specified amount

type Game

type Game struct {
	// contains filtered or unexported fields
}

Game represents the main game

func New

func New(numDecks int) *Game

New creates a new blackjack game

func (*Game) AddPlayer

func (bg *Game) AddPlayer(name string, options ...Option)

AddPlayer adds a player to the game

func (*Game) DealCard

func (bg *Game) DealCard() error

DealCard deals a card from the shoe

func (*Game) DealInitialCards

func (bg *Game) DealInitialCards() error

DealInitialCards deals two cards to each player and dealer

func (*Game) Dealer

func (bg *Game) Dealer() *Dealer

Dealer returns the dealer

func (*Game) DealerPlay

func (bg *Game) DealerPlay() error

DealerPlay handles the dealer's turn according to blackjack rules

func (*Game) EvaluateHand

func (bg *Game) EvaluateHand(playerHand *Hand) GameResult

EvaluateHand determines the result of a player's hand against the dealer

func (*Game) GetActivePlayer

func (bg *Game) GetActivePlayer() *Player

GetActivePlayer returns the first active player who hasn't finished their hand

func (*Game) GetGameStatus

func (bg *Game) GetGameStatus(showDealerHole bool) string

GetGameStatus returns a string representation of the current game state

func (*Game) GetPlayer

func (bg *Game) GetPlayer(name string) *Player

GetPlayer returns a player by name

func (*Game) IsRoundComplete

func (bg *Game) IsRoundComplete() bool

IsRoundComplete returns true if all players have finished their hands

func (*Game) PayoutResults

func (bg *Game) PayoutResults()

PayoutResults handles payouts for all players

func (*Game) PlayerDoubleDownHit

func (bg *Game) PlayerDoubleDownHit(playerName string) error

PlayerDoubleDownHit deals a card to a specific player as part of a double down

func (*Game) PlayerHit

func (bg *Game) PlayerHit(playerName string) error

PlayerHit deals a card to a specific player

func (*Game) PlayerSplit

func (bg *Game) PlayerSplit(playerName string) error

PlayerSplit processes a split action for the specified player.

func (*Game) PlayerStand

func (bg *Game) PlayerStand(playerName string) error

PlayerStand handles a player standing on their current hand

func (*Game) PlayerSurrender

func (bg *Game) PlayerSurrender(playerName string) error

PlayerSurrender handles a player surrendering their current hand

func (*Game) Players

func (bg *Game) Players() []*Player

Players returns a copy of the players slice

func (*Game) RemovePlayer

func (bg *Game) RemovePlayer(name string) bool

RemovePlayer removes a player from the game

func (*Game) Round

func (bg *Game) Round() int

Round returns the current round number

func (*Game) Shoe

func (bg *Game) Shoe() *Shoe

Shoe returns the shoe

func (*Game) StartNewRound

func (bg *Game) StartNewRound() error

StartNewRound starts a new round of blackjack

type GameResult

type GameResult int

GameResult represents the outcome of a hand

const (
	PlayerWin       GameResult // PlayerWin reprepsents a win for the player
	DealerWin                  // DealerWin represents a win for the dealer
	Push                       // Push represents a tie
	PlayerBlackjack            // PlayerBlackjack represents a player blackjack
	DealerBlackjack            // DealerBlackjack represents a dealer blackjack
)

func (GameResult) String

func (gr GameResult) String() string

String returns a string representation of the game result

type Hand

type Hand struct {
	// contains filtered or unexported fields
}

Hand represents a hand of cards in blackjack

func NewDealerHand added in v1.3.0

func NewDealerHand() *Hand

NewDealerHand creates a new dealer hand without a chip manager

func NewHand

func NewHand(player *Player) *Hand

NewHand creates a new empty hand

func (*Hand) ActionSummary

func (h *Hand) ActionSummary() string

ActionSummary returns a string summary of all actions taken on this hand

func (*Hand) Actions

func (h *Hand) Actions() []Action

Actions returns a copy of all actions taken on this hand

func (*Hand) AddCard

func (h *Hand) AddCard(card cards.Card)

AddCard adds a card to the hand

func (*Hand) AddCardWithAction

func (h *Hand) AddCardWithAction(card cards.Card, actionType ActionType, details string)

AddCardWithAction adds a card to the hand and records the specific action

func (*Hand) AddWinnings added in v1.1.0

func (h *Hand) AddWinnings(amount int)

AddWinnings adds to the winnings for this hand

func (*Hand) Bet added in v1.1.0

func (h *Hand) Bet() int

Bet returns the bet amount for this hand

func (*Hand) CanDoubleDown added in v1.3.0

func (h *Hand) CanDoubleDown() bool

CanDoubleDown returns true if the hand can be doubled down

func (*Hand) CanSplit

func (h *Hand) CanSplit() bool

CanSplit returns true if the hand can be split (two cards of same rank)

func (*Hand) CanSurrender added in v1.3.0

func (h *Hand) CanSurrender() bool

CanSurrender returns true if the player can surrender (typically only on first two cards)

func (*Hand) Cards

func (h *Hand) Cards() []cards.Card

Cards returns a copy of the cards in the hand

func (*Hand) Clear

func (h *Hand) Clear()

Clear removes all cards from the hand

func (*Hand) Count

func (h *Hand) Count() int

Count returns the number of cards in the hand

func (*Hand) DealCard added in v1.3.0

func (h *Hand) DealCard(card cards.Card)

DealCard adds a card to the player's hand as part of the initial deal

func (*Hand) DoubleDown added in v1.3.0

func (h *Hand) DoubleDown() error

DoubleDown performs the double down action on the hand

func (*Hand) DoubleDownHit added in v1.3.0

func (h *Hand) DoubleDownHit(card cards.Card)

DoubleDownHit adds a card to the player's hand as part of a double down

func (*Hand) Hit added in v1.3.0

func (h *Hand) Hit(card cards.Card)

Hit adds a card to the player's hand

func (*Hand) IsActive

func (h *Hand) IsActive() bool

IsActive returns true if this hand is still being played

func (*Hand) IsBlackjack

func (h *Hand) IsBlackjack() bool

IsBlackjack returns true if the hand is a natural blackjack (21 with 2 cards)

func (*Hand) IsBusted

func (h *Hand) IsBusted() bool

IsBusted returns true if the hand value is over 21

func (*Hand) IsSoft

func (h *Hand) IsSoft() bool

IsSoft returns true if the hand contains an ace counted as 11

func (*Hand) IsSplit

func (h *Hand) IsSplit() bool

IsSplit returns true if this hand was created by a split.

func (*Hand) IsStood

func (h *Hand) IsStood() bool

IsStood returns true if the player has stood on this hand

func (*Hand) IsSurrendered added in v1.3.2

func (h *Hand) IsSurrendered() bool

IsSurrendered returns true if the hand has been surrendered

func (*Hand) LoseBet added in v1.3.0

func (h *Hand) LoseBet()

LoseBet removes the player's bet for the current hand (already deducted when placed)

func (*Hand) PlaceBet added in v1.3.0

func (h *Hand) PlaceBet(amount int) error

PlaceBet places a bet for the player's current hand

func (*Hand) PushBet added in v1.3.0

func (h *Hand) PushBet()

PushBet returns the bet to the player for the current hand (tie)

func (*Hand) RecordAction

func (h *Hand) RecordAction(actionType ActionType, details string)

RecordAction records an action without a card (like stand, surrender)

func (*Hand) SetActive

func (h *Hand) SetActive(active bool)

SetActive sets the active status of the hand

func (*Hand) SetBet added in v1.1.0

func (h *Hand) SetBet(amount int)

SetBet sets the bet amount for this hand

func (*Hand) SetWinnings added in v1.1.0

func (h *Hand) SetWinnings(amount int)

SetWinnings sets the winnings for this hand

func (*Hand) Split added in v1.3.0

func (h *Hand) Split() error

Split splits the player's hand into two hands

func (*Hand) Stand

func (h *Hand) Stand()

Stand marks the hand as stood

func (*Hand) String

func (h *Hand) String() string

String returns a string representation of the hand

func (*Hand) StringHidden

func (h *Hand) StringHidden() string

StringHidden returns a string representation with the first card hidden (for dealer)

func (*Hand) Surrender added in v1.3.0

func (h *Hand) Surrender()

Surrender allows the player to forfeit their hand and lose half their bet

func (*Hand) Value

func (h *Hand) Value() int

Value calculates the blackjack value of the hand

func (*Hand) WinBet added in v1.3.0

func (h *Hand) WinBet(multiplier float64)

WinBet adds winnings to the player's chips for the current hand

func (*Hand) Winnings added in v1.1.0

func (h *Hand) Winnings() int

Winnings returns the winnings for this hand (can be negative for losses)

type Option

type Option func(*Player)

Option is a function that modifies a message.

func WithChipManager

func WithChipManager(cm ChipManager) Option

WithChipManager sets a custom chip manager for the player.

func WithChips

func WithChips(chips int) Option

WithAllowedMentions sets the allowed mentions for the message.

type Player

type Player struct {
	// contains filtered or unexported fields
}

Player represents a blackjack player

func NewPlayer

func NewPlayer(name string, options ...Option) *Player

NewPlayer creates a new player with the given name, initial chips, and optional settings

func (*Player) AddChips

func (p *Player) AddChips(amount int)

AddChips adds chips to the player's account

func (*Player) Chips

func (p *Player) Chips() int

Chips returns the player's current chip count

func (*Player) ClearHands added in v1.3.0

func (p *Player) ClearHands()

ClearHands clears all of the player's hands for a new round

func (*Player) CurrentHand

func (p *Player) CurrentHand() *Hand

CurrentHand returns the player's current hand

func (*Player) GetAllHandValues

func (p *Player) GetAllHandValues() []int

GetAllHandValues returns the values of all hands

func (*Player) GetCurrentHandNumber added in v1.3.0

func (p *Player) GetCurrentHandNumber() int

GetCurrentHandNumber returns the index of the current hand

func (*Player) Hands

func (p *Player) Hands() []*Hand

Hand returns all of the player's hands

func (*Player) HasActiveHands

func (p *Player) HasActiveHands() bool

HasActiveHands returns true if the player has any active hands left to play

func (*Player) IsActive

func (p *Player) IsActive() bool

IsActive returns whether the player is still active in the game

func (*Player) IsStanding

func (p *Player) IsStanding() bool

IsStanding returns true if the current hand should stand (busted, blackjack, or inactive)

func (*Player) MoveToNextActiveHand

func (p *Player) MoveToNextActiveHand() bool

MoveToNextActiveHand moves to the next active hand, returns true if successful

func (*Player) Name

func (p *Player) Name() string

Name returns the player's name

func (*Player) NextHand

func (p *Player) NextHand() bool

NextHand moves to the next hand if available, returning true if successful

func (*Player) SetActive

func (p *Player) SetActive(active bool)

SetActive sets the player's active status

func (*Player) String

func (p *Player) String() string

String returns a string representation of the player

type Shoe

type Shoe struct {
	// contains filtered or unexported fields
}

Shoe wraps the cards.Shoe with blackjack-specific functionality

func NewShoe

func NewShoe(numDecks int) *Shoe

NewShoe creates a new blackjack shoe with the specified number of decks

func (*Shoe) CardsRemaining

func (s *Shoe) CardsRemaining() int

CardsRemaining returns the number of cards left in the shoe

func (*Shoe) Draw

func (s *Shoe) Draw() (cards.Card, error)

Draw deals a card from the shoe

func (*Shoe) IsEmpty

func (s *Shoe) IsEmpty() bool

IsEmpty returns true if the shoe is empty

func (*Shoe) NeedsReshuffle

func (s *Shoe) NeedsReshuffle() bool

NeedsReshuffle returns true if the cut card has been reached

func (*Shoe) NumDecks

func (s *Shoe) NumDecks() int

NumDecks returns the number of decks in the shoe

func (*Shoe) Penetration

func (s *Shoe) Penetration() float64

Penetration returns the percentage of cards that have been dealt

func (*Shoe) Reshuffle

func (s *Shoe) Reshuffle()

Reshuffle creates a new shuffled shoe with the same number of decks

func (*Shoe) String

func (s *Shoe) String() string

String returns a string representation of the shoe

Directories

Path Synopsis
cmd
blackjack command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL