Package impl is the third rules engine for gno.land/r/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/shots: impl/v2 with a chain that plays well, a pot that must be won by two goals, and a share of it for the game's creator.
It owns the game state machine and pot rules; the state itself, and the coins, stay in the shots realm, reached through shots.Store.
THE CHAIN'S PLAY. impl/v1 and v2 read the chain's keeper plan and kick as uniform draws from the seed, which a player who has learnt the best shot and keeper plan beats about 97% of the time. Here the seed's first two bytes pick the chain's move from equilibrium mixes, worked out with the physics engine for the default constants (chainKeeper, chainKicks): against them no shot scores more than about 79%, and no keeper plan concedes less than about 77%, roughly the conversion rate of real penalties. Games with other constants use the same mixes, kicks scaled to their goal and plans kept in their mode (see keeperFor and kickFor), so they are only near equilibrium; a game may therefore change only four constants, a little (see checkConstants).
THE POT. Each game's creator chooses how much a challenger must win by (winBy in the config string, 1..3, DefaultWinBy if left out; WinBy reads it). Win by 1 is a normal shootout: five kicks each, ending as soon as one side cannot be caught, then sudden death. Win by 2 or 3 is five kicks each at most with no sudden death, won only by that many goals, ending as soon as that is certain either way. Against the chain above, a player who plays the best moves wins about 47%, 11% or 2.3% of shootouts, one who plays at random about 9.5%, 1.5% or 0.2%: the larger the margin, the longer a pot grows before it is won. The winner is paid all of it but CreatorPercent, which goes to the game's creator for having put up the seed pot. Games created before this implementation keep the rules they were made with: win by 1.
SETTLING. Every kick is recorded in the session while it is played (its ten inputs, seed and result: about 3 KB, paid for by the player's storage deposit). When the shootout ends (won, lost or forfeited) the session is rewritten without its kicks, keeping player, entry fee, score, status and heights, and a won game keeps its pot (the prize it paid) and winner. The deposit for the bytes freed goes back to the account that made the final move: on the deciding kick, the player (on a chain whose ugnot is transfer-restricted, the storage fee collector instead). Each kick remains in the transaction that played it, as that call's return value and Kick event, so it can still be re-run from the chain's history; shots' VerifySession has nothing left to re-run for a settled shootout.
Game: open --join--> playing --player wins--> won (pot paid out, closed)
Example
1^ |
2+----player loses-+ (entry fee stays in the pot)
3open --creator cancels, nobody played yet--> cancelled (pot refunded)
Session: each round the player shoots, then keeps. Every move is played out in the transaction that makes it, from 32 bytes derived from that block's timestamp (see entropy).
ENTROPY. The seed is SHA-256 of the block time (unix nanoseconds) and the kick's identity (game, session, kick number), so two kicks in one block still differ. The block time is not known when the player signs, but it is not secret either: the proposer chooses it within consensus bounds, and anything else running in the same transaction can read the result. In particular a transaction that makes a move and then, in a later message, reads the session and fails on a bad outcome reverts the whole kick, so a player can retry until they like the result (for the price of gas). Fine for play money; a commit-reveal or a VRF is needed before real stakes, and an upgraded implementation can switch without touching games or pots.