diff options
Diffstat (limited to 'internal/casino/house_edge_video_poker.go')
| -rw-r--r-- | internal/casino/house_edge_video_poker.go | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/internal/casino/house_edge_video_poker.go b/internal/casino/house_edge_video_poker.go new file mode 100644 index 0000000..52af719 --- /dev/null +++ b/internal/casino/house_edge_video_poker.go @@ -0,0 +1,82 @@ +package casino + +// Published optimal max-coin returns and royal frequencies. Source pages: +// https://wizardofodds.com/games/video-poker/tables/{jacks-or-better, +// bonus-poker,bonus-poker-deluxe,double-bonus,double-double-bonus, +// deuces-wild,joker-poker-kings-or-better}/ +// Below max coins we price the reduced royal using that schedule's strategy; +// this is a reference estimate rather than a second optimal-strategy solver. +type pokerReturn struct{ rtp, royal float64 } + +var pokerReturns = map[string]map[string]pokerReturn{ + "jacks_or_better": {"9/6": {.99543904, .00002476}, "9/5": {.984498, .000025}, "8/6": {.983927, .000025}, "8/5": {.97298434, .00002489}, "7/5": {.961472, .000025}}, + "bonus_poker": {"8/5": {.991660, .000025}, "7/5": {.980147, .000025}, "6/5": {.968687, .000025}}, + "bonus_poker_deluxe": {"9/6": {.996417, .000024}, "9/5": {.985495, .000025}, "8/6": {.984928, .000024}, "8/5": {.974009, .000025}, "7/5": {.962526, .000025}}, + "double_bonus": {"10/7": {1.001725, .000021}, "9/7": {.991065, .000021}, "9/6/5": {.978062, .000024}, "9/6/4": {.963754, .000024}, "9/5/4": {.952738, .000025}, "8/5": {.941897, .000025}}, + "double_double_bonus": {"10/6": {1.000670, .000025}, "9/6": {.989808, .000025}, "8/6": {.978949, .000025}, "9/5": {.978729, .000025}, "8/5": {.967861, .000025}}, + "deuces_wild": {"25/15/9/5/3/2": {1.007620, .000022}, "25/15/9/4/4/3": {.989131, .000023}, "20/12/10/4/4/3": {.975791, .000023}, "25/16/13/4/3/2": {.967651, .000023}}, + "joker_poker": {"50/17/7/5/3": {.980854, .000025}, "50/16/7/5/3": {.972349, .000025}, "50/15/7/5/3": {.963848, .000025}}, +} + +// Ultimate X is a continuing-game reference, not the value of cashing in a +// particular banked multiplier. Entries are single / three / five / ten hands. +// Sources: .../tables/ultimate-x-single-line/ and .../tables/ultimate-x/. +var pokerUXReturns = map[string]map[string][4]float64{ + "jacks_or_better": {"9/6": {.9972, 0, 0, 0}, "8/6": {.9857, .992978, .994008, .994220}, "8/5": {.9738, .978730, .979671, .979795}, "7/5": {.9623, .967211, .968142, .968267}}, + "bonus_poker": {"8/5": {.9921, .992828, .993408, .994034}, "7/5": {.9809, .981242, .981817, .982439}, "6/5": {.9694, 0, 0, 0}}, + "bonus_poker_deluxe": {"9/6": {.9996, 0, 0, 0}, "8/6": {.9882, .995712, .996777, .997022}, "8/5": {.9747, .981052, .982014, .982169}, "7/5": {.9632, .969549, .970502, .970658}}, + "double_bonus": {"9/7": {.9921, .988866, .989590, .990359}, "9/6/5": {.9811, .984517, .985227, .985987}, "9/6/4": {.9662, .973059, .973764, .974517}, "9/5/4": {.9539, .959471, .960118, .960810}}, + "double_double_bonus": {"9/6": {.9905, .997261, .997928, .998663}, "9/5": {.9793, .983633, .984253, .984940}, "8/5": {.9680, .972266, .972859, .973521}}, + "deuces_wild": {"25/15/9/4/4/3": {.9905, .991282, .992657, .994362}, "20/12/10/4/4/3": {.9788, .979863, .981217, .982817}, "25/16/13/4/3/2": {.9681, .969387, .970777, .972477}}, + "joker_poker": {"50/17/7/5/3": {.9824, .989856, .990144, .990445}, "50/16/7/5/3": {.9738, .981190, .981474, .981771}, "50/15/7/5/3": {.9652, .972528, .972808, .973102}}, +} + +func videoPokerHouseEdge(c TableConfig, bet string) HouseEdge { + r := c.VideoPoker.Normalize() + return pokerHouseEdge(r, bet, 5, true, CabinetHandCounts(r.Cabinet, r.Variant)[0]) +} + +func pokerHouseEdge(c VideoPokerConfig, bet string, coins int, feature bool, hands int) HouseEdge { + if bet != "main" { + return unknownEdge("unrecognized video poker wager") + } + c = c.Normalize() + ref, ok := pokerReturns[c.Variant][c.PayTable] + if !ok { + return unknownEdge("unrecognized poker schedule") + } + rtp := ref.rtp + if coins > 0 && coins < 5 { + rtp -= 550 * ref.royal + feature = false + } + if feature { + switch c.Feature { + case PokerUltimateX: + ix := 0 + if c.Cabinet == CabinetUXMulti { + switch hands { + case 3: + ix = 1 + case 5: + ix = 2 + case 10: + ix = 3 + default: + return unknownEdge("unsupported hand count") + } + } + rtp = pokerUXReturns[c.Variant][c.PayTable][ix] + if rtp == 0 { + return unknownEdge("unsupported Ultimate X schedule") + } + case PokerSuperTimesPay: + // E[multiplier] = 14/15 + (2*.17+3*.33+4*.16+5*.24+8*.06+10*.04)/15. + rtp *= (14.0/15 + 4.05/15) * 5 / 6 + case PokerHotRoll: + // Five extra coins buy 5/6 normal hands and 1/6 at mean 7x. + rtp *= (5.0/6 + 7.0/6) / 2 + } + } + return edge(1 - rtp) +} |
