DKL9 GitList
Repositories
DKL9 home
functle
Code
Commits
Branches
Tags
Search
Tree:
c4793cc
Branches
Tags
master
functle
guess.js
Improve instructions, export score, split code
dkl9
commited
c4793cc
at 2025-209 17:28:24
guess.js
Blame
History
Raw
const allowed = { terminals: ["x", "2", "1"], unary: ["^2", "^3", "sin", "exp", "ln"], binary: ["+", "-", "*", "/"] }; const allTokens = [...allowed.terminals, ...allowed.unary, ...allowed.binary]; const tl = 5; let target; let puzzleNum; if (true) { target = generateExpr(); puzzleNum = 0; } else { (async () => { const r = await fetch("https://dkl9.net/cgi-bin/fps_curr", { redirect: "follow" }); puzzleNum = parseInt(r.url.match(/n=(\d+)/)[1]); target = await r.text().split(" "); })(); } const container = document.getElementsByClassName("container")[0]; const boxes = {grey: "⬜️", yellow: "🟨", green: "🟩"}; const congrats = ["Incredible", "Amazing", "Great", "Nice", "You got it", "Whew"]; const gotitButton = document.getElementById("gotit"); if (localStorage.getItem("gotit") === "true") hideInstructions(gotitButton); if (gotitButton) { gotitButton.addEventListener("click", e => { if (localStorage.getItem("gotit") !== "true") { hideInstructions(e.target); localStorage.setItem("gotit", "true"); } }); } function submitGuess() { const row = grid.rows[currentRow]; document.getElementById("message").textContent = ""; if (currentInput.length !== tl) { document.getElementById("message").textContent = `Use exactly ${tl} tokens.`; shakeRow(row); return; } for (const t of currentInput) { if (!allTokens.includes(t)) { document.getElementById("message").textContent = "Invalid token: " + t; shakeRow(row); return; } } let ys; try { ys = xs.map(x => evalPostfix(currentInput, x)); } catch (e) { document.getElementById("message").textContent = "Syntax error."; shakeRow(row); return; } guessYsList.push(ys); redrawAll(); const close = closeEnough(ys, targetYs); animateFlip(currentInput, close); if (close) { setTimeout(() => { fillRow(currentInput, "green"); winMessage(); }, 300 * tl); return; } currentRow++; if (currentRow >= maxGuesses) loseMessage(); currentInput = []; } function animateFlip(tokens, close) { const row = grid.rows[currentRow]; const colors = Array(tl); if (close) { colors.fill("green"); } else { const freq = {}; for (const t of target) freq[t] = (freq[t] || 0) + 1; colors.fill("grey"); for (let i = 0; i < tl; i++) { if (tokens[i] === target[i]) { colors[i] = "green"; freq[tokens[i]]--; } } for (let i = 0; i < tl; i++) { if (colors[i] === "grey" && freq[tokens[i]] > 0) { colors[i] = "yellow"; freq[tokens[i]]--; } } } tokens.forEach((token, i) => { const cell = row.cells[i]; setTimeout(() => { cell.style.transform = "rotateX(90deg)"; setTimeout(() => { cell.classList.add(colors[i]); cell.style.transform = "rotateX(0deg)"; }, 150); }, 150 * i); }); } function fillRow(tokens, cls) { const row = grid.rows[currentRow]; for (let i = 0; i < tl; i++) { const cell = row.cells[i]; cell.textContent = tokens[i]; cell.classList.add(cls); } } function evalPostfix(tokens, x) { const s = []; for (const t of tokens) { if (t === "x") s.push(x); else if (t === "1") s.push(1); else if (t === "2") s.push(2); else if (allowed.unary.includes(t)) { if (s.length < 1) throw 0; const v = s.pop(); if (t === "^2") s.push(v ** 2); if (t === "^3") s.push(v ** 3); if (t === "sin") s.push(Math.sin(v)); if (t === "exp") s.push(Math.exp(v)); if (t === "ln") s.push(Math.log(v)); } else if (allowed.binary.includes(t)) { if (s.length < 2) throw 0; const b = s.pop(), a = s.pop(); if (t === "+") s.push(a + b); if (t === "-") s.push(a - b); if (t === "*") s.push(a * b); if (t === "/") s.push(a / b); } else throw 0; } if (s.length !== 1) throw 0; return s[0]; } function rand(l) { return l[Math.floor(l.length * Math.random())]; } function generateExpr(len = tl, force = true) { if (len == 1) { return force ? ["x"] : [rand(allowed.terminals)]; } else if (len == 2 || Math.random() >= 0.5) { const e = generateExpr(len - 1, force); e.push(rand(evalPostfix(e, 0.6) == 1 ? ["sin", "exp"] : allowed.unary)); return e; } else { const k = Math.floor(1 + (len - 2) * Math.random()); const f = force && Math.random() > 0.5; const a = generateExpr(k, f); const b = generateExpr(len - 1 - k, force && !f); const one = evalPostfix(a, 0.6) == 1 || evalPostfix(b, 0.6) == 1; const e = a.concat(b) e.push(rand(one ? ["+", "-"] : allowed.binary)); return e; } } function closeEnough(a, b) { for (let i = 0; i < a.length; i++) { if (isNaN(a[i]) && !isNaN(b[i])) return false; if (Math.abs(a[i] - b[i]) > 0.1 * (Math.abs(b[i]) + 1)) return false; } return true; } function hideInstructions(button) { const inst = document.getElementById("instructions"); inst.remove(); button.remove(); const heading = inst.firstElementChild; heading.remove(); const summary = document.createElement("summary"); summary.textContent = heading.textContent; const newInst = document.createElement("details"); newInst.append(summary); for (const c of inst.childNodes) newInst.append(c); container.append(newInst); } function winMessage() { let boast = `Functle ${puzzleNum} ${currentRow + 1}/${maxGuesses}\n`; for (i = 0; i <= currentRow; i++) { r = grid.rows[i]; for (c of r.cells) { const l = c.classList; let p = "?"; for (o in boxes) if (l.contains(o)) p = boxes[o]; boast = boast + p; } boast = boast + "\n"; } boast = boast + "https://DKL9.net/functle/"; navigator.clipboard.writeText(boast); const w = document.createElement("section"); w.append(`${congrats[currentRow]}! Share your score. It's on your clipboard. `); const b = document.createElement("button"); b.textContent = "Copy again"; b.type = "button"; b.addEventListener("click", () => navigator.clipboard.writeText(boast)); w.append(b); container.prepend(w); } function loseMessage() { const d = document.createElement("p"); d.textContent = `Better luck next time. The function was ${target.join(" ")}`; container.prepend(d); }