// Seeded Worlds: the same number always builds the same world.
// @description Two worlds side by side, each labeled with its seed. Copy one seed over and the halves match exactly.
//
// Part of the BLIT386 demo series.
//
// Prerequisites:
// Basics https://demos.blit386.dev/basics
// Random Basics https://demos.blit386.dev/random-basics
//
// WHAT YOU WILL SEE
// Two little worlds side by side. Each one was built out of random numbers - the hills, the
// buildings, the trees, the stars. Above each world is the number it grew from, called its
// "seed". Press the buttons to give either side a new seed and watch it rebuild.
//
// Then press "Copy left seed" and watch the right world turn into an exact twin of the left
// one. Same number in, same world out, every single time.
//
// WHAT YOU WILL LEARN
// - BT.randomSeed(n) tells the engine which number to start its randomness from
// - The same seed always produces the same sequence, so it produces the same world
// - BT.random.seedValue reads that number back, even the one the engine picked by itself
// - clone() copies a generator so both continue with the same numbers
// - fork() splits off a new generator that goes its own way
//
// WHY THIS IS USEFUL
// A game can hand you a puzzle from seed 4821 and know every player gets the identical
// puzzle. A saved game can store one number instead of a whole map. And when a run goes
// really well, you can write the seed down and play it again.
//
// The engine splits work the usual way: update() moves things; render() only draws.
// See the Basics demo for the full story: https://demos.blit386.dev/basics
import { function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>One-liner bootstrap function for BLIT386 demos.
Handles canvas retrieval and engine initialization. Backend selection
(WebGPU or software fallback) is managed internally by BTAPI.
This function provides a streamlined way to start a demo with sensible defaults
while allowing customization through options.bootstrap, const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32, class RandomSeeded PRNG with integer-first generators and stream control (`seed` / `clone` / `fork`).Random, class Rect2iInteger rectangle for pixel-perfect bounds and regions.
Used throughout the engine for sprite regions, display-space bounds, and
zero-allocation geometry helpers. Both convenience getters and allocation-free
`*To()` helpers are provided so callers can choose between readability and
hot-path efficiency.Rect2i, class Vector2iInteger 2D vector for pixel-perfect positioning.
Used for points, sizes, directions, and camera offsets throughout the engine.
The API includes both allocation-free `*To()` / `*InPlace()` variants and
convenience methods that return new vectors.Vector2i } from 'blit386';
import { import applyThemeapplyTheme, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js';
/** @typedef {import('blit386').IBTDemo} IBTDemo */
/** @typedef {import('blit386').Palette} Palette */
// This demo runs at the engine's default screen size of 320x240 "game pixels".
// Each world gets its own framed box. Two boxes, side by side, with a gap between them.
const const WORLD_W: 152WORLD_W = 152;
const const WORLD_H: 100WORLD_H = 100;
const const WORLD_Y: 18WORLD_Y = 18;
const const WORLD_LEFT_X: 4WORLD_LEFT_X = 4;
const const WORLD_RIGHT_X: 164WORLD_RIGHT_X = 164;
// How many of each thing lives in a world. Fixed counts keep the two sides comparable:
// only the positions and sizes change from seed to seed.
const const STAR_COUNT: 16STAR_COUNT = 16;
const const BUILDING_COUNT: 4BUILDING_COUNT = 4;
const const TREE_COUNT: 7TREE_COUNT = 7;
// The ground is drawn as a run of columns whose height is worked out from four control
// points. Fewer points means smoother, rounder hills.
const const HILL_POINTS: 4HILL_POINTS = 4;
// Seeds are kept small so they are easy to read and easy to type back in.
const const SEED_MIN: 1000SEED_MIN = 1000;
const const SEED_MAX: 9999SEED_MAX = 9999;
// How many numbers the stream comparison shows for each generator.
const const STREAM_SAMPLE: 3STREAM_SAMPLE = 3;
// Color slots. The shared UI kit owns slots 240-251, so scene colors stay well below that.
const const C_SKY: 1C_SKY = 1; // Night sky inside each world box.
const const C_STAR: 2C_STAR = 2; // Stars.
const const C_GROUND: 3C_GROUND = 3; // Solid earth under the hills.
const const C_HILL: 4C_HILL = 4; // The hill surface line.
const const C_TREE: 5C_TREE = 5; // Tree leaves.
const const C_TRUNK: 6C_TRUNK = 6; // Tree trunks.
const const C_INK: 7C_INK = 7; // Frames and bright text.
const const C_DIM: 8C_DIM = 8; // Faded lines.
// Four building tints, in slots 10-13.
const const C_BUILDING_BASE: 10C_BUILDING_BASE = 10;
const const BUILDING_TINTS: 4BUILDING_TINTS = 4;
/**
* Builds one complete world from a single seed.
*
* This is the heart of the demo. Every random number it needs comes from BT.random, and
* BT.random was just told where to start, so running this twice with the same seed walks
* through the exact same numbers in the exact same order - and therefore builds the exact
* same world.
*
* @param {number} seed - The number this world grows from.
* @returns {{ seed: number, hills: Array<number>, buildings: Array<object>, trees: Array<object>, stars: Array<Vector2i> }}
*/
function function generateWorld(seed: number): {
seed: number;
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
Builds one complete world from a single seed.
This is the heart of the demo. Every random number it needs comes from BT.random, and
BT.random was just told where to start, so running this twice with the same seed walks
through the exact same numbers in the exact same order - and therefore builds the exact
same world.generateWorld(seed: number- The number this world grows from.seed) {
// This is the line that makes everything below repeatable. From here on the engine's
// random numbers are no longer a surprise - they are decided by `seed`.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.randomSeed: (seed: number) => voidReseeds the default engine PRNG so subsequent draws are reproducible.randomSeed(seed: number- The number this world grows from.seed);
// Stars first, scattered across the top two thirds of the box.
const const stars: {}stars = [];
for (let let i: numberi = 0; let i: numberi < const STAR_COUNT: 16STAR_COUNT; let i: numberi++) {
// insideRect() hands back a Vector2i somewhere inside the rectangle we describe -
// like closing your eyes and pointing at a spot on a map.
const stars: {}stars.push(const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.random: RandomDefault engine PRNG (live reference - not a copy).
Time-seeded when the engine singleton is created. Call
{@link
BT.randomSeed
}
for a reproducible run. Mutating the instance (for example `BT.random.int(10)`)
advances the shared stream.random.Random.insideRect(rect: Rect2i): Vector2iReturns a random integer point inside a rectangle (half-open, like
{@link
Rect2i.isContaining
}
).insideRect(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(0, 0, const WORLD_W: 152WORLD_W, Math.floor(const WORLD_H: 100WORLD_H * 0.6))));
}
// The hills. We pick a few heights spread across the width, then fill in the columns
// between them by sliding smoothly from one height to the next.
const const controls: {}controls = [];
for (let let i: numberi = 0; let i: numberi < const HILL_POINTS: 4HILL_POINTS; let i: numberi++) {
// int(a, b) gives a whole number from a up to (but not including) b, so these
// heights land somewhere in the lower half of the box.
const controls: {}controls.push(const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.random: RandomDefault engine PRNG (live reference - not a copy).
Time-seeded when the engine singleton is created. Call
{@link
BT.randomSeed
}
for a reproducible run. Mutating the instance (for example `BT.random.int(10)`)
advances the shared stream.random.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).int(Math.floor(const WORLD_H: 100WORLD_H * 0.55), Math.floor(const WORLD_H: 100WORLD_H * 0.8)));
}
const const hills: {}hills = [];
for (let let x: numberx = 0; let x: numberx < const WORLD_W: 152WORLD_W; let x: numberx++) {
// Work out which pair of control points this column sits between, and how far along
// it is between them (0 means "right on the left point", 1 means "right on the right").
const const spanW: numberspanW = const WORLD_W: 152WORLD_W / (const HILL_POINTS: 4HILL_POINTS - 1);
const const span: anyspan = Math.min(const HILL_POINTS: 4HILL_POINTS - 2, Math.floor(let x: numberx / const spanW: numberspanW));
const const t: numbert = (let x: numberx - const span: anyspan * const spanW: numberspanW) / const spanW: numberspanW;
// Sliding straight from one height to the next gives sharp corners. Feeding the
// position through this curve first eases in and out, so the hills look rounded.
const const eased: numbereased = const t: numbert * const t: numbert * (3 - 2 * const t: numbert);
const hills: {}hills.push(Math.round(const controls: {}controls[const span: anyspan] + (const controls: {}controls[const span: anyspan + 1] - const controls: {}controls[const span: anyspan]) * const eased: numbereased));
}
// Buildings, sitting on whatever ground height is under them.
const const buildings: {}buildings = [];
for (let let i: numberi = 0; let i: numberi < const BUILDING_COUNT: 4BUILDING_COUNT; let i: numberi++) {
const const w: numberw = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.random: RandomDefault engine PRNG (live reference - not a copy).
Time-seeded when the engine singleton is created. Call
{@link
BT.randomSeed
}
for a reproducible run. Mutating the instance (for example `BT.random.int(10)`)
advances the shared stream.random.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).int(12, 24);
const const x: numberx = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.random: RandomDefault engine PRNG (live reference - not a copy).
Time-seeded when the engine singleton is created. Call
{@link
BT.randomSeed
}
for a reproducible run. Mutating the instance (for example `BT.random.int(10)`)
advances the shared stream.random.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).int(4, const WORLD_W: 152WORLD_W - const w: numberw - 4);
const const h: numberh = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.random: RandomDefault engine PRNG (live reference - not a copy).
Time-seeded when the engine singleton is created. Call
{@link
BT.randomSeed
}
for a reproducible run. Mutating the instance (for example `BT.random.int(10)`)
advances the shared stream.random.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).int(14, 34);
const buildings: {}buildings.push({
x: numberx,
w: numberw,
h: numberh,
// The building stands on the ground, so its top is the ground height minus its
// own height.
groundY: anygroundY: const hills: {}hills[const x: numberx + Math.floor(const w: numberw / 2)],
// int(4) is shorthand for int(0, 4): 0, 1, 2, or 3. That picks one of the four
// building shades.
tint: numbertint: const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.random: RandomDefault engine PRNG (live reference - not a copy).
Time-seeded when the engine singleton is created. Call
{@link
BT.randomSeed
}
for a reproducible run. Mutating the instance (for example `BT.random.int(10)`)
advances the shared stream.random.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).int(const BUILDING_TINTS: 4BUILDING_TINTS),
});
}
// Trees, scattered along the ground.
const const trees: {}trees = [];
for (let let i: numberi = 0; let i: numberi < const TREE_COUNT: 7TREE_COUNT; let i: numberi++) {
const const x: numberx = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.random: RandomDefault engine PRNG (live reference - not a copy).
Time-seeded when the engine singleton is created. Call
{@link
BT.randomSeed
}
for a reproducible run. Mutating the instance (for example `BT.random.int(10)`)
advances the shared stream.random.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).int(3, const WORLD_W: 152WORLD_W - 4);
const trees: {}trees.push({
x: numberx,
groundY: anygroundY: const hills: {}hills[const x: numberx],
h: numberh: const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.random: RandomDefault engine PRNG (live reference - not a copy).
Time-seeded when the engine singleton is created. Call
{@link
BT.randomSeed
}
for a reproducible run. Mutating the instance (for example `BT.random.int(10)`)
advances the shared stream.random.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).int(6, 13),
});
}
return { seed: numberseed, hills: Array<number>hills, buildings: Array<object>buildings, trees: Array<object>trees, stars: Array<Vector2i>stars };
}
/**
* Two worlds side by side, proving that the same seed rebuilds the same world.
*
* @implements {IBTDemo}
*/
class class DemoTwo worlds side by side, proving that the same seed rebuilds the same world.Demo {
/** @type {Palette | null} */
Demo.palette: Palette | nullpalette = null;
// Slot map for the shared UI kit theme, filled in by applyTheme() during init().
Demo.theme: nulltheme = null;
// The two worlds. Each remembers everything it was built from - that is the difference
// between this demo and the Coordinate Patterns one, which remembers nothing at all:
// https://demos.blit386.dev/coordinate-patterns
Demo.left: nullleft = null;
Demo.right: nullright = null;
// Whether the engine picked the very first seeds by itself. Once the user presses a
// button this turns false, because from then on the seeds were chosen on purpose.
Demo.seedsWereAutomatic: booleanseedsWereAutomatic = true;
// A snapshot of what clone() and fork() do, worked out once in init().
Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams = { seed: numberseed: 0, base: {}base: [], cloned: {}cloned: [], forked: {}forked: [], forkSeed: undefinedforkSeed: var undefinedundefined };
// A private generator used only for choosing new seeds.
//
// It cannot be the shared BT.random, because generateWorld() reseeds that one. Drawing the
// next seed from a stream we just reseeded makes the answer a fixed consequence of the
// seed we reseeded it with - so "Copy left seed" followed by "New right" would hand back
// the very same number every time, and the button would look broken.
/** @type {Random | null} */
Demo.seedPicker: Random | nullseedPicker = null;
/**
* Builds the palette, then grows both worlds from seeds nobody chose.
*
* @returns {Promise<boolean>}
*/
async Demo.init(): Promise<boolean>Builds the palette, then grows both worlds from seeds nobody chose.init() {
this.Demo.palette: Palette | nullpalette = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.paletteCreate: (size?: number) => PaletteCreates a standalone palette instance.paletteCreate(256);
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_SKY: 1C_SKY, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(16, 20, 38)); // Night sky.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_STAR: 2C_STAR, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(220, 228, 245)); // Stars.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_GROUND: 3C_GROUND, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(28, 40, 32)); // Solid earth.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_HILL: 4C_HILL, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(74, 116, 80)); // Grassy hill surface.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_TREE: 5C_TREE, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(96, 168, 104)); // Leaves.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_TRUNK: 6C_TRUNK, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(90, 68, 48)); // Trunks.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_INK: 7C_INK, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(226, 232, 244)); // Frames and bright text.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_DIM: 8C_DIM, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(72, 80, 102)); // Faded lines.
// Four window-lit building shades, from dull to bright.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BUILDING_BASE: 10C_BUILDING_BASE + 0, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(64, 72, 96));
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BUILDING_BASE: 10C_BUILDING_BASE + 1, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(84, 92, 118));
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BUILDING_BASE: 10C_BUILDING_BASE + 2, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(104, 112, 140));
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BUILDING_BASE: 10C_BUILDING_BASE + 3, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(126, 134, 162));
// Install the shared UI colors, then hand the finished palette to the engine.
this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palettepalette);
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.paletteSet: (palette: Palette) => voidStores the active engine palette.
Use this to swap the **entire palette** (e.g. switch between a day and night
theme). After this call the renderer uploads the new palette uniform on the
next frame.
**Palette-value swap (change what a slot looks like):** mutate the live
{@link
BT.palette
}
in place with `palette.set(slot, newColor)`. The renderer
uploads dirty slots on the next frame; no `paletteSet()` or
{@link
BT.spritesRefresh
}
needed.
**Palette-layout swap (same colors, different slot positions):** build a new
palette with the same colors at new indices, call `paletteSet()`, then call
{@link
BT.spritesRefresh
}
so every sprite sheet re-maps its original RGBA
pixels against the new slot layout.paletteSet(this.Demo.palette: Palettepalette);
// Set up the seed picker before anything asks it for a number. Left to itself, a new
// Random seeds from the clock, so this stream is unrelated to the shared one.
this.Demo.seedPicker: Random | nullseedPicker = new new Random(seed?: number): RandomCreates a PRNG. Omit `seed` to time-seed from `Date.now()` (lower 32 bits).Random();
// Nobody has chosen a seed yet. The engine seeded itself from the clock when it
// started up, and seedValue hands that number back - so we can build a world from
// it and still show which number it was.
//
// That is the part worth noticing: even the run you did not plan has a seed you can
// write down and return to.
this.Demo.left: nullleft = function generateWorld(seed: number): {
seed: number;
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
Builds one complete world from a single seed.
This is the heart of the demo. Every random number it needs comes from BT.random, and
BT.random was just told where to start, so running this twice with the same seed walks
through the exact same numbers in the exact same order - and therefore builds the exact
same world.generateWorld(const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.random: RandomDefault engine PRNG (live reference - not a copy).
Time-seeded when the engine singleton is created. Call
{@link
BT.randomSeed
}
for a reproducible run. Mutating the instance (for example `BT.random.int(10)`)
advances the shared stream.random.Random.seedValue: number | undefinedThe last seed passed to the constructor or
{@link
seed
}
, normalized to an unsigned 32-bit value (the
same representation
{@link
getState
}
uses, not necessarily the raw number passed in). `undefined`
after
{@link
setState
}
(the stream position no longer corresponds to a known seed) or on a
{@link
fork
}
ed child (a fork is a new stream and should not claim to have been seeded by its caller).
{@link
clone
}
copies whatever value the parent currently holds.seedValue ?? const SEED_MIN: 1000SEED_MIN);
this.Demo.right: nullright = function generateWorld(seed: number): {
seed: number;
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
Builds one complete world from a single seed.
This is the heart of the demo. Every random number it needs comes from BT.random, and
BT.random was just told where to start, so running this twice with the same seed walks
through the exact same numbers in the exact same order - and therefore builds the exact
same world.generateWorld(this.Demo.rollSeed(): numberPicks a fresh, easy-to-read seed.rollSeed());
this.Demo.sampleStreams(): voidWorks out, once, what clone() and fork() do to a generator.sampleStreams();
return true;
}
/**
* Nothing moves in this demo, so update() only keeps the UI kit's key shortcuts alive.
*/
Demo.update(): voidNothing moves in this demo, so update() only keeps the UI kit's key shortcuts alive.update() {
// Always first: this is what makes the { key } shortcuts on buttons work.
import uiui.tick();
}
/**
* Draws both worlds, their seeds, and the control panels.
*/
Demo.render(): voidDraws both worlds, their seeds, and the control panels.render() {
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.clear: (paletteIndex: number) => voidSets the frame clear color using a palette index.
The renderer uses this color when clearing the full display at the start
of the next frame.clear(const C_SKY: 1C_SKY);
this.Demo.renderWorld(world: {
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}, originX: number): void
Draws one world inside its frame.renderWorld(this.Demo.left: nullleft, const WORLD_LEFT_X: 4WORLD_LEFT_X);
this.Demo.renderWorld(world: {
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}, originX: number): void
Draws one world inside its frame.renderWorld(this.Demo.right: nullright, const WORLD_RIGHT_X: 164WORLD_RIGHT_X);
// Each world's seed sits directly above it, so there is no doubt which is which.
import uiui.caption(const WORLD_LEFT_X: 4WORLD_LEFT_X, 8, `seed ${this.Demo.left: nullleft.seed}`);
import uiui.caption(const WORLD_RIGHT_X: 164WORLD_RIGHT_X, 8, `seed ${this.Demo.right: nullright.seed}`);
// When both seeds match, say so plainly - this is the moment the demo exists for.
if (this.Demo.left: nullleft.seed === this.Demo.right: nullright.seed) {
import uiui.caption(96, const WORLD_Y: 18WORLD_Y + const WORLD_H: 100WORLD_H + 4, 'Same seed, same world', { color: stringcolor: 'accent' });
} else if (this.Demo.seedsWereAutomatic: booleanseedsWereAutomatic) {
// The left seed is long because nobody typed it: the engine made it from the
// clock when the demo started. seedValue is how we can still read it.
import uiui.caption(48, const WORLD_Y: 18WORLD_Y + const WORLD_H: 100WORLD_H + 4, 'Left seed came from the clock', { color: stringcolor: 'dim' });
}
this.Demo.renderSeedPanel(): voidThe three buttons that reseed the worlds.renderSeedPanel();
this.Demo.renderStreamPanel(): voidThe clone-versus-fork comparison.renderStreamPanel();
}
/**
* Draws one world inside its frame.
*
* @param {{ hills: Array<number>, buildings: Array<object>, trees: Array<object>, stars: Array<Vector2i> }} world
* @param {number} originX - Left edge of this world's box on screen.
*/
Demo.renderWorld(world: {
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}, originX: number): void
Draws one world inside its frame.renderWorld(world: {
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
world, originX: number- Left edge of this world's box on screen.originX) {
// Everything inside a world is stored in "world coordinates" starting at 0, so each
// thing is drawn by adding the box's own corner to it. That is what lets the exact
// same world data be drawn on either side of the screen.
for (const const star: Array<Vector2i>star of world: {
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
world.stars: Array<Vector2i>stars) {
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => voidDraws a single pixel.
Accepts either:
- `(posOrX: Vector2i, yOrColor: number)` where `yOrColor` is the palette index.
- `(posOrX: number, yOrColor: number, maybeColor: number)` for `(x, y, paletteIndex)`.drawPixel(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(originX: number- Left edge of this world's box on screen.originX + const star: Array<Vector2i>star.x, const WORLD_Y: 18WORLD_Y + const star: Array<Vector2i>star.y), const C_STAR: 2C_STAR);
}
// The ground: one vertical line per column, from the hill surface down to the bottom.
for (let let x: numberx = 0; let x: numberx < const WORLD_W: 152WORLD_W; let x: numberx++) {
const const top: Array<number>top = world: {
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
world.hills: Array<number>hills[let x: numberx];
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => voidDraws a pixel-perfect line between two points.
Uses rasterized line drawing without antialiasing.drawLine(
new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(originX: number- Left edge of this world's box on screen.originX + let x: numberx, const WORLD_Y: 18WORLD_Y + const top: Array<number>top),
new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(originX: number- Left edge of this world's box on screen.originX + let x: numberx, const WORLD_Y: 18WORLD_Y + const WORLD_H: 100WORLD_H - 1),
const C_GROUND: 3C_GROUND,
);
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawPixel: (posOrX: Vector2i | number, yOrColor: number, maybeColor?: number) => voidDraws a single pixel.
Accepts either:
- `(posOrX: Vector2i, yOrColor: number)` where `yOrColor` is the palette index.
- `(posOrX: number, yOrColor: number, maybeColor: number)` for `(x, y, paletteIndex)`.drawPixel(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(originX: number- Left edge of this world's box on screen.originX + let x: numberx, const WORLD_Y: 18WORLD_Y + const top: Array<number>top), const C_HILL: 4C_HILL);
}
for (const const b: Array<object>b of world: {
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
world.buildings: Array<object>buildings) {
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawRectFill: (rect: Rect2i, paletteIndex: number) => voidDraws a filled rectangle.drawRectFill(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(originX: number- Left edge of this world's box on screen.originX + const b: Array<object>b.x, const WORLD_Y: 18WORLD_Y + const b: Array<object>b.groundY - const b: Array<object>b.h, const b: Array<object>b.w, const b: Array<object>b.h), const C_BUILDING_BASE: 10C_BUILDING_BASE + const b: Array<object>b.tint);
}
for (const const t: Array<object>t of world: {
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
world.trees: Array<object>trees) {
// A trunk, then a blob of leaves on top of it.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => voidDraws a pixel-perfect line between two points.
Uses rasterized line drawing without antialiasing.drawLine(
new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(originX: number- Left edge of this world's box on screen.originX + const t: Array<object>t.x, const WORLD_Y: 18WORLD_Y + const t: Array<object>t.groundY),
new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(originX: number- Left edge of this world's box on screen.originX + const t: Array<object>t.x, const WORLD_Y: 18WORLD_Y + const t: Array<object>t.groundY - const t: Array<object>t.h),
const C_TRUNK: 6C_TRUNK,
);
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawRectFill: (rect: Rect2i, paletteIndex: number) => voidDraws a filled rectangle.drawRectFill(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(originX: number- Left edge of this world's box on screen.originX + const t: Array<object>t.x - 2, const WORLD_Y: 18WORLD_Y + const t: Array<object>t.groundY - const t: Array<object>t.h - 3, 5, 5), const C_TREE: 5C_TREE);
}
// The frame goes on last so it sits cleanly on top of everything.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 106 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawRect: (rect: Rect2i, paletteIndex: number) => voidDraws an unfilled rectangle outline.drawRect(new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(originX: number- Left edge of this world's box on screen.originX - 1, const WORLD_Y: 18WORLD_Y - 1, const WORLD_W: 152WORLD_W + 2, const WORLD_H: 100WORLD_H + 2), const C_DIM: 8C_DIM);
}
/**
* The three buttons that reseed the worlds.
*/
Demo.renderSeedPanel(): voidThe three buttons that reseed the worlds.renderSeedPanel() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_LEFT);
import uiui.panel('Seeds');
if (import uiui.button('New left', { key: stringkey: 'KeyQ' })) {
this.Demo.left: nullleft = function generateWorld(seed: number): {
seed: number;
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
Builds one complete world from a single seed.
This is the heart of the demo. Every random number it needs comes from BT.random, and
BT.random was just told where to start, so running this twice with the same seed walks
through the exact same numbers in the exact same order - and therefore builds the exact
same world.generateWorld(this.Demo.rollSeed(): numberPicks a fresh, easy-to-read seed.rollSeed());
this.Demo.seedsWereAutomatic: booleanseedsWereAutomatic = false;
}
if (import uiui.button('New right', { key: stringkey: 'KeyW' })) {
this.Demo.right: nullright = function generateWorld(seed: number): {
seed: number;
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
Builds one complete world from a single seed.
This is the heart of the demo. Every random number it needs comes from BT.random, and
BT.random was just told where to start, so running this twice with the same seed walks
through the exact same numbers in the exact same order - and therefore builds the exact
same world.generateWorld(this.Demo.rollSeed(): numberPicks a fresh, easy-to-read seed.rollSeed());
this.Demo.seedsWereAutomatic: booleanseedsWereAutomatic = false;
}
if (import uiui.button('Copy left seed', { key: stringkey: 'KeyE' })) {
// Nothing about the right world is copied here - only the number. The world is
// rebuilt from scratch, and it comes back identical because the number is the same.
this.Demo.right: nullright = function generateWorld(seed: number): {
seed: number;
hills: Array<number>;
buildings: Array<object>;
trees: Array<object>;
stars: Array<Vector2i>;
}
Builds one complete world from a single seed.
This is the heart of the demo. Every random number it needs comes from BT.random, and
BT.random was just told where to start, so running this twice with the same seed walks
through the exact same numbers in the exact same order - and therefore builds the exact
same world.generateWorld(this.Demo.left: nullleft.seed);
this.Demo.seedsWereAutomatic: booleanseedsWereAutomatic = false;
}
import uiui.end();
}
/**
* The clone-versus-fork comparison.
*/
Demo.renderStreamPanel(): voidThe clone-versus-fork comparison.renderStreamPanel() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_RIGHT);
import uiui.panel(`One more generator (seed ${this.Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams.seed: numberseed})`);
import uiui.kv('original', this.Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams.base: {}base.join(' '));
import uiui.kv('clone()', this.Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams.cloned: {}cloned.join(' '));
import uiui.kv('fork()', this.Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams.forked: {}forked.join(' '));
import uiui.separator();
import uiui.label('A clone carries on identically.', { color: stringcolor: 'dim' });
import uiui.label('A fork goes its own way, and its', { color: stringcolor: 'dim' });
import uiui.label(`seedValue reads ${this.Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams.forkSeed: undefinedforkSeed ?? 'unknown'}.`, { color: stringcolor: 'dim' });
import uiui.end();
}
/**
* Picks a fresh, easy-to-read seed.
*
* @returns {number}
*/
Demo.rollSeed(): numberPicks a fresh, easy-to-read seed.rollSeed() {
// intInclusive(a, b) can return b itself, unlike int(a, b) which stops just short of
// it. For a seed range meant to read as "1000 to 9999", inclusive is what we want.
//
// This comes from seedPicker rather than BT.random - see the field for why.
return this.Demo.seedPicker: Random | nullseedPicker.Random.intInclusive(min: number, max: number): numberReturns a pseudo-random integer in [min, max] (inclusive on both ends).intInclusive(const SEED_MIN: 1000SEED_MIN, const SEED_MAX: 9999SEED_MAX);
}
/**
* Works out, once, what clone() and fork() do to a generator.
*/
Demo.sampleStreams(): voidWorks out, once, what clone() and fork() do to a generator.sampleStreams() {
// A generator of our own, separate from the shared BT.random, so sampling it cannot
// disturb the worlds we just built. `new Random(seed)` is how you make one.
const const seed: numberseed = this.Demo.rollSeed(): numberPicks a fresh, easy-to-read seed.rollSeed();
const const original: Randomoriginal = new new Random(seed?: number): RandomCreates a PRNG. Omit `seed` to time-seed from `Date.now()` (lower 32 bits).Random(const seed: numberseed);
// Order matters here. fork() has to draw one number from the parent to seed the child,
// which nudges the parent forward. So we fork FIRST, and only then take the clone -
// that way the original and its clone are standing in the same place, and any
// difference you see between them would be a real one.
const const forked: Randomforked = const original: Randomoriginal.Random.fork(): RandomReturns an independent sub-stream. Advances this generator once to seed the child. The child's
{@link
seedValue
}
is always `undefined` - a fork should not claim a caller-chosen seed.fork();
const const cloned: Randomcloned = const original: Randomoriginal.Random.clone(): RandomReturns a new generator with the same state (identical stream from this point). The copy also shares
this generator's
{@link
seedValue
}
, including `undefined`.clone();
for (let let i: numberi = 0; let i: numberi < const STREAM_SAMPLE: 3STREAM_SAMPLE; let i: numberi++) {
// int(100) is shorthand for int(0, 100): a whole number from 0 to 99.
this.Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams.base: {}base.push(const original: Randomoriginal.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).int(100));
this.Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams.cloned: {}cloned.push(const cloned: Randomcloned.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).int(100));
this.Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams.forked: {}forked.push(const forked: Randomforked.Random.int(minOrMaxExclusive: number, maxExclusive?: number): numberReturns a pseudo-random integer in [0, maxExclusive) or [min, maxExclusive).int(100));
}
this.Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams.seed: numberseed = const seed: numberseed;
// A fork is a brand new stream, so the engine refuses to claim it was seeded by us -
// its seedValue is deliberately left unknown.
this.Demo.streams: {
seed: number;
base: {};
cloned: {};
forked: {};
forkSeed: undefined;
}
streams.forkSeed: undefinedforkSeed = const forked: Randomforked.Random.seedValue: number | undefinedThe last seed passed to the constructor or
{@link
seed
}
, normalized to an unsigned 32-bit value (the
same representation
{@link
getState
}
uses, not necessarily the raw number passed in). `undefined`
after
{@link
setState
}
(the stream position no longer corresponds to a known seed) or on a
{@link
fork
}
ed child (a fork is a new stream and should not claim to have been seeded by its caller).
{@link
clone
}
copies whatever value the parent currently holds.seedValue;
}
}
function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>One-liner bootstrap function for BLIT386 demos.
Handles canvas retrieval and engine initialization. Backend selection
(WebGPU or software fallback) is managed internally by BTAPI.
This function provides a streamlined way to start a demo with sensible defaults
while allowing customization through options.bootstrap(class DemoTwo worlds side by side, proving that the same seed rebuilds the same world.Demo);