diff --git a/apps/api/lambda.zip b/apps/api/lambda.zip index 0aac49c..6e6ae59 100644 Binary files a/apps/api/lambda.zip and b/apps/api/lambda.zip differ diff --git a/apps/api/src/lambda/index.ts b/apps/api/src/lambda/index.ts index 535fb2f..c76a2f6 100644 --- a/apps/api/src/lambda/index.ts +++ b/apps/api/src/lambda/index.ts @@ -23,14 +23,33 @@ import { import { z } from "zod"; /* ====================== - * 入力(solveActionのデモ用) + * Chess Piece Types * ====================== */ -const SolveActionInputSchema = z.object({ - from: z.string().regex(/^[a-h][1-8]$/, "from must be like a1-h8"), - to: z.string().regex(/^[a-h][1-8]$/, "to must be like a1-h8"), - order: z.string().min(1), // デモ。後で enum にしてもOK +const SixtyFourPositionSchema = z.object({ + x: z.number().optional(), + y: z.number().optional(), + z: z.number().optional(), }); -type SolveActionInput = z.infer; + +const PieceSchema = z.object({ + id: z.int().min(0).max(31), + exist: z.boolean().default(true), + type: z.enum(["pawn", "rook", "knight", "bishop", "queen", "king"]), + color: z.enum(["white", "black"]), + position: SixtyFourPositionSchema, +}); + +/* ====================== + * 入力(ResolveAction) + * ====================== */ +const ResolveActionInputSchema = z.object({ + piece_id: z.int().min(0).max(31), + pieces: z.array(PieceSchema), + from: z.string().regex(/^[a-h][1-8]$/, "Must be a valid chess position (e.g., 'g4')"), + to: z.string().regex(/^[a-h][1-8]$/, "Must be a valid chess position (e.g., 'f6')"), + order: z.string(), +}); +type ResolveActionInput = z.infer; /* ====================== * 出力(あなたの契約) @@ -86,20 +105,22 @@ export const handler = async (event: { typeof event.body === "string" ? JSON.parse(event.body) : (event.body ?? {}); // 2) 入力検証(ここが壊れてたら 400) - const input: SolveActionInput = SolveActionInputSchema.parse(rawBody); + const input: ResolveActionInput = ResolveActionInputSchema.parse(rawBody); // 3) デモ用指示文:構造化を最優先で強制 - // 盤面が無いので「入力の from/to をそのまま返す」方針にして安定させる + // 盤面情報(pieces)を含めてBedrockに渡す const userText = [ `You must call the tool "solve_action".`, `Do NOT output any plain text.`, - `This is a demo. Use the provided from/to as-is.`, + `Analyze the current board state and the user's command.`, `Return {from,to,attack,reason} as tool arguments.`, + `Current pieces: ${JSON.stringify(input.pieces)}`, + `piece_id: ${input.piece_id}`, `order: ${input.order}`, `from: ${input.from}`, `to: ${input.to}`, - `attack: set true only if you think this move captures (best-effort).`, - `reason: short reason.`, + `attack: set true only if this move captures an opponent's piece.`, + `reason: short reason for this action.`, ].join("\n"); const request: ConverseCommandInput = { diff --git a/apps/api/src/lib/create-game.ts b/apps/api/src/lib/create-game.ts index d9ed4b9..236ed2d 100644 --- a/apps/api/src/lib/create-game.ts +++ b/apps/api/src/lib/create-game.ts @@ -1,14 +1,10 @@ -import { - type initGame, - type initGameResponse, - initGameResponseSchema, -} from "@repo/schema"; +import { type initGame, type initGameResponse, initGameResponseSchema } from "@repo/schema"; import { DrizzleD1Database } from "drizzle-orm/d1"; import { games, personalitys } from "../db/schema"; export async function createGame( gameData: initGame, - db: DrizzleD1Database + db: DrizzleD1Database, ): Promise { try { const result = await db @@ -32,8 +28,7 @@ export async function createGame( const randomPersonalitys = []; for (let i = 0; i < 32; i++) { - randomPersonalitys[i] = - allPersonalitys[Math.floor(Math.random() * allPersonalitys.length)]; + randomPersonalitys[i] = allPersonalitys[Math.floor(Math.random() * allPersonalitys.length)]; } const response = initGameResponseSchema.parse({ @@ -43,9 +38,7 @@ export async function createGame( return response; } catch (error) { throw new Error( - `Failed to create game: ${ - error instanceof Error ? error.message : String(error) - }` + `Failed to create game: ${error instanceof Error ? error.message : String(error)}`, ); } } diff --git a/apps/api/src/lib/resolve-action.ts b/apps/api/src/lib/resolve-action.ts index f595a1a..dd38870 100644 --- a/apps/api/src/lib/resolve-action.ts +++ b/apps/api/src/lib/resolve-action.ts @@ -1,38 +1,35 @@ import type { AIResponse, ResolveActionInput } from "@repo/schema"; export async function resolveAction(input: ResolveActionInput): Promise { - const { from, to } = input; - - const randomAttack = Math.random() < 0.5; - console.log("=== AWS API Call Start ==="); + console.log("Input:", JSON.stringify(input, null, 2)); + const res = await fetch( "https://ue2gz6ytek.execute-api.ap-northeast-1.amazonaws.com/dev/v1/ai/solve/action", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ prompt: "テスト" }), + body: JSON.stringify({ + piece_id: input.piece_id, + pieces: input.pieces, + from: input.from, + to: input.to, + order: input.order, + }), }, ); + console.log("Status:", res.status, res.statusText); - const responseData = await res.json(); + + if (!res.ok) { + const errorText = await res.text(); + console.error("API Error:", errorText); + throw new Error(`API request failed: ${res.status} ${res.statusText}`); + } + + const responseData: AIResponse = await res.json(); console.log("Response:", JSON.stringify(responseData, null, 2)); console.log("=== AWS API Call End ==="); - const reasons = [ - "王を守るための移動", - "相手の駒を攻撃", - "中央を制圧する", - "戦術的な配置", - "防御的な手", - "攻撃的な展開", - ]; - const randomReason = reasons[Math.floor(Math.random() * reasons.length)]; - - return { - from, - to, - attack: randomAttack, - reason: randomReason, - }; + return responseData; } diff --git a/apps/frontend/src/features/chess/api/chess.ts b/apps/frontend/src/features/chess/api/chess.ts index 786f03c..395f7c8 100644 --- a/apps/frontend/src/features/chess/api/chess.ts +++ b/apps/frontend/src/features/chess/api/chess.ts @@ -1,27 +1,36 @@ import { client } from "@/client"; import { handleResponse } from "@/lib/api-client"; -import type {UsersResponse, Piece, AIResponse, initGame, initGameResponse } from "@repo/schema"; +import type { UsersResponse, Piece, AIResponse, initGame, initGameResponse } from "@repo/schema"; export async function fetch(): Promise { - const response = await client.users.$get({}); - return handleResponse(response); - } + const response = await client.users.$get({}); + return handleResponse(response); +} -export async function resolveAction(pieces: Piece[], from: string, to: string, order: string): Promise { - const response = await client.v1.resolveAction.$post({json : { - pieces: pieces, - from: from, - to: to, - order: order - }}) - return handleResponse(response); +export async function resolveAction( + pieces: Piece[], + from: string, + to: string, + order: string, +): Promise { + const response = await client.v1.resolveAction.$post({ + json: { + pieces: pieces, + from: from, + to: to, + order: order, + }, + }); + return handleResponse(response); } export async function initGame(initData: initGame): Promise { - const response = await client.v1.createGame.$post({json: { - player_id: initData.player_id, - enemy_id: initData.enemy_id, - first_player: initData.first_player - }}) - return handleResponse(response); + const response = await client.v1.createGame.$post({ + json: { + player_id: initData.player_id, + enemy_id: initData.enemy_id, + first_player: initData.first_player, + }, + }); + return handleResponse(response); } diff --git a/apps/frontend/src/features/chess/compomemt/ChessPieces.tsx b/apps/frontend/src/features/chess/compomemt/ChessPieces.tsx index 9edf141..537fb65 100644 --- a/apps/frontend/src/features/chess/compomemt/ChessPieces.tsx +++ b/apps/frontend/src/features/chess/compomemt/ChessPieces.tsx @@ -137,7 +137,7 @@ const vectors = new Map([ function LinkVoiceAndId( pieces: Piece[], - command: VoiceInput | null + command: VoiceInput | null, ): [Piece["id"], Position | null] { if (!command) return [-1, null]; @@ -158,7 +158,7 @@ function LinkVoiceAndId( (p) => p.position.x === fromPosition.x && p.position.y === fromPosition.y && - p.position.z === fromPosition.z + p.position.z === fromPosition.z, ); if (!piece) { @@ -172,7 +172,7 @@ function LinkVoiceAndId( function MoveCommand( pieces: Piece[], command: VoiceInput | null, - startAnimation: (id: number, from: Position, to: Position) => void + startAnimation: (id: number, from: Position, to: Position) => void, ): Piece[] { const [pieceID, toPosition] = LinkVoiceAndId(pieces, command); diff --git a/apps/frontend/src/features/chess/compomemt/ChooseFromSixPieces.tsx b/apps/frontend/src/features/chess/compomemt/ChooseFromSixPieces.tsx index 2f4aa26..82f6cca 100644 --- a/apps/frontend/src/features/chess/compomemt/ChooseFromSixPieces.tsx +++ b/apps/frontend/src/features/chess/compomemt/ChooseFromSixPieces.tsx @@ -196,7 +196,7 @@ const ChooseFromSixPieces = ({ piece }: ChessPieceProps) => { groupRef.current.position.set( animatingPiece.from.x!, animatingPiece.from.y!, - animatingPiece.from.z! + animatingPiece.from.z!, ); } }, [isAnimating, animatingPiece]); @@ -205,11 +205,7 @@ const ChooseFromSixPieces = ({ piece }: ChessPieceProps) => { if (!groupRef.current) return; if (isAnimating && animatingPiece) { - const target = new Vector3( - animatingPiece.to.x, - animatingPiece.to.y, - animatingPiece.to.z - ); + const target = new Vector3(animatingPiece.to.x, animatingPiece.to.y, animatingPiece.to.z); // 滑らかに移動(lerpの係数を調整で速度変更可能) groupRef.current.position.lerp(target, 0.02); diff --git a/apps/frontend/src/features/chess/compomemt/store.ts b/apps/frontend/src/features/chess/compomemt/store.ts index 95d1472..4af89f6 100644 --- a/apps/frontend/src/features/chess/compomemt/store.ts +++ b/apps/frontend/src/features/chess/compomemt/store.ts @@ -20,8 +20,7 @@ interface AnimationState { export const useTurnStore = create((set) => ({ turn: "white", - change: () => - set((state) => ({ turn: state.turn === "white" ? "black" : "white" })), + change: () => set((state) => ({ turn: state.turn === "white" ? "black" : "white" })), })); export const useAnimationStore = create((set) => ({ diff --git a/apps/frontend/src/features/users/api/users.ts b/apps/frontend/src/features/users/api/users.ts index 61ebcd6..85de028 100644 --- a/apps/frontend/src/features/users/api/users.ts +++ b/apps/frontend/src/features/users/api/users.ts @@ -8,9 +8,10 @@ export async function fetchUsers(): Promise { } export async function createUsers(name: string): Promise { - const response = await client.v1.users.$post({json : { - name: name - } - }) + const response = await client.v1.users.$post({ + json: { + name: name, + }, + }); return handleResponse(response); -} \ No newline at end of file +} diff --git a/packages/schema/src/resolve-action.ts b/packages/schema/src/resolve-action.ts index fa3cca2..6085315 100644 --- a/packages/schema/src/resolve-action.ts +++ b/packages/schema/src/resolve-action.ts @@ -6,6 +6,7 @@ import { PieceSchema } from "./chess"; * Input: { from: "g4", to: "f6", order: "命令" } */ export const ResolveActionInputSchema = z.object({ + piece_id: z.int().min(0).max(31), pieces: z.array(PieceSchema), from: z.string().regex(/^[a-h][1-8]$/, "Must be a valid chess position (e.g., 'g4')"), to: z.string().regex(/^[a-h][1-8]$/, "Must be a valid chess position (e.g., 'f6')"),