diff --git a/app/routes/api.agari.ts b/app/routes/api.agari.ts index cd8c3b2..ab843a8 100644 --- a/app/routes/api.agari.ts +++ b/app/routes/api.agari.ts @@ -1,14 +1,11 @@ import { redirect } from "react-router"; -import { z } from "zod"; import { getAuth } from "~/lib/auth"; import { getDB } from "~/lib/db"; import { getGameState, recordKyoku, restartGame } from "~/lib/game-service"; +import judgeAgari from "~/lib/hai/agari"; +import { sortTehai } from "~/lib/hai/types"; import type { Route } from "./+types/api.agari"; -const agariSchema = z.object({ - junme: z.coerce.number().optional(), -}); - export async function action({ context, request }: Route.ActionArgs) { const env = context.cloudflare.env; const auth = getAuth(env); @@ -17,15 +14,6 @@ export async function action({ context, request }: Route.ActionArgs) { return new Response("Unauthorized", { status: 401 }); } - const formData = await request.formData(); - const junme = formData.get("junme"); - const parsedData = agariSchema.safeParse({ - junme: junme === null || junme === "" ? undefined : junme, - }); - if (!parsedData.success) { - return new Response("Invalid form data", { status: 400 }); - } - const db = getDB(env); const userId = session.user.id; const gameStateRecord = await getGameState(db, userId); @@ -33,12 +21,21 @@ export async function action({ context, request }: Route.ActionArgs) { return new Response("Game state not found", { status: 404 }); } - const agariJunme = parsedData.data.junme ?? gameStateRecord.junme; + const tsumohai = gameStateRecord.tsumohai[0]; + if (!tsumohai) { + return new Response("No tile drawn - cannot declare win", { status: 400 }); + } + const canAgari = judgeAgari(sortTehai([...gameStateRecord.tehai, tsumohai])); + if (!canAgari) { + return new Response("Hand does not form a valid winning combination", { + status: 400, + }); + } // Record win with +8000 points await recordKyoku(db, userId, { didAgari: true, - agariJunme, + agariJunme: gameStateRecord.junme, shanten: 0, scoreDelta: 8000, }); diff --git a/app/routes/api.ryukyoku.ts b/app/routes/api.ryukyoku.ts index 9cc67da..9e20cef 100644 --- a/app/routes/api.ryukyoku.ts +++ b/app/routes/api.ryukyoku.ts @@ -19,6 +19,9 @@ export async function action({ context, request }: Route.ActionArgs) { if (!gameStateRecord) { return new Response("Game state not found", { status: 404 }); } + if (gameStateRecord.remainTsumo > 0) { + return new Response("Ryukyoku is not allowed yet", { status: 400 }); + } // Calculate shanten for current hand const shantenResult = calculateShanten(gameStateRecord.tehai); diff --git a/app/routes/api.seed.ts b/app/routes/api.seed.ts index a4b600e..78fbb7d 100644 --- a/app/routes/api.seed.ts +++ b/app/routes/api.seed.ts @@ -39,15 +39,11 @@ async function seedAndRespond(env: Env, countInput: unknown) { }); } -export async function loader({ context, request }: Route.LoaderArgs) { - const env = context.cloudflare.env; - const session = await ensureSession(request, env); - if (!session) { - return new Response("Unauthorized", { status: 401 }); - } - - const count = new URL(request.url).searchParams.get("count") ?? undefined; - return seedAndRespond(env, count); +export async function loader(_: Route.LoaderArgs) { + return new Response("Method Not Allowed", { + status: 405, + headers: { Allow: "POST" }, + }); } export async function action({ context, request }: Route.ActionArgs) { diff --git a/app/routes/api.tedashi.ts b/app/routes/api.tedashi.ts index 521ab00..443d8cf 100644 --- a/app/routes/api.tedashi.ts +++ b/app/routes/api.tedashi.ts @@ -6,7 +6,7 @@ import { getGameState, tedashi } from "~/lib/game-service"; import type { Route } from "./+types/api.tedashi"; const tedashiSchema = z.object({ - index: z.coerce.number(), + index: z.coerce.number().int().min(0), }); export async function action({ context, request }: Route.ActionArgs) {