@johnmorrisdotca/houseki
colourChains fallingTriplets gemSwap magneticBlocks nature stoneCollapse
namespace colourChains
import { colourChains } from "@johnmorrisdotca/houseki"; // or everything in it from "@johnmorrisdotca/houseki/colourChains"
Everything the @johnmorrisdotca/houseki/colourChains entry point exports, as one namespace.
namespace fallingTriplets
import { fallingTriplets } from "@johnmorrisdotca/houseki"; // or everything in it from "@johnmorrisdotca/houseki/fallingTriplets"
Everything the @johnmorrisdotca/houseki/fallingTriplets entry point exports, as one namespace.
namespace gemSwap
import { gemSwap } from "@johnmorrisdotca/houseki"; // or everything in it from "@johnmorrisdotca/houseki/gemSwap"
Everything the @johnmorrisdotca/houseki/gemSwap entry point exports, as one namespace.
namespace magneticBlocks
import { magneticBlocks } from "@johnmorrisdotca/houseki"; // or everything in it from "@johnmorrisdotca/houseki/magneticBlocks"
Everything the @johnmorrisdotca/houseki/magneticBlocks entry point exports, as one namespace.
namespace nature
import { nature } from "@johnmorrisdotca/houseki"; // or everything in it from "@johnmorrisdotca/houseki/nature"
Everything the @johnmorrisdotca/houseki/nature entry point exports, as one namespace.
namespace stoneCollapse
import { stoneCollapse } from "@johnmorrisdotca/houseki"; // or everything in it from "@johnmorrisdotca/houseki/stoneCollapse"
Everything the @johnmorrisdotca/houseki/stoneCollapse entry point exports, as one namespace.
@johnmorrisdotca/houseki/falling-triplets
Action actionsForTick advanceTicks applyAction CampaignLevel campaignManifest CampaignManifest ChallengeGoal ChallengeGoalChain ChallengeGoalEmpty ChallengeGoalTargets ChallengeHint ChallengeOptions ChallengeSettings Colour compactBoard createChallenge createGame createInputScheduler createLevel CreateOptions decodeGame DifficultyMetrics encodeGame findMatches GameEvent GameState Gem getLevel getTutorial landingY legalActions levelManifest LocalizedText Mode nextInt nextUint32 Phase Piece queueInputEdge RecordedAction releaseAllActions restartGame seedState setHeldAction Settings statusOf Transition Triplet TutorialDefinition tutorialManifest TutorialStep Wave
type Action
type Action = { readonly kind: 'left' | 'right' | 'soft-drop' | 'hard-drop' | 'place' | 'cycle-forward' | 'cycle-backward' | 'pause' | 'resume' | 'hint' | 'tick' };
function actionsForTick
actionsForTick(state: InputSchedulerState): readonly [readonly Action[], InputSchedulerState]
Resolves canonical actions in the design's per-tick order.
function advanceTicks
advanceTicks(state: GameState, ticks: number): Transition
Advances fixed 60 Hz game ticks. Arcade timing is applied to the active piece.
function applyAction
applyAction(state: GameState, action: Action): Transition
Applies and records one immutable action; rejected actions preserve state identity.
type CampaignLevel
interface CampaignLevel { readonly id: string; readonly seed: string; readonly number: number; readonly title: LocalizedText; /** SHA-256 fingerprint of the colour- and mirror-canonical duplicate key. */ readonly canonicalKeyHash: string; readonly width: number; readonly height: number; readonly colourCount: 4 | 5 | 6; readonly goal: ChallengeGoal; readonly board: readonly (Gem | null)[]; readonly queue: readonly Tr…
const campaignManifest
campaignManifest: CampaignManifest
type CampaignManifest
interface CampaignManifest { readonly count: number; readonly candidatePoolCount: number; readonly generationRevision: string; readonly gradingVersion: string; readonly category: string; readonly curationPolicy: string; readonly orderingPolicy: string; readonly grading: Readonly<Record<string, unknown>>; readonly sampleBudget: number; readonly checksum: string; readonly levels: readonly CampaignLevel[]; }
type ChallengeGoal
type ChallengeGoal = ChallengeGoalTargets | ChallengeGoalChain | ChallengeGoalEmpty;
type ChallengeGoalChain
interface ChallengeGoalChain { readonly type: 'chain'; readonly minimumChain: number }
type ChallengeGoalEmpty
interface ChallengeGoalEmpty { readonly type: 'empty' }
type ChallengeGoalTargets
interface ChallengeGoalTargets { readonly type: 'targets'; readonly targetIds: readonly number[] }
type ChallengeHint
interface ChallengeHint { readonly x: number; readonly orientation: 0 | 1 | 2 }
type ChallengeOptions
interface ChallengeOptions { readonly seed?: string; readonly width?: number; readonly height?: number; readonly colourCount?: 4 | 5 | 6; readonly board: readonly (Gem | null)[]; readonly queue: readonly Triplet[]; readonly goal: ChallengeGoal; readonly witness?: readonly ChallengeHint[] }
type ChallengeSettings
interface ChallengeSettings extends Settings { readonly mode: 'challenge'; readonly goal: ChallengeGoal['type']; readonly challengeBoard: readonly (Gem | null)[]; readonly challengeQueue: readonly Triplet[] }
type Colour
type Colour = 'red' | 'blue' | 'green' | 'gold' | 'purple' | 'teal';
A gem colour ID.
function compactBoard
compactBoard(board: readonly (Gem | null)[], width: number, height: number): readonly (Gem | null)[]
Compacts each column downward while preserving the top-to-bottom order of survivors.
function createChallenge
createChallenge(options: ChallengeOptions): GameState
Creates a challenge after validating its authored board, finite queue, goal, and witness.
function createGame
createGame(options?: CreateOptions): GameState
Creates a deterministic empty game with an active piece and three-piece preview.
function createInputScheduler
createInputScheduler(): InputSchedulerState
Creates an empty fixed-tick input scheduler state.
function createLevel
createLevel(id: string | number): GameState
Construct a fresh validated challenge state for a stable level ID or display number.
type CreateOptions
interface CreateOptions { readonly mode?: Exclude<Mode, 'challenge'>; readonly seed?: string; readonly preset?: 'compact' | 'narrow' | 'standard' | 'wide' | 'extraWide' | 'tall' | 'deep' | 'large'; readonly width?: number; readonly height?: number; readonly colourCount?: 4 | 5 | 6; readonly pieceLimit?: number }
function decodeGame
decodeGame(text: string): GameState
Replays and validates a bounded save, then verifies its checkpoint against the derived state.
type DifficultyMetrics
interface DifficultyMetrics { readonly seededGoalSuccesses: number; readonly seededGoalSamples: number; readonly seededGoalSuccessRate: number; readonly legalChoiceBreadth: number; readonly goalPreservingChoices: number; readonly probedChoices: number; readonly goalPreservingChoiceShare: number; readonly forcedChoiceShare: number; readonly setupPiecesBeforePayoff: number; readonly requiredChainDepth: number; readonl…
function encodeGame
encodeGame(state: GameState): string
Encodes the authoritative settings, action recording, and derived checkpoint.
function findMatches
findMatches(board: readonly (Gem | null)[], width: number, height: number): readonly MatchWave[]
Finds the union of every maximal horizontal, vertical, and diagonal run of at least three.
type GameEvent
interface GameEvent { readonly type: string; readonly [key: string]: unknown }
type GameState
interface GameState { readonly game: 'falling-triplets'; readonly rules: 'triplets-1'; readonly settings: Settings; readonly board: readonly (Gem | null)[]; readonly phase: Phase; readonly pausedPhase?: Phase; readonly active: Piece | null; readonly next: readonly Triplet[]; readonly bag: readonly Colour[]; readonly randomState: number; readonly nextId: number; readonly score: number; readonly maxChain: number; read…
type Gem
interface Gem { readonly id: number; readonly colour: Colour; readonly target?: boolean }
A settled or active gem with a stable run-local identity.
function getLevel
getLevel(id: string | number): CampaignLevel | undefined
Find a level by stable content ID or its current one-based display number.
function getTutorial
getTutorial(id: string): TutorialDefinition | undefined
Find a lesson by its persistent ID.
function landingY
landingY(state: GameState): number | null
Returns the exact landing row for the current rigid triplet.
function legalActions
legalActions(state: GameState): readonly Action[]
Lists the core actions currently available to the player.
const levelManifest
levelManifest: readonly CampaignLevel[]
type LocalizedText
interface LocalizedText { readonly en: string; readonly ja: string }
type Mode
type Mode = 'relaxed' | 'arcade' | 'daily' | 'challenge';
function nextInt
nextInt(state: number, n: number): readonly [number, number]
Draws an unbiased integer and updated state using rejection sampling.
function nextUint32
nextUint32(state: number): readonly [number, number]
Advances the reference xorshift32 source.
type Phase
type Phase = 'falling' | 'clear-mark' | 'clear-remove' | 'gravity' | 'paused' | 'won' | 'lost' | 'finished';
type Piece
interface Piece { readonly gems: readonly [Gem, Gem, Gem]; readonly x: number; readonly y: number; readonly orientation: 0 | 1 | 2; readonly level: number; readonly descent: number; readonly lockTicks: number; readonly lockResets: number; readonly lockStarted: boolean }
function queueInputEdge
queueInputEdge(state: InputSchedulerState, action: Action): InputSchedulerState
Queues a one-shot cycle, drop, or pause edge for the next logical tick.
type RecordedAction
interface RecordedAction { readonly tick: number; readonly ordinal: number; readonly action: Exclude<Action, { readonly kind: 'tick' }> }
One accepted canonical action and its logical tick position.
function releaseAllActions
releaseAllActions(state: InputSchedulerState): InputSchedulerState
Releases every held action after blur, pause, or pointer cancellation.
function restartGame
restartGame(state: GameState): GameState
Recreates the same authored mode, seed, and challenge configuration from its original setup.
function seedState
seedState(seed: string): number
Hashes a string seed with UTF-8 FNV-1a and returns a nonzero uint32 state.
function setHeldAction
setHeldAction(state: InputSchedulerState, action: HoldAction, held: boolean): InputSchedulerState
Starts or releases one held action; opposite lateral holds cancel during scheduling.
type Settings
interface Settings { readonly mode: Mode; readonly width: number; readonly height: number; readonly colourCount: 4 | 5 | 6; readonly seed: string; readonly pieceLimit?: number; readonly goal?: 'targets' | 'chain' | 'empty'; readonly targetIds?: readonly number[]; readonly minimumChain?: number; readonly challengeBoard?: readonly (Gem | null)[]; readonly challengeQueue?: readonly Triplet[]; readonly witness?: readonl…
function statusOf
statusOf(state: GameState): Readonly<{ phase: GameState["phase"]; score: number; maxChain: number; reason?: string; }>
Returns a plain status summary suitable for UI labels.
type Transition
interface Transition { readonly state: GameState; readonly events: readonly GameEvent[]; readonly accepted: boolean; readonly reason?: string }
type Triplet
type Triplet = readonly [Colour, Colour, Colour];
type TutorialDefinition
interface TutorialDefinition { readonly id: string; readonly title: LocalizedText; readonly objective: LocalizedText; readonly setup: { readonly board: readonly (Gem | null)[]; readonly queue: readonly Triplet[]; readonly goal: ChallengeGoal; readonly witness: readonly ChallengeHint[]; }; readonly steps: readonly TutorialStep[]; readonly tags: readonly string[]; }
const tutorialManifest
tutorialManifest: readonly TutorialDefinition[]
type TutorialStep
interface TutorialStep { readonly instruction: LocalizedText; readonly action: string }
type Wave
interface Wave { readonly clearedIds: readonly number[]; readonly cells: readonly number[]; readonly chain: number; readonly points: number }
@johnmorrisdotca/houseki/colour-chains
Action actionsForTick advanceTicks applyAction applyScheduledActions campaignManifest Cell ChainCampaignLevel ChainCampaignManifest ChainContentData ChainDifficultyMetrics ChainTutorialDefinition ChainTutorialStep ChallengeGem ChallengeGoal ChallengeOptions Colour createChallenge createGame createInputScheduler createLevel CreateOptions decodeGame encodeGame GameEvent GameState GameStatus Gem getLevel getTutorial HeldControl InputCommand Landing landingCells legalActions levelManifest LocalizedText Mode Orientation Pair Phase PlayAction powerDropPreview Preset processInputTick queueInputEdge RecordedAction releaseAllInput restartGame setHeldInput Settings statusOf StoneChainsOptionsError Transition tutorialManifest Wave WeatherSchedule WitnessStep
type Action
type Action = PlayAction;
function actionsForTick
actionsForTick(game: GameState, input: InputSchedulerState): readonly [readonly Action[], InputSchedulerState]
Resolves this tick's inputs in lateral, rotation, drop, soft-drop, pause order.
function advanceTicks
advanceTicks(state: GameState, ticks: number): Transition
Advances deterministic 60 Hz simulation and resolution; Relaxed and Challenges do not force a falling clock.
function applyAction
applyAction(state: GameState, action: Action): Transition
Applies one immutable accepted action and records its logical-tick position.
function applyScheduledActions
applyScheduledActions(game: GameState, input: InputSchedulerState, actions: readonly Action[], applyAction: (state: GameState, action: Action): Transition) => readonly [GameState, InputSchedulerState, readonly Transition[]]
Applies a scheduler batch until the pair locks or the run pauses; accepted actions are recorded by the engine.
const campaignManifest
campaignManifest: ChainCampaignManifest
type Cell
interface Cell { readonly x: number; readonly y: number }
A visible or hidden board coordinate.
type ChainCampaignLevel
interface ChainCampaignLevel { readonly id: string; readonly number: number; readonly title: LocalizedText; readonly canonicalKeyHash: string; readonly width: number; readonly height: number; readonly colourCount: 4 | 5 | 6; readonly seed: string; readonly board: readonly (Gem | null)[]; readonly queue: readonly (readonly [Colour, Colour])[]; readonly goal: ChallengeGoal; readonly witness: readonly WitnessStep[]; re…
type ChainCampaignManifest
interface ChainCampaignManifest { readonly count: number; readonly candidatePoolCount: number; readonly generationRevision: string; readonly gradingVersion: string; readonly category: string; readonly curationPolicy: string; readonly orderingPolicy: string; readonly grading: Readonly<Record<string, number | string>>; readonly sampleBudget: number; readonly checksum: string; readonly levels: readonly ChainCampaignLev…
type ChainContentData
interface ChainContentData { readonly campaign: ChainCampaignManifest; readonly tutorials: readonly ChainTutorialDefinition[] }
type ChainDifficultyMetrics
interface ChainDifficultyMetrics { readonly placementProbes: number; readonly legalPlacements: number; readonly goalPreservingPlacements: number; readonly forcedPlacementShare: number; readonly seededPlayoutSamples: number; readonly seededPlayoutQueueDepth: number; readonly seededPlayoutSuccesses: number; readonly seededPlayoutSuccessRate: number; readonly setupPairsBeforePayoff: number; readonly requiredSetupPairs:…
type ChainTutorialDefinition
interface ChainTutorialDefinition { readonly id: string; readonly title: LocalizedText; readonly objective: LocalizedText; readonly setup: { readonly board: readonly (Gem | null)[]; readonly queue: readonly (readonly [Colour, Colour])[]; readonly goal: ChallengeGoal; readonly witness: readonly WitnessStep[] }; readonly steps: readonly ChainTutorialStep[]; readonly tags: readonly string[]; }
type ChainTutorialStep
interface ChainTutorialStep { readonly instruction: LocalizedText; readonly action: string }
type ChallengeGem
type ChallengeGem = Omit<Gem, 'magnetic'>;
Authored Challenge layouts do not permit Shizen-marked stones.
type ChallengeGoal
type ChallengeGoal = { readonly kind: 'clear-targets'; readonly targetIds: readonly number[] } | { readonly kind: 'minimum-chain'; readonly chain: number } | { readonly kind: 'empty-board' };
Challenge objective; target IDs refer to the visible starting board.
type ChallengeOptions
interface ChallengeOptions { readonly id: string; readonly width?: number; readonly height?: number; readonly colourCount?: 4 | 5 | 6; readonly seed?: string; readonly board: readonly (ChallengeGem | null)[]; readonly queue: readonly (readonly [Colour, Colour])[]; readonly goal: ChallengeGoal; readonly witness?: readonly WitnessStep[] }
type Colour
type Colour = 'red' | 'blue' | 'green' | 'gold' | 'purple' | 'teal';
A gem colour used by Colour Chains.
function createChallenge
createChallenge(options: ChallengeOptions): GameState
Validates and creates a finite witnessed challenge. Hints use only the supplied verified pair placements.
function createGame
createGame(options?: CreateOptions): GameState
Creates a deterministic seeded game with one active pair and three previews.
function createInputScheduler
createInputScheduler(): InputSchedulerState
Creates an empty held-input scheduler.
function createLevel
createLevel(id: string | number): GameState
Create a fresh engine-validated challenge from a stable content ID or display number.
type CreateOptions
interface CreateOptions { readonly mode?: Exclude<Mode, 'challenge'>; readonly seed?: string; readonly dailyDate?: string; readonly preset?: Preset; readonly width?: number; readonly height?: number; readonly colourCount?: 4 | 5 | 6; readonly pairLimit?: number; readonly nature?: boolean; readonly weather?: WeatherSchedule }
function decodeGame
decodeGame(text: string): GameState
Reconstructs a bounded action replay and rejects any checkpoint that differs from replay.
function encodeGame
encodeGame(state: GameState): string
Encodes canonical initial settings, accepted ordinal actions and a derived checkpoint.
type GameEvent
interface GameEvent { readonly type: string; readonly [key: string]: unknown }
A domain event emitted by one transition.
type GameState
interface GameState { readonly game: 'colour-chains'; readonly rules: 'chains-1'; readonly settings: Settings; readonly board: readonly (Gem | null)[]; readonly phase: Phase; readonly pausedPhase?: Exclude<Phase, 'paused'>; readonly active: Pair | null; readonly next: readonly (readonly [Colour, Colour])[]; readonly bag: readonly Colour[]; readonly randomState: number; readonly nextMagnetic?: readonly (readonly [boo…
An immutable Colour Chains rules state. Board rows include the three hidden rows above the well.
type GameStatus
interface GameStatus { readonly mode: Mode; readonly phase: Phase; readonly score: number; readonly maxChain: number; readonly allClears: number; readonly completedPairs: number; readonly assisted: boolean; readonly reason?: string }
Compact player status derived from the current state.
type Gem
interface Gem { readonly id: number; readonly colour: Colour; readonly magnetic?: true }
A settled or active stone with a stable run-local identity.
function getLevel
getLevel(id: string | number): ChainCampaignLevel | undefined
Find a campaign challenge by stable content ID or its current one-based number.
function getTutorial
getTutorial(id: string): ChainTutorialDefinition | undefined
Find a persistent interactive lesson by its stable ID.
type HeldControl
type HeldControl = 'left' | 'right' | 'soft-drop';
type InputCommand
type InputCommand = { readonly kind: 'hold'; readonly control: HeldControl; readonly pressed: boolean } | { readonly kind: 'edge'; readonly action: Extract<PlayAction, { kind: 'rotate-clockwise' | 'rotate-anticlockwise' | 'hard-drop' | 'place' | 'pause' | 'resume' }> };
type Landing
type Landing = readonly [Cell, Cell];
Destination cells for the two independently settled ghost stones.
function landingCells
landingCells(state: GameState): Landing | null
Returns the exact independent landing cells for the current rigid pair without consuming random values.
function legalActions
legalActions(state: GameState): readonly Action[]
Lists only actions accepted by the current game state.
const levelManifest
levelManifest: readonly ChainCampaignLevel[]
type LocalizedText
interface LocalizedText { readonly en: string; readonly ja: string }
type Mode
type Mode = 'relaxed' | 'arcade' | 'daily' | 'challenge';
type Orientation
type Orientation = 'up' | 'right' | 'down' | 'left';
The pivot-to-satellite direction, in clockwise order.
type Pair
interface Pair { readonly pivot: Cell; readonly orientation: Orientation; readonly gems: readonly [Gem, Gem]; readonly level: number; readonly gravityTicks: number; readonly groundedTicks: number; readonly resetCount: number; readonly groundedStarted: boolean }
The active rigid pair. The pivot always retains its own gem identity.
type Phase
type Phase = 'falling' | 'clear-mark' | 'clear-remove' | 'gravity' | 'paused' | 'won' | 'lost' | 'finished';
Observable game phases.
type PlayAction
type PlayAction = { readonly kind: 'left' } | { readonly kind: 'right' } | { readonly kind: 'down' } | { readonly kind: 'rotate-clockwise' } | { readonly kind: 'rotate-anticlockwise' } | { readonly kind: 'hard-drop' } | { readonly kind: 'place' } | { readonly kind: 'pause' } | { readonly kind: 'resume' } | { readonly kind: 'hint' };
function powerDropPreview
powerDropPreview(state: GameState): PowerDropPreview | null
Returns the Shizen hard-drop landing and one-step rebound preview without changing state.
type Preset
type Preset = 'narrow' | 'standard' | 'wide' | 'tall' | 'extraWide' | 'deep' | 'large';
Supported well presets.
function processInputTick
processInputTick(game: GameState, input: InputSchedulerState, apply: (state: GameState, action: Action): Transition, tick: (state: GameState, ticks: number) => Transition) => readonly [GameState, InputSchedulerState, readonly Transition[]]
Applies a complete deterministic held-input and simulation tick, releasing controls on lock or resolution.
function queueInputEdge
queueInputEdge(state: InputSchedulerState, action: EdgeAction): InputSchedulerState
Queues a one-shot rotation, drop, Place, pause or resume edge for the next logical tick.
type RecordedAction
interface RecordedAction { readonly tick: number; readonly ordinal: number; readonly action: PlayAction }
function releaseAllInput
releaseAllInput(state: InputSchedulerState): InputSchedulerState
Releases every held or queued control after focus loss, lock, resolution or pause.
function restartGame
restartGame(state: GameState): GameState
Restarts the same seed/date or original witnessed challenge board and queue.
function setHeldInput
setHeldInput(state: InputSchedulerState, control: HeldControl, pressed: boolean): InputSchedulerState
Starts or releases one held control. Repeated press edges do not reset its repeat counter.
type Settings
interface Settings { readonly mode: Mode; readonly width: number; readonly height: number; readonly colourCount: 4 | 5 | 6; readonly seed: string; readonly date?: string; readonly pairLimit?: number; readonly nature?: true; readonly weather?: WeatherSchedule; readonly challengeId?: string; readonly goal?: ChallengeGoal; readonly initialBoard?: readonly (ChallengeGem | null)[]; readonly queue?: readonly (readonly [Co…
Run settings fixed at game creation.
function statusOf
statusOf(state: GameState): GameStatus
Returns score, mode, chain and completion fields derived from state.
const StoneChainsOptionsError
StoneChainsOptionsError: typeof StoneChainsOptionsError
Typed setup and authored challenge error.
type Transition
interface Transition { readonly state: GameState; readonly events: readonly GameEvent[]; readonly accepted: boolean; readonly reason?: string }
A deterministic transition; rejected actions retain the exact input state.
const tutorialManifest
tutorialManifest: readonly ChainTutorialDefinition[]
type Wave
interface Wave { readonly chain: number; readonly cells: readonly number[]; readonly ids: readonly number[]; readonly points: number }
A scored simultaneous match wave.
type WeatherSchedule
type WeatherSchedule = 'frequent' | 'rare';
type WitnessStep
interface WitnessStep { readonly pivotX: number; readonly orientation: Orientation }
@johnmorrisdotca/houseki/stone-collapse
Action advanceTicks applyAction BoardPreset BoardShape campaignManifest ChallengeDefinition ChallengeGoal ChallengeOptions CollapseCampaignLevel CollapseCampaignManifest CollapseDifficultyMetrics CollapseTutorialDefinition CollapseTutorialStep createChallenge createGame createLevel CreateOptions decodeGame encodeGame GameEvent GameMode GamePhase GameState GameStatus getLevel getTutorial legalActions levelManifest LocalizedText RecordedAction requestHint restartGame Settings statusOf Stone StoneCollapseOptionsError StoneColour StoredTool ToolInventory Transition tutorialManifest undo UndoFrame
type Action
type Action = | { readonly kind: 'select'; readonly stoneId: number } | { readonly kind: 'confirm' } | { readonly kind: 'cancel' } | { readonly kind: 'undo' } | { readonly kind: 'hint' } | { readonly kind: 'select-tool'; readonly tool: StoredTool } | { readonly kind: 'target-tool'; readonly cell: number } | { readonly kind: 'confirm-tool' } | { readonly kind: 'cancel-tool' };
function advanceTicks
advanceTicks(state: GameState, ticks: number): Transition
Advances the observable 7/6/9 tick removal and gravity stages, bounded to 3600 ticks.
function applyAction
applyAction(state: GameState, action: Action): Transition
Applies a selection, confirmation, or cancellation without mutating its input.
type BoardPreset
type BoardPreset = 'compact' | 'standard' | 'wide' | 'tall' | 'extraWide' | 'deep' | 'large';
type BoardShape
type BoardShape = 'heart' | 'star' | 'hexagon';
const campaignManifest
campaignManifest: CollapseCampaignManifest
type ChallengeDefinition
interface ChallengeDefinition { readonly id: string; readonly width: number; readonly height: number; readonly colourCount: 4 | 5 | 6; readonly seed: string | number; readonly mask: readonly boolean[]; readonly initialBoard: readonly (Stone | null)[]; readonly goal: ChallengeGoal; readonly moveLimit?: number; /** Each entry is the complete stable-ID set of the next witnessed group. */ readonly witness: readonly (rea…
type ChallengeGoal
type ChallengeGoal = | { readonly kind: 'clear-all' } | { readonly kind: 'clear-targets'; readonly targetIds: readonly number[] } | { readonly kind: 'score-target'; readonly minimumScore: number };
type ChallengeOptions
interface ChallengeOptions { readonly id: string; readonly width: number; readonly height: number; readonly colourCount: 4 | 5 | 6; readonly seed?: string | number; readonly mask?: readonly boolean[]; readonly initialBoard: readonly (Stone | null)[]; readonly goal: ChallengeGoal; readonly moveLimit?: number; readonly witness: readonly (readonly number[])[]; }
type CollapseCampaignLevel
interface CollapseCampaignLevel { readonly id: string; readonly number: number; readonly title: LocalizedText; readonly canonicalKeyHash: string; readonly width: number; readonly height: number; readonly colourCount: 4; readonly mask?: readonly boolean[]; readonly seed: string; readonly board: readonly (Stone | null)[]; readonly goal: ChallengeGoal; readonly moveLimit: number; readonly witness: readonly (readonly nu…
type CollapseCampaignManifest
interface CollapseCampaignManifest { readonly count: number; readonly candidatePoolCount: number; readonly generationRevision: string; readonly gradingVersion: string; readonly category: string; readonly curationPolicy: string; readonly orderingPolicy: string; readonly grading: Readonly<Record<string, unknown>>; readonly sampleBudget: number; readonly checksum: string; readonly levels: readonly CollapseCampaignLevel…
type CollapseDifficultyMetrics
interface CollapseDifficultyMetrics { readonly seededPlayoutSamples: number; readonly seededPlayoutSuccesses: number; readonly seededPlayoutSuccessRate: number; readonly legalGroupChoices: number; readonly witnessedDecisionCount: number; readonly sampledOrderFailureShare: number; readonly forcedSafeGroupShare: number; readonly averageGroupChoices: number; readonly witnessMoves: number; readonly moveBudgetSlack: numb…
type CollapseTutorialDefinition
interface CollapseTutorialDefinition { readonly id: string; readonly title: LocalizedText; readonly objective: LocalizedText; readonly setup: { readonly width: number; readonly height: number; readonly colourCount: 4; readonly board: readonly (Stone | null)[]; readonly goal: ChallengeGoal; readonly moveLimit?: number; readonly witness: readonly (readonly number[])[] }; readonly steps: readonly CollapseTutorialStep[]…
type CollapseTutorialStep
interface CollapseTutorialStep { readonly instruction: LocalizedText; readonly action: string }
function createChallenge
createChallenge(options: ChallengeOptions): GameState
Creates a fixed witnessed challenge after validating its board, objective, move budget, and every witness group.
function createGame
createGame(options?: CreateOptions): GameState
Creates a seeded full board; an all-singleton draw retries before using a checked adjacent pair.
function createLevel
createLevel(id: string | number): GameState
Construct a fresh engine-validated challenge from a content ID or display number.
type CreateOptions
interface CreateOptions { readonly mode?: Exclude<GameMode, 'challenge'>; readonly preset?: BoardPreset; readonly width?: number; readonly height?: number; readonly colourCount?: 4 | 5 | 6; readonly seed?: string | number; readonly shape?: BoardShape; /** Row-major custom active-cell mask. Supply width and height with a custom mask. */ readonly mask?: readonly boolean[]; /** Required for Daily; supplied by the host …
function decodeGame
decodeGame(text: string): GameState
Reconstructs and validates a bounded replay before accepting its checkpoint.
function encodeGame
encodeGame(state: GameState): string
Encodes the canonical initial configuration, accepted replay actions, and a derived checkpoint.
type GameEvent
interface GameEvent { readonly type: string; readonly [key: string]: unknown }
type GameMode
type GameMode = 'relaxed' | 'arcade' | 'daily' | 'challenge';
type GamePhase
type GamePhase = 'ready' | 'clear-mark' | 'clear-remove' | 'gravity' | 'won' | 'lost' | 'finished';
type GameState
interface GameState { readonly game: 'stone-collapse'; readonly rules: 'collapse-2'; readonly settings: Settings; readonly challenge: ChallengeDefinition | null; readonly initialBoard: readonly (Stone | null)[]; /** Row-major; masked and currently empty cells contain null. */ readonly board: readonly (Stone | null)[]; readonly phase: GamePhase; readonly selectedId: number | null; readonly selectedIds: readonly numbe…
type GameStatus
interface GameStatus { readonly mode: GameMode; readonly phase: GamePhase; readonly score: number; readonly moves: number; readonly removed: number; readonly remaining: number; readonly previewScore: number; readonly usedFallback: boolean; readonly assisted: boolean; readonly goal?: ChallengeGoal; readonly reason?: string; readonly inventory: ToolInventory; readonly toolProgress: number; readonly toolAwardCursor: 0 …
function getLevel
getLevel(id: string | number): CollapseCampaignLevel | undefined
Find a challenge by its persistent content ID or current one-based display number.
function getTutorial
getTutorial(id: string): CollapseTutorialDefinition | undefined
Find a persistent interactive lesson by its stable ID.
function legalActions
legalActions(state: GameState): readonly Action[]
Lists every selection and confirmation currently accepted by the rules.
const levelManifest
levelManifest: readonly CollapseCampaignLevel[]
type LocalizedText
interface LocalizedText { readonly en: string; readonly ja: string }
type RecordedAction
type RecordedAction = | { readonly kind: 'remove'; readonly move: number; readonly stoneId: number } | { readonly kind: 'select-tool'; readonly tool: StoredTool } | { readonly kind: 'target-tool'; readonly cell: number } | { readonly kind: 'confirm-tool'; readonly move: number } | { readonly kind: 'cancel-tool' } | { readonly kind: 'undo'; readonly move: number } | { readonly kind: 'hint'; readonly witnessIndex: num…
function requestHint
requestHint(state: GameState): Transition
Selects the next supplied witness group only while the run still matches its path; accepted hints mark the run assisted.
function restartGame
restartGame(state: GameState): GameState
Starts a fresh attempt from the identical seeded board or preserved challenge definition.
type Settings
interface Settings { readonly mode: GameMode; readonly width: number; readonly height: number; readonly colourCount: 4 | 5 | 6; readonly seed: string | number; readonly shape?: BoardShape; readonly mask: readonly boolean[]; readonly dailyDate?: string; readonly challengeId?: string; readonly goal?: ChallengeGoal; readonly moveLimit?: number; readonly tools: boolean; }
function statusOf
statusOf(state: GameState): GameStatus
Returns the stable public game status and the remaining-stone count.
type Stone
interface Stone { readonly id: number; readonly colour: StoneColour }
A stone keeps its run-local identity as it falls or changes column.
const StoneCollapseOptionsError
StoneCollapseOptionsError: typeof StoneCollapseOptionsError
Typed public-settings validation error.
type StoneColour
type StoneColour = 'red' | 'blue' | 'green' | 'gold' | 'purple' | 'teal';
A colour used by a stone.
type StoredTool
type StoredTool = 'bomb' | 'pick';
type ToolInventory
interface ToolInventory { readonly bomb: number; readonly pick: number }
type Transition
interface Transition { readonly state: GameState; readonly events: readonly GameEvent[]; readonly accepted: boolean; readonly reason?: string }
const tutorialManifest
tutorialManifest: readonly CollapseTutorialDefinition[]
function undo
undo(state: GameState): Transition
Restores the full pre-move state in Relaxed or Challenge practice and permanently marks the attempt assisted.
type UndoFrame
interface UndoFrame { readonly board: readonly (Stone | null)[]; readonly score: number; readonly moves: number; readonly removed: number; readonly randomState: number; readonly nextId: number; readonly usedFallback: boolean; readonly finishAdjustmentApplied: boolean; readonly selectedId: number | null; readonly selectedIds: readonly number[]; readonly previewScore: number; readonly witnessIndex: number; readonly in…
Data sufficient to restore the complete game immediately before a committed move.
@johnmorrisdotca/houseki/gem-swap
Action advanceTicks advanceTime applyAction BlackHolePortal BoardPreset BoardShape CAMPAIGN_CHECKSUM CAMPAIGN_COUNT CancelToolAction ChallengeRules ConfirmToolAction createGame CreateOptions decodeGame encodeGame GameEvent GameMode GameOutcome GamePhase GameState GameStatus Gem GEM_SWAP_CAMPAIGN GEM_SWAP_LESSONS GemColour GemSwapCampaignLevel GemSwapLesson GemSwapLessonStep GemSwapOptionsError GemSwapReviewStatus GENERATION_REVISION Goal GRADING_VERSION GRADING_WEIGHTS HintAction hintGame legalActions OrdinaryToolKind PlannedSpecial ReplayError ReplayOperation ReshuffleAction reshuffleGame restartGame SAMPLE_BUDGET Seal SelectToolAction Settings SpecialKind statusOf SwapAction TargetToolAction ToolInventory ToolKind Transition UndoAction undoGame validateChallengeWitness
type Action
type Action = SwapAction | SelectToolAction | TargetToolAction | ConfirmToolAction | CancelToolAction | UndoAction | HintAction | ReshuffleAction;
function advanceTicks
advanceTicks(state: GameState, ticks: number): Transition
Advances deterministic resolution ticks; wall-clock time is supplied separately by the host.
function advanceTime
advanceTime(state: GameState, milliseconds: number): Transition
Adds explicit host-measured elapsed time. Arcade runs end at 180 seconds; no clock is read here.
function applyAction
applyAction(state: GameState, action: Action): Transition
Applies one recorded player action. Rejected actions preserve state identity and are not recorded.
type BlackHolePortal
interface BlackHolePortal { readonly cell: number; readonly capacityRemaining: number; readonly movesRemaining: number; readonly consumedIds: readonly number[]; }
type BoardPreset
type BoardPreset = 'compact' | 'standard' | 'wide' | 'tall' | 'extraWide' | 'deep' | 'large';
type BoardShape
type BoardShape = 'heart' | 'star' | 'hexagon';
const CAMPAIGN_CHECKSUM
CAMPAIGN_CHECKSUM: "eb7c54a583db428d596b8fbdab0d63a9ad39ad5b9e39948dc4ccab84ba1196aa"
const CAMPAIGN_COUNT
CAMPAIGN_COUNT: 50
type CancelToolAction
interface CancelToolAction { readonly kind: 'cancel-tool' }
type ChallengeRules
interface ChallengeRules { readonly goals: readonly Goal[]; readonly moveLimit?: number; readonly seals?: readonly Seal[] }
type ConfirmToolAction
interface ConfirmToolAction { readonly kind: 'confirm-tool' }
function createGame
createGame(options?: CreateOptions): GameState
Creates a deterministic stable board with at least one legal normal swap.
type CreateOptions
interface CreateOptions { readonly preset?: BoardPreset; readonly width?: number; readonly height?: number; readonly colourCount?: 4 | 5 | 6; readonly seed?: string | number; readonly tools?: boolean; readonly advancedTools?: boolean; readonly shape?: BoardShape; /** Row-major active-cell mask. Requires explicit custom dimensions. */ readonly mask?: readonly boolean[]; readonly mode?: GameMode; /** Required for Dail…
function decodeGame
decodeGame(serialized: string): GameState
Validates and reconstructs a state by replay; serialized board snapshots are never trusted.
function encodeGame
encodeGame(state: GameState): string
Encodes the initial rules plus accepted actions and logical inputs in canonical JSON.
type GameEvent
interface GameEvent { readonly type: string; readonly [key: string]: unknown }
type GameMode
type GameMode = 'relaxed' | 'arcade' | 'daily' | 'challenge';
type GameOutcome
type GameOutcome = 'won' | 'lost' | 'finished' | null;
type GamePhase
type GamePhase = 'ready' | 'clear-mark' | 'clear-remove' | 'gravity' | 'refill' | 'finished';
type GameState
interface GameState { readonly game: 'gem-swap'; readonly rules: 'swap-1'; readonly settings: Settings; readonly initialOptions: CreateOptions; readonly mode: GameMode; readonly dailyDate: string | null; readonly challenge: ChallengeRules | null; readonly seals: readonly Seal[]; readonly sealsCleared: number; readonly elapsedMs: number; readonly outcome: GameOutcome; readonly clearedByColour: Readonly<Record<GemColo…
type GameStatus
interface GameStatus { readonly phase: GamePhase; readonly score: number; readonly moves: number; readonly legalMoveCount: number; readonly usedFallback: boolean; readonly availableToolCount: number; readonly inventory: ToolInventory; readonly toolProgress: number; readonly toolAwardCursor: number; readonly assisted: boolean; readonly blackHoleCharges: 0 | 1; readonly blackHoleProgress: number; readonly blackHole: B…
type Gem
interface Gem { readonly id: number; readonly colour: GemColour; readonly kind?: SpecialKind }
A gem has a stable identity and stored colour until it is cleared. Missing kind means normal.
const GEM_SWAP_CAMPAIGN
GEM_SWAP_CAMPAIGN: readonly GemSwapCampaignLevel[]
const GEM_SWAP_LESSONS
GEM_SWAP_LESSONS: readonly GemSwapLesson[]
type GemColour
type GemColour = 'red' | 'blue' | 'green' | 'gold' | 'purple' | 'teal';
A colour used by a normal gem.
type GemSwapCampaignLevel
interface GemSwapCampaignLevel { readonly id: string; readonly number: number; readonly title: { readonly en: string; readonly ja: string }; readonly options: CreateOptions; readonly width: number; readonly height: number; readonly colourCount: number; readonly seed: number; readonly challenge: NonNullable<CreateOptions['challenge']>; readonly witness: readonly ReplayOperation[]; readonly canonicalKeyHash: string; r…
type GemSwapLesson
interface GemSwapLesson { readonly id: string; readonly title: { readonly en: string; readonly ja: string }; readonly initial: CreateOptions; readonly steps: readonly GemSwapLessonStep[]; readonly witness: readonly ReplayOperation[]; readonly reviewStatus: GemSwapReviewStatus }
type GemSwapLessonStep
type GemSwapLessonStep = { readonly kind: 'action'; readonly id: string; readonly action: Action; readonly accepted: boolean; readonly reason?: string; readonly event?: string; readonly cell?: number; readonly text: { readonly en: string; readonly ja: string } } | { readonly kind: 'ticks'; readonly id: string; readonly count: number; readonly event?: string; readonly outcome?: string; readonly cell?: number; readonl…
const GemSwapOptionsError
GemSwapOptionsError: typeof GemSwapOptionsError
Typed error for invalid settings or a board that cannot be generated safely.
type GemSwapReviewStatus
type GemSwapReviewStatus = 'human-review-pending';
const GENERATION_REVISION
GENERATION_REVISION: "gem-swap-campaign-1.3.0"
type Goal
type Goal = | { readonly kind: 'score'; readonly target: number } | { readonly kind: 'collect'; readonly colour: GemColour; readonly target: number } | { readonly kind: 'chain'; readonly target: number } | { readonly kind: 'seals' };
const GRADING_VERSION
GRADING_VERSION: "swap-choice-forgiveness-1"
const GRADING_WEIGHTS
GRADING_WEIGHTS: { seededPlayoutDifficulty: number; choiceDifficulty: number; objectiveCoordination: number; chainDepth: number; witnessLength: number; specialCombination: number; }
type HintAction
interface HintAction { readonly kind: 'hint' }
function hintGame
hintGame(state: GameState): Transition
Returns a deterministic legal swap hint and permanently marks the run assisted.
function legalActions
legalActions(state: GameState): readonly Action[]
Lists all legal swaps that would be accepted from the current stable state.
type OrdinaryToolKind
type OrdinaryToolKind = Exclude<ToolKind, 'black-hole'>;
type PlannedSpecial
interface PlannedSpecial { readonly cell: number; readonly kind: SpecialKind }
const ReplayError
ReplayError: typeof ReplayError
type ReplayOperation
type ReplayOperation = { readonly kind: 'action'; readonly action: Action } | { readonly kind: 'ticks'; readonly count: number } | { readonly kind: 'time'; readonly milliseconds: number };
type ReshuffleAction
interface ReshuffleAction { readonly kind: 'reshuffle' }
function reshuffleGame
reshuffleGame(state: GameState): Transition
Shuffles existing gem records in Relaxed play, preserving their colour and special counts.
function restartGame
restartGame(state: GameState): GameState
Starts the exact initial ruleset and seed again, discarding all run progress.
const SAMPLE_BUDGET
SAMPLE_BUDGET: 24
type Seal
interface Seal { readonly cell: number; readonly layers: number }
type SelectToolAction
interface SelectToolAction { readonly kind: 'select-tool'; readonly tool: ToolKind }
type Settings
interface Settings { readonly width: number; readonly height: number; readonly colourCount: 4 | 5 | 6; readonly seed: string | number; readonly tools: boolean; readonly advancedTools: boolean; readonly shape?: BoardShape; readonly mask: readonly boolean[]; }
type SpecialKind
type SpecialKind = 'row-beam' | 'column-beam' | 'bomb' | 'colour-burst';
function statusOf
statusOf(state: GameState): GameStatus
type SwapAction
interface SwapAction { readonly kind: 'swap'; readonly from: number; readonly to: number }
Swaps two row-major cell addresses.
type TargetToolAction
interface TargetToolAction { readonly kind: 'target-tool'; readonly cell: number }
type ToolInventory
type ToolInventory = Readonly<Record<OrdinaryToolKind, number>>;
type ToolKind
type ToolKind = 'bomb' | 'row-clear' | 'colour-clear' | 'black-hole';
type Transition
interface Transition { readonly state: GameState; readonly events: readonly GameEvent[]; readonly accepted: boolean; readonly reason?: string }
type UndoAction
interface UndoAction { readonly kind: 'undo' }
function undoGame
undoGame(state: GameState): Transition
Restores the last committed swap in Relaxed or Challenge play and marks assistance.
function validateChallengeWitness
validateChallengeWitness(options: CreateOptions, witness: readonly ReplayOperation[]): { readonly valid: boolean; readonly state?: GameState; readonly reason?: string; }
Checks a complete authored witness through the public deterministic rules API.
@johnmorrisdotca/houseki/nature
applyEarthquake applyLightning applyMagneticPulse applyPowerDrop AttractionPairPreview createNatureState EarthquakeKind EarthquakeOptions EnvironmentEvent EnvironmentPreview EnvironmentSchedule EnvironmentTransition FaultPreview HorizontalDirection isEnvironmentTurnScheduled JumblePreview LightningOptions LightningPreview MagneticPreview MarkingOptions markMagneticStones NatureEntity NatureErrorCode NatureEvent NatureMove NatureOptions NatureOptionsError NaturePieceCell NatureRules NatureState NatureTransition PowerDropPreview PowerDropRequest previewEarthquake previewFault previewJumble previewLightning previewMagneticPulse previewPowerDrop QuarterTurn ReboundAttemptPreview ReboundDirectionMode
function applyEarthquake
applyEarthquake(state: NatureState, options: EarthquakeOptions): EnvironmentTransition
Commit one bounded environmental resolution; matching, gravity and score remain the host game's job.
function applyLightning
applyLightning(state: NatureState, options?: LightningOptions): EnvironmentTransition
Commit one lightning strike. It removes exposed stones only and consumes no scores or tools.
function applyMagneticPulse
applyMagneticPulse(state: NatureState): NatureTransition
Apply one attraction pulse. Disabled nature rules preserve the exact state reference and consume no random state.
function applyPowerDrop
applyPowerDrop(state: NatureState, request: PowerDropRequest): NatureTransition
Commit a previewed landing as one immutable operation. A failed preview leaves the original state untouched.
type AttractionPairPreview
interface AttractionPairPreview { readonly ids: readonly [number, number]; readonly axis: 'horizontal' | 'vertical'; readonly emptyGap: number; readonly midpoint: { readonly x: number; readonly y: number }; readonly anchoredId?: number; readonly moves: readonly NatureMove[]; }
function createNatureState
createNatureState(options: NatureOptions): NatureState
type EarthquakeKind
type EarthquakeKind = 'fault' | 'jumble' | 'combined';
type EarthquakeOptions
interface EarthquakeOptions { readonly kind: EarthquakeKind; readonly enabled?: boolean }
type EnvironmentEvent
type EnvironmentEvent = | { readonly type: 'fault-opened'; readonly path: readonly number[]; readonly fracturedCells: readonly number[]; readonly removedIds: readonly number[] } | { readonly type: 'jumble-completed'; readonly changed: boolean; readonly reason?: 'no-eligible-patch'; readonly patchCells: readonly number[]; readonly moves: readonly NatureMove[] } | { readonly type: 'lightning-struck'; readonly columns:…
type EnvironmentPreview
type EnvironmentPreview = EarthquakePreview | LightningPreview;
type EnvironmentSchedule
type EnvironmentSchedule = { readonly kind: 'frequent' } | { readonly kind: 'rare' } | { readonly kind: 'midlevel'; readonly turn: number } | { readonly kind: 'authored'; readonly turns: readonly number[] };
type EnvironmentTransition
interface EnvironmentTransition { readonly state: NatureState; readonly accepted: true; readonly events: readonly EnvironmentEvent[]; readonly preview: EnvironmentPreview }
type FaultPreview
interface FaultPreview { readonly path: readonly number[]; readonly fracturedCells: readonly number[]; readonly removedIds: readonly number[]; readonly cells: readonly (NatureEntity | null)[]; readonly activeCells: readonly boolean[]; readonly randomStateAfter: number }
type HorizontalDirection
type HorizontalDirection = 'left' | 'right';
function isEnvironmentTurnScheduled
isEnvironmentTurnScheduled(turn: number, schedule: EnvironmentSchedule): boolean
Pure one-based schedule lookup: frequent is every second turn; rare is every eighth turn.
type JumblePreview
interface JumblePreview { readonly patchCells: readonly number[]; readonly moves: readonly NatureMove[]; readonly changed: boolean; readonly reason?: 'no-eligible-patch'; readonly cells: readonly (NatureEntity | null)[]; readonly randomStateAfter: number }
type LightningOptions
interface LightningOptions { readonly enabled?: boolean; readonly columns?: readonly number[]; readonly columnCount?: 1 | 2 | 3 }
type LightningPreview
interface LightningPreview { readonly columns: readonly number[]; readonly removedCells: readonly number[]; readonly removedIds: readonly number[]; readonly cells: readonly (NatureEntity | null)[]; readonly randomStateAfter: number }
type MagneticPreview
interface MagneticPreview { readonly pairs: readonly AttractionPairPreview[]; readonly moves: readonly NatureMove[]; readonly cells: readonly (NatureEntity | null)[]; }
type MarkingOptions
interface MarkingOptions { readonly probability: number; readonly maximum?: number; readonly anchoredProbability?: number }
function markMagneticStones
markMagneticStones(state: NatureState, options: MarkingOptions): NatureTransition
Deterministically assign magnetic markings to settled stones without changing their IDs or colours.
type NatureEntity
interface NatureEntity { readonly id: number; readonly kind: 'stone' | 'obstacle'; readonly colour?: string; readonly magnetic?: boolean; readonly anchored?: boolean; }
Stable board occupant. Obstacles collide and block attraction but cannot move or attract.
type NatureErrorCode
type NatureErrorCode = 'invalid-nature-options' | 'invalid-grid-size' | 'invalid-seed' | 'invalid-mask' | 'invalid-cell' | 'duplicate-id' | 'invalid-marking-options' | 'invalid-power-drop' | 'piece-collision' | 'invalid-piece-position';
type NatureEvent
type NatureEvent = | { readonly type: 'magnetic-pulse'; readonly pairs: readonly AttractionPairPreview[]; readonly moves: readonly NatureMove[] } | { readonly type: 'magnetic-stones-marked'; readonly ids: readonly number[]; readonly randomState: number } | { readonly type: 'power-drop-landed'; readonly power: boolean; readonly rebound: boolean; readonly direction?: HorizontalDirection; readonly quarterTurn: QuarterT…
type NatureMove
interface NatureMove { readonly id: number; readonly from: { readonly x: number; readonly y: number }; readonly to: { readonly x: number; readonly y: number }; }
type NatureOptions
interface NatureOptions { readonly width: number; readonly height: number; readonly seed?: string; /** Row-major passability mask. False cells are permanent gaps and cannot hold pieces. */ readonly activeCells?: readonly boolean[]; /** Row-major initial occupants; false mask cells must be empty. */ readonly cells?: readonly (NatureEntity | null)[]; readonly magneticEnabled?: boolean; readonly reboundEnabled?: boolea…
const NatureOptionsError
NatureOptionsError: typeof NatureOptionsError
type NaturePieceCell
interface NaturePieceCell extends NatureEntity { readonly kind: 'stone'; readonly x: number; readonly y: number; }
type NatureRules
interface NatureRules { readonly magneticEnabled: boolean; readonly reboundEnabled: boolean; readonly reboundDirectionMode: ReboundDirectionMode; }
type NatureState
interface NatureState { readonly width: number; readonly height: number; readonly activeCells: readonly boolean[]; readonly cells: readonly (NatureEntity | null)[]; readonly seed: string; readonly randomState: number; readonly nextId: number; readonly committedPlacements: number; readonly rules: NatureRules; }
type NatureTransition
interface NatureTransition { readonly state: NatureState; readonly accepted: boolean; readonly reason?: string; readonly events: readonly NatureEvent[]; readonly preview?: MagneticPreview | PowerDropPreview; }
type PowerDropPreview
interface PowerDropPreview { readonly normalLanding: readonly NaturePieceCell[]; readonly attempts: readonly ReboundAttemptPreview[]; readonly rebound: boolean; readonly selectedDirection?: HorizontalDirection; readonly quarterTurn: QuarterTurn; readonly finalCells: readonly NaturePieceCell[]; readonly moves: readonly NatureMove[]; readonly randomStateAfter: number; }
type PowerDropRequest
interface PowerDropRequest { readonly piece: readonly NaturePieceCell[]; readonly pivotId: number; readonly power: boolean; readonly lastHorizontalDirection?: HorizontalDirection; /** A caller-selected quarter-turn applied with the one-cell rebound. */ readonly quarterTurn?: QuarterTurn; }
function previewEarthquake
previewEarthquake(state: NatureState, options: EarthquakeOptions): EarthquakePreview
Preview an earthquake, with Combined always fracturing before its one jumble attempt.
function previewFault
previewFault(state: NatureState): FaultPreview
Preview a bounded top-to-bottom fracture. The path keeps at least one column active on each side.
function previewJumble
previewJumble(state: NatureState): JumblePreview
Preview one seeded, nontrivial stone permutation inside a fully active connected rectangle no larger than 3×3.
function previewLightning
previewLightning(state: NatureState, options?: LightningOptions): LightningPreview
Preview seeded or explicitly selected columns; a mask gap or obstacle blocks the strike below it.
function previewMagneticPulse
previewMagneticPulse(state: NatureState): MagneticPreview
Preview one bounded deterministic attraction pulse without mutating the state.
function previewPowerDrop
previewPowerDrop(state: NatureState, request: PowerDropRequest): PowerDropPreview
Preview a single normal landing and, for a power drop, at most one validated one-cell rebound.
type QuarterTurn
type QuarterTurn = -1 | 0 | 1;
type ReboundAttemptPreview
interface ReboundAttemptPreview { readonly direction: HorizontalDirection; readonly quarterTurn: QuarterTurn; readonly valid: boolean; }
type ReboundDirectionMode
type ReboundDirectionMode = 'input-or-alternate' | 'seeded';
@johnmorrisdotca/houseki/magnetic-blocks
Action advanceTicks applyAction Block blockBonds blockCells Bond calmGravity canPlace Colour createGame CreateOptions decodeGame encodeGame Floor FloorSchedule GameEvent GameState GameStatus Gem Goal GRAVITY_TICKS impactSupportIds landingY legalActions MagneticBlocksOptionsError magneticGravity MARK_TICKS matchGroups Mode Phase RecordedAction REMOVE_TICKS removeIds restartGame Settings settle statusOf Transition
type Action
type Action = | { readonly kind: 'left' } | { readonly kind: 'right' } | { readonly kind: 'soft-drop' } | { readonly kind: 'rotate-clockwise' } | { readonly kind: 'rotate-anticlockwise' } | { readonly kind: 'hard-drop' } | { readonly kind: 'land' } | { readonly kind: 'pause' } | { readonly kind: 'resume' } | { readonly kind: 'set-floor-override'; readonly floor: Floor } | { readonly kind: 'cancel-floor-override' };
function advanceTicks
advanceTicks(state: GameState, ticks: number): Transition
Advances fixed 60 Hz time, resolving phases in all modes and falling/locking only in Arcade.
function applyAction
applyAction(state: GameState, action: Action): Transition
Applies one movement, rotation, floor-switch or placement action without mutation.
type Block
interface Block { readonly x: number; readonly y: number; readonly orientation: 0 | 1 | 2 | 3; readonly gems: readonly [Gem, Gem, Gem, Gem]; readonly descent: number; readonly lockTicks: number; readonly lockResets: number; readonly lockStarted: boolean }
function blockBonds
blockBonds(block: Block): readonly Bond[]
function blockCells
blockCells(block: Block, width: number): readonly { readonly index: number; readonly gem: Gem; }[]
Returns stable-gem addresses after rotating the square block around its centre.
type Bond
interface Bond { readonly a: number; readonly b: number }
function calmGravity
calmGravity(initial: readonly (Gem | null)[], bonds: readonly Bond[], width: number, height: number, mask: readonly boolean[]): readonly (Gem | null)[]
Settles bonded connected components one cell per deterministic bottom/ID priority step.
function canPlace
canPlace(block: Block, board: readonly (Gem | null)[], width: number, height: number, mask: readonly boolean[]): boolean
type Colour
type Colour = 'red' | 'blue' | 'green' | 'gold' | 'purple' | 'teal';
A standard gem colour used by a magnetic block.
function createGame
createGame(options?: import("./types.js").CreateOptions): GameState
Creates an immutable seeded 2×2-block game with a fixed queue and explicit floor schedule.
type CreateOptions
interface CreateOptions { readonly mode?: Mode; readonly width?: number; readonly height?: number; readonly colourCount?: 4 | 5 | 6; readonly seed?: string | number; readonly mask?: readonly boolean[]; readonly initialBoard?: readonly (Gem | null)[]; readonly bonds?: readonly Bond[]; readonly queue?: readonly (readonly [Colour, Colour, Colour, Colour])[]; readonly pieceLimit?: number; readonly schedule?: FloorSchedu…
function decodeGame
decodeGame(text: string): GameState
Replays bounded canonical input and verifies every saved checkpoint field.
function encodeGame
encodeGame(state: GameState): string
Encodes canonical settings, accepted tick-stamped actions and a verified replay checkpoint.
type Floor
type Floor = 'calm' | 'magnetic';
type FloorSchedule
type FloorSchedule = | { readonly kind: 'frequent' } | { readonly kind: 'occasional' } | { readonly kind: 'infrequent' } | { readonly kind: 'mid-level'; readonly placement: number } | { readonly kind: 'fixed'; readonly floor: Floor } | { readonly kind: 'authored'; readonly magneticPlacements: readonly number[]; readonly defaultFloor?: Floor };
type GameEvent
interface GameEvent { readonly type: string; readonly [key: string]: unknown }
type GameState
interface GameState { readonly game: 'magnetic-blocks'; readonly rules: 'magnetic-blocks-1'; readonly settings: Settings; readonly board: readonly (Gem | null)[]; readonly bonds: readonly Bond[]; readonly active: Block | null; readonly queue: readonly (readonly [Colour, Colour, Colour, Colour])[]; readonly randomState: number; readonly nextId: number; readonly phase: Phase; readonly pausedPhase?: Exclude<Phase, 'pau…
type GameStatus
interface GameStatus { readonly phase: Phase; readonly score: number; readonly moves: number; readonly placements: number; readonly floor: Floor; readonly nextFloor: Floor; readonly floorSwitchCharges: number; readonly pendingFloorOverride: Floor | null; readonly maxChain: number; readonly reason?: string }
type Gem
interface Gem { readonly id: number; readonly colour: Colour }
type Goal
type Goal = { readonly kind: 'clear-all' } | { readonly kind: 'clear-targets'; readonly targetIds: readonly number[] };
const GRAVITY_TICKS
GRAVITY_TICKS: 9
function impactSupportIds
impactSupportIds(block: Block, board: readonly (Gem | null)[], width: number, height: number, mask: readonly boolean[]): readonly number[]
Collects the occupied one-cell support layer under both block columns at first contact.
function landingY
landingY(block: Block, board: readonly (Gem | null)[], width: number, height: number, mask: readonly boolean[]): number
function legalActions
legalActions(state: GameState): readonly Action[]
Lists accepted controls for the current falling or resolution phase.
const MagneticBlocksOptionsError
MagneticBlocksOptionsError: typeof MagneticBlocksOptionsError
function magneticGravity
magneticGravity(initial: readonly (Gem | null)[], width: number, height: number, mask: readonly boolean[]): readonly (Gem | null)[]
Magnetic floor removes all bonds and settles each column segment independently.
const MARK_TICKS
MARK_TICKS: 7
function matchGroups
matchGroups(board: readonly (Gem | null)[], width: number, height: number, mask: readonly boolean[]): readonly (readonly number[])[]
Computes deterministic orthogonal colour components of at least four gems.
type Mode
type Mode = 'relaxed' | 'arcade';
type Phase
type Phase = 'falling' | 'clear-mark' | 'clear-remove' | 'gravity' | 'paused' | 'won' | 'lost' | 'finished';
type RecordedAction
interface RecordedAction { readonly tick: number; readonly ordinal: number; readonly action: Action }
const REMOVE_TICKS
REMOVE_TICKS: 6
function removeIds
removeIds(board: readonly (Gem | null)[], bonds: readonly Bond[], indices: readonly number[]): { readonly board: readonly (Gem | null)[]; readonly bonds: readonly Bond[]; readonly ids: readonly number[]; }
function restartGame
restartGame(state: GameState): GameState
Restarts the same seed, custom starting board, fixed queue, rules and floor options.
type Settings
interface Settings { readonly mode: Mode; readonly width: number; readonly height: number; readonly colourCount: 4 | 5 | 6; readonly seed: string | number; readonly mask: readonly boolean[]; readonly schedule: FloorSchedule; readonly floorSwitch: boolean; readonly magneticImpact: boolean; readonly pieceLimit: number; readonly initialBoard: readonly (Gem | null)[]; readonly bonds: readonly Bond[]; readonly initialQue…
function settle
settle(initial: readonly (Gem | null)[], bonds: readonly Bond[], floor: Floor, width: number, height: number, mask: readonly boolean[]): { readonly board: readonly (Gem | null)[]; readonly bonds: readonly Bond[]; }
function statusOf
statusOf(state: GameState): GameStatus
Returns a compact immutable status including current and upcoming floor state.
type Transition
interface Transition { readonly state: GameState; readonly events: readonly GameEvent[]; readonly accepted: boolean; readonly reason?: string }