How Japanese Chinchiro and Chinese Bo Bing dice games are scored (and simulating them with 3D physics in the browser)
Two of the most fun "roll and shout" dice games in East Asia are Chinchiro (チンチロ / チンチロリン) from Japan and Bo Bing (博饼, "cake gambling") from Xiamen, China, traditionally played around the Mid-Autumn Festival. Both are si
Two of the most fun "roll and shout" dice games in East Asia are Chinchiro (チンチロ / チンチロリン) from Japan and Bo Bing (博饼, "cake gambling") from Xiamen, China, traditionally played around the Mid-Autumn Festival. Both are simple to play, but their scoring tables hide some interesting probability. In this post I'll go through the rules, the combination tables, the exact probabilities (computed by enumerating every outcome), and finally how to make the dice actually tumble in the browser with a physics engine.
Part 1: Chinchiro (3 dice)
Rules
- Players roll three dice into a bowl (traditionally a rice bowl).
- Each player gets up to three tries to make a scoring combination. The first scoring roll stands.
- A die that jumps out of the bowl is usually an immediate loss (shonben).
- In the classic banker game, each player's hand is compared to the banker's, and some hands pay multiples of the bet.
Combination table
| Hand | Japanese | Dice | Typical payout |
|---|---|---|---|
| Pinzoro | ピンゾロ | 1-1-1 | wins 5x |
| Arashi / Zorome | 嵐・ゾロ目 | triple 2–6 | wins 3x |
| Shigoro | シゴロ | 4-5-6 | wins 2x |
| Point (me) | 目 | a pair + one odd die; the odd die is the point (1–6) | compare points |
| Menashi | 目なし | anything else | no hand (roll again, up to 3 times) |
| Hifumi | ヒフミ | 1-2-3 | loses 2x |
Payouts vary between house rules; ranks are almost always the same: Pinzoro > other triples > 4-5-6 > point 6 … point 1 > no hand > 1-2-3.
Probabilities
Three dice have 6³ = 216 equally likely outcomes.
| Hand | Count | Probability |
|---|---|---|
| 1-1-1 | 1 | 0.46% |
| Triple 2–6 | 5 | 2.31% |
| 4-5-6 (any order) | 3! = 6 | 2.78% |
| 1-2-3 (any order) | 6 | 2.78% |
| Point (pair + odd die) | 6 × 5 × 3 = 90 | 41.67% |
| – each specific point 1…6 | 15 | 6.94% |
| No hand | 108 | 50.00% |
Exactly half of all single rolls are "no hand". With three tries, the chance of ending with no hand is (1/2)³ = 12.5%, and the chance of eventually making something is 87.5%.
function chinchiro(d) {
const [a, b, c] = [...d].sort((x, y) => x - y);
if (a === c) return a === 1 ? 'pinzoro' : 'arashi';
if (a === 4 && b === 5 && c === 6) return 'shigoro';
if (a === 1 && b === 2 && c === 3) return 'hifumi';
if (a === b) return 'point ' + c;
if (b === c) return 'point ' + a;
return 'menashi';
}
// Exact enumeration
const tally = {};
for (let x = 1; x <= 6; x++)
for (let y = 1; y <= 6; y++)
for (let z = 1; z <= 6; z++) {
const k = chinchiro([x, y, z]);
tally[k] = (tally[k] || 0) + 1;
}
console.log(tally); // menashi: 108, point N: 15 each, ...
Part 2: Bo Bing (6 dice)
Rules
- Players take turns throwing six dice into a large porcelain bowl.
- Each throw is checked against the prize table; if it matches a tier, the player takes a prize (traditionally mooncakes) from that tier.
- Fours are special: the 4 face is red, so the number of fours drives most tiers.
- The game ends when all prizes are claimed. The top prize, Zhuangyuan, can be "stolen" by a later, higher Zhuangyuan roll.
Combination table (from highest to lowest)
| Tier | Chinese | Condition |
|---|---|---|
| Zhuangyuan (top scholar) | 状元 | 4 fours, or 5 of a kind, or 6 of a kind (sub-ranked: 6 fours > 6 of a kind > 4 fours + 2 ones > 5 fours > 5 of a kind > 4 fours) |
| Duitang (straight) | 对堂 | 1-2-3-4-5-6 |
| Sanhong (three reds) | 三红 | exactly 3 fours |
| Sijin (four of a kind) | 四进 | 4 of the same non-4 face |
| Erju (two reds) | 二举 | exactly 2 fours (and not Sijin) |
| Yixiu (one red) | 一秀 | exactly 1 four (and nothing higher) |
| — | — | anything else: no prize |
Probabilities
Six dice have 6⁶ = 46,656 outcomes. Counting by k, the number of fours:
- Zhuangyuan: six 4s (1) + five 4s (6 × 5 = 30) + four 4s (C(6,4) × 5² = 375) + five of a kind of another face (5 × 6 × 5 = 150) + six of a kind of another face (5) = 561
- Duitang: 6! = 720
- Sanhong: C(6,3) × 5³ = 2,500
- Sijin: 5 faces × C(6,4) × 5² = 1,875 (this includes four-of-a-kind with one or two fours alongside, which still ranks as Sijin)
- Erju: C(6,2) × 5⁴ = 9,375, minus the 75 where the other four dice are a four of a kind = 9,300
- Yixiu: 6 × 5⁵ = 18,750, minus 720 straights, minus 30 five-of-a-kinds, minus 600 four-of-a-kinds = 17,400
- Nothing: the rest = 14,300
| Tier | Count | Probability | About 1 in |
|---|---|---|---|
| Zhuangyuan | 561 | 1.20% | 83 |
| Duitang | 720 | 1.54% | 65 |
| Sanhong | 2,500 | 5.36% | 18.7 |
| Sijin | 1,875 | 4.02% | 24.9 |
| Erju | 9,300 | 19.93% | 5.0 |
| Yixiu | 17,400 | 37.29% | 2.7 |
| No prize | 14,300 | 30.65% | 3.3 |
Two fun observations:
- Sijin is rarer than Sanhong but ranks below it. The table is driven by tradition (the color red and the imperial exam metaphor), not strictly by probability.
- Roughly 69% of throws win something, which keeps the game lively for a family table.
A verification script that enumerates all 46,656 outcomes:
function boBing(d) {
const c = Array(7).fill(0);
d.forEach(v => c[v]++);
const max = Math.max(...c.slice(1));
const fours = c[4];
if (fours >= 4 || max >= 5) return 'zhuangyuan';
if (c.slice(1).every(n => n === 1)) return 'duitang';
if (fours === 3) return 'sanhong';
if (max === 4) return 'sijin';
if (fours === 2) return 'erju';
if (fours === 1) return 'yixiu';
return 'none';
}
const t = {};
for (let i = 0; i < 6 ** 6; i++) {
const d = [];
let n = i;
for (let j = 0; j < 6; j++) { d.push(n % 6 + 1); n = Math.floor(n / 6); }
const k = boBing(d);
t[k] = (t[k] || 0) + 1;
}
console.log(t);
// { zhuangyuan: 561, duitang: 720, sanhong: 2500, sijin: 1875,
// erju: 9300, yixiu: 17400, none: 14300 }
Part 3: Rolling the dice with 3D physics in the browser
Math.random() gives perfectly fair dice, but it's not much fun to watch. For a "real" feel you can simulate rigid bodies and read the result from the final orientation:
-
Physics world – use a library like cannon-es or Rapier (WASM). Create a static floor plus walls (or a bowl built from a few static shapes) and one
Boxbody per die. - Throw – give each die a random position above the bowl, a random rotation, a linear velocity toward the center, and a random angular velocity. Step the world at a fixed timestep (e.g. 1/60 s) and copy body transforms to your Three.js meshes each frame.
- Settle detection – consider the roll finished when every body's linear and angular speed stays below a small threshold for a few frames (or let the engine put bodies to sleep).
-
Read the face – rotate each face's local normal (+X, −X, +Y, −Y, +Z, −Z) by the body's quaternion and pick the one whose world-space normal has the largest dot product with the up vector
(0, 1, 0). Map that face to its pip value. - Edge cases – if the best dot product is below ~0.9 the die is "cocked" against a wall; nudge it or re-roll. For Chinchiro, a die whose final position is outside the bowl radius is a shonben (instant loss), which a physics sim models for free.
const faces = [
{ n: new CANNON.Vec3(0, 1, 0), v: 1 }, { n: new CANNON.Vec3(0, -1, 0), v: 6 },
{ n: new CANNON.Vec3(1, 0, 0), v: 2 }, { n: new CANNON.Vec3(-1, 0, 0), v: 5 },
{ n: new CANNON.Vec3(0, 0, 1), v: 3 }, { n: new CANNON.Vec3(0, 0, -1), v: 4 },
];
function readDie(body) {
let best = null, bestDot = -Infinity;
for (const f of faces) {
const w = body.quaternion.vmult(f.n);
if (w.y > bestDot) { bestDot = w.y; best = f.v; }
}
return { value: best, cocked: bestDot < 0.9 };
}
Because the initial conditions are random and the dynamics are chaotic, the resulting face distribution is very close to uniform, and you can sanity-check it by running a few thousand headless throws and comparing the counts against the tables above.
Wrap-up
- Chinchiro: 216 outcomes, half of single rolls are "no hand", 1-1-1 is 1/216.
- Bo Bing: 46,656 outcomes, Zhuangyuan about 1.2%, and about 69% of throws win a prize.
- Physics-based dice add drama (and the occasional die flying out of the bowl) without hurting fairness.
If you want to try physics-based dice rolling in your browser, I built a free set of 3D dice tools here: https://3droll.com/
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.