From 0d2d8cd897e94bd94e0b3ae7c6ce9aa580a44a0a Mon Sep 17 00:00:00 2001 From: LLLL Colonq Date: Wed, 26 Aug 2026 23:47:59 -0400 Subject: Add a_tension_span --- 2026/games/liquidcake1/LICENSE | 21 -- 2026/games/liquidcake1/funge.mjs | 42 --- 2026/games/liquidcake1/index.html | 415 ----------------------------- 2026/games/liquidcake1/instructions.mjs | 265 ------------------- 2026/games/liquidcake1/interpreter.mjs | 449 -------------------------------- 2026/games/liquidcake1/jit.mjs | 259 ------------------ 2026/games/liquidcake1/queue.mjs | 30 --- 2026/games/liquidcake1/wasm.mjs | 51 ---- 8 files changed, 1532 deletions(-) delete mode 100644 2026/games/liquidcake1/LICENSE delete mode 100644 2026/games/liquidcake1/funge.mjs delete mode 100644 2026/games/liquidcake1/index.html delete mode 100644 2026/games/liquidcake1/instructions.mjs delete mode 100644 2026/games/liquidcake1/interpreter.mjs delete mode 100644 2026/games/liquidcake1/jit.mjs delete mode 100644 2026/games/liquidcake1/queue.mjs delete mode 100644 2026/games/liquidcake1/wasm.mjs (limited to '2026/games/liquidcake1') diff --git a/2026/games/liquidcake1/LICENSE b/2026/games/liquidcake1/LICENSE deleted file mode 100644 index 847e49b..0000000 --- a/2026/games/liquidcake1/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 liquidcake1 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/2026/games/liquidcake1/funge.mjs b/2026/games/liquidcake1/funge.mjs deleted file mode 100644 index a6fb723..0000000 --- a/2026/games/liquidcake1/funge.mjs +++ /dev/null @@ -1,42 +0,0 @@ -let file_name = process.argv[2]; - -import { readFileSync } from "fs"; - -let field_s = readFileSync(file_name, "utf8"); -//console.log(field_s); - -let field = field_s.split("\n"); -field.pop(); -field = field.map( x => x.split("").map( c => c.charCodeAt(0) ) ); -//console.log(field); - -import { Interpreter } from "./interpreter.mjs"; -import { load_wasm } from "./wasm.mjs"; -import { overlay as SOCK_overlay } from "./overlays/SOCK.mjs"; -import { overlay as S_overlay } from "./overlays/S.mjs"; -import { overlay as WS_overlay } from "./overlays/WS.mjs"; - -let interpreter = new Interpreter(); - -interpreter.load_overlay("S", S_overlay); -interpreter.load_overlay("SOCK", SOCK_overlay); -interpreter.load_overlay("WS", WS_overlay); - -let instance = await load_wasm(interpreter); - -interpreter.field = field; -interpreter.input_queue.push(["set_speed", 100]); -//interpreter.input_queue.push(["set_speed", 90]); -interpreter.input_queue.push(["pause"]); -interpreter.input_queue.push(["unpause"]); -interpreter.add_handler("char_out", function(arg) { process.stdout.write(arg); }); -//interpreter.add_handler("output_field", function(lines) { lines.forEach(function(line) {process.stdout.write(line + "\n"); })}); -interpreter.add_handler("terminate", function() { process.exit(0); }); -interpreter.go(); -process.stdin.on('data', function (chunk) { - chunk.forEach(x => interpreter.input_queue.push(["stdin", x])); -}); -/*setTimeout(function () { - console.log("sending @"); - interpreter.input_queue.push(["stdin", "@".charCodeAt(0)]); -}, 2000);*/ diff --git a/2026/games/liquidcake1/index.html b/2026/games/liquidcake1/index.html deleted file mode 100644 index 8bff36c..0000000 --- a/2026/games/liquidcake1/index.html +++ /dev/null @@ -1,415 +0,0 @@ - - - - - - - Befunge - - -
Code!
-
Placing:
Which does:
-
Speed
-
-
- -
-

Output:

-

Previous:

-

Target:

Hello, world!
-
x
-
Don't get exploded: 💣......💥
- - diff --git a/2026/games/liquidcake1/instructions.mjs b/2026/games/liquidcake1/instructions.mjs deleted file mode 100644 index 58c6aab..0000000 --- a/2026/games/liquidcake1/instructions.mjs +++ /dev/null @@ -1,265 +0,0 @@ -export let instructions_raw = { - "+": { - impl: function (thread) { thread.stack.push(thread.pop() + thread.pop()); }, - desc: "b a → b + a", - can_jit: true, - stack_min: 2, - stack_return: 1, - unchecked_js_code: "stack[stack.length - 2] += stack.pop();", - }, - "-": { - impl: function (thread) { thread.stack.push(-thread.pop() + thread.pop()); }, - desc: "b a → b - a", - can_jit: true, - stack_min: 2, - stack_return: 1, - unchecked_js_code: "stack[stack.length - 2] -= stack.pop();", - }, - "*": { - impl: function (thread) { thread.stack.push(thread.pop() * thread.pop()); }, - desc: "b a → b * a", - can_jit: true, - stack_min: 2, - stack_return: 1, - unchecked_js_code: "stack[stack.length - 2] *= stack.pop();", - }, - "/": { - impl: function (thread) { let a = thread.pop(); let b = thread.pop(); thread.stack.push(Math.round(b / a)); }, - desc: "b a → b // a", - can_jit: true, - stack_min: 2, - stack_return: 1, - unchecked_js_code: "stack[stack.length - 2] /= stack.pop();", - }, - "%": { - impl: function (thread) { let a = thread.pop(); let b = thread.pop(); thread.stack.push(b % a); }, - desc: "b a → b % a", - can_jit: true, - stack_min: 2, - stack_return: 1, - unchecked_js_code: "stack[stack.length - 2] %= stack.pop();", - }, - "!": { - impl: function (thread) { thread.stack.push(thread.pop() == 0 ? 1 : 0); }, - desc: "a → a == 0 ? 1 : 0", - can_jit: true, - stack_min: 1, - stack_return: 1, - unchecked_js_code: "stack[stack.length - 1] = stack[stack.length - 1] ? 1 : 0;", - }, - "`": { - impl: function (thread) { thread.stack.push(thread.pop() < thread.pop() ? 1 : 0); }, - desc: "b a → b > a ? 1 : 0", - can_jit: true, - stack_min: 2, - stack_return: 1, - unchecked_js_code: "stack[stack.length - 2] = stack.pop() < stack[stack.length - 1] ? 0 : 1;", - }, - "<": { - impl: function (thread) { thread.cold = -1; thread.rowd = 0; }, - desc: "() → (); go left", - stack_min: 0, - stack_return: 0, - unchecked_js_code: "", - can_jit: true, - }, - ">": { - impl: function (thread) { thread.cold = 1; thread.rowd = 0; }, - desc: "() → (); go right", - stack_min: 0, - stack_return: 0, - unchecked_js_code: "", - can_jit: true, - }, - "^": { - impl: function (thread) { thread.cold = 0; thread.rowd = -1; }, - desc: "() → (); go up", - stack_min: 0, - stack_return: 0, - unchecked_js_code: "", - can_jit: true, - }, - "v": { - impl: function (thread) { thread.cold = 0; thread.rowd = 1; }, - desc: "() → (); go down", - stack_min: 0, - stack_return: 0, - unchecked_js_code: "", - can_jit: true, - }, - "?": { - impl: function (thread) { let x = Math.floor(Math.random() * 4); thread.cold = ((x % 2) * 2 - 1) * Math.floor(x / 2); thread.rowd = ((x % 2) * 2 - 1) * ( 1 - Math.floor(x / 2)); }, - desc: "() → (); go random cardinal", - can_jit: false, - }, - "_": { - impl: function (thread) { thread.rowd = 0; thread.cold = thread.pop() == 0 ? 1 : -1; }, - desc: "a → (); a == 0 ? go right : go left", - can_jit: false, - }, - "|": { - impl: function (thread) { thread.cold = 0; thread.rowd = thread.pop() == 0 ? 1 : -1; }, - desc: "a → (); a == 0 ? go down : go up", - can_jit: false, - }, - '"': { - impl: function (thread) { thread.mode = "string"; }, - desc: "; toggle string mode", - can_jit: false, // TODO this should be OK, but we'll need JIT for string mode. - stack_min: 0, - stack_return: 0, - }, - ":": { - impl: function (thread) { thread.stack.push(thread.stack[thread.stack.length-1]); }, - desc: "a → a a", - can_jit: true, - stack_min: 1, - stack_return: 2, - unchecked_js_code: "stack.push(stack[stack.length-1]);", - }, - "\\": { - impl: function (thread) { let a = thread.pop(); var b = thread.pop(); thread.stack.push(a, b); }, - desc: "b a → a b", - can_jit: true, - stack_min: 2, - stack_return: 2, - unchecked_js_code: "stack.push(stack.pop(), stack.pop());", - }, - "$": { - impl: function (thread) { thread.pop(); }, - desc: "a → ()", - can_jit: true, - stack_min: 1, - stack_return: 0, - unchecked_js_code: "stack.pop();", - }, - ".": { - impl: function (thread) { thread.interpreter.out(thread.pop() + " "); }, - desc: "a → (); output a", - can_jit: false, - }, - ",": { - impl: function (thread) { thread.interpreter.out(String.fromCharCode(thread.pop())); }, - desc: "a → (); output chr(a)", - can_jit: false, - }, - "#": { - impl: function (thread) { thread.col += thread.cold; thread.row += thread.rowd; }, - desc: "; jump one cell", - can_jit: true, - stack_min: 0, - stack_return: 0, - unchecked_js_code: "", - }, - "g": { - impl: function (thread) { let row = thread.pop(); var col = thread.pop(); thread.stack.push(thread.interpreter.get_field(thread, col, row)); }, - desc: "col row → field[row][col]", - can_jit: true, - stack_min: 2, - stack_return: 1, - unchecked_js_code: "{let row = stack.pop(); stack[stack.length-1] = (thread.interpreter.get_field(thread, stack[stack.length-1], row));}", - }, - "p": { - impl: function (thread) { let row = thread.pop(); var col = thread.pop(); var val = thread.pop(); thread.interpreter.set_field(thread, col, row, val); }, - desc: "val col row → (); field[row][col] = val", - can_jit: true, - stack_min: 3, - stack_return: 0, - unchecked_js_code: function (thread_state) { - return ` - { - let row = stack.pop(); - let col = stack.pop(); - thread.interpreter.set_field(thread, col, row, stack.pop()); - for (let i=0; i 0) { - console.log("Have data!"); - thread.stack.push(thread.interpreter.stdin_queue.pop()); - } else { - console.log("Have no data!"); - return new Promise(r => thread.interpreter.stdin_waiters.push(r)).then(function (char_code) {thread.stack.push(char_code); thread.blocked = false;}); - } - }, - desc: "() → x; read a character from input", - can_jit: false, - }, - "@": { - impl: function (thread) { thread.interpreter.output_error(thread, "Normal termination!"); thread.running = false; }, - desc: "; stop current thread", - can_jit: false, - }, - "]": { - impl: function (thread) { thread.interpreter.output_error(thread, "Debug!"); }, - desc: "; dump thread debug", - can_jit: false, - }, - " ": { - impl: function (thread) { }, - desc: "; no-op", - can_jit: true, - stack_min: 0, - stack_return: 0, - unchecked_js_code: "", - }, - "(": { - impl: function (thread) { - const n = thread.pop(); - let x = 0; - for(let i=0; i x.charCodeAt(0))) { - x = x * 256 + c; - } - return x; -} - -function deep_copy(obj) { - return JSON.parse(JSON.stringify(obj)); -} - -function shallow_copy(obj) { - return Object.fromEntries(Object.entries(obj)); -} - -function shallowish_copy(obj, i) { - if (i > 0) { - return shallowish_copy(obj, i - 1); - } else { - return shallow_copy(obj); - } -} - -class Thread { - running = true; - mode = "normal"; - jit = null; - tick_count = 0; - - constructor(interpreter, col, row, cold, rowd, stack, overlays) { - this.interpreter = interpreter; - this.col = col; - this.row = row; - this.cold = cold; - this.rowd = rowd; - this.stack = stack; - this.overlays = overlays || {}; - } - - split_thread() { - let thread = new Thread( - this.interpreter, - this.col, - this.row, - -this.cold, - -this.rowd, - Array.from(this.stack), - shallow_copy(this.overlays), - ); - thread.tick_forwards(); - thread.tick_count = this.tick_count; // Ensure new thread has equal priority to old thread. - return thread; - } - - tick_forwards() { - this.col += this.cold; - this.row += this.rowd; - if (this.row == this.interpreter.field.length && this.rowd > 0) this.row = 0; - else if (this.row == -1 && this.rowd < 0) this.row = this.interpreter.field.length - 1; - if (this.col == this.interpreter.field[this.row].length && this.cold > 0) this.col = 0; - else if (this.col == -1 && this.cold < 0) this.col = this.interpreter.field[this.row].length - 1; - } - - pop() { - if (this.stack.length > 0) { - return this.stack.pop(); - } else { - this.interpreter.error(this, "Stack underflow"); - return 0; - } - } -} - -export class Interpreter { - max_loops = 1000000000000; - ticks = 0; - paused_wake = null; - paused_event = null; - slice_loops = 1; - slice_sleep = 20; - running = false; - threads = []; - events = {}; - awake_sleep = null; - stats = []; - field = []; - instructions = shallow_copy(instructions); - instructions_raw = shallow_copy(instructions_raw); - overlays = {}; - input_queue = new Queue(); // Queue things like: change speed, input, etc. - stdin_queue = new Queue(); // Chars from stdin. - stdin_waiters = []; - handlers = {}; - - constructor() { - this.jit = new Jit(this); - } - - load_overlay(fingerprint, overlay) { - this.overlays[gen_fingerprint(fingerprint)] = overlay; - } - - add_handler(event_name, f) { - if (this.handlers[event_name] === undefined) { - this.handlers[event_name] = []; - } - this.handlers[event_name].push(f); - } - - trigger_event(event_name, ...args) { - //console.log(`Event: ${event_name} -- ${args}`); - let handlers = this.handlers[event_name]; - if (handlers === undefined) { - //console.log(`Unhandled event: ${event_name} -- ${args}`); - } else { - handlers.forEach(f => f(...args)); - } - } - - set_speed(raw_speed) { - if (raw_speed > 0) { - this.slice_sleep = 1; - this.slice_loops = Math.floor(1.1 ** raw_speed); - } else { - this.slice_sleep = Math.floor(1.1 ** -raw_speed); - this.slice_loops = 1; - } - this.trigger_event("speed_changed", raw_speed, this.slice_sleep, this.slice_loops); - } - - toggle_pause(e) { - if (this.paused_wake === null) { - this.pause(); - } else { - this.unpause(); - } - } - - pause() { - let thisthis = this; - this.paused_event = new Promise(function (r) { - thisthis.paused_wake = r; - // We shouldn't be in the sleep loop if paused. - if (thisthis.awake_sleep != null) thisthis.awake_sleep(); - }); - // These really need outputting from the main loop, not here! - this.trigger_event("paused", true); - this.threads.forEach(function (thread) { - thisthis.trigger_event("thread_paused", thread, true); - }); - } - - unpause() { - let old_wake = this.paused_wake; - let thisthis = this; - this.paused_wake = null; - this.trigger_event("paused", false); - this.threads.forEach(function (thread) { - thisthis.trigger_event("thread_paused", thread, false) - }); - if (old_wake) - old_wake(); - } - - async sleep_until_unpaused(all_blocked) { // TODO??? - if (!this.paused_event) - await this.sleep(all_blocked ? 50 : this.slice_sleep); - if (this.paused_event) { - let paused_event = this.paused_event; - this.paused_event = null; - let unpause_data = await paused_event; - return unpause_data; - } - } - - step(e) { - let old_wake = this.paused_wake; - this.pause(); - if (old_wake !== null) { - old_wake({count: 1}); - } - } - - step_thread(thread) { - let old_wake = this.paused_wake; - this.pause(); - if (old_wake !== null) { - old_wake({threads: [thread], count: 1}); - } - } - - async go() { - //console.log(this.threads); - this.stop(); - if (this.running_promise != null) { - console.log("Waiting for last thread to exit"); - await this.running_promise; - console.log("Last thread has exited"); - } - let thread = new Thread(this, 0, 0, 1, 0, []); - this.new_thread(thread); - this.trigger_event("started"); - this.main_loop(); - } - - new_thread(thread) { - this.threads.push(thread); - this.trigger_event("thread_created", thread); - } - - stop() { - if (this.threads.length > 0) { - this.trigger_event("stopped"); - } - this.threads.forEach(function (thread) { thread.running = false; }); - this.unpause(); - if (this.awake_sleep != null) this.awake_sleep(); - } - - check(state, col, row) { - if (row < 0 || row >= this.field.length) this.error(state, `Row ${row} is out of bounds`); - //else if (col < 0 || col >= this.field[row].length) this.error(state, `Col ${col} is out of bounds`); - else return true; - } - - set_field(state, col, row, val) { - if (this.check(state, col, row)) { - this.set_cell(col, row, val); - } else { - console.log(`out of bounds access ${state} ${col} ${row} ${val}`); - } - } - - set_cell(col, row, val) { - let val_str = String.fromCharCode(val); - this.field[row][col] = val; - let title = `(${col},${row})=${val} (${val_str})`; - if (this.instructions_raw[val_str] !== undefined) - title += ": " + this.instructions_raw[val_str].desc; - this.jit.cell_changed(row, col); - this.trigger_event("cell_changed", col, row, val); - } - - get_field(state, col, row, val) { - if (this.check(state, col, row)) { - let val = this.field[row][col]; - return val === undefined ? 0 : val; - } - } - - out(s) { - this.trigger_event("char_out", s); - //console.log("Out: " + s); - } - oute(s) { - this.trigger_event("error_occurred", s); - //console.log("Err: " + s); - } - - error(thread, message) { - thread.running = false; - this.output_error(thread, message); - } - - output_error(thread, message) { - this.oute(`ERROR: ${message}`); - this.oute(`State was: row=${thread.row} col=${thread.col} stack=${thread.stack}`); - this.output_field(); - } - - output_field() { - let lines = []; - for(let row=0; row 31 && c < 127 ? String.fromCharCode(this.field[row][col]) : "?"); - } - lines.push(s); - } - this.trigger_event("output_field", lines); - } - - async sleep(ms) { - await new Promise(r => { this.awake_sleep = r; setTimeout(r, ms) }); - this.awake_sleep = null; - } - - process_events() { - while (this.input_queue.length > 0) { - let item = this.input_queue.pop(); - if (item[0] == "set_speed") { - this.set_speed(item[1]); - } else if (item[0] == "pause") { - this.pause(); - } else if (item[0] == "unpause") { - this.unpause(); - } else if (item[0] == "stdin") { - if (this.stdin_waiters.length > 0) { - this.stdin_waiters.shift()(item[1]); - } else { - this.stdin_queue.push(item[1]); - } - } - } - } - - async main_loop() { - let ticks = 0; - this.running = true; - let running_wake = null; - this.running_promise = new Promise(r => { running_wake = r; }); - let highlighted_cells = []; - let start_time = new Date().getTime(); - let all_blocked = false; - while(this.threads.length > 0) { - this.process_events(); - let slice_loops = this.slice_loops; - let limit_threads = null; - let need_sleep = true; - - this.threads.forEach(thread => this.trigger_event("thread_state_updated", thread)); - this.trigger_event("thread_state_synced", this.threads); - - if (ticks > this.max_loops) { - this.ticks += ticks; - ticks = 0; - let end_time = new Date().getTime(); - this.oute(`Ran out of ticks! (${ticks} > ${this.max_loops})`); - console.log(`${ticks} in ${end_time - start_time} is ${ticks / (end_time - start_time) / 1000} MHz`); - this.pause(); - this.output_field(); - } - - let unpause_data = await this.sleep_until_unpaused(all_blocked); - if (unpause_data) { - console.log(`Unpaused; resetting ticks to 0 from ${ticks}`); - start_time = new Date().getTime(); - if (unpause_data.count !== undefined) { - need_sleep = false; - slice_loops = unpause_data.count; - } - if (unpause_data.threads !== undefined) - limit_threads = unpause_data.threads; - } - - let count = 0; - let dead_threads = []; - while(count < slice_loops && this.threads.length > 0) { - // This will _not_ iterate over newly-added threads. - all_blocked = true; - let to_iter = limit_threads !== null ? limit_threads : this.threads; - to_iter = to_iter.filter(thread => !thread.blocked); - if (to_iter.length == 0) { - break; - } - to_iter.forEach(function (thread) { if (!thread.waiter) { all_blocked = false; } }); - let min_count = Math.min(...to_iter.map(x => x.tick_count)); - to_iter.forEach((thread, i) => { - if (thread.tick_count == min_count) { - let tick_count = this.tick(thread, slice_loops - count, i); - count += tick_count; - thread.tick_count += tick_count; - } - }); - let new_dead_threads = this.threads.filter(t => !t.running); - if (new_dead_threads) { - dead_threads.push(...new_dead_threads); - this.threads = this.threads.filter(t => t.running); - } - } - - dead_threads.forEach( - dead_thread => this.trigger_event("thread_dead", dead_thread)); - - ticks += count; - } - running_wake(); - //console.log(`exited main_loop with ticks ${ticks}`); - this.ticks += ticks; - let end_time = new Date().getTime(); - //console.log(`${ticks} in ${end_time - start_time} is ${ticks / (end_time - start_time) / 1000} MHz`); - this.output_field(); - this.trigger_event("terminate"); - } - - tick(thread, target_ticks, thread_num) { - if (thread.waiter) { - try { - if (thread.waiter(thread)) { - thread.waiter = null; - } - } catch (err) { - this.error(thread, "Exception caught during waiter: " + err.message); - //console.log(err.stack); - } - return 1; - } - if (thread.mode == "normal") { - let symbol = this.field[thread.row][thread.col] || 32; - let instruction = thread.overlays[symbol] || this.instructions[symbol]; - //console.log(`Executing ${String.fromCharCode(symbol)} at ${thread.col},${thread.row} stack_length=${thread.stack.length} stack_tail=${thread.stack.slice(-10)}`); - //console.log("Executing " + String.fromCharCode(symbol)); - let jit_return = this.jit.step_jit(thread, target_ticks); - if (jit_return > 0) { - return jit_return; - } - if (instruction == null) { - this.error(thread, "Invalid instruction: " + symbol); - } else { - try { - let ret = instruction(thread, thread_num); - if (ret && ret.constructor === Promise) { - thread.blocked = true; - } - } catch (err) { - this.error(thread, "Exception caught during " + symbol + ": " + err.message); - //console.log(err.stack); - } - } - } else if (thread.mode == "string") { - let symbol = this.field[thread.row][thread.col]; - if (symbol == '"'.charCodeAt(0)) { - thread.mode = "normal"; - } else { - thread.stack.push(symbol); - } - } - thread.tick_count += 1; - thread.tick_forwards(); - return 1; - } -} diff --git a/2026/games/liquidcake1/jit.mjs b/2026/games/liquidcake1/jit.mjs deleted file mode 100644 index da0d2fb..0000000 --- a/2026/games/liquidcake1/jit.mjs +++ /dev/null @@ -1,259 +0,0 @@ -// JIT TODO: -// * OK, it's probably about 6x faster with JIT than without, but we can go another 4-5x faster by loop unrolling. -// * We don't bounds-check the stack before executing a JITted routine. -// * We can probably do a lot better in speed by unrolling and aliasing the stack. Name top N stack vars a1 to aN, -// then unpack the top N into those, then run code, then pack aN to a?. -// * We don't have support for branches, even just the dumb "primary only, bail otherwise" variety. -// * We don't ever remove JIT. We should destroy JIT when a cell is modified. -// * If a cell is modified _DURING_ a JITted sequence, what do? If external, we can just claim it doesn't matter. -// If the thread self-modified, we'll need a means to abort the JITted sequence early. -// * There is no JIT visualisation. - -function diagonalise_positive(row, col) { - /* - 0 1 3 6 - 2 4 7 - 5 8 - 9 - */ - let shell = row + col; - let prev_shell_indices = shell * (shell + 1) / 2; - return prev_shell_indices + row; -} - -function dediagonalise_positive(diagonal_index) { - // n (n + 1) / 2 = x; - let shell = Math.floor((Math.sqrt(8 * diagonal_index + 1) - 1) / 2); - let row_0_index = diagonalise_positive(0, shell); - let row = diagonal_index - row_0_index; - let col = shell - row; - return [row, col]; -} - -function diagonalise_any(row, col) { - /* - 35 21 11 23 39 - 20 10 4 12 24 - 9 3 0 1 5 - 18 8 2 6 14 - 31 17 7 15 27 - */ - let shell = Math.abs(row) + Math.abs(col); - if (shell == 0) return 0; - let prev_shell_indices = 2 * shell * (shell - 1); - if (row >= 0) { - return prev_shell_indices + shell + 1 - col; - } else { - return prev_shell_indices + 3 * shell + 1 + col; - } -} - -function dediagonalise_any(diagonal_index) { - if (diagonal_index == 0) { - return [0, 0]; - } - // cumulative past shell size is (shell-1)^2 + shell^2 - // so 2s^2 - 2s + 1 = n - // (2 + (4-8(1-n))^0.5)/4 = ((2n-1)^0.5+1)/2 - let shell = Math.floor((Math.sqrt(2*diagonal_index-1)+1)/2); - let row_0_pos_index = diagonalise_any(0, shell); - let col = shell - diagonal_index + row_0_pos_index; - if (col < -shell) { - col = -2 * shell - col; - } - let row = diagonal_index - row_0_pos_index; - if (row > shell) { - row = 2 * shell - row; - } - if (row < -shell) { - row = -2 * shell - row; - } - return [row, col]; -} - -function diagonalise_with_dir(row, col, rowd, cold) { - return 4 * diagonalise_positive(row, col) + diagonalise_any(rowd, cold); -} - -function dediagonalise_with_dir(diagonal_index) { - let dir_diag = diagonal_index % 4; - let pos_diag = diagonal_index / 4; - return dediagonalise_any(pos_diag).concat(dediagonalise_positive(dir_diag)); -} - -class CellStats { - hit_count = 1; - jit_starts = []; - jit = null; -} - -class JitPath { - path = []; - code = ""; - stack_req = 0; - stack_delta = 0; - instruction_count = 0; - constructor() { - } -} - -class JitFragment { - end_row = null; - end_col = null; - end_rowd = null; - end_cold = null; - instruction_count = null; - path = null; - stack_req = null; - code = null; - jit_starts = []; // Should just be the initial cell, but kept for consistency - - constructor(thread, instruction_count, path, stack_req, code) { - // TODO: let [end_row, end_col, end_rowd, end_cold] = dediagonalise_with_dir(resting_place); - this.end_row = thread.row; - this.end_col = thread.col; - this.end_rowd = thread.rowd; - this.end_cold = thread.cold; - this.instruction_count = instruction_count; - this.path = path; - this.stack_req = stack_req; - this.code = code; - } - - maybe_run_in_thread(thread, target_ticks) { - // TODO include this fragment in the JIT code itself. - // TODO should store and check thread fingerprints! - if (this.instruction_count > target_ticks || thread.stack.length < this.stack_req) { - // Checking requirements not met. Just interpret. - //console.log(`Not running JIT. ${this.instruction_count} > ${target_ticks} || ${thread.stack.length} < ${this.stack_req}`); - return 0; - } - try { - this.call(thread); - } catch (err) { - thread.interpreter.error(thread, `Exception caught in JIT: ${err.message}`); - // TODO: Stop thread?! - } - thread.tick_count += this.instruction_count; - thread.row = this.end_row; - thread.col = this.end_col; - thread.rowd = this.end_rowd; - thread.cold = this.end_cold; - return this.instruction_count; - } -} - -export class Jit { - cell_stats = {}; - path = null; - threshold = 10000000000; - interpreter = null; - - constructor(interpreter) { - this.interpreter = interpreter; - } - - step_jit(thread, target_ticks) { - let diagonal_index = diagonalise_with_dir(thread.row, thread.col, thread.rowd, thread.cold); - let jit_path = thread.jit_path; - if (jit_path === undefined) { - let cell_stats = this.cell_stats[diagonal_index]; - // Run jitted code, if present, else increment count and possibly start a JIT follow. - if (!cell_stats) { - this.cell_stats[diagonal_index] = new CellStats(); - return 0; - } else if (cell_stats.jit) { - return cell_stats.jit.maybe_run_in_thread(thread, target_ticks); - } else { - cell_stats.hit_count += 1; - if (cell_stats.hit_count < this.threshold) { - return 0; - } - //console.log(`loop found at ${diagonal_index}?`); - thread.jit_path = jit_path = new JitPath(); - // Fall through to below now. - } - } - //console.log(`${jit_path}`); - if (jit_path.path.length > 0 && diagonal_index == jit_path.path[0]) { - // We completed a path. - let jit = this.complete_jit(thread); - // Run it, else we'll run the current instruction and then begin a new - // loop at the next cell, which will prevent evaluation of this one! - return jit.maybe_run_in_thread(thread, target_ticks); - } - // We're following a path. - // TODO this should not need to do string futzing. - let symbol = this.interpreter.field[thread.row][thread.col]; - if (thread.overlays[symbol] !== undefined) { - // TODO - //console.log("Stopping JIT as we're entering an instruction from an overlay."); - this.complete_jit(thread); - return 0; - } - let raw_instruction = this.interpreter.instructions_raw[String.fromCharCode(symbol)]; - if (raw_instruction === undefined) { - //console.log("Stopping JIT as we're entering an unknown instruction."); - this.complete_jit(thread); - return 0; - } - if (!raw_instruction.can_jit) { - //console.log("Stopping JIT as we're entering an instruction I don't understand."); - //console.log(jit_path); - this.complete_jit(thread); - return 0; - } - jit_path.code += `// ${String.fromCharCode(symbol)} at row ${thread.row}, col ${thread.col}, heading ${thread.rowd}, ${thread.cold}\n`; - let real_code = raw_instruction.unchecked_js_code; - if (typeof real_code == 'function') { - real_code = real_code(thread); - } - jit_path.code += `${real_code}\n`; - jit_path.stack_req = Math.max(jit_path.stack_req, -jit_path.stack_delta - raw_instruction.stack_min); - jit_path.stack_delta += raw_instruction.stack_return - raw_instruction.stack_min; - jit_path.path.push(diagonal_index); - jit_path.instruction_count += 1; - //console.log(jit_path); - return 0; - } - - complete_jit(thread) { - let jit_path = thread.jit_path; - thread.jit_path = undefined; - if (jit_path.path.length == 0) { - //console.log("JIT path is empty."); - return; - } - console.log("Performing JIT compile, length " + jit_path.path.length); - //console.log(jit_path); - // TODO: jit_path.compile() - let code = `jit.call=function (thread) {\nlet stack = thread.stack;\n${jit_path.code}\n}`; - //console.log(code); - let jit = new JitFragment(thread, jit_path.instruction_count, jit_path.path, jit_path.stack_req, code); - //console.log(jit); - eval(code); - //console.log(jit); - let starting_place = jit_path.path[0]; - this.cell_stats[jit_path.path[0]].jit = jit; - for(let i=0; i this.max_len) { - content.push([]); - } - content[content.length - 1].push(x); - this.length += 1; - } - pop() { - this.index += 1; - if (this.index >= this.content[0].length) { - if (this.content.length == 1) { - throw("empty"); - } - this.content.shift(); - this.index = 0; - } - this.length -= 1; - return this.content[0][this.index]; - } -} diff --git a/2026/games/liquidcake1/wasm.mjs b/2026/games/liquidcake1/wasm.mjs deleted file mode 100644 index 18bf547..0000000 --- a/2026/games/liquidcake1/wasm.mjs +++ /dev/null @@ -1,51 +0,0 @@ -export function load_wasm(interpreter) { - /* - (memory $memory 1) - (export "memory" (memory $memory)) - (func (export "load_first_item_in_mem") (param) (result i32) - i32.const 0 - - ;; load first item in memory and return the result - i32.load - ;; store 10000 in the first location in memory - i32.const 0 - i32.const 10000 - i32.store - ) - */ - /* - (module -(func $pop (import "my_namespace" "pop") (param i32) (result i32)) -(func $push (import "my_namespace" "push") (param i32 i32)) -(func (export "plus") (param i32) - local.get 0 - local.get 0 - call $pop - local.get 0 - call $pop - i32.add - call $push -)) -*/ - //let wasm_string = "AGFzbQEAAAABBwFgAn9/AX8DAgEABwoBBmFkZFR3bwAACgkBBwAgACABagsACgRuYW1lAgMBAAA="; - let wasm_string = "AGFzbQEAAAABDwNgAX8Bf2ACf38AYAF/AAIoAgxteV9uYW1lc3BhY2UDcG9wAAAMbXlfbmFtZXNwYWNlBHB1c2gAAQMCAQIHCAEEcGx1cwACChEBDwAgACAAEAAgABAAahABCwAcBG5hbWUBDAIAA3BvcAEEcHVzaAIHAwAAAQACAA=="; - // Fake minus version - //let wasm_string = "AGFzbQEAAAABDwNgAX8Bf2ACf38AYAF/AAIoAgxteV9uYW1lc3BhY2UDcG9wAAAMbXlfbmFtZXNwYWNlBHB1c2gAAQMCAQIHCAEEcGx1cwACChEBDwAgACAAEAAgABAAaxABCwAcBG5hbWUBDAIAA3BvcAEEcHVzaAIHAwAAAQACAA=="; - let wasm_array = new TextEncoder().encode(atob(wasm_string)) - const importObject = { - my_namespace: { - pop: threadNo => interpreter.threads[threadNo].pop(), - push: (threadNo, i) => interpreter.threads[threadNo].stack.push(i), - } - }; - return WebAssembly.compile(wasm_array).then((mod) => - WebAssembly.instantiate(mod, importObject).then(instance => { - patch_interpreter(interpreter, instance); - return instance; - }) - ); -} -function patch_interpreter(interpreter, instance) { - interpreter.instructions_raw["+"].impl = function (thread, thread_num) { instance.exports.plus(thread_num); }; - interpreter.instructions["+".charCodeAt(0)] = function (thread, thread_num) { instance.exports.plus(thread_num); }; -} -- cgit v1.3.1