EP adjective game

This commit is contained in:
adueck 2023-01-13 17:47:10 +05:00
parent 1550409440
commit dd2a69985b
8 changed files with 297 additions and 69 deletions

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

View File

@ -0,0 +1,48 @@
import {
Types as T,
Examples,
defaultTextOptions as opts,
firstVariation,
removeFVarients,
getInflectionPattern,
HumanReadableInflectionPattern,
} from "@lingdocs/ps-react";
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" style={{ minWidth: "10rem", maxWidth: "15rem" }}>
<div className="card-body" style={{ padding: "0.25rem" }}>
<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 WordCard;

View File

@ -10,6 +10,7 @@ import InflectionPatterns from "./sub-cores/InflectionPatterns";
import InflectionsWriting from "./sub-cores/InflectionsWriting";
import PerfectVerbsIntransitive from "./sub-cores/PerfectGame";
import NPAdjWriting from "./sub-cores/NPAdjGame";
import EPAdjGame from "./sub-cores/EPAdjGame";
// NOUNS
@ -398,8 +399,22 @@ export const perfectGameMix = makeGameRecord({
});
// ADJECTIVES
export const epWithAdjectivesHints = makeGameRecord({
title: "Write the adjective to match the subject (with inf. pattern hints)",
id: "adjective-predicate-hints",
link: "/inflection/inflection-patterns/",
level: "hints",
SubCore: EPAdjGame,
});
export const epWithAdjectives = makeGameRecord({
title: "Write the predicate adjective to match the subject",
id: "adjective-predicate",
link: "/inflection/inflection-patterns/",
level: "no-hints",
SubCore: EPAdjGame,
});
export const npWithAdjectivesHints = makeGameRecord({
title: "Write the adjective and noun togehter (with inflection hints)",
title: "Write the adjective and noun togehter (with inf. pattern hints)",
id: "adjective-nps-hints",
link: "/phrase-structure/np/",
level: "hints",
@ -493,6 +508,8 @@ const games: { chapter: string, items: GameRecord[] }[] = [
{
chapter: "Adjectives",
items: [
epWithAdjectivesHints,
epWithAdjectives,
npWithAdjectivesHints,
npWithAdjectivesNoHints,
],

View File

@ -0,0 +1,224 @@
import { useState } from "react";
import {
comparePs,
} from "../../lib/game-utils";
import GameCore from "../GameCore";
import {
Types as T,
renderEP,
compileEP,
randFromArray,
defaultTextOptions as opts,
Examples,
blank,
isPashtoScript,
} from "@lingdocs/ps-react";
import { wordQuery } from "../../words/words";
import { makePool } from "../../lib/pool";
import { getPredicateSelectionFromBlocks } from "@lingdocs/ps-react/dist/lib/src/phrase-building/blocks-utils";
import WordCard from "../../components/WordCard";
const amount = 12;
const timeLimit = 140;
const pronouns: T.Person[] = [
0, 1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 11,
];
const adjectives = wordQuery("adjectives", [
"muR",
"sheen",
"soor",
"rixtooney",
"stuRey",
"ghuT",
"xu",
"khufa",
"takRa",
"puT",
"tuGey",
"koochney",
"pradey",
"zoR",
"moR",
"khoG",
"droond",
"loomRey",
"Roond",
"prot",
"soR",
"post",
"pokh",
"rooN",
"woR",
"kooN",
"koG",
]);
type Question = {
EPS: T.EPSelectionComplete,
phrase: { ps: T.PsString[], e?: string[] },
adjective: T.Rendered<T.AdjectiveSelection>,
};
export default function EPAdjGame({ inChapter, id, link, level }: { inChapter: boolean, id: string, link: string, level: "hints" | "no-hints" }) {
const pronounPool = makePool(pronouns);
const adjPool = makePool(adjectives);
function getQuestion(): Question {
const subject: T.NPSelection = {
type: "NP",
selection: {
type: "pronoun",
person: pronounPool(),
distance: randFromArray(["far", "far", "near"]),
},
};
const EPS = makeEPS(subject, adjPool(), "present");
const EP = renderEP(EPS);
const compiled = compileEP(
EP,
true,
{ predicate: true },
);
const phrase = {
ps: compiled.ps,
e: compiled.e,
};
return {
EPS,
phrase,
adjective: getAdjectiveFromRendered(EP),
};
};
function Display({ question, callback }: QuestionDisplayProps<Question>) {
const [answer, setAnswer] = useState<string>("");
const handleInput = ({ target: { value }}: React.ChangeEvent<HTMLInputElement>) => {
setAnswer(value);
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const correct = comparePs(answer, question.adjective.ps);
if (correct) {
setAnswer("");
}
callback(correct);
}
return <div>
<div className="mb-2" style={{ maxWidth: "300px", margin: "0 auto" }}>
<Examples lineHeight={1} opts={opts}>{
addUserAnswer(answer, question.phrase.ps[0])
}</Examples>
{question.phrase.e && question.phrase.e.map((e, i) => (
<div key={e+i} className="text-muted">{e}</div>
))}
</div>
<div className="d-flex flex-row justify-content-center mb-3">
<WordCard
entry={question.adjective.entry}
showHint={level === "hints"}
selection={undefined}
/>
</div>
<form onSubmit={handleSubmit}>
<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">
<button className="btn btn-primary" type="submit">submit </button>
</div>
</form>
</div>
}
function Instructions() {
return <div>
<p className="lead">
Fill in the blank with the correct inflection of the adjective
</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 {
return <div>
<div>
{question.adjective.ps.reduce(((accum, curr, i): JSX.Element[] => (
[
...accum,
...i > 0 ? [<span className="text-muted"> or </span>] : [],
<span key={i}>{curr.p}</span>,
]
)), [] as JSX.Element[])}
</div>
</div>;
}
function addUserAnswer(a: string, ps: T.PsString): T.PsString {
if (!a) return ps;
const field = isPashtoScript(a) ? "p" : "f";
return {
...ps,
[field]: ps[field].replace(blank[field], a),
};
}
function getAdjectiveFromRendered(EP: T.EPRendered): T.Rendered<T.AdjectiveSelection> {
const pred = getPredicateSelectionFromBlocks(EP.blocks);
if (pred.selection.type !== "complement" || pred.selection.selection.type !== "adjective") {
throw new Error("adjective not found in predicate");
}
return pred.selection.selection;
}
function makeEPS(subject: T.NPSelection, predicate: T.AdjectiveEntry, tense: T.EquativeTense): T.EPSelectionComplete {
return {
blocks: [
{
key: Math.random(),
block: {
type: "subjectSelection",
selection: subject,
},
},
],
predicate: {
type: "predicateSelection",
selection: {
type: "complement",
selection: {
type: "adjective",
entry: predicate,
sandwich: undefined,
},
},
},
equative: {
tense,
negative: false,
},
omitSubject: false,
};
}

View File

@ -1,19 +1,14 @@
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";
import WordCard from "../../components/WordCard";
const amount = 15;
const timeLimit = 230;
@ -38,9 +33,6 @@ const NPAdjWriting: GameSubCore<Level> = ({ inChapter, id, link, 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);
@ -153,41 +145,4 @@ function DisplayCorrectAnswer({ question }: { question: Question }): JSX.Element
</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

@ -350,22 +350,7 @@ function DisplayCorrectAnswer({ question }: { 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>;
}
// 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 addUserAnswer(a: { withBa: boolean, answer: string }, ps: T.PsString): T.PsString {
function addBa(x: T.PsString) {

View File

@ -58,7 +58,6 @@ const adjectives = wordQuery("adjectives", [
"droond",
"loomRey",
"Roond",
"droond",
"prot",
"soR",
"post",

View File

@ -2726,10 +2726,10 @@
rambda "^6.7.0"
react-select "^5.2.2"
"@lingdocs/ps-react@^5.3.1":
version "5.3.1"
resolved "https://npm.lingdocs.com/@lingdocs%2fps-react/-/ps-react-5.3.1.tgz#a8836d00df5ca884d8b26dd4caae7c720269ce26"
integrity sha512-JY0BQLnxKNjxqHlf7uvmH2ehmbbPvGoWdcxK68ZEZbwbXP0WXHmyFUji6hB9oRHtwrE8MS7guVX0e3/eZTolEA==
"@lingdocs/ps-react@^5.3.3":
version "5.3.3"
resolved "https://npm.lingdocs.com/@lingdocs%2fps-react/-/ps-react-5.3.3.tgz#05066c27059b5133dc0028d733d6dddfbfc9f9b0"
integrity sha512-qsX9iRqSc62JGDuBNvddfZgX3Skk5MM8XtuaydnyK3A7LZrP9VuiJZ0+k6zvi2shi+Gg52aAEPJstEieycBKrA==
dependencies:
"@formkit/auto-animate" "^1.0.0-beta.3"
classnames "^2.2.6"