Skip to content

Commit

Permalink
jewels4all game added.
Browse files Browse the repository at this point in the history
  • Loading branch information
schwarzlichtbezirk committed Apr 22, 2024
1 parent b464f70 commit c5796c1
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 37 deletions.
35 changes: 35 additions & 0 deletions config/links/jewels4all_linl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//go:build !prod || full || novomatic

package links

import (
"context"

"github.com/slotopol/server/game/jewels4all"
"github.com/spf13/pflag"
)

func init() {
FlagsSetters = append(FlagsSetters, func(flags *pflag.FlagSet) {
flags.Bool("jewels4all", false, "'Jewels 4 All' Novomatic 5x3 slots")
})
ScatIters = append(ScatIters, func(flags *pflag.FlagSet, ctx context.Context) {
if is, _ := flags.GetBool("jewels4all"); is {
var rn, _ = flags.GetString("reels")
jewels4all.CalcStat(ctx, rn)
}
})

for _, alias := range []string{
"jewels4all",
} {
GameAliases[alias] = "jewels4all"
}

GameFactory["jewels4all"] = func(rd string) any {
if _, ok := jewels4all.ChanceMap[rd]; ok {
return jewels4all.NewGame(rd)
}
return nil
}
}
1 change: 1 addition & 0 deletions config/links/justjewels_link.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func init() {

for _, alias := range []string{
"justjewels",
"justjewelsdeluxe",
} {
GameAliases[alias] = "justjewels"
}
Expand Down
File renamed without changes.
File renamed without changes.
14 changes: 7 additions & 7 deletions game/jewels/jewels.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,13 @@ var ReelsMap = map[string]*game.Reels5x{

// Lined payment.
var LinePay = [7][5]float64{
{0, 0, 20, 200, 2000}, // crown
{0, 0, 15, 100, 500}, // gold
{0, 0, 15, 100, 500}, // money
{0, 0, 10, 50, 200}, // ruby
{0, 0, 10, 50, 200}, // sapphire
{0, 0, 5, 25, 100}, // emerald
{0, 0, 5, 25, 100}, // amethyst
{0, 0, 20, 200, 2000}, // 1 crown
{0, 0, 15, 100, 500}, // 2 gold
{0, 0, 15, 100, 500}, // 3 money
{0, 0, 10, 50, 200}, // 4 ruby
{0, 0, 10, 50, 200}, // 5 sapphire
{0, 0, 5, 25, 100}, // 6 emerald
{0, 0, 5, 25, 100}, // 7 amethyst
}

const (
Expand Down
176 changes: 176 additions & 0 deletions game/jewels4all/jewels4all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package jewels4all

import "github.com/slotopol/server/game"

// RTP(no eu) = 67.344781%
// RTP(eu at y=1,5) = 1706.345577%
// RTP(eu at y=2,3,4) = 7818.930041%
// euro avr: rtpeu = 5373.896256%
var Reels = game.Reels5x{
{1, 1, 1, 2, 2, 2, 6, 6, 3, 3, 3, 7, 7, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7},
{1, 1, 1, 2, 2, 2, 6, 6, 3, 3, 3, 7, 7, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7},
{1, 1, 1, 2, 2, 2, 6, 6, 3, 3, 3, 7, 7, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7},
{1, 1, 1, 2, 2, 2, 6, 6, 3, 3, 3, 7, 7, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7},
{1, 1, 1, 2, 2, 2, 6, 6, 3, 3, 3, 7, 7, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7},
}

// Map with wild chances.
var ChanceMap = map[string]float64{
// RTP = 67.345(sym) + wc*5373.9(eu) = 90.019449%
"90": 1 / 237.,
// RTP = 67.345(sym) + wc*5373.9(eu) = 91.995681%
"92": 1 / 218.,
// RTP = 67.345(sym) + wc*5373.9(eu) = 93.948228%
"94": 1 / 202.,
// RTP = 67.345(sym) + wc*5373.9(eu) = 96.082194%
"96": 1 / 187.,
// RTP = 67.345(sym) + wc*5373.9(eu) = 98.052760%
"98": 1 / 175.,
// RTP = 67.345(sym) + wc*5373.9(eu) = 99.913849%
"100": 1 / 165.,
// RTP = 67.345(sym) + wc*5373.9(eu) = 110.335951%
"110": 1 / 125.,
}

// Lined payment.
var LinePay = [8][5]float64{
{0, 0, 20, 100, 1000}, // 1 crown
{0, 0, 10, 60, 500}, // 2 gold
{0, 0, 10, 60, 500}, // 3 money
{0, 0, 5, 40, 200}, // 4 ruby
{0, 0, 5, 40, 200}, // 5 sapphire
{0, 0, 5, 20, 100}, // 6 emerald
{0, 0, 5, 20, 100}, // 7 amethyst
{0, 0, 0, 0, 0}, // 8 euro
}

const (
jid = 1 // jackpot ID
)

// Jackpot win combinations.
var Jackpot = [8][5]int{
{0, 0, 0, 0, 0}, // 1 crown
{0, 0, 0, 0, 0}, // 2 gold
{0, 0, 0, 0, 0}, // 3 money
{0, 0, 0, 0, 0}, // 4 ruby
{0, 0, 0, 0, 0}, // 5 sapphire
{0, 0, 0, 0, 0}, // 6 emerald
{0, 0, 0, 0, 0}, // 7 amethyst
{0, 0, 0, 0, 0}, // 8 euro
}

type Game struct {
game.Slot5x3 `yaml:",inline"`
}

func NewGame(rd string) *Game {
return &Game{
Slot5x3: game.Slot5x3{
RD: rd,
SBL: game.MakeSblNum(5),
Bet: 1,
},
}
}

const wild = 8

var bl = game.BetLinesNvm10

func (g *Game) Scanner(screen game.Screen, ws *game.WinScan) {
g.ScanLined(screen, ws)
}

// Lined symbols calculation.
func (g *Game) ScanLined(screen game.Screen, ws *game.WinScan) {
var scrnwild game.Screen5x3 = *screen.(*game.Screen5x3)
for x := 1; x <= 5; x++ {
for y := 1; y <= 3; y++ {
if screen.At(x, y) == wild {
for i := max(0, x-2); i <= min(4, x); i++ {
for j := max(0, y-2); j <= min(2, y); j++ {
scrnwild[i][j] = wild
}
}
}
}
}

for li := g.SBL.Next(0); li != 0; li = g.SBL.Next(li) {
var line = bl.Line(li)

var sym3 = scrnwild.Pos(3, line)
var xy = game.NewLine5x()
var num = 1
xy.Set(3, line.At(3))
if sym2 := scrnwild.Pos(2, line); sym2 == sym3 || sym2 == wild || sym3 == wild {
if sym3 == wild {
sym3 = sym2
}
xy.Set(2, line.At(2))
num++
if sym1 := scrnwild.Pos(1, line); sym1 == sym3 || sym1 == wild || sym3 == wild {
if sym3 == wild {
sym3 = sym1
}
xy.Set(1, line.At(1))
num++
}
}
if sym4 := scrnwild.Pos(4, line); sym4 == sym3 || sym4 == wild || sym3 == wild {
if sym3 == wild {
sym3 = sym4
}
xy.Set(4, line.At(4))
num++
if sym5 := scrnwild.Pos(5, line); sym5 == sym3 || sym5 == wild || sym3 == wild {
if sym3 == wild {
sym3 = sym5
}
xy.Set(5, line.At(5))
num++
}
}

if num >= 3 {
ws.Wins = append(ws.Wins, game.WinItem{
Pay: g.Bet * LinePay[sym3-1][num-1],
Mult: 1,
Sym: sym3,
Num: num,
Line: li,
XY: xy,
})
} else {
xy.Free()
}
}
}

func (g *Game) Spin(screen game.Screen) {
screen.Spin(&Reels)
}

func (g *Game) SetLines(sbl game.SBL) error {
var mask game.SBL = (1<<len(bl) - 1) << 1
if sbl == 0 {
return game.ErrNoLineset
}
if sbl&^mask != 0 {
return game.ErrLinesetOut
}
if g.FreeSpins() > 0 {
return game.ErrNoFeature
}
g.SBL = sbl
return nil
}

func (g *Game) SetReels(rd string) error {
if _, ok := ChanceMap[rd]; !ok {
return game.ErrNoReels
}
g.RD = rd
return nil
}
109 changes: 109 additions & 0 deletions game/jewels4all/jewels4all_calc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package jewels4all

import (
"context"
"fmt"
"time"

"github.com/slotopol/server/game"
)

func BruteForce5x(ctx context.Context, s game.Stater, g game.SlotGame, reels game.Reels, x, y int) {
var screen = g.NewScreen()
defer screen.Free()
var ws game.WinScan
var r1 = reels.Reel(1)
var r2 = reels.Reel(2)
var r3 = reels.Reel(3)
var r4 = reels.Reel(4)
var r5 = reels.Reel(5)
for i1 := range r1 {
screen.SetCol(1, r1, i1)
for i2 := range r2 {
screen.SetCol(2, r2, i2)
for i3 := range r3 {
screen.SetCol(3, r3, i3)
for i4 := range r4 {
screen.SetCol(4, r4, i4)
for i5 := range r5 {
screen.SetCol(5, r5, i5)
var sym game.Sym
if x > 0 {
sym = screen.At(x, y)
screen.Set(x, y, wild)
}
g.Scanner(screen, &ws)
if x > 0 {
screen.Set(x, y, sym)
}
s.Update(&ws)
ws.Reset()
if s.Count()&100 == 0 {
select {
case <-ctx.Done():
return
default:
}
}
}
}
}
}
}
}

func CalcStatEuro(ctx context.Context, x, y int) float64 {
var reels = &Reels
var g = NewGame("92")
g.SBL = game.MakeSblNum(1)
var sbl = float64(g.SBL.Num())
var s game.Stat

fmt.Printf("calculations of euro at [%d,%d]\n", x, y)

var total = float64(reels.Reshuffles())
var dur = func() time.Duration {
var t0 = time.Now()
var ctx2, cancel2 = context.WithCancel(ctx)
defer cancel2()
go s.Progress(ctx2, time.NewTicker(2*time.Second), sbl, total)
BruteForce5x(ctx2, &s, g, reels, x, y)
return time.Since(t0)
}()

var reshuf = float64(s.Reshuffles)
var lrtp = s.LinePay / reshuf / sbl * 100
_ = jid
fmt.Printf("completed %.5g%%, selected %d lines, time spent %v\n", reshuf/total*100, g.SBL.Num(), 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[%d,%d] = %.6f%%\n", x, y, lrtp)
return lrtp
}

func CalcStat(ctx context.Context, rn string) (rtp float64) {
var wc float64
if rn != "" {
var ok bool
if wc, ok = ChanceMap[rn]; !ok {
return 0
}
} else {
wc, rn = ChanceMap["95"], "95"
}

var b = 1 / wc
fmt.Printf("wild chance %.5g, b = %.5g\n", wc, b)
var rtp00 = CalcStatEuro(ctx, 0, 0)
var rtpeu float64
for x := 1; x <= 5; x++ {
for y := 1; y <= 3; y++ {
rtpeu += CalcStatEuro(ctx, x, y)
}
}
rtpeu /= 15
rtp = rtp00 + wc*rtpeu
fmt.Printf("euro avr: rtpeu = %.6f%%\n", rtpeu)
fmt.Printf("RTP = %.5g(sym) + wc*%.5g(eu) = %.6f%%\n", rtp00, rtpeu, rtp)
return
}
16 changes: 8 additions & 8 deletions game/justjewels/justjewels.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ var ReelsMap = map[string]*game.Reels5x{

// Lined payment.
var LinePay = [8][5]float64{
{0, 0, 50, 500, 5000}, // crown
{0, 0, 30, 150, 500}, // gold
{0, 0, 30, 150, 500}, // money
{0, 0, 15, 50, 200}, // ruby
{0, 0, 15, 50, 200}, // sapphire
{0, 0, 10, 25, 150}, // emerald
{0, 0, 10, 25, 150}, // amethyst
{0, 0, 0, 0, 0}, // euro
{0, 0, 50, 500, 5000}, // 1 crown
{0, 0, 30, 150, 500}, // 2 gold
{0, 0, 30, 150, 500}, // 3 money
{0, 0, 15, 50, 200}, // 4 ruby
{0, 0, 15, 50, 200}, // 5 sapphire
{0, 0, 10, 25, 150}, // 6 emerald
{0, 0, 10, 25, 150}, // 7 amethyst
{0, 0, 0, 0, 0}, // 8 euro
}

// Scatters payment.
Expand Down
2 changes: 1 addition & 1 deletion game/powerstars/powerstars.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var Reels = game.Reels5x{
{6, 4, 7, 6, 5, 7, 3, 5, 2, 8, 4, 7, 2, 5, 6, 7, 3, 8, 5, 1, 2, 8, 3, 6, 4, 8, 7, 4, 5, 3, 6, 8, 1},
}

// Map with star chances.
// Map with wild chances.
var ChanceMap = map[string]float64{
// free spins: q = 0.036141, 1/q = 27.669, rtpfs = 470.021964%
// RTP = 69.063(sym) + q*470.02(fg) = 86.049978%
Expand Down
Loading

0 comments on commit c5796c1

Please sign in to comment.