package casino import ( "fmt" "strings" ) // BlackjackRulesText is the table's rule card, built from effective defaults. // Keep fixed engine rules here too: players should not have to guess them. func (c TableConfig) BlackjackRulesText() string { c = c.Normalize() b := c.Blackjack yesNo := func(v bool) string { if v { return "Yes" } return "No" } dealer := "stands on" if b.DealerHitsSoft17() { dealer = "hits" } surrender := "Not offered" if b.AllowsSurrender() { surrender = "Late (original two-card hand only)" } split := "Identical ranks only" if b.AllowsUnlikeTenSplit() { split = "Identical ranks or any two ten-valued cards" } return strings.Join([]string{ fmt.Sprintf("Blackjack table %s — rules", c.ID), fmt.Sprintf("Number of decks: %d", b.Decks), "Blackjack pays " + payoutRatio(b.BlackjackPayout), "Dealer " + dealer + " soft 17", "Dealer hole card / peek: Yes; blackjack checked before player actions (after insurance)", "Ordinary wins pay 1:1; ties push; player and dealer blackjack push", "Double down: Any initial two cards; one additional card only", "Double after split: " + yesNo(b.AllowsDoubleAfterSplit()), fmt.Sprintf("Maximum split hands: %d (including original hand)", b.MaxSplitHands), "Re-splitting: " + yesNo(b.MaxSplitHands > 2) + " (within maximum hands)", "Re-splitting aces: " + yesNo(b.AllowsResplitAces()) + " (within maximum hands)", "Split pairs: " + split, "Split aces: One card per ace; no hitting or doubling", "Split-hand 21 pays 1:1, except the optional bonus below", "Split-and-doubled 21 receives blackjack payout: " + yesNo(b.CountsDoubleSplitBlackjack()), "Surrender: " + surrender, "Insurance: Offered against an ace; costs half the original bet; pays " + payoutRatio(b.InsurancePayout), "Fractional credits: Rounded down (including insurance stake and surrender return)", fmt.Sprintf("Shuffle threshold: %.6g%% of shoe remaining; may shuffle earlier for round coverage", b.ShuffleAt*100), "Shoe exhaustion: Reshuffle mid-round if necessary", "Bet limits: " + betRangeText(c.MinBet, c.MaxBet) + " credits", fmt.Sprintf("Seats: %d; betting window: %d ticks; action window: %d ticks", c.MaxPlayers, c.BettingTicks, b.ActionTicks), }, "\n") } func payoutRatio(p float64) string { switch p { case 1.5: return "3:2" case 1.2: return "6:5" default: return fmt.Sprintf("%g:1", p) } }