diff --git a/src/components/src/vp-explorer/VPExplorerQuiz.tsx b/src/components/src/vp-explorer/VPExplorerQuiz.tsx index 1a017e0..b51518e 100644 --- a/src/components/src/vp-explorer/VPExplorerQuiz.tsx +++ b/src/components/src/vp-explorer/VPExplorerQuiz.tsx @@ -10,524 +10,665 @@ 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, - adjustSubjectSelection, - getObjectSelection, - getObjectSelectionFromBlocks, - getSubjectSelectionFromBlocks, - getSubjectSelection, + adjustObjectSelection, + adjustSubjectSelection, + getObjectSelection, + getObjectSelectionFromBlocks, + getSubjectSelectionFromBlocks, + 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", - answer: { +type QuizState = ( + | { + stage: "multiple choice"; + answer: { ps: T.SingleOrLengthOpts; e?: string[] | undefined; - }, - options: T.PsString[], -} | { - stage: "blanks", - answer: { - ps: T.PsString[], - withBa: boolean, - }, -}) & { - qNumber: number, - vps: T.VPSelectionComplete, - result: "waiting" | "fail", -} + }; + options: T.PsString[]; + } + | { + stage: "blanks"; + answer: { + 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("loading"); - const [showCheck, setShowCheck] = useState(false); - const [answerBlank, setAnswerBlank] = useState(""); - const [withBa, setWithBa] = useState(false); - const [currentCorrectEmoji, setCurrentCorrectEmoji] = useState(randFromArray(correctEmoji)); - useEffect(() => { - setQuizState(tickQuizState(completeVPs(props.vps))); - }, [props.vps]); - if (quizState === "loading") { - return
; + const [quizState, setQuizState] = useState("loading"); + const [showCheck, setShowCheck] = useState(false); + const [answerBlank, setAnswerBlank] = useState(""); + const [withBa, setWithBa] = useState(false); + const [currentCorrectEmoji, setCurrentCorrectEmoji] = useState( + randFromArray(correctEmoji) + ); + useEffect(() => { + setQuizState(tickQuizState(completeVPs(props.vps))); + }, [props.vps]); + if (quizState === "loading") { + return
; + } + function handleRestart() { + if (quizState === "loading") return; + setWithBa(false); + setAnswerBlank(""); + setQuizState(tickQuizState(quizState.vps)); + } + function checkAnswer(a: T.PsString | { text: string; withBa: boolean }) { + if (quizState === "loading") return; + if (!quizState) return; + const correct = + "p" in a + ? isInAnswer(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])}`); + setShowCheck(true); + setTimeout(() => { + setQuizState((o) => { + if (o === "loading") return o; + return tickQuizState(o); + }); + }, checkDuration / 2); + setTimeout(() => { + setShowCheck(false); + }, checkDuration); + // this sucks, have to do this so the emoji doesn't change in the middle of animation + setTimeout(() => { + setCurrentCorrectEmoji(randFromArray(correctEmoji)); + }, checkDuration * 2); + } else { + playAudio(`wrong-${randFromArray([1, 2])}`); + navigator.vibrate(300); + setQuizState({ + ...quizState, + result: "fail", + }); } - function handleRestart() { - if (quizState === "loading") return; - setWithBa(false); - setAnswerBlank(""); - setQuizState(tickQuizState(quizState.vps)); - } - function checkAnswer(a: T.PsString | { text: string, withBa: boolean }) { - if (quizState === "loading") return; - if (!quizState) return; - const correct = "p" in a - ? isInAnswer(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])}`); - setShowCheck(true); - setTimeout(() => { - setQuizState(o => { - if (o === "loading") return o; - return tickQuizState(o); - }); - }, checkDuration / 2); - setTimeout(() => { - setShowCheck(false); - }, checkDuration); - // this sucks, have to do this so the emoji doesn't change in the middle of animation - setTimeout(() => { - setCurrentCorrectEmoji(randFromArray(correctEmoji)); - }, checkDuration * 2); - } else { - playAudio(`wrong-${randFromArray([1,2])}`); - navigator.vibrate(300); - setQuizState({ - ...quizState, - result: "fail", - }); - } - } - const rendered = renderVP(quizState.vps); - const subject: T.Rendered = getSubjectSelectionFromBlocks(rendered.blocks).selection; - const object = getObjectSelectionFromBlocks(rendered.blocks).selection; - const { e } = compileVP(rendered, { removeKing: false, shrinkServant: false }); - return
- -
-
-
Subject
- {subject} -
- {(object !== "none") &&
-
Object
- {object} -
} -
- null} - mode={"quiz"} - /> -
+ } + const rendered = renderVP(quizState.vps); + const subject: T.Rendered = getSubjectSelectionFromBlocks( + rendered.blocks + ).selection; + const object = getObjectSelectionFromBlocks(rendered.blocks).selection; + const { e } = compileVP(rendered, { + removeKing: false, + shrinkServant: false, + }); + return ( +
+ +
+
+
Subject
+ + {subject} +
- {e &&
- {e.map(eLine =>
{eLine}
)} -
} -
-
- {currentCorrectEmoji} -
- {quizState.qNumber === stageLength ? -
-

👏 Congratulations

-

You finished the first two levels!

-

The other levels are still in development... In the meantime have an energy drink.

-
- energy-dring -
- + {object !== "none" && ( +
+
Object
+ + {object} + +
+ )} +
+ null} + mode={"quiz"} + /> +
+
+ {e && ( +
+ {e.map((eLine) => ( +
{eLine}
+ ))} +
+ )} +
+
+ {currentCorrectEmoji} +
+ {quizState.qNumber === stageLength ? ( +
+

👏 Congratulations

+

You finished the first two levels!

+

There may be other levels in the future...

+ +
+ ) : quizState.result === "waiting" ? ( + quizState.stage === "multiple choice" ? ( + <> +
Choose a correct answer:
+ {quizState.options.map((o) => ( +
+
- : (quizState.result === "waiting" - ? (quizState.stage === "multiple choice" ? <> -
Choose a correct answer:
- {quizState.options.map(o =>
- -
)} - :
-
Type the verb in Pashto script to finish the phrase:
-
{ - if (!answerBlank) { - alert("Enter the verb in Pashto script"); - }; - e.preventDefault(); - checkAnswer({ text: answerBlank, withBa }); - }}> -
- setAnswerBlank(e.target.value)} - /> -
-
- setWithBa(e.target.checked)} - id="addBa" - /> - -
- -
-
) - :
-
❌ Wrong 😭
- {quizState.stage === "multiple choice" ? -
-
The correct answer was:
- - {quizState.options.find(x => isInAnswer(x, quizState.answer)) as T.PsString} - -
- : -
-
Possible correct answers were:
- {quizState.answer.ps.map((p, i) =>
- {p} -
)} -
- {("withBa" in quizState.answer && quizState.answer.withBa) ? "With" : "without"} - {` `} - a {baParticle} in the phrase -
-
- } - -
) - } - -
-
; + ))} + + ) : ( +
+
+ Type the verb in Pashto script to finish the + phrase: +
+
{ + if (!answerBlank) { + alert("Enter the verb in Pashto script"); + } + e.preventDefault(); + checkAnswer({ text: answerBlank, withBa }); + }} + > +
+ setAnswerBlank(e.target.value)} + /> +
+
+ setWithBa(e.target.checked)} + id="addBa" + /> + +
+ +
+
+ ) + ) : ( +
+
❌ Wrong 😭
+ {quizState.stage === "multiple choice" ? ( +
+
The correct answer was:
+ + { + quizState.options.find((x) => + isInAnswer(x, quizState.answer) + ) as T.PsString + } + +
+ ) : ( +
+
Possible correct answers were:
+ {quizState.answer.ps.map((p, i) => ( +
+ {p} +
+ ))} +
+ + {"withBa" in quizState.answer && quizState.answer.withBa + ? "With" + : "without"} + + {` `}a {baParticle} in + the phrase +
+
+ )} + +
+ )} + +
+
+ ); } -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) - ); +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); } function ProgressBar({ quizState }: { quizState: QuizState }) { - 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 }); - return `${num}%`; - } - return
-
-
- -
-
- {quizState.stage === "multiple choice" - ? "Level 1: Multiple Choice" - : "Level 2: Type the Verb"} -
-
; + 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, + }); + return `${num}%`; + } + return ( +
+
+
+
+
+ {quizState.stage === "multiple choice" + ? "Level 1: Multiple Choice" + : "Level 2: Type the Verb"} +
+
+ ); } -function QuizNPDisplay({ children, stage, opts }: { - stage: "blanks" | "multiple choice", - children: T.Rendered | T.Person.ThirdPlurMale, - opts: T.TextOptions, +function QuizNPDisplay({ + children, + stage, + opts, +}: { + stage: "blanks" | "multiple choice"; + children: T.Rendered | T.Person.ThirdPlurMale; + opts: T.TextOptions; }) { - return
- {(typeof children === "number") - ?
Unspoken 3rd Pers. Masc. Plur.
- :
- {stage === "blanks" &&
- {children.selection.ps[0]} -
} -
{children.selection.e}
-
} -
; + return ( +
+ {typeof children === "number" ? ( +
Unspoken 3rd Pers. Masc. Plur.
+ ) : ( +
+ {stage === "blanks" && ( +
+ {children.selection.ps[0]} +
+ )} +
{children.selection.e}
+
+ )} +
+ ); } /** * creates a fresh QuizState when a VPSelection is passed * advances a QuizState when a QuizState is passed - * - * @param startingWith - * @returns + * + * @param startingWith + * @returns */ -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 wrongVpsS: T.VPSelectionComplete[] = []; - // don't do the SO switches every time - const wholeTimeSOSwitch = randFromArray([true, false]); - [1, 2, 3].forEach(() => { - let v: T.VPSelectionComplete; - do { - 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, - ); - // eslint-disable-next-line - } 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 stage = beatFirstStage - ? "blanks" - : ("stage" in startingWith ? startingWith.stage : "multiple choice"); - const blanksAnswer = getBlanksAnswer(newVps); - if (stage === "blanks") { - return { - stage, - qNumber: beatFirstStage ? 0 : qNumber, - vps: newVps, - answer: blanksAnswer, - result: "waiting", - }; - } - const answer = makeRes(newVps); - const wrongAnswers = wrongVpsS.map(makeRes); - const allAnswers = shuffleArray([...wrongAnswers, answer]); - const options = allAnswers.map(getOptionFromResult); - const out: QuizState = { - stage, - qNumber: beatFirstStage ? 0 : qNumber, - vps: newVps, - answer, - options, - result: "waiting", - }; - return out; -} - -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 => { - // const y = removeBa(x); - // if (perfectiveHead) { - // return concatPsString(perfectiveHead.ps, y); - // } - // return y; - // }); +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 wrongVpsS: T.VPSelectionComplete[] = []; + // don't do the SO switches every time + const wholeTimeSOSwitch = randFromArray([true, false]); + [1, 2, 3].forEach(() => { + let v: T.VPSelectionComplete; + do { + 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 + ); + // eslint-disable-next-line + } 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 stage = beatFirstStage + ? "blanks" + : "stage" in startingWith + ? startingWith.stage + : "multiple choice"; + const blanksAnswer = getBlanksAnswer(newVps); + if (stage === "blanks") { return { - ps: [{ p: "TOOD", f: "TODO" }], - withBa: false, // verb.block.hasBa, - } + stage, + qNumber: beatFirstStage ? 0 : qNumber, + vps: newVps, + answer: blanksAnswer, + result: "waiting", + }; + } + const answer = makeRes(newVps); + const wrongAnswers = wrongVpsS.map(makeRes); + const allAnswers = shuffleArray([...wrongAnswers, answer]); + const options = allAnswers.map(getOptionFromResult); + const out: QuizState = { + stage, + qNumber: beatFirstStage ? 0 : qNumber, + vps: newVps, + answer, + options, + result: "waiting", + }; + return out; } -function isInAnswer(a: T.PsString, answer: { +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 => { + // const y = removeBa(x); + // if (perfectiveHead) { + // return concatPsString(perfectiveHead.ps, y); + // } + // return y; + // }); + return { + ps: [{ p: "TOOD", f: "TODO" }], + withBa: false, // verb.block.hasBa, + }; +} + +function isInAnswer( + a: T.PsString, + answer: { ps: T.SingleOrLengthOpts; e?: string[] | undefined; -}): boolean { - if ("long" in answer.ps) { - 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 })); - } - return answer.ps.some((x) => psStringEquals(x, a)); + } +): boolean { + if ("long" in answer.ps) { + 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 })) + ); + } + return answer.ps.some((x) => psStringEquals(x, a)); } - function getOptionFromResult(r: { - ps: T.SingleOrLengthOpts; - e?: string[] | undefined; + ps: T.SingleOrLengthOpts; + e?: string[] | undefined; }): T.PsString { - const ps = "long" in r.ps - ? r.ps[randFromArray(["short", "long"] as ("short" | "long")[])] - : r.ps; - // not randomizing version pick (for now) - return ps[0]; + const ps = + "long" in r.ps + ? r.ps[randFromArray(["short", "long"] as ("short" | "long")[])] + : r.ps; + // not randomizing version pick (for now) + return ps[0]; } 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" - ? vpsSubj.selection.person - : undefined; - const oldObj = (typeof vpsObj === "object" && vpsObj.selection.type === "pronoun") - ? vpsObj.selection.person - : undefined; - const { subj, obj } = randomSubjObj( - oldSubj === undefined - ? undefined - : { - subj: oldSubj, - obj: oldObj, - } - ); - const t = getTenseFromVerbSelection(vps.verb); - const verb: T.VerbSelectionComplete = { - ...vps.verb, - tense: isImperativeTense(t) ? "presentVerb" : t, - }; - return { - ...vps, - blocks: adjustObjectSelection( - adjustSubjectSelection(vps.blocks, { - type: "NP", - selection: { - type: "pronoun", - distance: "far", - person: subj, - }, - }), - ( - (typeof vpsObj === "object" && !(vpsObj.selection.type === "noun" && vpsObj.selection.dynamicComplement)) - || - vpsObj === undefined - ) - ? { - type: "NP", - selection: { - type: "pronoun", - distance: "far", - person: obj, - } - } - : vpsObj, - ), - verb, - }; + const vpsSubj = getSubjectSelection(vps.blocks).selection; + const vpsObj = getObjectSelection(vps.blocks).selection; + const oldSubj = + vpsSubj?.selection.type === "pronoun" + ? vpsSubj.selection.person + : undefined; + const oldObj = + typeof vpsObj === "object" && vpsObj.selection.type === "pronoun" + ? vpsObj.selection.person + : undefined; + const { subj, obj } = randomSubjObj( + oldSubj === undefined + ? undefined + : { + subj: oldSubj, + obj: oldObj, + } + ); + const t = getTenseFromVerbSelection(vps.verb); + const verb: T.VerbSelectionComplete = { + ...vps.verb, + tense: isImperativeTense(t) ? "presentVerb" : t, + }; + return { + ...vps, + blocks: adjustObjectSelection( + adjustSubjectSelection(vps.blocks, { + type: "NP", + selection: { + type: "pronoun", + distance: "far", + person: subj, + }, + }), + (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 + ), + verb, + }; } function getRandomVPSelection(mix: MixType = "both") { - // TODO: Type safety to make sure it's safe? - return (VPS: T.VPSelectionComplete): T.VPSelectionComplete => { - const subject = getSubjectSelection(VPS.blocks).selection; - const object = getObjectSelection(VPS.blocks).selection; - const verb = VPS.verb; - const oldSubj = (subject.selection.type === "pronoun") - ? subject.selection.person - : undefined; - const oldObj = (typeof object === "object" && object.selection.type === "pronoun") - ? object.selection.person - : undefined; - const { subj, obj } = randomSubjObj( - oldSubj !== undefined ? { subj: oldSubj, obj: oldObj } : undefined - ); - const randSubj: T.NPSelection = { - type: "NP", - 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" ? { - ...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, - )), - verb: randomizeTense(verb, true), - form: { removeKing: false, shrinkServant: false }, - externalComplement: undefined, + // TODO: Type safety to make sure it's safe? + return (VPS: T.VPSelectionComplete): T.VPSelectionComplete => { + const subject = getSubjectSelection(VPS.blocks).selection; + const object = getObjectSelection(VPS.blocks).selection; + const verb = VPS.verb; + const oldSubj = + subject.selection.type === "pronoun" + ? subject.selection.person + : undefined; + const oldObj = + typeof object === "object" && object.selection.type === "pronoun" + ? object.selection.person + : undefined; + const { subj, obj } = randomSubjObj( + oldSubj !== undefined ? { subj: oldSubj, obj: oldObj } : undefined + ); + const randSubj: T.NPSelection = { + type: "NP", + selection: + subject?.selection.type === "pronoun" + ? { + ...subject.selection, + person: subj, } - } - return { - blocks: possibleShuffleArray(adjustObjectSelection( - adjustSubjectSelection(VPS.blocks, randSubj), - ( - (typeof object === "object" && !(object.selection.type === "noun" && object.selection.dynamicComplement)) - || - object === undefined - ) - ? randObj - : object, - )), - verb: randomizeTense(verb, true), - form: { removeKing: false, shrinkServant: false }, - externalComplement: undefined, - }; + : { + type: "pronoun", + distance: "far", + person: subj, + }, }; -}; + const randObj: T.NPSelection = { + type: "NP", + 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 + ) + ), + verb: randomizeTense(verb, true), + form: { removeKing: false, shrinkServant: false }, + externalComplement: undefined, + }; + } + return { + blocks: possibleShuffleArray( + adjustObjectSelection( + adjustSubjectSelection(VPS.blocks, randSubj), + (typeof object === "object" && + !( + object.selection.type === "noun" && + (object.selection.dynamicComplement || + object.selection.genStativeComplement) + )) || + object === undefined + ? randObj + : object + ) + ), + verb: randomizeTense(verb, true), + form: { removeKing: false, shrinkServant: false }, + externalComplement: undefined, + }; + }; +} function possibleShuffleArray(arr: X[]): X[] { - const willShuffle = randFromArray([true, false, false]); - if (willShuffle) { - return shuffleArray(arr); - } - return arr; + const willShuffle = randFromArray([true, false, false]); + if (willShuffle) { + return shuffleArray(arr); + } + return arr; } -function randomizeTense(verb: T.VerbSelectionComplete, dontRepeatTense: boolean): T.VerbSelectionComplete { - return { - ...verb, - tense: getRandomTense( - dontRepeatTense ? verb.tense : undefined, - ), - }; +function randomizeTense( + verb: T.VerbSelectionComplete, + dontRepeatTense: boolean +): T.VerbSelectionComplete { + return { + ...verb, + tense: getRandomTense(dontRepeatTense ? verb.tense : undefined), + }; } -export default VPExplorerQuiz; \ No newline at end of file +export default VPExplorerQuiz; diff --git a/src/components/src/vp-explorer/energy-drink.jpg b/src/components/src/vp-explorer/energy-drink.jpg deleted file mode 100644 index 36df01e..0000000 Binary files a/src/components/src/vp-explorer/energy-drink.jpg and /dev/null differ diff --git a/src/lib/src/phrase-building/render-vp.ts b/src/lib/src/phrase-building/render-vp.ts index e478ca6..75cbf7f 100644 --- a/src/lib/src/phrase-building/render-vp.ts +++ b/src/lib/src/phrase-building/render-vp.ts @@ -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,