beta new verb engine
This commit is contained in:
parent
3b6d013402
commit
5edf0d1e02
|
@ -10,11 +10,14 @@ import { psStringEquals } from "../../../lib/src/p-text-helpers";
|
|||
import { renderVP } from "../../../lib/src/phrase-building/render-vp";
|
||||
import { compileVP } from "../../../lib/src/phrase-building/compile";
|
||||
import { getRandomTense } from "./TensePicker";
|
||||
import { getTenseFromVerbSelection, removeBa, switchSubjObj } from "../../../lib/src/phrase-building/vp-tools";
|
||||
import {
|
||||
getTenseFromVerbSelection,
|
||||
removeBa,
|
||||
switchSubjObj,
|
||||
} from "../../../lib/src/phrase-building/vp-tools";
|
||||
import playAudio from "../play-audio";
|
||||
import TensePicker from "./TensePicker";
|
||||
import Keyframes from "../Keyframes";
|
||||
import energyDrink from "./energy-drink.jpg";
|
||||
import { isImperativeTense } from "../../../lib/src/type-predicates";
|
||||
import {
|
||||
adjustObjectSelection,
|
||||
|
@ -25,51 +28,71 @@ import {
|
|||
getSubjectSelection,
|
||||
} from "../../../lib/src/phrase-building/blocks-utils";
|
||||
|
||||
const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"];
|
||||
const correctEmoji = [
|
||||
"✅",
|
||||
"🤓",
|
||||
"✅",
|
||||
"😊",
|
||||
"🌹",
|
||||
"✅",
|
||||
"✅",
|
||||
"🥳",
|
||||
"👏",
|
||||
"✅",
|
||||
"💯",
|
||||
"😎",
|
||||
"✅",
|
||||
"👍",
|
||||
];
|
||||
|
||||
const answerFeedback: CSSProperties = {
|
||||
"fontSize": "4rem",
|
||||
"transition": "opacity 0.3s ease-in",
|
||||
"opacity": 0.9,
|
||||
"position": "fixed",
|
||||
"top": "60%",
|
||||
"left": "50%",
|
||||
"zIndex": 99999999,
|
||||
"transform": "translate(-50%, -50%)",
|
||||
}
|
||||
fontSize: "4rem",
|
||||
transition: "opacity 0.3s ease-in",
|
||||
opacity: 0.9,
|
||||
position: "fixed",
|
||||
top: "60%",
|
||||
left: "50%",
|
||||
zIndex: 99999999,
|
||||
transform: "translate(-50%, -50%)",
|
||||
};
|
||||
|
||||
const checkDuration = 400;
|
||||
const stageLength = 5;
|
||||
|
||||
type QuizState = ({
|
||||
stage: "multiple choice",
|
||||
type QuizState = (
|
||||
| {
|
||||
stage: "multiple choice";
|
||||
answer: {
|
||||
ps: T.SingleOrLengthOpts<T.PsString[]>;
|
||||
e?: string[] | undefined;
|
||||
},
|
||||
options: T.PsString[],
|
||||
} | {
|
||||
stage: "blanks",
|
||||
};
|
||||
options: T.PsString[];
|
||||
}
|
||||
| {
|
||||
stage: "blanks";
|
||||
answer: {
|
||||
ps: T.PsString[],
|
||||
withBa: boolean,
|
||||
},
|
||||
}) & {
|
||||
qNumber: number,
|
||||
vps: T.VPSelectionComplete,
|
||||
result: "waiting" | "fail",
|
||||
}
|
||||
ps: T.PsString[];
|
||||
withBa: boolean;
|
||||
};
|
||||
}
|
||||
) & {
|
||||
qNumber: number;
|
||||
vps: T.VPSelectionComplete;
|
||||
result: "waiting" | "fail";
|
||||
};
|
||||
type MixType = "NPs" | "tenses" | "both";
|
||||
|
||||
function VPExplorerQuiz(props: {
|
||||
opts: T.TextOptions,
|
||||
vps: T.VPSelectionState,
|
||||
opts: T.TextOptions;
|
||||
vps: T.VPSelectionState;
|
||||
}) {
|
||||
const [quizState, setQuizState] = useState<QuizState | "loading">("loading");
|
||||
const [showCheck, setShowCheck] = useState<boolean>(false);
|
||||
const [answerBlank, setAnswerBlank] = useState<string>("");
|
||||
const [withBa, setWithBa] = useState<boolean>(false);
|
||||
const [currentCorrectEmoji, setCurrentCorrectEmoji] = useState<string>(randFromArray(correctEmoji));
|
||||
const [currentCorrectEmoji, setCurrentCorrectEmoji] = useState<string>(
|
||||
randFromArray(correctEmoji)
|
||||
);
|
||||
useEffect(() => {
|
||||
setQuizState(tickQuizState(completeVPs(props.vps)));
|
||||
}, [props.vps]);
|
||||
|
@ -82,21 +105,22 @@ function VPExplorerQuiz(props: {
|
|||
setAnswerBlank("");
|
||||
setQuizState(tickQuizState(quizState.vps));
|
||||
}
|
||||
function checkAnswer(a: T.PsString | { text: string, withBa: boolean }) {
|
||||
function checkAnswer(a: T.PsString | { text: string; withBa: boolean }) {
|
||||
if (quizState === "loading") return;
|
||||
if (!quizState) return;
|
||||
const correct = "p" in a
|
||||
const correct =
|
||||
"p" in a
|
||||
? isInAnswer(a, quizState.answer)
|
||||
// @ts-ignore // TODO: CLEANUP
|
||||
: blanksAnswerCorrect(a, quizState.answer);
|
||||
: // @ts-ignore // TODO: CLEANUP
|
||||
blanksAnswerCorrect(a, quizState.answer);
|
||||
setAnswerBlank("");
|
||||
setWithBa(false);
|
||||
if (correct) {
|
||||
const toPlay = randFromArray([true, false, false]);
|
||||
if (toPlay) playAudio(`correct-${randFromArray([1,2,3])}`);
|
||||
if (toPlay) playAudio(`correct-${randFromArray([1, 2, 3])}`);
|
||||
setShowCheck(true);
|
||||
setTimeout(() => {
|
||||
setQuizState(o => {
|
||||
setQuizState((o) => {
|
||||
if (o === "loading") return o;
|
||||
return tickQuizState(o);
|
||||
});
|
||||
|
@ -109,7 +133,7 @@ function VPExplorerQuiz(props: {
|
|||
setCurrentCorrectEmoji(randFromArray(correctEmoji));
|
||||
}, checkDuration * 2);
|
||||
} else {
|
||||
playAudio(`wrong-${randFromArray([1,2])}`);
|
||||
playAudio(`wrong-${randFromArray([1, 2])}`);
|
||||
navigator.vibrate(300);
|
||||
setQuizState({
|
||||
...quizState,
|
||||
|
@ -118,20 +142,32 @@ function VPExplorerQuiz(props: {
|
|||
}
|
||||
}
|
||||
const rendered = renderVP(quizState.vps);
|
||||
const subject: T.Rendered<T.NPSelection> = getSubjectSelectionFromBlocks(rendered.blocks).selection;
|
||||
const subject: T.Rendered<T.NPSelection> = getSubjectSelectionFromBlocks(
|
||||
rendered.blocks
|
||||
).selection;
|
||||
const object = getObjectSelectionFromBlocks(rendered.blocks).selection;
|
||||
const { e } = compileVP(rendered, { removeKing: false, shrinkServant: false });
|
||||
return <div className="mt-4">
|
||||
const { e } = compileVP(rendered, {
|
||||
removeKing: false,
|
||||
shrinkServant: false,
|
||||
});
|
||||
return (
|
||||
<div className="mt-4">
|
||||
<ProgressBar quizState={quizState} />
|
||||
<div className="d-flex flex-row justify-content-around flex-wrap">
|
||||
<div className="my-2">
|
||||
<div className="h5 text-center">Subject</div>
|
||||
<QuizNPDisplay opts={props.opts} stage={quizState.stage}>{subject}</QuizNPDisplay>
|
||||
<QuizNPDisplay opts={props.opts} stage={quizState.stage}>
|
||||
{subject}
|
||||
</QuizNPDisplay>
|
||||
</div>
|
||||
{(object !== "none") && <div className="my-2">
|
||||
{object !== "none" && (
|
||||
<div className="my-2">
|
||||
<div className="h5 text-center">Object</div>
|
||||
<QuizNPDisplay opts={props.opts} stage={quizState.stage}>{object}</QuizNPDisplay>
|
||||
</div>}
|
||||
<QuizNPDisplay opts={props.opts} stage={quizState.stage}>
|
||||
{object}
|
||||
</QuizNPDisplay>
|
||||
</div>
|
||||
)}
|
||||
<div className="my-2">
|
||||
<TensePicker
|
||||
vpsComplete={quizState.vps}
|
||||
|
@ -140,52 +176,81 @@ function VPExplorerQuiz(props: {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
{e && <div className="text-center text-muted">
|
||||
{e.map(eLine => <div key={eLine}>{eLine}</div>)}
|
||||
</div>}
|
||||
{e && (
|
||||
<div className="text-center text-muted">
|
||||
{e.map((eLine) => (
|
||||
<div key={eLine}>{eLine}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-center">
|
||||
<div style={showCheck ? answerFeedback : { ...answerFeedback, opacity: 0 } as any}>
|
||||
<div
|
||||
style={
|
||||
showCheck
|
||||
? answerFeedback
|
||||
: ({ ...answerFeedback, opacity: 0 } as any)
|
||||
}
|
||||
>
|
||||
{currentCorrectEmoji}
|
||||
</div>
|
||||
{quizState.qNumber === stageLength ?
|
||||
{quizState.qNumber === stageLength ? (
|
||||
<div className="mt-4" style={{ animation: "fade-in 0.5s" }}>
|
||||
<h4>👏 Congratulations</h4>
|
||||
<p className="lead">You finished the first two levels!</p>
|
||||
<p>The <strong>other levels are still in development</strong>... In the meantime have an energy drink.</p>
|
||||
<div className="mb-4">
|
||||
<img src={energyDrink} alt="energy-dring" className="img-fluid" />
|
||||
</div>
|
||||
<button type="button" className="btn btn-primary" onClick={handleRestart}>
|
||||
<p>There may be other levels in the future...</p>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={handleRestart}
|
||||
>
|
||||
Restart
|
||||
</button>
|
||||
</div>
|
||||
: (quizState.result === "waiting"
|
||||
? (quizState.stage === "multiple choice" ? <>
|
||||
) : quizState.result === "waiting" ? (
|
||||
quizState.stage === "multiple choice" ? (
|
||||
<>
|
||||
<div className="text-muted my-3">Choose a correct answer:</div>
|
||||
{quizState.options.map(o => <div className="pb-3" key={o.f} style={{ animation: "fade-in 0.5s" }}>
|
||||
{quizState.options.map((o) => (
|
||||
<div
|
||||
className="pb-3"
|
||||
key={o.f}
|
||||
style={{ animation: "fade-in 0.5s" }}
|
||||
>
|
||||
<button
|
||||
className="btn btn-answer btn-outline-secondary"
|
||||
onClick={() => checkAnswer(o)}>
|
||||
onClick={() => checkAnswer(o)}
|
||||
>
|
||||
<InlinePs opts={props.opts}>{o}</InlinePs>
|
||||
</button>
|
||||
</div>)}
|
||||
</> : <div>
|
||||
<div className="text-muted my-3">Type the <strong>verb in Pashto script</strong> to finish the phrase:</div>
|
||||
<form onSubmit={e => {
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<div className="text-muted my-3">
|
||||
Type the <strong>verb in Pashto script</strong> to finish the
|
||||
phrase:
|
||||
</div>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
if (!answerBlank) {
|
||||
alert("Enter the verb in Pashto script");
|
||||
};
|
||||
}
|
||||
e.preventDefault();
|
||||
checkAnswer({ text: answerBlank, withBa });
|
||||
}}>
|
||||
<div className="mb-3" style={{ maxWidth: "250px", margin: "0 auto"}}>
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="mb-3"
|
||||
style={{ maxWidth: "250px", margin: "0 auto" }}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
dir="auto"
|
||||
className="form-control"
|
||||
placeholder="type verb here"
|
||||
value={answerBlank}
|
||||
onChange={e => setAnswerBlank(e.target.value)}
|
||||
onChange={(e) => setAnswerBlank(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-check mb-4" style={{ fontSize: "large" }}>
|
||||
|
@ -193,102 +258,142 @@ function VPExplorerQuiz(props: {
|
|||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={withBa}
|
||||
onChange={e => setWithBa(e.target.checked)}
|
||||
onChange={(e) => setWithBa(e.target.checked)}
|
||||
id="addBa"
|
||||
/>
|
||||
<label className="form-check-label text-muted" htmlFor="addBa">
|
||||
add <InlinePs opts={props.opts}>{baParticle}</InlinePs> in kids' section
|
||||
<label
|
||||
className="form-check-label text-muted"
|
||||
htmlFor="addBa"
|
||||
>
|
||||
add <InlinePs opts={props.opts}>{baParticle}</InlinePs> in
|
||||
kids' section
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Check
|
||||
</button>
|
||||
</form>
|
||||
</div>)
|
||||
: <div style={{ animation: "fade-in 0.5s" }}>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div style={{ animation: "fade-in 0.5s" }}>
|
||||
<div className="h4 mt-4">❌ Wrong 😭</div>
|
||||
{quizState.stage === "multiple choice" ?
|
||||
{quizState.stage === "multiple choice" ? (
|
||||
<div>
|
||||
<div className="my-4 lead">The correct answer was:</div>
|
||||
<InlinePs opts={props.opts}>
|
||||
{quizState.options.find(x => isInAnswer(x, quizState.answer)) as T.PsString}
|
||||
{
|
||||
quizState.options.find((x) =>
|
||||
isInAnswer(x, quizState.answer)
|
||||
) as T.PsString
|
||||
}
|
||||
</InlinePs>
|
||||
</div>
|
||||
:
|
||||
) : (
|
||||
<div>
|
||||
<div className="my-4 lead">Possible correct answers were:</div>
|
||||
{quizState.answer.ps.map((p, i) => <div key={i}>
|
||||
{quizState.answer.ps.map((p, i) => (
|
||||
<div key={i}>
|
||||
<InlinePs opts={props.opts}>{p}</InlinePs>
|
||||
</div>)}
|
||||
</div>
|
||||
))}
|
||||
<div className="mt-2">
|
||||
<strong>{("withBa" in quizState.answer && quizState.answer.withBa) ? "With" : "without"}</strong>
|
||||
{` `}
|
||||
a <InlinePs opts={props.opts}>{baParticle}</InlinePs> in the phrase
|
||||
<strong>
|
||||
{"withBa" in quizState.answer && quizState.answer.withBa
|
||||
? "With"
|
||||
: "without"}
|
||||
</strong>
|
||||
{` `}a <InlinePs opts={props.opts}>{baParticle}</InlinePs> in
|
||||
the phrase
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<button type="button" className="btn btn-primary mt-4" onClick={handleRestart}>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary mt-4"
|
||||
onClick={handleRestart}
|
||||
>
|
||||
Try Again
|
||||
</button>
|
||||
</div>)
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
<Keyframes name="fade-in" from={{ opacity: 0 }} to={{ opacity: 1 }} />
|
||||
</div>
|
||||
</div>;
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function blanksAnswerCorrect(a: { text: string, withBa: boolean }, answer: { ps: T.PsString[], withBa?: boolean }): boolean {
|
||||
function blanksAnswerCorrect(
|
||||
a: { text: string; withBa: boolean },
|
||||
answer: { ps: T.PsString[]; withBa?: boolean }
|
||||
): boolean {
|
||||
const p = standardizePashto(a.text).trim();
|
||||
const given = removeBa({ p, f: "" }).p;
|
||||
return (
|
||||
a.withBa === answer.withBa
|
||||
&&
|
||||
answer.ps.some(x => x.p === given)
|
||||
);
|
||||
return a.withBa === answer.withBa && answer.ps.some((x) => x.p === given);
|
||||
}
|
||||
|
||||
function ProgressBar({ quizState }: { quizState: QuizState }) {
|
||||
function getPercentageDone({ current, total }: { current: number, total: number }): number {
|
||||
return Math.round(
|
||||
(current / total) * 100
|
||||
);
|
||||
function getPercentageDone({
|
||||
current,
|
||||
total,
|
||||
}: {
|
||||
current: number;
|
||||
total: number;
|
||||
}): number {
|
||||
return Math.round((current / total) * 100);
|
||||
}
|
||||
function getProgressWidth(): string {
|
||||
const num = getPercentageDone({ current: quizState.qNumber, total: stageLength });
|
||||
const num = getPercentageDone({
|
||||
current: quizState.qNumber,
|
||||
total: stageLength,
|
||||
});
|
||||
return `${num}%`;
|
||||
}
|
||||
return <div className="mb-3">
|
||||
return (
|
||||
<div className="mb-3">
|
||||
<div className="progress mb-1" style={{ height: "3px" }}>
|
||||
<div
|
||||
className={`progress-bar bg-${quizState.result === "fail" ? "danger" : "primary"}`}
|
||||
className={`progress-bar bg-${
|
||||
quizState.result === "fail" ? "danger" : "primary"
|
||||
}`}
|
||||
role="progressbar"
|
||||
style={{ width: getProgressWidth() }}
|
||||
/>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
{quizState.stage === "multiple choice"
|
||||
? "Level 1: Multiple Choice"
|
||||
: "Level 2: Type the Verb"}
|
||||
</div>
|
||||
</div>;
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function QuizNPDisplay({ children, stage, opts }: {
|
||||
stage: "blanks" | "multiple choice",
|
||||
children: T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale,
|
||||
opts: T.TextOptions,
|
||||
function QuizNPDisplay({
|
||||
children,
|
||||
stage,
|
||||
opts,
|
||||
}: {
|
||||
stage: "blanks" | "multiple choice";
|
||||
children: T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale;
|
||||
opts: T.TextOptions;
|
||||
}) {
|
||||
return <div className="mb-3">
|
||||
{(typeof children === "number")
|
||||
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||
: <div className="text-centered" style={{ fontSize: "large" }}>
|
||||
{stage === "blanks" && <div>
|
||||
return (
|
||||
<div className="mb-3">
|
||||
{typeof children === "number" ? (
|
||||
<div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||
) : (
|
||||
<div className="text-centered" style={{ fontSize: "large" }}>
|
||||
{stage === "blanks" && (
|
||||
<div>
|
||||
<InlinePs opts={opts}>{children.selection.ps[0]}</InlinePs>
|
||||
</div>}
|
||||
</div>
|
||||
)}
|
||||
<div>{children.selection.e}</div>
|
||||
</div>}
|
||||
</div>;
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -298,13 +403,18 @@ function QuizNPDisplay({ children, stage, opts }: {
|
|||
* @param startingWith
|
||||
* @returns
|
||||
*/
|
||||
function tickQuizState(startingWith: T.VPSelectionComplete | QuizState): QuizState {
|
||||
function tickQuizState(
|
||||
startingWith: T.VPSelectionComplete | QuizState
|
||||
): QuizState {
|
||||
function makeRes(x: T.VPSelectionComplete) {
|
||||
return compileVP(renderVP(x), { removeKing: false, shrinkServant: false });
|
||||
}
|
||||
const oldVps = "stage" in startingWith ? startingWith.vps : startingWith;
|
||||
// for now, always inforce positive
|
||||
const newVps = getRandomVPSelection("both")({ ...oldVps, verb: { ...oldVps.verb, negative: false }});
|
||||
const newVps = getRandomVPSelection("both")({
|
||||
...oldVps,
|
||||
verb: { ...oldVps.verb, negative: false },
|
||||
});
|
||||
const wrongVpsS: T.VPSelectionComplete[] = [];
|
||||
// don't do the SO switches every time
|
||||
const wholeTimeSOSwitch = randFromArray([true, false]);
|
||||
|
@ -314,17 +424,22 @@ function tickQuizState(startingWith: T.VPSelectionComplete | QuizState): QuizSta
|
|||
const SOSwitch = wholeTimeSOSwitch && randFromArray([true, false]);
|
||||
// TODO: if switich subj and obj, include the tense being correct maybe
|
||||
v = getRandomVPSelection("tenses")(
|
||||
SOSwitch ? switchSubjObj(newVps) : newVps,
|
||||
SOSwitch ? switchSubjObj(newVps) : newVps
|
||||
);
|
||||
// eslint-disable-next-line
|
||||
} while (wrongVpsS.find(x => x.verb.tense === v.verb.tense));
|
||||
} while (wrongVpsS.find((x) => x.verb.tense === v.verb.tense));
|
||||
wrongVpsS.push(v);
|
||||
});
|
||||
const qNumber = "stage" in startingWith ? (startingWith.qNumber + 1) : 0;
|
||||
const beatFirstStage = "stage" in startingWith && (qNumber === stageLength) && startingWith.stage === "multiple choice";
|
||||
const qNumber = "stage" in startingWith ? startingWith.qNumber + 1 : 0;
|
||||
const beatFirstStage =
|
||||
"stage" in startingWith &&
|
||||
qNumber === stageLength &&
|
||||
startingWith.stage === "multiple choice";
|
||||
const stage = beatFirstStage
|
||||
? "blanks"
|
||||
: ("stage" in startingWith ? startingWith.stage : "multiple choice");
|
||||
: "stage" in startingWith
|
||||
? startingWith.stage
|
||||
: "multiple choice";
|
||||
const blanksAnswer = getBlanksAnswer(newVps);
|
||||
if (stage === "blanks") {
|
||||
return {
|
||||
|
@ -350,7 +465,10 @@ function tickQuizState(startingWith: T.VPSelectionComplete | QuizState): QuizSta
|
|||
return out;
|
||||
}
|
||||
|
||||
function getBlanksAnswer(vps: T.VPSelectionComplete): { ps: T.PsString[], withBa: boolean } {
|
||||
function getBlanksAnswer(vps: T.VPSelectionComplete): {
|
||||
ps: T.PsString[];
|
||||
withBa: boolean;
|
||||
} {
|
||||
// TODO: !!!
|
||||
// const { verb, perfectiveHead } = getVerbAndHeadFromBlocks(renderVP(vps).blocks);
|
||||
// const ps = flattenLengths(verb.block.ps).map(x => {
|
||||
|
@ -363,27 +481,32 @@ function getBlanksAnswer(vps: T.VPSelectionComplete): { ps: T.PsString[], withBa
|
|||
return {
|
||||
ps: [{ p: "TOOD", f: "TODO" }],
|
||||
withBa: false, // verb.block.hasBa,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function isInAnswer(a: T.PsString, answer: {
|
||||
function isInAnswer(
|
||||
a: T.PsString,
|
||||
answer: {
|
||||
ps: T.SingleOrLengthOpts<T.PsString[]>;
|
||||
e?: string[] | undefined;
|
||||
}): boolean {
|
||||
}
|
||||
): boolean {
|
||||
if ("long" in answer.ps) {
|
||||
return isInAnswer(a, { ...answer, ps: answer.ps.long }) ||
|
||||
return (
|
||||
isInAnswer(a, { ...answer, ps: answer.ps.long }) ||
|
||||
isInAnswer(a, { ...answer, ps: answer.ps.short }) ||
|
||||
!!(answer.ps.mini && isInAnswer(a, { ...answer, ps: answer.ps.mini }));
|
||||
!!(answer.ps.mini && isInAnswer(a, { ...answer, ps: answer.ps.mini }))
|
||||
);
|
||||
}
|
||||
return answer.ps.some((x) => psStringEquals(x, a));
|
||||
}
|
||||
|
||||
|
||||
function getOptionFromResult(r: {
|
||||
ps: T.SingleOrLengthOpts<T.PsString[]>;
|
||||
e?: string[] | undefined;
|
||||
}): T.PsString {
|
||||
const ps = "long" in r.ps
|
||||
const ps =
|
||||
"long" in r.ps
|
||||
? r.ps[randFromArray(["short", "long"] as ("short" | "long")[])]
|
||||
: r.ps;
|
||||
// not randomizing version pick (for now)
|
||||
|
@ -393,10 +516,12 @@ function getOptionFromResult(r: {
|
|||
function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
||||
const vpsSubj = getSubjectSelection(vps.blocks).selection;
|
||||
const vpsObj = getObjectSelection(vps.blocks).selection;
|
||||
const oldSubj = vpsSubj?.selection.type === "pronoun"
|
||||
const oldSubj =
|
||||
vpsSubj?.selection.type === "pronoun"
|
||||
? vpsSubj.selection.person
|
||||
: undefined;
|
||||
const oldObj = (typeof vpsObj === "object" && vpsObj.selection.type === "pronoun")
|
||||
const oldObj =
|
||||
typeof vpsObj === "object" && vpsObj.selection.type === "pronoun"
|
||||
? vpsObj.selection.person
|
||||
: undefined;
|
||||
const { subj, obj } = randomSubjObj(
|
||||
|
@ -423,20 +548,22 @@ function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
|||
person: subj,
|
||||
},
|
||||
}),
|
||||
(
|
||||
(typeof vpsObj === "object" && !(vpsObj.selection.type === "noun" && vpsObj.selection.dynamicComplement))
|
||||
||
|
||||
(typeof vpsObj === "object" &&
|
||||
!(
|
||||
vpsObj.selection.type === "noun" &&
|
||||
(vpsObj.selection.dynamicComplement ||
|
||||
vpsObj.selection.genStativeComplement)
|
||||
)) ||
|
||||
vpsObj === undefined
|
||||
)
|
||||
? {
|
||||
type: "NP",
|
||||
selection: {
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
person: obj,
|
||||
},
|
||||
}
|
||||
}
|
||||
: vpsObj,
|
||||
: vpsObj
|
||||
),
|
||||
verb,
|
||||
};
|
||||
|
@ -448,10 +575,12 @@ function getRandomVPSelection(mix: MixType = "both") {
|
|||
const subject = getSubjectSelection(VPS.blocks).selection;
|
||||
const object = getObjectSelection(VPS.blocks).selection;
|
||||
const verb = VPS.verb;
|
||||
const oldSubj = (subject.selection.type === "pronoun")
|
||||
const oldSubj =
|
||||
subject.selection.type === "pronoun"
|
||||
? subject.selection.person
|
||||
: undefined;
|
||||
const oldObj = (typeof object === "object" && object.selection.type === "pronoun")
|
||||
const oldObj =
|
||||
typeof object === "object" && object.selection.type === "pronoun"
|
||||
? object.selection.person
|
||||
: undefined;
|
||||
const { subj, obj } = randomSubjObj(
|
||||
|
@ -459,59 +588,70 @@ function getRandomVPSelection(mix: MixType = "both") {
|
|||
);
|
||||
const randSubj: T.NPSelection = {
|
||||
type: "NP",
|
||||
selection: (
|
||||
subject?.selection.type === "pronoun" ? {
|
||||
selection:
|
||||
subject?.selection.type === "pronoun"
|
||||
? {
|
||||
...subject.selection,
|
||||
person: subj,
|
||||
} : {
|
||||
}
|
||||
: {
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
person: subj,
|
||||
}
|
||||
),
|
||||
},
|
||||
};
|
||||
const randObj: T.NPSelection = {
|
||||
type: "NP",
|
||||
selection: (
|
||||
typeof object === "object" && object.selection.type === "pronoun" ? {
|
||||
selection:
|
||||
typeof object === "object" && object.selection.type === "pronoun"
|
||||
? {
|
||||
...object.selection,
|
||||
person: obj,
|
||||
} : {
|
||||
}
|
||||
: {
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
person: obj,
|
||||
}
|
||||
),
|
||||
},
|
||||
};
|
||||
// ensure that the verb selection is complete
|
||||
if (mix === "tenses") {
|
||||
return {
|
||||
blocks: possibleShuffleArray(adjustObjectSelection(
|
||||
adjustSubjectSelection(VPS.blocks, subject !== undefined ? subject : randSubj),
|
||||
object !== undefined ? object : randObj,
|
||||
)),
|
||||
blocks: possibleShuffleArray(
|
||||
adjustObjectSelection(
|
||||
adjustSubjectSelection(
|
||||
VPS.blocks,
|
||||
subject !== undefined ? subject : randSubj
|
||||
),
|
||||
object !== undefined ? object : randObj
|
||||
)
|
||||
),
|
||||
verb: randomizeTense(verb, true),
|
||||
form: { removeKing: false, shrinkServant: false },
|
||||
externalComplement: undefined,
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
blocks: possibleShuffleArray(adjustObjectSelection(
|
||||
blocks: possibleShuffleArray(
|
||||
adjustObjectSelection(
|
||||
adjustSubjectSelection(VPS.blocks, randSubj),
|
||||
(
|
||||
(typeof object === "object" && !(object.selection.type === "noun" && object.selection.dynamicComplement))
|
||||
||
|
||||
(typeof object === "object" &&
|
||||
!(
|
||||
object.selection.type === "noun" &&
|
||||
(object.selection.dynamicComplement ||
|
||||
object.selection.genStativeComplement)
|
||||
)) ||
|
||||
object === undefined
|
||||
)
|
||||
? randObj
|
||||
: object,
|
||||
)),
|
||||
: object
|
||||
)
|
||||
),
|
||||
verb: randomizeTense(verb, true),
|
||||
form: { removeKing: false, shrinkServant: false },
|
||||
externalComplement: undefined,
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function possibleShuffleArray<X>(arr: X[]): X[] {
|
||||
const willShuffle = randFromArray([true, false, false]);
|
||||
|
@ -521,12 +661,13 @@ function possibleShuffleArray<X>(arr: X[]): X[] {
|
|||
return arr;
|
||||
}
|
||||
|
||||
function randomizeTense(verb: T.VerbSelectionComplete, dontRepeatTense: boolean): T.VerbSelectionComplete {
|
||||
function randomizeTense(
|
||||
verb: T.VerbSelectionComplete,
|
||||
dontRepeatTense: boolean
|
||||
): T.VerbSelectionComplete {
|
||||
return {
|
||||
...verb,
|
||||
tense: getRandomTense(
|
||||
dontRepeatTense ? verb.tense : undefined,
|
||||
),
|
||||
tense: getRandomTense(dontRepeatTense ? verb.tense : undefined),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 6.8 KiB |
|
@ -81,7 +81,11 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
|||
kids: getVPKids(hasBa, VP.blocks, VP.form, king),
|
||||
englishBase: renderEnglishVPBase({
|
||||
subjectPerson,
|
||||
object: VP.verb.isCompound === "dynamic" ? "none" : object,
|
||||
object:
|
||||
VP.verb.isCompound === "dynamic" ||
|
||||
VP.verb.isCompound === "generative stative"
|
||||
? "none"
|
||||
: object,
|
||||
vs: VP.verb,
|
||||
}),
|
||||
form: VP.form,
|
||||
|
|
Loading…
Reference in New Issue