Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions app/routes/api.agari.ts
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -17,28 +14,28 @@ 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);
if (!gameStateRecord) {
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,
});
Expand Down
3 changes: 3 additions & 0 deletions app/routes/api.ryukyoku.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 5 additions & 9 deletions app/routes/api.seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion app/routes/api.tedashi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down