diff options
Diffstat (limited to 'internal/casino/video_poker_profiles.go')
| -rw-r--r-- | internal/casino/video_poker_profiles.go | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/internal/casino/video_poker_profiles.go b/internal/casino/video_poker_profiles.go new file mode 100644 index 0000000..9471ee6 --- /dev/null +++ b/internal/casino/video_poker_profiles.go @@ -0,0 +1,171 @@ +package casino + +import "math/rand" + +// Bonus feature identifiers (cabinet editions). +const ( + PokerUltimateX = "ultimate_x" + PokerSuperTimesPay = "super_times_pay" + PokerHotRoll = "hot_roll" +) + +// NextAward exposes the published Ultimate X multiplier the named hand earns +// for the selected hand count; 1 means the line earned nothing. +func (c VideoPokerConfig) NextAward(hands int, category string) int { + if cabinetFeature(c.Normalize().Cabinet) != PokerUltimateX { + return 1 + } + return pokerNextMultiplier(c.Cabinet, c.Variant, hands, category) +} + +// Each tuple is [single-line, 3-play, 5-play, 10-play]. These are the +// published Ultimate X award schedules, NOT an interpolation. +func pokerNextMultiplier(cabinet, variant string, hands int, category string) int { + ix := 0 + switch { + case cabinet == CabinetUXSingle: + ix = 0 + case hands == 3: + ix = 1 + case hands == 5: + ix = 2 + case hands == 10: + ix = 3 + } + var m map[string][4]int + switch variant { + case "deuces_wild": + m = map[string][4]int{"Natural royal flush": {2, 2, 2, 4}, "Four deuces": {2, 2, 2, 4}, "Wild royal flush": {2, 2, 2, 4}, "Five of a kind": {2, 2, 3, 3}, "Straight flush": {11, 12, 12, 12}, "Four of a kind": {7, 7, 7, 7}, "Full house": {5, 5, 5, 5}, "Flush": {5, 5, 5, 5}, "Straight": {3, 3, 3, 3}, "Three of a kind": {2, 2, 2, 2}} + case "joker_poker": + m = map[string][4]int{"Natural royal flush": {2, 2, 3, 4}, "Five of a kind": {2, 2, 3, 4}, "Wild royal flush": {2, 2, 3, 4}, "Straight flush": {2, 2, 3, 4}, "Four of a kind": {2, 2, 2, 2}, "Full house": {6, 7, 7, 7}, "Flush": {6, 6, 6, 6}, "Straight": {5, 5, 5, 5}, "Three of a kind": {4, 4, 4, 4}, "Two pair": {3, 3, 3, 3}, "Kings or better": {2, 2, 2, 2}} + case "double_bonus", "double_double_bonus": + m = map[string][4]int{"Royal flush": {2, 2, 2, 4}, "Straight flush": {2, 2, 2, 4}, "Four aces": {2, 2, 2, 4}, "Four 2s-4s": {2, 2, 2, 4}, "Four aces with 2-4 kicker": {2, 2, 2, 4}, "Four 2s-4s with A-4 kicker": {2, 2, 2, 4}, "Four 5s-Ks": {2, 2, 3, 3}, "Full house": {12, 12, 12, 12}, "Flush": {10, 10, 10, 10}, "Straight": {7, 8, 8, 8}, "Three of a kind": {4, 4, 4, 4}, "Two pair": {3, 3, 3, 3}, "Jacks or better": {2, 2, 2, 2}} + if variant == "double_double_bonus" { + for _, k := range []string{"Four aces", "Four 2s-4s", "Four aces with 2-4 kicker", "Four 2s-4s with A-4 kicker"} { + row := m[k] + row[0] = 4 + m[k] = row + } + m["Royal flush"] = [4]int{4, 2, 2, 4} + m["Straight flush"] = [4]int{4, 2, 2, 4} + m["Four 5s-Ks"] = [4]int{4, 2, 3, 3} + } + default: + m = map[string][4]int{"Royal flush": {2, 2, 2, 7}, "Straight flush": {2, 2, 2, 7}, "Four of a kind": {2, 2, 3, 3}, "Full house": {12, 12, 12, 12}, "Flush": {10, 11, 11, 11}, "Straight": {7, 7, 7, 7}, "Three of a kind": {4, 4, 4, 4}, "Two pair": {3, 3, 3, 3}, "Jacks or better": {2, 2, 2, 2}} + } + if variant == "bonus_poker" { + for _, k := range []string{"Royal flush", "Straight flush", "Four aces", "Four 2s-4s", "Four 5s-Ks"} { + m[k] = [4]int{3, 2, 2, 4} + } + m["Four 5s-Ks"] = [4]int{3, 2, 3, 3} + m["Straight"] = [4]int{7, 8, 8, 8} + } + if row, ok := m[category]; ok { + return row[ix] + } + return 1 +} + +// FeatureRules names each feature's published cost and trigger behavior. +func (c VideoPokerConfig) FeatureRules() string { + switch cabinetFeature(c.Normalize().Cabinet) { + case PokerUltimateX: + return "Ultimate X: feature on costs five extra coins per hand. Each winning line earns the NEXT hand's multiplier for that line; a current multiplier never multiplies another multiplier. The feature is not required to use a multiplier that is already showing. Multipliers stay with the machine until they are used." + case PokerSuperTimesPay: + return "Super Times Pay: feature on costs one extra coin per hand. Before the deal a 1-in-15 trigger awards one multiplier shared by every line: 2x/3x/4x/5x/8x/10x with probabilities 17/33/16/24/6/4 percent." + case PokerHotRoll: + return "Hot Roll: feature on costs five extra coins per hand. Before the deal a 1-in-6 trigger rolls two fair dice; their sum multiplies every line this round." + } + return "This cabinet carries no bonus feature." +} + +// FeatureFeeCoins is the extra coin cost per hand while the feature is on. +func (c VideoPokerConfig) FeatureFeeCoins() int { + switch cabinetFeature(c.Normalize().Cabinet) { + case PokerUltimateX, PokerHotRoll: + return 5 + case PokerSuperTimesPay: + return 1 + } + return 0 +} + +// pokerCoinPay is the total coin return of one line: rates are per coin, and +// the 52-card natural royal pays the flat 250-per-coin table below max bet. +func pokerCoinPay(category string, perCoin, coins int) int { + if coins < 1 || coins > 5 { + return 0 + } + if (category == "Royal flush" || category == "Natural royal flush") && coins < 5 { + return 250 * coins + } + return perCoin * coins +} + +// PayRows renders the pay table for the selected game and hand count. +func (c VideoPokerConfig) PayRows(hands int) []PokerPayRow { + c = c.Normalize() + schedule, ok := pokerPayTable(c) + if !ok { + return nil + } + var rows []PokerPayRow + for _, k := range pokerCategories { + if n := schedule.pays[k]; n > 0 { + row := PokerPayRow{Name: k, NextAward: c.NextAward(hands, k)} + for i := range row.Coins { + row.Coins[i] = pokerCoinPay(k, n, i+1) + } + rows = append(rows, row) + } + } + return rows +} + +// pokerRandomMultiplier rolls the deal-time bonus of Super Times Pay and +// Hot Roll; 1 means no multiplier. Hot Roll returns the dice that produced it. +func pokerRandomMultiplier(feature string, rng *rand.Rand) (int, [2]int) { + if feature == PokerHotRoll && rng.Intn(6) == 0 { + dice := [2]int{rng.Intn(6) + 1, rng.Intn(6) + 1} + return dice[0] + dice[1], dice + } + if feature == PokerSuperTimesPay && rng.Intn(15) == 0 { + n := rng.Intn(100) + for _, v := range []struct{ n, m int }{{17, 2}, {33, 3}, {16, 4}, {24, 5}, {6, 8}, {4, 10}} { + if n < v.n { + return v.m, [2]int{} + } + n -= v.n + } + } + return 1, [2]int{} +} + +// pokerHelpTopic names the help page the "rules" command opens while seated +// at this cabinet; every shipped cabinet type has its own page. +func pokerHelpTopic(c VideoPokerConfig) string { + switch c.Normalize().Cabinet { + case CabinetUXSingle: + return "ultimate x single line" + case CabinetUXMulti: + return "ultimate x multi hand" + case CabinetSuperTimesPay: + return "super times pay" + case CabinetHotRoll: + return "hot roll" + } + return "video poker" +} + +// FeatureLabel is the feature's display name for badges and menus. +func (c VideoPokerConfig) FeatureLabel() string { + switch cabinetFeature(c.Normalize().Cabinet) { + case PokerUltimateX: + return "Ultimate X" + case PokerSuperTimesPay: + return "Super Times Pay" + case PokerHotRoll: + return "Hot Roll" + } + return "Classic" +} |
