Randomly generate target problem
dkl9

dkl9 commited on 2025-209 17:15:36
Showing 1 changed files, with 50 additions and 32 deletions.

... ...
@@ -4,7 +4,7 @@
4 4
         <style>
5 5
             body { font-family: sans-serif; }
6 6
             #plot { border: 1px solid #000; }
7
-            #grid { border-collapse: collapse; margin-top: 10px; }
7
+            #grid { border-collapse: separate; border-spacing: 5px 10px; margin-top: 10px; }
8 8
             #grid td { width: 40px; height: 40px; border: 1px solid #ccc; text-align: center; vertical-align: middle; font-weight: bold; }
9 9
             .grey { background: #787c7e; color: #fff; }
10 10
             .yellow { background: #c9b458; color: #fff; }
... ...
@@ -32,8 +32,51 @@
32 32
                 unary: ["^2", "^3", "sin", "exp", "ln"],
33 33
                 binary: ["+", "-", "*", "/"]
34 34
             };
35
-            const target = ["x", "sin", "1", "1", "+", "*"]; // 2*sin(x)
36
-            const tl = target.length;
35
+            const allTokens = [...allowed.terminals, ...allowed.unary, ...allowed.binary];
36
+            const tl = 6;
37
+            
38
+            function evalPostfix(tokens, x) {
39
+                const s = [];
40
+                for (const t of tokens) {
41
+                    if (t === "x") s.push(x);
42
+                    else if (t === "1") s.push(1);
43
+                    else if (t === "2") s.push(2);
44
+                    else if (allowed.unary.includes(t)) {
45
+                        if (s.length < 1) throw 0;
46
+                        const v = s.pop();
47
+                        if (t === "^2") s.push(v ** 2);
48
+                        if (t === "^3") s.push(v ** 3);
49
+                        if (t === "sin") s.push(Math.sin(v));
50
+                        if (t === "exp") s.push(Math.exp(v));
51
+                        if (t === "ln") s.push(Math.log(v));
52
+                    } else if (allowed.binary.includes(t)) {
53
+                        if (s.length < 2) throw 0;
54
+                        const b = s.pop(), a = s.pop();
55
+                        if (t === "+") s.push(a + b);
56
+                        if (t === "-") s.push(a - b);
57
+                        if (t === "*") s.push(a * b);
58
+                        if (t === "/") s.push(a / b);
59
+                    } else throw 0;
60
+                }
61
+                if (s.length !== 1) throw 0;
62
+                return s[0];
63
+            }
64
+            
65
+            function generateTarget() {
66
+                while (true) {
67
+                    const cand = [];
68
+                    for (let i = 0; i < tl; i++) {
69
+                        cand.push(allTokens[Math.floor(Math.random() * allTokens.length)]);
70
+                    }
71
+                    try {
72
+                        // test on a sample of x values to ensure valid
73
+                        for (let xTest of [0, 1, -1]) evalPostfix(cand, xTest);
74
+                        return cand;
75
+                    } catch (e) { continue; }
76
+                }
77
+            }
78
+            
79
+            const target = generateTarget();
37 80
             const maxGuesses = 6;
38 81
             let currentRow = 0;
39 82
             const width = 400, height = 400;
... ...
@@ -57,13 +101,11 @@
57 101
                 const x0 = mapX(0);
58 102
                 ctx.moveTo(x0, 0);
59 103
                 ctx.lineTo(x0, height);
60
-                // ticks at (1,0) and (-1,0)
61 104
                 [1, -1].forEach(val => {
62 105
                     const px = mapX(val);
63 106
                     ctx.moveTo(px, y0 - 5);
64 107
                     ctx.lineTo(px, y0 + 5);
65 108
                 });
66
-                // ticks at (0,1) and (0,-1)
67 109
                 [1, -1].forEach(val => {
68 110
                     const py = mapY(val);
69 111
                     ctx.moveTo(x0 - 5, py);
... ...
@@ -90,9 +134,10 @@
90 134
                     drawCurve(ys, color);
91 135
                 });
92 136
             }
93
-            // initial draw
137
+            
94 138
             drawAxes();
95 139
             drawCurve(targetYs, "green");
140
+            
96 141
             const grid = document.getElementById("grid");
97 142
             for (let i = 0; i < maxGuesses; i++) {
98 143
                 const tr = document.createElement("tr");
... ...
@@ -110,7 +156,7 @@
110 156
                     return;
111 157
                 }
112 158
                 for (const t of inp) {
113
-                    if (![...allowed.terminals, ...allowed.unary, ...allowed.binary].includes(t)) {
159
+                    if (!allTokens.includes(t)) {
114 160
                         document.getElementById("message").textContent = "Invalid token: " + t;
115 161
                         return;
116 162
                     }
... ...
@@ -137,31 +183,7 @@
137 183
                     document.getElementById("submitBtn").disabled = true;
138 184
                 }
139 185
             };
140
-            function evalPostfix(tokens, x) {
141
-                const s = [];
142
-                for (const t of tokens) {
143
-                    if (t === "x") s.push(x);
144
-                    else if (t === "1") s.push(1);
145
-                    else if (t === "2") s.push(2);
146
-                    else if (allowed.unary.includes(t)) {
147
-                        const v = s.pop();
148
-                        if (t === "^2") s.push(v ** 2);
149
-                        if (t === "^3") s.push(v ** 3);
150
-                        if (t === "sin") s.push(Math.sin(v));
151
-                        if (t === "exp") s.push(Math.exp(v));
152
-                        if (t === "ln") s.push(Math.log(v));
153
-                    } else {
154
-                        const b = s.pop(), a = s.pop();
155
-                        if (t === "+") s.push(a + b);
156
-                        if (t === "-") s.push(a - b);
157
-                        if (t === "*") s.push(a * b);
158
-                        if (t === "/") s.push(a / b);
159
-                    }
160
-                    if (s.length < 0) throw 0;
161
-                }
162
-                if (s.length !== 1) throw 0;
163
-                return s[0];
164
-            }
186
+            
165 187
             function closeEnough(a, b) {
166 188
                 for (let i = 0; i < a.length; i++) {
167 189
                     if (isNaN(a[i]) || Math.abs(a[i] - b[i]) > 0.1 * (Math.abs(b[i]) + 1)) return false;
168 190