diff options
Diffstat (limited to '2026/games/liquidcake1')
| -rw-r--r-- | 2026/games/liquidcake1/LICENSE | 21 | ||||
| -rw-r--r-- | 2026/games/liquidcake1/funge.mjs | 42 | ||||
| -rw-r--r-- | 2026/games/liquidcake1/index.html | 415 | ||||
| -rw-r--r-- | 2026/games/liquidcake1/instructions.mjs | 265 | ||||
| -rw-r--r-- | 2026/games/liquidcake1/interpreter.mjs | 449 | ||||
| -rw-r--r-- | 2026/games/liquidcake1/jit.mjs | 259 | ||||
| -rw-r--r-- | 2026/games/liquidcake1/queue.mjs | 30 | ||||
| -rw-r--r-- | 2026/games/liquidcake1/wasm.mjs | 51 |
8 files changed, 0 insertions, 1532 deletions
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 @@ -<!DOCTYPE html> -<html> - <head> - <meta charset="UTF-8"> - <script language="javascript" type="module"> - import { Interpreter } from "./interpreter.mjs"; - let interpreter = new Interpreter(); - interpreter.add_handler("char_out", out); - interpreter.add_handler("error_occurred", oute); - interpreter.add_handler("cell_changed", handle_set_cell); - interpreter.add_handler("output_field", console.log); - interpreter.add_handler("thread_created", setup_thread_gui); - interpreter.add_handler("thread_paused", handle_thread_paused); - interpreter.add_handler("thread_state_updated", update_thread_state); - interpreter.add_handler("thread_state_synced", handle_thread_state_synced); - interpreter.add_handler("terminate", handle_thread_state_synced); - interpreter.add_handler("thread_dead", handle_thread_dead); - interpreter.max_loops = 1000; - window.befunge_interpreter = interpreter; - var start_time; - var end_time; - var difficulty; - async function handle_message(ev) { - console.log("handle_message", ev); - console.log("handle_message", ev.data); - console.log("handle_message", ev.data.op); - if (ev.data.op == "start") { - winner = loser = false; - console.log("Picking level"); - await pick_level(); - set_speed(); - console.log("Picked level; explaining started."); - window.parent.postMessage({op: "started", verb: "catch!"}); - difficulty = ev.data.difficulty; - let timeout; - if (difficulty < 10) { - timeout = 150000 / (10 + difficulty); - } else { - timeout = 5000 + 150000 / difficulty; - } - console.log("Starting... with difficulty ", difficulty, "which is", timeout); - start_time = new Date().getTime(); - end_time = start_time + timeout; - check_time(); - go(); - let code = document.getElementById("code"); - code.style.animation = "none"; - window.setTimeout(function () { - code.style.animation = ""; - }, 10); - console.log("Started."); - } - } - function check_time() { - if (winner) { - console.log("Check time: winner"); - return; - } - let now = new Date().getTime(); - let fraction_done = (now - start_time) / (end_time - start_time); - let bomb = document.getElementById("bomb"); - if (fraction_done > 1) { - console.log("Check time: loser"); - loser = true; - bomb.innerText = "💥"; - interpreter.pause(); - return; - } - let ticks = 10 - Math.ceil(fraction_done * 10); - bomb.innerText = "💣" + ".".repeat(ticks) + "💥"; - window.setTimeout(check_time, 50); - - } - async function load() { - document.getElementById("speed").addEventListener("input", set_speed); - document.addEventListener("mousemove", function (e) { - let follower = document.getElementById("follower"); - follower.style.left = e.pageX + "px"; - follower.style.top = e.pageY + "px"; - }); - set_speed(); - window.addEventListener("message", handle_message); - window.parent.postMessage({op: "ready"}); - } - let levels = [ - '"!dlrow ,olleH",,,,,,,,,,,,,@', - '"H","e","l","l","o",","," "v\n @,"!","d","l","r","o","w",<', - '0>:2g:,"!"v\n ^_@#\\+1\\-<\nHello, world!', - ]; - let current_level; - let removed; - let placed_row = -1; - let placed_col = -1; - function test_level(content) { - let test_int = new Interpreter(); - let out = ""; - let fulfil; - let test_stopping = false; - test_int.add_handler("char_out", function (c) { out += c; }); - test_int.add_handler("thread_paused", function () { if (test_stopping) return; test_stopping = true; test_int.stop(); }); - test_int.add_handler("thread_dead", function (thread) { console.log("dead!"); fulfil([out, thread.tick_count]); }); - test_int.max_loops = 10000; - test_int.set_speed(100) - let lines = content.split("\n"); - let width = 0; - for(let i=0; i<lines.length; i++) { - if (lines[i].length > width) { - width = lines[i].length; - } - } - for(let i=0; i<lines.length; i++) { - test_int.field.push([]); - for(let j=0; j<width; j++) { - if (j < lines[i].length) { - test_int.field[i].push(lines[i].charCodeAt(j)) - } else { - test_int.field[i].push(32); - } - } - } - let p = new Promise(function(f) { fulfil = f; test_int.go(); }); - return p; - } - function ticks_to_speed(ticks) { - let speed = Math.log(ticks / 100) / Math.log(1.1); - if (ticks < 100) { - speed -= 20; - } - return speed; - } - async function pick_level() { - let level_idx = Math.floor(Math.random() * levels.length); - const base_level = levels[level_idx]; - let base_res = await test_level(base_level); - if (base_res[0] == "Hello, world!") { - console.log("Pre-edit validated!"); - } else { - console.log("Ooops!"); - } - let tries = 0; - while(tries < 100) { - tries += 1; - let idx = Math.floor(Math.random() * base_level.length); - removed = base_level[idx]; - if (removed == "\n") { - continue; - } - let replace; - if (difficulty < 10) { - replace = " "; - } else { - replace = String.fromCharCode(Math.floor(Math.random() * 94) + 33); - } - current_level = base_level.slice(0, idx) + replace + base_level.slice(idx + 1); - console.log(idx); - let current_res = await test_level(current_level); - if (current_res[0] == "Hello, world!") { - console.log("Ooops! Try again!"); - } else { - console.log("Post-edit validated!"); - document.getElementById("to_place").innerText = removed; - document.getElementById("follower_cell").innerText = removed; - let ticks = current_res[1]; - if (ticks > base_res[1] * 2) { ticks = base_res[1] * 2; } - document.getElementById("speed").value = ticks_to_speed(ticks); - if (interpreter.instructions_raw.hasOwnProperty(removed)) { - document.getElementById("place_description").innerText = interpreter.instructions_raw[removed].desc; - } else { - document.getElementById("place_description").innerText = "not an instruction"; - - } - console.log("level picked!"); - return; - } - } - } - function set_speed(e) { - let raw_speed = document.getElementById("speed").value; - interpreter.set_speed(raw_speed); - } - function setup_thread_gui(thread) { - const this_thread = thread; - let thb = document.createElement("th"); - let table = document.createElement("table"); - let tr = document.createElement("tr"); - let th = document.createElement("th"); - tr.appendChild(thb); - tr.appendChild(th); - table.appendChild(tr); - let thread_info = document.createElement("p"); - thread_info.appendChild(table); - document.getElementById("threads").appendChild(thread_info); - thread.info_elt = thread_info; - } - function update_thread_state(thread) { - let tr = thread.info_elt.children[0].children[0]; - tr.children[1].innerText = `(${("" + thread.col).padStart(3, "\u00a0")},${("" + thread.row).padStart(3, "\u00a0")})`; - while(tr.children.length > thread.stack.length + 2) { - tr.removeChild(tr.children[tr.children.length - 1]); - } - while(tr.children.length < thread.stack.length + 2) { - tr.appendChild(document.createElement("td")); - } - thread.stack.forEach(function (val, idx) { - if (val > 32 && val < 127) { - val = String.fromCharCode(val); - } - tr.children[thread.stack.length - idx + 1].innerText = val; - }); - } - let winner = false; - let loser = false; - let just_placed = false; - async function go() { - if (winner || loser) { - if (winner) { - console.log("We are winner!"); - } else { - console.log("We are loser!"); - } - window.parent.postMessage({op: "done", win: winner}); - document.getElementById("out").classList.remove("right"); - placed_col = placed_row = -1; - placed_td = undefined; - let table = document.getElementById("table"); - while(table.children.length > 0) { - table.removeChild(table.children[table.children.length-1]); - interpreter.field.pop(); - } - document.getElementById("out").innerText = ""; - return; - } - if (document.getElementById("out").innerText == "Hello, world!") { - // WIN! - winner = true; - document.getElementById("out").classList.add("right"); - placed_td.classList.add("right"); - placed_td.classList.remove("wrong"); - //console.log(dead_thread_ticks, ticks_to_speed(dead_thread_ticks / 10)); - interpreter.set_speed(ticks_to_speed(dead_thread_ticks / 5)); - } else if (just_placed) { - // Run once, super fast. We'll get back here immediately. - interpreter.set_speed(100); - just_placed = false; - load_level(current_level); - document.getElementById("out").innerText = ""; - interpreter.go(); - return; - } else { - set_speed(); - } - document.getElementById("outlast").innerHTML = document.getElementById("out").innerHTML; - document.getElementById("out").innerText = ""; - load_level(current_level); - interpreter.go(); - } - function set_cell(col, row, val) { - interpreter.set_cell(col, row, val); - } - function handle_set_cell(col, row, val) { - let val_str = String.fromCharCode(val); - document.getElementById("table").children[row].children[col].innerText = val_str == " " ? "\u00a0" : val_str; - let title = `(${col},${row})=${val} (${val_str})`; - if (interpreter.instructions_raw[val_str] !== undefined) - title += ": " + interpreter.instructions_raw[val_str].desc; - document.getElementById("table").children[row].children[col].title = title; - } - function out(s) { - document.getElementById("out").appendChild(document.createTextNode(s)); - } - function oute(s) { - if (s == "ERROR: Normal termination!" || s.startsWith("State was: ")) { - return; - } - let span = document.createElement("p"); - span.className = "error"; - span.innerText = s; - document.getElementById("out").appendChild(span); - } - let highlighted_cells = []; - function handle_thread_state_synced(threads) { - threads ||= []; - let new_highlighted_cells = threads.map(thread => document.getElementById("table").children[thread.row].children[thread.col]); - highlighted_cells.filter(x => !new_highlighted_cells.includes(x)).forEach( - cell => cell.classList.remove("active")); - highlighted_cells = new_highlighted_cells; - highlighted_cells.forEach(cell => cell.classList.add("active")); - } - let stopping; - function handle_thread_paused(paused_thread) { - if (stopping) { - // We'll unpause and then pause again... - return; - } - stopping = true; - interpreter.stop(); - } - let dead_thread_ticks; - function handle_thread_dead(dead_thread) { - dead_thread_ticks = dead_thread.tick_count; - stopping = false; - document.getElementById("threads").removeChild(dead_thread.info_elt); - go(); - } - let placed_td; - function click_cell(col, row, td) { - if (winner) { - return; - } - placed_col = col; - placed_row = row; - if (placed_td) { - placed_td.classList.remove("wrong"); - } - placed_td = td; - td.classList.add("wrong"); - set_cell(placed_col, placed_row, removed.charCodeAt(0)); - just_placed = true; - interpreter.pause(); - } - function load_level(content) { - const lines = content.split("\n"); - const height = lines.length; - let width = 0; - for(let i=0; i<height; i++) { - if (lines[i].length > width) { - width = lines[i].length; - } - } - let table = document.getElementById("table"); - while(table.children.length > height) { - table.removeChild(table.children[table.children.length-1]); - interpreter.field.pop(); - } - while(table.children.length < height) { - table.appendChild(document.createElement("tr")); - interpreter.field.push([]); - } - for(let i=0; i<height; i++) { - let line; - if (i >= lines.length) - line = ""; - else - line = lines[i]; - let row = table.children[i]; - while(row.children.length > width) { - row.removeChild(row.children[row.children.length-1]); - interpreter.field[i].pop(); - } - while(row.children.length < width) { - const td = document.createElement("td"); - const rown = i; - const coln = row.children.length; - td.addEventListener("click", function() { click_cell(coln, rown, td); }); - row.appendChild(td); - interpreter.field[i].push(32); - } - for(let j=0; j<width; j++) { - let val; - if (j >= line.length) - val = " "; - else - val = line[j]; - if (j == placed_col && i == placed_row) { - val = removed; - } - set_cell(j, i, val.charCodeAt(0)); - } - } - } - window.addEventListener("load", load); - </script> -<style> -body { font-size: 4vh; } -table { border: black solid 1px; padding: 0px; } -th { font-family: monospace; font-weight: normal; } -td { font-family: monospace; background: lightgrey; width: 1em; padding: 0px; text-align: center; } -.out1 > p { display: inline-block; margin-top: 0; margin-bottom: 0; min-height: 1em; } -.out1 > div { font-family: monospace; border: black solid 1px; display: inline-block; white-space-collapse: preserve; } -.out1 > p:empty::before { - content: ""; - display: inline-block; -} -.error { background-color: red; margin: 0px; } -.active { background-color: lightpink; } -.wrong { background-color: orange; } -.right { background-color: green; } -.follower { position: absolute; transform: translate(-50%, -50%); pointer-events: none; } -body { cursor: none; } -#threads { border: black solid 1px; padding: 0px; min-height: 2em;} -#threads > p { margin: 0px; } -@keyframes fadeOut { - from { opacity: 1; } - to { opacity: 0; } -} - -#code { position: absolute; font-weight: bold; font-size: 50vh; pointer-events: none; animation: fadeOut 2s ease-out forwards; background: white; pointer-events: none; } -</style> - <title>Befunge</title> - </head> - <body> - <div id="code">Code!</div> - <div style="display: inline-block;"><big>Placing:</big></div><table style="display: inline-block;"><tr><td id="to_place"></td><th>Which does: <div id="place_description" style="display: inline-block;"></div></th></tr></table> - <div style="display: inline-block;">Speed <input type="range" min="-100" max="150" value="40" id="speed" style="height: 1em;"/></div> - <br/> - <div id="threads"></div> - <table id="table"> - </table> - <div class="out1"><p>Output:</p><div id="out"></div></div> - <div class="out1"><p>Previous:</p><div id="outlast"></div></div> - <div class="out1"><p>Target:</p><div>Hello, world!</div></div> - <div id="follower" class="follower"><table><tr><td id="follower_cell">x</td></tr></table></div> - <div>Don't get exploded: <code id="bomb">💣......💥</code></div> - </body> -</html> 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<jit.path.length; i++) { - if (jit.path[i][0] == row && jit.path[i][1] == col) { - thread.row = ${thread_state.row} + ${thread_state.rowd}; - thread.col = ${thread_state.col} + ${thread_state.cold}; - thread.rowd = ${thread_state.rowd}; - thread.cold = ${thread_state.cold}; - return; - } - } - }`}, - }, - "t": { - impl: function (thread) { thread.interpreter.new_thread(thread.split_thread()); }, - desc: "; create new thread in reverse direction", - can_jit: false, - }, - // "&" input character TODO - "~": { // TODO handle EOF properly (push nothing) - impl: function (thread) { - if (thread.interpreter.stdin_queue.length > 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<n; i++) { - x = x * 256 + thread.pop(); - } - if (thread.interpreter.overlays[x]) { - // We don't support ")" anyway so for now don't - // need to know how to undo... - for(let [k, v] of Object.entries(thread.interpreter.overlays[x])) { - thread.overlays[k.charCodeAt(0)] = v.impl; - } - thread.stack.push(x); - thread.stack.push(1); - } else { - // Failure, reverse direction. - thread.cold *= -1; - thread.rowd *= -1; - } - }, - desc: "xn ... x1 n → f=(x1 + x2*256 + ... + xn*256^(n-1)) 1 | (); load semantic f or reverse", - can_jit: false, // Varargs approach makes JIT hard! - }, -}; - -// Add numbers 0-9 -for(let i=0; i<10; i++) { - const j = i; // Prevent any capture shenanigans. - instructions_raw[i] = { - impl: function (state) { state.stack.push(j); }, - desc: `() → ${i}`, - can_jit: true, - stack_min: 0, - stack_return: 1, - unchecked_js_code: `stack.push(${i});`, - }; -} - -// Fast access instructions array. -export let instructions = {}; -for(let [s, val] of Object.entries(instructions_raw)) { - instructions[s.charCodeAt(0)] = val.impl; -} diff --git a/2026/games/liquidcake1/interpreter.mjs b/2026/games/liquidcake1/interpreter.mjs deleted file mode 100644 index 703b2d7..0000000 --- a/2026/games/liquidcake1/interpreter.mjs +++ /dev/null @@ -1,449 +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. - -import { instructions, instructions_raw } from "./instructions.mjs"; -import { Queue } from "./queue.mjs"; -import { Jit } from "./jit.mjs"; - -function gen_fingerprint(s) { - let x = 0; - for(let c of Array.from(s).map(x => 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<this.field.length; row++) { - let s = ""; - for(let col=0; col<this.field[row].length; col++) { - let c = this.field[row][col]; - s += (c > 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<jit_path.path.length; i++) { - this.cell_stats[jit_path.path[i]].jit_starts.push(starting_place); - } - // Drop out of follow mode, now. - return jit; - } - - cell_changed(row, col) { - let dirs = [[-1, 0], [1, 0], [0, -1], [0, 1]]; - for(let i=0; i<4; i++) { - let rowd = dirs[i][0]; - let cold = dirs[i][1]; - let cell_diagonal_index = diagonalise_with_dir(row, col, rowd, cold); - let cell_stats = this.cell_stats[cell_diagonal_index]; - if (cell_stats) { - for(let j=0; j<cell_stats.jit_starts.length; j++) { - this.cell_stats[cell_stats.jit_starts[j]] = undefined; - } - } - } - } -} diff --git a/2026/games/liquidcake1/queue.mjs b/2026/games/liquidcake1/queue.mjs deleted file mode 100644 index e6c3efe..0000000 --- a/2026/games/liquidcake1/queue.mjs +++ /dev/null @@ -1,30 +0,0 @@ -export class Queue { - /* Structure is a (hopefully short) list of (longer) lists. - * Queue to the last list. - * Hold index into first list. - */ - length = 0; - index = -1; - content = [[]]; - max_len = 100; - push(x) { - let content = this.content; - if (content[content.length - 1].length > 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); }; -} |
