aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/config_validate.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/casino/config_validate.go')
-rw-r--r--internal/casino/config_validate.go31
1 files changed, 31 insertions, 0 deletions
diff --git a/internal/casino/config_validate.go b/internal/casino/config_validate.go
index ee8e8c9..38d426d 100644
--- a/internal/casino/config_validate.go
+++ b/internal/casino/config_validate.go
@@ -141,6 +141,37 @@ func (p SlotProfile) Validate() error {
return fmt.Errorf("unsupported slot geometry %dx%d: the runtime evaluator supports %dx%d only",
profile.Rows, profile.Reels, slotGridRows, slotGridReels)
}
+ // A named trigger must exist, or the feature silently never fires while
+ // the RTP model also computes a zero trigger probability and reports green.
+ if profile.FreeSpinTrigger != "" {
+ found := false
+ positiveWeight := false
+ for _, symbol := range profile.Symbols {
+ if symbol.ID == profile.FreeSpinTrigger {
+ found = true
+ positiveWeight = symbol.Weight > 0
+ break
+ }
+ }
+ if !found {
+ return fmt.Errorf("free_spin_trigger %q names no symbol in symbols", profile.FreeSpinTrigger)
+ }
+ if !positiveWeight {
+ return fmt.Errorf("free_spin_trigger %q names a symbol with no reel weight, so the feature can never trigger", profile.FreeSpinTrigger)
+ }
+ }
+ // A paytable must not have a gap: evaluateSlot pays only the longest run,
+ // so Payout{3: 0.5} with no Payout[4] makes every 4-run pay zero — less
+ // than the 3-run — while the RTP model agrees and nothing else flags it.
+ // A symbol whose shorter runs also pay zero (e.g. only a 5-run pays) is a
+ // legitimate shape and is not flagged.
+ for _, symbol := range profile.Symbols {
+ for length := 4; length <= 5; length++ {
+ if symbol.Payout[length] <= 0 && symbol.Payout[length-1] > 0 {
+ return fmt.Errorf("symbol %q pays for a run of %d but not %d: a longer run would pay zero", symbol.ID, length-1, length)
+ }
+ }
+ }
return nil
}