commit 8f215d9f24129f4786e22004d19cf082e342fdd3
parent fcb438ef47ed5540024277756369aef69283d127
Author: tongong <tongong@gmx.net>
Date: Sat, 15 Jan 2022 11:43:23 +0100
added trainer mode
Diffstat:
6 files changed, 220 insertions(+), 18 deletions(-)
diff --git a/src/components/key-boxes.js b/src/components/key-boxes.js
@@ -1,11 +1,12 @@
const m = require("mithril");
const input = require("../modules/input.js");
-/* inputCallback:
- * expects one argument: char to input
+/* inputCallback: expects one argument: char to input
+ * keyCallback: can be used for event handling of other pressed keys
*/
module.exports = (vnode) => {
- let cb = vnode.attrs.inputCallback;
+ let icb = vnode.attrs.inputCallback;
+ let kcb = vnode.attrs.keyCallback;
let active = [false, false, false];
// contains indizes of pressed key in the order in which they were pressed
let pressed = [];
@@ -15,10 +16,13 @@ module.exports = (vnode) => {
oncreate: () => {
document.onkeydown = (e) => {
let index = input.keys.indexOf(e.key);
- if (!e.repeat && index != -1) {
- active[index] = true;
- pressed.push(index);
- m.redraw();
+ if (!e.repeat) {
+ if (kcb) kcb(e);
+ if (index != -1) {
+ active[index] = true;
+ pressed.push(index);
+ m.redraw();
+ }
}
}
document.onkeyup = (e) => {
@@ -30,7 +34,7 @@ module.exports = (vnode) => {
pressed = [];
let charMaybe = input.gestures2char(gestures);
if (charMaybe) {
- cb(charMaybe);
+ icb(charMaybe);
gestures = [];
}
}
diff --git a/src/components/page-input.js b/src/components/page-input.js
@@ -2,16 +2,14 @@ const m = require("mithril");
const keyBoxes = require("./key-boxes.js");
module.exports = () => {
- let written = "";
- let ongesture = (g) => {
- written += g;
+ let written = "> ";
+ let oninput = (c) => {
+ written += c;
}
return {
view: () => m("div",
- m("div", {style: "height:40%;width:100%"}, written),
- m("div", {style: "height:60%;width:100%"},
- m(keyBoxes, {inputCallback: ongesture})
- )
+ m("div.inputTop", written),
+ m("div.inputBottom", m(keyBoxes, {inputCallback: oninput}))
)
}
};
diff --git a/src/components/page-trainer.js b/src/components/page-trainer.js
@@ -1,9 +1,53 @@
const m = require("mithril");
+const keyBoxes = require("./key-boxes.js");
+const training = require("../modules/training.js");
+const input = require("../modules/input.js");
+
+function fixSpace(c) {
+ if (c == " ") return "_";
+ return c;
+}
module.exports = () => {
- return {
- view: () => {
- return m("p", "will be added later");
+ let test;
+ let showHint = false;
+ let hintWasShown = false;
+ let wrongSubmit = false;
+ let hinthandler = (e) => {
+ if (e.key == input.hintkey) showHint = !showHint;
+ if (showHint) hintWasShown = true;
+ m.redraw();
+ }
+ let newTest = () => {
+ test = training.getTest();
+ showHint = false;
+ hintWasShown = false;
+ wrongSubmit = false;
+ }
+ let oninput = (c) => {
+ if (test[0] == c) {
+ training.saveTest(test[0], !hintWasShown && !wrongSubmit);
+ newTest();
}
+ else wrongSubmit = true;
+ }
+ return {
+ oninit: () => {
+ newTest();
+ },
+ view: () => m("div",
+ m(".trainerTop",
+ m(".bigletter", {
+ class: wrongSubmit ? "red" : ""
+ }, m("span", fixSpace(test[0]))),
+ m(".hint", {
+ class: showHint ? "" : "invisible"
+ }, "Hint: " + test[1])
+ ),
+ m(".trainerBottom", m(keyBoxes, {
+ inputCallback: oninput,
+ keyCallback: hinthandler
+ }))
+ )
}
};
diff --git a/src/modules/input.js b/src/modules/input.js
@@ -1,5 +1,6 @@
// maybe add a configuration option for this later
const keys = ["j", "k", "l"];
+const hintkey = "h";
/* pressed is an array of pressed indizes from one gesture */
function pressed2gesture(pressed) {
@@ -44,6 +45,7 @@ function gestures2char(gestures) {
module.exports = {
keys,
+ hintkey,
pressed2gesture,
gestures2char
}
diff --git a/src/modules/training.js b/src/modules/training.js
@@ -0,0 +1,111 @@
+function unixtime() {
+ return (new Date).getTime();
+}
+
+function getData() {
+ let ls = localStorage.getItem("progress");
+ if (ls) {
+ return JSON.parse(ls);
+ }
+
+ /* sorted by frequency */
+ let chars = " -.etaonihsrlducmwyfgpbvkjxqz0123456789";
+ let hints = {
+ " ": "G7",
+ "-": "G8",
+ ".": "G9"
+ };
+ for (let i = 0; i < 36; i++) {
+ let hint = "G" + Math.floor(i / 6 + 1) + " G" + (i % 6 + 1);
+ let ch = i < 26 ?
+ String.fromCharCode("a".charCodeAt() + i) :
+ (i - 26) + "";
+ hints[ch] = hint;
+ }
+ let data = chars.split("").map(c => ({
+ c: c,
+ hint: hints[c],
+ numt: 0, /* how often this character was tested */
+ score: 0,
+ lastt: 0, /* timestamp of the last test */
+ }));
+ return data;
+}
+
+function setData(data) {
+ localStorage.setItem("progress", JSON.stringify(data));
+}
+
+
+function getMaxIndex(valueFn, list) {
+ let max;
+ let maxValue = 0;
+ for (let i = 0; i < list.length; i++) {
+ let v = valueFn(list[i]);
+ if (v != 0 && (!max || v > maxValue)) {
+ max = i;
+ maxValue = v;
+ }
+ }
+ return max;
+}
+
+/* basic idea:
+ * - remove previously tested character from the list of possible ones (so
+ * that no character is tested twice)
+ * - if there are chars older than a week, test the oldest
+ * - if there are chars with a score less than 20 test the one with minimal
+ * score
+ * - else test a new char
+ * - if all chars are known take the char with minimal score
+ *
+ * score:
+ * - starts at 0
+ * - test correct? +5
+ * - test incorrect? /2
+ * - showing hints counts as incorrect (maybe also set a time limit?)
+ *
+ * return value consists of the character and a hint
+ */
+function getTest() {
+ let data = getData();
+
+ let prev = getMaxIndex(e => e.lastt, data);
+ if (prev) {
+ data.splice(prev, 1);
+ }
+
+ let old = getMaxIndex(e => -e.lastt, data);
+ if (old && data[old].lastt < unixtime() - 1000 * 60 * 60 * 24 * 7) {
+ return [data[old].c, data[old].hint];
+ }
+
+ let known = data.filter(e => e.numt > 0).sort(
+ (a, b) => (a.score - b.score)
+ );
+ let unknown = data.filter(e => e.numt == 0);
+
+ // console.log(known);
+ // console.log(unknown);
+
+ if ((known.length != 0 && known[0].score < 20) || unknown.length == 0) {
+ return [known[0].c, known[0].hint];
+ }
+ return [unknown[0].c, unknown[0].hint];
+}
+
+function saveTest(c, success) {
+ // console.log(c, success);
+ let data = getData();
+ let elem = data.find(e => e.c == c);
+ elem.numt++;
+ elem.lastt = unixtime();
+ if (success) elem.score += 5;
+ else elem.score = Math.floor(elem.score / 2);
+ setData(data);
+}
+
+module.exports = {
+ getTest,
+ saveTest
+}
diff --git a/src/styles.css b/src/styles.css
@@ -27,6 +27,9 @@ h1 {
text-align: center;
line-height: 1.1em;
}
+.invisible {
+ display: none !important;
+}
/* LAYOUT */
@@ -85,3 +88,43 @@ h1 {
top: 0;
right: 0;
}
+
+/* input page */
+.inputTop {
+ white-space: pre;
+ width: 100%;
+ height: 40%;
+}
+.inputBottom {
+ width: 100%;
+ height: 60%;
+}
+
+/* trainer page */
+.trainerTop {
+ position: relative;
+ width: 100%;
+ height: 40%;
+}
+.trainerBottom {
+ width: 100%;
+ height: 60%;
+}
+.bigletter {
+ width: 100%;
+ height: 100%;
+ font-size: 200px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+.hint {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ text-align: center;
+}
+.red {
+ color: red;
+}