Spaces:
Sleeping
Sleeping
| 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<GameResponse> => { | |
| const response = await axios.post(`${API_URL}/game/new`, options); | |
| return response.data; | |
| }; | |
| export const makeMove = async (move: string, promotionPiece?: PromotionPiece): Promise<GameResponse> => { | |
| 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<GameResponse> => { | |
| const response = await axios.get(`${API_URL}/game/state`); | |
| return response.data; | |
| }; | |
| export const getHint = async (): Promise<HintResponse> => { | |
| const response = await axios.post(`${API_URL}/game/hint`); | |
| return response.data; | |
| }; | |
| export const undoMove = async (count: number = 2): Promise<GameResponse> => { | |
| const response = await axios.post(`${API_URL}/game/undo`, { count }); | |
| return response.data; | |
| }; | |
| export const resignGame = async (): Promise<GameResponse> => { | |
| 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<PromotionPiece, string> = { | |
| 'queen': 'q', | |
| 'rook': 'r', | |
| 'bishop': 'b', | |
| 'knight': 'n' // Knight uses 'n' in UCI notation, not 'k' | |
| }; | |
| return `${from}${to}${pieceMap[piece]}`; | |
| }; |