aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/blackjack.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/casino/blackjack.go')
-rw-r--r--internal/casino/blackjack.go132
1 files changed, 31 insertions, 101 deletions
diff --git a/internal/casino/blackjack.go b/internal/casino/blackjack.go
index a158a24..80a4d15 100644
--- a/internal/casino/blackjack.go
+++ b/internal/casino/blackjack.go
@@ -17,28 +17,8 @@ const (
blackjackPlaying blackjackPhase = "playing"
)
-type blackjackCard struct {
- rank string
- suit string
-}
-
-func (c blackjackCard) String() string { return c.rank + " of " + c.suit }
-
-func (c blackjackCard) value() int {
- switch c.rank {
- case "A":
- return 11
- case "K", "Q", "J":
- return 10
- default:
- var value int
- fmt.Sscanf(c.rank, "%d", &value)
- return value
- }
-}
-
type blackjackHand struct {
- cards []blackjackCard
+ cards []card
bet int
fromSplit bool
doubled bool
@@ -49,9 +29,9 @@ type blackjackHand struct {
func (h *blackjackHand) total() (int, bool) {
total, aces := 0, 0
- for _, card := range h.cards {
- total += card.value()
- if card.rank == "A" {
+ for _, c := range h.cards {
+ total += c.blackjackValue()
+ if c.rank == "A" {
aces++
}
}
@@ -76,45 +56,9 @@ type blackjackPlayer struct {
lastBet int
}
-type blackjackShoe struct {
- cards []blackjackCard
- rng *rand.Rand
- decks int
-}
-
-func newBlackjackShoe(decks int, rng *rand.Rand) *blackjackShoe {
- s := &blackjackShoe{rng: rng, decks: decks}
- s.shuffle()
- return s
-}
-
-func (s *blackjackShoe) shuffle() {
- ranks := []string{"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}
- suits := []string{"clubs", "diamonds", "hearts", "spades"}
- s.cards = make([]blackjackCard, 0, s.decks*52)
- for deck := 0; deck < s.decks; deck++ {
- for _, suit := range suits {
- for _, rank := range ranks {
- s.cards = append(s.cards, blackjackCard{rank: rank, suit: suit})
- }
- }
- }
- s.rng.Shuffle(len(s.cards), func(i, j int) { s.cards[i], s.cards[j] = s.cards[j], s.cards[i] })
-}
-
-func (s *blackjackShoe) draw(rules BlackjackConfig) blackjackCard {
- if len(s.cards) <= int(float64(s.decks*52)*rules.ShuffleAt) {
- s.shuffle()
- }
- card := s.cards[len(s.cards)-1]
- s.cards = s.cards[:len(s.cards)-1]
- return card
-}
-
type blackjackTable struct {
- mu sync.Mutex
- RoomID int
- Config TableConfig
+ mu sync.Mutex
+ tableBase
Rules BlackjackConfig
participants map[string]*blackjackPlayer
order []string
@@ -123,16 +67,16 @@ type blackjackTable struct {
activePlayer int
activeHand int
dealer blackjackHand
- shoe *blackjackShoe
+ shoe *shoe
}
func newBlackjackTable(roomID int, cfg TableConfig, rng *rand.Rand) *blackjackTable {
cfg = cfg.Normalize()
rules := cfg.Blackjack.Normalize()
return &blackjackTable{
- RoomID: roomID, Config: cfg, Rules: rules,
+ tableBase: tableBase{RoomID: roomID, Config: cfg}, Rules: rules,
participants: make(map[string]*blackjackPlayer), phase: blackjackWaiting,
- shoe: newBlackjackShoe(rules.Decks, rng),
+ shoe: newShoe(rules.Decks, rules.ShuffleAt, rng),
}
}
@@ -146,23 +90,6 @@ func (t *blackjackTable) seats() []*Participant {
return seats
}
-func (t *blackjackTable) event(message string, public bool) Event {
- return Event{RoomID: t.RoomID, TableID: t.Config.ID, Type: EventResult, Message: message, Public: public, Color: "casino_action"}
-}
-
-func (t *blackjackTable) private(name, message string) Event {
- e := t.event(message, false)
- e.PrivateTo = name
- return e
-}
-
-func (t *blackjackTable) playerAction(name, publicMessage, privateMessage string) Event {
- e := t.event(publicMessage, true)
- e.PrivateTo = name
- e.PrivateMessage = privateMessage
- return e
-}
-
func (t *blackjackTable) menuEvent(name string) Event {
e := t.private(name, t.menu())
e.Color = "casino_menu"
@@ -218,13 +145,16 @@ func (t *blackjackTable) leave(name string) []Event {
return events
}
-func (t *blackjackTable) bet(name string, amount int, wallet Wallet) []Event {
+func (t *blackjackTable) bet(name, spot string, amount int, wallet Wallet) []Event {
t.mu.Lock()
defer t.mu.Unlock()
p := t.participants[name]
if p == nil {
return []Event{t.private(name, "You aren't playing blackjack.")}
}
+ if spot != "" {
+ return []Event{t.private(name, "Blackjack bets don't take a bet target.")}
+ }
if t.phase != blackjackWaiting && t.phase != blackjackBetting {
return []Event{t.private(name, "That blackjack table isn't accepting bets.")}
}
@@ -317,7 +247,7 @@ func (t *blackjackTable) action(name, action string) []Event {
if fromSplitAces(hand) {
return []Event{t.private(name, "Split aces cannot be hit.")}
}
- hand.cards = append(hand.cards, t.shoe.draw(t.Rules))
+ hand.cards = append(hand.cards, t.shoe.draw())
total, _ := hand.total()
if total > 21 {
hand.bust = true
@@ -344,7 +274,7 @@ func (t *blackjackTable) action(name, action string) []Event {
hand.bet *= 2
hand.doubled = true
hand.stood = true
- hand.cards = append(hand.cards, t.shoe.draw(t.Rules))
+ hand.cards = append(hand.cards, t.shoe.draw())
if total, _ := hand.total(); total > 21 {
hand.bust = true
}
@@ -430,10 +360,10 @@ func (t *blackjackTable) splitHand(name string, p *blackjackPlayer, hand *blackj
if _, ok := p.Wallet.Wager(hand.bet); !ok {
return []Event{t.private(name, "You don't have enough chips and credits to split that hand.")}
}
- left := &blackjackHand{cards: []blackjackCard{hand.cards[0]}, bet: hand.bet, fromSplit: true}
- right := &blackjackHand{cards: []blackjackCard{hand.cards[1]}, bet: hand.bet, fromSplit: true}
- left.cards = append(left.cards, t.shoe.draw(t.Rules))
- right.cards = append(right.cards, t.shoe.draw(t.Rules))
+ left := &blackjackHand{cards: []card{hand.cards[0]}, bet: hand.bet, fromSplit: true}
+ right := &blackjackHand{cards: []card{hand.cards[1]}, bet: hand.bet, fromSplit: true}
+ left.cards = append(left.cards, t.shoe.draw())
+ right.cards = append(right.cards, t.shoe.draw())
index := t.activeHand
p.hands = append(p.hands[:index], append([]*blackjackHand{left, right}, p.hands[index+1:]...)...)
e := t.playerAction(name, fmt.Sprintf("%s splits the hand.", name), "You split the hand.")
@@ -449,21 +379,21 @@ func (t *blackjackTable) splitHand(name string, p *blackjackPlayer, hand *blackj
return events
}
-func splitCompatible(a, b blackjackCard, unlikeTens bool) bool {
+func splitCompatible(a, b card, unlikeTens bool) bool {
if a.rank == b.rank {
return true
}
- return unlikeTens && a.value() == 10 && b.value() == 10
+ return unlikeTens && a.blackjackValue() == 10 && b.blackjackValue() == 10
}
func (t *blackjackTable) startRound() []Event {
- t.dealer = blackjackHand{cards: []blackjackCard{t.shoe.draw(t.Rules), t.shoe.draw(t.Rules)}}
+ t.dealer = blackjackHand{cards: []card{t.shoe.draw(), t.shoe.draw()}}
for _, name := range t.order {
p := t.participants[name]
if p == nil || !p.HasBet {
continue
}
- p.hands = []*blackjackHand{{cards: []blackjackCard{t.shoe.draw(t.Rules), t.shoe.draw(t.Rules)}, bet: p.Wager.Total}}
+ p.hands = []*blackjackHand{{cards: []card{t.shoe.draw(), t.shoe.draw()}, bet: p.Wager.Total}}
if p.hands[0].bet/2 <= 0 {
// A half-bet that rounds to zero cannot be insured.
p.declinedInsurance = true
@@ -595,7 +525,7 @@ func (t *blackjackTable) settle() []Event {
if total > 17 || total == 17 && (!soft || !t.Rules.DealerHitsSoft17()) {
break
}
- t.dealer.cards = append(t.dealer.cards, t.shoe.draw(t.Rules))
+ t.dealer.cards = append(t.dealer.cards, t.shoe.draw())
}
dealerTotal, _ := t.dealer.total()
dealerEvent := t.event("Dealer's hand:", true)
@@ -679,11 +609,11 @@ func (t *blackjackTable) resetRound() {
}
}
-func (t *blackjackTable) rebetAmount(name string) int {
+func (t *blackjackTable) rebet(name string) (string, int) {
if p := t.participants[name]; p != nil && p.lastBet > 0 {
- return p.lastBet
+ return "", p.lastBet
}
- return t.Config.MinBet
+ return "", t.Config.MinBet
}
func (t *blackjackTable) bettorCount() int {
@@ -700,10 +630,10 @@ func (t *blackjackTable) snapshot(revealDealer bool) *BlackjackSnapshot {
snapshot := &BlackjackSnapshot{}
for i, card := range t.dealer.cards {
if i == 0 && !revealDealer {
- snapshot.Dealer = append(snapshot.Dealer, BlackjackCardView{Rank: "?", Hidden: true})
+ snapshot.Dealer = append(snapshot.Dealer, CardView{Rank: "?", Hidden: true})
continue
}
- snapshot.Dealer = append(snapshot.Dealer, BlackjackCardView{Rank: card.rank, Suit: card.suit})
+ snapshot.Dealer = append(snapshot.Dealer, CardView{Rank: card.rank, Suit: card.suit})
}
for _, name := range t.order {
p := t.participants[name]
@@ -713,13 +643,13 @@ func (t *blackjackTable) snapshot(revealDealer bool) *BlackjackSnapshot {
for _, hand := range p.hands {
view := BlackjackPlayerView{Name: name, Score: handScore(hand)}
for _, card := range hand.cards {
- view.Cards = append(view.Cards, BlackjackCardView{Rank: card.rank, Suit: card.suit})
+ view.Cards = append(view.Cards, CardView{Rank: card.rank, Suit: card.suit})
}
snapshot.Players = append(snapshot.Players, view)
}
}
if len(t.dealer.cards) > 1 {
- snapshot.DealerShownScore = handScore(&blackjackHand{cards: []blackjackCard{t.dealer.cards[1]}})
+ snapshot.DealerShownScore = handScore(&blackjackHand{cards: []card{t.dealer.cards[1]}})
}
snapshot.DealerScore = handScore(&t.dealer)
snapshot.DealerRevealed = revealDealer