// Random Basics: the shuffling, weighting, and scattering tools built into the engine.
// @description Five scenes for the BT.random generators: shuffling, weighted drops, scatter, flips, and walkers.
//
// Part of the BLIT386 demo series.
//
// Prerequisites:
// Basics https://demos.blit386.dev/basics
// Primitives https://demos.blit386.dev/primitives
// Colors https://demos.blit386.dev/colors
//
// WHAT YOU WILL SEE
// Five small scenes, one at a time. Press 1-5 (or tap the buttons) to switch between them.
// Each scene shows off one way of asking the engine for a random result:
// 1. Shuffle - mixing a row of cards, and the difference between the two ways to do it
// 2. Weighted - a treasure chest where rare prizes really are rare
// 3. Gaussian - arrows landing near a target instead of anywhere at all
// 4. Sign - a coin flip that answers "left" or "right"
// 5. Directions - two bugs walking a grid, one allowed 4 ways, the other allowed 8
//
// WHAT YOU WILL LEARN
// - BT.random.shuffle() hands back a NEW mixed-up copy and leaves your list alone
// - BT.random.shuffleInPlace() mixes up the list you gave it, so the old order is gone
// - BT.random.weighted() lets you say "this prize should show up 70 times as often"
// - BT.random.gaussian() clumps results near a middle value instead of spreading them evenly
// - BT.random.sign() answers -1 or 1, which is perfect for "which way?" questions
// - BT.random.direction4() and direction8() hand back a ready-made step as a Vector2i
//
// A NOTE ON WHAT IS NOT HERE
// The everyday tools - int(), float(), and pick() - already appear all over the other demos,
// so this one covers the ones you have not met yet. The Camera demo uses int() and
// pointInRange() to scatter buildings: https://demos.blit386.dev/camera
//
// 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 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", which is what
// every position and size below is measured in.
// The five scenes, in the order the number keys select them.
const const MODE_SHUFFLE: 0MODE_SHUFFLE = 0;
const const MODE_WEIGHTED: 1MODE_WEIGHTED = 1;
const const MODE_GAUSSIAN: 2MODE_GAUSSIAN = 2;
const const MODE_SIGN: 3MODE_SIGN = 3;
const const MODE_DIRECTIONS: 4MODE_DIRECTIONS = 4;
// Names shown in the mode panel. The array position matches the MODE_* numbers above.
const const MODE_NAMES: {}MODE_NAMES = ['Shuffle', 'Weighted', 'Gaussian', 'Sign', 'Directions'];
// Color slots. The shared UI kit owns slots 240-251, so scene colors stay well below that.
const const C_BG: 1C_BG = 1; // Dark background behind every scene.
const const C_INK: 2C_INK = 2; // Bright text and outlines.
const const C_DIM: 3C_DIM = 3; // Faded lines: guides, grids, and old trails.
const const C_TARGET: 4C_TARGET = 4; // The target rings in the Gaussian scene.
const const C_SHOT: 5C_SHOT = 5; // Arrows that used gaussian().
const const C_SHOT_FLAT: 6C_SHOT_FLAT = 6; // Arrows that used float() instead, for comparison.
const const C_BUG_A: 7C_BUG_A = 7; // The 4-direction bug.
const const C_BUG_B: 8C_BUG_B = 8; // The 8-direction bug.
// The eight cards in the shuffle scene each get their own color, in slots 10-17.
const const C_CARD_BASE: 10C_CARD_BASE = 10;
const const CARD_COUNT: 8CARD_COUNT = 8;
// Card size, and how far apart their left edges sit. Eight cards at 34 pixels apart, starting
// 22 pixels in, reach x = 294 - just inside the 320-pixel screen.
const const CARD_W: 26CARD_W = 26;
const const CARD_H: 34CARD_H = 34;
const const CARD_STRIDE: 34CARD_STRIDE = 34;
const const CARD_LEFT: 22CARD_LEFT = 22;
// The four treasure tiers each get their own color, in slots 20-23.
const const C_TIER_BASE: 20C_TIER_BASE = 20;
// Treasure tiers, rarest last. `TIER_INDEXES` is what we actually hand to weighted():
// asking for the index rather than the name means the answer can be used to look up both
// the label and the color without searching the array afterward.
const const TIER_NAMES: {}TIER_NAMES = ['Common', 'Uncommon', 'Rare', 'Legendary'];
const const TIER_INDEXES: {}TIER_INDEXES = [0, 1, 2, 3];
const const TIER_WEIGHTS: {}TIER_WEIGHTS = [70, 20, 9, 1];
// How many update ticks pass between automatic treasure drops.
const const DROP_EVERY_TICKS: 8DROP_EVERY_TICKS = 8;
// The falling gems disappear once they reach this line.
const const DROP_FLOOR_Y: 132DROP_FLOOR_Y = 132;
// How many arrows stay on screen in the Gaussian scene before the oldest is reused.
const const SHOT_COUNT: 120SHOT_COUNT = 120;
// How many update ticks pass between arrows.
const const SHOT_EVERY_TICKS: 3SHOT_EVERY_TICKS = 3;
// Where the target sits, and how wide its rings are.
const const TARGET_CENTER: Vector2iTARGET_CENTER = new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(160, 76);
const const TARGET_RINGS: {}TARGET_RINGS = [44, 33, 22, 11];
// The Sign scene walks a dot along this line.
const const WALK_Y: 88WALK_Y = 88;
const const WALK_LEFT: 40WALK_LEFT = 40;
const const WALK_RIGHT: 280WALK_RIGHT = 280;
// How many update ticks pass between walker steps and bug steps.
const const STEP_EVERY_TICKS: 6STEP_EVERY_TICKS = 6;
// The Directions scene gives each bug its own square to wander, and remembers this many
// of its recent positions as a trail.
const const BUG_AREA_A: Rect2iBUG_AREA_A = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(24, 28, 120, 94);
const const BUG_AREA_B: Rect2iBUG_AREA_B = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(176, 28, 120, 94);
const const TRAIL_LENGTH: 90TRAIL_LENGTH = 90;
// Each bug is drawn as a small square centered on its position, reaching BUG_REACH pixels
// out in every direction. The bug therefore has to stop BUG_REACH short of its square's
// edge, or half the marker would hang outside the frame.
const const BUG_REACH: 2BUG_REACH = 2;
const const BUG_SIZE: numberBUG_SIZE = const BUG_REACH: 2BUG_REACH * 2 + 1;
/**
* Clamps a whole number so it never leaves the range min..max.
*
* "Clamp" means "keep it inside the fence": if the value wandered past either end, this
* pushes it back to the nearest edge.
*
* @param {number} value
* @param {number} min
* @param {number} max
* @returns {number}
*/
function function clampInt(value: number, min: number, max: number): numberClamps a whole number so it never leaves the range min..max.
"Clamp" means "keep it inside the fence": if the value wandered past either end, this
pushes it back to the nearest edge.clampInt(value: numbervalue, min: numbermin, max: numbermax) {
if (value: numbervalue < min: numbermin) {
return min: numbermin;
}
if (value: numbervalue > max: numbermax) {
return max: numbermax;
}
return value: numbervalue;
}
/**
* Five small scenes covering the parts of BT.random the other demos do not use.
*
* @implements {IBTDemo}
*/
class class DemoFive small scenes covering the parts of BT.random the other demos do not use.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;
// Which scene is on screen right now. Starts on the shuffle cards.
Demo.mode: numbermode = const MODE_SHUFFLE: 0MODE_SHUFFLE;
// Counts update ticks so the scenes can act every few ticks instead of every single one.
Demo.ticks: numberticks = 0;
// SHUFFLE SCENE
// `deck` is the real list of cards. `shuffled` is the copy that shuffle() handed back.
// Keeping them apart is the whole point of this scene: one button changes only the copy,
// the other button changes the list itself.
Demo.deck: {}deck = [];
Demo.shuffled: {}shuffled = [];
// WEIGHTED SCENE
// How many of each tier have dropped so far, and the gems currently falling.
Demo.tierCounts: {}tierCounts = [0, 0, 0, 0];
Demo.gems: {}gems = [];
// GAUSSIAN SCENE
// A fixed-size ring of arrow positions. `shotNext` says which slot the next arrow
// overwrites, so the oldest arrow quietly disappears without any array shuffling.
Demo.shots: {}shots = [];
Demo.shotNext: numbershotNext = 0;
Demo.shotSpread: numbershotSpread = 18;
Demo.useGaussian: booleanuseGaussian = true;
// SIGN SCENE
// Where the walker stands, and how many times each answer has come up.
Demo.walkX: numberwalkX = (const WALK_LEFT: 40WALK_LEFT + const WALK_RIGHT: 280WALK_RIGHT) / 2;
Demo.leftCount: numberleftCount = 0;
Demo.rightCount: numberrightCount = 0;
// DIRECTIONS SCENE
// Each bug remembers where it is and where it has been.
Demo.bugA: {
pos: Vector2i;
trail: {};
}
bugA = { pos: Vector2ipos: new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(0, 0), trail: {}trail: [] };
Demo.bugB: {
pos: Vector2i;
trail: {};
}
bugB = { pos: Vector2ipos: new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(0, 0), trail: {}trail: [] };
/**
* Builds the palette and fills in the starting state of every scene.
*
* @returns {Promise<boolean>}
*/
async Demo.init(): Promise<boolean>Builds the palette and fills in the starting state of every scene.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_BG: 1C_BG, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(14, 16, 26)); // Near-black blue background.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_INK: 2C_INK, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(226, 232, 244)); // Bright text and outlines.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_DIM: 3C_DIM, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(72, 80, 102)); // Faded guides and grids.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_TARGET: 4C_TARGET, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(196, 148, 64)); // Amber target rings.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_SHOT: 5C_SHOT, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(120, 226, 168)); // Green arrows: gaussian().
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_SHOT_FLAT: 6C_SHOT_FLAT, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(226, 120, 140)); // Red arrows: float().
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BUG_A: 7C_BUG_A, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(126, 190, 255)); // Blue 4-direction bug.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BUG_B: 8C_BUG_B, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 186, 110)); // Orange 8-direction bug.
// Eight card colors spread around the rainbow, so a reordering is obvious at a glance.
// A "hue" is a position on the color wheel: 0 is red, 120 is green, 240 is blue, and
// 360 comes back around to red. Stepping evenly around the wheel is the quickest way
// to get eight colors nobody could confuse with each other.
for (let let i: numberi = 0; let i: numberi < const CARD_COUNT: 8CARD_COUNT; let i: numberi++) {
const const hue: numberhue = (let i: numberi / const CARD_COUNT: 8CARD_COUNT) * 360;
// fromHSL takes the hue, then how colorful it is (0-100), then how light (0-100).
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_CARD_BASE: 10C_CARD_BASE + let i: numberi, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32.Color32.fromHSL(h: number, s: number, l: number, a?: number): Color32Creates a color from HSL values.fromHSL(const hue: numberhue, 70, 62));
}
// Four treasure colors, getting brighter as the prize gets rarer.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_TIER_BASE: 20C_TIER_BASE + 0, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(140, 150, 165)); // Common: dull gray.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_TIER_BASE: 20C_TIER_BASE + 1, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(120, 210, 140)); // Uncommon: green.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_TIER_BASE: 20C_TIER_BASE + 2, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(120, 170, 255)); // Rare: blue.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_TIER_BASE: 20C_TIER_BASE + 3, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 210, 110)); // Legendary: gold.
// Install the shared UI colors, then hand the finished palette to the engine.
// applyTheme() must come before paletteSet() so the kit's colors are included.
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);
// The deck starts in order: card 0, card 1, card 2, and so on. Starting tidy means
// the first shuffle is easy to see.
for (let let i: numberi = 0; let i: numberi < const CARD_COUNT: 8CARD_COUNT; let i: numberi++) {
this.Demo.deck: {}deck.push(let i: numberi);
}
// Before anyone presses a button, the "shuffled copy" is just a copy of the deck.
this.Demo.shuffled: {}shuffled = this.Demo.deck: {}deck.slice();
// Fill the arrow ring with off-screen positions so nothing is drawn until the first
// arrow is actually fired.
for (let let i: numberi = 0; let i: numberi < const SHOT_COUNT: 120SHOT_COUNT; let i: numberi++) {
this.Demo.shots: {}shots.push({ x: numberx: -100, y: numbery: -100, wasGaussian: booleanwasGaussian: true });
}
// Start each bug in the middle of its own square.
this.Demo.bugA: {
pos: Vector2i;
trail: {};
}
bugA.pos: Vector2ipos = new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(
const BUG_AREA_A: Rect2iBUG_AREA_A.Rect2i.x: numberLeft edge X coordinate (defaults to 0).x + Math.floor(const BUG_AREA_A: Rect2iBUG_AREA_A.Rect2i.width: numberWidth in pixels (defaults to 0).width / 2),
const BUG_AREA_A: Rect2iBUG_AREA_A.Rect2i.y: numberTop edge Y coordinate (defaults to 0).y + Math.floor(const BUG_AREA_A: Rect2iBUG_AREA_A.Rect2i.height: numberHeight in pixels (defaults to 0).height / 2),
);
this.Demo.bugB: {
pos: Vector2i;
trail: {};
}
bugB.pos: Vector2ipos = new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(
const BUG_AREA_B: Rect2iBUG_AREA_B.Rect2i.x: numberLeft edge X coordinate (defaults to 0).x + Math.floor(const BUG_AREA_B: Rect2iBUG_AREA_B.Rect2i.width: numberWidth in pixels (defaults to 0).width / 2),
const BUG_AREA_B: Rect2iBUG_AREA_B.Rect2i.y: numberTop edge Y coordinate (defaults to 0).y + Math.floor(const BUG_AREA_B: Rect2iBUG_AREA_B.Rect2i.height: numberHeight in pixels (defaults to 0).height / 2),
);
return true;
}
/**
* Advances whichever scene is currently on screen.
*/
Demo.update(): voidAdvances whichever scene is currently on screen.update() {
// Always first: this is what makes the { key } shortcuts on buttons work.
import uiui.tick();
this.Demo.ticks: numberticks++;
// Number keys jump straight to a scene. Reading key presses here in update() - never
// in render() - is what keeps a quick tap from being missed.
if (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.isKeyPressed: (key: string, repeatRate?: number) => booleanChecks whether a keyboard key was pressed on the current fixed-update tick.
Optional `repeatRate` is in fixed ticks between repeats (`0` or omitted =
edge only). When `repeatRate > 0`, repeats fire while held per
`(ticks - firstPressTick) > 0 && (ticks - firstPressTick) % repeatRate === 0`.
Call from `update()`, not `render()`: the press edge clears once per fixed-update
tick, which always runs before that frame's `render()`, so a press read from
`render()` can be intermittently missed under rapid input.isKeyPressed('Digit1')) {
this.Demo.mode: numbermode = const MODE_SHUFFLE: 0MODE_SHUFFLE;
}
if (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.isKeyPressed: (key: string, repeatRate?: number) => booleanChecks whether a keyboard key was pressed on the current fixed-update tick.
Optional `repeatRate` is in fixed ticks between repeats (`0` or omitted =
edge only). When `repeatRate > 0`, repeats fire while held per
`(ticks - firstPressTick) > 0 && (ticks - firstPressTick) % repeatRate === 0`.
Call from `update()`, not `render()`: the press edge clears once per fixed-update
tick, which always runs before that frame's `render()`, so a press read from
`render()` can be intermittently missed under rapid input.isKeyPressed('Digit2')) {
this.Demo.mode: numbermode = const MODE_WEIGHTED: 1MODE_WEIGHTED;
}
if (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.isKeyPressed: (key: string, repeatRate?: number) => booleanChecks whether a keyboard key was pressed on the current fixed-update tick.
Optional `repeatRate` is in fixed ticks between repeats (`0` or omitted =
edge only). When `repeatRate > 0`, repeats fire while held per
`(ticks - firstPressTick) > 0 && (ticks - firstPressTick) % repeatRate === 0`.
Call from `update()`, not `render()`: the press edge clears once per fixed-update
tick, which always runs before that frame's `render()`, so a press read from
`render()` can be intermittently missed under rapid input.isKeyPressed('Digit3')) {
this.Demo.mode: numbermode = const MODE_GAUSSIAN: 2MODE_GAUSSIAN;
}
if (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.isKeyPressed: (key: string, repeatRate?: number) => booleanChecks whether a keyboard key was pressed on the current fixed-update tick.
Optional `repeatRate` is in fixed ticks between repeats (`0` or omitted =
edge only). When `repeatRate > 0`, repeats fire while held per
`(ticks - firstPressTick) > 0 && (ticks - firstPressTick) % repeatRate === 0`.
Call from `update()`, not `render()`: the press edge clears once per fixed-update
tick, which always runs before that frame's `render()`, so a press read from
`render()` can be intermittently missed under rapid input.isKeyPressed('Digit4')) {
this.Demo.mode: numbermode = const MODE_SIGN: 3MODE_SIGN;
}
if (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.isKeyPressed: (key: string, repeatRate?: number) => booleanChecks whether a keyboard key was pressed on the current fixed-update tick.
Optional `repeatRate` is in fixed ticks between repeats (`0` or omitted =
edge only). When `repeatRate > 0`, repeats fire while held per
`(ticks - firstPressTick) > 0 && (ticks - firstPressTick) % repeatRate === 0`.
Call from `update()`, not `render()`: the press edge clears once per fixed-update
tick, which always runs before that frame's `render()`, so a press read from
`render()` can be intermittently missed under rapid input.isKeyPressed('Digit5')) {
this.Demo.mode: numbermode = const MODE_DIRECTIONS: 4MODE_DIRECTIONS;
}
// Only the scene being looked at needs to do any work.
if (this.Demo.mode: numbermode === const MODE_WEIGHTED: 1MODE_WEIGHTED) {
this.Demo.updateWeighted(): voidDrops a new gem every few ticks and moves the falling ones down the screen.updateWeighted();
} else if (this.Demo.mode: numbermode === const MODE_GAUSSIAN: 2MODE_GAUSSIAN) {
this.Demo.updateGaussian(): voidFires an arrow at the target every few ticks.updateGaussian();
} else if (this.Demo.mode: numbermode === const MODE_SIGN: 3MODE_SIGN) {
this.Demo.updateSign(): voidSteps the walker left or right.updateSign();
} else if (this.Demo.mode: numbermode === const MODE_DIRECTIONS: 4MODE_DIRECTIONS) {
this.Demo.updateDirections(): voidSteps both bugs and records where they have been.updateDirections();
}
}
/**
* Draws the current scene, then the panels on top of it.
*/
Demo.render(): voidDraws the current scene, then the panels on top of it.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_BG: 1C_BG);
if (this.Demo.mode: numbermode === const MODE_SHUFFLE: 0MODE_SHUFFLE) {
this.Demo.renderShuffle(): voidDraws the two rows of cards.renderShuffle();
} else if (this.Demo.mode: numbermode === const MODE_WEIGHTED: 1MODE_WEIGHTED) {
this.Demo.renderWeighted(): voidDraws the chest, the falling gems, and the tier tally.renderWeighted();
} else if (this.Demo.mode: numbermode === const MODE_GAUSSIAN: 2MODE_GAUSSIAN) {
this.Demo.renderGaussian(): voidDraws the target and every arrow currently stuck in it.renderGaussian();
} else if (this.Demo.mode: numbermode === const MODE_SIGN: 3MODE_SIGN) {
this.Demo.renderSign(): voidDraws the walking line and the dot standing on it.renderSign();
} else {
this.Demo.renderDirections(): voidDraws both bugs and their trails.renderDirections();
}
// The mode picker sits in the same corner in every scene, so it never moves around.
// Two arrow buttons rather than five named ones: at 320x240 a five-row panel would
// swallow half the screen, and the number keys still jump straight to a scene.
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_LEFT);
import uiui.panel(`Scene ${this.Demo.mode: numbermode + 1}/5 (keys 1-5)`);
import uiui.label(const MODE_NAMES: {}MODE_NAMES[this.Demo.mode: numbermode], { color: stringcolor: 'accent' });
// The two buttons are tappable, so someone on a phone can reach every scene without
// a keyboard. Adding MODE_NAMES.length before taking the remainder keeps the answer
// positive when stepping back from scene 1.
if (import uiui.button('< Prev')) {
this.Demo.mode: numbermode = (this.Demo.mode: numbermode + const MODE_NAMES: {}MODE_NAMES.length - 1) % const MODE_NAMES: {}MODE_NAMES.length;
}
if (import uiui.button('Next >')) {
this.Demo.mode: numbermode = (this.Demo.mode: numbermode + 1) % const MODE_NAMES: {}MODE_NAMES.length;
}
import uiui.end();
// Each scene adds its own controls and readouts in the opposite corner.
if (this.Demo.mode: numbermode === const MODE_SHUFFLE: 0MODE_SHUFFLE) {
this.Demo.renderShufflePanel(): voidThe two shuffle buttons, which are the whole lesson of this scene.renderShufflePanel();
} else if (this.Demo.mode: numbermode === const MODE_WEIGHTED: 1MODE_WEIGHTED) {
this.Demo.renderWeightedPanel(): voidThe running tally of how often each tier has dropped.renderWeightedPanel();
} else if (this.Demo.mode: numbermode === const MODE_GAUSSIAN: 2MODE_GAUSSIAN) {
this.Demo.renderGaussianPanel(): voidThe spread slider and the flat-versus-clumped switch.renderGaussianPanel();
} else if (this.Demo.mode: numbermode === const MODE_SIGN: 3MODE_SIGN) {
this.Demo.renderSignPanel(): voidHow many times sign() answered each way.renderSignPanel();
} else {
this.Demo.renderDirectionsPanel(): voidA reminder of what separates the two bugs.renderDirectionsPanel();
}
}
/**
* Drops a new gem every few ticks and moves the falling ones down the screen.
*/
Demo.updateWeighted(): voidDrops a new gem every few ticks and moves the falling ones down the screen.updateWeighted() {
if (this.Demo.ticks: numberticks % const DROP_EVERY_TICKS: 8DROP_EVERY_TICKS === 0) {
this.Demo.dropGem(): voidAsks weighted() for one treasure tier and starts a gem falling.dropGem();
}
// Walk the list backward so removing an item cannot make the loop skip the next one.
for (let let i: numberi = this.Demo.gems: {}gems.length - 1; let i: numberi >= 0; let i: numberi--) {
const const gem: anygem = this.Demo.gems: {}gems[let i: numberi];
const gem: anygem.y += 2;
if (const gem: anygem.y >= const DROP_FLOOR_Y: 132DROP_FLOOR_Y) {
this.Demo.gems: {}gems.splice(let i: numberi, 1);
}
}
}
/**
* Asks weighted() for one treasure tier and starts a gem falling.
*/
Demo.dropGem(): voidAsks weighted() for one treasure tier and starts a gem falling.dropGem() {
// weighted() picks one entry from the first list, using the second list as the
// "how often should this win?" numbers. The weights here are 70, 20, 9, and 1, so
// Common turns up roughly 70 times for every 1 Legendary.
//
// We hand it TIER_INDEXES (0, 1, 2, 3) rather than the names, because the number it
// returns is then ready to use as a position in TIER_NAMES and as a color slot.
const const tier: anytier = 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.weighted<any>(items: readonly any[], weights: readonly number[]): anyReturns one item chosen by relative weights.weighted(const TIER_INDEXES: {}TIER_INDEXES, const TIER_WEIGHTS: {}TIER_WEIGHTS);
this.Demo.tierCounts: {}tierCounts[const tier: anytier]++;
this.Demo.gems: {}gems.push({
// int(a, b) gives a whole number from a up to (but not including) b, so this lands
// somewhere across the middle of the screen.
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(110, 210),
y: numbery: 40,
tier: anytier,
});
}
/**
* Fires an arrow at the target every few ticks.
*/
Demo.updateGaussian(): voidFires an arrow at the target every few ticks.updateGaussian() {
if (this.Demo.ticks: numberticks % const SHOT_EVERY_TICKS: 3SHOT_EVERY_TICKS !== 0) {
return;
}
const const shot: anyshot = this.Demo.shots: {}shots[this.Demo.shotNext: numbershotNext];
if (this.Demo.useGaussian: booleanuseGaussian) {
// gaussian(middle, spread) clumps its answers around the middle value. Most land
// close to it, a few land further out, and the really wild ones are rare - the
// same way most people are close to average height and giants are unusual.
const shot: anyshot.x = const TARGET_CENTER: Vector2iTARGET_CENTER.Vector2i.x: numberHorizontal component (defaults to 0).x + Math.round(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.gaussian(mean?: number, stddev?: number): numberReturns a sample from an approximate normal distribution (Box-Muller, no spare).gaussian(0, this.Demo.shotSpread: numbershotSpread));
const shot: anyshot.y = const TARGET_CENTER: Vector2iTARGET_CENTER.Vector2i.y: numberVertical component (defaults to 0).y + Math.round(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.gaussian(mean?: number, stddev?: number): numberReturns a sample from an approximate normal distribution (Box-Muller, no spare).gaussian(0, this.Demo.shotSpread: numbershotSpread));
} else {
// float(a, b) is the flat version: every distance from the middle is equally
// likely, so the arrows spread out into an even square with no clump at all.
const const reach: numberreach = this.Demo.shotSpread: numbershotSpread * 2;
const shot: anyshot.x = const TARGET_CENTER: Vector2iTARGET_CENTER.Vector2i.x: numberHorizontal component (defaults to 0).x + Math.round(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.float(min: number, max: number): numberReturns the next pseudo-random float in [min, max).float(-const reach: numberreach, const reach: numberreach));
const shot: anyshot.y = const TARGET_CENTER: Vector2iTARGET_CENTER.Vector2i.y: numberVertical component (defaults to 0).y + Math.round(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.float(min: number, max: number): numberReturns the next pseudo-random float in [min, max).float(-const reach: numberreach, const reach: numberreach));
}
const shot: anyshot.wasGaussian = this.Demo.useGaussian: booleanuseGaussian;
// Move to the next slot, wrapping back to 0 at the end. This is why the oldest arrow
// vanishes without any list being rebuilt.
this.Demo.shotNext: numbershotNext = (this.Demo.shotNext: numbershotNext + 1) % const SHOT_COUNT: 120SHOT_COUNT;
}
/**
* Steps the walker left or right.
*/
Demo.updateSign(): voidSteps the walker left or right.updateSign() {
if (this.Demo.ticks: numberticks % const STEP_EVERY_TICKS: 6STEP_EVERY_TICKS !== 0) {
return;
}
// sign() answers -1 or 1, nothing else. Multiplying a distance by it turns "how far"
// into "how far, and which way" in a single step.
const const direction: 1 | -1direction = 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.sign(): -1 | 1Returns -1 or 1 with equal probability.sign();
if (const direction: 1 | -1direction < 0) {
this.Demo.leftCount: numberleftCount++;
} else {
this.Demo.rightCount: numberrightCount++;
}
this.Demo.walkX: numberwalkX = function clampInt(value: number, min: number, max: number): numberClamps a whole number so it never leaves the range min..max.
"Clamp" means "keep it inside the fence": if the value wandered past either end, this
pushes it back to the nearest edge.clampInt(this.Demo.walkX: numberwalkX + const direction: 1 | -1direction * 6, const WALK_LEFT: 40WALK_LEFT, const WALK_RIGHT: 280WALK_RIGHT);
}
/**
* Steps both bugs and records where they have been.
*/
Demo.updateDirections(): voidSteps both bugs and records where they have been.updateDirections() {
if (this.Demo.ticks: numberticks % const STEP_EVERY_TICKS: 6STEP_EVERY_TICKS !== 0) {
return;
}
// direction4() hands back a Vector2i that is one step up, down, left, or right - the
// four ways a rook moves in chess. No diagonals.
this.Demo.stepBug(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}, step: Vector2i, area: Rect2i): void
Moves one bug by one step and remembers the position it left behind.stepBug(this.Demo.bugA: {
pos: Vector2i;
trail: {};
}
bugA, 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.direction4(): Vector2iReturns one of the four cardinal unit directions (Y-down).
Possible values: `(1, 0)`, `(-1, 0)`, `(0, 1)`, `(0, -1)`.direction4(), const BUG_AREA_A: Rect2iBUG_AREA_A);
// direction8() adds the four diagonals, so this bug has eight choices - the way a king
// moves in chess. Its trail ends up looking rounder and less blocky.
this.Demo.stepBug(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}, step: Vector2i, area: Rect2i): void
Moves one bug by one step and remembers the position it left behind.stepBug(this.Demo.bugB: {
pos: Vector2i;
trail: {};
}
bugB, 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.direction8(): Vector2iReturns one of the eight king-move unit directions (Y-down).
Cardinals plus diagonals: `(±1, 0)`, `(0, ±1)`, `(±1, ±1)`.direction8(), const BUG_AREA_B: Rect2iBUG_AREA_B);
}
/**
* Moves one bug by one step and remembers the position it left behind.
*
* @param {{ pos: Vector2i, trail: Array<Vector2i> }} bug
* @param {Vector2i} step - One step, from direction4() or direction8().
* @param {Rect2i} area - The square this bug is allowed to wander inside.
*/
Demo.stepBug(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}, step: Vector2i, area: Rect2i): void
Moves one bug by one step and remembers the position it left behind.stepBug(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug, step: Vector2i- One step, from direction4() or direction8().step, area: Rect2i- The square this bug is allowed to wander inside.area) {
// Remember where the bug was standing before it moved, so the trail grows behind it.
bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.trail: Array<Vector2i>trail.push(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.pos: Vector2ipos.Vector2i.x: numberHorizontal component (defaults to 0).x, bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.pos: Vector2ipos.Vector2i.y: numberVertical component (defaults to 0).y));
// Once the trail is long enough, drop the oldest position off the front. shift()
// removes the first item in a list.
if (bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.trail: Array<Vector2i>trail.length > const TRAIL_LENGTH: 90TRAIL_LENGTH) {
bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.trail: Array<Vector2i>trail.shift();
}
// Steps are 3 pixels so the trail is easy to see. clampInt keeps the bug from walking
// out of its own square.
//
// The limits are pulled in twice over. The last pixel inside a square is one short of
// its width, so a square 120 wide starting at x = 24 ends at x = 143. Then BUG_REACH
// comes off each end as well, because the position is the middle of the marker rather
// than its corner - without that the square would straddle the frame.
bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.pos: Vector2ipos = new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(
function clampInt(value: number, min: number, max: number): numberClamps a whole number so it never leaves the range min..max.
"Clamp" means "keep it inside the fence": if the value wandered past either end, this
pushes it back to the nearest edge.clampInt(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.pos: Vector2ipos.Vector2i.x: numberHorizontal component (defaults to 0).x + step: Vector2i- One step, from direction4() or direction8().step.Vector2i.x: numberHorizontal component (defaults to 0).x * 3, area: Rect2i- The square this bug is allowed to wander inside.area.Rect2i.x: numberLeft edge X coordinate (defaults to 0).x + const BUG_REACH: 2BUG_REACH, area: Rect2i- The square this bug is allowed to wander inside.area.Rect2i.x: numberLeft edge X coordinate (defaults to 0).x + area: Rect2i- The square this bug is allowed to wander inside.area.Rect2i.width: numberWidth in pixels (defaults to 0).width - 1 - const BUG_REACH: 2BUG_REACH),
function clampInt(value: number, min: number, max: number): numberClamps a whole number so it never leaves the range min..max.
"Clamp" means "keep it inside the fence": if the value wandered past either end, this
pushes it back to the nearest edge.clampInt(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.pos: Vector2ipos.Vector2i.y: numberVertical component (defaults to 0).y + step: Vector2i- One step, from direction4() or direction8().step.Vector2i.y: numberVertical component (defaults to 0).y * 3, area: Rect2i- The square this bug is allowed to wander inside.area.Rect2i.y: numberTop edge Y coordinate (defaults to 0).y + const BUG_REACH: 2BUG_REACH, area: Rect2i- The square this bug is allowed to wander inside.area.Rect2i.y: numberTop edge Y coordinate (defaults to 0).y + area: Rect2i- The square this bug is allowed to wander inside.area.Rect2i.height: numberHeight in pixels (defaults to 0).height - 1 - const BUG_REACH: 2BUG_REACH),
);
}
/**
* Draws the two rows of cards.
*/
Demo.renderShuffle(): voidDraws the two rows of cards.renderShuffle() {
import uiui.caption(const CARD_LEFT: 22CARD_LEFT, 10, 'The list itself');
import uiui.caption(const CARD_LEFT: 22CARD_LEFT, 74, 'What shuffle() handed back', { color: stringcolor: 'dim' });
this.Demo.renderCardRow(cards: Array<number>, y: number): voidDraws one row of colored cards.renderCardRow(this.Demo.deck: {}deck, 24);
this.Demo.renderCardRow(cards: Array<number>, y: number): voidDraws one row of colored cards.renderCardRow(this.Demo.shuffled: {}shuffled, 88);
}
/**
* Draws one row of colored cards.
*
* @param {Array<number>} cards - Card numbers, in the order they should appear.
* @param {number} y - Top edge of the row.
*/
Demo.renderCardRow(cards: Array<number>, y: number): voidDraws one row of colored cards.renderCardRow(cards: Array<number>- Card numbers, in the order they should appear.cards, y: number- Top edge of the row.y) {
for (let let i: numberi = 0; let i: numberi < cards: Array<number>- Card numbers, in the order they should appear.cards.length; let i: numberi++) {
const const x: numberx = const CARD_LEFT: 22CARD_LEFT + let i: numberi * const CARD_STRIDE: 34CARD_STRIDE;
// The card's number decides its color, so a card keeps its color wherever it moves.
// That is what makes a reordering visible.
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(const x: numberx, y: number- Top edge of the row.y, const CARD_W: 26CARD_W, const CARD_H: 34CARD_H), const C_CARD_BASE: 10C_CARD_BASE + cards: Array<number>- Card numbers, in the order they should appear.cards[let i: numberi]);
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(const x: numberx, y: number- Top edge of the row.y, const CARD_W: 26CARD_W, const CARD_H: 34CARD_H), const C_INK: 2C_INK);
}
}
/**
* Draws the chest, the falling gems, and the tier tally.
*/
Demo.renderWeighted(): voidDraws the chest, the falling gems, and the tier tally.renderWeighted() {
import uiui.caption(112, 8, 'Treasure chest');
// The chest itself is just a box - the interesting part is what comes out 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.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(140, 22, 40, 16), const C_DIM: 3C_DIM);
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(140, 22, 40, 16), const C_INK: 2C_INK);
for (const const gem: anygem of this.Demo.gems: {}gems) {
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(const gem: anygem.x, const gem: anygem.y, 6, 6), const C_TIER_BASE: 20C_TIER_BASE + const gem: anygem.tier);
}
}
/**
* Draws the target and every arrow currently stuck in it.
*/
Demo.renderGaussian(): voidDraws the target and every arrow currently stuck in it.renderGaussian() {
import uiui.caption(96, 8, 'Aim for the middle');
// Concentric squares stand in for a round target - the engine draws rectangles, lines,
// and pixels, so a "ring" here is a square outline.
for (const const radius: anyradius of const TARGET_RINGS: {}TARGET_RINGS) {
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(const TARGET_CENTER: Vector2iTARGET_CENTER.Vector2i.x: numberHorizontal component (defaults to 0).x - const radius: anyradius, const TARGET_CENTER: Vector2iTARGET_CENTER.Vector2i.y: numberVertical component (defaults to 0).y - const radius: anyradius, const radius: anyradius * 2, const radius: anyradius * 2),
const C_TARGET: 4C_TARGET,
);
}
for (const const shot: anyshot of this.Demo.shots: {}shots) {
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(const shot: anyshot.x, const shot: anyshot.y, 2, 2), const shot: anyshot.wasGaussian ? const C_SHOT: 5C_SHOT : const C_SHOT_FLAT: 6C_SHOT_FLAT);
}
}
/**
* Draws the walking line and the dot standing on it.
*/
Demo.renderSign(): voidDraws the walking line and the dot standing on it.renderSign() {
import uiui.caption(88, 50, 'Left or right, nothing else');
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(const WALK_LEFT: 40WALK_LEFT, const WALK_Y: 88WALK_Y), new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const WALK_RIGHT: 280WALK_RIGHT, const WALK_Y: 88WALK_Y), const C_DIM: 3C_DIM);
// A tick mark at the starting point, so it is obvious how far the walker has drifted.
const const start: numberstart = (const WALK_LEFT: 40WALK_LEFT + const WALK_RIGHT: 280WALK_RIGHT) / 2;
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(const start: numberstart, const WALK_Y: 88WALK_Y - 6), new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const start: numberstart, const WALK_Y: 88WALK_Y + 6), const C_DIM: 3C_DIM);
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(this.Demo.walkX: numberwalkX - 3, const WALK_Y: 88WALK_Y - 3, 7, 7), const C_INK: 2C_INK);
}
/**
* Draws both bugs and their trails.
*/
Demo.renderDirections(): voidDraws both bugs and their trails.renderDirections() {
import uiui.caption(const BUG_AREA_A: Rect2iBUG_AREA_A.Rect2i.x: numberLeft edge X coordinate (defaults to 0).x, 12, '4 ways', { color: stringcolor: 'info' });
import uiui.caption(const BUG_AREA_B: Rect2iBUG_AREA_B.Rect2i.x: numberLeft edge X coordinate (defaults to 0).x, 12, '8 ways', { color: stringcolor: 'warm' });
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(const BUG_AREA_A: Rect2iBUG_AREA_A, const C_DIM: 3C_DIM);
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(const BUG_AREA_B: Rect2iBUG_AREA_B, const C_DIM: 3C_DIM);
this.Demo.renderBug(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}, colorSlot: number): void
Draws one bug's trail as single pixels, then the bug itself as a small square.renderBug(this.Demo.bugA: {
pos: Vector2i;
trail: {};
}
bugA, const C_BUG_A: 7C_BUG_A);
this.Demo.renderBug(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}, colorSlot: number): void
Draws one bug's trail as single pixels, then the bug itself as a small square.renderBug(this.Demo.bugB: {
pos: Vector2i;
trail: {};
}
bugB, const C_BUG_B: 8C_BUG_B);
}
/**
* Draws one bug's trail as single pixels, then the bug itself as a small square.
*
* @param {{ pos: Vector2i, trail: Array<Vector2i> }} bug
* @param {number} colorSlot
*/
Demo.renderBug(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}, colorSlot: number): void
Draws one bug's trail as single pixels, then the bug itself as a small square.renderBug(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug, colorSlot: numbercolorSlot) {
for (const const point: Array<Vector2i>point of bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.trail: Array<Vector2i>trail) {
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(const point: Array<Vector2i>point, const C_DIM: 3C_DIM);
}
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(bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.pos: Vector2ipos.Vector2i.x: numberHorizontal component (defaults to 0).x - const BUG_REACH: 2BUG_REACH, bug: {
pos: Vector2i;
trail: Array<Vector2i>;
}
bug.pos: Vector2ipos.Vector2i.y: numberVertical component (defaults to 0).y - const BUG_REACH: 2BUG_REACH, const BUG_SIZE: numberBUG_SIZE, const BUG_SIZE: numberBUG_SIZE), colorSlot: numbercolorSlot);
}
/**
* The two shuffle buttons, which are the whole lesson of this scene.
*/
Demo.renderShufflePanel(): voidThe two shuffle buttons, which are the whole lesson of this scene.renderShufflePanel() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_RIGHT);
import uiui.panel('Shuffle');
if (import uiui.button('shuffle() a copy', { key: stringkey: 'KeyC' })) {
// shuffle() builds a NEW mixed-up list and hands that back. The original list is
// untouched, which is why the top row does not move when you press this.
this.Demo.shuffled: {}shuffled = 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.shuffle<any>(arr: readonly any[]): any[]Returns a new array with the same elements in shuffled order (Fisher-Yates).shuffle(this.Demo.deck: {}deck);
}
if (import uiui.button('shuffleInPlace()', { key: stringkey: 'KeyP' })) {
// shuffleInPlace() mixes up the list you handed it. The top row jumps, and the old
// order is gone for good - there is no copy to go back to.
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.shuffleInPlace<any>(arr: any[]): any[]Shuffles an array in place (Fisher-Yates) and returns it.shuffleInPlace(this.Demo.deck: {}deck);
}
import uiui.separator();
import uiui.label('Copy leaves the top row alone.', { color: stringcolor: 'dim' });
import uiui.label('In place changes it for real.', { color: stringcolor: 'dim' });
import uiui.end();
}
/**
* The running tally of how often each tier has dropped.
*/
Demo.renderWeightedPanel(): voidThe running tally of how often each tier has dropped.renderWeightedPanel() {
// Adding the counts up gives us something to measure each tier against.
let let total: numbertotal = 0;
for (const const count: anycount of this.Demo.tierCounts: {}tierCounts) {
let total: numbertotal += const count: anycount;
}
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_RIGHT);
import uiui.panel('Drops so far');
for (let let i: numberi = 0; let i: numberi < const TIER_NAMES: {}TIER_NAMES.length; let i: numberi++) {
// A meter wants a fraction from 0 to 1, so each count is divided by the total.
// Guarding against a total of 0 avoids dividing by zero on the very first frame.
const const share: numbershare = let total: numbertotal > 0 ? this.Demo.tierCounts: {}tierCounts[let i: numberi] / let total: numbertotal : 0;
import uiui.meter(`${const TIER_NAMES: {}TIER_NAMES[let i: numberi]} ${this.Demo.tierCounts: {}tierCounts[let i: numberi]}`, const share: numbershare);
}
import uiui.separator();
import uiui.label(`Asked for 70 / 20 / 9 / 1`, { color: stringcolor: 'dim' });
import uiui.label(`Total drops: ${let total: numbertotal}`, { color: stringcolor: 'dim' });
import uiui.end();
}
/**
* The spread slider and the flat-versus-clumped switch.
*/
Demo.renderGaussianPanel(): voidThe spread slider and the flat-versus-clumped switch.renderGaussianPanel() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_RIGHT);
import uiui.panel('Scatter');
this.Demo.useGaussian: booleanuseGaussian = import uiui.checkbox('Use gaussian()', this.Demo.useGaussian: booleanuseGaussian, { key: stringkey: 'KeyG' });
this.Demo.shotSpread: numbershotSpread = Math.round(import uiui.slider('Spread', this.Demo.shotSpread: numbershotSpread, { min: numbermin: 4, max: numbermax: 40 }));
import uiui.separator();
if (this.Demo.useGaussian: booleanuseGaussian) {
import uiui.label('Clumped near the middle.', { color: stringcolor: 'dim' });
} else {
import uiui.label('Even, edge to edge.', { color: stringcolor: 'dim' });
}
import uiui.label('Toggle it and watch.', { color: stringcolor: 'dim' });
import uiui.end();
}
/**
* How many times sign() answered each way.
*/
Demo.renderSignPanel(): voidHow many times sign() answered each way.renderSignPanel() {
const const total: numbertotal = this.Demo.leftCount: numberleftCount + this.Demo.rightCount: numberrightCount;
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_RIGHT);
import uiui.panel('Coin flips');
import uiui.kv('Left (-1)', this.Demo.leftCount: numberleftCount);
import uiui.kv('Right (+1)', this.Demo.rightCount: numberrightCount);
import uiui.kv('Total', const total: numbertotal);
import uiui.separator();
import uiui.label('Close to even over time,', { color: stringcolor: 'dim' });
import uiui.label('but never exactly even.', { color: stringcolor: 'dim' });
import uiui.end();
}
/**
* A reminder of what separates the two bugs.
*/
Demo.renderDirectionsPanel(): voidA reminder of what separates the two bugs.renderDirectionsPanel() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_RIGHT);
import uiui.panel('Two bugs');
import uiui.label('Blue: direction4()', { color: stringcolor: 'info' });
import uiui.label('up, down, left, right', { color: stringcolor: 'dim' });
import uiui.spacer(4);
import uiui.label('Orange: direction8()', { color: stringcolor: 'warm' });
import uiui.label('those four, plus diagonals', { color: stringcolor: 'dim' });
import uiui.separator();
import uiui.label('Both hand back a Vector2i.', { color: stringcolor: 'dim' });
import uiui.end();
}
}
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 DemoFive small scenes covering the parts of BT.random the other demos do not use.Demo);