153 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { usePrisma } from "../../../../utils/prisma";
interface MemoryCard {
id: number;
emoji: string;
matchedBy: "X" | "O" | null;
}
interface MemoryState {
cards: MemoryCard[];
flipped: number[];
scores: { X: number; O: number };
mismatchRevealed: boolean;
}
export default defineEventHandler(async (event) => {
const user = await requireUser(event);
const id = getRouterParam(event, "id");
if (!id) throw createError({ statusCode: 400, message: "id fehlt" });
const body = await readBody(event) as { cardIndex: number };
const { cardIndex } = body;
if (typeof cardIndex !== "number" || cardIndex < 0 || cardIndex > 15) {
throw createError({ statusCode: 400, message: "Ungültiger Zug" });
}
const db = usePrisma();
const challenge = await db.gameChallenge.findUnique({ where: { id } });
if (!challenge) throw createError({ statusCode: 404, message: "Challenge nicht gefunden" });
if (challenge.status !== "ACTIVE") throw createError({ statusCode: 409, message: "Spiel nicht aktiv" });
if (challenge.gameType !== "memory") throw createError({ statusCode: 400, message: "Kein Memory-Spiel" });
const isChallenger = user.id === challenge.challengerId;
const isOpponent = user.id === challenge.opponentId;
if (!isChallenger && !isOpponent) throw createError({ statusCode: 403, message: "Nicht autorisiert" });
const myMark: "X" | "O" = isChallenger ? "X" : "O";
if (challenge.currentTurn !== myMark) throw createError({ statusCode: 409, message: "Nicht dein Zug" });
const state = JSON.parse(JSON.stringify(challenge.memoryState)) as MemoryState;
const card = state.cards[cardIndex];
if (!card) throw createError({ statusCode: 400, message: "Ungültige Karte" });
if (card.matchedBy !== null) throw createError({ statusCode: 409, message: "Karte bereits gefunden" });
let newStatus = challenge.status as string;
let newWinner: string | null = challenge.winner;
let nextTurn: string = myMark;
// Mismatch pending: player must click one of their 2 revealed cards to hide them
if (state.mismatchRevealed) {
if (!state.flipped.includes(cardIndex)) {
throw createError({ statusCode: 409, message: "Decke zuerst deine aufgedeckten Karten zu" });
}
// Hide both cards and switch turn
state.flipped = [];
state.mismatchRevealed = false;
nextTurn = myMark === "X" ? "O" : "X";
} else {
if (state.flipped.includes(cardIndex)) {
throw createError({ statusCode: 409, message: "Karte bereits aufgedeckt" });
}
if (state.flipped.length === 0) {
state.flipped = [cardIndex];
} else {
// Second flip
const firstId = state.flipped[0]!;
const firstCard = state.cards[firstId]!;
state.flipped = [firstId, cardIndex];
if (firstCard.emoji === card.emoji) {
// Match mark both, clear flipped, stay on same turn
state.cards[firstId]!.matchedBy = myMark;
state.cards[cardIndex]!.matchedBy = myMark;
state.scores[myMark]++;
state.flipped = [];
const totalPairs = state.cards.length / 2;
const matchedPairs = state.cards.filter(c => c.matchedBy !== null).length / 2;
if (matchedPairs === totalPairs) {
newStatus = "FINISHED";
const { X, O } = state.scores;
newWinner = X > O ? "X" : O > X ? "O" : "draw";
}
// nextTurn stays as myMark (scorer goes again)
} else {
// Mismatch show cards, stay on same turn until player hides them
state.mismatchRevealed = true;
// nextTurn stays as myMark
}
}
}
const updated = await db.gameChallenge.update({
where: { id },
data: {
memoryState: state as any,
currentTurn: nextTurn,
...(newStatus !== challenge.status && { status: newStatus as any }),
...(newWinner !== challenge.winner && { winner: newWinner }),
},
});
if (newStatus === "FINISHED" && challenge.opponentId && challenge.opponentName) {
const challengerId = challenge.challengerId;
const opponentId = challenge.opponentId;
const challengerName = challenge.challengerName;
const opponentName = challenge.opponentName;
if (newWinner === "draw") {
await Promise.all([
db.gameScore.upsert({
where: { userId: challengerId },
update: { draws: { increment: 1 }, points: { increment: 1 }, playerName: challengerName },
create: { userId: challengerId, playerName: challengerName, draws: 1, points: 1 },
}),
db.gameScore.upsert({
where: { userId: opponentId },
update: { draws: { increment: 1 }, points: { increment: 1 }, playerName: opponentName },
create: { userId: opponentId, playerName: opponentName, draws: 1, points: 1 },
}),
]);
} else {
const winnerId = newWinner === "X" ? challengerId : opponentId;
const loserId = newWinner === "X" ? opponentId : challengerId;
const winnerName = newWinner === "X" ? challengerName : opponentName;
const loserName = newWinner === "X" ? opponentName : challengerName;
await Promise.all([
db.gameScore.upsert({
where: { userId: winnerId },
update: { wins: { increment: 1 }, points: { increment: 3 }, playerName: winnerName },
create: { userId: winnerId, playerName: winnerName, wins: 1, points: 3 },
}),
db.gameScore.upsert({
where: { userId: loserId },
update: { losses: { increment: 1 }, playerName: loserName },
create: { userId: loserId, playerName: loserName, losses: 1 },
}),
]);
}
if (challenge.postId) {
await db.communityPost.delete({ where: { id: challenge.postId } }).catch(() => {});
}
}
return updated;
});