import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { environment } from '../../environments/environment'; import { GameFull, GameState, GameStreamEvent, LegalMovesResponse, PlayerInfo, } from '../models/game.models'; import { AnalysisRequest, AnalysisResponse, RawAnalysisResponse } from '../models/analysis.models'; import { StreamHandlerService } from './stream-handler.service'; @Injectable({ providedIn: 'root' }) export class GameApiService { private readonly apiBase = environment.apiBaseUrl; private readonly wsBase = environment.wsBaseUrl; private readonly apiPath = environment.apiPath; private readonly streamHandler = inject(StreamHandlerService); constructor(private readonly http: HttpClient) {} createGame(): Observable { return this.http.post(`${this.apiBase}${this.apiPath}`, {}); } createGameVsBot(difficulty: 'easy' | 'medium' | 'hard' = 'medium'): Observable { const playerColor = Math.random() > 0.5 ? 'white' : 'black'; const playerInfo: PlayerInfo = { id: `player-${Date.now()}`, displayName: 'You', }; const botInfo: PlayerInfo = { id: `bot-${difficulty}`, displayName: `Bot (${difficulty})`, }; const payload = playerColor === 'white' ? { white: playerInfo, black: botInfo } : { white: botInfo, black: playerInfo }; return this.http.post(`${this.apiBase}${this.apiPath}/vs-bot`, payload); } getGame(gameId: string): Observable { return this.http.get(`${this.apiBase}${this.apiPath}/${gameId}`); } makeMove(gameId: string, uci: string): Observable { return this.http.post(`${this.apiBase}${this.apiPath}/${gameId}/move/${uci}`, {}); } getLegalMoves(gameId: string, square?: string): Observable { let params = new HttpParams(); if (square) { params = params.set('square', square); } return this.http.get(`${this.apiBase}${this.apiPath}/${gameId}/moves`, { params, }); } importFen(fen: string): Observable { return this.http.post(`${this.apiBase}${this.apiPath}/import/fen`, { fen }); } importPgn(pgn: string): Observable { return this.http.post(`${this.apiBase}${this.apiPath}/import/pgn`, { pgn }); } resignGame(gameId: string): Observable { return this.http.post(`${this.apiBase}${this.apiPath}/${gameId}/resign`, {}); } offerDraw(gameId: string): Observable { return this.http.post(`${this.apiBase}${this.apiPath}/${gameId}/draw/offer`, {}); } getFenHistory(gameId: string): Observable { return this.http .get<{ fens: string[] }>(`${this.apiBase}${this.apiPath}/${gameId}/fen-history`) .pipe(map((r) => r.fens)); } analyzePosition(request: AnalysisRequest): Observable { return this.http .post(`${this.apiBase}/api/analysis/position`, request) .pipe(map((raw) => this.mapAnalysisResponse(raw))); } private mapAnalysisResponse(raw: RawAnalysisResponse): AnalysisResponse { const evalPawns = raw.evaluation / 100; return { eval: evalPawns, winChance: 1 / (1 + Math.exp(-0.374 * evalPawns)), depth: raw.depth, bestMove: raw.bestMove, mate: raw.mate, continuations: raw.continuationMoves ?? [], }; } private resolveWsBase(): string { if (this.wsBase) { return this.wsBase; } const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; return `${wsProtocol}://${window.location.host}`; } streamGame(gameId: string): Observable { const wsUrl = `${this.resolveWsBase()}${this.apiPath}/${gameId}/ws`; const token = localStorage.getItem('token') ?? ''; return this.streamHandler.createGameStream(wsUrl, gameId, token); } }