diff options
author | elioat <elioat@tilde.institute> | 2025-05-05 21:31:05 -0400 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2025-05-05 21:31:05 -0400 |
commit | 8dd07060047e906a0cddb846898f32e620edf83b (patch) | |
tree | c7570824c7b696f708cf4b9d3151321b6a64ea89 /html/kgame | |
parent | f74e0ee83e3bfb6a1e51c62855e0847b46a33815 (diff) | |
download | tour-master.tar.gz |
Diffstat (limited to 'html/kgame')
-rw-r--r-- | html/kgame/index.html | 14 | ||||
-rw-r--r-- | html/kgame/script.js | 162 |
2 files changed, 121 insertions, 55 deletions
diff --git a/html/kgame/index.html b/html/kgame/index.html index 453f40e..233c1d6 100644 --- a/html/kgame/index.html +++ b/html/kgame/index.html @@ -34,9 +34,17 @@ <li><code>G @ (50 * !50 + (49 - !50)) : 1</code> (Diagonal line from top-right - demonstrates - operator)</li> <li><code>G @ (!2500) : ((!2500) / 50 + (!2500) % 50) % 2</code> (Checkerboard pattern - demonstrates / and % operators)</li> - <li><strong>Special Operations:</strong></li> - <li><code>G @ (where G=1) : 0</code> (Turn off currently 'on' cells - demonstrates where clause)</li> - <li><code>G @ (!2500) : ((!2500) / 50 - 25) * ((!2500) / 50 - 25) + ((!2500) % 50 - 25) * ((!2500) % 50 - 25) < 625</code> (Circular pattern - demonstrates complex math)</li> + <li><strong>Unary Operators:</strong></li> + <li><code>G @ (!25) : ~(!25)</code> (First cell 1, rest 0 - demonstrates ~ not operator)</li> + <li><code>G @ (!25) : |(!25)</code> (Reverse sequence - demonstrates | reverse operator)</li> + <li><code>G @ (!25) : $(!25)</code> (Rotate sequence - demonstrates $ rotate operator)</li> + <li><code>G @ (!25) : #(!25)</code> (Reshape sequence - demonstrates # reshape operator)</li> + + <li><strong>Operator Composition:</strong></li> + <li><code>G @ (!25) : ~((!25) / 5 + (!25) % 5) % 2</code> (Inverted checkerboard - demonstrates ~ with math)</li> + <li><code>G @ (!25) : |((!25) / 5 + (!25) % 5) % 2</code> (Flipped checkerboard - demonstrates | with math)</li> + <li><code>G @ (!25) : $((!25) / 5 + (!25) % 5) % 2</code> (Rotated checkerboard - demonstrates $ with math)</li> + <li><code>G @ (!25) : #((!25) % 2)</code> (Alternating columns reshaped - demonstrates # with math)</li> </ul> <canvas id="gridCanvas"></canvas> diff --git a/html/kgame/script.js b/html/kgame/script.js index a11d5f5..ed71eeb 100644 --- a/html/kgame/script.js +++ b/html/kgame/script.js @@ -74,7 +74,7 @@ document.addEventListener('DOMContentLoaded', () => { code = code.replace(/\s+/g, ' ').trim(); // Define all operators and special characters - const operators = ['+', '-', '*', '/', '%', '(', ')', '!', '@', ':', '=', '<', '>', "'"]; + const operators = ['+', '-', '*', '/', '%', '(', ')', '!', '@', ':', '=', '<', '>', "'", '|', '$', '#', '~']; // Add spaces around all operators operators.forEach(op => { @@ -102,10 +102,9 @@ document.addEventListener('DOMContentLoaded', () => { function parseAtom() { let token = tokens.shift(); if (!token) throw new Error("Unexpected end of expression"); - console.log('Parsing atom:', token, 'Remaining tokens:', tokens); if (token === '(') { - const value = parseAddSub(); // Start parsing inside parenthesis + const value = parseAddSub(); if (tokens.length === 0) { throw new Error("Missing closing parenthesis"); } @@ -119,49 +118,26 @@ document.addEventListener('DOMContentLoaded', () => { if (typeof operand !== 'number' || !Number.isInteger(operand) || operand < 0) { throw new Error("Operand for ! (iota) must be a non-negative integer"); } - // Only generate indices that fit within our grid const maxIndex = GRID_SIZE * GRID_SIZE; - return Array.from({length: Math.min(operand, maxIndex)}, (_, i) => i); - } else if (token === "'") { // Each adverb - const operand = parseAtom(); - if (!Array.isArray(operand)) { - throw new Error("Each adverb (') requires an array operand"); - } - return operand.map(x => Array.isArray(x) ? x.map(y => y) : x); - } else if (token === '/') { // Over adverb - const operand = parseAtom(); - if (!Array.isArray(operand)) { - throw new Error("Over adverb (/) requires an array operand"); - } - return operand.reduce((a, b) => { - if (Array.isArray(a) && Array.isArray(b)) { - return a.map((x, i) => x + b[i]); - } - return a + b; - }); - } else if (token === 'G') { - // Return the grid as a flat array of [row,col] pairs - const result = []; - for (let row = 0; row < GRID_SIZE; row++) { - for (let col = 0; col < GRID_SIZE; col++) { - result.push([row, col]); - } - } + const result = Array.from({length: Math.min(operand, maxIndex)}, (_, i) => i); + console.log(`Iota generated array of length ${result.length}, first few values:`, result.slice(0, 5)); return result; - } else if (token === 'where') { - if (tokens.shift() !== 'G') throw new Error("Expected 'G' after 'where'"); - if (tokens.shift() !== '=') throw new Error("Expected '=' after 'where G'"); - if (tokens.shift() !== '1') throw new Error("Expected '1' after 'where G='"); - // Find all coordinates where G[row][col] === 1 - const indices = []; - for(let row = 0; row < GRID_SIZE; row++) { - for(let col = 0; col < GRID_SIZE; col++) { - if (G[row][col] === 1) { - indices.push(row * GRID_SIZE + col); // Convert to 1D index - } - } + } else if (token === '~') { // Not operator + const operand = parseAtom(); + if (Array.isArray(operand)) { + const result = operand.map(x => { + const val = x === 0 ? 1 : 0; + console.log(`Not operation: ${x} -> ${val}`); + return val; + }); + console.log(`Not operation on array, first few results:`, result.slice(0, 5)); + console.log(`Input array first few values:`, operand.slice(0, 5)); + return result; + } else { + const result = operand === 0 ? 1 : 0; + console.log(`Not operation (scalar): ${operand} -> ${result}`); + return result; } - return indices; } else if (/^-?\d+$/.test(token)) { return parseInt(token, 10); } else { @@ -169,6 +145,47 @@ document.addEventListener('DOMContentLoaded', () => { } } + function parseUnary() { + let token = tokens[0]; + if (token === '|' || token === '$' || token === '#') { + tokens.shift(); + const operand = parseUnary(); + let result; + switch (token) { + case '|': + result = [...operand].reverse(); + break; + case '$': + if (!Array.isArray(operand)) { + throw new Error("Rotate operator ($) requires an array operand"); + } + const size = Math.sqrt(operand.length); + if (size * size !== operand.length) { + throw new Error("Rotate operator ($) requires a square array"); + } + result = []; + for (let col = 0; col < size; col++) { + for (let row = size - 1; row >= 0; row--) { + result.push(operand[row * size + col]); + } + } + break; + case '#': + if (!Array.isArray(operand)) { + throw new Error("Reshape operator (#) requires an array operand"); + } + result = []; + for (let i = 0; i < GRID_SIZE; i++) { + result.push(operand.slice(i * GRID_SIZE, (i + 1) * GRID_SIZE)); + } + result = result.flat(); + break; + } + return result; + } + return parseAtom(); + } + function applyOperation(a, b, op) { const isAList = Array.isArray(a); const isBList = Array.isArray(b); @@ -180,13 +197,12 @@ document.addEventListener('DOMContentLoaded', () => { case '-': result = x - y; break; case '*': result = x * y; break; case '%': result = y === 0 ? 0 : x % y; break; - case '/': result = y === 0 ? 0 : Math.floor(x / y); break; // Use integer division + case '/': result = y === 0 ? 0 : Math.floor(x / y); break; case '<': result = x < y ? 1 : 0; break; case '>': result = x > y ? 1 : 0; break; case '=': result = x === y ? 1 : 0; break; default: throw new Error(`Unknown operator: ${op}`); } - console.log(`Operation ${op}: ${x} ${op} ${y} = ${result}`); return result; }; @@ -198,29 +214,59 @@ document.addEventListener('DOMContentLoaded', () => { // Handle array operations const arrayOp = (arr, val) => { if (Array.isArray(arr)) { - return arr.map(x => arrayOp(x, val)); + const result = arr.map(x => arrayOp(x, val)); + if (op === '/' || op === '%') { + console.log(`Array operation ${op} with ${val}, first few results:`, result.slice(0, 5)); + } + return result; } return scalarOp(arr, val); }; if (isAList && !isBList) { - return arrayOp(a, b); + const result = arrayOp(a, b); + if (op === '+') { + console.log(`Array + scalar operation, first few values:`, { + array: a.slice(0, 5), + scalar: b, + result: result.slice(0, 5) + }); + } + return result; } else if (!isAList && isBList) { - return arrayOp(b, a); + const result = arrayOp(b, a); + if (op === '+') { + console.log(`Scalar + array operation, first few values:`, { + scalar: a, + array: b.slice(0, 5), + result: result.slice(0, 5) + }); + } + return result; } else { // Both are arrays if (a.length !== b.length) { throw new Error(`List length mismatch for operator ${op}: ${a.length} vs ${b.length}`); } - return a.map((x, i) => arrayOp(x, b[i])); + const result = a.map((x, i) => { + const val = arrayOp(x, b[i]); + if (op === '+') { + console.log(`Adding values at index ${i}: ${x} + ${b[i]} = ${val}`); + } + return val; + }); + if (op === '+') { + console.log(`Array addition, first few results:`, result.slice(0, 5)); + } + return result; } } function parseMulDivMod() { - let left = parseAtom(); + let left = parseUnary(); while (tokens.length > 0 && (tokens[0] === '*' || tokens[0] === '%' || tokens[0] === '/')) { const op = tokens.shift(); - const right = parseAtom(); + const right = parseUnary(); left = applyOperation(left, right, op); } return left; @@ -246,7 +292,16 @@ document.addEventListener('DOMContentLoaded', () => { return left; } - return parseComparison(); + function parseNot() { + let left = parseComparison(); + while (tokens.length > 0 && tokens[0] === '~') { + tokens.shift(); + left = Array.isArray(left) ? left.map(x => x === 0 ? 1 : 0) : (left === 0 ? 1 : 0); + } + return left; + } + + return parseNot(); } // Main execution function @@ -297,6 +352,9 @@ document.addEventListener('DOMContentLoaded', () => { if (row >= 0 && row < GRID_SIZE && col >= 0 && col < GRID_SIZE) { const valueToAssign = valuesArray[i % valuesArray.length]; + if (count < 5) { // Only log first few assignments + console.log(`Assignment [${row},${col}]: ${valueToAssign} (from index ${idx})`); + } G[row][col] = valueToAssign % 2; return count + 1; } |