diff --git a/README.md b/README.md index ad9ed84a..7d81a6b0 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ Slots games server. Releases functionality for Megajack, Novomatic, NetEnt, BetS Server provides HTTP-based API for popular slots and have well-optimized performance for thousands requests per second. Can be deployed on dedicated server or as portable application for Linux or Windows. ```text -total: 137 games, 71 algorithms, 9 providers -AGT: 55 games +total: 138 games, 72 algorithms, 9 providers +AGT: 56 games Aristocrat: 4 games BetSoft: 3 games Megajack: 3 games @@ -30,6 +30,7 @@ Slotopol: 4 games *Last added games*: +* '[Valentine's Day](https://demo.agtsoftware.com/games/agt/valentine)' AGT 5x3 videoslot * '[Halloween](https://demo.agtsoftware.com/games/agt/halloween)' AGT 3x3 videoslot * '[Merry Christmas](https://demo.agtsoftware.com/games/agt/christmas)' AGT 3x3 videoslot * '[Ice Fruits](https://demo.agtsoftware.com/games/agt/megaice)' AGT 5x3 videoslot diff --git a/cmd/links.go b/cmd/links.go index 5343b8fe..31cdadb8 100644 --- a/cmd/links.go +++ b/cmd/links.go @@ -87,6 +87,7 @@ import ( _ "github.com/slotopol/server/game/slot/agt/shiningstars" _ "github.com/slotopol/server/game/slot/agt/shiningstars100" _ "github.com/slotopol/server/game/slot/agt/suncity" + _ "github.com/slotopol/server/game/slot/agt/valentinesday" _ "github.com/slotopol/server/game/slot/agt/valkyrie" _ "github.com/slotopol/server/game/slot/agt/wizard" ) diff --git a/cmd/list.go b/cmd/list.go index 7e4855f7..6bfc2821 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -139,6 +139,9 @@ func FormatGameInfo(gi *game.GameInfo, ai int) string { if gi.GP&game.GPbsym > 0 { b.WriteString(", has big symbols") } + if gi.GP&game.GPfill > 0 { + b.WriteString(", has multiplier on filled screen") + } } if fRTP && len(gi.RTP) > 0 { b.WriteString(", RTP: ") diff --git a/docs/list-all.md b/docs/list-all.md index 97f6acd8..b0074b62 100644 --- a/docs/list-all.md +++ b/docs/list-all.md @@ -133,6 +133,7 @@ 'Tropic Hot' AGT 3x3 videoslot 'Ultra Hot' Novomatic 3x3 videoslot 'Unicorn Magic' Novomatic 5x3 videoslot +'Valentine's Day' AGT 5x3 videoslot 'Valkyrie' AGT 5x3 videoslot 'Viking's Treasure' NetEnt 5x3 videoslot 'Voodoo Vibes' NetEnt 5x3 videoslot @@ -140,8 +141,8 @@ 'Wild Witches' NetEnt 5x3 videoslot 'Wizard' AGT 5x4 videoslot -total: 137 games, 71 algorithms, 9 providers -AGT: 55 games +total: 138 games, 72 algorithms, 9 providers +AGT: 56 games Aristocrat: 4 games BetSoft: 3 games Megajack: 3 games diff --git a/game/linkdata.go b/game/linkdata.go index 5f239cf4..d2d4d425 100644 --- a/game/linkdata.go +++ b/game/linkdata.go @@ -11,19 +11,20 @@ import ( type GP uint const ( // Game properties - GPsel GP = 0b_0000_0000_0001 // user can change lines - GPjack GP = 0b_0000_0000_0010 // cumulative jackpot is present - GPfgno GP = 0 // free games are absent - GPfghas GP = 0b_0000_0000_0100 // non-retriggered free games are present - GPretrig GP = 0b_0000_0000_1000 // free games are present and can be retriggered - GPfgmult GP = 0b_0000_0001_0000 // any multipliers on free games - GPfgreel GP = 0b_0000_0010_0000 // separate reels on free games - GPscat GP = 0b_0000_0100_0000 // has scatters - GPwild GP = 0b_0000_1000_0000 // has wild symbols - GPrwild GP = 0b_0001_0000_0000 // has reel wild symbols - GPbwild GP = 0b_0010_0000_0000 // has big wild (3x3) - GPwmult GP = 0b_0100_0000_0000 // has multiplier on wilds - GPbsym GP = 0b_1000_0000_0000 // has big symbol (usually 3x3 in the center on free games) + GPsel GP = 0b_0000_0000_0000_0001 // user can change lines + GPjack GP = 0b_0000_0000_0000_0010 // cumulative jackpot is present + GPfgno GP = 0 // free games are absent + GPfghas GP = 0b_0000_0000_0000_0100 // non-retriggered free games are present + GPretrig GP = 0b_0000_0000_0000_1000 // free games are present and can be retriggered + GPfgmult GP = 0b_0000_0000_0001_0000 // any multipliers on free games + GPfgreel GP = 0b_0000_0000_0010_0000 // separate reels on free games + GPscat GP = 0b_0000_0000_0100_0000 // has scatters + GPwild GP = 0b_0000_0000_1000_0000 // has wild symbols + GPrwild GP = 0b_0000_0001_0000_0000 // has reel wild symbols + GPbwild GP = 0b_0000_0010_0000_0000 // has big wild (3x3) + GPwmult GP = 0b_0000_0100_0000_0000 // has multiplier on wilds + GPbsym GP = 0b_0000_1000_0000_0000 // has big symbol (usually 3x3 in the center on free games) + GPfill GP = 0b_0001_0000_0000_0000 // has multiplier on filled screen ) type ( diff --git a/game/slot/agt/doubleice/doubleice_link.go b/game/slot/agt/doubleice/doubleice_link.go index 4ab25af3..42f408d7 100644 --- a/game/slot/agt/doubleice/doubleice_link.go +++ b/game/slot/agt/doubleice/doubleice_link.go @@ -11,7 +11,8 @@ var Info = game.GameInfo{ {Prov: "AGT", Name: "Double Ice"}, {Prov: "AGT", Name: "Double Hot"}, // see: https://demo.agtsoftware.com/games/agt/double }, - GP: game.GPfgno, + GP: game.GPfgno | + game.GPfill, SX: 3, SY: 3, SN: len(LinePay), diff --git a/game/slot/agt/valentinesday/valentinesday_calc.go b/game/slot/agt/valentinesday/valentinesday_calc.go new file mode 100644 index 00000000..b2ad0fbd --- /dev/null +++ b/game/slot/agt/valentinesday/valentinesday_calc.go @@ -0,0 +1,29 @@ +package valentinesday + +import ( + "context" + "fmt" + "time" + + "github.com/slotopol/server/game/slot" +) + +func CalcStat(ctx context.Context, mrtp float64) float64 { + var reels, _ = slot.FindReels(ReelsMap, mrtp) + var g = NewGame() + var sln float64 = 1 + g.Sel = int(sln) + var s slot.Stat + + var dur = slot.ScanReels5x(ctx, &s, g, reels, + time.Tick(2*time.Second), time.Tick(2*time.Second)) + + var reshuf = float64(s.Reshuffles) + var lrtp, srtp = s.LinePay / reshuf / sln * 100, s.ScatPay / reshuf / sln * 100 + var rtpsym = lrtp + srtp + fmt.Printf("completed %.5g%%, selected %d lines, time spent %v\n", reshuf/float64(s.Planned())*100, g.Sel, dur) + fmt.Printf("reels lengths [%d, %d, %d, %d, %d], total reshuffles %d\n", + len(reels.Reel(1)), len(reels.Reel(2)), len(reels.Reel(3)), len(reels.Reel(4)), len(reels.Reel(5)), reels.Reshuffles()) + fmt.Printf("RTP = %.5g(lined) + %.5g(scatter) = %.6f%%\n", lrtp, srtp, rtpsym) + return rtpsym +} diff --git a/game/slot/agt/valentinesday/valentinesday_link.go b/game/slot/agt/valentinesday/valentinesday_link.go new file mode 100644 index 00000000..fce96077 --- /dev/null +++ b/game/slot/agt/valentinesday/valentinesday_link.go @@ -0,0 +1,26 @@ +//go:build !prod || full || agt + +package valentinesday + +import ( + "github.com/slotopol/server/game" +) + +var Info = game.GameInfo{ + Aliases: []game.GameAlias{ + {Prov: "AGT", Name: "Valentine's Day"}, + }, + GP: game.GPfgno | + game.GPscat | + game.GPfill, + SX: 5, + SY: 3, + SN: len(LinePay), + LN: len(BetLines), + BN: 0, + RTP: game.MakeRtpList(ReelsMap), +} + +func init() { + Info.SetupFactory(func() any { return NewGame() }, CalcStat) +} diff --git a/game/slot/agt/valentinesday/valentinesday_reel.yaml b/game/slot/agt/valentinesday/valentinesday_reel.yaml new file mode 100644 index 00000000..87ccbcba --- /dev/null +++ b/game/slot/agt/valentinesday/valentinesday_reel.yaml @@ -0,0 +1,212 @@ + +# RTP list of available reels: +# 90.501440 +# 91.076962 +# 91.536036 +# 92.112735 +# 92.534456 +# 93.189474 +# 94.009439 +# 94.489085 +# 94.706484 +# 95.057793 +# 96.027857 +# 96.935829 +# 97.780407 +# 98.566342 +# 99.575261 +# 100.166759 +# 100.538917 +# 102.105221 +# 104.587955 +# 107.700329 +# 110.212934 + +# reels lengths [36, 36, 36, 36, 36], total reshuffles 60466176 +# RTP = 79.442(lined) + 11.06(scatter) = 90.501440% +90.501440: + - [3, 4, 4, 4, 4, 4, 8, 8, 8, 8, 2, 2, 3, 8, 8, 8, 5, 5, 5, 5, 5, 2, 3, 6, 6, 6, 6, 6, 1, 7, 7, 7, 7, 7, 3, 1] + - [8, 8, 8, 3, 4, 4, 4, 4, 4, 2, 1, 8, 8, 8, 8, 5, 5, 5, 5, 5, 3, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 3, 1, 2, 3, 2] + - [6, 6, 6, 6, 6, 8, 8, 8, 8, 1, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 3, 3, 1, 7, 7, 7, 7, 7, 3, 2, 8, 8, 8, 2] + - [4, 4, 4, 4, 4, 1, 3, 2, 2, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 3, 8, 8, 8, 3, 8, 8, 8, 8, 1, 3, 2, 6, 6, 6, 6, 6] + - [2, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 3, 2, 3, 1, 8, 8, 8, 8, 3, 6, 6, 6, 6, 6, 8, 8, 8, 7, 7, 7, 7, 7, 2, 1, 3] + +# reels lengths [36, 36, 36, 35, 36], total reshuffles 58786560 +# RTP = 79.816(lined) + 11.261(scatter) = 91.076962% +91.076962: + - [3, 4, 4, 4, 4, 4, 8, 8, 8, 8, 2, 2, 3, 8, 8, 8, 5, 5, 5, 5, 5, 2, 3, 6, 6, 6, 6, 6, 1, 7, 7, 7, 7, 7, 3, 1] + - [8, 8, 8, 3, 4, 4, 4, 4, 4, 2, 1, 8, 8, 8, 8, 5, 5, 5, 5, 5, 3, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 3, 1, 2, 3, 2] + - [6, 6, 6, 6, 6, 8, 8, 8, 8, 1, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 3, 3, 1, 7, 7, 7, 7, 7, 3, 2, 8, 8, 8, 2] + - [6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 3, 2, 1, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 1, 3, 5, 5, 5, 5, 5, 3, 2, 3, 2] + - [2, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 3, 2, 3, 1, 8, 8, 8, 8, 3, 6, 6, 6, 6, 6, 8, 8, 8, 7, 7, 7, 7, 7, 2, 1, 3] + +# reels lengths [36, 36, 36, 34, 35], total reshuffles 55520640 +# RTP = 79.854(lined) + 11.682(scatter) = 91.536036% +91.536036: + - [3, 4, 4, 4, 4, 4, 8, 8, 8, 8, 2, 2, 3, 8, 8, 8, 5, 5, 5, 5, 5, 2, 3, 6, 6, 6, 6, 6, 1, 7, 7, 7, 7, 7, 3, 1] + - [8, 8, 8, 3, 4, 4, 4, 4, 4, 2, 1, 8, 8, 8, 8, 5, 5, 5, 5, 5, 3, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 3, 1, 2, 3, 2] + - [6, 6, 6, 6, 6, 8, 8, 8, 8, 1, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 3, 3, 1, 7, 7, 7, 7, 7, 3, 2, 8, 8, 8, 2] + - [8, 8, 8, 8, 8, 3, 2, 1, 3, 2, 2, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 1, 3, 3, 6, 6, 6, 6, 6] + - [2, 3, 2, 2, 6, 6, 6, 6, 6, 3, 1, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 1, 5, 5, 5, 5, 5, 3, 8, 8, 8, 8, 8, 8, 3] + +# reels lengths [36, 35, 36, 36, 36], total reshuffles 58786560 +# RTP = 80.852(lined) + 11.261(scatter) = 92.112735% +92.112735: + - [3, 4, 4, 4, 4, 4, 8, 8, 8, 8, 2, 2, 3, 8, 8, 8, 5, 5, 5, 5, 5, 2, 3, 6, 6, 6, 6, 6, 1, 7, 7, 7, 7, 7, 3, 1] + - [7, 7, 7, 7, 7, 1, 3, 2, 3, 3, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 3, 2, 8, 8, 8, 8, 8, 8, 1, 2, 5, 5, 5, 5, 5] + - [6, 6, 6, 6, 6, 8, 8, 8, 8, 1, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 3, 3, 1, 7, 7, 7, 7, 7, 3, 2, 8, 8, 8, 2] + - [4, 4, 4, 4, 4, 1, 3, 2, 2, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 3, 8, 8, 8, 3, 8, 8, 8, 8, 1, 3, 2, 6, 6, 6, 6, 6] + - [2, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 3, 2, 3, 1, 8, 8, 8, 8, 3, 6, 6, 6, 6, 6, 8, 8, 8, 7, 7, 7, 7, 7, 2, 1, 3] + +# reels lengths [35, 36, 36, 35, 36], total reshuffles 57153600 +# RTP = 81.069(lined) + 11.466(scatter) = 92.534456% +92.534456: + - [8, 8, 8, 8, 8, 8, 3, 3, 6, 6, 6, 6, 6, 2, 3, 1, 5, 5, 5, 5, 5, 2, 2, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 3] + - [8, 8, 8, 3, 4, 4, 4, 4, 4, 2, 1, 8, 8, 8, 8, 5, 5, 5, 5, 5, 3, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 3, 1, 2, 3, 2] + - [6, 6, 6, 6, 6, 8, 8, 8, 8, 1, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 3, 3, 1, 7, 7, 7, 7, 7, 3, 2, 8, 8, 8, 2] + - [6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 3, 2, 1, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 1, 3, 5, 5, 5, 5, 5, 3, 2, 3, 2] + - [2, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 3, 2, 3, 1, 8, 8, 8, 8, 3, 6, 6, 6, 6, 6, 8, 8, 8, 7, 7, 7, 7, 7, 2, 1, 3] + +# reels lengths [36, 36, 35, 34, 35], total reshuffles 53978400 +# RTP = 81.296(lined) + 11.894(scatter) = 93.189474% +93.189474: + - [3, 4, 4, 4, 4, 4, 8, 8, 8, 8, 2, 2, 3, 8, 8, 8, 5, 5, 5, 5, 5, 2, 3, 6, 6, 6, 6, 6, 1, 7, 7, 7, 7, 7, 3, 1] + - [8, 8, 8, 3, 4, 4, 4, 4, 4, 2, 1, 8, 8, 8, 8, 5, 5, 5, 5, 5, 3, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 3, 1, 2, 3, 2] + - [8, 8, 8, 8, 8, 8, 2, 1, 2, 4, 4, 4, 4, 4, 3, 6, 6, 6, 6, 6, 3, 2, 1, 5, 5, 5, 5, 5, 3, 3, 7, 7, 7, 7, 7] + - [8, 8, 8, 8, 8, 3, 2, 1, 3, 2, 2, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 1, 3, 3, 6, 6, 6, 6, 6] + - [2, 3, 2, 2, 6, 6, 6, 6, 6, 3, 1, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 1, 5, 5, 5, 5, 5, 3, 8, 8, 8, 8, 8, 8, 3] + +# reels lengths [35, 35, 36, 36, 35], total reshuffles 55566000 +# RTP = 82.336(lined) + 11.673(scatter) = 94.009439% +94.009439: + - [8, 8, 8, 8, 8, 8, 3, 3, 6, 6, 6, 6, 6, 2, 3, 1, 5, 5, 5, 5, 5, 2, 2, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 3] + - [7, 7, 7, 7, 7, 1, 3, 2, 3, 3, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 3, 2, 8, 8, 8, 8, 8, 8, 1, 2, 5, 5, 5, 5, 5] + - [6, 6, 6, 6, 6, 8, 8, 8, 8, 1, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 3, 3, 1, 7, 7, 7, 7, 7, 3, 2, 8, 8, 8, 2] + - [4, 4, 4, 4, 4, 1, 3, 2, 2, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 3, 8, 8, 8, 3, 8, 8, 8, 8, 1, 3, 2, 6, 6, 6, 6, 6] + - [2, 3, 2, 2, 6, 6, 6, 6, 6, 3, 1, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 1, 5, 5, 5, 5, 5, 3, 8, 8, 8, 8, 8, 8, 3] + +# reels lengths [35, 35, 36, 35, 36], total reshuffles 55566000 +# RTP = 82.816(lined) + 11.673(scatter) = 94.489085% +94.489085: + - [8, 8, 8, 8, 8, 8, 3, 3, 6, 6, 6, 6, 6, 2, 3, 1, 5, 5, 5, 5, 5, 2, 2, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 3] + - [7, 7, 7, 7, 7, 1, 3, 2, 3, 3, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 3, 2, 8, 8, 8, 8, 8, 8, 1, 2, 5, 5, 5, 5, 5] + - [6, 6, 6, 6, 6, 8, 8, 8, 8, 1, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 3, 3, 1, 7, 7, 7, 7, 7, 3, 2, 8, 8, 8, 2] + - [6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 3, 2, 1, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 1, 3, 5, 5, 5, 5, 5, 3, 2, 3, 2] + - [2, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 3, 2, 3, 1, 8, 8, 8, 8, 3, 6, 6, 6, 6, 6, 8, 8, 8, 7, 7, 7, 7, 7, 2, 1, 3] + +# reels lengths [36, 35, 35, 35, 36], total reshuffles 55566000 +# RTP = 83.033(lined) + 11.673(scatter) = 94.706484% +94.706484: + - [3, 4, 4, 4, 4, 4, 8, 8, 8, 8, 2, 2, 3, 8, 8, 8, 5, 5, 5, 5, 5, 2, 3, 6, 6, 6, 6, 6, 1, 7, 7, 7, 7, 7, 3, 1] + - [7, 7, 7, 7, 7, 1, 3, 2, 3, 3, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 3, 2, 8, 8, 8, 8, 8, 8, 1, 2, 5, 5, 5, 5, 5] + - [8, 8, 8, 8, 8, 8, 2, 1, 2, 4, 4, 4, 4, 4, 3, 6, 6, 6, 6, 6, 3, 2, 1, 5, 5, 5, 5, 5, 3, 3, 7, 7, 7, 7, 7] + - [6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 3, 2, 1, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 1, 3, 5, 5, 5, 5, 5, 3, 2, 3, 2] + - [2, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 3, 2, 3, 1, 8, 8, 8, 8, 3, 6, 6, 6, 6, 6, 8, 8, 8, 7, 7, 7, 7, 7, 2, 1, 3] + +# reels lengths [35, 35, 36, 35, 33], total reshuffles 50935500 +# RTP = 82.712(lined) + 12.345(scatter) = 95.057793% +95.057793: + - [8, 8, 8, 8, 8, 8, 3, 3, 6, 6, 6, 6, 6, 2, 3, 1, 5, 5, 5, 5, 5, 2, 2, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 3] + - [7, 7, 7, 7, 7, 1, 3, 2, 3, 3, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 3, 2, 8, 8, 8, 8, 8, 8, 1, 2, 5, 5, 5, 5, 5] + - [6, 6, 6, 6, 6, 8, 8, 8, 8, 1, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 3, 3, 1, 7, 7, 7, 7, 7, 3, 2, 8, 8, 8, 2] + - [6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 3, 2, 1, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 1, 3, 5, 5, 5, 5, 5, 3, 2, 3, 2] + - [3, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 1, 2, 5, 5, 5, 5, 5, 2, 2, 3, 3, 3, 6, 6, 6, 6, 6, 8, 8, 8, 8] + +# reels lengths [35, 35, 35, 36, 35], total reshuffles 54022500 +# RTP = 84.143(lined) + 11.885(scatter) = 96.027857% +96.027857: + - [8, 8, 8, 8, 8, 8, 3, 3, 6, 6, 6, 6, 6, 2, 3, 1, 5, 5, 5, 5, 5, 2, 2, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 3] + - [7, 7, 7, 7, 7, 1, 3, 2, 3, 3, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 3, 2, 8, 8, 8, 8, 8, 8, 1, 2, 5, 5, 5, 5, 5] + - [8, 8, 8, 8, 8, 8, 2, 1, 2, 4, 4, 4, 4, 4, 3, 6, 6, 6, 6, 6, 3, 2, 1, 5, 5, 5, 5, 5, 3, 3, 7, 7, 7, 7, 7] + - [4, 4, 4, 4, 4, 1, 3, 2, 2, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 3, 8, 8, 8, 3, 8, 8, 8, 8, 1, 3, 2, 6, 6, 6, 6, 6] + - [2, 3, 2, 2, 6, 6, 6, 6, 6, 3, 1, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 1, 5, 5, 5, 5, 5, 3, 8, 8, 8, 8, 8, 8, 3] + +# reels lengths [35, 35, 35, 35, 35], total reshuffles 52521875 +# RTP = 84.837(lined) + 12.099(scatter) = 96.935829% +96.935829: + - [8, 8, 8, 8, 8, 8, 3, 3, 6, 6, 6, 6, 6, 2, 3, 1, 5, 5, 5, 5, 5, 2, 2, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 3] + - [7, 7, 7, 7, 7, 1, 3, 2, 3, 3, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 3, 2, 8, 8, 8, 8, 8, 8, 1, 2, 5, 5, 5, 5, 5] + - [8, 8, 8, 8, 8, 8, 2, 1, 2, 4, 4, 4, 4, 4, 3, 6, 6, 6, 6, 6, 3, 2, 1, 5, 5, 5, 5, 5, 3, 3, 7, 7, 7, 7, 7] + - [6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 3, 2, 1, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 1, 3, 5, 5, 5, 5, 5, 3, 2, 3, 2] + - [2, 3, 2, 2, 6, 6, 6, 6, 6, 3, 1, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 1, 5, 5, 5, 5, 5, 3, 8, 8, 8, 8, 8, 8, 3] + +# reels lengths [35, 35, 34, 35, 35], total reshuffles 51021250 +# RTP = 85.454(lined) + 12.326(scatter) = 97.780407% +97.780407: + - [8, 8, 8, 8, 8, 8, 3, 3, 6, 6, 6, 6, 6, 2, 3, 1, 5, 5, 5, 5, 5, 2, 2, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 3] + - [7, 7, 7, 7, 7, 1, 3, 2, 3, 3, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 3, 2, 8, 8, 8, 8, 8, 8, 1, 2, 5, 5, 5, 5, 5] + - [2, 3, 7, 7, 7, 7, 7, 3, 8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 1, 3, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 1] + - [6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 3, 2, 1, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 1, 3, 5, 5, 5, 5, 5, 3, 2, 3, 2] + - [2, 3, 2, 2, 6, 6, 6, 6, 6, 3, 1, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 1, 5, 5, 5, 5, 5, 3, 8, 8, 8, 8, 8, 8, 3] + +# reels lengths [35, 34, 35, 34, 35], total reshuffles 49563500 +# RTP = 86.009(lined) + 12.557(scatter) = 98.566342% +98.566342: + - [8, 8, 8, 8, 8, 8, 3, 3, 6, 6, 6, 6, 6, 2, 3, 1, 5, 5, 5, 5, 5, 2, 2, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 3] + - [8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 3, 5, 5, 5, 5, 5, 3, 2, 3, 1, 4, 4, 4, 4, 4, 1, 3, 2, 7, 7, 7, 7, 7] + - [8, 8, 8, 8, 8, 8, 2, 1, 2, 4, 4, 4, 4, 4, 3, 6, 6, 6, 6, 6, 3, 2, 1, 5, 5, 5, 5, 5, 3, 3, 7, 7, 7, 7, 7] + - [8, 8, 8, 8, 8, 3, 2, 1, 3, 2, 2, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 1, 3, 3, 6, 6, 6, 6, 6] + - [2, 3, 2, 2, 6, 6, 6, 6, 6, 3, 1, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 1, 5, 5, 5, 5, 5, 3, 8, 8, 8, 8, 8, 8, 3] + +# reels lengths [34, 35, 34, 35, 34], total reshuffles 48147400 +# RTP = 86.783(lined) + 12.792(scatter) = 99.575261% +99.575261: + - [5, 5, 5, 5, 5, 2, 4, 4, 4, 4, 4, 2, 8, 8, 8, 8, 8, 3, 2, 3, 1, 3, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 1, 3] + - [7, 7, 7, 7, 7, 1, 3, 2, 3, 3, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 3, 2, 8, 8, 8, 8, 8, 8, 1, 2, 5, 5, 5, 5, 5] + - [2, 3, 7, 7, 7, 7, 7, 3, 8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 1, 3, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 1] + - [6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 3, 2, 1, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 1, 3, 5, 5, 5, 5, 5, 3, 2, 3, 2] + - [8, 8, 8, 8, 8, 3, 3, 1, 7, 7, 7, 7, 7, 3, 6, 6, 6, 6, 6, 3, 4, 4, 4, 4, 4, 2, 2, 5, 5, 5, 5, 5, 2, 1] + +# reels lengths [35, 34, 34, 34, 34], total reshuffles 46771760 +# RTP = 87.136(lined) + 13.031(scatter) = 100.166759% +100.166759: + - [8, 8, 8, 8, 8, 8, 3, 3, 6, 6, 6, 6, 6, 2, 3, 1, 5, 5, 5, 5, 5, 2, 2, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 3] + - [8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 3, 5, 5, 5, 5, 5, 3, 2, 3, 1, 4, 4, 4, 4, 4, 1, 3, 2, 7, 7, 7, 7, 7] + - [2, 3, 7, 7, 7, 7, 7, 3, 8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 1, 3, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 1] + - [8, 8, 8, 8, 8, 3, 2, 1, 3, 2, 2, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 1, 3, 3, 6, 6, 6, 6, 6] + - [8, 8, 8, 8, 8, 3, 3, 1, 7, 7, 7, 7, 7, 3, 6, 6, 6, 6, 6, 3, 4, 4, 4, 4, 4, 2, 2, 5, 5, 5, 5, 5, 2, 1] + +# reels lengths [34, 34, 35, 34, 34], total reshuffles 46771760 +# RTP = 87.508(lined) + 13.031(scatter) = 100.538917% +100.538917: + - [5, 5, 5, 5, 5, 2, 4, 4, 4, 4, 4, 2, 8, 8, 8, 8, 8, 3, 2, 3, 1, 3, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 1, 3] + - [8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 3, 5, 5, 5, 5, 5, 3, 2, 3, 1, 4, 4, 4, 4, 4, 1, 3, 2, 7, 7, 7, 7, 7] + - [8, 8, 8, 8, 8, 8, 2, 1, 2, 4, 4, 4, 4, 4, 3, 6, 6, 6, 6, 6, 3, 2, 1, 5, 5, 5, 5, 5, 3, 3, 7, 7, 7, 7, 7] + - [8, 8, 8, 8, 8, 3, 2, 1, 3, 2, 2, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 1, 3, 3, 6, 6, 6, 6, 6] + - [8, 8, 8, 8, 8, 3, 3, 1, 7, 7, 7, 7, 7, 3, 6, 6, 6, 6, 6, 3, 4, 4, 4, 4, 4, 2, 2, 5, 5, 5, 5, 5, 2, 1] + +# reels lengths [34, 34, 34, 34, 34], total reshuffles 45435424 +# RTP = 88.832(lined) + 13.273(scatter) = 102.105221% +102.105221: + - [5, 5, 5, 5, 5, 2, 4, 4, 4, 4, 4, 2, 8, 8, 8, 8, 8, 3, 2, 3, 1, 3, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 1, 3] + - [8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 3, 5, 5, 5, 5, 5, 3, 2, 3, 1, 4, 4, 4, 4, 4, 1, 3, 2, 7, 7, 7, 7, 7] + - [2, 3, 7, 7, 7, 7, 7, 3, 8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 1, 3, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 1] + - [8, 8, 8, 8, 8, 3, 2, 1, 3, 2, 2, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 1, 3, 3, 6, 6, 6, 6, 6] + - [8, 8, 8, 8, 8, 3, 3, 1, 7, 7, 7, 7, 7, 3, 6, 6, 6, 6, 6, 3, 4, 4, 4, 4, 4, 2, 2, 5, 5, 5, 5, 5, 2, 1] + +# reels lengths [33, 34, 34, 34, 33], total reshuffles 42802056 +# RTP = 90.796(lined) + 13.792(scatter) = 104.587955% +104.587955: + - [3, 2, 3, 5, 5, 5, 5, 5, 3, 1, 8, 8, 8, 8, 7, 7, 7, 7, 7, 3, 4, 4, 4, 4, 4, 2, 6, 6, 6, 6, 6, 2, 1] + - [8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 3, 5, 5, 5, 5, 5, 3, 2, 3, 1, 4, 4, 4, 4, 4, 1, 3, 2, 7, 7, 7, 7, 7] + - [2, 3, 7, 7, 7, 7, 7, 3, 8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 1, 3, 4, 4, 4, 4, 4, 3, 5, 5, 5, 5, 5, 2, 1] + - [8, 8, 8, 8, 8, 3, 2, 1, 3, 2, 2, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 1, 3, 3, 6, 6, 6, 6, 6] + - [3, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 1, 2, 5, 5, 5, 5, 5, 2, 2, 3, 3, 3, 6, 6, 6, 6, 6, 8, 8, 8, 8] + +# reels lengths [33, 34, 34, 34, 33], total reshuffles 42802056 +# RTP = 93.908(lined) + 13.792(scatter) = 107.700329% +107.700329: + - [3, 2, 3, 5, 5, 5, 5, 5, 3, 1, 8, 8, 8, 8, 7, 7, 7, 7, 7, 3, 4, 4, 4, 4, 4, 2, 6, 6, 6, 6, 6, 2, 1] + - [8, 8, 8, 8, 8, 2, 6, 6, 6, 6, 6, 3, 5, 5, 5, 5, 5, 3, 2, 3, 1, 4, 4, 4, 4, 4, 1, 3, 2, 7, 7, 7, 7, 7] + - [6, 6, 6, 6, 6, 3, 3, 2, 3, 5, 5, 5, 5, 5, 1, 7, 7, 7, 7, 7, 1, 2, 4, 4, 4, 4, 4, 3, 2, 8, 8, 8, 8, 3] + - [8, 8, 8, 8, 8, 3, 2, 1, 3, 2, 2, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 1, 3, 3, 6, 6, 6, 6, 6] + - [3, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 1, 2, 5, 5, 5, 5, 5, 2, 2, 3, 3, 3, 6, 6, 6, 6, 6, 8, 8, 8, 8] + +# reels lengths [33, 33, 33, 33, 33], total reshuffles 39135393 +# RTP = 95.609(lined) + 14.604(scatter) = 110.212934% +110.212934: + - [3, 2, 3, 5, 5, 5, 5, 5, 3, 1, 8, 8, 8, 8, 7, 7, 7, 7, 7, 3, 4, 4, 4, 4, 4, 2, 6, 6, 6, 6, 6, 2, 1] + - [7, 7, 7, 7, 7, 2, 8, 8, 8, 8, 5, 5, 5, 5, 5, 3, 1, 6, 6, 6, 6, 6, 3, 2, 1, 3, 2, 3, 4, 4, 4, 4, 4] + - [8, 8, 8, 8, 7, 7, 7, 7, 7, 1, 2, 2, 3, 3, 5, 5, 5, 5, 5, 2, 4, 4, 4, 4, 4, 1, 6, 6, 6, 6, 6, 3, 3] + - [3, 5, 5, 5, 5, 5, 3, 2, 2, 8, 8, 8, 8, 2, 1, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 3, 3, 1, 4, 4, 4, 4, 4] + - [3, 4, 4, 4, 4, 4, 1, 7, 7, 7, 7, 7, 1, 2, 5, 5, 5, 5, 5, 2, 2, 3, 3, 3, 6, 6, 6, 6, 6, 8, 8, 8, 8] diff --git a/game/slot/agt/valentinesday/valentinesday_rule.go b/game/slot/agt/valentinesday/valentinesday_rule.go new file mode 100644 index 00000000..15336f66 --- /dev/null +++ b/game/slot/agt/valentinesday/valentinesday_rule.go @@ -0,0 +1,137 @@ +package valentinesday + +// See: https://demo.agtsoftware.com/games/agt/valentine + +import ( + _ "embed" + + "github.com/slotopol/server/game/slot" +) + +//go:embed valentinesday_reel.yaml +var reels []byte + +var ReelsMap = slot.ReadMap[*slot.Reels5x](reels) + +// Lined payment. +var LinePay = [8][5]float64{ + {}, // 1 scatter + {0, 0, 100, 1000, 5000}, // 2 angel + {0, 0, 40, 400, 1000}, // 3 nymph + {0, 0, 24, 60, 200}, // 4 soul + {0, 0, 20, 50, 200}, // 5 toy + {0, 0, 20, 50, 200}, // 6 balloon + {0, 5, 16, 40, 160}, // 7 hearts + {0, 0, 5, 20, 100}, // 8 medallion +} + +// Scatters payment. +var ScatPay = [5]float64{0, 0, 2, 12, 60} // 1 scatter + +// Bet lines +var BetLines = slot.BetLinesAgt5x3[:5] + +type Game struct { + slot.Slot5x3 `yaml:",inline"` +} + +// Declare conformity with SlotGame interface. +var _ slot.SlotGame = (*Game)(nil) + +func NewGame() *Game { + return &Game{ + Slot5x3: slot.Slot5x3{ + Sel: len(BetLines), + Bet: 1, + }, + } +} + +const scat = 1 + +func (g *Game) Scanner(screen slot.Screen, wins *slot.Wins) { + var scrn5x3 = screen.(*slot.Screen5x3) + g.ScanLined(scrn5x3, wins) + g.ScanScatters(scrn5x3, wins) +} + +func FillMult(screen *slot.Screen5x3) float64 { + var sym = screen[2][1] // center symbol + var r *[3]slot.Sym + if r = &screen[2]; r[0] != sym || r[2] != sym { + return 1 + } + var n float64 = 1 + if r = &screen[1]; r[0] == sym && r[1] == sym && r[2] == sym { + n++ + if r = &screen[0]; r[0] == sym && r[1] == sym && r[2] == sym { + n++ + } + } + if r = &screen[3]; r[0] == sym && r[1] == sym && r[2] == sym { + n++ + if r = &screen[4]; r[0] == sym && r[1] == sym && r[2] == sym { + n++ + } + } + if n < 3 { + return 1 + } + return n - 1 +} + +// Lined symbols calculation. +func (g *Game) ScanLined(screen *slot.Screen5x3, wins *slot.Wins) { + var fm float64 // fill mult + for li := 1; li <= g.Sel; li++ { + var line = BetLines[li-1] + + var numl slot.Pos = 5 + var syml = screen.Pos(1, line) + var x slot.Pos + for x = 2; x <= 5; x++ { + var sx = screen.Pos(x, line) + if sx != syml { + numl = x - 1 + break + } + } + + if pay := LinePay[syml-1][numl-1]; pay > 0 { + if fm == 0 { // lazy calculation + fm = FillMult(screen) + } + *wins = append(*wins, slot.WinItem{ + Pay: g.Bet * pay, + Mult: fm, + Sym: syml, + Num: numl, + Line: li, + XY: line.CopyL(numl), + }) + } + } +} + +// Scatters calculation. +func (g *Game) ScanScatters(screen *slot.Screen5x3, wins *slot.Wins) { + if count := screen.ScatNum(scat); count >= 3 { + var pay = ScatPay[count-1] + *wins = append(*wins, slot.WinItem{ + Pay: g.Bet * float64(g.Sel) * pay, + Mult: 1, + Sym: scat, + Num: count, + XY: screen.ScatPos(scat), + }) + } +} + +func (g *Game) Spin(screen slot.Screen, mrtp float64) { + var reels, _ = slot.FindReels(ReelsMap, mrtp) + screen.Spin(reels) +} + +func (g *Game) SetSel(sel int) error { + return g.SetSelNum(sel, len(BetLines)) +} diff --git a/game/slot/novomatic/ultrahot/ultrahot_link.go b/game/slot/novomatic/ultrahot/ultrahot_link.go index b172cef7..7e5c963a 100644 --- a/game/slot/novomatic/ultrahot/ultrahot_link.go +++ b/game/slot/novomatic/ultrahot/ultrahot_link.go @@ -10,7 +10,8 @@ var Info = game.GameInfo{ Aliases: []game.GameAlias{ {Prov: "Novomatic", Name: "Ultra Hot"}, }, - GP: game.GPfgno, + GP: game.GPfgno | + game.GPfill, SX: 3, SY: 3, SN: len(LinePay), diff --git a/go.mod b/go.mod index d2a8f285..6d31b9e7 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,7 @@ require ( golang.org/x/net v0.33.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect - google.golang.org/protobuf v1.36.0 // indirect + google.golang.org/protobuf v1.36.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 8eaaec74..94d6d101 100644 --- a/go.sum +++ b/go.sum @@ -164,8 +164,8 @@ golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/helper/valentinesday.lua b/helper/valentinesday.lua new file mode 100644 index 00000000..f261f4e4 --- /dev/null +++ b/helper/valentinesday.lua @@ -0,0 +1,28 @@ +local path = arg[0]:match("(.*[/\\])") +dofile(path.."lib/reelgen.lua") + +local symset = { + 2, -- 1 scatter + 2, -- 2 angel 5000 + 4, -- 3 nymph 1000 + 5, -- 4 soul 200 + 5, -- 5 toy 200 + 5, -- 6 balloon 200 + 5, -- 7 hearts 160 + 6, -- 8 medallion 100 +} + +local chunklen = { + 1, -- 1 scatter + 1, -- 2 angel + 1, -- 3 nymph + 6, -- 4 soul + 6, -- 5 toy + 6, -- 6 balloon + 6, -- 7 hearts + 6, -- 8 medallion +} + +math.randomseed(os.time()) +local reel, iter = makereelhot(symset, 3, {[1]=true}, chunklen) +printreel(reel, iter)