parent
d9bd748354
commit
55512ce788
|
@ -7,7 +7,7 @@
|
|||
"@formkit/auto-animate": "^1.0.0-beta.1",
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
"@lingdocs/lingdocs-main": "^0.3.1",
|
||||
"@lingdocs/pashto-inflector": "^3.7.3",
|
||||
"@lingdocs/pashto-inflector": "^3.7.6",
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import EquativeGame from "./sub-cores/EquativeGame";
|
||||
import VerbGame from "./sub-cores/VerbGame";
|
||||
import GenderGame from "./sub-cores/GenderGame";
|
||||
import UnisexNounGame from "./sub-cores/UnisexNounGame";
|
||||
|
||||
|
@ -16,6 +17,13 @@ function makeGameRecord(
|
|||
}
|
||||
}
|
||||
|
||||
export const presentVerbGame = makeGameRecord(
|
||||
"Write the present verb",
|
||||
"present-verbs-write",
|
||||
"/verbs/present-verbs/",
|
||||
(id, link) => () => <VerbGame id={id} level="presentVerb" link={link} />
|
||||
)
|
||||
|
||||
export const nounGenderGame1 = makeGameRecord(
|
||||
"Identify Noun Genders - Level 1",
|
||||
"gender-nouns-1",
|
||||
|
@ -137,6 +145,12 @@ const games: { chapter: string, items: GameRecord[] }[] = [
|
|||
equativeGameAllProduce,
|
||||
],
|
||||
},
|
||||
{
|
||||
chapter: "Verbs",
|
||||
items: [
|
||||
presentVerbGame,
|
||||
],
|
||||
}
|
||||
];
|
||||
|
||||
export default games;
|
||||
|
|
|
@ -0,0 +1,323 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
comparePs,
|
||||
makeProgress,
|
||||
} from "../../lib/game-utils";
|
||||
import GameCore from "../GameCore";
|
||||
import {
|
||||
Types as T,
|
||||
Examples,
|
||||
defaultTextOptions as opts,
|
||||
typePredicates as tp,
|
||||
makeNounSelection,
|
||||
randFromArray,
|
||||
renderEP,
|
||||
compileEP,
|
||||
flattenLengths,
|
||||
randomPerson,
|
||||
InlinePs,
|
||||
grammarUnits,
|
||||
randomSubjObj,
|
||||
renderVP,
|
||||
makeVPSelectionState,
|
||||
compileVP,
|
||||
blockUtils,
|
||||
concatPsString,
|
||||
} from "@lingdocs/pashto-inflector";
|
||||
import { basicVerbs, intransitivePast } from "../../content/verbs/basic-present-verbs";
|
||||
import { psStringEquals } from "@lingdocs/pashto-inflector/dist/lib/p-text-helpers";
|
||||
|
||||
const kidsColor = "#017BFE";
|
||||
|
||||
const amount = 10;
|
||||
const timeLimit = 80;
|
||||
|
||||
type Question = {
|
||||
rendered: T.VPRendered,
|
||||
phrase: { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] },
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const nouns: T.NounEntry[] = [
|
||||
{"ts":1527815251,"i":7790,"p":"سړی","f":"saRéy","g":"saRey","e":"man","c":"n. m.","ec":"man","ep":"men"},
|
||||
{"ts":1527812797,"i":8605,"p":"ښځه","f":"xúdza","g":"xudza","e":"woman, wife","c":"n. f.","ec":"woman","ep":"women"},
|
||||
{"ts":1527812881,"i":11691,"p":"ماشوم","f":"maashoom","g":"maashoom","e":"child, kid","c":"n. m. anim. unisex","ec":"child","ep":"children"},
|
||||
{"ts":1527815197,"i":2503,"p":"پښتون","f":"puxtoon","g":"puxtoon","e":"Pashtun","c":"n. m. anim. unisex / adj.","infap":"پښتانه","infaf":"puxtaanu","infbp":"پښتن","infbf":"puxtan"},
|
||||
{"ts":1527815737,"i":484,"p":"استاذ","f":"Ustaaz","g":"Ustaaz","e":"teacher, professor, expert, master (in a field)","c":"n. m. anim. unisex anim.","ec":"teacher"},
|
||||
{"ts":1527816747,"i":6418,"p":"ډاکټر","f":"DaakTar","g":"DaakTar","e":"doctor","c":"n. m."},
|
||||
{"ts":1527812661,"i":13938,"p":"هلک","f":"halík, halúk","g":"halik,haluk","e":"boy, young lad","c":"n. m. anim."},
|
||||
].filter(tp.isNounEntry);
|
||||
|
||||
const pronounTypes = [
|
||||
[T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
[T.Person.SecondSingMale, T.Person.SecondSingFemale],
|
||||
[T.Person.ThirdSingMale],
|
||||
[T.Person.ThirdSingFemale],
|
||||
[T.Person.FirstPlurMale, T.Person.FirstPlurFemale],
|
||||
[T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
|
||||
[T.Person.ThirdPlurMale, T.Person.ThirdPlurFemale],
|
||||
];
|
||||
|
||||
export default function VerbGame({ id, link, level }: { id: string, link: string, level: T.VerbTense }) {
|
||||
function* questions (): Generator<Current<Question>> {
|
||||
let pool = [...pronounTypes];
|
||||
function makeRandPronoun(): T.PronounSelection {
|
||||
let person: T.Person;
|
||||
do {
|
||||
person = randomPerson();
|
||||
// eslint-disable-next-line
|
||||
} while (!pool.some(p => p.includes(person)));
|
||||
pool = pool.filter(p => !p.includes(person));
|
||||
if (pool.length === 0) {
|
||||
pool = pronounTypes;
|
||||
}
|
||||
return {
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
person,
|
||||
};
|
||||
}
|
||||
function makeRandomNoun(): T.NounSelection {
|
||||
const n = makeNounSelection(randFromArray(nouns), undefined);
|
||||
return {
|
||||
...n,
|
||||
gender: n.genderCanChange ? randFromArray(["masc", "fem"]) : n.gender,
|
||||
number: n.numberCanChange ? randFromArray(["singular", "plural"]) : n.number,
|
||||
};
|
||||
}
|
||||
function makeRandomVPS(l: T.VerbTense): T.VPSelectionComplete {
|
||||
function makePronoun(p: T.Person): T.PronounSelection {
|
||||
return {
|
||||
type: "pronoun",
|
||||
person: p,
|
||||
distance: "far",
|
||||
};
|
||||
}
|
||||
function makeNPSelection(pr: T.PronounSelection): T.NPSelection {
|
||||
return {
|
||||
type: "NP",
|
||||
selection: pr,
|
||||
};
|
||||
}
|
||||
const verb = randFromArray(basicVerbs);
|
||||
const { subj, obj } = randomSubjObj();
|
||||
// const subj: T.NPSelection = {
|
||||
// type: "NP",
|
||||
// selection: randFromArray([
|
||||
// makeRandPronoun,
|
||||
// makeRandPronoun,
|
||||
// makeRandomNoun,
|
||||
// makeRandPronoun,
|
||||
// ])(),
|
||||
// };
|
||||
// const tense = (l === "allIdentify" || l === "allProduce")
|
||||
// ? randFromArray(tenses)
|
||||
// : l;
|
||||
const tense = l;
|
||||
return makeVPS({
|
||||
verb,
|
||||
subject: makeNPSelection(makePronoun(subj)),
|
||||
object: makeNPSelection(makePronoun(obj)),
|
||||
tense,
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < amount; i++) {
|
||||
const VPS = makeRandomVPS("presentVerb");
|
||||
const VP = renderVP(VPS);
|
||||
const compiled = compileVP(
|
||||
VP,
|
||||
{ removeKing: false, shrinkServant: false },
|
||||
true,
|
||||
"verb",
|
||||
);
|
||||
const phrase = {
|
||||
ps: compiled.ps,
|
||||
e: compiled.e,
|
||||
};
|
||||
yield {
|
||||
progress: makeProgress(i, amount),
|
||||
question: {
|
||||
rendered: VP,
|
||||
phrase,
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function Display({ question, callback }: QuestionDisplayProps<Question>) {
|
||||
const [answer, setAnswer] = useState<string>("");
|
||||
const [withBa, setWithBa] = useState<boolean>(false);
|
||||
const handleInput = ({ target: { value }}: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAnswer(value);
|
||||
}
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
if ("situation" in question) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
const correct = comparePs(answer, getVerbPs(question.rendered))
|
||||
&& (withBa === verbHasBa(question.rendered));
|
||||
if (correct) {
|
||||
setAnswer("");
|
||||
}
|
||||
callback(!correct ? makeCorrectAnswer(question) : true);
|
||||
}
|
||||
// useEffect(() => {
|
||||
// if (level === "allProduce") setWithBa(false);
|
||||
// }, [question]);
|
||||
return <div>
|
||||
<QuestionDisplay question={question} />
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-check mt-1">
|
||||
<input
|
||||
id="baCheckbox"
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={withBa}
|
||||
onChange={e => setWithBa(e.target.checked)}
|
||||
/>
|
||||
<label className="form-check-label text-muted" htmlFor="baCheckbox">
|
||||
with <InlinePs opts={opts}>{grammarUnits.baParticle}</InlinePs> in the <span style={{ color: kidsColor }}>kids' section</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="my-1" style={{ maxWidth: "200px", margin: "0 auto" }}>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
autoComplete="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
dir="auto"
|
||||
value={answer}
|
||||
onChange={handleInput}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-center my-2">
|
||||
{/* <div> */}
|
||||
<button className="btn btn-primary" type="submit">return ↵</button>
|
||||
{/* </div> */}
|
||||
{/* <div className="text-muted small text-center mt-2">
|
||||
Type <kbd>Enter</kbd> to check
|
||||
</div> */}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
|
||||
function Instructions() {
|
||||
return <div>
|
||||
<p className="lead">Write the present verb to complete the phrase</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
return <GameCore
|
||||
studyLink={link}
|
||||
questions={questions}
|
||||
id={id}
|
||||
Display={Display}
|
||||
timeLimit={timeLimit}
|
||||
Instructions={Instructions}
|
||||
/>
|
||||
};
|
||||
|
||||
function QuestionDisplay(question: Question) {
|
||||
const ps = flattenLengths(question.phrase.ps)[0];
|
||||
return <div>
|
||||
<div>{ps.p}</div>
|
||||
<div>{ps.f}</div>
|
||||
{question.phrase.e && <div>
|
||||
{question.phrase.e.map(x => <div key={Math.random()}>
|
||||
{x}
|
||||
</div>)}
|
||||
</div>}
|
||||
</div>;
|
||||
}
|
||||
|
||||
function makeCorrectAnswer(question: Question): JSX.Element {
|
||||
return <div>
|
||||
<div>
|
||||
{getVerbPs(question.rendered).reduce(((accum, curr, i): JSX.Element[] => (
|
||||
[
|
||||
...accum,
|
||||
...i > 0 ? [<span className="text-muted"> or </span>] : [],
|
||||
<span>{curr.p}</span>,
|
||||
]
|
||||
)), [] as JSX.Element[])}
|
||||
</div>
|
||||
<div><strong>{verbHasBa(question.rendered) ? "with" : "without"}</strong> a <InlinePs opts={opts}>{grammarUnits.baParticle}</InlinePs> in the kids' section.</div>
|
||||
</div>;
|
||||
}
|
||||
function modExs(exs: T.PsString[], withBa: boolean): { p: JSX.Element, f: JSX.Element }[] {
|
||||
return exs.map(ps => {
|
||||
if (!ps.p.includes(" ___ ")) {
|
||||
return {
|
||||
p: <>{ps.p}</>,
|
||||
f: <>{ps.f}</>,
|
||||
};
|
||||
}
|
||||
const splitP = ps.p.split(" ___ ");
|
||||
const splitF = ps.f.split(" ___ ");
|
||||
return {
|
||||
p: <>{splitP[0]} <span style={{ color: kidsColor }}>{withBa ? "به" : "__"}</span> {splitP[1]}</>,
|
||||
f: <>{splitF[0]} <span style={{ color: kidsColor }}>{withBa ? "ba" : "__"}</span> {splitF[1]}</>,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function humanReadableTense(tense: T.EquativeTense | "allProduce"): string {
|
||||
return tense === "allProduce"
|
||||
? ""
|
||||
: tense === "pastSubjunctive"
|
||||
? "past subjunctive"
|
||||
: tense === "wouldBe"
|
||||
? `"would be"`
|
||||
: tense === "wouldHaveBeen"
|
||||
? `"would have been"`
|
||||
: tense;
|
||||
}
|
||||
|
||||
function makeVPS({ verb, subject, object, tense }: {
|
||||
verb: T.VerbEntry,
|
||||
subject: T.NPSelection,
|
||||
object: T.NPSelection,
|
||||
tense: T.VerbTense,
|
||||
}): T.VPSelectionComplete {
|
||||
const vps = makeVPSelectionState(verb);
|
||||
return {
|
||||
...vps,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
tense,
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
key: Math.random(),
|
||||
block: {
|
||||
type: "subjectSelection",
|
||||
selection: subject,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: Math.random(),
|
||||
block: {
|
||||
type: "objectSelection",
|
||||
selection: object,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function getVerbPs({ blocks }: T.VPRendered): T.PsString[] {
|
||||
const { perfectiveHead, verb } = blockUtils.getVerbAndHeadFromBlocks(blocks);
|
||||
if (!perfectiveHead) {
|
||||
return flattenLengths(verb.block.ps);
|
||||
}
|
||||
return flattenLengths(verb.block.ps).map(r => concatPsString(perfectiveHead.ps, r));
|
||||
}
|
||||
|
||||
function verbHasBa({ blocks }: T.VPRendered): boolean {
|
||||
const verb = blockUtils.getVerbFromBlocks(blocks);
|
||||
return verb.block.hasBa;
|
||||
}
|
|
@ -1803,10 +1803,10 @@
|
|||
rambda "^6.7.0"
|
||||
react-select "^5.2.2"
|
||||
|
||||
"@lingdocs/pashto-inflector@^3.6.8":
|
||||
version "3.6.8"
|
||||
resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-3.6.8.tgz#8e6cde1730d96458117f4efda9874b7ced329c36"
|
||||
integrity sha512-wgS8AGad1fa+T4rEQRmbP9iV1jyRfLaT18fnFr1WjF4RC/B+U6Imr5ykKXV2pMQd/6yVi26yJFd8iFvRe3KybQ==
|
||||
"@lingdocs/pashto-inflector@^3.7.5":
|
||||
version "3.7.5"
|
||||
resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-3.7.5.tgz#6421b88ccf544cdbc7f4cefc66ff75b3b7f1bb44"
|
||||
integrity sha512-36ULd7ETZBz9/gs2K1s2XJFzEA3kDZfR9rPPN6B5oJvW9yAZXfgaJg9W2jzhodfu2WS3lpQvizis8zQjPioV8w==
|
||||
dependencies:
|
||||
"@formkit/auto-animate" "^1.0.0-beta.1"
|
||||
classnames "^2.2.6"
|
||||
|
|
Loading…
Reference in New Issue