The pack
rooftopRunner2d
function rooftopRunner2d():Pack<{rooftopRunner2d:typeof rules}>
Install renderer-independent runner rules and generation.
Helpers
createRooftop
function createRooftop(config: RooftopConfig)
Start a run on config, validated and copied. The result has config, state (a copy), tick(input?) (one 1/60 s
tick; does nothing once won or lost; returns the state), reset(), rewind(frames = 120), recording() (the inputs
played up to now) and replay(frames) (plays a RooftopFrame[]; at most 24000 ticks). Throws on an invalid config.
createRooftopStream
function createRooftopStream(options: RooftopOptions)
Start an endless run. It begins like a 6-roof generateRooftop course, and each time the runner stands on the
last roof it appends a certified 5-roof stretch. The result has state (the runner's state plus chunk, the
stretches appended), config (the terrain still kept), tick(input?), reset(), rewind(frames = 120),
recording() and solveAhead() (an input route to win the stretch ahead, or null).
generateRooftop
function generateRooftop(options: RooftopOptions): RooftopLevel
Make a course for options and prove it winnable by planning and replaying a solution. Throws on invalid options, or if no candidate course can be solved within 100 tries.
planRooftop
function planRooftop(config: RooftopConfig, maxFrames = 12000, start?: RooftopState): RooftopFrame[] | null
Searches takeoff time AND hold length at the actual incoming speed for each landing.
ROOFTOP_STEP
const ROOFTOP_STEP: number
Seconds in one fixed tick of the rules: 1/60.
rooftopStyles
const rooftopStyles: readonly ["skyline", "girders", "obstacles", "mixed"]
Every RooftopStyle, for menus and validation.
validateRooftop
function validateRooftop(c: RooftopConfig)
Check a course. Throws on the first fault: a number out of range or not finite, a roof or obstacle with a duplicate id, a bad shape or an unknown kind.
Types
Roof
type Roof = { … }
A roof the runner can land on, in world units with x to the right and y down.
id: string— Unique among the config's roofs and obstacles;RooftopState.supportnames it while the runner stands on it.left: number— The x of the roof's left edge.right: number— The x of the roof's right edge; at least 30 units pastleft.top: number— The y of the walking surface; a larger y is lower.kind: 'building' | 'girder' | 'tower'—'building'and'tower'have solid sides that end the run if struck below the surface; a'girder''s side is only 18 units deep.
RoofObstacle
type RoofObstacle = { … }
Something on or above a roof that the runner must clear, in world units.
id: string— Unique among the config's roofs and obstacles; a clipped crate's id is listed inRooftopState.hits.x: number— The x of the obstacle's centre.y: number— The y of the obstacle's centre.w: number— Width, at least 1.h: number— Height, at least 1.kind: 'crate' | 'hazard' | 'ceiling'—'crate'is clipped once and costs speed;'hazard'and'ceiling'(an overhead beam) end the run on contact.
Rooftop
type Rooftop = ReturnType<typeof createRooftop>
A run made by createRooftop.
RooftopConfig
type RooftopConfig = { … }
A finite course and the runner's motor, in world units and seconds. validateRooftop states the accepted ranges.
roofs: Roof[]— The roofs, 1 to 200.obstacles: RoofObstacle[]— The obstacles, 0 to 300.start: { /** The x of the centre. */ x: number; /** The y of the centre; standing on a roof at \top` it is `top - 13`. */ y: number; }` — Where the runner's 16 by 26 body starts: its centre.finish: number— The x the runner must reach while on a roof to win.speed: number— The starting horizontal speed, in units a second; 100 to 700.maxSpeed: number— The horizontal speed the run accelerates to, in units a second; fromspeedup to 900.acceleration: number— Horizontal acceleration, in units a second squared; 0 to 100.gravity: number— Downward acceleration, in units a second squared; 400 to 1800.jumpSpeed: number— Upward speed a jump starts with, in units a second; 200 to 700.deathY: number— Falling below this y loses the run.
RooftopFrame
type RooftopFrame = { … }
An input held for a run of ticks: a replay or a solution is a list of these.
input: RooftopInput— The input applied on every tick of the run.frames: number— How many consecutive ticks of 1/60 s the input is held; a whole number from 1 to 24000.
RooftopInput
type RooftopInput = { … }
One tick's input.
jump?: boolean— Whether the jump button is held this tick: a press starts a jump, releasing while rising cuts it short. Anything but a boolean throws.
RooftopLevel
type RooftopLevel = { … }
A generated course with the input route that proves it can be won.
seed: number— The seed it was made from.parameters: Required<RooftopOptions>— The options with every default filled in.config: RooftopConfig— The course, ready forcreateRooftop.solution: RooftopFrame[]— An input route, compressed into runs of ticks, that replays to a win.metrics: { /** Ticks the solution takes to reach the finish. */ frames: number; /** Jumps the solution makes. */ jumps: number; /** Which candidate layout (counting from 0) was the first that could be solved. */ attempt: number; /** The roofs and obstacles as JSON: equal signatures mean equal layouts. */ signature: string; /** Roofs in the course. */ roofs: number; }— Facts about the solved course.
RooftopOptions
type RooftopOptions = { … }
What generateRooftop takes.
seed: number— Unsigned 32-bit integer; the same seed and options make the same course.difficulty?: Difficulty—'easy','medium','hard'or'very-hard'; default'medium'. Harder is faster, with wider gaps and bigger height changes.style?: RooftopStyle— The kind of course; default'mixed'.roofs?: number— Roofs in the course, 3 to 30; default 6 plus 2 for each difficulty step plus the seed modulo 3.
RooftopState
type RooftopState = { … }
Where the runner is and how the run stands; plain data, copied on every read.
x: number— The x of the body's centre, in world units.y: number— The y of the body's centre, in world units; a larger y is lower.vx: number— Horizontal speed, in units a second.vy: number— Vertical speed, in units a second; negative is rising.frame: number— Ticks played since the start.grounded: boolean— Whether the runner stands on a roof.support: string | null— The id of the roof it stands on, or null in the air.held: boolean— Whether jump was held on the last tick; a jump needs a fresh press.coyote: number— Ticks left (at most 4) in which a jump is still allowed after leaving a roof.jumps: number— Jumps made so far.hits: string[]— The ids of the crates already clipped.won: boolean— Whether the finish was reached.lost: boolean— Whether the run has ended in a loss.reason: string— A sentence on how the run stands, such as why it was lost.distance: number— The furthest the runner has got from the start along x, in world units.
RooftopStream
type RooftopStream = ReturnType<typeof createRooftopStream>
An endless run made by createRooftopStream.
RooftopStyle
type RooftopStyle = 'skyline' | 'girders' | 'obstacles' | 'mixed'
The look of a generated course: 'skyline' buildings and towers, 'girders' girder roofs between buildings, 'obstacles' crates and hazards on the roofs, 'mixed' a bit of each.


