/p/moul/x/games/clock/v0
gno.land/p/moul/x/games/clock/v0
A deadline that actions push forward, and that still ends: New, Bump,
BumpBy, BumpShare, Expired, Remaining, Deadline, HardEnd, Final.
1import "gno.land/p/moul/x/games/clock/v0"
2
3c, _ := clock.New(height, 2000, 300, 100000) // now, window, floor, life
4c.Deadline() // height+2000
5c.BumpShare(h, 20) // a write buys a fifth of the gap to the hard end
6c.Expired(h) // the game is over, and cannot be reopened
7c.Final() // the deadline has hit the hard end: holding now beats acting
Every last-action-wins game has this clock, and written the obvious way it fails in one of two opposite directions.
With a constant extension it never ends. The pot grows faster than the price
of the next action, so the pot-to-price ratio rises, there is always a rational
next action, and the second-to-last actor is always the mark. life is the
terminator: an absolute end, fixed when the clock opens, that no bump can pass.
With a decaying extension it ends too suddenly. A grant that shrinks toward
zero makes the last stretch shorter than the time a transaction needs to land,
so whoever is holding at that moment wins by being unreachable rather than by
paying. floor is the anti-snipe: a bump always leaves at least that much on the
clock.
Four invariants, deliberately in the library and not in the caller:
- An expired clock never restarts. A late action cannot reopen a settled game, which is the difference between a closed pot and a stolen one.
- The deadline never moves backwards. A small grant late in the game leaves the existing deadline alone instead of shortening it.
- A bump always leaves at least the floor, however little there was to share.
- The deadline never passes the hard end, against any grant, including one that would overflow an int64.
The caller owns the policy, the clock owns the guards. BumpBy takes
whatever grant the game computed: a constant, a function of the amount paid,
anything. BumpShare is the one worth knowing, and it is a share of the distance
to the hard end, not of the time remaining: now + (deadline-now)*pct/100 is
always before the deadline, so the obvious formula extends nothing at all. A
share of the gap to the hard end makes each action close a fraction of what is
left, so the grants shrink as the game runs, which is the pressure a constant
grant never relieves.
Times are int64 and the unit is the caller's, block heights or unix seconds, as
long as it is consistent. Nothing here reads the chain, so a realm can test its
whole endgame without one.
Live game: r/moul/x/games/lastwords
· render it at /r/moul/x/games/lastwords/v0.
Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.
🧪 Highly experimental — potentially vibe-coded. Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: DISCLAIMER.