Skip to content

fix(cluster): dedupe win/explode positions — bonus freeze when a wild is in multiple clusters#32

Open
auraladigital wants to merge 1 commit into
StakeEngine:mainfrom
auraladigital:fix/cluster-dedupe-win-positions
Open

fix(cluster): dedupe win/explode positions — bonus freeze when a wild is in multiple clusters#32
auraladigital wants to merge 1 commit into
StakeEngine:mainfrom
auraladigital:fix/cluster-dedupe-win-positions

Conversation

@auraladigital

Copy link
Copy Markdown

Addresses #14 ("game sticks when bonus purchase") — a concrete, reproducible cause of the bonus / free-spin freeze.

Cause

A board cell can legitimately belong to more than one win: a wild substitutes into multiple adjacent clusters, so the cluster math lists that cell in each win's positions. After _.flatten(wins.map(w => w.positions)) the cell appears more than once.

Board.svelte › boardWithAnimateSymbols and TumbleBoard.svelte › tumbleBoardExplode store each position's resolver on the symbol:

await waitForResolve((resolve) => (reelSymbol.oncomplete = resolve));

oncomplete is a single slot. A duplicate position overwrites the first resolver, so the symbol's one complete resolves only the last await — the earlier duplicate's promise never settles, Promise.all never resolves, and the round hangs forever.

It only triggers when an event contains a repeated cell (a wild bridging two clusters), which is why it surfaces in bonus and not base, and why the committed Storybook fixtures (bonus_books.ts — 50 books / 308 winInfo events) don't surface it: none of them happen to contain an overlap. Such events do occur in normal 0_0_cluster sims.

Repro

A winInfo (or tumble) event whose wins share a cell (illustrative shape):

wins: [
  { positions: [{ reel: 0, row: 2 }, { reel: 0, row: 3 }] }, // cluster A, incl. wild (0,2)
  { positions: [{ reel: 0, row: 2 }, { reel: 1, row: 2 }] }, // cluster B, same wild (0,2)
]

(0,2) is awaited twice → the round never returns to idle.

Fix

Dedupe positions by (reel,row) before building the promise array, in both handlers. A cell is one symbol with one oncomplete; awaiting it once is correct and the visuals are unchanged.

A board cell can belong to more than one win — a wild substituting into
multiple adjacent clusters lists its position in each. boardWithAnimateSymbols
and tumbleBoardExplode store each position's resolver on the symbol via
`reelSymbol.oncomplete = resolve`; a duplicate overwrites the prior resolver,
so the symbol's single `complete` resolves only the last await — the earlier
one never settles, Promise.all hangs, and the round freezes.

Dedupe positions by (reel,row) before awaiting in both handlers.

Addresses StakeEngine#14.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant