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" }