adjective game

This commit is contained in:
adueck 2023-01-13 16:09:11 +05:00
parent cd774fc304
commit 1550409440
17 changed files with 3410 additions and 1628 deletions

1
.env Normal file
View File

@ -0,0 +1 @@
FAST_REFRESH=false

View File

@ -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/ps-react": "^5.3.0",
"@lingdocs/ps-react": "^5.3.1",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",

View File

@ -94,6 +94,8 @@ Now we have two words, but it's still **one NP**, one building block. We can add
}
}</EditableBlock>
It's important to note that the adjective will <Link to="/inflection/inflection-patterns/">inflect</Link> according to the noun it's attached to.
#### Adding a possesor
We can also add a **possesor** by adding another NP <Link to="/sandwiches/sandwiches/">sandwiched</Link> in with a <InlinePs opts={opts} ps={{ p: "د", f: "du", e: "of"}} />. (Notice that the word sandwiched in there will <Link to="/inflection/inflection-intro/">inflect</Link> if possible.) Now we have a NP inside of an NP, but it's still all **one NP** or **one building block**.

View File

@ -9,6 +9,7 @@ import VerbFormulas from "./sub-cores/VerbFormulas";
import InflectionPatterns from "./sub-cores/InflectionPatterns";
import InflectionsWriting from "./sub-cores/InflectionsWriting";
import PerfectVerbsIntransitive from "./sub-cores/PerfectGame";
import NPAdjWriting from "./sub-cores/NPAdjGame";
// NOUNS
@ -396,6 +397,22 @@ export const perfectGameMix = makeGameRecord({
SubCore: PerfectVerbsIntransitive,
});
// ADJECTIVES
export const npWithAdjectivesHints = makeGameRecord({
title: "Write the adjective and noun togehter (with inflection hints)",
id: "adjective-nps-hints",
link: "/phrase-structure/np/",
level: "hints",
SubCore: NPAdjWriting,
});
export const npWithAdjectivesNoHints = makeGameRecord({
title: "Write the adjective and noun togehter",
id: "adjective-nps-no-hints",
link: "/phrase-structure/np/",
level: "no-hints",
SubCore: NPAdjWriting,
});
const games: { chapter: string, items: GameRecord[] }[] = [
{
chapter: "Nouns",
@ -473,6 +490,13 @@ const games: { chapter: string, items: GameRecord[] }[] = [
perfectGameMix,
],
},
{
chapter: "Adjectives",
items: [
npWithAdjectivesHints,
npWithAdjectivesNoHints,
],
},
];
// check to make sure we have no duplicate game keys

View File

@ -143,7 +143,7 @@ function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element
[
...accum,
...i > 0 ? [<span className="text-muted"> or </span>] : [],
<span>{curr.p}</span>,
<span key={i}>{curr.p}</span>,
]
)), [] as JSX.Element[])}
</div>

View File

@ -0,0 +1,193 @@
import GameCore from "../GameCore";
import {
Types as T,
Examples,
defaultTextOptions as opts,
firstVariation,
renderNPSelection,
getEnglishFromRendered,
removeFVarients,
concatPsString,
getInflectionPattern,
HumanReadableInflectionPattern,
} from "@lingdocs/ps-react";
import { makeNPAdjGenerator } from "../../lib/np-adj-generator";
import { useState } from "react";
import { comparePs } from "../../lib/game-utils";
const amount = 15;
const timeLimit = 230;
type Question = {
selection: T.NPSelection,
answer: T.PsString[],
english: string,
};
type Level = "hints" | "no-hints";
// LEVELS
// - without plurals
// - with inflection category hinting
const NPAdjWriting: GameSubCore<Level> = ({ inChapter, id, link, level }: {
inChapter: boolean,
id: string,
link: string,
level: Level,
}) => {
const npPool = makeNPAdjGenerator();
// todo زړې ډاکټرې should work!
// feminineplural space issue
function getQuestion(): Question {
const np = npPool();
const rendered = renderNPSelection(np, false, false, "subject", "none", false);
const renderedAdj: T.Rendered<T.AdjectiveSelection> | undefined = rendered.selection.adjectives && rendered.selection.adjectives[0];
if (!renderedAdj) {
throw new Error("error getting rendered adjective");
}
const answer = renderedAdj.ps.flatMap((adjPs) => (
rendered.selection.ps.map((nounPs) => (
concatPsString(adjPs, " ", nounPs)
))
));
return {
selection: np,
answer,
english: getEnglishFromRendered(rendered) || "ERROR",
};
};
function Display({ question, callback }: QuestionDisplayProps<Question>) {
const [answer, setAnswer] = useState<string>("");
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const correct = comparePs(answer, question.answer);
if (correct) {
setAnswer("");
}
callback(correct);
}
if (question.selection.type !== "NP" || question.selection.selection.type !== "noun") {
throw new Error("QUESTION ERROR");
}
const nounEntry = question.selection.selection.entry;
const adjEntry: T.AdjectiveEntry | undefined = question.selection.selection.adjectives[0]?.entry;
if (!adjEntry) {
throw new Error("QUESTION ERROR - MISSING ADJECTIVE");
}
const handleInput = ({ target: { value }}: React.ChangeEvent<HTMLInputElement>) => {
setAnswer(value);
}
return <div>
<div className="my-2" style={{ maxWidth: "300px", margin: "0 auto" }}>
<div className="d-flex flex-row justify-content-center">
<WordCard
showHint={level === "hints"}
entry={adjEntry}
selection={undefined}
/>
<WordCard
showHint={level === "hints"}
entry={nounEntry}
selection={question.selection.selection}
/>
</div>
<div className="my-3 h5">
{question.english}
</div>
<form onSubmit={handleSubmit}>
<div className="mt-2 mb-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-3">
<button className="btn btn-primary" type="submit">submit </button>
</div>
</form>
</div>
</div>
}
function Instructions() {
return <div>
<p className="lead">Write the adjective and noun together with the proper inflections.</p>
</div>
}
return <GameCore
inChapter={inChapter}
studyLink={link}
getQuestion={getQuestion}
id={id}
Display={Display}
DisplayCorrectAnswer={DisplayCorrectAnswer}
timeLimit={timeLimit}
amount={amount}
Instructions={Instructions}
/>
};
function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element {
// TODO: extract this reduces for the or, or different variations, used in VerbGame (etc.?)
return <div>
<div>
{question.answer.reduce(((accum, curr, i): JSX.Element[] => (
[
...accum,
...i > 0 ? [<span className="text-muted"> or </span>] : [],
<span key={i}>{curr.p} - {curr.f}</span>,
]
)), [] as JSX.Element[])}
</div>
</div>;
}
function WordCard({ showHint, entry, selection }: {
showHint: boolean
} & ({
entry: T.AdjectiveEntry,
selection: undefined,
} | {
entry: T.NounEntry,
selection: T.NounSelection,
})) {
const e = removeFVarients(entry);
return <div className="card ml-2" style={{ minWidth: "10rem"}}>
<div className="card-body" style={{ padding: "0.75rem" }}>
<Examples opts={opts}>{[
{
p: e.p,
f: e.f,
e: `${firstVariation(e.e)} - ${e.c}`,
}
]}</Examples>
{selection && <div style={{ marginTop: "-0.75rem"}}>
{selection.genderCanChange && selection.gender === "fem"
&& <span style={{ margin: "0 0.2rem" }}><strong>feminine</strong></span>}
{selection.numberCanChange && selection.number === "plural"
&& <span style={{ marginLeft: "0 0.2rem" }}><strong>plural</strong></span>}
</div>}
{showHint && <EntryCategoryTip entry={entry} />}
</div>
</div>;
}
function EntryCategoryTip({ entry }: { entry: T.AdjectiveEntry | T.NounEntry }) {
const pattern = getInflectionPattern(entry);
return <div className="badge bg-light small">
{HumanReadableInflectionPattern(pattern, opts)}
</div>;
}
export default NPAdjWriting;

View File

@ -342,7 +342,7 @@ function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element
[
...accum,
...i > 0 ? [<span className="text-muted"> or </span>] : [],
<span>{curr.p} - {curr.f}</span>,
<span key={i}>{curr.p} - {curr.f}</span>,
]
)), [] as JSX.Element[])}
</div>

View File

@ -343,7 +343,7 @@ function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element
[
...accum,
...i > 0 ? [<span className="text-muted"> or </span>] : [],
<span>{curr.p} - {curr.f}</span>,
<span key={i}>{curr.p} - {curr.f}</span>,
]
)), [] as JSX.Element[])}
</div>

View File

@ -28,7 +28,8 @@ export function compareF(input: string, answer: string): boolean {
return inp === (hasAccents(inp) ? ans : removeAccents(ans));
}
export function comparePs(input: string, answer: T.SingleOrLengthOpts<T.PsString | T.PsString[]>): boolean {
export function comparePs(inputRaw: string, answer: T.SingleOrLengthOpts<T.PsString | T.PsString[]>): boolean {
const input = inputRaw.replace(/\s+/g, " ");
if ("long" in answer) {
return comparePs(input, flattenLengths(answer));
}

View File

@ -0,0 +1,97 @@
import {
Types as T,
makeNounSelection,
makeAdjectiveSelection,
randFromArray,
} from "@lingdocs/ps-react";
import { makePool } from "./pool";
import { wordQuery } from "../words/words";
const nouns = wordQuery("nouns", [
"saRey",
"xudza",
"maashoom",
"Ustaaz",
"puxtoon",
"DaakTar",
"halik",
"tajriba",
"melma",
"khabura",
"kitaab",
"oobu",
"korba",
"shpoon",
"gaawanDey",
"lmasey",
"lobghaaRey",
"sandurghaaRey",
"malgurey",
"shpoonkey",
"khalk",
"ghul",
"khur",
]);
const adjectives = wordQuery("adjectives", [
"muR",
"sheen",
"soor",
"rixtooney",
"pakhwaaney",
"stuRey",
"ooGd",
"ghuT",
"xu",
"khufa",
"takRa",
"puT",
"tuGey",
"koochney",
"wroostey",
"pradey",
"zoR",
"moR",
"treekh",
"oom",
"khoG",
"droond",
"loomRey",
"Roond",
"droond",
"prot",
"soR",
"post",
"pokh",
"rooN",
"woR",
"tod",
"khpor",
"kooN",
"koG",
"zeeG",
]);
export function makeNPAdjGenerator(avoidPlurals?: boolean): () => T.NPSelection {
const nounPool = makePool(nouns);
const adjPool = makePool(adjectives);
return () => {
const ns = makeNounSelection(nounPool(), undefined);
const selection: T.NounSelection = {
...ns,
adjectives: [makeAdjectiveSelection(adjPool())],
...(ns.numberCanChange && !avoidPlurals) ? {
number: randFromArray(["singular", "plural", "plural", "plural", "singular"]),
} : {},
...ns.genderCanChange ? {
gender: randFromArray(["masc", "fem", "fem", "fem", "masc"]),
} : {},
};
return {
type: "NP",
selection,
};
};
}

View File

@ -10,7 +10,6 @@ module.exports = [
1527822788, // بېګاته - begáata
1527812183, // ورپام - wărpaam
1527814711, // لنډ پر لنډه - lanD par lanDa
1527821930, // بالبدیهه - bilbadeehá
1527814164, // دېخوا - dekhwaa
1527815160, // پرون - paroon
1623688539364, // ګانګوړ - gaangóR, gaangwÚR

View File

@ -1,5 +1,7 @@
module.exports = [
1527815329, // tajriba
1527812797, // xudza
1527812766, // khabura
1527816466, // صلح - sUlha
1527816589, // طرح - tarha
1589023873660, // فتح - fathá

View File

@ -15,4 +15,5 @@ module.exports = [
1527822725, // لوېشت - lwesht
1527811609, // منګل - mangul
1527821684, // ورېځ - wurédz
1527815129, // oobu
];

View File

@ -1,4 +1,6 @@
module.exports = [
1527812342, // khalk
1527814694, // mawaad
1527815177, // پلار
1527812828, // کور
1527812432, // آسمان - aasmaan

View File

@ -12,7 +12,6 @@ module.exports = [
1527818402, // اتلولي - atalwalée
1527814060, // اساني - asaanee
1527821293, // امادګي - amaadagee
1527819502, // باچهي - baachahee
1527820035, // باداري - baadaaree
1527817732, // بدبختي - badbakhtee
1588786872582, // بدنامي - badnaamee

View File

@ -16,4 +16,5 @@ module.exports = [
1527815087, // مړ - muR
1527814151, // مل - mal
1527813580, // یو - yo
1527819902, // zeeG
];

4700
yarn.lock

File diff suppressed because it is too large Load Diff