diff --git a/package.json b/package.json index 7821dc1..0f0ce9a 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "bootstrap": "4.5.3", "classnames": "^2.3.1", "cron": "^1.8.2", + "fast-deep-equal": "^3.1.3", "froebel": "^0.21.3", "lokijs": "^1.5.12", "markdown-to-jsx": "^7.1.3", diff --git a/src/games/games.tsx b/src/games/games.tsx index 28563f0..6a744db 100644 --- a/src/games/games.tsx +++ b/src/games/games.tsx @@ -5,6 +5,7 @@ import UnisexNounGame from "./sub-cores/UnisexNounGame"; import EquativeSituations from "./sub-cores/EquativeSituations"; import VerbSituations from "./sub-cores/VerbSituations"; import EquativeIdentify from "./sub-cores/EquativeIdentify"; +import VerbFormulas from "./sub-cores/VerbFormulas"; // NOUNS export const nounGenderGame1 = makeGameRecord({ @@ -270,6 +271,13 @@ export const verbSituationsGame = makeGameRecord({ level: "situations", SubCore: VerbSituations, }); +export const verbFormulasGame = makeGameRecord({ + title: "Choose the verb tense formula", + id: "verb-tense-formulas", + link: "/verbs/master-chart/", + level: "all", + SubCore: VerbFormulas, +}); const games: { chapter: string, items: GameRecord[] }[] = [ { @@ -322,6 +330,7 @@ const games: { chapter: string, items: GameRecord[] }[] = [ allVerbGame1, allVerbGame2, verbSituationsGame, + verbFormulasGame, ], }, ]; diff --git a/src/games/sub-cores/VerbFormulas.tsx b/src/games/sub-cores/VerbFormulas.tsx new file mode 100644 index 0000000..fdcaa56 --- /dev/null +++ b/src/games/sub-cores/VerbFormulas.tsx @@ -0,0 +1,285 @@ +import GameCore from "../GameCore"; +import { + humanReadableVerbForm, + Types as T, + InlinePs, + grammarUnits, + defaultTextOptions as opts, +} from "@lingdocs/pashto-inflector"; +import { makePool } from "../../lib/pool"; +import { CSSProperties, useEffect, useState } from "react"; +import classNames from "classnames"; + +const amount = 10; +const timeLimit = 60; + +type StemRoot = "imperfective stem" | "perfective stem" | "imperfective root" | "perfective root"; +type Ending = "present" | "past" | "imperative"; +type Formula = { + ba: boolean, + stemRoot: StemRoot, + ending: Ending, +}; + +type Question = { + tense: T.VerbTense, + formula: Formula, +} + +const questions: Question[] = [ + { + tense: "presentVerb", + formula: { + ba: false, + stemRoot: "imperfective stem", + ending: "present", + }, + }, + { + tense: "subjunctiveVerb", + formula: { + ba: false, + stemRoot: "perfective stem", + ending: "present", + }, + }, + { + tense: "imperfectiveFuture", + formula: { + ba: true, + stemRoot: "imperfective stem", + ending: "present", + }, + }, + { + tense: "perfectiveFuture", + formula: { + ba: true, + stemRoot: "perfective stem", + ending: "present", + }, + }, + { + tense: "imperfectivePast", + formula: { + ba: false, + stemRoot: "imperfective root", + ending: "past", + }, + }, + { + tense: "perfectivePast", + formula: { + ba: false, + stemRoot: "perfective root", + ending: "past", + }, + }, + { + tense: "habitualImperfectivePast", + formula: { + ba: true, + stemRoot: "imperfective root", + ending: "past", + }, + }, + { + tense: "habitualPerfectivePast", + formula: { + ba: true, + stemRoot: "perfective root", + ending: "past", + }, + }, +]; + +export default function VerbFormulas({ inChapter, id, link, level }: { inChapter: boolean, id: string, link: string, level: "all" }) { + const questionsPool = makePool(questions); + function getQuestion() { + return questionsPool(); + } + function Instructions() { + return

Pick the formula for each verb tense

; + } + + return +}; + +function Display({ question, callback }: QuestionDisplayProps) { + const [ba, setBa] = useState(false); + const [stemRoot, setStemRoot] = useState(""); + const [ending, setEnding] = useState(""); + useEffect(() => { + setBa(false); + setStemRoot(""); + setEnding(""); + }, [question]); + const canSubmit = !!(stemRoot && ending); + function handleSubmit() { + const { formula } = question; + callback( + (ba === formula.ba) + && + (stemRoot === formula.stemRoot) + && + (ending === formula.ending) + ); + } + return
+
+

+ {humanReadableVerbForm(question.tense)} +

+
+
+ + + + +
+ {canSubmit ? printFormula({ ba, stemRoot, ending }) : " "} +
+} + +function printFormula(f: Formula): string { + return `${f.ba ? "ba + " : ""}${f.stemRoot} + ${f.ending} ending`; +} + +function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element { + return
+ {printFormula(question.formula)} +
; +} + +function EndingPicker({ onChange, ending }: { ending: Ending | "", onChange: (e: Ending | "") => void }) { + const options: { label: string, value: Ending }[] = [ + { label: "Present", value: "present" }, + { label: "Past", value: "past" }, + { label: "Imperative", value: "imperative" }, + ]; + function handleClick(e: Ending) { + // onChange(ending === e ? "" : e); + onChange(e); + } + return
+ Ending: +
+ {options.map((option) => ( + + ))} +
+
; +} + +function BaPicker({ onChange, hasBa }: { hasBa: boolean, onChange: (b: boolean) => void }) { + return
+ onChange(e.target.checked)} + /> + +
+} + +function RootsAndStemsPicker({ onChange, stemRoot }: { stemRoot: StemRoot | "", onChange: (s: StemRoot | "" ) => void }) { + const colClass = "col col-md-5 px-0 mb-1"; + const rowClass = "row justify-content-between"; + const title: CSSProperties = { + fontWeight: "bolder", + paddingBottom: "1.75rem", + paddingTop: "1.75rem", + }; + const highlight = { + background: "rgba(255, 227, 10, 0.6)", + }; + function handleStemRootClick(s: StemRoot) { + onChange(stemRoot === s ? "" : s); + } + return
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
handleStemRootClick("imperfective stem")}> +
Imperfective Stem
+
+
+
+
handleStemRootClick("perfective stem")}> +
Perfective Stem
+
+
+
+
+
+
handleStemRootClick("imperfective root")}> +
Imperfective Root
+
+
+
+
+
handleStemRootClick("perfective root")}> +
Perfective Root
+
+
+
+
+
+
; +} + diff --git a/src/lib/pool.ts b/src/lib/pool.tsx similarity index 88% rename from src/lib/pool.ts rename to src/lib/pool.tsx index 11c365a..2dc3248 100644 --- a/src/lib/pool.ts +++ b/src/lib/pool.tsx @@ -1,4 +1,5 @@ import { randFromArray } from "@lingdocs/pashto-inflector"; +import equal from "fast-deep-equal"; /** * @@ -22,7 +23,7 @@ export function makePool

(poolBase: P[], removalLaxity = 0): () => P { if (shouldStillKeepIt()) { return pick; } - const index = pool.findIndex(v => matches(v, pick)) + const index = pool.findIndex(v => equal(v, pick)) if (index === -1) throw new Error("could not find pick from pool"); pool.splice(index, 1); // If the pool is empty, reset it @@ -34,8 +35,3 @@ export function makePool

(poolBase: P[], removalLaxity = 0): () => P { return pickRandomFromPool; } -function matches(a: unknown, b: unknown): boolean { - return JSON.stringify(a) === JSON.stringify(b); -} - - diff --git a/yarn.lock b/yarn.lock index b42431e..fcb9010 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5648,7 +5648,7 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==