lila/apps/api/src/services/gameService.ts

89 lines
2.3 KiB
TypeScript

import { randomUUID } from "crypto";
import { getGameTerms, getDistractors } from "@lila/db";
import type {
GameRequest,
GameSession,
GameQuestion,
AnswerOption,
AnswerSubmission,
AnswerResult,
} from "@lila/shared";
import type { GameSessionStore } from "../gameSessionStore/index.js";
import { NotFoundError } from "../errors/AppError.js";
import { shuffleArray } from "../lib/utils.js";
export const createGameSession = async (
request: GameRequest,
store: GameSessionStore,
): Promise<GameSession> => {
const terms = await getGameTerms(
request.source_language,
request.target_language,
request.pos,
request.difficulty,
request.rounds,
);
const answerKey = new Map<string, number>();
const questions: GameQuestion[] = await Promise.all(
terms.map(async (term) => {
const distractorTexts = await getDistractors(
term.termId,
term.targetText,
request.target_language,
request.pos,
request.difficulty,
3,
);
const optionTexts = [term.targetText, ...distractorTexts];
const shuffledTexts = shuffleArray(optionTexts);
const correctOptionId = shuffledTexts.indexOf(term.targetText);
const options: AnswerOption[] = shuffledTexts.map((text, index) => ({
optionId: index,
text,
}));
const questionId = randomUUID();
answerKey.set(questionId, correctOptionId);
return {
questionId,
prompt: term.sourceText,
gloss: term.sourceGloss,
options,
};
}),
);
const sessionId = randomUUID();
await store.create(sessionId, { answers: answerKey });
return { sessionId, questions };
};
export const evaluateAnswer = async (
submission: AnswerSubmission,
store: GameSessionStore,
): Promise<AnswerResult> => {
const session = await store.get(submission.sessionId);
if (!session) {
throw new NotFoundError(`Game session not found: ${submission.sessionId}`);
}
const correctOptionId = session.answers.get(submission.questionId);
if (correctOptionId === undefined) {
throw new NotFoundError(`Question not found: ${submission.questionId}`);
}
return {
questionId: submission.questionId,
isCorrect: submission.selectedOptionId === correctOptionId,
correctOptionId,
selectedOptionId: submission.selectedOptionId,
};
};