Initial commit: vibe-code a prototype
dkl9

dkl9 commited on 2025-209 17:09:01
Showing 1 changed files, with 193 additions and 0 deletions.

... ...
@@ -0,0 +1,193 @@
1
+<!DOCTYPE html>
2
+<html>
3
+    <head>
4
+        <style>
5
+            body { font-family: sans-serif; }
6
+            #plot { border: 1px solid #000; }
7
+            #grid { display: grid; grid-template-rows: repeat(6, 1fr); gap: 5px; margin-top: 10px; }
8
+            .row { display: grid; grid-template-columns: repeat(6, 1fr); gap: 5px; }
9
+            .cell { width: 40px; height: 40px; border: 1px solid #ccc; display: flex; align-items: center; justify-content: center; font-weight: bold; }
10
+            .grey { background: #787c7e; color: #fff; }
11
+            .yellow { background: #c9b458; color: #fff; }
12
+            .green { background: #6aaa64; color: #fff; }
13
+            #message { color: red; margin-top: 5px; }
14
+        </style>
15
+    </head>
16
+    <body>
17
+        <canvas id="plot" width="400" height="400"></canvas>
18
+        <div>
19
+            <input id="guessInput" placeholder="token1 token2 ... token6" style="width: 300px;">
20
+            <button id="submitBtn">Submit</button>
21
+            <div id="message"></div>
22
+        </div>
23
+        <div id="grid"></div>
24
+        <script>
25
+            const allowed = {
26
+                terminals: ["x", "1", "2"],
27
+                unary: ["^2", "^3", "sin", "exp", "ln"],
28
+                binary: ["+", "-", "*", "/"]
29
+            };
30
+            const target = ["x", "sin", "1", "1", "+", "*"]; // 2*sin(x)
31
+            const tl = target.length;
32
+            const maxGuesses = 6;
33
+            let currentRow = 0;
34
+            const width = 400, height = 400;
35
+            const ctx = document.getElementById("plot").getContext("2d");
36
+            const bound = 10;
37
+            const xs = [];
38
+            const targetYs = [];
39
+            for (let i = 0; i <= width; i++) {
40
+                const x = bound * (2 * i / width - 1);
41
+                xs.push(x);
42
+                targetYs.push(evalPostfix(target, x));
43
+            }
44
+            const yMin = Math.min(...targetYs), yMax = Math.max(...targetYs);
45
+            drawAxes();
46
+            drawCurve(targetYs, "green");
47
+            for (let i = 0; i < maxGuesses; i++) {
48
+                const row = document.createElement("div");
49
+                row.className = "row";
50
+                for (let j = 0; j < tl; j++) {
51
+                    const cell = document.createElement("div");
52
+                    cell.className = "cell";
53
+                    row.appendChild(cell);
54
+                }
55
+                document.getElementById("grid").appendChild(row);
56
+            }
57
+            document.getElementById("submitBtn").onclick = () => {
58
+                const inp = document.getElementById("guessInput").value.trim().split(/\s+/);
59
+                document.getElementById("message").textContent = "";
60
+                if (inp.length !== tl) {
61
+                    document.getElementById("message").textContent = `Exactly ${tl} tokens.`;
62
+                    return;
63
+                }
64
+                for (const t of inp) {
65
+                    if (![...allowed.terminals, ...allowed.unary, ...allowed.binary].includes(t)) {
66
+                        document.getElementById("message").textContent = "Invalid token: " + t;
67
+                        return;
68
+                    }
69
+                }
70
+                let ys;
71
+                try {
72
+                    ys = xs.map(x => evalPostfix(inp, x));
73
+                } catch (e) {
74
+                    document.getElementById("message").textContent = "Syntax error.";
75
+                    return;
76
+                }
77
+                if (closeEnough(ys, targetYs)) {
78
+                    fillRow(inp, "green");
79
+                    alert("Correct.");
80
+                    document.getElementById("submitBtn").disabled = true;
81
+                    return;
82
+                }
83
+                markRow(inp);
84
+                drawCurve(ys, "red");
85
+                currentRow++;
86
+                if (currentRow >= maxGuesses) {
87
+                    alert("Out of guesses.");
88
+                    document.getElementById("submitBtn").disabled = true;
89
+                }
90
+            };
91
+
92
+            function evalPostfix(tokens, x) {
93
+                const s = [];
94
+                for (const t of tokens) {
95
+                    if (t === "x") s.push(x);
96
+                    else if (t === "1") s.push(1);
97
+                    else if (t === "2") s.push(2);
98
+                    else if (allowed.unary.includes(t)) {
99
+                        const v = s.pop();
100
+                        if (t === "^2") s.push(v ** 2);
101
+                        if (t === "^3") s.push(v ** 3);
102
+                        if (t === "sin") s.push(Math.sin(v));
103
+                        if (t === "exp") s.push(Math.exp(v));
104
+                        if (t === "ln") s.push(Math.log(v));
105
+                    } else {
106
+                        const b = s.pop(),
107
+                            a = s.pop();
108
+                        if (t === "+") s.push(a + b);
109
+                        if (t === "-") s.push(a - b);
110
+                        if (t === "*") s.push(a * b);
111
+                        if (t === "/") s.push(a / b);
112
+                    }
113
+                    if (s.length < 0) throw 0;
114
+                }
115
+                if (s.length !== 1) throw 0;
116
+                return s[0];
117
+            }
118
+
119
+            function closeEnough(a, b) {
120
+                for (let i = 0; i < a.length; i++) {
121
+                    if (isNaN(a[i]) || Math.abs(a[i] - b[i]) > 0.1 * (Math.abs(b[i]) + 1)) return false;
122
+                }
123
+                return true;
124
+            }
125
+
126
+            function fillRow(tokens, cls) {
127
+                const row = document.getElementById("grid").children[currentRow];
128
+                for (let i = 0; i < 6; i++) {
129
+                    row.children[i].textContent = tokens[i];
130
+                    row.children[i].classList.add(cls);
131
+                }
132
+            }
133
+
134
+            function markRow(tokens) {
135
+                const freq = {};
136
+                for (const t of target) freq[t] = (freq[t] || 0) + 1;
137
+                const colors = Array(tl).fill("grey");
138
+                for (let i = 0; i < tl; i++) {
139
+                    if (tokens[i] === target[i]) {
140
+                        colors[i] = "green";
141
+                        freq[tokens[i]]--;
142
+                    }
143
+                }
144
+                for (let i = 0; i < tl; i++) {
145
+                    if (colors[i] === "grey" && freq[tokens[i]] > 0) {
146
+                        colors[i] = "yellow";
147
+                        freq[tokens[i]]--;
148
+                    }
149
+                }
150
+                const row = document.getElementById("grid").children[currentRow];
151
+                for (let i = 0; i < tl; i++) {
152
+                    row.children[i].textContent = tokens[i];
153
+                    row.children[i].classList.add(colors[i]);
154
+                }
155
+            }
156
+
157
+            function drawAxes() {
158
+                ctx.clearRect(0, 0, width, height);
159
+                ctx.beginPath();
160
+                // x-axis
161
+                const y0 = mapY(0);
162
+                ctx.moveTo(0, y0);
163
+                ctx.lineTo(width, y0);
164
+                // y-axis
165
+                const x0 = mapX(0);
166
+                ctx.moveTo(x0, 0);
167
+                ctx.lineTo(x0, height);
168
+                ctx.strokeStyle = "black";
169
+                ctx.stroke();
170
+            }
171
+
172
+            function drawCurve(ys, color) {
173
+                ctx.beginPath();
174
+                for (let i = 0; i < xs.length; i++) {
175
+                    const px = mapX(xs[i]),
176
+                        py = mapY(ys[i]);
177
+                    if (i === 0) ctx.moveTo(px, py);
178
+                    else ctx.lineTo(px, py);
179
+                }
180
+                ctx.strokeStyle = color;
181
+                ctx.stroke();
182
+            }
183
+
184
+            function mapX(x) {
185
+                return (x + bound) / (2 * bound) * width;
186
+            }
187
+
188
+            function mapY(y) {
189
+                return height - (y - yMin) / (yMax - yMin) * height;
190
+            }
191
+        </script>
192
+    </body>
193
+</html>
0 194