forked from StakeEngine/math-sdk
-
-
Notifications
You must be signed in to change notification settings - Fork 2
Event System
Richard Fu edited this page Jan 24, 2026
·
1 revision
The Math SDK uses a standardized event system to track all game actions and outcomes.
Events are the primary way to record game actions:
- Wins and payouts
- Feature triggers (free spins, bonuses)
- Board transformations (tumbles, upgrades)
- Special symbol actions (multipliers, wilds)
All events are stored in the books files and consumed by the frontend to render animations.
Always use EventConstants instead of hardcoded strings:
from src.events.event_constants import EventConstants
# ✅ Good
event_type = EventConstants.WIN.value
# ❌ Bad
event_type = "win"Wins
-
EventConstants.WIN- Basic win event -
EventConstants.SET_FINAL_WIN- Set final win amount -
EventConstants.SET_WIN- Set win for current stage -
EventConstants.SET_TOTAL_WIN- Set cumulative win -
EventConstants.WIN_CAP- Win cap applied
Free Spins
-
EventConstants.TRIGGER_FREE_SPINS- Trigger free spins -
EventConstants.RETRIGGER_FREE_SPINS- Retrigger during free spins -
EventConstants.END_FREE_SPINS- Free spins ended
Tumbles/Cascades
-
EventConstants.TUMBLE_BOARD- Board tumble/cascade -
EventConstants.SET_TUMBLE_WIN- Win from tumble -
EventConstants.UPDATE_TUMBLE_WIN- Update tumble win
Special
-
EventConstants.UPDATE_GLOBAL_MULT- Global multiplier change -
EventConstants.UPGRADE- Symbol upgrade -
EventConstants.REVEAL- Reveal hidden symbols -
EventConstants.EXPAND_WILD- Expanding wild -
EventConstants.STICKY_SYMBOL- Sticky symbol applied
from src.events.event_constants import EventConstants
from src.events.events import construct_event
event = construct_event(
event_type=EventConstants.WIN.value,
amount=10.0
)
self.book.add_event(event)event = construct_event(
event_type=EventConstants.WIN.value,
amount=10.0,
details={
"symbols": ["A", "A", "A"],
"positions": [[0, 0], [0, 1], [0, 2]],
"multiplier": 2
}
)
self.book.add_event(event)event = construct_event(
event_type=EventConstants.TRIGGER_FREE_SPINS.value,
details={
"count": 10,
"scatter_positions": [[1, 0], [2, 1], [3, 2]]
}
)
self.book.add_event(event)event = construct_event(
event_type=EventConstants.TUMBLE_BOARD.value,
details={
"removed_positions": [[0, 0], [0, 1]],
"new_symbols": [["K", 0, 0], ["Q", 0, 1]]
}
)
self.book.add_event(event)event = construct_event(
event_type=EventConstants.UPDATE_GLOBAL_MULT.value,
details={
"old_multiplier": 1,
"new_multiplier": 2,
"increment": 1
}
)
self.book.add_event(event)Typical event sequence:
def run_spin(self):
# 1. Draw board
self.draw_board()
# 2. Calculate and record wins
self.calculate_wins() # Adds WIN events
# 3. Check for tumbles/cascades
if self.has_winning_clusters():
while self.has_wins():
self.book.add_event(construct_event(
event_type=EventConstants.TUMBLE_BOARD.value
))
self.remove_winning_symbols()
self.drop_symbols()
self.calculate_wins()
# 4. Check for feature triggers
if self.check_fs_condition():
self.book.add_event(construct_event(
event_type=EventConstants.TRIGGER_FREE_SPINS.value,
details={"count": 10}
))
self.run_free_spin_from_base()
# 5. Set final win
self.book.add_event(construct_event(
event_type=EventConstants.SET_FINAL_WIN.value,
amount=self.book.get_total_win()
))
return self.bookFor game-specific events, extend EventConstants:
# games/<game_name>/game_events.py
from enum import Enum
class CustomEvents(Enum):
COLLECT_SYMBOL = "collect_symbol"
FILL_METER = "fill_meter"
TRANSFORM_REEL = "transform_reel"Usage:
from games.my_game.game_events import CustomEvents
event = construct_event(
event_type=CustomEvents.COLLECT_SYMBOL.value,
details={"symbol": "M", "position": [2, 1]}
)Structure event details for frontend consumption:
# Symbol collection mechanic
event = construct_event(
event_type=CustomEvents.COLLECT_SYMBOL.value,
details={
"symbol": "coin",
"position": [2, 1],
"value": 5,
"total_collected": 25,
"meter_progress": 0.5 # 50% full
}
)
# Reel transformation
event = construct_event(
event_type=CustomEvents.TRANSFORM_REEL.value,
details={
"reel": 2,
"from_symbols": ["A", "K", "Q"],
"to_symbols": ["W", "W", "W"], # All wilds
"trigger": "scatter_on_reel_5"
}
)# ✅ Good
EventConstants.WIN.value
# ❌ Bad
"win"# ✅ Good - frontend knows what to animate
event = construct_event(
event_type=EventConstants.WIN.value,
amount=10.0,
details={
"symbols": ["A", "A", "A"],
"positions": [[0,0], [0,1], [0,2]],
"payline": "L1"
}
)
# ❌ Bad - frontend doesn't know what won
event = construct_event(
event_type=EventConstants.WIN.value,
amount=10.0
)# ✅ Good - logical sequence
self.book.add_event(win_event)
self.book.add_event(multiplier_event)
self.book.add_event(final_win_event)
# ❌ Bad - confusing order
self.book.add_event(final_win_event)
self.book.add_event(win_event)# ✅ Good - separate events
self.book.add_event(tumble_event)
self.book.add_event(new_win_event)
# ❌ Bad - combining unrelated actions
self.book.add_event({
"tumble": True,
"win": 10.0
})Events appear in books as:
{
"board": [["A", "K", "Q"], ...],
"events": [
{
"type": "win",
"amount": 5.0,
"details": {
"symbols": ["A", "A", "A"],
"positions": [[0, 0], [0, 1], [0, 2]]
}
},
{
"type": "triggerFreeSpins",
"details": {
"count": 10,
"scatter_positions": [[1, 0], [2, 1], [3, 2]]
}
},
{
"type": "setFinalWin",
"amount": 5.0
}
],
"final_win": 5.0
}The frontend processes events sequentially:
- Parse event type: Determine animation to play
- Extract details: Get positions, symbols, values
- Animate: Play corresponding animation
- Update UI: Show wins, meters, counters
Example frontend logic:
// Pseudo-code
events.forEach(event => {
switch(event.type) {
case 'win':
highlightSymbols(event.details.positions);
showWinAmount(event.amount);
break;
case 'trigger_free_spins':
playTriggerAnimation();
show_free_spin_count(event.details.count);
break;
case 'tumble_board':
animateTumble(event.details.removed_positions);
dropNewSymbols(event.details.new_symbols);
break;
}
});# In game_state.py
def run_spin(self):
# ... game logic ...
# Debug: print all events
for event in self.book.events:
print(f"Event: {event['type']}, Amount: {event.get('amount', 'N/A')}")# Check event structure
def validate_event(event):
assert "type" in event, "Event missing type"
assert event["type"] in [e.value for e in EventConstants], f"Unknown event type: {event['type']}"
if "amount" in event:
assert isinstance(event["amount"], (int, float)), "Amount must be numeric"- Game Structure - Where events fit in game architecture
- Running Games - Viewing events in books files