DKL9 GitList
Repositories
DKL9 home
site_root
Code
Commits
Branches
Tags
Search
Tree:
993e20c
Branches
Tags
master
site_root
tools
speed_exp_log.html
Initial commit: track much site content
dkl9
commited
993e20c
at 2025-352 15:27:28
speed_exp_log.html
Blame
History
Raw
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Speed Exp/Log</title> <link rel="stylesheet" href="/style.css"> <link rel="icon" type="image/png" href="/favicon.png"> <style> #pdsa { font-size: 4em; } #ria { font-size: 1em; } </style> </head> <body> <noscript>Enable JavaScript, or use a different browser</noscript> <h1>Speed Exp/Log: mental computation practice</h1> <p>Someday I might write directions.</p> <p>Originally developed in 2021. Comments and questions are welcome. Email them to <a href="mailto:contact@dkl9.net">contact@dkl9.net</a>. <span style="display: none;">And not <a href="mailto:sucrose@dkl9.net">sucrose@dkl9.net</a>, unless you are a spambot.</span></p> <p><a href="/">dkl9 home</a></p> <details> <summary>Settings</summary> <label>Time limit: <span id="scs_tl">10</span>s <input class="cfg" id="cfg_tl" type="range" value="10" min="1" max="30" step="1"></label><br> <label>Do logarithms instead of exponents? <input class="cfg" id="cfg_inv" type="checkbox" checked></label><br> <label>Minimum value involved: <span id="scs_qmin">1</span> <input class="cfg" id="cfg_qmin" type="range" value="0" min="-4" max="2" step="0.1"></label><br> <label>Maxmimum value involved: <span id="scs_qmax">992</span> <input class="cfg" id="cfg_qmax" type="range" value="6.9" min="0" max="10" step="0.1"></label><br> <label>Precision required in answers: <span id="scs_rp">1</span> digits <input class="cfg" id="cfg_rp" type="range" value="1" min="0" max="4" step="1"></label><br> <label>Allowed failures: <span id="scs_pfc">1</span> <input class="cfg" id="cfg_pfc" type="range" value="1" min="0" max="4" step="1"></label> </details> <p id="ctrd">0/0</p> <div id="pdsa"> <p id="qda"></p> <input id="ria" type="number"> </div> <script> // constants // no fontawesome needed! just use unicode! const INDICATORS = { "success": "✔", "failure": "✘", "badfail": "👎", "enter": "⏎", "oot": "⏱", "question": "❔" }; // element handles const counterDisp = document.getElementById("ctrd"); const questionArea = document.getElementById("qda"); const responseArea = document.getElementById("ria"); // globals const settings = { "tl": 10, "inv": true, "qmin": 0, "qmax": 6.9, "rp": 1, "pfc": 1 }; // 0: initialising/invalid // 1: question asked, waiting for answer // 2: answered, result displayed, waiting for enter let state = 0; let question; let ootTimeout; let wrongCount; let failCount = 0; let succCount = 0; // functions // rounds `x` to `d` decimal places, which is a number // of significant figures if `s` is true, and a number of digits // after the decimal point if `s` is false function dynRound(x, d, s) { const mag = Math.ceil(Math.log10(x)); const sc = 10 ** (d - s * mag); return Math.round(x * sc) / sc; } // handles the `change` input event to reconfigure a setting function changeConfig(ev) { const settingName = ev.target.id.split("_")[1]; let newValue; switch (ev.target.type) { case "range": newValue = parseFloat(ev.target.value); break; case "checkbox": newValue = ev.target.checked; break; default: alert("something went wrong (unrecognised input type)"); return; } settings[settingName] = newValue; const dispElement = document.getElementById("scs_" + settingName); if (dispElement) { switch (settingName) { case "tl": dispElement.innerText = newValue; break; case "inv": break; case "qmin": case "qmax": dispElement.innerText = dynRound(Math.exp(newValue), 3, true); break; case "rp": case "pfc": dispElement.innerText = newValue; break; default: alert("something went wrong (unrecognised setting display)"); return; } } } // generate and return a random number in the range from `a` to `b` function genRange(a, b) { return a + (b - a) * Math.random(); } // generate and return a question, in accordance with // the current settings function genQuestion() { return { "mode": settings.inv, "num": dynRound(genRange(settings.qmin, settings.qmax), settings.rp - !settings.inv, false), "st": Date.now() }; } // return a string representation of the question `q` function dispQuestion(q) { const nd = q.mode ? dynRound(Math.exp(q.num), 3, true) : dynRound(q.num, 2, false); return `${INDICATORS.question} ${q.mode ? "log" : "exp"}(${nd})`; } // returns a number that would work as // a correct answer for question `q` function getAnswer(q) { return q.mode ? dynRound(q.num, settings.rp, false) : dynRound(Math.exp(q.num), settings.rp, true); } // check answer-number `x` to question `q`, // returning `true` for correct and `false` for incorrect, // regardless of timing function checkAnswer(q, x) { return getAnswer(q) == dynRound(x, settings.rp, !q.mode); } // cancel the question due to running out of time function outOfTime() { questionArea.innerText = `${INDICATORS.oot} answer: ${getAnswer(question)} (${INDICATORS.enter} continue)`; failCount++; state = 2; } // handle a keypress in the game area function gameKeypress(ev) { if (ev.key == "Enter") { switch (state) { case 0: case 2: responseArea.value = ""; question = genQuestion(); questionArea.innerText = dispQuestion(question); ootTimeout = setTimeout(outOfTime, 1000 * settings.tl); wrongCount = 0; state = 1; break; case 1: if (checkAnswer(question, parseFloat(responseArea.value))) { questionArea.innerText = `${INDICATORS.success} correct (${INDICATORS.enter} continue)`; clearTimeout(ootTimeout); succCount++; state = 2; } else { wrongCount++; if (wrongCount > settings.pfc) { questionArea.innerText = `${INDICATORS.badfail} answer: ${getAnswer(question)} (${INDICATORS.enter} continue)`; clearTimeout(ootTimeout); failCount++; state = 2; } else { questionArea.innerText = `${dispQuestion(question)}\n${INDICATORS.failure} try again`; } } counterDisp.innerText = `${succCount}/${succCount + failCount}`; break; default: alert("something went wrong (impossible state)"); return; } } } // handler assignment const cfgInputs = document.getElementsByClassName("cfg"); for (let i = 0; i < cfgInputs.length; i++) { cfgInputs[i].onchange = changeConfig; } document.getElementById("pdsa").onkeydown = gameKeypress; // init questionArea.innerText = INDICATORS.enter + " to begin"; </script> </body> </html>