The pack
stackTiming
function stackTiming():Pack<{stackTiming:typeof kit}>
Install the public rules and seeded generator, independently of rendering.
Helpers
createStackTiming
const createStackTiming = (config: StackConfig)=> …
Create a round from config, the same as new StackTiming(config).
generateStackTiming
function generateStackTiming(options: StackOptions = {}): StackLevel
Generate a tower from options and prove it can be built by playing it through the rules. Throws on floors, speed
or tolerance outside their bounds, an unknown difficulty or pattern, a regrow or scoring value out of range, or a
seed outside 0 to 4294967295, and if the replay does not finish the tower.
Classes
StackTiming
class StackTiming
A stacking round: slabs sweep across and are dropped onto the tower, trimmed to what overlaps.
readonly config: StackConfig— The tower it was created with (an owned copy).constructor(c: StackConfig)get state()— A fresh copy of the state.get solved()— Whether every course has been placed.get lost()— Whether a drop missed the stack and ended the round.target(): StackSlab | null— Intended landing footprint; inset courses remain inside the preceding slab.moving(): StackSlab | null— The sweeping slab now: the target footprint shifted along the stage's axis. Null once the round is solved or lost.step(dt: number)— Advance the clock bydtseconds, moving the slab (it holds still once the round is over). Throws unlessdtis from 0 to 0.1, so split longer frames.drop()— Drop the moving slab. Returns{ valid, reason }: invalid with 'Round finished' when the round is over, otherwise valid withreasonperfect,trimmedormiss. A trimmed slab keeps only its overlap; a miss (overlap of 0.035 or less) ends the round.hint()—{ wait }: the seconds from now until the moving slab next crosses its target, where a drop is perfect; null once the round is over. It reads the current state and moves nothing, so stepping exactlywaitand dropping replays a perfect drop.undo()— Take back the last drop; false when there is nothing to undo.redo()— Replay a drop thatundotook back; false when there is none.reset()— Rebuild the round from its tower: only the base slab, no score, history cleared.addStages(stages: readonly StackStage[]): number— Append courses on top of the tower's list, for an endless tower: add more before the player reaches the last. A solved round becomes live again with the next appended course moving. Returns the new number of courses. Throws on a stage value out of range or more than 10000 courses in all.
Types
StackConfig
interface StackConfig
A whole tower: the base slab, the slab thickness, the perfect window and every course.
width: number— Width (x) of the base slab, world units from 0.5 to 20; each course is cut from the one below.depth: number— Depth (z) of the base slab, world units from 0.5 to 20.height: number— Thickness of every slab, world units from 0.1 to 2.perfectTolerance: number— How far, in world units from 0.001 to 0.5, a drop may be from the target and still count as perfect and snap onto it.stages: StackStage[]— The courses to place, from the bottom up: from 1 to 10000, a bound that keeps state copies and undo snapshots small.addStagesappends more during play, for an endless tower.regrow?: StackRegrow— Regrow the slab after a perfect streak, as in Stack; omit (the default) for no regrowth.scoring?: StackScoring— Points per drop; omitted fields score as before: perfect 10 plus 2 per streak, trimmed 5.
StackLevel
interface StackLevel
A generated tower and the proof that it can be built.
seed: number— The seed used.difficulty: Difficulty— The difficulty used.config: StackConfig— The tower to play: pass it tocreateStackTiming.parameters: Required<StackOptions>— The options with every default filled in; pass them back togenerateStackTimingto get the same level.solution: { /** Seconds to step from the moment the course starts moving before dropping it. */ wait: number; }[]— A winning performance: for each course in turn, the seconds to step before dropping. Replay by stepping exactly that long, then callingdrop.metrics: { /** The tower family the level was built from, such as 'spiral'. */ family: string; /** Number of courses. */ floors: number; /** The shortest time, in seconds, the moving slab spends inside the perfect window on any course. */ minimumPerfectWindow: number; /** Courses whose sweep axis differs from the one below. */ axisChanges: number; /** JSON fingerprint of the footprint and each course's axis, motion, coarse speed and target, to tell towers apart. */ structure: string; }— Measurements of the level.
StackOptions
interface StackOptions
Controls for generateStackTiming; every one is optional and checked against its bounds.
seed?: number— Unsigned 32-bit seed, 0 to 4294967295. Default 1.difficulty?: Difficulty—easy,medium,hardorvery-hard. Defaultmedium.floors?: number— Courses to place, a whole number from 2 to 60. Default 8, 12, 18 or 24 by difficulty, plus 0 to 2 from the seed.speed?: number— Base sweep rate, world units a second, from 0.5 to 8. Default 1.8, 2.6, 3.5 or 4.5 by difficulty.perfectTolerance?: number— The perfect window, world units from 0.01 to 0.3. Default 0.18, 0.12, 0.075 or 0.045 by difficulty.pattern?: 'auto' | 'alternating' | 'runs' | 'mixed' | 'terraces' | 'spiral' | 'needle' | 'bridge'— The tower family:auto(default) picks one from the seed. Others are alternating, runs, mixed, terraces, spiral, needle and bridge.regrow?: StackRegrow | null— Copied into the generated tower'sStackConfig.regrow; default null, no regrowth.scoring?: StackScoring | null— Copied into the generated tower'sStackConfig.scoring; default null, the standard scores.
StackRegrow
interface StackRegrow
The Stack-style regrowth reward: after a run of perfect drops each further perfect drop widens the placed slab along the axis it swept, never past the base slab's size on that axis.
after: number— Perfect drops in a row, a whole number from 1 to 100, before a perfect drop starts to regrow the slab.by: number— World units, 0.01 to 5, added to the slab's size on the swept axis by each regrowing perfect drop.
StackScoring
interface StackScoring
Points per drop; every field is optional and defaults as described.
perfect?: number— Points for a perfect drop before the streak bonus, 0 to 1000; default 10.streakBonus?: number— Extra points per drop of the current perfect streak (counting this drop), added to a perfect drop, 0 to 1000; default 2.trimmed?: number— Points for a trimmed drop, 0 to 1000; default 5.
StackSlab
interface StackSlab
A placed or moving slab: its centre and size in world units.
x: number— Centre across the width, world units.y: number— Height in world units: the base slab is 0 and each course sitsconfig.heightabove the one below.z: number— Centre across the depth, world units.width: number— Size along x, world units.depth: number— Size along z, world units.
StackStage
interface StackStage
One course of the tower: how its moving slab sweeps back and forth until the player drops it.
axis: 'x' | 'z'— The axis the moving slab sweeps along:x(its width) orz(its depth).speed: number— Sweep rate in world units a second, from 0.1 to 20: thesinemotion peaks at this speed and thelinearone moves at 2/π of it.travel: number— Half the sweep range, world units from 1 to 30: the slab swings between -travel and +travel either side of the target.phase: number— Starting phase of the sweep in radians, from -100 to 100; the slab begins at the offsetsin(phase) * travel.motion: 'linear' | 'sine'—sineslows towards each end;linearkeeps a constant speed and turns sharply.target?: { x: number; z: number; scaleX: number; scaleZ: number; }— Where this course should land, as an inset of the slab below. Default the whole slab below.scaleXandscaleZ(0.5 to 1) scale its width and depth, andxandz(-1 to 1) slide the smaller footprint within the old one, -1 and 1 being flush with its edges.
StackState
interface StackState
The round's state, an owned copy; StackTiming.state makes a fresh one each read.
time: number— Seconds stepped since the round started or was reset.stageTime: number— Seconds since the current slab started to move; back to 0 at every drop.placed: StackSlab[]— The slabs set so far, the base first; the next course to place isconfig.stages[placed.length - 1].offcuts: (StackSlab & { born: number; vx: number; vz: number; })[]— The trimmed overhangs, for the renderer: each carriesborn, thetimeit was cut, and a sideways drift velocity (vx,vz). They never affect later contacts.streak: number— Consecutive perfect drops now; a trimmed drop or a miss resets it to 0.bestStreak: number— The longest streak this round.score: number— Points: by default a perfect drop scores 10 plus twice the streak so far (counting that drop), a trimmed one 5, a miss 0;StackConfig.scoringchanges the amounts.lost: boolean— Whether a drop missed the stack and ended the round.last: 'ready' | 'perfect' | 'trimmed' | 'miss'— How the last drop went:readybefore the first, thenperfect,trimmedormiss.
StackTimingApi
type StackTimingApi=typeof kit
Public generator and rule constructors exposed on composed games.


