diff --git a/package.json b/package.json index 36c3e31..155c62c 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "@formkit/auto-animate": "^1.0.0-beta.1", "@fortawesome/fontawesome-free": "^5.15.4", "@lingdocs/lingdocs-main": "^0.3.3", - "@lingdocs/pashto-inflector": "^3.9.4", + "@lingdocs/pashto-inflector": "^3.9.6", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", diff --git a/src/games/GameCore.tsx b/src/games/GameCore.tsx index 8f5edca..ccf9390 100644 --- a/src/games/GameCore.tsx +++ b/src/games/GameCore.tsx @@ -50,7 +50,7 @@ type GameReducerAction = { } | { type: "timeout", } | { - type: "show answer", + type: "toggle show answer", } | { type: "skip", } @@ -176,12 +176,12 @@ function GameCore({ inChapter, getQuestion, amount, Display, DisplayCo showAnswer: false, }; } - if (action.type === "show answer") { - if (gs.mode === "practice" && gs.justStruck) { + if (action.type === "toggle show answer") { + if (gs.mode === "practice") { return { ...gs, justStruck: false, - showAnswer: true, + showAnswer: !gs.showAnswer, }; } return gs; @@ -310,13 +310,12 @@ function GameCore({ inChapter, getQuestion, amount, Display, DisplayCo question={state.current} callback={(correct) => dispatch({ type: "handle question response", payload: { correct }})} />} - {(state.mode === "practice" && state.justStruck) &&
-
} {(state.showAnswer && state.mode === "practice") &&
-
The correct answer was:
diff --git a/src/games/games.tsx b/src/games/games.tsx index 31ad945..04270cd 100644 --- a/src/games/games.tsx +++ b/src/games/games.tsx @@ -7,6 +7,8 @@ import VerbSituations from "./sub-cores/VerbSituations"; import EquativeIdentify from "./sub-cores/EquativeIdentify"; import VerbFormulas from "./sub-cores/VerbFormulas"; import InflectionPatterns from "./sub-cores/InflectionPatterns"; +import InflectionsWriting from "./sub-cores/InflectionsWriting"; + // NOUNS export const nounGenderGame1 = makeGameRecord({ @@ -32,6 +34,48 @@ export const unisexNounGame = makeGameRecord({ }); // INFLECTIONS +export const inflectionTableGame1 = makeGameRecord({ + 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`, + 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`, + 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`, + 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`, + 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`, + id: "write-inflections-6", + link: "/inflection/inflection-patterns/#6-inanimate-feminine-nouns-ending-in-ي---ee", + level: 6, + SubCore: InflectionsWriting, +}); export const inflectionPatternsGame1 = makeGameRecord({ title: "Identify the inflection pattern (Level 1)", id: "inflection-patterns-1", @@ -308,6 +352,12 @@ const games: { chapter: string, items: GameRecord[] }[] = [ { chapter: "Inflection", items: [ + inflectionTableGame1, + inflectionTableGame2, + inflectionTableGame3, + inflectionTableGame4, + inflectionTableGame5, + inflectionTableGame6, inflectionPatternsGame1, inflectionPatternsGame2, ], @@ -363,8 +413,8 @@ const games: { chapter: string, items: GameRecord[] }[] = [ games.forEach(({ items }) => { const allAreUnique = (arr: unknown[]) => arr.length === new Set(arr).size; const ids = items.map(x => x.id); - const title = items.map(x => x.title); - if (!allAreUnique(title)) throw new Error("duplicate game title"); + const titles = items.map(x => x.title); + if (!allAreUnique(titles)) throw new Error("duplicate game title"); if (!allAreUnique(ids)) throw new Error("duplicate game key"); }) diff --git a/src/games/sub-cores/InflectionsWriting.tsx b/src/games/sub-cores/InflectionsWriting.tsx new file mode 100644 index 0000000..fb8c3ba --- /dev/null +++ b/src/games/sub-cores/InflectionsWriting.tsx @@ -0,0 +1,207 @@ +import GameCore from "../GameCore"; +import { + Types as T, + getInflectionPattern, + Examples, + defaultTextOptions as opts, + firstVariation, + inflectWord, + humanReadableInflectionPattern, + isUnisexSet, + InflectionsTable, +} from "@lingdocs/pashto-inflector"; +import { makePool } from "../../lib/pool"; +import { nouns, adjectives } from "../../words/words"; +import { isAdverbEntry } from "@lingdocs/pashto-inflector/dist/lib/type-predicates"; +import { ChangeEvent, FormEvent, useEffect, useRef, useState } from "react"; +import { comparePs } from "../../lib/game-utils"; + +const amount = 8; +const timeLimit = 300; + +type Question = { + entry: T.NounEntry | T.AdjectiveEntry, + inflections: T.Inflections, +}; + +type InfFormContent = { + masc: [string, string, string], + fem: [string, string, string], +}; + +export default function InflectionsWriting({ inChapter, id, link, level }: { + inChapter: boolean, + id: string, + link: string, + level: T.InflectionPattern, +}) { + const wordPool = makePool( + [...nouns, ...adjectives] + .filter(x => { + if (isAdverbEntry(x)) return false; + const infs = inflectWord(x); + if (!infs || !infs.inflections) return false; + return (getInflectionPattern(x) === level); + }) + ); + + function getQuestion(): Question { + const word = wordPool(); + const r = inflectWord(word); + if (!r || !r.inflections) { + throw new Error(`error getting inflections for ${word.f}`); + } + return { + entry: word, + inflections: r.inflections, + }; + }; + + function Display({ question, callback }: QuestionDisplayProps) { + function handleAnswer(inf: InfFormContent) { + callback(infAnswerCorrect(inf, question.inflections)); + } + return
+
+ {[ + { + p: firstVariation(question.entry.p), + f: firstVariation(question.entry.f), + e: `${firstVariation(question.entry.e)} - ${question.entry.c}`, + } + ]} +
+ +
+ } + + function Instructions() { + return
+

Complete the inflections for the {humanReadableInflectionPattern(level, opts)} pattern word

+
+ } + + return +}; + +function InflectionTableForm({ onSubmit, genders, question }: { + onSubmit: (i: InfFormContent) => void, + genders: T.Gender | "unisex", + question: Question, +}) { + const [inf, setInf] = useState({ fem: ["", "", ""], masc: ["", "", ""] }); + const mascInputRef = useRef(null); + const femInputRef = useRef(null); + useEffect(() => { + setInf({ fem: ["", "", ""], masc: ["", "", ""] }); + femInputRef.current && femInputRef.current.focus(); + mascInputRef.current && mascInputRef.current.focus(); + }, [question, setInf]); + + function handleClearInf() { + setInf({ fem: ["", "", ""], masc: ["", "", ""] }); + } + function handleInfInput(gender: T.Gender, inflection: number) { + return ({ target: { value }}: ChangeEvent) => { + inf[gender][inflection] = value; + setInf({...inf}); + }; + } + function handleSubmit(e: FormEvent) { + e.preventDefault(); + onSubmit(inf); + } + return
+ + + + + {genders !== "fem" && } + {genders !== "masc" && } + + + + {["Plain", "1st", "2nd"].map((title, i) => ( + + + {genders !== "fem" && } + {genders !== "masc" && } + + ))} + +
MasculineFeminine
{title} + + + +
+
+ + +
+
; +} + +function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element { + return
+ +
; +} + +function infAnswerCorrect(answer: InfFormContent, inf: T.Inflections): boolean { + function genInfCorrect(gender: T.Gender): boolean { + // @ts-ignore + const genInf = inf[gender] as T.InflectionSet; + return genInf.every((x, i) => ( + x.some(ps => comparePs(answer[gender][i], ps)) + )); + } + if (isUnisexSet(inf)) { + return genInfCorrect("masc") && genInfCorrect("fem"); + } + return genInfCorrect("masc" in inf ? "masc": "fem"); +} + diff --git a/src/words/adverbs/adverbs.js b/src/words/adverbs/adverbs.js index 509ba4d..4919941 100644 --- a/src/words/adverbs/adverbs.js +++ b/src/words/adverbs/adverbs.js @@ -284,7 +284,6 @@ module.exports = [ 1527823047, // اچانک - achaanak 1527813881, // په نامه - pu naama 1527817130, // ترخوا - turkhwaa - 1595515640766, // تېر و بېر - ter-U-ber 1527822042, // راپورته - raaporta 1527818051, // تر ابده - tur abada 1585821444042, // اتفاقاً - itifaaqan diff --git a/yarn.lock b/yarn.lock index 4777686..a81a29c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1804,10 +1804,10 @@ rambda "^6.7.0" react-select "^5.2.2" -"@lingdocs/pashto-inflector@^3.9.4": - version "3.9.4" - resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-3.9.4.tgz#b85bd8b9c235da1b77a04bd4178be91371a94726" - integrity sha512-bthgBbzkFHIYUOTFNUNcwWFuhCkFlX7RCPQIbZdXFPOdUoEGtlngqJkECKm+pIShQioEGuMxhW0plhXDIccA8w== +"@lingdocs/pashto-inflector@^3.9.6": + version "3.9.6" + resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-3.9.6.tgz#308d50c81108f47fccedadecdf32c2eaaa7a797c" + integrity sha512-UGei0f8Yf19CFvrtBBMABxe1ICAFcepk9ytnsbguX6F1hCNRD13RmNcT8GnmIm+wPIXRS1Ae0Z7zgmx7fKcT7w== dependencies: "@formkit/auto-animate" "^1.0.0-beta.1" classnames "^2.2.6"