mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-01 18:19:43 +00:00
feat(quiz): couples quiz with shareable watermarked results card
Replaces the notify-me quiz stub with a full loop: quiz sessions per couple, answer-and-guess flow, dual-direction scoring, and a score-only share card (no question or answer content) captured via ViewShot with Sauci branding. API: migration 0019 (quiz_questions, quiz_sessions, quiz_answers, 12 seeded questions), new quiz domain with session start / current / answers / result routes, couple isolation, route + integration + scoring unit tests. Mobile: src/features/quiz feature (intro, per-question self+guess flow, waiting-for-partner nudge, results with breakdown), quizStore with sign-out clearing, QuizShareModal following SharePreviewModal, discovery QuizTile, analytics events, and tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
30aa7bc386
commit
6523ddeb1c
40 files changed
+2973
-237
No files matched your search
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* One partner's guess of the other's self-answer, for a single question. Scoring
|
||||
* always runs both directions: A guessing B and B guessing A each count separately,
|
||||
* so a couple who only ever gets half the guesses right lands at 50%, not 100%.
|
||||
*/
|
||||
export interface QuizAnswerPair {
|
||||
questionId: string;
|
||||
aSelfIndex: number;
|
||||
aGuessIndex: number;
|
||||
bSelfIndex: number;
|
||||
bGuessIndex: number;
|
||||
}
|
||||
|
||||
export interface QuizScoreQuestionResult extends QuizAnswerPair {
|
||||
aGuessedRight: boolean;
|
||||
bGuessedRight: boolean;
|
||||
}
|
||||
|
||||
export interface QuizScoreResult {
|
||||
scorePercent: number;
|
||||
perQuestion: QuizScoreQuestionResult[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure scoring function for a completed quiz session. Each question contributes
|
||||
* up to two hits (partner A's guess about B, and B's guess about A), so the
|
||||
* denominator is twice the question count.
|
||||
*/
|
||||
export function computeQuizScore(pairs: QuizAnswerPair[]): QuizScoreResult {
|
||||
const perQuestion = pairs.map((pair) => ({
|
||||
...pair,
|
||||
aGuessedRight: pair.aGuessIndex === pair.bSelfIndex,
|
||||
bGuessedRight: pair.bGuessIndex === pair.aSelfIndex,
|
||||
}));
|
||||
const hits = perQuestion.reduce(
|
||||
(sum, question) => sum + (question.aGuessedRight ? 1 : 0) + (question.bGuessedRight ? 1 : 0),
|
||||
0,
|
||||
);
|
||||
const scorePercent = pairs.length === 0 ? 0 : Math.round((100 * hits) / (2 * pairs.length));
|
||||
return { scorePercent, perQuestion };
|
||||
}
|
||||
Reference in new issue
Block a user