The pack
dropTower
function dropTower():Pack<{dropTower:typeof kit}>
Install the public rules and seeded generator, independently of rendering.
Helpers
createDropTower
const createDropTower = (config: TowerConfig, controls: TowerControls = {})=> …
Create a tower from config, played with controls (see TowerControls; the defaults are the reference game's).
Throws if a value is outside its range, the preset or a sector kind is unknown, or the
layers do not descend.
generateDropTower
function generateDropTower(o: TowerOptions = {}): TowerLevel
Generate a tower for o and certify it by replaying a winning solution; medium and harder towers must also defeat
idle play. Throws if an option is out of range, or if no tower certifies within 80 attempts.
towerPhysics
function towerPhysics(preset: 'helix' | 'smash'): TowerPhysics
The physics generateDropTower uses for a preset when TowerOptions.physics leaves a field out.
Classes
DropTower
class DropTower
A drop-tower round: continuous vertical flight of a bouncing ball and angular-footprint contacts with turning
platforms. Rules only; it draws nothing. Build one with createDropTower.
readonly config: TowerConfig & TowerPhysics— The validated tower configuration: a copy of the one passed in, with the optional physics filled in.readonly controls: Required<TowerControls>— The pointer controls in force, defaults filled in.constructor(c: TowerConfig, controls: TowerControls = {})get state()— A copy of the current state; changing it does not affect play.get solved()— Whether the ball has passed every platform without losing.get lost()— Whether a hazard has ended the round.sector(layer = this.data.layer, time = this.data.time, rotation = this.data.rotation): TowerSector— What lies under the ball's footprint on platformlayerattimeseconds with the tower turnedrotationradians; each defaults to now. Samples the centre and both edges of the footprint: 'gap' if all three are open or the layer does not exist, 'hazard' if any is a hazard, otherwise 'safe'.rotate(delta: number)— Turn the tower bydeltaradians. Only turns in the helix preset while the round is live; otherwise it does nothing. Throws unlessdeltais finite and within -2π to 2π.hold(value: boolean)— Set whether the hold input is down. In smash, while held the ball dives at 7 world units per second and breaks safe platforms, but a hazard then ends the round.checkpoint()— Mark the present state as somewhereundocan return to (an undo point, added to the automatic one before each landing). Not the same as the automatic checkpoints everycheckpointEverylayers, whichretryCheckpointrestores.press()— The player puts a finger or the mouse down: an undo point (unlesscontrols.undoOnPressis false), and in smash the hold goes down.drag(dx: number)— The pressed pointer moveddxworld units across: in helix the tower turnsdx × controls.dragTurnradians, at mostcontrols.maxDragTurna call. Throws unlessdxis finite.release()— The player lets go: in smash the hold comes up.addLayers(layers: readonly TowerLayer[]): number— Append platforms below the last one, for an endless or streamed descent: each must be more than 0.15 below the one before, the first more than 0.15 below the current last layer. Append before the ball passes the last platform; a solved tower becomes live again and the ball falls on. Returns the new number of layers. Throws on a value out of range, layers that do not descend, or more than 10000 layers in all.step(dt: number)— Advance the simulation bydtseconds in fixed 1/120-second ticks; any remainder carries over to the next call. Throws unlessdtis finite and 0 to 0.1.hint()— The demonstration controller's input for the next 1/120-second step,{ rotate, hold }, or null once the round is solved or lost. It uses the same bounded rotation (at mostturnSpeed/ 120 radians a step) and hold input as a player: helix turns towards the nearest gap that will be under the ball when it lands; smash holds unless a hazard is under the ball now or will be when it lands.undo()— Return to the last undo point: just before the most recent landing, or the lastcheckpoint(). From the live present the state undo leaves is kept, soredocan return to it.redo()— Return to the stateundoleft. Returns false when there is nothing to redo.reset()— Start again from the top: initial state, no checkpoint and no undo history.retryCheckpoint()— Return to the latest automatic checkpoint (the start if there is none), with the ballstartHeightabove that platform and the score and streak as they were then. Clears the undo history.
Types
DropTowerApi
type DropTowerApi=typeof kit
Public generator and rule constructors exposed on composed games.
TowerConfig
interface TowerConfig
A complete tower: the platforms, the ball and the physics. Pass it to createDropTower; generateDropTower builds
one.
preset: 'helix' | 'smash'— 'helix': turn the tower to bring a gap under the ball; a hazard ends the round. 'smash': turning is disabled, holding dives the ball and breaks safe platforms, and a hazard hit while holding ends the round.layers: TowerLayer[]— Platforms from the top down, 1 to 10000 of them, each lower than the last.addLayersappends more during play up to the same bound, which only keeps the per-read state copy and the undo snapshots small.gravity: number— Downward acceleration in world units per second squared; 2 to 40.bounce: number— Upward speed in world units per second the ball gets when it bounces off a platform; 1 to 10.ballRadius: number— Ball radius in world units, 0.05 to 0.4. The ball rests with its centre this far above a platform.trackRadius: number— Distance of the ball from the tower axis in world units, 1 to 4. WithballRadiusit sets the ball's angular footprint of plus or minus asin(ballRadius / trackRadius) radians about its centre.turnSpeed: number— Fastest turn, in radians per second, thathint()asks for; 0.2 to 12.rotateitself accepts any single turn of up to a full circle.smashAfter: number— Consecutive descents (through gaps or smashed platforms) that charge a power smash, a whole number from 2 to 10. Once the streak reaches this, the next platform breaks whatever its sectors.checkpointEvery: number— Layers between automatic checkpoints forretryCheckpoint, a whole number from 0 to 20; 0 turns them off.maxFallSpeed?: number— Fastest the ball falls, in world units per second, 1 to 40; default 8 (its terminal speed under gravity).diveSpeed?: number— Speed the ball dives at while the hold input is down in the smash preset, world units per second, 1 to 40; default 7.startHeight?: number— Height of the ball's centre above the top platform at the start, and above the checkpoint platform afterretryCheckpoint, in world units; more thanballRadiusand at most 20; default 1.4.
TowerControls
interface TowerControls
How a player's pointer plays a tower, for press, drag and release. Every field is optional.
dragTurn?: number— Radians the tower turns per world unit of horizontal drag, in helix; above 0. Default 0.012.maxDragTurn?: number— The most onedragcall turns the tower, radians, 0 to 2π. Default 1.undoOnPress?: boolean— Whether eachpressmarks an undo point, soundoreturns to the start of that gesture. Default true.
TowerLayer
interface TowerLayer
One horizontal platform: a ring of equal angular sectors that turns about the tower axis.
y: number— Height of the platform's top surface in world units, any finite value. Layers must descend: each is more than 0.15 below the one before. Generated towers start at 0 and go down.phase: number— Angle in radians the sector pattern is turned at time 0, added to the player's rotation and the layer's spin; -100 to 100.spin: number— Angular speed of the platform in radians per second, added to the player's rotation; the sign sets the direction and 0 holds it still. -4 to 4.radius: number— Outer radius of the platform in world units, fromtrackRadius + ballRadiusto 8. The contact rules ignore it; it is for presentation.shape: 'circle' | 'square' | 'hexagon'— Outline of the platform: 'circle', 'square' or 'hexagon'. The contact rules ignore it; it is for presentation.sectors: TowerSector[]— The platform's equal angular slices in order around the ring, from 8 to 40 of them.
TowerLevel
interface TowerLevel
A generated, certified tower with the settings and measurements that describe it.
seed: number— The seed the level was generated from.difficulty: Difficulty— The difficulty the level was generated for.config: TowerConfig— The tower to play; pass it tocreateDropTower.parameters: Required<TowerOptions>— Every option with its default filled in. Pass it back togenerateDropTowerto reproduce this level.solution: { /** Radians to pass to \rotate` for this step. / rotate: number; /* Value to pass to `hold` for this step. */ hold: boolean; }[]` — A winning performance replayed during generation: one entry per 1/120-second step, giving the turn and hold input for that step.metrics: { /** * Layout family: 'helix' for the helix preset; for smash one of 'cone', 'hourglass', 'drums', 'corkscrew', * 'split-sectors' or 'alternating-bands', chosen by seed. */ family: string; /** Number of platforms. */ floors: number; /** Hazard sectors in the whole tower. */ hazardSectors: number; /** Gap sectors in the whole tower; always 0 for smash. */ gapSectors: number; /** * Platforms whose phase differs from the one above by more than 0.1 radians, so the player has to turn between * them. */ rotationChanges: number; /** Seconds the certifying solution takes to finish the tower. */ duration: number; /** * Platforms cleared by idle play (no turning; held throughout in smash). Medium and harder levels are only * kept when this stays at or below 65% of the floors. */ idleCleared: number; /** * JSON fingerprint of each platform's shape, radius, spacing, spin direction, sectors and phase, rounded, used * to tell towers apart. */ structure: string; }— Measurements of the generated level.
TowerOptions
interface TowerOptions
Controls for generateDropTower. Every field is optional and defaults as described; a value outside its range
throws.
seed?: number— Unsigned 32-bit integer, zero included, that fixes the layout; default 1.difficulty?: Difficulty— 'easy', 'medium', 'hard' or 'very-hard'; sets the defaults for floors, gap width, hazards and spin and how far each layer turns; default 'medium'.preset?: 'helix' | 'smash'— 'helix' (turn to find gaps) or 'smash' (hold to break safe platforms); default 'helix'.floors?: number— Number of platforms, 3 to 50; default 9, 14, 20 or 28 by difficulty, plus seed % 3.sectors?: number— Sectors per platform, 12 to 32; default 24.gapWidth?: number— Consecutive gap sectors per helix platform, from 2 up to half ofsectors; default 7, 5, 4 or 3 by difficulty. Smash cuts no gaps, but the value still limits how manyhazardsfit.hazards?: number— Hazard sectors per platform, from 1 tosectors - gapWidth - 2; default 3, 6, 9 or 12 by difficulty.spin?: number— Platform spin in radians per second, 0 to 2.5. Default for helix is 0 on easy and medium, 0.3 on hard and 0.45 on very-hard; for smash 0.65, 0.95, 1.35 or 1.7 by difficulty.shape?: 'auto' | 'circle' | 'square' | 'hexagon'— Platform outline: 'auto' picks one from the seed (and the section, in the alternating-bands family), or force 'circle', 'square' or 'hexagon'; default 'auto'.physics?: Partial<TowerPhysics>— Ball and physics settings for the generated tower; each field left out takestowerPhysics(preset)'s value (gravity 14, bounce 4.8 for helix and 2.8 for smash, ball radius 0.16, track radius 2.25, turn speed 6, smash after 3, checkpoint every 5, max fall speed 8, dive speed 7, start height 1.4). Platform radii follow the track and ball radius. The level is certified with these settings, so a combination its controller cannot win throws.
TowerPhysics
type TowerPhysics = Required<Pick<TowerConfig, 'gravity' | 'bounce' | 'ballRadius' | 'trackRadius' | 'turnSpeed' | 'smashAfter' | 'checkpointEvery' | 'maxFallSpeed' | 'diveSpeed' | 'startHeight'>>
The ball and physics settings of a tower, every one filled in: what TowerOptions.physics sets for a generated tower
and what generateDropTower records in parameters.physics. See TowerConfig for each field's meaning and range.
TowerSector
type TowerSector = 'safe' | 'hazard' | 'gap'
One angular slice of a platform: 'safe' is solid, 'hazard' is solid and ends the round when hit (always in helix, only while holding in smash), 'gap' is open so the ball falls through.
TowerState
interface TowerState
Everything that changes during play: the clock, the tower's rotation, the ball, the streak and score, and the outcome.
time: number— Seconds simulated since the start or the lastreset, in steps of 1/120.rotation: number— The player's tower rotation in radians, wrapped to 0 up to 2π. Stays 0 in the smash preset.y: number— Height of the ball's centre in world units, on the same axis asTowerLayer.y; startsstartHeight(default 1.4) above the top platform.vy: number— Vertical velocity in world units per second; negative is falling. Falling is capped atmaxFallSpeed(default 8), and holding in smash sets it to minusdiveSpeed(default 7).layer: number— Index of the platform the ball is falling towards; equals the number of layers once the tower is solved.streak: number— Consecutive descents since the last bounce, counting towards a power smash; reset by a bounce and after a power smash.bestStreak: number— Higheststreakreached this round.score: number— Points so far: each descent adds 10 times (1 plus the streak after it).lost: boolean— Whether a hazard has ended the round.holding: boolean— Whether the hold input is down, as set byhold.broken: number[]— Indices of the platforms the ball smashed through, as opposed to passing through a gap.last: string— Short message about the latest event, such as 'Bounced' or 'Power smash!', for display; 'Ready' before play starts.checkpoint: number— Index of the layer the latest automatic checkpoint was taken at; 0 before the first.


