EP adjective game
This commit is contained in:
parent
1550409440
commit
dd2a69985b
|
@ -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.3",
|
"@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/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",
|
||||||
|
|
|
@ -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;
|
|
@ -10,6 +10,7 @@ import InflectionPatterns from "./sub-cores/InflectionPatterns";
|
||||||
import InflectionsWriting from "./sub-cores/InflectionsWriting";
|
import InflectionsWriting from "./sub-cores/InflectionsWriting";
|
||||||
import PerfectVerbsIntransitive from "./sub-cores/PerfectGame";
|
import PerfectVerbsIntransitive from "./sub-cores/PerfectGame";
|
||||||
import NPAdjWriting from "./sub-cores/NPAdjGame";
|
import NPAdjWriting from "./sub-cores/NPAdjGame";
|
||||||
|
import EPAdjGame from "./sub-cores/EPAdjGame";
|
||||||
|
|
||||||
|
|
||||||
// NOUNS
|
// NOUNS
|
||||||
|
@ -398,8 +399,22 @@ export const perfectGameMix = makeGameRecord({
|
||||||
});
|
});
|
||||||
|
|
||||||
// ADJECTIVES
|
// 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({
|
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",
|
id: "adjective-nps-hints",
|
||||||
link: "/phrase-structure/np/",
|
link: "/phrase-structure/np/",
|
||||||
level: "hints",
|
level: "hints",
|
||||||
|
@ -493,6 +508,8 @@ const games: { chapter: string, items: GameRecord[] }[] = [
|
||||||
{
|
{
|
||||||
chapter: "Adjectives",
|
chapter: "Adjectives",
|
||||||
items: [
|
items: [
|
||||||
|
epWithAdjectivesHints,
|
||||||
|
epWithAdjectives,
|
||||||
npWithAdjectivesHints,
|
npWithAdjectivesHints,
|
||||||
npWithAdjectivesNoHints,
|
npWithAdjectivesNoHints,
|
||||||
],
|
],
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,19 +1,14 @@
|
||||||
import GameCore from "../GameCore";
|
import GameCore from "../GameCore";
|
||||||
import {
|
import {
|
||||||
Types as T,
|
Types as T,
|
||||||
Examples,
|
|
||||||
defaultTextOptions as opts,
|
|
||||||
firstVariation,
|
|
||||||
renderNPSelection,
|
renderNPSelection,
|
||||||
getEnglishFromRendered,
|
getEnglishFromRendered,
|
||||||
removeFVarients,
|
|
||||||
concatPsString,
|
concatPsString,
|
||||||
getInflectionPattern,
|
|
||||||
HumanReadableInflectionPattern,
|
|
||||||
} from "@lingdocs/ps-react";
|
} from "@lingdocs/ps-react";
|
||||||
import { makeNPAdjGenerator } from "../../lib/np-adj-generator";
|
import { makeNPAdjGenerator } from "../../lib/np-adj-generator";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { comparePs } from "../../lib/game-utils";
|
import { comparePs } from "../../lib/game-utils";
|
||||||
|
import WordCard from "../../components/WordCard";
|
||||||
|
|
||||||
const amount = 15;
|
const amount = 15;
|
||||||
const timeLimit = 230;
|
const timeLimit = 230;
|
||||||
|
@ -38,9 +33,6 @@ const NPAdjWriting: GameSubCore<Level> = ({ inChapter, id, link, level }: {
|
||||||
}) => {
|
}) => {
|
||||||
const npPool = makeNPAdjGenerator();
|
const npPool = makeNPAdjGenerator();
|
||||||
|
|
||||||
// todo زړې ډاکټرې should work!
|
|
||||||
// feminineplural space issue
|
|
||||||
|
|
||||||
function getQuestion(): Question {
|
function getQuestion(): Question {
|
||||||
const np = npPool();
|
const np = npPool();
|
||||||
const rendered = renderNPSelection(np, false, false, "subject", "none", false);
|
const rendered = renderNPSelection(np, false, false, "subject", "none", false);
|
||||||
|
@ -153,41 +145,4 @@ function DisplayCorrectAnswer({ question }: { question: Question }): 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;
|
export default NPAdjWriting;
|
||||||
|
|
|
@ -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><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 }[] {
|
|
||||||
// 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 addUserAnswer(a: { withBa: boolean, answer: string }, ps: T.PsString): T.PsString {
|
||||||
function addBa(x: T.PsString) {
|
function addBa(x: T.PsString) {
|
||||||
|
|
|
@ -58,7 +58,6 @@ const adjectives = wordQuery("adjectives", [
|
||||||
"droond",
|
"droond",
|
||||||
"loomRey",
|
"loomRey",
|
||||||
"Roond",
|
"Roond",
|
||||||
"droond",
|
|
||||||
"prot",
|
"prot",
|
||||||
"soR",
|
"soR",
|
||||||
"post",
|
"post",
|
||||||
|
|
|
@ -2726,10 +2726,10 @@
|
||||||
rambda "^6.7.0"
|
rambda "^6.7.0"
|
||||||
react-select "^5.2.2"
|
react-select "^5.2.2"
|
||||||
|
|
||||||
"@lingdocs/ps-react@^5.3.1":
|
"@lingdocs/ps-react@^5.3.3":
|
||||||
version "5.3.1"
|
version "5.3.3"
|
||||||
resolved "https://npm.lingdocs.com/@lingdocs%2fps-react/-/ps-react-5.3.1.tgz#a8836d00df5ca884d8b26dd4caae7c720269ce26"
|
resolved "https://npm.lingdocs.com/@lingdocs%2fps-react/-/ps-react-5.3.3.tgz#05066c27059b5133dc0028d733d6dddfbfc9f9b0"
|
||||||
integrity sha512-JY0BQLnxKNjxqHlf7uvmH2ehmbbPvGoWdcxK68ZEZbwbXP0WXHmyFUji6hB9oRHtwrE8MS7guVX0e3/eZTolEA==
|
integrity sha512-qsX9iRqSc62JGDuBNvddfZgX3Skk5MM8XtuaydnyK3A7LZrP9VuiJZ0+k6zvi2shi+Gg52aAEPJstEieycBKrA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@formkit/auto-animate" "^1.0.0-beta.3"
|
"@formkit/auto-animate" "^1.0.0-beta.3"
|
||||||
classnames "^2.2.6"
|
classnames "^2.2.6"
|
||||||
|
|
Loading…
Reference in New Issue