really basic present and subjunctive verb games

This commit is contained in:
lingdocs 2022-08-24 16:54:34 +04:00
parent 55512ce788
commit 1c3f9c2f62
7 changed files with 122 additions and 75 deletions

View File

@ -7,7 +7,7 @@
"@formkit/auto-animate": "^1.0.0-beta.1", "@formkit/auto-animate": "^1.0.0-beta.1",
"@fortawesome/fontawesome-free": "^5.15.4", "@fortawesome/fontawesome-free": "^5.15.4",
"@lingdocs/lingdocs-main": "^0.3.1", "@lingdocs/lingdocs-main": "^0.3.1",
"@lingdocs/pashto-inflector": "^3.7.6", "@lingdocs/pashto-inflector": "^3.7.9",
"@testing-library/jest-dom": "^5.11.4", "@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0", "@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10", "@testing-library/user-event": "^12.1.10",

View File

@ -13,6 +13,10 @@ import Formula from "../../components/formula/Formula";
import realityGraph from "./reality-graph.svg"; import realityGraph from "./reality-graph.svg";
import presentTime from "./present-time.svg"; import presentTime from "./present-time.svg";
import BasicVerbShowCase from "./../../components/BasicVerbShowCase"; import BasicVerbShowCase from "./../../components/BasicVerbShowCase";
import {
presentVerbGame,
} from "../../games/games";
import GameDisplay from "../../games/GameDisplay";
The first verb form we'll learn is the **present**. This will be the first tool in our toolbox of verb forms. 🧰 With each verb form we'll learn two things: The first verb form we'll learn is the **present**. This will be the first tool in our toolbox of verb forms. 🧰 With each verb form we'll learn two things:
@ -118,4 +122,6 @@ Here are some examples of how the present form is used in different situations:
}, },
])}</Examples> ])}</Examples>
<GameDisplay record={presentVerbGame} />

View File

@ -15,6 +15,10 @@ import Formula from "../../components/formula/Formula";
import presentInReality from "./present-in-reality.svg"; import presentInReality from "./present-in-reality.svg";
import subjunctiveAboveReality from "./subjunctive-above-reality.svg"; import subjunctiveAboveReality from "./subjunctive-above-reality.svg";
import BasicVerbShowCase from "../../components/BasicVerbShowCase"; import BasicVerbShowCase from "../../components/BasicVerbShowCase";
import {
subjunctiveVerbGame,
} from "../../games/games";
import GameDisplay from "../../games/GameDisplay";
The **subjunctive** is a very important verb form in Pashto, but it's often ignored by English-speaking learners because we don't really have anything like it in English. So, we need to understand what it is, and then train our brains to reach for it and use it in the right situations! The **subjunctive** is a very important verb form in Pashto, but it's often ignored by English-speaking learners because we don't really have anything like it in English. So, we need to understand what it is, and then train our brains to reach for it and use it in the right situations!
@ -103,3 +107,5 @@ As you can see, the subjunctive is often used in [subordinate clauses](https://e
- <InlinePs opts={opts} ps={{ p: "په کار دی چې ...", f: "pu kaar dey che ...", e: "It's necessary that ..." }} /> - <InlinePs opts={opts} ps={{ p: "په کار دی چې ...", f: "pu kaar dey che ...", e: "It's necessary that ..." }} />
...etc. ...etc.
<GameDisplay record={subjunctiveVerbGame} />

View File

@ -22,7 +22,13 @@ export const presentVerbGame = makeGameRecord(
"present-verbs-write", "present-verbs-write",
"/verbs/present-verbs/", "/verbs/present-verbs/",
(id, link) => () => <VerbGame id={id} level="presentVerb" link={link} /> (id, link) => () => <VerbGame id={id} level="presentVerb" link={link} />
) );
export const subjunctiveVerbGame = makeGameRecord(
"Write the subjunctive verb",
"subjunctive-verbs-write",
"/verbs/subjunctive-verbs/",
(id, link) => () => <VerbGame id={id} level="subjunctiveVerb" link={link} />
);
export const nounGenderGame1 = makeGameRecord( export const nounGenderGame1 = makeGameRecord(
"Identify Noun Genders - Level 1", "Identify Noun Genders - Level 1",
@ -149,6 +155,7 @@ const games: { chapter: string, items: GameRecord[] }[] = [
chapter: "Verbs", chapter: "Verbs",
items: [ items: [
presentVerbGame, presentVerbGame,
subjunctiveVerbGame,
], ],
} }
]; ];

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { useState } from "react";
import { import {
comparePs, comparePs,
makeProgress, makeProgress,
@ -6,26 +6,23 @@ import {
import GameCore from "../GameCore"; import GameCore from "../GameCore";
import { import {
Types as T, Types as T,
Examples,
defaultTextOptions as opts, defaultTextOptions as opts,
typePredicates as tp, typePredicates as tp,
makeNounSelection, makeNounSelection,
randFromArray, randFromArray,
renderEP,
compileEP,
flattenLengths, flattenLengths,
randomPerson, randomPerson,
InlinePs, InlinePs,
grammarUnits, grammarUnits,
randomSubjObj,
renderVP, renderVP,
makeVPSelectionState, makeVPSelectionState,
compileVP, compileVP,
blockUtils, blockUtils,
concatPsString, concatPsString,
isInvalidSubjObjCombo,
} from "@lingdocs/pashto-inflector"; } from "@lingdocs/pashto-inflector";
import { basicVerbs, intransitivePast } from "../../content/verbs/basic-present-verbs"; import { isThirdPerson } from "@lingdocs/pashto-inflector/dist/lib/phrase-building/vp-tools";
import { psStringEquals } from "@lingdocs/pashto-inflector/dist/lib/p-text-helpers"; import { maybeShuffleArray } from "../../lib/shuffle-array";
const kidsColor = "#017BFE"; const kidsColor = "#017BFE";
@ -37,6 +34,12 @@ type Question = {
phrase: { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] }, phrase: { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] },
}; };
const verbs: T.VerbEntry[] = [
{"ts":1527812856,"i":11630,"p":"لیکل","f":"leekul","g":"leekul","e":"to write, draw","c":"v. trans./gramm. trans.","ec":"write,writes,writing,wrote,written"},
{"ts":1527815399,"i":14480,"p":"وهل","f":"wahul","g":"wahul","e":"to hit","c":"v. trans.","tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"},
{"ts":1527812275,"i":11608,"p":"لیدل","f":"leedul","g":"leedul","e":"to see","c":"v. trans./gramm. trans.","psp":"وین","psf":"ween","tppp":"لید","tppf":"leed","ec":"see,sees,seeing,saw,seen"},
{"ts":1577049208257,"i":1068,"p":"اورېدل","f":"awredul","g":"awredul","e":"to hear, listen","c":"v. trans./gramm. trans.","psp":"اور","psf":"awr","tppp":"اورېد","tppf":"awred","ec":"hear,hears,hearing,heard"},
].map(entry => ({ entry })) as T.VerbEntry[];
// @ts-ignore // @ts-ignore
const nouns: T.NounEntry[] = [ const nouns: T.NounEntry[] = [
{"ts":1527815251,"i":7790,"p":"سړی","f":"saRéy","g":"saRey","e":"man","c":"n. m.","ec":"man","ep":"men"}, {"ts":1527815251,"i":7790,"p":"سړی","f":"saRéy","g":"saRey","e":"man","c":"n. m.","ec":"man","ep":"men"},
@ -61,7 +64,7 @@ const pronounTypes = [
export default function VerbGame({ id, link, level }: { id: string, link: string, level: T.VerbTense }) { export default function VerbGame({ id, link, level }: { id: string, link: string, level: T.VerbTense }) {
function* questions (): Generator<Current<Question>> { function* questions (): Generator<Current<Question>> {
let pool = [...pronounTypes]; let pool = [...pronounTypes];
function makeRandPronoun(): T.PronounSelection { function getRandPersFromPool(): T.Person {
let person: T.Person; let person: T.Person;
do { do {
person = randomPerson(); person = randomPerson();
@ -71,11 +74,7 @@ export default function VerbGame({ id, link, level }: { id: string, link: string
if (pool.length === 0) { if (pool.length === 0) {
pool = pronounTypes; pool = pronounTypes;
} }
return { return person;
type: "pronoun",
distance: "far",
person,
};
} }
function makeRandomNoun(): T.NounSelection { function makeRandomNoun(): T.NounSelection {
const n = makeNounSelection(randFromArray(nouns), undefined); const n = makeNounSelection(randFromArray(nouns), undefined);
@ -86,43 +85,48 @@ export default function VerbGame({ id, link, level }: { id: string, link: string
}; };
} }
function makeRandomVPS(l: T.VerbTense): T.VPSelectionComplete { function makeRandomVPS(l: T.VerbTense): T.VPSelectionComplete {
function makePronoun(p: T.Person): T.PronounSelection { function personToNPSelection(p: T.Person): T.NPSelection {
if (!isThirdPerson(p)) {
return {
type: "NP",
selection: randFromArray([
() => makePronounS(p),
makeRandomNoun,
() => makePronounS(p),
])(),
};
}
return {
type: "NP",
selection: makePronounS(p),
};
}
function makePronounS(p: T.Person): T.PronounSelection {
return { return {
type: "pronoun", type: "pronoun",
person: p, person: p,
distance: "far", distance: randFromArray(["far", "near", "far"]),
}; };
} }
function makeNPSelection(pr: T.PronounSelection): T.NPSelection { const verb = randFromArray(verbs);
return { const subj = getRandPersFromPool();
type: "NP", let obj: T.Person;
selection: pr, do {
}; obj = randomPerson();
} } while (isInvalidSubjObjCombo(subj, obj));
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") // const tense = (l === "allIdentify" || l === "allProduce")
// ? randFromArray(tenses) // ? randFromArray(tenses)
// : l; // : l;
const tense = l; const tense = l;
return makeVPS({ return makeVPS({
verb, verb,
subject: makeNPSelection(makePronoun(subj)), subject: personToNPSelection(subj),
object: makeNPSelection(makePronoun(obj)), object: personToNPSelection(obj),
tense, tense,
}); });
} }
for (let i = 0; i < amount; i++) { for (let i = 0; i < amount; i++) {
const VPS = makeRandomVPS("presentVerb"); const VPS = makeRandomVPS(level);
const VP = renderVP(VPS); const VP = renderVP(VPS);
const compiled = compileVP( const compiled = compileVP(
VP, VP,
@ -144,7 +148,6 @@ export default function VerbGame({ id, link, level }: { id: string, link: string
}; };
} }
function Display({ question, callback }: QuestionDisplayProps<Question>) { function Display({ question, callback }: QuestionDisplayProps<Question>) {
const [answer, setAnswer] = useState<string>(""); const [answer, setAnswer] = useState<string>("");
const [withBa, setWithBa] = useState<boolean>(false); const [withBa, setWithBa] = useState<boolean>(false);
@ -207,7 +210,7 @@ export default function VerbGame({ id, link, level }: { id: string, link: string
function Instructions() { function Instructions() {
return <div> return <div>
<p className="lead">Write the present verb to complete the phrase</p> <p className="lead">Write the {humanReadableVerbTense(level)} verb to complete the phrase</p>
</div> </div>
} }
@ -221,12 +224,12 @@ export default function VerbGame({ id, link, level }: { id: string, link: string
/> />
}; };
function QuestionDisplay(question: Question) { function QuestionDisplay({ question }: { question: Question }) {
const ps = flattenLengths(question.phrase.ps)[0]; const ps = flattenLengths(question.phrase.ps)[0];
return <div> return <div className="mb-3">
<div>{ps.p}</div> <div>{ps.p}</div>
<div>{ps.f}</div> <div>{ps.f}</div>
{question.phrase.e && <div> {question.phrase.e && <div className="text-muted mt-2">
{question.phrase.e.map(x => <div key={Math.random()}> {question.phrase.e.map(x => <div key={Math.random()}>
{x} {x}
</div>)} </div>)}
@ -248,33 +251,40 @@ function makeCorrectAnswer(question: Question): JSX.Element {
<div><strong>{verbHasBa(question.rendered) ? "with" : "without"}</strong> a <InlinePs opts={opts}>{grammarUnits.baParticle}</InlinePs> in the kids' section.</div> <div><strong>{verbHasBa(question.rendered) ? "with" : "without"}</strong> a <InlinePs opts={opts}>{grammarUnits.baParticle}</InlinePs> in the kids' section.</div>
</div>; </div>;
} }
function modExs(exs: T.PsString[], withBa: boolean): { p: JSX.Element, f: JSX.Element }[] { // function modExs(exs: T.PsString[], withBa: boolean): { p: JSX.Element, f: JSX.Element }[] {
return exs.map(ps => { // return exs.map(ps => {
if (!ps.p.includes(" ___ ")) { // if (!ps.p.includes(" ___ ")) {
return { // return {
p: <>{ps.p}</>, // p: <>{ps.p}</>,
f: <>{ps.f}</>, // f: <>{ps.f}</>,
}; // };
} // }
const splitP = ps.p.split(" ___ "); // const splitP = ps.p.split(" ___ ");
const splitF = ps.f.split(" ___ "); // const splitF = ps.f.split(" ___ ");
return { // return {
p: <>{splitP[0]} <span style={{ color: kidsColor }}>{withBa ? "به" : "__"}</span> {splitP[1]}</>, // p: <>{splitP[0]} <span style={{ color: kidsColor }}>{withBa ? "به" : "__"}</span> {splitP[1]}</>,
f: <>{splitF[0]} <span style={{ color: kidsColor }}>{withBa ? "ba" : "__"}</span> {splitF[1]}</>, // f: <>{splitF[0]} <span style={{ color: kidsColor }}>{withBa ? "ba" : "__"}</span> {splitF[1]}</>,
}; // };
}); // });
} // }
function humanReadableTense(tense: T.EquativeTense | "allProduce"): string {
return tense === "allProduce" function humanReadableVerbTense(tense: T.VerbTense): string {
? "" return tense === "presentVerb"
: tense === "pastSubjunctive" ? "present"
? "past subjunctive" : tense === "subjunctiveVerb"
: tense === "wouldBe" ? "subjunctive"
? `"would be"` : tense === "imperfectiveFuture"
: tense === "wouldHaveBeen" ? "imperfective future"
? `"would have been"` : tense === "perfectiveFuture"
: tense; ? "perfective future"
: tense === "perfectivePast"
? "simple past"
: tense === "imperfectivePast"
? "continuous past"
: tense === "habitualImperfectivePast"
? "habitual simple past"
: "habitual continuous past";
} }
function makeVPS({ verb, subject, object, tense }: { function makeVPS({ verb, subject, object, tense }: {
@ -284,13 +294,17 @@ function makeVPS({ verb, subject, object, tense }: {
tense: T.VerbTense, tense: T.VerbTense,
}): T.VPSelectionComplete { }): T.VPSelectionComplete {
const vps = makeVPSelectionState(verb); const vps = makeVPSelectionState(verb);
const transitivity = (vps.verb.transitivity === "transitive" && vps.verb.canChangeTransitivity)
? "grammatically transitive"
: vps.verb.transitivity;
return { return {
...vps, ...vps,
verb: { verb: {
...vps.verb, ...vps.verb,
transitivity,
tense, tense,
}, },
blocks: [ blocks: maybeShuffleArray([
{ {
key: Math.random(), key: Math.random(),
block: { block: {
@ -302,10 +316,14 @@ function makeVPS({ verb, subject, object, tense }: {
key: Math.random(), key: Math.random(),
block: { block: {
type: "objectSelection", type: "objectSelection",
selection: object, selection: transitivity === "intransitive"
? "none"
: transitivity === "grammatically transitive"
? T.Person.ThirdPlurMale
: object,
}, },
}, },
], ]),
}; };
} }

View File

@ -1,5 +1,7 @@
// https://stackoverflow.com/a/2450976 // https://stackoverflow.com/a/2450976
import { randFromArray } from "@lingdocs/pashto-inflector";
function shuffleArray<T>(arr: Readonly<Array<T>>): Array<T> { function shuffleArray<T>(arr: Readonly<Array<T>>): Array<T> {
let currentIndex = arr.length, temporaryValue, randomIndex; let currentIndex = arr.length, temporaryValue, randomIndex;
@ -21,4 +23,12 @@ function shuffleArray<T>(arr: Readonly<Array<T>>): Array<T> {
return array; return array;
} }
export function maybeShuffleArray<T>(arr: Array<T>): Array<T> {
const shuffle = randFromArray([true, false, true, false, false]);
if (shuffle) {
return shuffleArray(arr);
}
return arr;
}
export default shuffleArray; export default shuffleArray;

View File

@ -1803,10 +1803,10 @@
rambda "^6.7.0" rambda "^6.7.0"
react-select "^5.2.2" react-select "^5.2.2"
"@lingdocs/pashto-inflector@^3.7.5": "@lingdocs/pashto-inflector@^3.7.9":
version "3.7.5" version "3.7.9"
resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-3.7.5.tgz#6421b88ccf544cdbc7f4cefc66ff75b3b7f1bb44" resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-3.7.9.tgz#a1dc7b6635d9415d8dddfee9a09bb5b95190d7f0"
integrity sha512-36ULd7ETZBz9/gs2K1s2XJFzEA3kDZfR9rPPN6B5oJvW9yAZXfgaJg9W2jzhodfu2WS3lpQvizis8zQjPioV8w== integrity sha512-2LKY5TRUs9h7nE9gQaUUiNV0eLOh5Tr4eJtYp2RGaxeMZhOZhVbbiXc+FV2BPe9bOEpYK3hy678RLBIx0C18nQ==
dependencies: dependencies:
"@formkit/auto-animate" "^1.0.0-beta.1" "@formkit/auto-animate" "^1.0.0-beta.1"
classnames "^2.2.6" classnames "^2.2.6"