const DEFAULT_SIZE = 128; export class FreeList { #data: T[] = []; #free: LinkedList = new LinkedList(); #length: number = 0; constructor(arr?: T[]) { if(arr && arr.length > 0) this.#data = arr; } insert(element: T): number { if(this.#free.empty) { return this.#data.push(element) - 1; } else { const index = this.#free.pop()!; this.#data[index] = element; return index; } } erase(n: number): void { delete this.#data[n]; this.#free.add(n); } clear(): void { this.#data.length = 0; this.#free.clear(); } get(index: number): T { return this.#data[index]; } set(index: number, value: T): void { this.#data[index] = value; } get length(): number { return this.#length; } } export class IntList { #data: number[]; #fields: number; #capacity: number = DEFAULT_SIZE; #length: number = 0; #free: number = -1; constructor(fields: number) { if(fields <= 0) throw new Error("Invalid field count"); this.#data = new Array(this.#capacity * fields); this.#fields = fields; } get length(): number { return this.#length; } get(index: number, field: number): number { if(index >= this.#length || index < 0) throw new Error("Invalid index"); if(field >= this.#fields || field < 0) throw new Error("Invalid field"); return this.#data[index*this.#fields + field]; } set(index: number, field: number, value: number): void { if(index >= this.#length || index < 0) throw new Error("Invalid index"); if(field >= this.#fields || field < 0) throw new Error("Invalid field"); this.#data[index*this.#fields + field] = value; } clear(): void { //Never edit the array during clear, we'll keep every data since they *must* always be override before being read. this.#length = 0; this.#free = -1; } push(): number { const pos = (this.#length + 1) * this.#fields; if(pos > this.#capacity) { this.#capacity *= 2; this.#data.length = this.#capacity * this.#fields; } return this.#length++; } pop(): void { if(this.#length <= 0) return; --this.#length; } insert(): number { if(this.#free !== -1) { const index = this.#free; this.#free = this.#data[index * this.#fields]; return index; } else return this.push(); } erase(index: number): void { if(index >= this.#length || index < 0) throw new Error("Invalid index"); this.#data[index * this.#fields] = this.#free; this.#free = index; } toArray(): number[] { return this.#data.slice(0, this.#length); } //DEBUG printReadable(names: string[] = []): void { const size = this.#length * this.#fields; for(let i = 0; i < size; i) { const obj: Record = {}; for(let j = 0; j < this.#fields; ++i, ++j) { obj[names[j] ?? j] = this.#data[i]; } console.log(obj); } } } export class Stack { #arr: T[]; #pos: number; //Index of the last non empty value #bucketSize: number; #bucketCount: number = 1; constructor(size?: number) { this.#bucketSize = size ?? DEFAULT_SIZE; this.#arr = new Array(this.#bucketSize * this.#bucketCount); this.#pos = 0; } push(item: T): void { if(this.#pos >= this.#arr.length) this.#expand(); this.#arr[this.#pos] = item; this.#pos++; } pop(): T { if(this.length === 0) throw new Error("Empty queue."); const item = this.#arr[this.#pos]; delete this.#arr[this.#pos]; this.#pos--; return item; } peek(): T { return this.#arr[this.#pos]; } clear(): void { this.#pos = 0; } get length(): number { return this.#pos; } #expand(): void { if(this.length >= this.#bucketSize * this.#bucketCount) this.#bucketCount++; this.#arr.length = this.#bucketSize * this.#bucketCount; } } export class Queue { #arr: T[]; #idx: number; //Index of the first non empty value #pos: number; //Index of the last non empty value #bucketSize: number; #bucketCount: number = 1; constructor(size?: number) { this.#bucketSize = size ?? DEFAULT_SIZE; this.#arr = new Array(this.#bucketSize * this.#bucketCount); this.#idx = 0; this.#pos = 0; } push(item: T): void { if(this.#pos >= this.#arr.length) this.#expand(); this.#arr[this.#pos] = item; this.#pos++; } pull(): T { if(this.length === 0) throw new Error("Empty queue."); const item = this.#arr[this.#idx]; delete this.#arr[this.#idx]; this.#idx++; return item; } peek(): T { return this.#arr[this.#idx]; } clear(): void { this.#idx = 0; this.#pos = 0; } get length(): number { return this.#pos - this.#idx; } #shrink(): void { this.#arr.splice(0, this.#idx); this.#arr.length = this.#bucketSize * this.#bucketCount; this.#idx = 0; } #expand(): void { if(this.#idx !== 0) this.#shrink(); if(this.length >= this.#bucketSize * this.#bucketCount) this.#bucketCount++; this.#arr.length = this.#bucketSize * this.#bucketCount; } } export class LinkedList { #head: LinkedElmt | null; constructor() { this.#head = null; } get empty(): boolean { return this.#head === null; } add(item: T): void { const head = this.#head; this.#head = { elmt: item, next: head }; } pop(): T | null { if(this.#head === null) return null; const head = this.#head; this.#head = head.next; return head.elmt; } peek(): T | null { return this.#head?.elmt ?? null; } forEach(cb: (e: T, i: number) => void): void { let current = this.#head, i = 0; while(current) { cb(current.elmt, i++); current = current.next; } } toArray(): T[] { const result: T[] = []; this.forEach(e => result.push(e)); return result; } clear() { this.#head = null; } } interface LinkedElmt { elmt: T; next: LinkedElmt | null; } export function clamp(x: number, min: number, max: number): number { return x > max ? max : x < min ? min : x; } export function lerp(x: number, a: number, b: number): number { return (1-x)*a+x*b; } export class Random { #seed: number; #hasher: () => number; constructor(seed: number) { this.#seed = seed; this.#hasher = mb32(hash(seed)); } next(): number { return this.#hasher(); } nextInt(min?: number, max?: number): number { return Math.floor(this.nextFloat(min, max)); } nextFloat(min?: number, max?: number): number { if(min === undefined && max === undefined) { min = 0; max = 1; } else if(max === undefined) { max = min; min = 0; } else if(min === undefined) { min = 0; } return lerp(this.#hasher() / 2**32, min!, max!); } } const hash = (n: number) => Math.imul(n,2654435761) >>> 0; const mb32 = (a: number) => (t?: number) => (a = a + 1831565813|0, t = Math.imul(a^a>>>15,1|a), t = t + Math.imul(t^t>>>7,61|t)^t, (t^t>>>14)>>>0);