with sandwiches game

This commit is contained in:
adueck 2023-01-14 18:56:02 +05:00
parent dd2a69985b
commit 89261434ee
5 changed files with 130 additions and 28 deletions

View File

@ -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.3", "@lingdocs/ps-react": "^5.4.1",
"@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",

View File

@ -427,6 +427,13 @@ export const npWithAdjectivesNoHints = makeGameRecord({
level: "no-hints", level: "no-hints",
SubCore: NPAdjWriting, SubCore: NPAdjWriting,
}); });
export const npWithAdjectivesInSandwiches = makeGameRecord({
title: "Write the adjective and noun togehter in sandwiches 🥪",
id: "adjective-nps-in-sandwiches",
link: "/phrase-structure/ap/#sandwich-",
level: "sandwiches",
SubCore: NPAdjWriting,
});
const games: { chapter: string, items: GameRecord[] }[] = [ const games: { chapter: string, items: GameRecord[] }[] = [
{ {
@ -512,6 +519,7 @@ const games: { chapter: string, items: GameRecord[] }[] = [
epWithAdjectives, epWithAdjectives,
npWithAdjectivesHints, npWithAdjectivesHints,
npWithAdjectivesNoHints, npWithAdjectivesNoHints,
npWithAdjectivesInSandwiches,
], ],
}, },
]; ];

View File

@ -3,6 +3,11 @@ import {
Types as T, Types as T,
renderNPSelection, renderNPSelection,
getEnglishFromRendered, getEnglishFromRendered,
randFromArray,
getPashtoFromRendered,
renderAPSelection,
InlinePs,
defaultTextOptions as opts,
concatPsString, concatPsString,
} from "@lingdocs/ps-react"; } from "@lingdocs/ps-react";
import { makeNPAdjGenerator } from "../../lib/np-adj-generator"; import { makeNPAdjGenerator } from "../../lib/np-adj-generator";
@ -14,12 +19,82 @@ const amount = 15;
const timeLimit = 230; const timeLimit = 230;
type Question = { type Question = {
selection: T.NPSelection, selection: T.NPSelection | T.APSelection,
answer: T.PsString[], answer: T.PsString[],
english: string, english: string,
}; };
type Level = "hints" | "no-hints"; type Level = "hints" | "no-hints" | "sandwiches";
export const sandwiches: T.Sandwich[] = [
{
type: "sandwich",
before: { p: "له", f: "la" },
after: { p: "نه", f: "na" },
e: "from",
},
{
type: "sandwich",
before: { p: "له", f: "la" },
after: { p: "څخه", f: "tsuxa" },
e: "from",
},
{
type: "sandwich",
before: { p: "له", f: "la" },
after: { p: "سره", f: "sara" },
e: "with",
},
{
type: "sandwich",
before: undefined,
after: { p: "ته", f: "ta" },
e: "to",
},
{
type: "sandwich",
before: { p: "د", f: "du" },
after: { p: "لپاره", f: "lapaara" },
e: "for",
},
{
type: "sandwich",
before: { p: "د", f: "du" },
after: { p: "په څانګ", f: "pu tsaang" },
e: "beside",
},
// {
// type: "sandwich",
// before: { p: "په", f: "pu" },
// after: { p: "کې", f: "ke" },
// e: "in",
// },
{
type: "sandwich",
before: { p: "د", f: "du" },
after: { p: "لاندې", f: "laande" },
e: "under",
},
{
type: "sandwich",
before: { p: "د", f: "du" },
after: { p: "په شان", f: "pu shaan" },
e: "like",
},
{
type: "sandwich",
before: { p: "د", f: "du" },
after: { p: "غوندې", f: "ghwunde" },
e: "like",
},
// {
// type: "sandwich",
// before: { p: "د", f: "du" },
// after: { p: "په اړه", f: "pu aRa" },
// e: "about",
// },
];
// LEVELS // LEVELS
// - without plurals // - without plurals
@ -35,18 +110,20 @@ const NPAdjWriting: GameSubCore<Level> = ({ inChapter, id, link, level }: {
function getQuestion(): Question { function getQuestion(): Question {
const np = npPool(); const np = npPool();
const rendered = renderNPSelection(np, false, false, "subject", "none", false); const selection: T.NPSelection | T.APSelection = level === "sandwiches"
const renderedAdj: T.Rendered<T.AdjectiveSelection> | undefined = rendered.selection.adjectives && rendered.selection.adjectives[0]; ? {
if (!renderedAdj) { type: "AP",
throw new Error("error getting rendered adjective"); selection: {
} ...randFromArray(sandwiches),
const answer = renderedAdj.ps.flatMap((adjPs) => ( inside: np,
rendered.selection.ps.map((nounPs) => ( },
concatPsString(adjPs, " ", nounPs) } : np;
)) const rendered: T.Rendered<T.NPSelection> | T.Rendered<T.APSelection> = selection.type === "AP"
)); ? renderAPSelection(selection, 0) // WOULD BE CLEANER IF THIS WAS JUST A PURE SANDWICH, NOT AT AP
: renderNPSelection(np, false, false, "subject", "none", false);
const answer = getPashtoFromRendered(rendered, false);
return { return {
selection: np, selection,
answer, answer,
english: getEnglishFromRendered(rendered) || "ERROR", english: getEnglishFromRendered(rendered) || "ERROR",
}; };
@ -62,20 +139,29 @@ const NPAdjWriting: GameSubCore<Level> = ({ inChapter, id, link, level }: {
} }
callback(correct); callback(correct);
} }
if (question.selection.type !== "NP" || question.selection.selection.type !== "noun") { if (!(
throw new Error("QUESTION ERROR"); (question.selection.type === "AP" && question.selection.selection.type === "sandwich" && question.selection.selection.inside.selection.type === "noun")
||
(question.selection.type === "NP" && question.selection.selection.type === "noun")
)) {
throw new Error("QUESTION ERROR - BAD SELECTION")
} }
const nounEntry = question.selection.selection.entry; const nounSelection: T.NounSelection = question.selection.type === "AP"
const adjEntry: T.AdjectiveEntry | undefined = question.selection.selection.adjectives[0]?.entry; ? question.selection.selection.inside.selection as T.NounSelection // ts being dumb
: question.selection.selection;
const adjEntry: T.AdjectiveEntry | undefined = nounSelection.adjectives[0]?.entry;
if (!adjEntry) { if (!adjEntry) {
throw new Error("QUESTION ERROR - MISSING ADJECTIVE"); throw new Error("QUESTION ERROR - MISSING ADJECTIVE");
} }
const handleInput = ({ target: { value }}: React.ChangeEvent<HTMLInputElement>) => { const handleInput = ({ target: { value }}: React.ChangeEvent<HTMLInputElement>) => {
setAnswer(value); setAnswer(value);
} }
const sandwich = question.selection.type === "AP"
? question.selection.selection
: undefined;
return <div> return <div>
<div className="my-2" style={{ maxWidth: "300px", margin: "0 auto" }}> <div className="my-2" style={{ maxWidth: "300px", margin: "0 auto" }}>
<div className="d-flex flex-row justify-content-center"> <div className="d-flex flex-row justify-content-center" style={{ gap: "1rem"}}>
<WordCard <WordCard
showHint={level === "hints"} showHint={level === "hints"}
entry={adjEntry} entry={adjEntry}
@ -83,10 +169,15 @@ const NPAdjWriting: GameSubCore<Level> = ({ inChapter, id, link, level }: {
/> />
<WordCard <WordCard
showHint={level === "hints"} showHint={level === "hints"}
entry={nounEntry} entry={nounSelection.entry}
selection={question.selection.selection} selection={nounSelection}
/> />
</div> </div>
{sandwich && <div className="mt-2">
<InlinePs opts={opts}>
{concatPsString(sandwich.before, " ... ", sandwich.after)}
</InlinePs>
</div>}
<div className="my-3 h5"> <div className="my-3 h5">
{question.english} {question.english}
</div> </div>
@ -113,7 +204,7 @@ const NPAdjWriting: GameSubCore<Level> = ({ inChapter, id, link, level }: {
function Instructions() { function Instructions() {
return <div> return <div>
<p className="lead">Write the adjective and noun together with the proper inflections.</p> <p className="lead">Write the {level === "sandwiches" ? "sandwich including the" : ""} adjective and noun together with the proper inflections.</p>
</div> </div>
} }

View File

@ -29,7 +29,10 @@ export function compareF(input: string, answer: string): boolean {
} }
export function comparePs(inputRaw: 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, " "); function cleanSpaces(s: string): string {
return s.replace(/\s+/g, " ");
}
const input = cleanSpaces(inputRaw);
if ("long" in answer) { if ("long" in answer) {
return comparePs(input, flattenLengths(answer)); return comparePs(input, flattenLengths(answer));
} }
@ -39,5 +42,5 @@ export function comparePs(inputRaw: string, answer: T.SingleOrLengthOpts<T.PsStr
const stand = standardizePhonetics( const stand = standardizePhonetics(
standardizePashto(input) standardizePashto(input)
).trim(); ).trim();
return stand === answer.p || compareF(stand, answer.f); return stand === cleanSpaces(answer.p) || compareF(stand, cleanSpaces(answer.f));
} }

View File

@ -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.3": "@lingdocs/ps-react@^5.4.1":
version "5.3.3" version "5.4.1"
resolved "https://npm.lingdocs.com/@lingdocs%2fps-react/-/ps-react-5.3.3.tgz#05066c27059b5133dc0028d733d6dddfbfc9f9b0" resolved "https://npm.lingdocs.com/@lingdocs%2fps-react/-/ps-react-5.4.1.tgz#60ce096dd36910d04523af3c7e74143a583743f2"
integrity sha512-qsX9iRqSc62JGDuBNvddfZgX3Skk5MM8XtuaydnyK3A7LZrP9VuiJZ0+k6zvi2shi+Gg52aAEPJstEieycBKrA== integrity sha512-wczQzyO/BYasmpf67R4FgjTG2OC9o++jeIQM7bMThl52Zw9rexDEy3H1+WITRdVOf+Ug//8YiAhpBlrB/nBmAA==
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"