diff --git a/src/content/verbs/all-perfect-verbs.mdx b/src/content/verbs/all-perfect-verbs.mdx index 1b6964e..b58d9a3 100644 --- a/src/content/verbs/all-perfect-verbs.mdx +++ b/src/content/verbs/all-perfect-verbs.mdx @@ -18,7 +18,11 @@ import BasicVerbShowCase from "../../components/BasicVerbShowCase"; import perfectDogMeme from "./perfect-dog-meme.jpg"; import chemistryPerfect from "./chemistry-perfect.jpg"; import BasicBlocks from "../../components/BasicBlocks"; - +import GameDisplay from "../../games/GameDisplay"; +import { + perfectGameOne, + perfectGameMix, +} from "../../games/games"; In the previous chapter we explained how [perfect](https://en.wikipedia.org/wiki/Perfect_(grammar)) forms are made by combining the past participle of a verb with an equative. @@ -316,4 +320,8 @@ Just like the "w f: "no tu ba raaghúle waay ku nu!", e: "Ah, you (f.) should have come!", }, -])} \ No newline at end of file +])} + + + + diff --git a/src/content/verbs/perfect-verbs-intro.mdx b/src/content/verbs/perfect-verbs-intro.mdx index 27b98d2..77e55ab 100644 --- a/src/content/verbs/perfect-verbs-intro.mdx +++ b/src/content/verbs/perfect-verbs-intro.mdx @@ -20,6 +20,12 @@ import presentPerfect from "./present-perfect.svg"; import chemistryPerfect from "./chemistry-perfect.jpg"; import BasicBlocks from "../../components/BasicBlocks"; import VideoPlayer from "../../components/VideoPlayer"; +import GameDisplay from "../../games/GameDisplay"; +import { + intransitivePresentPerfectGameOne, + intransitivePresentPerfectGameMix, + presentPerfectGame, +} from "../../games/games"; ## Introduction @@ -236,6 +242,12 @@ With transitive verbs everything works the same, but we follow the + + + + + + ## Negatives with the perfect To make perfect forms negative you add a block, just as you would with any other verb. But interestingly, **the past participle and equative blocks switch places**. diff --git a/src/games/games.tsx b/src/games/games.tsx index 04270cd..e4b3565 100644 --- a/src/games/games.tsx +++ b/src/games/games.tsx @@ -8,6 +8,7 @@ import EquativeIdentify from "./sub-cores/EquativeIdentify"; import VerbFormulas from "./sub-cores/VerbFormulas"; import InflectionPatterns from "./sub-cores/InflectionPatterns"; import InflectionsWriting from "./sub-cores/InflectionsWriting"; +import PerfectVerbsIntransitive from "./sub-cores/PerfectGame"; // NOUNS @@ -35,42 +36,42 @@ export const unisexNounGame = makeGameRecord({ // INFLECTIONS export const inflectionTableGame1 = makeGameRecord({ - title: `Write the inflections - Pattern #1`, + title: "Write the inflections - Pattern #1", id: "write-inflections-1", link: "/inflection/inflection-patterns/#1-basic", level: 1, SubCore: InflectionsWriting, }); export const inflectionTableGame2 = makeGameRecord({ - title: `Write the inflections - Pattern #2`, + title: "Write the inflections - Pattern #2", id: "write-inflections-2", link: "/inflection/inflection-patterns/#2-words-ending-in-an-unstressed-ی---ey", level: 2, SubCore: InflectionsWriting, }); export const inflectionTableGame3 = makeGameRecord({ - title: `Write the inflections - Pattern #3`, + title: "Write the inflections - Pattern #3", id: "write-inflections-3", link: "/inflection/inflection-patterns/#3-words-ending-in-a-stressed-ی---éy", level: 3, SubCore: InflectionsWriting, }); export const inflectionTableGame4 = makeGameRecord({ - title: `Write the inflections - Pattern #4`, + title: "Write the inflections - Pattern #4", id: "write-inflections-4", link: "/inflection/inflection-patterns/#4-words-with-the-pashtoon-pattern", level: 4, SubCore: InflectionsWriting, }); export const inflectionTableGame5 = makeGameRecord({ - title: `Write the inflections - Pattern #5`, + title: "Write the inflections - Pattern #5", id: "write-inflections-5", link: "/inflection/inflection-patterns/#5-shorter-words-that-squish", level: 5, SubCore: InflectionsWriting, }); export const inflectionTableGame6 = makeGameRecord({ - title: `Write the inflections - Pattern #6`, + title: "Write the inflections - Pattern #6", id: "write-inflections-6", link: "/inflection/inflection-patterns/#6-inanimate-feminine-nouns-ending-in-ي---ee", level: 6, @@ -340,6 +341,61 @@ export const verbFormulasGame = makeGameRecord({ SubCore: VerbFormulas, }); +export const intransitivePresentPerfectGameOne = makeGameRecord({ + title: "Write the present perfect verb - intransitive (one)", + id: "present-perfect-intransitive-one", + link: "/verbs/perfect-verbs-intro/", + level: { + level: 1, + type: "intransitive", + }, + SubCore: PerfectVerbsIntransitive, +}); + +export const intransitivePresentPerfectGameMix = makeGameRecord({ + title: "Write the present perfect verb - intransitive (mix)", + id: "present-perfect-intransitive-mix", + link: "/verbs/perfect-verbs-intro/", + level: { + level: 2, + type: "intransitive", + }, + SubCore: PerfectVerbsIntransitive, +}); + +export const presentPerfectGame = makeGameRecord({ + title: "Write the present perfect verb - transitive/intransitive mix", + id: "present-perfect", + link: "/verbs/perfect-verbs-intro/", + level: { + level: 2, + type: "transitive-intransitive", + }, + SubCore: PerfectVerbsIntransitive, +}); + +export const perfectGameOne = makeGameRecord({ + title: "Write the perfect verb - all perfect tenses (one verb)", + id: "all-perfect-one", + link: "/verbs/all-perfect-verbs/", + level: { + level: 1, + type: "all-tenses", + }, + SubCore: PerfectVerbsIntransitive, +}); + +export const perfectGameMix = makeGameRecord({ + title: "Write the perfect verb - all perfect tenses (mix)", + id: "all-perfect-mix", + link: "/verbs/all-perfect-verbs/", + level: { + level: 2, + type: "all-tenses", + }, + SubCore: PerfectVerbsIntransitive, +}); + const games: { chapter: string, items: GameRecord[] }[] = [ { chapter: "Nouns", @@ -407,6 +463,16 @@ const games: { chapter: string, items: GameRecord[] }[] = [ verbFormulasGame, ], }, + { + chapter: "Perfect Verbs", + items: [ + intransitivePresentPerfectGameOne, + intransitivePresentPerfectGameMix, + presentPerfectGame, + perfectGameOne, + perfectGameMix, + ], + }, ]; // check to make sure we have no duplicate game keys diff --git a/src/games/sub-cores/PerfectGame.tsx b/src/games/sub-cores/PerfectGame.tsx new file mode 100644 index 0000000..31b7d26 --- /dev/null +++ b/src/games/sub-cores/PerfectGame.tsx @@ -0,0 +1,453 @@ +import { useState } from "react"; +import { + comparePs, +} from "../../lib/game-utils"; +import GameCore from "../GameCore"; +import { + Types as T, + defaultTextOptions as opts, + makeNounSelection, + randFromArray, + flattenLengths, + randomPerson, + InlinePs, + grammarUnits, + renderVP, + makeVPSelectionState, + compileVP, + blockUtils, + concatPsString, + isInvalidSubjObjCombo, + removeFVarients, + getEnglishVerb, + RootsAndStems, + getVerbInfo, + defaultTextOptions, + humanReadableVerbForm, + blank, + kidsBlank, + isPashtoScript, +} from "@lingdocs/ps-react"; +import { isPastTense, isThirdPerson } from "@lingdocs/ps-react"; +import { maybeShuffleArray } from "../../lib/shuffle-array"; +import { getVerbFromBlocks } from "@lingdocs/ps-react/dist/lib/src/phrase-building/blocks-utils"; +import { baParticle } from "@lingdocs/ps-react/dist/lib/src/grammar-units"; +import { intransitivePastVerbs } from "../../content/verbs/basic-present-verbs"; +import { makePool } from "../../lib/pool"; +import { wordQuery } from "../../words/words"; +import { isImperativeTense } from "@lingdocs/ps-react/dist/lib/src/type-predicates"; + +const kidsColor = "#017BFE"; + +const amount = 12; +const timeLimit = 140; + +type Question = { + rendered: T.VPRendered, + phrase: { ps: T.SingleOrLengthOpts, e?: string[] }, +}; + +const transitivePastVerbs = wordQuery("verbs", [ + "leedul", + "wahul", + "khoRul", + "shărmawul", + "pejzandul", + "taRul", +]); + +const verbs = wordQuery("verbs", [ + "leekul", + "wahul", + "leedul", + "awredul", + "khoRul", + "akhistul", + "katul", + "lwedul", +]); + +const nouns = wordQuery("nouns", [ + "saRey", + "xudza", + "maashoom", + "puxtoon", + "Ustaaz", + "DaakTar", + "halik", +]); + +const persons: T.Person[] = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +]; + +type PerfectGameLevel = { + /* 1 is just a single verb, 2 picks a random verb for every question */ + level: 1 | 2, + type: "intransitive" | "transitive-intransitive" | "all-tenses", +} +type VerbPoolName = "basic" | "transitivePast" | "intransitivePast" | "mixedPast" | "mixedAll"; + +type LevelInfo = { + description: string | JSX.Element, + tense: T.PerfectTense | "allTenses", + pool: VerbPoolName, +} + +const levelInfos: Record = { + "intransitive": { + description: "present perfect form of the verb", + tense: "presentPerfect", + pool: "intransitivePast", + }, + "transitive-intransitive": { + description: "present perfect form of the verb", + tense: "presentPerfect", + pool: "mixedPast", + }, + "all-tenses": { + description: "correct perfect form of the verb", + tense: "allTenses", + pool: "mixedPast", + }, +} + +// TODO: Level where you create the formulas (seperate file) +// level where you choose the right situation + +const VerbGame: GameSubCore = ({ id, link, level, inChapter }: { + inChapter: boolean, + id: string, + link: string, + level: PerfectGameLevel, + }) => { + const levelInfo = levelInfos[level.type]; + const personPool = makePool(persons); + const verbPools: Record T.VerbEntry> = { + basic: makePool(verbs, 15), + transitivePast: makePool(transitivePastVerbs, 15), + intransitivePast: makePool(intransitivePastVerbs, 15), + mixedPast: makePool([...transitivePastVerbs, ...intransitivePastVerbs], 15), + mixedAll: makePool([...verbs, ...transitivePastVerbs, ...intransitivePastVerbs], 15), + }; + const tensePool = makePool([ + "presentPerfect", "pastPerfect", "subjunctivePerfect", "habitualPerfect", + "pastPerfect", "futurePerfect", "wouldBePerfect", "pastSubjunctivePerfect", + "wouldHaveBeenPerfect", + ]); + const oneVerb: T.VerbEntry = verbPools[levelInfo.pool](); + const getVerb = level.level === 1 + ? () => oneVerb + : () => verbPools[levelInfo.pool](); + 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(tense: T.PerfectTense): T.VPSelectionComplete { + 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 { + type: "pronoun", + person: p, + distance: randFromArray(["far", "near", "far"]), + }; + } + const verb = getVerb(); + const king = personPool(); + let servant: T.Person; + do { + servant = randomPerson(); + } while (isInvalidSubjObjCombo(king, servant)); + return makeVPS({ + verb, + king: personToNPSelection(king), + servant: personToNPSelection(servant), + tense, + defaultTransitivity: level.type.startsWith("transitive") + ? "transitive" + : "grammatically transitive", + }); + } + function getQuestion(): Question { + const VPS = makeRandomVPS(levelInfo.tense === "allTenses" + ? tensePool() + : levelInfo.tense + ); + const VP = renderVP(VPS); + const compiled = compileVP( + VP, + { removeKing: false, shrinkServant: false }, + true, + { ba: levelInfo.tense === "allTenses", verb: true, negative: true }, + ); + const phrase = { + ps: compiled.ps, + e: compiled.e, + }; + return { + rendered: VP, + phrase, + }; + } + + function Display({ question, callback }: QuestionDisplayProps) { + const [answer, setAnswer] = useState(""); + const [withBa, setWithBa] = useState(false); + const handleInput = ({ target: { value }}: React.ChangeEvent) => { + if (value === "به " || value === "به ") { + setWithBa(true); + setAnswer(""); + return; + } + setAnswer(value); + } + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + const correct = comparePs(answer, getVerbPs(question.rendered)) + && (withBa === verbHasBa(question.rendered)); + if (correct) { + setAnswer(""); + } + callback(correct); + } + // useEffect(() => { + // if (level === "allProduce") setWithBa(false); + // }, [question]); + return
+ +
+ {level.type === "all-tenses" &&
+ setWithBa(e.target.checked)} + /> + +
} +
+ +
+
+ {/*
*/} + + {/*
*/} + {/*
+ Type Enter to check +
*/} +
+
+
+ } + + function Instructions() { + const desc = levelInfo.description; + return
+

+ Write the {desc} verb to complete the phrase + {desc ? "" : " (all tenses)"} +

+
+ } + + return +}; + +export default VerbGame; + +function QuestionDisplay({ question, userAnswer }: { + question: Question, + userAnswer: { withBa: boolean, answer: string }, +}) { + const ps = addUserAnswer( + userAnswer, + flattenLengths(question.phrase.ps)[0] + ); + const v = getVerbFromBlocks(question.rendered.blocks); + const vEntry = v.block.verb.entry; + const infoV = getVerbInfo(vEntry) + const info = "grammaticallyTransitive" in infoV + ? infoV.grammaticallyTransitive + : "stative" in infoV + ? infoV.stative + : infoV; + return
+
+
{vEntry.p} - {removeFVarients(vEntry.f)} {vEntry.c}
+
"{getEnglishVerb(vEntry)}"
+
+
+ 🌳 Show roots and stems + +
+
{ps.p}
+
{ps.f}
+ {question.phrase.e &&
+ {question.phrase.e.map(x =>
+ {x} +
)} +
} +
{humanReadableVerbForm(v.block.tense)}
+
; +} + +function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element { + return
+
+ {getVerbPs(question.rendered).reduce(((accum, curr, i): JSX.Element[] => ( + [ + ...accum, + ...i > 0 ? [ or ] : [], + {curr.p} - {curr.f}, + ] + )), [] as JSX.Element[])} +
+
{verbHasBa(question.rendered) ? "with" : "without"} a {grammarUnits.baParticle} in the kids' section.
+
; +} +// 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]} {withBa ? "به" : "__"} {splitP[1]}, +// f: <>{splitF[0]} {withBa ? "ba" : "__"} {splitF[1]}, +// }; +// }); +// } + +function addUserAnswer(a: { withBa: boolean, answer: string }, ps: T.PsString): T.PsString { + function addBa(x: T.PsString) { + if (!a.withBa) return x; + return { + p: x.p.replace(kidsBlank.p, baParticle.p), + f: x.f.replace(kidsBlank.f, baParticle.f), + } + } + function addAnswer(x: T.PsString): T.PsString { + if (!a.answer) return x; + const field = isPashtoScript(a.answer) ? "p" : "f"; + return { + ...x, + [field]: x[field].replace(blank[field], a.answer), + }; + } + return addAnswer(addBa(ps)); +} + +function makeVPS({ verb, king, servant, tense, defaultTransitivity }: { + verb: T.VerbEntry, + king: T.NPSelection, + servant: T.NPSelection, + tense: T.PerfectTense, + defaultTransitivity: "transitive" | "grammatically transitive" +}): T.VPSelectionComplete { + const vps = makeVPSelectionState(verb); + const transitivity = (vps.verb.transitivity === "transitive" && vps.verb.canChangeTransitivity) + ? defaultTransitivity + : vps.verb.transitivity; + const ergative = vps.verb.transitivity !== "intransitive" && isPastTense(tense); + const subject = ergative ? servant : king; + const object = ergative ? king : servant; + return { + ...vps, + verb: { + ...vps.verb, + negative: isImperativeTense(tense) + ? randFromArray([false, false, true]) + : false, + transitivity, + tense, + }, + blocks: maybeShuffleArray([ + { + key: Math.random(), + block: { + type: "subjectSelection", + selection: subject, + }, + }, + { + key: Math.random(), + block: { + type: "objectSelection", + selection: transitivity === "intransitive" + ? "none" + : transitivity === "grammatically transitive" + ? T.Person.ThirdPlurMale + : object, + }, + }, + ]), + }; +} + +function getVerbPs({ blocks }: T.VPRendered): T.PsString[] { + const { perfectiveHead, verb } = blockUtils.getVerbAndHeadFromBlocks(blocks); + const mU = blocks[0].find(b => b.block.type === "negative" && b.block.imperative); + function vBase() { + if (!perfectiveHead) { + return flattenLengths(verb.block.ps); + } + return flattenLengths(verb.block.ps).map(r => concatPsString(perfectiveHead.ps, r)); + } + if (mU) { + return vBase().map(b => concatPsString({ p: "مه", f: "mú" }, " ", b)); + } + return vBase(); +} + +function verbHasBa({ blocks }: T.VPRendered): boolean { + const verb = blockUtils.getVerbFromBlocks(blocks); + return verb.block.hasBa; +} \ No newline at end of file diff --git a/src/games/sub-cores/VerbGame.tsx b/src/games/sub-cores/VerbGame.tsx index 60c30fb..46f7a1f 100644 --- a/src/games/sub-cores/VerbGame.tsx +++ b/src/games/sub-cores/VerbGame.tsx @@ -266,7 +266,7 @@ const VerbGame: GameSubCore = ({ id, link, level, inChapter }: {
{/*
*/} - + {/*
*/} {/*
Type Enter to check