aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/roulette.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-29 17:50:33 -0400
committerhistoria <[not public]>2026-09-29 17:50:33 -0400
commitff9002b96e35ad0808945e7c35791460c9ca8776 (patch)
tree34c99c64176a7a2d26201f21f97ef7b54b64eb59 /internal/casino/roulette.go
parent00fe5f7ead43627fa283035b27365924b76c5a0f (diff)
downloadthehouseoficarus-ff9002b96e35ad0808945e7c35791460c9ca8776.tar.gz
feat: roulette racetrack betting, rename casino to domita fortuna
Diffstat (limited to 'internal/casino/roulette.go')
-rw-r--r--internal/casino/roulette.go32
1 files changed, 19 insertions, 13 deletions
diff --git a/internal/casino/roulette.go b/internal/casino/roulette.go
index cae9d0d..252ac4c 100644
--- a/internal/casino/roulette.go
+++ b/internal/casino/roulette.go
@@ -8,8 +8,11 @@ import (
)
// RouletteConfig selects European (single zero) or American (double zero) rules.
+// Racetrack adds the French call bets and neighbours bets; it is a single-zero
+// layout only.
type RouletteConfig struct {
DoubleZero bool `yaml:"double_zero,omitempty"`
+ Racetrack bool `yaml:"racetrack,omitempty"`
SpinTicks int `yaml:"spin_ticks,omitempty"`
}
@@ -501,7 +504,7 @@ func newRouletteTable(room int, cfg TableConfig, rng *rand.Rand) *rouletteTable
return &rouletteTable{tableCore: newTableCore[*roulettePlayer](&tableBase{RoomID: room, Config: cfg}), rng: rng}
}
func (t *rouletteTable) view() *RouletteSnapshot {
- s := &RouletteSnapshot{DoubleZero: t.Config.Roulette.DoubleZero, Phase: string(t.phase), Wheel: rouletteWheel(t.Config.Roulette.DoubleZero)}
+ s := &RouletteSnapshot{DoubleZero: t.Config.Roulette.DoubleZero, Racetrack: t.Config.Roulette.Racetrack, Phase: string(t.phase), Wheel: rouletteWheel(t.Config.Roulette.DoubleZero)}
for _, p := range t.players.ordered() {
s.Players = append(s.Players, p.Name)
}
@@ -559,15 +562,16 @@ func (t *rouletteTable) bet(name, spot string, amount int, w Wallet) []Event {
spot = p.lastSpot
}
spot = normalizeRouletteSpot(spot)
- _, mult := rouletteBet(spot, t.Config.Roulette.DoubleZero)
- if mult == 0 {
+ resolved, ok := rouletteWagerFor(spot, t.Config.Roulette.DoubleZero, t.Config.Roulette.Racetrack)
+ if !ok {
return []Event{t.private(name, "Unknown roulette target. Use table to see all available bets.")}
}
if amount < t.Config.MinBet || amount > t.Config.MaxBet {
return []Event{t.private(name, "Roulette bets must be "+betRangeText(t.Config.MinBet, t.Config.MaxBet)+".")}
}
- // Check total returns before committing even an unlimited wager.
- if _, err := MulPayout(amount, float64(mult)); err != nil {
+ // Check every possible winning return before committing even an unlimited
+ // wager: a call bet's components pay different multiples.
+ if err := resolved.validate(amount); err != nil {
return []Event{t.private(name, "That wager's potential payout is too large.")}
}
wager, err := w.Wager(amount)
@@ -633,15 +637,14 @@ func (t *rouletteTable) settle() []Event {
if !p.HasBet {
continue
}
- covered, mult := rouletteBet(p.lastSpot, t.Config.Roulette.DoubleZero)
+ wager, resolved := rouletteWagerFor(p.lastSpot, t.Config.Roulette.DoubleZero, t.Config.Roulette.Racetrack)
payout := 0
result := "loss"
var err error
- for _, n := range covered {
- if n == pocket {
+ if resolved {
+ if share, ok := wager.covers(pocket); ok {
result = "win"
- payout, err = MulPayout(p.Wager.Total, float64(mult))
- break
+ payout, err = share.payout(p.Wager.Total)
}
}
// PayCredits supports a zero return and atomically consumes a losing
@@ -746,9 +749,12 @@ func (t *rouletteTable) refundAll() []Event {
// colors, Unicode preferences, or card-art settings.
type RouletteSnapshot struct {
DoubleZero bool
- Phase string
- Pockets [3]string // physical wheel neighbors, not numerical neighbors
- Players []string
+ // Racetrack reports that the table also offers the French call bets and
+ // neighbours bets, so the renderer draws the racetrack panel.
+ Racetrack bool
+ Phase string
+ Pockets [3]string // physical wheel neighbors, not numerical neighbors
+ Players []string
// Wheel is the pocket order clockwise from the top; the renderer colors
// and places each pocket. It carries the zero count (one or two) so the
// drawn wheel always matches the configured table.