about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2025-05-05 21:31:05 -0400
committerelioat <elioat@tilde.institute>2025-05-05 21:31:05 -0400
commit8dd07060047e906a0cddb846898f32e620edf83b (patch)
treec7570824c7b696f708cf4b9d3151321b6a64ea89
parentf74e0ee83e3bfb6a1e51c62855e0847b46a33815 (diff)
downloadtour-8dd07060047e906a0cddb846898f32e620edf83b.tar.gz
-rw-r--r--html/kgame/index.html14
-rw-r--r--html/kgame/script.js162
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;
                     }