DocsGames
Wheel
A Wheel spin is one float multiplied by the number of segments, and what comes back is a segment index, never a prize.
floor(float × segments). That is the entire game.
import { play } from '@galabet/fair';
const seeds = {
serverSeed: '5c1f7d3e8a2b4c6d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d',
clientSeed: 'galabet',
nonce: 42,
};
const spin = await play({ game: 'wheel', ...seeds });
console.log(spin);
console.log(spin.floats[0] * 10);
for (const segments of [2, 50]) {
const { result } = await play({ game: 'wheel', ...seeds, params: { segments } });
console.log(`${segments} segments: ${result}`);
}
{ result: 5, cursor: 0, floats: [ 0.5611712262034416 ] }
5.611712262034416
2 segments: 1
50 segments: 28
The float times ten is 5.61 and a bit, which floors to 5. Segments are numbered from 0, so that's the sixth one. Put the same float on a wheel of 50 and it lands on 28.
Checking a spin somebody gave you? result in the record is that index and nothing more. Paste the record into the verifier to confirm the index came from the seeds. What the segment was worth isn't in the record, so the verifier can't tell you, and you'll need the site's prize table from the day you played.
| Fact | Value |
|---|---|
| Server seed | 5c1f7d3e8a2b4c6d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d |
| Client seed | galabet |
| Nonce | 42 |
| Result | a whole number from 0 to segments - 1 |
| Floats read | 1 |
| Cursor | always 0 |
segments | 10 when you leave it out |
Prize Tables
The library has no idea what segment 5 pays. Galabet's demo keeps ten multipliers in apps/api/src/demo/rules.ts: [0, 1.5, 1.2, 1.5, 0, 2, 1.2, 1.5, 0, 1.0]. They add up to 9.9, so over ten segments the table returns 99%, and segment 5 pays 2. Every segment is the same width. The 0 is in that list three times because repeating a value is the only way to make it likelier.
Now suppose you rearrange the table next month.
const tables = {
'example-1': [0, 1.5, 1.2, 1.5, 0, 2, 1.2, 1.5, 0, 1.0],
'example-2': [0, 1.5, 2, 1.5, 0, 1.2, 1.2, 1.5, 0, 1.0],
};
const bet = { segment: 5, stake: 100, table: 'example-1' };
for (const [version, table] of Object.entries(tables)) {
console.log(version, Math.floor(bet.stake * table[bet.segment]));
}
console.log('settled under', bet.table);
example-1 200
example-2 120
settled under example-1
The record says 5 under both tables, and it verifies under both. It can't say more. params for Wheel takes segments and nothing else, and inspectRecord turns away a record with anything extra in it: params.table: not supported for wheel. So the table version lives with the bet, beside the record. The demo API does it with a ruleVersion string on every receipt, currently galabet-demo/2026-09-20.1. Skip that, change the table once, and nobody can say what an old 5 paid.
The segments Parameter
A whole number, 2 or more. wheel sets no upper limit, but inspectRecord does, and the verifier page is built on it. A 1,000-segment spin passes verifyRecord. Paste the same record into the verifier and it answers segments: enter a whole number from 2 to 100. Stay at 100 or under if you want players to check their own spins there.
Always write segments into params, even when it's 10. A record with params: {} is read as a ten-segment wheel, and a reader shouldn't have to know that.
Are the segments equally likely? To within one part in 429 million. 4,294,967,296 floats don't divide by ten, so six of the ten segments have 429,496,730 floats behind them and the other four have 429,496,729.
The test vectors in vectors/gfs-1.0.json cover 10, 30 and 54 segments, 112 spins each.
| Message | What happened |
|---|---|
segments must be an integer >= 2 | segments was 0, 1, negative, a fraction, or a string such as "10" |
Seed and nonce errors are the same for every game and are listed on the Dice page.
