In The Integer Reduction, we showed that running an ODE with uniform rates on a tic-tac-toe analysis net recovers the strategic hierarchy center=4, corner=3, edge=2 purely from topology. The values are incidence degrees — arc counts to terminal transitions — and the ODE recovers them without game-specific heuristics.
That post noted that Texas Hold'em falls on the other side of the boundary: competing resource flows produce genuinely dynamic values, and the ODE earns its keep. But it left a question open: can the same technique derive the hand ranking itself from network structure?
It can. We build a hand analysis net where drain counts encode combinatorial frequency, run the ODE, and the entire poker hand hierarchy emerges: straight flush at the top, high card at the bottom. We then prove every game action with Groth16 ZK proofs and export the topology-derived values into a Solidity contract as provably fair payout multipliers.
The insight is to encode how common each hand is as structural outflow. Each of the nine hand categories gets:
src_H (1 token) — constant inflow via catalytic arcval_H (0 tokens) — accumulation targetplay_H — moves tokens from source to value, returning the source tokenval_H at a rate proportional to hand frequencyThe drain counts are log-scaled from actual 5-card combination counts:
| Hand | 5-Card Combos | Drains | ODE Value |
|---|---|---|---|
| Straight Flush | 40 | 1 | 32.0 |
| Four of a Kind | 624 | 2 | 16.0 |
| Full House | 3,744 | 4 | 8.0 |
| Flush | 5,108 | 5 | 6.4 |
| Straight | 10,200 | 8 | 4.0 |
| Three of a Kind | 54,912 | 12 | 2.7 |
| Two Pair | 123,552 | 16 | 2.0 |
| One Pair | 1,098,240 | 24 | 1.3 |
| High Card | 1,302,540 | 32 | 1.0 |
The full net has 18 places and 113 transitions (9 play + 104 drain).
At equilibrium under mass-action kinetics, inflow equals outflow for each value place:
rate * [src_H] = num_drains * rate * [val_H]
Since src_H is catalytic (always 1) and rates are uniform:
val_H = 1 / num_drains
Rare hands have fewer drains, accumulate more tokens, and produce higher equilibrium values. Common hands drain fast, accumulate little, and score low. The ranking is strictly determined by topology — no poker knowledge is injected beyond the frequency encoding.
This is the same mechanism as tic-tac-toe, but inverted. In TTT, more connections to win transitions meant higher strategic value, so we inverted the concentration. Here, fewer drain connections mean higher hand value, and the raw concentrations already encode the correct ordering. The topology speaks both ways.
The game itself runs on a separate Petri net (18 places, 16 transitions) that models:
player_turn and house_turn places enforce alternationplayer_chips(100), house_chips(100), pot(0) as token countsplayer_wins, house_wins, game_over as terminal placesThe net enforces valid game flow structurally. A player can't bet when it's the house's turn — player_turn has no tokens. A call can't fire without bet_open. Phase transitions require round_complete. Invalid sequences are impossible, not just checked.
Each game action fires a transition and produces a 128-byte Groth16 proof over BN254:
ZK: deal | prove 53ms | verify 0.8ms | 128B | VALID
ZK: p_check | prove 54ms | verify 0.8ms | 128B | VALID
ZK: h_check | prove 54ms | verify 0.8ms | 128B | VALID
ZK: deal_flop | prove 55ms | verify 0.8ms | 128B | VALID
ZK: p_bet | prove 54ms | verify 0.8ms | 128B | VALID
ZK: h_call | prove 54ms | verify 0.8ms | 128B | VALID
...
ZK: showdown | prove 54ms | verify 0.8ms | 128B | VALID
ZK: resolve_... | prove 55ms | verify 0.8ms | 128B | VALID
The proof chain verifies that the sequence of markings is consistent — each post-state Poseidon hash becomes the next pre-state hash. A Groth16 verifier on-chain can validate the entire game history in ~0.8ms per action.
The setup cost is low because the game net is smaller than the TTT game net (16 transitions vs 35). The transition multiplexer — boolean selectors gating all transitions for a single proving key — compiles in ~46ms.
Card dealing uses commit-reveal with Poseidon hashing:
Poseidon(seed) as a commitmentdeck[0..2] player, deck[2..4] house, deck[4..9] communityPoseidon(seed) == commitment and derive the cards independentlyIf the house doesn't reveal within the timeout window, the player claims the pot. The commitment is binding — the house can't change the shuffle after seeing how the game unfolds.
The integer reduction values become payout multipliers in the Solidity contract. The winner takes the pot plus a bonus proportional to hand strength:
bonus = HAND_STRENGTH[rank] * ante
Where HAND_STRENGTH is baked from the ODE: [1, 1, 2, 3, 4, 6, 8, 16, 32]. A straight flush win pays 32x the ante as bonus. A pair win pays 1x. These aren't tuned by a game designer — they're derived from the network topology of 5-card poker combinations.
The house AI is deterministic: it evaluates its hand against the topology-derived strength values and applies a fixed strategy based on hand strength and pot odds. This determinism means the house strategy is enforceable on-chain — the contract can verify that the house played optimally given its cards.
The implementation is a single Rust example in pflow-rs:
# AI vs AI demo
cargo run --example zk_holdem -p pflow --features zk-arkworks --release -- --demo
# Interactive: human vs house
cargo run --example zk_holdem -p pflow --features zk-arkworks --release
# Export Solidity contracts
cargo run --example zk_holdem -p pflow --features zk-arkworks --release -- --export-solidity ./contracts/
The --export-solidity flag generates Groth16Verifier.sol and ZKHoldem.sol with the verifying key, initial state root, and hand strength values embedded.
This is now the second game where integer reduction derives strategic values from Petri net topology:
| Game | What Reduction Recovers | Net Size |
|---|---|---|
| Tic-Tac-Toe | Position value: center=4, corner=3, edge=2 | 18p x 33t |
| Hold'em | Hand ranking: SF=32, 4K=16, ..., HC=1 | 18p x 113t |
In both cases, the ODE simulation with uniform rates extracts integers (or integer-like ratios) that encode strategic value. No game-specific heuristics. No training data. Just topology.
The question from The Integer Reduction was where the boundary lies between pure counting and genuine dynamics. Poker hand ranking turns out to sit on the counting side — the drain transitions are independent sinks with no shared upstream resources, so the equilibrium values are exactly 1/drains. The game play sits on the dynamics side — competing chip flows, conditional phases, betting interactions. One net for counting, one net for dynamics. The same proof system covers both.