verb formulas game

This commit is contained in:
lingdocs 2022-09-10 15:42:09 +04:00
parent 6eab368245
commit b0e1789914
2 changed files with 97 additions and 21 deletions

View File

@ -4,6 +4,7 @@ title: Verb Forms Master Chart
import { Camera, Video } from "../../components/terms-links"; import { Camera, Video } from "../../components/terms-links";
import Link from "../../components/Link"; import Link from "../../components/Link";
import GameDisplay from "../../games/GameDisplay";
## Basic Verb Tenses ## Basic Verb Tenses

View File

@ -9,20 +9,22 @@ import {
import { makePool } from "../../lib/pool"; import { makePool } from "../../lib/pool";
import { CSSProperties, useEffect, useState } from "react"; import { CSSProperties, useEffect, useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { isImperativeTense } from "@lingdocs/pashto-inflector/dist/lib/type-predicates";
const amount = 10; const amount = 12;
const timeLimit = 60; const timeLimit = 125;
type StemRoot = "imperfective stem" | "perfective stem" | "imperfective root" | "perfective root"; type StemRoot = "imperfective stem" | "perfective stem" | "imperfective root" | "perfective root";
type Ending = "present" | "past" | "imperative"; type Ending = "present" | "past" | "imperative";
type Formula = { type Formula = {
ba: boolean, ba: boolean,
mu: boolean,
stemRoot: StemRoot, stemRoot: StemRoot,
ending: Ending, ending: Ending,
}; };
type Question = { type Question = {
tense: T.VerbTense, tense: T.VerbTense | T.ImperativeTense | "negative imperative",
formula: Formula, formula: Formula,
} }
@ -31,6 +33,7 @@ const questions: Question[] = [
tense: "presentVerb", tense: "presentVerb",
formula: { formula: {
ba: false, ba: false,
mu: false,
stemRoot: "imperfective stem", stemRoot: "imperfective stem",
ending: "present", ending: "present",
}, },
@ -39,6 +42,7 @@ const questions: Question[] = [
tense: "subjunctiveVerb", tense: "subjunctiveVerb",
formula: { formula: {
ba: false, ba: false,
mu: false,
stemRoot: "perfective stem", stemRoot: "perfective stem",
ending: "present", ending: "present",
}, },
@ -47,6 +51,7 @@ const questions: Question[] = [
tense: "imperfectiveFuture", tense: "imperfectiveFuture",
formula: { formula: {
ba: true, ba: true,
mu: false,
stemRoot: "imperfective stem", stemRoot: "imperfective stem",
ending: "present", ending: "present",
}, },
@ -55,6 +60,7 @@ const questions: Question[] = [
tense: "perfectiveFuture", tense: "perfectiveFuture",
formula: { formula: {
ba: true, ba: true,
mu: false,
stemRoot: "perfective stem", stemRoot: "perfective stem",
ending: "present", ending: "present",
}, },
@ -63,6 +69,7 @@ const questions: Question[] = [
tense: "imperfectivePast", tense: "imperfectivePast",
formula: { formula: {
ba: false, ba: false,
mu: false,
stemRoot: "imperfective root", stemRoot: "imperfective root",
ending: "past", ending: "past",
}, },
@ -71,6 +78,7 @@ const questions: Question[] = [
tense: "perfectivePast", tense: "perfectivePast",
formula: { formula: {
ba: false, ba: false,
mu: false,
stemRoot: "perfective root", stemRoot: "perfective root",
ending: "past", ending: "past",
}, },
@ -79,6 +87,7 @@ const questions: Question[] = [
tense: "habitualImperfectivePast", tense: "habitualImperfectivePast",
formula: { formula: {
ba: true, ba: true,
mu: false,
stemRoot: "imperfective root", stemRoot: "imperfective root",
ending: "past", ending: "past",
}, },
@ -87,10 +96,38 @@ const questions: Question[] = [
tense: "habitualPerfectivePast", tense: "habitualPerfectivePast",
formula: { formula: {
ba: true, ba: true,
mu: false,
stemRoot: "perfective root", stemRoot: "perfective root",
ending: "past", ending: "past",
}, },
}, },
{
tense: "perfectiveImperative",
formula: {
ba: false,
mu: false,
stemRoot: "perfective stem",
ending: "imperative",
},
},
{
tense: "imperfectiveImperative",
formula: {
ba: false,
mu: false,
stemRoot: "imperfective stem",
ending: "imperative",
},
},
{
tense: "negative imperative",
formula: {
ba: false,
mu: true,
stemRoot: "imperfective stem",
ending: "imperative",
},
}
]; ];
export default function VerbFormulas({ inChapter, id, link, level }: { inChapter: boolean, id: string, link: string, level: "all" }) { export default function VerbFormulas({ inChapter, id, link, level }: { inChapter: boolean, id: string, link: string, level: "all" }) {
@ -101,7 +138,6 @@ export default function VerbFormulas({ inChapter, id, link, level }: { inChapter
function Instructions() { function Instructions() {
return <p className="lead">Pick the formula for each verb tense</p>; return <p className="lead">Pick the formula for each verb tense</p>;
} }
return <GameCore return <GameCore
inChapter={inChapter} inChapter={inChapter}
studyLink={link} studyLink={link}
@ -117,19 +153,24 @@ export default function VerbFormulas({ inChapter, id, link, level }: { inChapter
function Display({ question, callback }: QuestionDisplayProps<Question>) { function Display({ question, callback }: QuestionDisplayProps<Question>) {
const [ba, setBa] = useState<boolean>(false); const [ba, setBa] = useState<boolean>(false);
const [mu, setMu] = useState<boolean>(false);
const [stemRoot, setStemRoot] = useState<StemRoot | "">(""); const [stemRoot, setStemRoot] = useState<StemRoot | "">("");
const [ending, setEnding] = useState<Ending | "">(""); const [ending, setEnding] = useState<Ending | "">("");
useEffect(() => { useEffect(() => {
setBa(false); setBa(false);
setStemRoot(""); setStemRoot("");
setEnding(""); setEnding("");
setMu(false);
}, [question]); }, [question]);
const canSubmit = !!(stemRoot && ending); const canSubmit = !!(stemRoot && ending);
const showMu = question.tense === "negative imperative" || isImperativeTense(question.tense);
function handleSubmit() { function handleSubmit() {
const { formula } = question; const { formula } = question;
callback( callback(
(ba === formula.ba) (ba === formula.ba)
&& &&
(mu === formula.mu)
&&
(stemRoot === formula.stemRoot) (stemRoot === formula.stemRoot)
&& &&
(ending === formula.ending) (ending === formula.ending)
@ -138,13 +179,14 @@ function Display({ question, callback }: QuestionDisplayProps<Question>) {
return <div> return <div>
<div className="my-4" style={{ maxWidth: "300px", margin: "0 auto" }}> <div className="my-4" style={{ maxWidth: "300px", margin: "0 auto" }}>
<p className="lead"> <p className="lead">
{humanReadableVerbForm(question.tense)} {humanReadableT(question.tense)}
</p> </p>
</div> </div>
<div className="text-center mb-2"> <div className="text-center mb-2">
<BaPicker hasBa={ba} onChange={setBa} /> <BaPicker value={ba} onChange={setBa} />
<RootsAndStemsPicker stemRoot={stemRoot} onChange={setStemRoot} /> {showMu && <MuPicker value={mu} onChange={setMu} />}
<EndingPicker ending={ending} onChange={setEnding} /> <RootsAndStemsPicker value={stemRoot} onChange={setStemRoot} />
<EndingPicker value={ending} onChange={setEnding} />
<button <button
className="btn btn-primary my-2" className="btn btn-primary my-2"
disabled={!canSubmit} disabled={!canSubmit}
@ -153,12 +195,30 @@ function Display({ question, callback }: QuestionDisplayProps<Question>) {
Submit Submit
</button> </button>
</div> </div>
<samp>{canSubmit ? printFormula({ ba, stemRoot, ending }) : " "}</samp> <samp>{printFormula({ ba, stemRoot, ending, mu })}</samp>
</div> </div>
} }
function printFormula(f: Formula): string { function humanReadableT(tense: T.VerbTense | T.ImperativeTense | "negative imperative"): string {
return `${f.ba ? "ba + " : ""}${f.stemRoot} + ${f.ending} ending`; if (tense === "negative imperative") {
return tense;
}
return humanReadableVerbForm(tense);
}
function printFormula({ mu, ba, stemRoot, ending }: {
mu: boolean,
ba: boolean,
stemRoot: StemRoot | "",
ending: Ending | "",
}): string {
const elements = [
mu ? "mu" : undefined,
ba ? "ba" : undefined,
stemRoot,
ending ? `${ending} ending` : undefined,
].filter(x => x) as string[];
return elements.join(" + ");
} }
function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element { function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element {
@ -167,7 +227,7 @@ function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element
</div>; </div>;
} }
function EndingPicker({ onChange, ending }: { ending: Ending | "", onChange: (e: Ending | "") => void }) { function EndingPicker({ onChange, value }: { value: Ending | "", onChange: (e: Ending | "") => void }) {
const options: { label: string, value: Ending }[] = [ const options: { label: string, value: Ending }[] = [
{ label: "Present", value: "present" }, { label: "Present", value: "present" },
{ label: "Past", value: "past" }, { label: "Past", value: "past" },
@ -187,7 +247,7 @@ function EndingPicker({ onChange, ending }: { ending: Ending | "", onChange: (e:
className={classNames( className={classNames(
"btn", "btn",
"btn-outline-secondary", "btn-outline-secondary",
{ active: ending === option.value }, { active: value === option.value },
)} )}
onClick={() => handleClick(option.value)} onClick={() => handleClick(option.value)}
> >
@ -198,13 +258,13 @@ function EndingPicker({ onChange, ending }: { ending: Ending | "", onChange: (e:
</div>; </div>;
} }
function BaPicker({ onChange, hasBa }: { hasBa: boolean, onChange: (b: boolean) => void }) { function BaPicker({ onChange, value }: { value: boolean, onChange: (b: boolean) => void }) {
return <div className="form-check my-3" style={{ fontSize: "larger" }}> return <div className="form-check my-3" style={{ fontSize: "larger" }}>
<input <input
id="baCheckbox" id="baCheckbox"
className="form-check-input" className="form-check-input"
type="checkbox" type="checkbox"
checked={hasBa} checked={value}
onChange={e => onChange(e.target.checked)} onChange={e => onChange(e.target.checked)}
/> />
<label className="form-check-label text-muted" htmlFor="baCheckbox"> <label className="form-check-label text-muted" htmlFor="baCheckbox">
@ -213,7 +273,22 @@ function BaPicker({ onChange, hasBa }: { hasBa: boolean, onChange: (b: boolean)
</div> </div>
} }
function RootsAndStemsPicker({ onChange, stemRoot }: { stemRoot: StemRoot | "", onChange: (s: StemRoot | "" ) => void }) { function MuPicker({ onChange, value }: { value: boolean, onChange: (b: boolean) => void }) {
return <div className="form-check my-3" style={{ fontSize: "larger" }}>
<input
id="baCheckbox"
className="form-check-input"
type="checkbox"
checked={value}
onChange={e => onChange(e.target.checked)}
/>
<label className="form-check-label text-muted" htmlFor="baCheckbox">
with <InlinePs opts={opts}>{{ p: "مه", f: "mú" }}</InlinePs>
</label>
</div>
}
function RootsAndStemsPicker({ onChange, value }: { value: StemRoot | "", onChange: (s: StemRoot | "" ) => void }) {
const colClass = "col col-md-5 px-0 mb-1"; const colClass = "col col-md-5 px-0 mb-1";
const rowClass = "row justify-content-between"; const rowClass = "row justify-content-between";
const title: CSSProperties = { const title: CSSProperties = {
@ -225,7 +300,7 @@ function RootsAndStemsPicker({ onChange, stemRoot }: { stemRoot: StemRoot | "",
background: "rgba(255, 227, 10, 0.6)", background: "rgba(255, 227, 10, 0.6)",
}; };
function handleStemRootClick(s: StemRoot) { function handleStemRootClick(s: StemRoot) {
onChange(stemRoot === s ? "" : s); onChange(value === s ? "" : s);
} }
return <div className="verb-info" style={{ return <div className="verb-info" style={{
textAlign: "center", textAlign: "center",
@ -254,24 +329,24 @@ function RootsAndStemsPicker({ onChange, stemRoot }: { stemRoot: StemRoot | "",
</div> </div>
</div> </div>
<div className={rowClass}> <div className={rowClass}>
<div className={colClass} style={stemRoot === "imperfective stem" ? highlight : {}}> <div className={colClass} style={value === "imperfective stem" ? highlight : {}}>
<div className="clickable" style={title} onClick={() => handleStemRootClick("imperfective stem")}> <div className="clickable" style={title} onClick={() => handleStemRootClick("imperfective stem")}>
<div>Imperfective Stem</div> <div>Imperfective Stem</div>
</div> </div>
</div> </div>
<div className={colClass} style={stemRoot === "perfective stem" ? highlight : {}}> <div className={colClass} style={value === "perfective stem" ? highlight : {}}>
<div className="clickable" style={title} onClick={() => handleStemRootClick("perfective stem")}> <div className="clickable" style={title} onClick={() => handleStemRootClick("perfective stem")}>
<div>Perfective Stem</div> <div>Perfective Stem</div>
</div> </div>
</div> </div>
</div> </div>
<div className={rowClass}> <div className={rowClass}>
<div className={colClass} style={stemRoot === "imperfective root" ? highlight : {}}> <div className={colClass} style={value === "imperfective root" ? highlight : {}}>
<div className="clickable" style={title} onClick={() => handleStemRootClick("imperfective root")}> <div className="clickable" style={title} onClick={() => handleStemRootClick("imperfective root")}>
<div>Imperfective Root</div> <div>Imperfective Root</div>
</div> </div>
</div> </div>
<div className={colClass} style={stemRoot === "perfective root" ? highlight : {}}> <div className={colClass} style={value === "perfective root" ? highlight : {}}>
<div> <div>
<div className="clickable" style={title} onClick={() => handleStemRootClick("perfective root")}> <div className="clickable" style={title} onClick={() => handleStemRootClick("perfective root")}>
<div>Perfective Root</div> <div>Perfective Root</div>