Skip to content

Commit

Permalink
agt/extraspin game added.
Browse files Browse the repository at this point in the history
  • Loading branch information
schwarzlichtbezirk committed Dec 9, 2024
1 parent 47d1d60 commit 0fb46d0
Show file tree
Hide file tree
Showing 14 changed files with 641 additions and 21 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: 127 games, 64 algorithms, 9 providers
AGT: 45 games
total: 128 games, 65 algorithms, 9 providers
AGT: 46 games
Aristocrat: 4 games
BetSoft: 3 games
Megajack: 3 games
Expand All @@ -30,6 +30,7 @@ Slotopol: 4 games

*Last added games*:

* '[Extra Spin](https://demo.agtsoftware.com/games/agt/extraspin)' AGT 5x3 videoslot
* '[Book of Set](https://demo.agtsoftware.com/games/agt/bookofset)' AGT 5x3 videoslot
* '[Pharaoh II](https://demo.agtsoftware.com/games/agt/pharaoh2)' AGT 5x3 videoslot
* '[Lucky Slot](https://demo.agtsoftware.com/games/agt/luckyslot)' AGT 5x3 videoslot
Expand Down
1 change: 1 addition & 0 deletions cmd/links.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import (
_ "github.com/slotopol/server/game/slot/agt/cherryhot"
_ "github.com/slotopol/server/game/slot/agt/doubleice"
_ "github.com/slotopol/server/game/slot/agt/egypt"
_ "github.com/slotopol/server/game/slot/agt/extraspin"
_ "github.com/slotopol/server/game/slot/agt/happysanta"
_ "github.com/slotopol/server/game/slot/agt/happysanta50"
_ "github.com/slotopol/server/game/slot/agt/hotclover"
Expand Down
5 changes: 3 additions & 2 deletions docs/list-all.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
'Dynasty Of Ming' Novomatic 5x3 videoslot
'Egypt' AGT 5x3 videoslot
'Excalibur' NetEnt 5x3 videoslot
'Extra Spin' AGT 5x3 videoslot
'Fire Joker' Play'n GO 5x3 videoslot
'Fire Keno' Slotopol 80 spots lottery
'Firefighters' AGT 5x3 videoslot
Expand Down Expand Up @@ -130,8 +131,8 @@
'Wild Witches' NetEnt 5x3 videoslot
'Wizard' AGT 5x4 videoslot
total: 127 games, 64 algorithms, 9 providers
AGT: 45 games
total: 128 games, 65 algorithms, 9 providers
AGT: 46 games
Aristocrat: 4 games
BetSoft: 3 games
Megajack: 3 games
Expand Down
36 changes: 36 additions & 0 deletions game/slot/agt/extraspin/extraspin_calc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package extraspin

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(5*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
var q = float64(s.FreeCount) / reshuf
var sq = 1 / (1 - q)
var rtpfs = sq * rtpsym * 3
var rtp = rtpsym + q*rtpfs
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("symbols: %.5g(lined) + %.5g(scatter) = %.6f%%\n", lrtp, srtp, rtpsym)
fmt.Printf("free spins %d, q = %.5g, sq = 1/(1-q) = %.6f\n", s.FreeCount, q, sq)
fmt.Printf("free games frequency: 1/%.5g\n", reshuf/float64(s.FreeHits))
fmt.Printf("RTP = %.5g(sym) + %.5g*%.5g(fg) = %.6f%%\n", rtpsym, q, rtpfs, rtp)
return rtp
}
28 changes: 28 additions & 0 deletions game/slot/agt/extraspin/extraspin_link.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//go:build !prod || full || agt

package extraspin

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

var Info = game.GameInfo{
Aliases: []game.GameAlias{
{Prov: "AGT", Name: "Extra Spin"},
},
GP: game.GPsel |
game.GPretrig |
game.GPfgmult |
game.GPscat |
game.GPwild,
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)
}
359 changes: 359 additions & 0 deletions game/slot/agt/extraspin/extraspin_reel.go

Large diffs are not rendered by default.

126 changes: 126 additions & 0 deletions game/slot/agt/extraspin/extraspin_rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package extraspin

// See: https://demo.agtsoftware.com/games/agt/extraspin

import (
"github.com/slotopol/server/game/slot"
)

// Lined payment.
var LinePay = [9][5]float64{
{0, 10, 50, 250, 1000}, // 1 wild
{}, // 2 scatter
{0, 0, 40, 120, 500}, // 3 strawberry
{0, 0, 20, 40, 200}, // 4 papaya
{0, 0, 20, 40, 200}, // 5 grapes
{0, 0, 10, 20, 100}, // 6 orange
{0, 0, 10, 20, 100}, // 7 plum
{0, 0, 5, 10, 50}, // 8 cherry
{0, 0, 5, 10, 50}, // 9 pear
}

// Bet lines
var BetLines = slot.BetLinesAgt5x3[:10]

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 wild, scat = 1, 2

func (g *Game) Scanner(screen slot.Screen, wins *slot.Wins) {
g.ScanLined(screen, wins)
g.ScanScatters(screen, wins)
}

// Lined symbols calculation.
func (g *Game) ScanLined(screen slot.Screen, wins *slot.Wins) {
for li := 1; li <= g.Sel; li++ {
var line = BetLines[li-1]

var numw, numl slot.Pos = 0, 5
var syml slot.Sym
var x slot.Pos
for x = 1; x <= 5; x++ {
var sx = screen.Pos(x, line)
if sx == wild {
if syml == 0 {
numw = x
}
} else if syml == 0 && sx != scat {
syml = sx
} else if sx != syml {
numl = x - 1
break
}
}

var payw, payl float64
if numw > 0 {
payw = LinePay[wild-1][numw-1]
}
if numl > 0 && syml > 0 {
payl = LinePay[syml-1][numl-1]
}
if payl > payw {
var mm float64 = 1 // mult mode
if g.FSR > 0 {
mm = 3
}
*wins = append(*wins, slot.WinItem{
Pay: g.Bet * payl,
Mult: mm,
Sym: syml,
Num: numl,
Line: li,
XY: line.CopyL(numl),
})
} else if payw > 0 {
var mm float64 = 1 // mult mode
if g.FSR > 0 {
mm = 3
}
*wins = append(*wins, slot.WinItem{
Pay: g.Bet * payw,
Mult: mm,
Sym: wild,
Num: numw,
Line: li,
XY: line.CopyL(numw),
})
}
}
}

// Scatters calculation.
func (g *Game) ScanScatters(screen slot.Screen, wins *slot.Wins) {
if count := screen.ScatNum(scat); count >= 1 {
*wins = append(*wins, slot.WinItem{
Sym: scat,
Num: count,
XY: screen.ScatPos(scat),
Free: int(count),
})
}
}

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))
}
2 changes: 1 addition & 1 deletion game/slot/agt/happysanta/happysanta_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var LinePay = [10][5]float64{
{0, 0, 12, 60, 160}, // 6 redstar
{0, 0, 10, 40, 120}, // 7 plum
{0, 0, 10, 40, 120}, // 8 peach
{0, 0, 6, 30, 80}, // 9 quince
{0, 0, 6, 30, 80}, // 9 papaya
{0, 0, 6, 30, 80}, // 10 cherry
}

Expand Down
2 changes: 1 addition & 1 deletion game/slot/agt/luckyslot/luckyslot_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var LinePay = [10][5]float64{
{0, 0, 20, 40, 200}, // 6 pear
{0, 0, 12, 30, 160}, // 7 plum
{0, 0, 12, 30, 160}, // 8 peach
{0, 0, 8, 24, 120}, // 9 quince
{0, 0, 8, 24, 120}, // 9 papaya
{0, 0, 8, 24, 120}, // 10 cherry
}

Expand Down
57 changes: 57 additions & 0 deletions helper/extraspin.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
local path = arg[0]:match("(.*[/\\])")
dofile(path.."lib/reelgen.lua")

local symset15 = {
2, -- 1 wild
0, -- 2 scatter (always 0 here)
3, -- 3 strawberry
4, -- 4 papaya
4, -- 5 grapes
7, -- 6 orange
7, -- 7 plum
8, -- 8 cherry
8, -- 9 pear
}

local symset234 = {
4, -- 1 wild
1, -- 2 scatter (always 1 here)
6, -- 3 strawberry
10, -- 4 papaya
10, -- 5 grapes
13, -- 6 orange
13, -- 7 plum
15, -- 8 cherry
15, -- 9 pear
}

local chunklen15 = {
1, -- 1 wild
1, -- 2 scatter
1, -- 3 strawberry
1, -- 4 papaya
1, -- 5 grapes
3, -- 6 orange
3, -- 7 plum
3, -- 8 cherry
3, -- 9 pear
}

local chunklen234 = {
1, -- 1 wild
1, -- 2 scatter
1, -- 3 strawberry
1, -- 4 papaya
1, -- 5 grapes
7, -- 6 orange
7, -- 7 plum
5, -- 8 cherry
5, -- 9 pear
}

math.randomseed(os.time())
local reel, iter
reel, iter = makereelhot(symset15, 3, {[1]=true, [2]=true}, chunklen15, true)
printreel(reel, iter)
reel, iter = makereelhot(symset234, 3, {[1]=true, [2]=true}, chunklen234, true)
printreel(reel, iter)
4 changes: 2 additions & 2 deletions helper/happysanta.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ local symset = {
5, -- 6 redstar
6, -- 7 plum
6, -- 8 peach
6, -- 9 quince
6, -- 9 papaya
6, -- 10 cherry
}

Expand All @@ -23,7 +23,7 @@ local chunklen = {
3, -- 6 redstar
3, -- 7 plum
3, -- 8 peach
3, -- 9 quince
3, -- 9 papaya
3, -- 10 cherry
}

Expand Down
4 changes: 2 additions & 2 deletions helper/happysanta50.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ local symset = {
5, -- 6 redstar
6, -- 7 plum
6, -- 8 peach
6, -- 9 quince
6, -- 9 papaya
6, -- 10 cherry
}

Expand All @@ -23,7 +23,7 @@ local chunklen = {
8, -- 6 redstar
8, -- 7 plum
8, -- 8 peach
8, -- 9 quince
8, -- 9 papaya
8, -- 10 cherry
}

Expand Down
29 changes: 20 additions & 9 deletions helper/lib/reelgen.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,33 @@ function makereelhot(symset, sy, scat, chunklen, strict)
shuffle(chunks)
local ok, n = true, 0
local last = 0
for _, c in pairs(chunks) do
if strict and c.sym == last then
ok = false
break
end
if scat[c.sym] then
if n < sy then
for i = 1, #chunks do
local c1, c2 = chunks[i], chunks[i+1]
if strict and c1.sym == last then
if not c2 or c2.sym == last then
ok = false
break
else
c1, c2 = c2, c1
chunks[i], chunks[i+1] = c1, c2
end
end
if scat[c1.sym] then
if n < sy then
if not c2 or scat[c2.sym] or (strict and c2.sym == last) then
ok = false
break
else
c1, c2 = c2, c1
chunks[i], chunks[i+1] = c1, c2
end
else
n = 0
end
else
n = n + c.n
n = n + c1.n
end
last = c.sym
last = c1.sym
end
iter = iter + 1
until ok or iter >= 1000
Expand Down
Loading

0 comments on commit 0fb46d0

Please sign in to comment.