import axios from 'axios'; // In development, the API URL is '/api', but in production, it's just '/' const API_URL = ''; // Define types based on the backend API export interface NewGameOptions { player_color: 'white' | 'black'; difficulty: 'beginner' | 'easy' | 'medium' | 'hard' | 'expert' | 'master'; time_limit: number; use_opening_book: boolean; enable_analysis: boolean; } export interface MoveRequest { move: string; } // Promotion-related types export type PromotionPiece = 'queen' | 'rook' | 'bishop' | 'knight'; export interface PromotionMove { from: string; // e.g., "e7" to: string; // e.g., "e8" piece?: PromotionPiece; // promotion piece selection required: boolean; // true if promotion piece selection is required } export interface PromotionRequirement { from: string; to: string; available_pieces: PromotionPiece[]; } export interface PromotionRequest { move: string; promotion_piece: PromotionPiece; } export interface GameState { fen: string; turn: 'white' | 'black'; game_state: string; legal_moves: string[]; in_check: boolean; move_count: number; } export interface GameResponse { status: string; message?: string; board_state: GameState; player_color?: 'white' | 'black'; player_move?: string; ai_move?: string; analysis?: any; result?: string; winner?: string; reason?: string; // Promotion support promotion_required?: PromotionRequirement; } export interface HintResponse { status: string; hint: string; explanation: string; board_state: GameState; } // API functions export const startNewGame = async (options: NewGameOptions): Promise => { const response = await axios.post(`${API_URL}/game/new`, options); return response.data; }; export const makeMove = async (move: string, promotionPiece?: PromotionPiece): Promise => { try { // If promotion piece is specified, append it to the move in UCI notation const moveWithPromotion = promotionPiece ? createPromotionMove(move.substring(0, 2), move.substring(2, 4), promotionPiece) : move; const response = await axios.post(`${API_URL}/game/move`, { move: moveWithPromotion }); return response.data; } catch (error) { // Handle promotion-required errors specifically if (axios.isAxiosError(error) && error.response?.status === 400) { const errorData = error.response.data; // Check if this is a promotion-required error if (errorData.status === 'promotion_required' || errorData.promotion_details) { return { status: 'promotion_required', message: errorData.message || 'Pawn promotion requires piece selection', board_state: errorData.board_state || {} as GameState, promotion_required: { from: errorData.promotion_details?.from || move.substring(0, 2), to: errorData.promotion_details?.to || move.substring(2, 4), available_pieces: errorData.promotion_details?.available_pieces || ['queen', 'rook', 'bishop', 'knight'] } }; } } // Re-throw other errors throw error; } }; export const getGameState = async (): Promise => { const response = await axios.get(`${API_URL}/game/state`); return response.data; }; export const getHint = async (): Promise => { const response = await axios.post(`${API_URL}/game/hint`); return response.data; }; export const undoMove = async (count: number = 2): Promise => { const response = await axios.post(`${API_URL}/game/undo`, { count }); return response.data; }; export const resignGame = async (): Promise => { const response = await axios.post(`${API_URL}/game/resign`); return response.data; }; // Utility functions for promotion handling export const isPromotionRequired = (response: GameResponse): boolean => { return response.status === 'promotion_required' && !!response.promotion_required; }; export const getPromotionRequirement = (response: GameResponse): PromotionRequirement | null => { return response.promotion_required || null; }; export const createPromotionMove = (from: string, to: string, piece: PromotionPiece): string => { // Map promotion pieces to UCI notation const pieceMap: Record = { 'queen': 'q', 'rook': 'r', 'bishop': 'b', 'knight': 'n' // Knight uses 'n' in UCI notation, not 'k' }; return `${from}${to}${pieceMap[piece]}`; };