From 8df52fb2f78dd62381e6a20ea92cf65f01fc99bc Mon Sep 17 00:00:00 2001 From: Antoine Greuzard Date: Thu, 31 Oct 2024 20:21:02 +0100 Subject: [PATCH 01/27] Feature/save game (#65) * feat: add saving game * refactor * fix * fix: some bug * feat: better ai * fix: tests en passant * fix: isValidMove returns true for en passant capture * fix: isValidMove returns true for en passant capture * fix: add true * Create CODE_OF_CONDUCT.md Signed-off-by: Antoine Greuzard * Update issue templates * refactor ai * refactor board * refactor piece * refactor pieces * refactor utils * refactor canvas * refactor timer * fix: drag impossible on ai pieces * fix: remove save game --------- Signed-off-by: Antoine Greuzard Co-authored-by: Antoine GREUZARD --- public/.gitkeep | 0 src/ai.ts | 107 ++++++++++++++++++++++------------- src/board.ts | 82 +++++++++++++++------------ src/canvas-renderer.ts | 9 ++- src/evaluator.ts | 64 ++++++++++----------- src/game.ts | 19 ++++++- src/index.ts | 20 ++++--- src/piece.ts | 4 +- src/pieces/bishop.ts | 4 ++ src/pieces/king.ts | 11 ++++ src/pieces/knight.ts | 5 ++ src/pieces/pawn.ts | 5 ++ src/pieces/queen.ts | 4 ++ src/pieces/rook.ts | 4 ++ src/timer.ts | 23 ++++---- src/utils/utils.ts | 8 ++- tests/board.test.ts | 8 +-- tests/canvasRenderer.test.ts | 6 +- tests/piece.test.ts | 7 ++- tests/pieces/bishop.test.ts | 9 ++- tests/pieces/king.test.ts | 9 ++- tests/pieces/knight.test.ts | 11 ++-- tests/pieces/pawn.test.ts | 9 ++- tests/pieces/queen.test.ts | 9 ++- tests/pieces/rook.test.ts | 9 ++- 25 files changed, 284 insertions(+), 162 deletions(-) create mode 100644 public/.gitkeep diff --git a/public/.gitkeep b/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/ai.ts b/src/ai.ts index de9c554..272a396 100644 --- a/src/ai.ts +++ b/src/ai.ts @@ -24,6 +24,12 @@ export class AI { score: number; }[] >; // Heuristic des coups efficaces + private moveHistory: { + fromX: number; + fromY: number; + toX: number; + toY: number; + }[] = []; constructor( private readonly color: PieceColor, @@ -39,38 +45,39 @@ export class AI { public makeMove( board: Board, ): { fromX: number; fromY: number; toX: number; toY: number } | null { + // Vérifie si un mouvement d'ouverture est disponible const openingMove = this.getOpeningMove(board); if (openingMove) { + this.moveHistory.push(openingMove); // Ajoute à l'historique des coups return openingMove; } - // Récupérer le mouvement d'ouverture en fonction des mouvements passés + // Vérifie si un mouvement d'ouverture basé sur les coups passés est disponible const pastMoves = this.getPastMoves(); const chosenMove = this.chooseMove(pastMoves); - if (chosenMove) { + this.moveHistory.push(chosenMove); // Ajoute à l'historique des coups return chosenMove; } - - if (chosenMove) { - return chosenMove; - } - - // Vérifie si on peut utiliser une table de fin de partie const endgameMove = this.useEndgameTablebase(board); if (endgameMove) { + this.moveHistory.push(endgameMove); // Ajoute à l'historique des coups return endgameMove; } - // Détermine si MCTS est pertinent pour la position actuelle + // Utilise MCTS pour les positions complexes ou de fin de partie if (this.shouldUseMCTS(board)) { - return this.mcts(board); // Utilise MCTS pour les positions complexes ou de fin de partie + const mctsMove = this.mcts(board); + if (mctsMove) { + this.moveHistory.push(mctsMove); // Ajoute à l'historique des coups + } + return mctsMove; } - // Si MCTS n'est pas utilisé, continue avec Minimax + // Utilise Minimax avec Alpha-Beta Pruning si aucun autre mouvement n'est trouvé let bestMove = null; let bestValue = -Infinity; - const maxDepth = 10; // Augmentation de la profondeur maximale de recherche + const maxDepth = 10; // Profondeur maximale de recherche pour Minimax this.startTime = Date.now(); for (let depth = 1; depth <= maxDepth; depth++) { @@ -107,23 +114,31 @@ export class AI { bestMove = move; } - // Limite le temps de réflexion + // Vérifie le temps de réflexion et arrête si le maximum est atteint if (Date.now() - this.startTime > this.maxTime) { break; } } - // Limite le temps de réflexion + // Vérifie encore une fois le temps de réflexion à la fin de chaque profondeur if (Date.now() - this.startTime > this.maxTime) { break; } } + // Ajoute le meilleur mouvement trouvé à l'historique des coups si existant + if (bestMove) { + this.moveHistory.push(bestMove); + } + return bestMove; } + // Fonction pour récupérer les coups passés en format abrégé private getPastMoves(): string[] { - return []; // Retourne les coups passés selon votre logique. + return this.moveHistory.map( + (move) => `${move.fromX}${move.fromY}${move.toX}${move.toY}`, + ); } // Fonction Minimax avec Alpha-Beta Pruning et table de transposition @@ -261,7 +276,10 @@ export class AI { depth: number, move: { fromX: number; fromY: number; toX: number; toY: number }, ) { - const killers = this.killerMoves.get(depth) || []; + // Récupère les killer moves actuels pour la profondeur spécifiée ou initialise un tableau vide + const killers = this.killerMoves.get(depth) ?? []; + + // Vérifie si le mouvement est déjà présent et augmente son score, sinon l'ajoute const existingMove = killers.find( (k) => k.move.fromX === move.fromX && k.move.fromY === move.fromY, ); @@ -272,9 +290,12 @@ export class AI { killers.push({ move, score: 1 }); } + // Trie et limite le tableau des killer moves aux deux meilleurs par profondeur this.killerMoves.set( depth, - killers.toSorted((a, b) => b.score - a.score).slice(0, 2), + killers + .sort((a, b) => b.score - a.score) // Trie les moves par score décroissant + .slice(0, 2), // Garde uniquement les deux meilleurs ); } @@ -287,34 +308,36 @@ export class AI { ): number { const maxQuiescenceDepth = 5; - if (depth >= maxQuiescenceDepth) return evaluateBoard(board, this.color); + if (depth >= maxQuiescenceDepth) { + return evaluateBoard(board, this.color); + } const standPat = evaluateBoard(board, this.color); if (standPat >= beta) return beta; if (alpha < standPat) alpha = standPat; - const moves = this.getAllValidMoves(board).filter((move) => + const captureMoves = this.getAllValidMoves(board).filter((move) => board.isCapture(move.fromX, move.fromY, move.toX, move.toY), ); - for (const move of moves) { + for (const move of captureMoves) { const fromPiece = board.getPiece(move.fromX, move.fromY); const toPiece = board.getPiece(move.toX, move.toY); board.movePiece(move.fromX, move.fromY, move.toX, move.toY); const kingSafe = !board.isKingInCheck(this.color); + let score = standPat; if (kingSafe) { - const score = -this.quiescenceSearch(board, -beta, -alpha, depth + 1); - board.setPiece(move.fromX, move.fromY, fromPiece); - board.setPiece(move.toX, move.toY, toPiece); - - if (score >= beta) return beta; - if (score > alpha) alpha = score; - } else { - board.setPiece(move.fromX, move.fromY, fromPiece); - board.setPiece(move.toX, move.toY, toPiece); + score = -this.quiescenceSearch(board, -beta, -alpha, depth + 1); } + + board.setPiece(move.fromX, move.fromY, fromPiece); + board.setPiece(move.toX, move.toY, toPiece); + + // Met à jour alpha si un meilleur score est trouvé + if (score >= beta) return beta; + if (score > alpha) alpha = score; } return alpha; @@ -404,7 +427,17 @@ export class AI { ): { fromX: number; fromY: number; toX: number; toY: number } | null { const iterations = 1000; // Nombre de simulations const moveScores: Map = new Map(); - const validMoves = this.getAllValidMoves(board); + + // Filtre les mouvements valides pour la simulation + const validMoves = this.getAllValidMoves(board).filter( + (move) => + move.fromX !== undefined && + move.fromY !== undefined && + move.toX !== undefined && + move.toY !== undefined, + ); + + if (validMoves.length === 0) return null; for (let i = 0; i < iterations; i++) { const move = validMoves[Math.floor(Math.random() * validMoves.length)]; @@ -427,18 +460,16 @@ export class AI { moveKey, (moveScores.get(moveKey) ?? 0) + simulationResult, ); - } - // Vérifie si moveScores est vide avant d'utiliser reduce - if (moveScores.size === 0) { - return null; // Aucun mouvement valide trouvé + // Vérifie le temps de réflexion pour arrêter les itérations si nécessaire + if (Date.now() - this.startTime > this.maxTime) { + break; + } } - // Sélectionne le mouvement avec la meilleure note moyenne + // Trouve le mouvement avec la meilleure note moyenne const bestMoveKey = Array.from(moveScores.entries()).reduce( - (best, current) => { - return current[1] > best[1] ? current : best; - }, + (best, current) => (current[1] > best[1] ? current : best), )[0]; const [fromX, fromY, toX, toY] = bestMoveKey.split(',').map(Number); diff --git a/src/board.ts b/src/board.ts index b026e65..fec45f7 100644 --- a/src/board.ts +++ b/src/board.ts @@ -13,7 +13,9 @@ export class Board implements BoardInterface { private currentPlayer: PieceColor = PieceColor.WHITE; constructor() { - this.grid = []; + this.grid = Array(8) + .fill(null) + .map(() => Array(8).fill(null)); } public async init(): Promise { @@ -30,8 +32,8 @@ export class Board implements BoardInterface { await createPiece(PieceType.ROOK, PieceColor.WHITE), await createPiece(PieceType.KNIGHT, PieceColor.WHITE), await createPiece(PieceType.BISHOP, PieceColor.WHITE), - await createPiece(PieceType.KING, PieceColor.WHITE), await createPiece(PieceType.QUEEN, PieceColor.WHITE), + await createPiece(PieceType.KING, PieceColor.WHITE), await createPiece(PieceType.BISHOP, PieceColor.WHITE), await createPiece(PieceType.KNIGHT, PieceColor.WHITE), await createPiece(PieceType.ROOK, PieceColor.WHITE), @@ -47,8 +49,8 @@ export class Board implements BoardInterface { await createPiece(PieceType.ROOK, PieceColor.BLACK), await createPiece(PieceType.KNIGHT, PieceColor.BLACK), await createPiece(PieceType.BISHOP, PieceColor.BLACK), - await createPiece(PieceType.KING, PieceColor.BLACK), await createPiece(PieceType.QUEEN, PieceColor.BLACK), + await createPiece(PieceType.KING, PieceColor.BLACK), await createPiece(PieceType.BISHOP, PieceColor.BLACK), await createPiece(PieceType.KNIGHT, PieceColor.BLACK), await createPiece(PieceType.ROOK, PieceColor.BLACK), @@ -89,21 +91,6 @@ export class Board implements BoardInterface { return validMoves; } - private isCastlingMove( - fromX: number, - fromY: number, - toX: number, - toY: number, - ): boolean { - const piece = this.getPiece(fromX, fromY); - return ( - (piece && - piece.type === PieceType.KING && - Math.abs(toX - fromX) === 2 && - fromY === toY) - ); - } - public captureEnPassantIfValid( fromX: number, fromY: number, @@ -114,6 +101,7 @@ export class Board implements BoardInterface { const movingPawn = this.getPiece(fromX, fromY); if (!movingPawn) return; + // Détermine la position du pion capturé en fonction de la couleur const capturedPawnY = toY + (movingPawn.color === PieceColor.WHITE ? -1 : 1); @@ -156,7 +144,8 @@ export class Board implements BoardInterface { return false; } - if (piece?.type === PieceType.KING && Math.abs(toX - fromX) === 2) { + // Gestion du roque pour le roi + if (piece.type === PieceType.KING && Math.abs(toX - fromX) === 2) { if (this.isCastlingValid(piece, fromX, fromY, toX)) { this.handleCastling(toX, fromY); piece.hasMoved = true; // Met à jour le statut de mouvement du roi @@ -177,7 +166,6 @@ export class Board implements BoardInterface { // Sauvegarde l'état avant de simuler le mouvement this.grid[toY][toX] = piece; this.grid[fromY][fromX] = null; - piece.hasMoved = true; // Vérifie si le mouvement met le roi du joueur en échec if (this.isKingInCheck(piece.color)) { @@ -187,10 +175,12 @@ export class Board implements BoardInterface { return false; } - // Mise à jour de l'état après un mouvement valide - if ('hasMoved' in piece) { - (piece as any).hasMoved = true; + // Mise à jour de `hasMoved` pour les rois et tours + if (piece.type === PieceType.KING || piece.type === PieceType.ROOK) { + piece.hasMoved = true; } + + // Mise à jour de l'état après un mouvement valide this.updateEnPassantTarget(fromX, fromY, toX, toY, piece); // Réinitialise le compteur pour la règle des 50 coups si un pion bouge ou une capture a lieu @@ -247,12 +237,18 @@ export class Board implements BoardInterface { if ( rook?.type === PieceType.ROOK && !rook.hasMoved && - king?.type === PieceType.KING + king?.type === PieceType.KING && + !king.hasMoved ) { + // Déplace la tour et le roi pour le petit roque this.setPiece(5, kingY, rook); // Déplace la tour this.setPiece(7, kingY, null); // Enlève la tour de sa position initiale this.setPiece(6, kingY, king); // Déplace le roi vers sa nouvelle position this.setPiece(4, kingY, null); // Enlève le roi de sa position initiale + + // Marque le roi et la tour comme ayant bougé + king.hasMoved = true; + rook.hasMoved = true; } } // Grand roque (roi vers la gauche) @@ -262,12 +258,18 @@ export class Board implements BoardInterface { if ( rook?.type === PieceType.ROOK && !rook.hasMoved && - king?.type === PieceType.KING + king?.type === PieceType.KING && + !king.hasMoved ) { + // Déplace la tour et le roi pour le grand roque this.setPiece(3, kingY, rook); // Déplace la tour this.setPiece(0, kingY, null); // Enlève la tour de sa position initiale this.setPiece(2, kingY, king); // Déplace le roi vers sa nouvelle position this.setPiece(4, kingY, null); // Enlève le roi de sa position initiale + + // Marque le roi et la tour comme ayant bougé + king.hasMoved = true; + rook.hasMoved = true; } } } @@ -279,14 +281,17 @@ export class Board implements BoardInterface { toY: number, piece: Piece, ): void { + // Vérifie que la pièce est un pion et qu'il avance de deux cases if ( piece?.type === PieceType.PAWN && Math.abs(toY - fromY) === 2 && fromX === toX ) { - // Si le pion avance de deux cases, configure la cible pour la prise en passant + // Si le pion avance de deux cases, configure `enPassantTarget` + // pour permettre une prise en passant lors du tour suivant this.enPassantTarget = { x: toX, y: (fromY + toY) / 2 }; } else { + // Réinitialise `enPassantTarget` si aucune condition de prise en passant n'est remplie this.enPassantTarget = null; } } @@ -308,9 +313,10 @@ export class Board implements BoardInterface { const capturedPawn = this.getPiece(toX, capturedPawnY); if (capturedPawn && capturedPawn.type === PieceType.PAWN) { + // Supprime le pion capturé de la grille this.grid[capturedPawnY][toX] = null; - // Déclare explicitement le type de captureData pour éviter l'erreur + // Initialise les données de capture const captureData: { capturedWhite: PieceType[]; capturedBlack: PieceType[]; @@ -319,13 +325,14 @@ export class Board implements BoardInterface { capturedBlack: [], }; + // Met à jour les données de capture selon la couleur du pion capturé if (capturedPawn.color === PieceColor.WHITE) { captureData.capturedWhite.push(capturedPawn.type); } else { captureData.capturedBlack.push(capturedPawn.type); } - // Appelle updateCapturedPieces pour mettre à jour le DOM + // Appelle updateCapturedPieces pour mettre à jour l'interface updateCapturedPieces(capturedPawn.type, capturedPawn.color); return captureData; @@ -398,8 +405,12 @@ export class Board implements BoardInterface { } public isCheckmate(color: PieceColor): boolean { - if (!this.isKingInCheck(color)) { - return false; // Pas de mat si le roi n'est pas en échec + // Vérifie si le roi de la couleur donnée est en échec + const kingInCheck = this.isKingInCheck(color); + + // Si le roi n'est pas en échec, ce n'est pas un échec et mat + if (!kingInCheck) { + return false; } // Parcourt chaque pièce de la couleur donnée pour trouver un mouvement légal @@ -670,27 +681,28 @@ export class Board implements BoardInterface { } public getWinner(): PieceColor | null { - // Si c'est un échec et mat pour les Noirs, Blancs gagnent + // Vérifie si les Noirs sont en échec et mat, dans ce cas les Blancs gagnent if (this.isCheckmate(PieceColor.BLACK)) { return PieceColor.WHITE; } - // Si c'est un échec et mat pour les Blancs, Noirs gagnent + // Vérifie si les Blancs sont en échec et mat, dans ce cas les Noirs gagnent if (this.isCheckmate(PieceColor.WHITE)) { return PieceColor.BLACK; } - // Si c'est un pat, une égalité par matériel insuffisant, ou la règle des 50 coups, la partie est nulle + // Vérifie les conditions de partie nulle : pat, matériel insuffisant ou règle des 50 coups if ( this.isStalemate(PieceColor.WHITE) || this.isStalemate(PieceColor.BLACK) || this.isInsufficientMaterial() || this.isFiftyMoveRule() ) { - return null; + return null; // Partie nulle, aucun gagnant } - return null; // Retourne null si le jeu n'est pas encore terminé + // Le jeu est en cours, retourne `null` pour indiquer qu'aucun gagnant n'est déterminé + return null; } public getPieces(): Piece[] { diff --git a/src/canvas-renderer.ts b/src/canvas-renderer.ts index e5f4def..1fdf547 100644 --- a/src/canvas-renderer.ts +++ b/src/canvas-renderer.ts @@ -24,7 +24,10 @@ export class CanvasRenderer { flipBoard: boolean = false, // Argument pour inverser le plateau si le joueur est Noir ) { this.canvas = document.getElementById(canvasId) as HTMLCanvasElement; - this.context = this.canvas.getContext('2d')!; + if (!this.canvas) throw new Error(`Canvas with id ${canvasId} not found`); + this.context = this.canvas.getContext('2d') as CanvasRenderingContext2D; + if (!this.context) + throw new Error(`Canvas context could not be initialized`); this.tileSize = this.canvas.width / 8; this.flipBoard = flipBoard; @@ -192,7 +195,7 @@ export class CanvasRenderer { const { x: adjustedX, y: adjustedY } = this.getInverseCoordinates(x, y); const piece = this.board.getPiece(adjustedX, adjustedY); - if (piece) { + if (piece && piece.color === this.board.getPlayerColor()) { this.draggingPiece = piece; this.startX = adjustedX; this.startY = adjustedY; @@ -201,6 +204,8 @@ export class CanvasRenderer { this.highlightedMoves = this.board.getValidMoves(adjustedX, adjustedY); this.drawBoard(); this.highlightValidMoves(this.highlightedMoves); + } else { + this.draggingPiece = null; } } diff --git a/src/evaluator.ts b/src/evaluator.ts index 476eb4a..6cff893 100644 --- a/src/evaluator.ts +++ b/src/evaluator.ts @@ -124,43 +124,32 @@ export function evaluateBoard( for (let y = 0; y < 8; y++) { for (let x = 0; x < 8; x++) { const piece = board.getPiece(x, y); - if (piece) { - let pieceScore = pieceValues[piece.type]; + if (!piece) continue; - // Applique les tables de position adaptées à flipBoard - pieceScore += getPieceSquareValue(piece.type, x, y, flipBoard); + // Applique la valeur de base et la table de position + let pieceScore = pieceValues[piece.type]; + pieceScore += getPieceSquareValue(piece.type, x, y, flipBoard); - // Contrôle du centre du plateau - const positionKey = `${x},${y}`; - if (centerControlBonus[positionKey]) { - pieceScore += centerControlBonus[positionKey]; - } - - // Bonus pour les pions passés et autres évaluations de structure - if (piece.type === PieceType.PAWN) { - pieceScore += evaluatePawnStructure(board, x, y, piece.color); - if (isPassedPawn(board, x, y, piece.color)) { - pieceScore += 1.0; - } - } - // Structure des pions pour vérifier les pions passés - if (piece.type === PieceType.PAWN) { - pieceScore += evaluatePawnStructure(board, x, y, piece.color); - if (isPassedPawn(board, x, y, piece.color)) { - pieceScore += 1.0; // Bonus pour les pions passés - } - } + // Ajoute le bonus pour le contrôle du centre + const positionKey = `${x},${y}`; + if (centerControlBonus[positionKey]) { + pieceScore += centerControlBonus[positionKey]; + } - // Vérifier si le roi est exposé - if ( - piece.type === PieceType.KING && - isKingExposed(board, x, y, piece.color) - ) { - pieceScore -= 0.5; // Réduction pour les rois exposés - } + // Évalue les pions pour structure et bonus de pion passé + if (piece.type === PieceType.PAWN) { + pieceScore += evaluatePawnStructure(board, x, y, piece.color); + } - score += piece.color === color ? pieceScore : -pieceScore; + // Pénalise les rois exposés + if ( + piece.type === PieceType.KING && + isKingExposed(board, x, y, piece.color) + ) { + pieceScore -= 0.5; } + + score += piece.color === color ? pieceScore : -pieceScore; } } @@ -175,9 +164,18 @@ function evaluatePawnStructure( color: PieceColor, ): number { let score = 0; + + // Pénalise les pions doublés score -= checkDoubledPawns(board, x, y, color) * 1.5; + + // Pénalise les pions isolés score -= checkIsolatedPawns(board, x, y, color) * 1.5; - score += isPassedPawn(board, x, y, color) ? 1.0 : 0; + + // Bonus pour les pions passés + if (isPassedPawn(board, x, y, color)) { + score += 1.0; + } + return score; } diff --git a/src/game.ts b/src/game.ts index 6b581c6..3aa5157 100644 --- a/src/game.ts +++ b/src/game.ts @@ -13,14 +13,31 @@ export class Game { toX: number; toY: number; } | null = null; + private readonly moveHistory: { + fromX: number; + fromY: number; + toX: number; + toY: number; + pieceType: PieceType; + }[][]; - constructor(playerColor: PieceColor) { + constructor( + playerColor: PieceColor, + moveHistory: { + fromX: number; + fromY: number; + toX: number; + toY: number; + pieceType: PieceType; + }[][], + ) { this.board = new Board(); this.aiWorker = new Worker(new URL('./ai.worker.ts', import.meta.url), { type: 'module', }); this.aiColor = playerColor === PieceColor.WHITE ? PieceColor.BLACK : PieceColor.WHITE; + this.moveHistory = moveHistory; } public async getBoard(): Promise { diff --git a/src/index.ts b/src/index.ts index a5ff50e..c43f702 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +// src/index.ts import { Game } from './game'; import { CanvasRenderer } from './canvas-renderer'; import { Timer } from './timer'; @@ -10,7 +11,14 @@ import { // Fonction asynchrone pour initialiser le jeu export async function initializeGame(playerColor: PieceColor) { - const game = new Game(playerColor); + let moveHistory: { + fromX: number; + fromY: number; + toX: number; + toY: number; + pieceType: PieceType; + }[][] = [[]]; + const game = new Game(playerColor, moveHistory); const board = await game.getBoard(); board.setPlayerColor(playerColor); @@ -37,13 +45,6 @@ export async function initializeGame(playerColor: PieceColor) { let currentPlayer: PieceColor = PieceColor.WHITE; let gameState: 'playing' | 'waiting' = 'playing'; let hasMoved = false; - let moveHistory: { - fromX: number; - fromY: number; - toX: number; - toY: number; - pieceType: PieceType; - }[][] = [[]]; let isGameEnded = false; let isAITurn = false; @@ -95,6 +96,9 @@ export async function initializeGame(playerColor: PieceColor) { replayButton.style.display = 'block'; if (whiteTimer.isRunning) whiteTimer.stop(); if (blackTimer.isRunning) blackTimer.stop(); + + // Appel de la fonction de sauvegarde + game.saveGameToFile(); } function clearMessage() { diff --git a/src/piece.ts b/src/piece.ts index 946b4be..c72a9ed 100644 --- a/src/piece.ts +++ b/src/piece.ts @@ -34,9 +34,9 @@ export interface BoardInterface { toY: number, ): boolean; - promotePawn(x: number, y: number, pieceType: string): void; + promotePawn(x: number, y: number, pieceType: PieceType): Promise; - isSquareUnderAttack(x: number, y: number, color: string): boolean; + isSquareUnderAttack(x: number, y: number, color: PieceColor): boolean; isKing(x: number, y: number): boolean; diff --git a/src/pieces/bishop.ts b/src/pieces/bishop.ts index 7454a23..6e2514c 100644 --- a/src/pieces/bishop.ts +++ b/src/pieces/bishop.ts @@ -13,6 +13,10 @@ export class Bishop extends Piece { toY: number, board: BoardInterface, ): boolean { + if (toX < 0 || toX >= 8 || toY < 0 || toY >= 8) { + return false; + } + // Le fou se déplace en diagonale if (Math.abs(toX - fromX) === Math.abs(toY - fromY)) { // Vérifie que la trajectoire est dégagée diff --git a/src/pieces/king.ts b/src/pieces/king.ts index 8121ac5..4de86fe 100644 --- a/src/pieces/king.ts +++ b/src/pieces/king.ts @@ -15,11 +15,17 @@ export class King extends Piece { toY: number, board: BoardInterface, ): boolean { + // Vérifie que la destination est dans les limites du plateau + if (toX < 0 || toX >= 8 || toY < 0 || toY >= 8) { + return false; + } + const dx = Math.abs(toX - fromX); const dy = Math.abs(toY - fromY); // Mouvement classique du roi if (dx <= 1 && dy <= 1) { + // Vérifie que la case cible contient une pièce ennemie ou est vide return ( this.canCapture(toX, toY, board) && !board.isAdjacentToAnotherKing(toX, toY, this.color) @@ -28,6 +34,11 @@ export class King extends Piece { // Vérifications pour le roque if (!this.hasMoved && dy === 0 && dx === 2) { + // Vérifie que la position de départ du roi n'est pas sous attaque + if (board.isSquareUnderAttack(fromX, fromY, this.color)) { + return false; + } + const direction = toX > fromX ? 1 : -1; // Vers la droite ou la gauche const rookX = toX > fromX ? 7 : 0; // Position initiale de la tour const rook = board.getPiece(rookX, fromY); diff --git a/src/pieces/knight.ts b/src/pieces/knight.ts index 82003f6..4b44921 100644 --- a/src/pieces/knight.ts +++ b/src/pieces/knight.ts @@ -13,6 +13,11 @@ export class Knight extends Piece { toY: number, board: BoardInterface, ): boolean { + // Vérifie que la destination est dans les limites du plateau + if (toX < 0 || toX >= 8 || toY < 0 || toY >= 8) { + return false; + } + // Le Cavalier se déplace en L : 2 cases dans une direction puis 1 case perpendiculairement const dx = Math.abs(toX - fromX); const dy = Math.abs(toY - fromY); diff --git a/src/pieces/pawn.ts b/src/pieces/pawn.ts index c13a0cb..0a6160b 100644 --- a/src/pieces/pawn.ts +++ b/src/pieces/pawn.ts @@ -18,6 +18,10 @@ export class Pawn extends Piece { toY: number, board: BoardInterface, ): boolean { + if (toX < 0 || toX >= 8 || toY < 0 || toY >= 8) { + return false; + } + const playerColor = board.getPlayerColor(); const direction = this.color === PieceColor.WHITE ? 1 : -1; const startRow = this.color === PieceColor.WHITE ? 1 : 6; @@ -57,6 +61,7 @@ export class Pawn extends Piece { !board.getPiece(fromX, fromY + direction) ) { board.updateEnPassantTarget(fromX, fromY, toX, toY, this); + this.hasMoved = true; // Marque que le pion a bougé return true; } diff --git a/src/pieces/queen.ts b/src/pieces/queen.ts index c8294a2..a6238d4 100644 --- a/src/pieces/queen.ts +++ b/src/pieces/queen.ts @@ -13,6 +13,10 @@ export class Queen extends Piece { toY: number, board: BoardInterface, ): boolean { + if (toX < 0 || toX >= 8 || toY < 0 || toY >= 8) { + return false; + } + // La reine peut se déplacer en ligne droite ou en diagonale if ( fromX === toX || // Déplacement en colonne diff --git a/src/pieces/rook.ts b/src/pieces/rook.ts index ea4fdab..fb8fba1 100644 --- a/src/pieces/rook.ts +++ b/src/pieces/rook.ts @@ -18,6 +18,10 @@ export class Rook extends Piece { toY: number, board: BoardInterface, ): boolean { + if (toX < 0 || toX >= 8 || toY < 0 || toY >= 8) { + return false; + } + const isStraightMove = fromX === toX || fromY === toY; if (!isStraightMove) { return false; diff --git a/src/timer.ts b/src/timer.ts index 3199931..d4631cc 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -1,15 +1,12 @@ // src/timer.ts export class Timer { - private intervalId: number | null = null; + private intervalId: NodeJS.Timeout | null = null; private currentTime: number; private readonly onTimeUpdate: (timeLeft: number) => void; public isRunning: boolean = false; - constructor( - private initialTime: number, - onTimeUpdate: (timeLeft: number) => void, - ) { - this.currentTime = initialTime; + constructor(initialTime: number, onTimeUpdate: (timeLeft: number) => void) { + this.currentTime = Math.max(0, initialTime); this.onTimeUpdate = onTimeUpdate; } @@ -18,8 +15,8 @@ export class Timer { if (this.isRunning) return; this.isRunning = true; - this.intervalId = window.setInterval(() => { - this.currentTime--; + this.intervalId = setInterval(() => { + this.currentTime = Math.max(this.currentTime - 1, 0); this.onTimeUpdate(this.currentTime); if (this.currentTime <= 0) { @@ -45,10 +42,12 @@ export class Timer { } // Réinitialiser le temps - public reset(time: number): void { + public reset(time: number, startImmediately: boolean = true): void { this.stop(); - this.currentTime = time; - this.onTimeUpdate(this.currentTime); // Appel immédiat pour la mise à jour du temps - this.start(); + this.currentTime = Math.max(0, time); + this.onTimeUpdate(this.currentTime); + if (startImmediately) { + this.start(); + } } } diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 9097409..f7dfc1c 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -9,8 +9,10 @@ export function showMessage(message: string) { const gameMessageElement = document.getElementById( 'gameMessage', ) as HTMLDivElement; - gameMessageElement.textContent = message; - gameMessageElement.style.display = 'block'; // Afficher le message + if (gameMessageElement) { + gameMessageElement.textContent = message; + gameMessageElement.style.display = 'block'; // Afficher le message + } } export function getPieceSymbol(piece: PieceType, color: PieceColor): string { @@ -44,7 +46,7 @@ export function updateCapturedPieces(piece: PieceType, color: PieceColor) { updateCapturedPiecesDOM(); } -export function updateCapturedPiecesDOM() { +export function updateCapturedPiecesDOM(): void { const capturedWhiteElement = document.getElementById( 'capturedWhite', ) as HTMLDivElement; diff --git a/tests/board.test.ts b/tests/board.test.ts index d2c6afc..9b1ad9f 100644 --- a/tests/board.test.ts +++ b/tests/board.test.ts @@ -19,10 +19,10 @@ describe('Board', () => { board = new Board(); await board.init(); // Initialisation complète, pas de vidage de grille - const whiteQueen = board.getPiece(4, 0); - const whiteKing = board.getPiece(3, 0); - const blackQueen = board.getPiece(4, 7); - const blackKing = board.getPiece(3, 7); + const whiteQueen = board.getPiece(3, 0); + const whiteKing = board.getPiece(4, 0); + const blackQueen = board.getPiece(3, 7); + const blackKing = board.getPiece(4, 7); const whitePawn = board.getPiece(0, 1); expect(whiteQueen?.type).toBe(PieceType.QUEEN); diff --git a/tests/canvasRenderer.test.ts b/tests/canvasRenderer.test.ts index 834c0f3..1f8db64 100644 --- a/tests/canvasRenderer.test.ts +++ b/tests/canvasRenderer.test.ts @@ -68,13 +68,13 @@ describe('CanvasRenderer Tests', () => { test('Displays king in check with red highlight', async () => { const whiteKing = await createPiece(PieceType.KING, PieceColor.WHITE); const blackRook = await createPiece(PieceType.ROOK, PieceColor.BLACK); - board.setPiece(3, 0, blackRook); - board.setPiece(3, 7, whiteKing); + board.setPiece(4, 0, blackRook); + board.setPiece(4, 7, whiteKing); renderer.drawBoard(); // Vérifie que drawTiles a été appelé pour mettre en évidence l'échec du roi - const kingPosition = renderer['getCoordinates'](3, 7); + const kingPosition = renderer['getCoordinates'](4, 7); expect(renderer['kingInCheckPosition']).toEqual(kingPosition); }); }); diff --git a/tests/piece.test.ts b/tests/piece.test.ts index 5de5c84..e900bf2 100644 --- a/tests/piece.test.ts +++ b/tests/piece.test.ts @@ -22,7 +22,9 @@ class MockBoard implements BoardInterface { return false; } - promotePawn(): void {} + promotePawn(x: number, y: number, pieceType: PieceType): Promise { + throw new Error('Method not implemented.'); + } isSquareUnderAttack(): boolean { return false; @@ -46,7 +48,8 @@ class MockBoard implements BoardInterface { fromY: number, toX: number, toY: number, - ): void {} + ): void { + } } describe('Piece', () => { diff --git a/tests/pieces/bishop.test.ts b/tests/pieces/bishop.test.ts index dd2ede0..f7dec1a 100644 --- a/tests/pieces/bishop.test.ts +++ b/tests/pieces/bishop.test.ts @@ -1,6 +1,6 @@ // tests/bishop.test.ts import { Bishop } from '../../src/pieces/bishop'; -import { BoardInterface, PieceColor } from '../../src/piece'; +import { BoardInterface, PieceColor, PieceType } from '../../src/piece'; class MockBoard implements BoardInterface { private board: (Bishop | null)[][] = Array(8) @@ -21,7 +21,9 @@ class MockBoard implements BoardInterface { return false; } - promotePawn(): void {} + promotePawn(x: number, y: number, pieceType: PieceType): Promise { + throw new Error('Method not implemented.'); + } isSquareUnderAttack(): boolean { return false; @@ -44,7 +46,8 @@ class MockBoard implements BoardInterface { fromY: number, toX: number, toY: number, - ): void {} + ): void { + } } describe('Bishop', () => { diff --git a/tests/pieces/king.test.ts b/tests/pieces/king.test.ts index 2fa6091..17091df 100644 --- a/tests/pieces/king.test.ts +++ b/tests/pieces/king.test.ts @@ -3,6 +3,10 @@ import { King } from '../../src/pieces/king'; import { BoardInterface, PieceColor, PieceType } from '../../src/piece'; class MockBoard implements BoardInterface { + promotePawn(x: number, y: number, pieceType: PieceType): Promise { + throw new Error('Method not implemented.'); + } + private board: (King | null)[][] = Array(8) .fill(null) .map(() => Array(8).fill(null)); @@ -22,8 +26,6 @@ class MockBoard implements BoardInterface { return false; } - promotePawn(): void {} - isSquareUnderAttack(x: number, y: number, color: PieceColor): boolean { return this.isSquareUnderAttackCalled; } @@ -45,7 +47,8 @@ class MockBoard implements BoardInterface { fromY: number, toX: number, toY: number, - ): void {} + ): void { + } } describe('King', () => { diff --git a/tests/pieces/knight.test.ts b/tests/pieces/knight.test.ts index 928a045..21c5ded 100644 --- a/tests/pieces/knight.test.ts +++ b/tests/pieces/knight.test.ts @@ -1,6 +1,6 @@ // tests/knight.test.ts import { Knight } from '../../src/pieces/knight'; -import { BoardInterface, PieceColor } from '../../src/piece'; +import { BoardInterface, PieceColor, PieceType } from '../../src/piece'; class MockBoard implements BoardInterface { private board: (Knight | null)[][] = Array(8) @@ -21,8 +21,10 @@ class MockBoard implements BoardInterface { return false; } - promotePawn(): void {} - + promotePawn(x: number, y: number, pieceType: PieceType): Promise { + throw new Error('Method not implemented.'); + } + isSquareUnderAttack(): boolean { return false; } @@ -44,7 +46,8 @@ class MockBoard implements BoardInterface { fromY: number, toX: number, toY: number, - ): void {} + ): void { + } } describe('Knight', () => { diff --git a/tests/pieces/pawn.test.ts b/tests/pieces/pawn.test.ts index 6dda7bd..1a0f938 100644 --- a/tests/pieces/pawn.test.ts +++ b/tests/pieces/pawn.test.ts @@ -1,6 +1,6 @@ // tests/pawn.test.ts import { Pawn } from '../../src/pieces/pawn'; -import { BoardInterface, PieceColor } from '../../src/piece'; +import { BoardInterface, PieceColor, PieceType } from '../../src/piece'; class MockBoard implements BoardInterface { private board: (Pawn | null)[][] = Array(8) @@ -35,7 +35,9 @@ class MockBoard implements BoardInterface { return this.enPassantTarget?.x === toX && this.enPassantTarget?.y === toY; } - promotePawn(): void {} + promotePawn(x: number, y: number, pieceType: PieceType): Promise { + throw new Error('Method not implemented.'); + } isSquareUnderAttack(): boolean { return false; @@ -58,7 +60,8 @@ class MockBoard implements BoardInterface { fromY: number, toX: number, toY: number, - ): void {} + ): void { + } } describe('Pawn', () => { diff --git a/tests/pieces/queen.test.ts b/tests/pieces/queen.test.ts index f9182bd..72652fa 100644 --- a/tests/pieces/queen.test.ts +++ b/tests/pieces/queen.test.ts @@ -1,6 +1,6 @@ // tests/queen.test.ts import { Queen } from '../../src/pieces/queen'; -import { BoardInterface, PieceColor } from '../../src/piece'; +import { BoardInterface, PieceColor, PieceType } from '../../src/piece'; class MockBoard implements BoardInterface { private board: (Queen | null)[][] = Array(8) @@ -21,7 +21,9 @@ class MockBoard implements BoardInterface { return false; } - promotePawn(): void {} + promotePawn(x: number, y: number, pieceType: PieceType): Promise { + throw new Error('Method not implemented.'); + } isSquareUnderAttack(): boolean { return false; @@ -44,7 +46,8 @@ class MockBoard implements BoardInterface { fromY: number, toX: number, toY: number, - ): void {} + ): void { + } } describe('Queen', () => { diff --git a/tests/pieces/rook.test.ts b/tests/pieces/rook.test.ts index a18f194..ba6c72c 100644 --- a/tests/pieces/rook.test.ts +++ b/tests/pieces/rook.test.ts @@ -1,6 +1,6 @@ // tests/rook.test.ts import { Rook } from '../../src/pieces/rook'; -import { BoardInterface, PieceColor } from '../../src/piece'; +import { BoardInterface, PieceColor, PieceType } from '../../src/piece'; class MockBoard implements BoardInterface { private board: (Rook | null)[][] = Array(8) @@ -21,7 +21,9 @@ class MockBoard implements BoardInterface { return false; } - promotePawn(): void {} + promotePawn(x: number, y: number, pieceType: PieceType): Promise { + throw new Error('Method not implemented.'); + } isSquareUnderAttack(): boolean { return false; @@ -44,7 +46,8 @@ class MockBoard implements BoardInterface { fromY: number, toX: number, toY: number, - ): void {} + ): void { + } } describe('Rook', () => { From 9f9a1f32e19e017927fd681b99ad1dc7bac42631 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Thu, 31 Oct 2024 21:48:17 +0100 Subject: [PATCH 02/27] fix: remove saveGameToFile --- src/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index c43f702..acd0d95 100644 --- a/src/index.ts +++ b/src/index.ts @@ -96,9 +96,6 @@ export async function initializeGame(playerColor: PieceColor) { replayButton.style.display = 'block'; if (whiteTimer.isRunning) whiteTimer.stop(); if (blackTimer.isRunning) blackTimer.stop(); - - // Appel de la fonction de sauvegarde - game.saveGameToFile(); } function clearMessage() { From b5db0b91a886d031239864ad16df221c48ae1946 Mon Sep 17 00:00:00 2001 From: Antoine Greuzard Date: Thu, 31 Oct 2024 21:50:45 +0100 Subject: [PATCH 03/27] Update pull_request_template.md Signed-off-by: Antoine Greuzard --- .github/pull_request_template.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0b12690..5dddcf9 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,10 +2,6 @@ Décrivez les modifications effectuées dans cette PR (Pull Request). Quelles fonctionnalités ou corrections ont été ajoutées ? -## Résolution de Problèmes - -Clôture de : #NuméroDuTicket - ## Type de modification Cochez les cases correspondantes : From d74e2be895242bb51cfd2a943aef420392ba716e Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 08:44:52 +0100 Subject: [PATCH 04/27] fix: remove saveGameToFile --- src/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index c43f702..acd0d95 100644 --- a/src/index.ts +++ b/src/index.ts @@ -96,9 +96,6 @@ export async function initializeGame(playerColor: PieceColor) { replayButton.style.display = 'block'; if (whiteTimer.isRunning) whiteTimer.stop(); if (blackTimer.isRunning) blackTimer.stop(); - - // Appel de la fonction de sauvegarde - game.saveGameToFile(); } function clearMessage() { From acdd7798b05919de42a1cde5ef97b93997f1762b Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 08:49:04 +0100 Subject: [PATCH 05/27] refactor: add ai folder --- src/ai.ts | 6 +++--- src/{ => ai}/endgameTablebase.ts | 4 ++-- src/{ => ai}/evaluator.ts | 4 ++-- src/{ => ai}/openingBook.ts | 0 tests/endgameTablebase.test.ts | 2 +- tests/evaluateBoard.test.ts | 2 +- tests/openingBook.test.ts | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) rename src/{ => ai}/endgameTablebase.ts (98%) rename src/{ => ai}/evaluator.ts (98%) rename src/{ => ai}/openingBook.ts (100%) diff --git a/src/ai.ts b/src/ai.ts index 272a396..fe14283 100644 --- a/src/ai.ts +++ b/src/ai.ts @@ -5,9 +5,9 @@ import { centerControlBonus, evaluateBoard, evaluateKingSafety, -} from './evaluator'; -import { getEndgameMove } from './endgameTablebase'; -import { flipMove, getNextOpeningMove, openingBook } from './openingBook'; +} from './ai/evaluator'; +import { getEndgameMove } from './ai/endgameTablebase'; +import { flipMove, getNextOpeningMove, openingBook } from './ai/openingBook'; // Classe AI utilisant l'algorithme Minimax avec Alpha-Beta Pruning et Transposition Table export class AI { diff --git a/src/endgameTablebase.ts b/src/ai/endgameTablebase.ts similarity index 98% rename from src/endgameTablebase.ts rename to src/ai/endgameTablebase.ts index 0808a15..91fb5ff 100644 --- a/src/endgameTablebase.ts +++ b/src/ai/endgameTablebase.ts @@ -1,6 +1,6 @@ // src/endgameTablebase.ts -import { Board } from './board'; -import { PieceColor, PieceType } from './piece'; +import { Board } from '../board'; +import { PieceColor, PieceType } from '../piece'; type Move = { fromX: number; fromY: number; toX: number; toY: number }; diff --git a/src/evaluator.ts b/src/ai/evaluator.ts similarity index 98% rename from src/evaluator.ts rename to src/ai/evaluator.ts index 6cff893..bf6af06 100644 --- a/src/evaluator.ts +++ b/src/ai/evaluator.ts @@ -1,6 +1,6 @@ // src/evaluator.ts -import { Board } from './board'; -import { PieceColor, PieceType } from './piece'; +import { Board } from '../board'; +import { PieceColor, PieceType } from '../piece'; // Valeurs des pièces (évaluation de base) const pieceValues: { [key in PieceType]: number } = { diff --git a/src/openingBook.ts b/src/ai/openingBook.ts similarity index 100% rename from src/openingBook.ts rename to src/ai/openingBook.ts diff --git a/tests/endgameTablebase.test.ts b/tests/endgameTablebase.test.ts index db276cb..372494c 100644 --- a/tests/endgameTablebase.test.ts +++ b/tests/endgameTablebase.test.ts @@ -1,7 +1,7 @@ // tests/endgameTablebase.test.ts import { Board } from '../src/board'; import { PieceColor, PieceType } from '../src/piece'; -import { getEndgameMove } from '../src/endgameTablebase'; +import { getEndgameMove } from '../src/ai/endgameTablebase'; import { createPiece } from '../src/utils/pieceFactory'; // Fonction d'aide pour placer une pièce à une position donnée diff --git a/tests/evaluateBoard.test.ts b/tests/evaluateBoard.test.ts index deca5a3..69db3a6 100644 --- a/tests/evaluateBoard.test.ts +++ b/tests/evaluateBoard.test.ts @@ -7,7 +7,7 @@ import { Bishop } from '../src/pieces/bishop'; import { Rook } from '../src/pieces/rook'; import { Queen } from '../src/pieces/queen'; import { King } from '../src/pieces/king'; -import { evaluateBoard } from '../src/evaluator'; +import { evaluateBoard } from '../src/ai/evaluator'; describe('evaluateBoard', () => { let board: Board; diff --git a/tests/openingBook.test.ts b/tests/openingBook.test.ts index b914cbd..e65c425 100644 --- a/tests/openingBook.test.ts +++ b/tests/openingBook.test.ts @@ -1,5 +1,5 @@ // tests/openingBook.test.ts -import { openingBook } from '../src/openingBook'; +import { openingBook } from '../src/ai/openingBook'; describe('Opening Book Tests', () => { test('Ruy Lopez Opening', () => { From 29c8b6e5db2a8b1184e4828bef3b6e8ad9f828cf Mon Sep 17 00:00:00 2001 From: Antoine Greuzard Date: Fri, 1 Nov 2024 08:52:05 +0100 Subject: [PATCH 06/27] Create dependabot.yml Signed-off-by: Antoine Greuzard --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5f0889c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" From ebc5b41bdf9daf28f049c11133fd558d7d8cf4b3 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 09:02:36 +0100 Subject: [PATCH 07/27] feat: add some jobs --- .github/workflows/ci.yml | 45 +++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38b6bd1..bba239f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,36 +14,57 @@ jobs: runs-on: ubuntu-latest steps: - # Checkout du dépôt + # Checkout du dépôt - name: Checkout code uses: actions/checkout@v4 - # Configuration de Node.js + # Configuration de Node.js - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: 20 # Assurez-vous d'utiliser une version compatible avec votre projet - # Installation des dépendances + # Cache Node modules + - name: Cache Node modules + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + # Run Security Audit + - name: Run Security Audit + run: npm audit + + # Installation des dépendances - name: Install dependencies run: npm install - # Test de la compilation TypeScript + # Test de la compilation TypeScript - name: Build TypeScript run: npm run build - # Lancer les tests unitaires + # Lancer les tests unitaires - name: Run Unit Tests run: npm test - # Lancer Prettier pour formater le code + # Coverage tests + - name: Generate Coverage Report + run: npm run test -- --coverage + + # Lancer Prettier pour formater le code - name: Run Prettier run: npm run prettier-check - # Lancer ESLint pour vérifier la qualité du code + # Lancer ESLint pour vérifier la qualité du code - name: Run ESLint run: npm run lint + # Check bundle size + - name: Check bundle size + uses: andresz1/size-limit-action@v1 + deploy: runs-on: ubuntu-latest permissions: @@ -52,25 +73,25 @@ jobs: group: ${{ github.workflow }}-${{ github.ref }} needs: build-and-test steps: - # Checkout du dépôt + # Checkout du dépôt - name: Checkout code uses: actions/checkout@v4 - # Configuration de Node.js + # Configuration de Node.js - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: 20 - # Installation des dépendances + # Installation des dépendances - name: Install dependencies run: npm install - # Construction de l'application + # Construction de l'application - name: Build TypeScript run: npm run build - # Déploiement sur GitHub Pages + # Déploiement sur GitHub Pages - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 with: From d7df7ca377ca790b3e5f15755da21dc43b4218ca Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 09:05:36 +0100 Subject: [PATCH 08/27] fix: add token --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bba239f..1ce918e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,8 @@ jobs: # Check bundle size - name: Check bundle size uses: andresz1/size-limit-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} deploy: runs-on: ubuntu-latest From b793a3a4d09e39e802d2f55af99a7930f0f3c790 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 09:07:09 +0100 Subject: [PATCH 09/27] fix: rename token to github_token --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ce918e..315ce15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: - name: Check bundle size uses: andresz1/size-limit-action@v1 with: - token: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} deploy: runs-on: ubuntu-latest From 2223919802fba934ca66a895e0be5a92b34539c7 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 09:20:38 +0100 Subject: [PATCH 10/27] feat: add letters and x,y on canvas --- src/canvas-renderer.ts | 21 +++++++++++++++++++++ src/index.ts | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/canvas-renderer.ts b/src/canvas-renderer.ts index 1fdf547..3debc53 100644 --- a/src/canvas-renderer.ts +++ b/src/canvas-renderer.ts @@ -114,6 +114,8 @@ export class CanvasRenderer { // Dessiner les cases de l'échiquier private drawTiles(): void { + const letters = 'abcdefgh'; + for (let y = 0; y < 8; y++) { for (let x = 0; x < 8; x++) { const { x: newX, y: newY } = this.getCoordinates(x, y); @@ -138,6 +140,25 @@ export class CanvasRenderer { ); } } + + // Dessiner les indicateurs a-h et 1-8 + this.context.font = '16px Arial'; + this.context.fillStyle = 'black'; + for (let i = 0; i < 8; i++) { + // Afficher les lettres a-h en bas + const letterX = + this.getCoordinates(i, 0).x * this.tileSize + this.tileSize / 2; + const letterY = this.canvas.height - 5; + this.context.textAlign = 'center'; + this.context.fillText(letters[i], letterX, letterY); + + // Afficher les numéros 1-8 sur le côté + const numberX = 5; + const numberY = + this.getCoordinates(0, i).y * this.tileSize + this.tileSize / 2; + this.context.textBaseline = 'middle'; + this.context.fillText(`${8 - i}`, numberX, numberY); + } } // Dessiner toutes les pièces sur l'échiquier diff --git a/src/index.ts b/src/index.ts index acd0d95..96b4099 100644 --- a/src/index.ts +++ b/src/index.ts @@ -176,7 +176,8 @@ export async function initializeGame(playerColor: PieceColor) { toY: number, pieceType: PieceType, ) { - const moveText = `${getPieceSymbol(pieceType, currentPlayer)} de (${fromX}, ${fromY}) à (${toX}, ${toY})`; + const letters = 'abcdefgh'; + const moveText = `${getPieceSymbol(pieceType, currentPlayer)} de (${letters[fromX]}${8 - fromY}) à (${letters[toX]}${8 - toY})`; const listItem = document.createElement('li'); listItem.textContent = moveText; From bc85dfa62db9775c74277c61992959c757b78c71 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 09:21:31 +0100 Subject: [PATCH 11/27] remove dependabot --- .github/dependabot.yml | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 5f0889c..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file - -version: 2 -updates: - - package-ecosystem: "npm" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" From 656a93c90f4c0344bcebcf993d1106b44cdcf6ac Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 09:26:27 +0100 Subject: [PATCH 12/27] feat: add @size-limit --- .DS_Store | Bin 0 -> 8196 bytes package-lock.json | 1159 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 11 +- 3 files changed, 1169 insertions(+), 1 deletion(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..952d41c9179ca8f89d1f03ae7b9d12488d391246 GIT binary patch literal 8196 zcmeHMU2GIp6u#edDKi7Lw?9DH0W#90MOttR7AP3l{_&Gy3T*!<;JUjrz=Y{c-I?8j zgjhAcD5&wp2gScvqtORVtkLL`O?(m?BM)k#i7%RnJ{l7}Gj|qffd>;pnm9MP_sqHH zoO|y%_d9dv?lQ*EQ!+XjYhsMaR0GPFQFEIT$b))Ik)oaiQII_|yn>x~U7MJ=#1V)i z5Jw=6KpcTM0*f;O(J?LG;oM)Gi;hQ=-BY(G>&2oci@hFCEcoL4`R(booFuGol*`!r5tjJyd5% z3mUg^1mXzX8i62v63k#0^O%40{k@t^vJ%TPn?!~DlsB2T-8?DJAYXaTHbGw52+C?(W?;FgRCpUoEdw8`N=%ljB<_ zEwgwu=M_xHu$)uFQV^XqB>T}TaTA4 z{~T|Y<#3L5nywxd+b5V1;|4r$gPU4cocAX)D|i^K4UhXrpBjTZIpZ&RuGQHdb&aCdfrk5q*2KZtc-v z+JiaIADlFuU`ew=)o$zg zho+UQYP7>zM&?&!+8N6!TLfy_ae-~QIlWPAPUvKFIwd%TPOsH$Go6zA*>Pr4>YQU2 z*=6=RdzXF0zF=Rmuh~`h1N)Kv#C~SKu|L`0NT3F_;HX0qt=NE#Xv0HDqZ>UKz#$w) z217W3JPI&z5-v(8<1EhMX*`GLaS1Qr6}*YJ@HXDT3_ii9_zYj-3ckS{zQ^ylhClF^ zl#rH4)l#j*rFyABYLQx{l+-Eh77Ldu$#Qf)=A=EuFHDx`6(HHSOO|B+-hGQBS*|V* z)1$tjamBjk4V$)X+qLI9j0El~P`eqx;fBt`dWhOEeHI{YF3cl!&Du)h+(;#nTEhf) z^Q;_X9bX!0-@jfaxbr0u+tNzx0IwEovaMaF)Z$Xes>xJ`LJ7pHBDSrQSO`Y#+Nn?~ z@kGR~*-b19nPjr3mxh_;@_)za2W*CY&Su#h;q({wE4#-2flTO3LPtx8&o*qrHf+ZZ z?8L+9$6oBi0Yd5!hB1OMj1x``Lh314D8hqJXnhjXcnZ(pS;FdzcnL4#RlJ7Fcq2mX z`}lYP4iC)7A!g?DF?SauP$kA{3hw{?cmDnVF3uPa6-OYBz+#I45`CGz9$Ho9V_@L| zgS)m)^)aeJ3G}7~4Na&CRD%BSI!^TZABOa-N3wK8rv)Vmm3#g}0DX7H=l?C=sqy(A JqVfOJ{0otF9+m(A literal 0 HcmV?d00001 diff --git a/package-lock.json b/package-lock.json index a2a2c2e..f61693d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@babel/preset-env": "^7.26.0", "@babel/preset-typescript": "^7.26.0", "@jest/globals": "^29.7.0", + "@size-limit/preset-app": "^11.1.6", "@types/jest": "^29.5.14", "@types/node": "^22.8.1", "@typescript-eslint/eslint-plugin": "^8.11.0", @@ -24,6 +25,7 @@ "jest-environment-jsdom": "^29.7.0", "jest-fetch-mock": "^3.0.3", "prettier": "^3.3.3", + "size-limit": "^11.1.6", "ts-jest": "^29.2.5", "typescript": "^5.6.3", "undici": "^6.20.1", @@ -3060,6 +3062,29 @@ "node": ">= 8" } }, + "node_modules/@puppeteer/browsers": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz", + "integrity": "sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.3.6", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.4.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", @@ -3311,6 +3336,63 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@sitespeed.io/tracium": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@sitespeed.io/tracium/-/tracium-0.3.3.tgz", + "integrity": "sha512-dNZafjM93Y+F+sfwTO5gTpsGXlnc/0Q+c2+62ViqP3gkMWvHEMSKkaEHgVJLcLg3i/g19GSIPziiKpgyne07Bw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@size-limit/file": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/@size-limit/file/-/file-11.1.6.tgz", + "integrity": "sha512-ojzzJMrTfcSECRnaTjGy0wNIolTCRdyqZTSWG9sG5XEoXG6PNgHXDDS6gf6YNxnqb+rWfCfVe93u6aKi3wEocQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "size-limit": "11.1.6" + } + }, + "node_modules/@size-limit/preset-app": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/@size-limit/preset-app/-/preset-app-11.1.6.tgz", + "integrity": "sha512-soQSMEWAWSxjc8IPL9IYP/lYVeZZ9mqFuos6N+a8h5yAzuiVvLJN/lVk6av6il+ecAdlB/RoD4IQBrYTIeq6uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@size-limit/file": "11.1.6", + "@size-limit/time": "11.1.6", + "size-limit": "11.1.6" + }, + "peerDependencies": { + "size-limit": "11.1.6" + } + }, + "node_modules/@size-limit/time": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/@size-limit/time/-/time-11.1.6.tgz", + "integrity": "sha512-NIlJEPvUIxw87gHjriHpPhvd9fIC94S9wq7OW25K7Ctn14FZ2NlOTezPCfVViPmdlXjBYdi8vjsbc7kLCF1EpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "estimo": "^3.0.3" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "size-limit": "11.1.6" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -3321,6 +3403,13 @@ "node": ">= 10" } }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3481,6 +3570,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz", @@ -3848,6 +3948,19 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -3862,6 +3975,13 @@ "dev": true, "license": "MIT" }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -4047,6 +4167,88 @@ "dev": true, "license": "MIT" }, + "node_modules/bare-events": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.2.tgz", + "integrity": "sha512-EFZHSIBkDgSHIwj2l2QZfP4U5OcD4xFAOwhSb/vlr9PIqyGJGvB/nfClJbcnh3EY4jtPE4zsb5ztae96bVF79A==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.20.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -4126,6 +4328,41 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -4133,6 +4370,16 @@ "dev": true, "license": "MIT" }, + "node_modules/bytes-iec": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes-iec/-/bytes-iec-3.1.1.tgz", + "integrity": "sha512-fey6+4jDK7TFtFg/klGSvNKJctyU7n2aQdnM+CO0ruLPbqqMOM8Tio0Pc+deqUeVKX1tL5DQep1zQ7+37aTAsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4217,6 +4464,22 @@ "node": ">=10" } }, + "node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -4227,6 +4490,21 @@ "node": ">=10" } }, + "node_modules/chromium-bidi": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.17.tgz", + "integrity": "sha512-BqOuIWUgTPj8ayuBFJUYCCuwIcwjBsb3/614P7tt1bEPJ4i1M0kCdIl0Wi9xhtswBXnfO2bTpTMkHD71H8rJMg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0", + "zod": "3.22.4" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -4326,6 +4604,16 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4435,6 +4723,16 @@ "dev": true, "license": "MIT" }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -4520,6 +4818,21 @@ "node": ">=0.10.0" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4557,6 +4870,13 @@ "node": ">=8" } }, + "node_modules/devtools-protocol": { + "version": "0.0.1262051", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1262051.tgz", + "integrity": "sha512-YJe4CT5SA8on3Spa+UDtNhEqtuV6Epwz3OZ4HQVLhlRccpZ9/PAYk0/cy/oKxFKRrZPBUPyxympQci4yWNWZ9g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -4624,6 +4944,16 @@ "dev": true, "license": "MIT" }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -4930,6 +5260,45 @@ "node": ">=4.0" } }, + "node_modules/estimo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estimo/-/estimo-3.0.3.tgz", + "integrity": "sha512-qSibrDHo82yvmgeOW7onGgeOzS/nnqa8r2exQ8LyTSH8rAma10VBJE+hPSdukV1nQrqFvEz7BVe5puUK2LZJXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sitespeed.io/tracium": "^0.3.3", + "commander": "^12.0.0", + "find-chrome-bin": "2.0.2", + "nanoid": "5.0.7", + "puppeteer-core": "22.6.5" + }, + "bin": { + "estimo": "scripts/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/estimo/node_modules/nanoid": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", + "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", @@ -5000,6 +5369,43 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5007,6 +5413,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -5071,6 +5484,16 @@ "bser": "2.1.1" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5120,6 +5543,19 @@ "node": ">=8" } }, + "node_modules/find-chrome-bin": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/find-chrome-bin/-/find-chrome-bin-2.0.2.tgz", + "integrity": "sha512-KlggCilbbvgETk/WEq9NG894U8yu4erIW0SjMm1sMPm2xihCHeNoybpzGoxEzHRthwF3XrKOgHYtfqgJzpCH2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@puppeteer/browsers": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -5173,6 +5609,31 @@ "node": ">= 6" } }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -5303,6 +5764,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -5491,6 +5968,27 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5567,6 +6065,27 @@ "dev": true, "license": "ISC" }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -6406,6 +6925,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.3.3.tgz", + "integrity": "sha512-EX4oNDwcXSivPrw2qKH2LB5PoFxEvgtv2JgwW0bU858HoLQ+kutSvjLMUqBd0PeJYEinLWhoI9Ol0eYMqj/wNQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6426,6 +6955,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, "node_modules/jsdom": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", @@ -6526,6 +7062,29 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6570,6 +7129,19 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -6794,6 +7366,13 @@ "dev": true, "license": "ISC" }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -6840,6 +7419,16 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/nanospinner": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/nanospinner/-/nanospinner-1.2.0.tgz", + "integrity": "sha512-dGxYcEj8YhuxjVO3PYmnj1nBhtwUkvuwYbLl/MduBPmQUPy3xBtG/ScJgqZgntQkX44UQaCSlFeW4rS5fUR/Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -6847,6 +7436,16 @@ "dev": true, "license": "MIT" }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -7063,6 +7662,81 @@ "node": ">=6" } }, + "node_modules/pac-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", + "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.5", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7145,6 +7819,13 @@ "dev": true, "license": "MIT" }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7327,6 +8008,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/promise-polyfill": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", @@ -7348,6 +8039,84 @@ "node": ">= 6" } }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -7355,6 +8124,17 @@ "dev": true, "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7365,6 +8145,144 @@ "node": ">=6" } }, + "node_modules/puppeteer-core": { + "version": "22.6.5", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.6.5.tgz", + "integrity": "sha512-s0/5XkAWe0/dWISiljdrybjwDCHhgN31Nu/wznOZPKeikgcJtZtbvPKBz0t802XWqfSQnQDt3L6xiAE5JLlfuw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.2.2", + "chromium-bidi": "0.5.17", + "debug": "4.3.4", + "devtools-protocol": "0.0.1262051", + "ws": "8.16.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core/node_modules/@puppeteer/browsers": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.2.tgz", + "integrity": "sha512-hZ/JhxPIceWaGSEzUZp83/8M49CoxlkuThfTR7t4AoCu5+ZvJ3vktLm60Otww2TXeROB5igiZ8D9oPQh6ckBVg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.4.0", + "semver": "7.6.0", + "tar-fs": "3.0.5", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/puppeteer-core/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/puppeteer-core/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/puppeteer-core/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/puppeteer-core/node_modules/tar-fs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", + "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } + }, + "node_modules/puppeteer-core/node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/puppeteer-core/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -7410,6 +8328,13 @@ ], "license": "MIT" }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true, + "license": "MIT" + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -7432,6 +8357,20 @@ "node": ">= 6" } }, + "node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -7804,6 +8743,28 @@ "dev": true, "license": "MIT" }, + "node_modules/size-limit": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/size-limit/-/size-limit-11.1.6.tgz", + "integrity": "sha512-S5ux2IB8rU26xwVgMskmknGMFkieaIAqDLuwgKiypk6oa4lFsie8yFPrzRFV+yrLDY2GddjXuCaVk5PveVOHiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes-iec": "^3.1.1", + "chokidar": "^4.0.1", + "jiti": "^2.0.0", + "lilconfig": "^3.1.2", + "nanospinner": "^1.1.0", + "picocolors": "^1.1.0", + "tinyglobby": "^0.2.7" + }, + "bin": { + "size-limit": "bin.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7814,6 +8775,60 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7875,6 +8890,21 @@ "node": ">=8" } }, + "node_modules/streamx": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -8011,6 +9041,33 @@ "node": ">=10" } }, + "node_modules/tar-fs": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -8057,6 +9114,13 @@ "node": "*" } }, + "node_modules/text-decoder": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", + "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -8064,6 +9128,55 @@ "dev": true, "license": "MIT" }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", + "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -8175,6 +9288,13 @@ } } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8224,6 +9344,17 @@ "node": ">=14.17" } }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "node_modules/undici": { "version": "6.20.1", "resolved": "https://registry.npmjs.org/undici/-/undici-6.20.1.tgz", @@ -8347,6 +9478,13 @@ "requires-port": "^1.0.0" } }, + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", + "dev": true, + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -8659,6 +9797,17 @@ "node": ">=12" } }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -8671,6 +9820,16 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 03076f2..7dbd7ff 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,12 @@ "license": "MIT", "type": "module", "main": "dist/index.js", + "size-limit": [ + { + "path": "dist/**/*.js", + "limit": "200 KB" + } + ], "scripts": { "build": "vite build", "start": "vite", @@ -12,13 +18,15 @@ "prettier-check": "prettier --check 'src/**/*.{js,ts,html,css}'", "lint": "eslint 'src/**/*.{js,ts}' --config ./eslint.config.js", "test-server": "npm run start & sleep 5 && pkill -f vite", - "test": "jest" + "test": "jest", + "size": "size-limit" }, "devDependencies": { "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "@babel/preset-typescript": "^7.26.0", "@jest/globals": "^29.7.0", + "@size-limit/preset-app": "^11.1.6", "@types/jest": "^29.5.14", "@types/node": "^22.8.1", "@typescript-eslint/eslint-plugin": "^8.11.0", @@ -30,6 +38,7 @@ "jest-environment-jsdom": "^29.7.0", "jest-fetch-mock": "^3.0.3", "prettier": "^3.3.3", + "size-limit": "^11.1.6", "ts-jest": "^29.2.5", "typescript": "^5.6.3", "undici": "^6.20.1", From d20c571adc90f863b8371dbddd0d31ee4a823a89 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 09:34:45 +0100 Subject: [PATCH 13/27] remove size-limit --- package-lock.json | 1151 +-------------------------------------------- package.json | 2 - 2 files changed, 2 insertions(+), 1151 deletions(-) diff --git a/package-lock.json b/package-lock.json index f61693d..d5e0960 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "@babel/preset-env": "^7.26.0", "@babel/preset-typescript": "^7.26.0", "@jest/globals": "^29.7.0", - "@size-limit/preset-app": "^11.1.6", "@types/jest": "^29.5.14", "@types/node": "^22.8.1", "@typescript-eslint/eslint-plugin": "^8.11.0", @@ -25,7 +24,6 @@ "jest-environment-jsdom": "^29.7.0", "jest-fetch-mock": "^3.0.3", "prettier": "^3.3.3", - "size-limit": "^11.1.6", "ts-jest": "^29.2.5", "typescript": "^5.6.3", "undici": "^6.20.1", @@ -3062,29 +3060,6 @@ "node": ">= 8" } }, - "node_modules/@puppeteer/browsers": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz", - "integrity": "sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.3.6", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.4.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", @@ -3336,63 +3311,6 @@ "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@sitespeed.io/tracium": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@sitespeed.io/tracium/-/tracium-0.3.3.tgz", - "integrity": "sha512-dNZafjM93Y+F+sfwTO5gTpsGXlnc/0Q+c2+62ViqP3gkMWvHEMSKkaEHgVJLcLg3i/g19GSIPziiKpgyne07Bw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@size-limit/file": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/@size-limit/file/-/file-11.1.6.tgz", - "integrity": "sha512-ojzzJMrTfcSECRnaTjGy0wNIolTCRdyqZTSWG9sG5XEoXG6PNgHXDDS6gf6YNxnqb+rWfCfVe93u6aKi3wEocQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "size-limit": "11.1.6" - } - }, - "node_modules/@size-limit/preset-app": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/@size-limit/preset-app/-/preset-app-11.1.6.tgz", - "integrity": "sha512-soQSMEWAWSxjc8IPL9IYP/lYVeZZ9mqFuos6N+a8h5yAzuiVvLJN/lVk6av6il+ecAdlB/RoD4IQBrYTIeq6uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@size-limit/file": "11.1.6", - "@size-limit/time": "11.1.6", - "size-limit": "11.1.6" - }, - "peerDependencies": { - "size-limit": "11.1.6" - } - }, - "node_modules/@size-limit/time": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/@size-limit/time/-/time-11.1.6.tgz", - "integrity": "sha512-NIlJEPvUIxw87gHjriHpPhvd9fIC94S9wq7OW25K7Ctn14FZ2NlOTezPCfVViPmdlXjBYdi8vjsbc7kLCF1EpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "estimo": "^3.0.3" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "size-limit": "11.1.6" - } - }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -3403,13 +3321,6 @@ "node": ">= 10" } }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3570,17 +3481,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz", @@ -3948,19 +3848,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -3975,13 +3862,6 @@ "dev": true, "license": "MIT" }, - "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -4167,88 +4047,6 @@ "dev": true, "license": "MIT" }, - "node_modules/bare-events": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", - "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", - "dev": true, - "license": "Apache-2.0", - "optional": true - }, - "node_modules/bare-fs": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", - "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-events": "^2.0.0", - "bare-path": "^2.0.0", - "bare-stream": "^2.0.0" - } - }, - "node_modules/bare-os": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", - "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", - "dev": true, - "license": "Apache-2.0", - "optional": true - }, - "node_modules/bare-path": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", - "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-os": "^2.1.0" - } - }, - "node_modules/bare-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.2.tgz", - "integrity": "sha512-EFZHSIBkDgSHIwj2l2QZfP4U5OcD4xFAOwhSb/vlr9PIqyGJGvB/nfClJbcnh3EY4jtPE4zsb5ztae96bVF79A==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "streamx": "^2.20.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -4328,41 +4126,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -4370,16 +4133,6 @@ "dev": true, "license": "MIT" }, - "node_modules/bytes-iec": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes-iec/-/bytes-iec-3.1.1.tgz", - "integrity": "sha512-fey6+4jDK7TFtFg/klGSvNKJctyU7n2aQdnM+CO0ruLPbqqMOM8Tio0Pc+deqUeVKX1tL5DQep1zQ7+37aTAsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4464,22 +4217,6 @@ "node": ">=10" } }, - "node_modules/chokidar": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", - "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -4490,21 +4227,6 @@ "node": ">=10" } }, - "node_modules/chromium-bidi": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.17.tgz", - "integrity": "sha512-BqOuIWUgTPj8ayuBFJUYCCuwIcwjBsb3/614P7tt1bEPJ4i1M0kCdIl0Wi9xhtswBXnfO2bTpTMkHD71H8rJMg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0", - "zod": "3.22.4" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -4604,16 +4326,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4723,16 +4435,6 @@ "dev": true, "license": "MIT" }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -4818,21 +4520,6 @@ "node": ">=0.10.0" } }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4870,13 +4557,6 @@ "node": ">=8" } }, - "node_modules/devtools-protocol": { - "version": "0.0.1262051", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1262051.tgz", - "integrity": "sha512-YJe4CT5SA8on3Spa+UDtNhEqtuV6Epwz3OZ4HQVLhlRccpZ9/PAYk0/cy/oKxFKRrZPBUPyxympQci4yWNWZ9g==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -4944,16 +4624,6 @@ "dev": true, "license": "MIT" }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -5260,45 +4930,6 @@ "node": ">=4.0" } }, - "node_modules/estimo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estimo/-/estimo-3.0.3.tgz", - "integrity": "sha512-qSibrDHo82yvmgeOW7onGgeOzS/nnqa8r2exQ8LyTSH8rAma10VBJE+hPSdukV1nQrqFvEz7BVe5puUK2LZJXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sitespeed.io/tracium": "^0.3.3", - "commander": "^12.0.0", - "find-chrome-bin": "2.0.2", - "nanoid": "5.0.7", - "puppeteer-core": "22.6.5" - }, - "bin": { - "estimo": "scripts/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/estimo/node_modules/nanoid": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", - "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.js" - }, - "engines": { - "node": "^18 || >=20" - } - }, "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", @@ -5369,43 +5000,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5413,13 +5007,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true, - "license": "MIT" - }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -5484,16 +5071,6 @@ "bser": "2.1.1" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5543,19 +5120,6 @@ "node": ">=8" } }, - "node_modules/find-chrome-bin": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/find-chrome-bin/-/find-chrome-bin-2.0.2.tgz", - "integrity": "sha512-KlggCilbbvgETk/WEq9NG894U8yu4erIW0SjMm1sMPm2xihCHeNoybpzGoxEzHRthwF3XrKOgHYtfqgJzpCH2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@puppeteer/browsers": "^2.1.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -5609,31 +5173,6 @@ "node": ">= 6" } }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs-extra/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -5764,22 +5303,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-uri": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", - "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", - "dev": true, - "license": "MIT", - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -5968,27 +5491,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6065,27 +5567,6 @@ "dev": true, "license": "ISC" }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -6931,6 +6412,8 @@ "integrity": "sha512-EX4oNDwcXSivPrw2qKH2LB5PoFxEvgtv2JgwW0bU858HoLQ+kutSvjLMUqBd0PeJYEinLWhoI9Ol0eYMqj/wNQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -6955,13 +6438,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true, - "license": "MIT" - }, "node_modules/jsdom": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", @@ -7062,29 +6538,6 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonfile/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7129,19 +6582,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -7366,13 +6806,6 @@ "dev": true, "license": "ISC" }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -7419,16 +6852,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/nanospinner": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/nanospinner/-/nanospinner-1.2.0.tgz", - "integrity": "sha512-dGxYcEj8YhuxjVO3PYmnj1nBhtwUkvuwYbLl/MduBPmQUPy3xBtG/ScJgqZgntQkX44UQaCSlFeW4rS5fUR/Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "picocolors": "^1.1.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -7436,16 +6859,6 @@ "dev": true, "license": "MIT" }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -7662,81 +7075,6 @@ "node": ">=6" } }, - "node_modules/pac-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", - "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.5", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "dev": true, - "license": "MIT", - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7819,13 +7157,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -8008,16 +7339,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/promise-polyfill": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", @@ -8039,84 +7360,6 @@ "node": ">= 6" } }, - "node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, - "license": "MIT" - }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -8124,17 +7367,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -8145,144 +7377,6 @@ "node": ">=6" } }, - "node_modules/puppeteer-core": { - "version": "22.6.5", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.6.5.tgz", - "integrity": "sha512-s0/5XkAWe0/dWISiljdrybjwDCHhgN31Nu/wznOZPKeikgcJtZtbvPKBz0t802XWqfSQnQDt3L6xiAE5JLlfuw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "2.2.2", - "chromium-bidi": "0.5.17", - "debug": "4.3.4", - "devtools-protocol": "0.0.1262051", - "ws": "8.16.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core/node_modules/@puppeteer/browsers": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.2.tgz", - "integrity": "sha512-hZ/JhxPIceWaGSEzUZp83/8M49CoxlkuThfTR7t4AoCu5+ZvJ3vktLm60Otww2TXeROB5igiZ8D9oPQh6ckBVg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "4.3.4", - "extract-zip": "2.0.1", - "progress": "2.0.3", - "proxy-agent": "6.4.0", - "semver": "7.6.0", - "tar-fs": "3.0.5", - "unbzip2-stream": "1.4.3", - "yargs": "17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/puppeteer-core/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/puppeteer-core/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/puppeteer-core/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/puppeteer-core/node_modules/tar-fs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", - "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" - } - }, - "node_modules/puppeteer-core/node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/puppeteer-core/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -8328,13 +7422,6 @@ ], "license": "MIT" }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true, - "license": "MIT" - }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -8357,20 +7444,6 @@ "node": ">= 6" } }, - "node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -8743,28 +7816,6 @@ "dev": true, "license": "MIT" }, - "node_modules/size-limit": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/size-limit/-/size-limit-11.1.6.tgz", - "integrity": "sha512-S5ux2IB8rU26xwVgMskmknGMFkieaIAqDLuwgKiypk6oa4lFsie8yFPrzRFV+yrLDY2GddjXuCaVk5PveVOHiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes-iec": "^3.1.1", - "chokidar": "^4.0.1", - "jiti": "^2.0.0", - "lilconfig": "^3.1.2", - "nanospinner": "^1.1.0", - "picocolors": "^1.1.0", - "tinyglobby": "^0.2.7" - }, - "bin": { - "size-limit": "bin.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -8775,60 +7826,6 @@ "node": ">=8" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.1", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -8890,21 +7887,6 @@ "node": ">=8" } }, - "node_modules/streamx": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", - "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-fifo": "^1.3.2", - "queue-tick": "^1.0.1", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -9041,33 +8023,6 @@ "node": ">=10" } }, - "node_modules/tar-fs": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", - "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" - } - }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -9114,13 +8069,6 @@ "node": "*" } }, - "node_modules/text-decoder": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", - "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -9128,55 +8076,6 @@ "dev": true, "license": "MIT" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", - "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", - "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -9288,13 +8187,6 @@ } } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -9344,17 +8236,6 @@ "node": ">=14.17" } }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, "node_modules/undici": { "version": "6.20.1", "resolved": "https://registry.npmjs.org/undici/-/undici-6.20.1.tgz", @@ -9478,13 +8359,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true, - "license": "MIT" - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -9797,17 +8671,6 @@ "node": ">=12" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -9820,16 +8683,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zod": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", - "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } } } diff --git a/package.json b/package.json index 7dbd7ff..8187eef 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ "@babel/preset-env": "^7.26.0", "@babel/preset-typescript": "^7.26.0", "@jest/globals": "^29.7.0", - "@size-limit/preset-app": "^11.1.6", "@types/jest": "^29.5.14", "@types/node": "^22.8.1", "@typescript-eslint/eslint-plugin": "^8.11.0", @@ -38,7 +37,6 @@ "jest-environment-jsdom": "^29.7.0", "jest-fetch-mock": "^3.0.3", "prettier": "^3.3.3", - "size-limit": "^11.1.6", "ts-jest": "^29.2.5", "typescript": "^5.6.3", "undici": "^6.20.1", From cfa0b3507fadb0ed9023ccdaff97420d5b0e353d Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 09:35:16 +0100 Subject: [PATCH 14/27] remove size-limit --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 8187eef..eee55f4 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,7 @@ "prettier-check": "prettier --check 'src/**/*.{js,ts,html,css}'", "lint": "eslint 'src/**/*.{js,ts}' --config ./eslint.config.js", "test-server": "npm run start & sleep 5 && pkill -f vite", - "test": "jest", - "size": "size-limit" + "test": "jest" }, "devDependencies": { "@babel/core": "^7.26.0", From f399879229f11b67a702712be5eb2bee7670feb7 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 09:36:47 +0100 Subject: [PATCH 15/27] remove size-limit job --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 315ce15..7ce3211 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,12 +61,6 @@ jobs: - name: Run ESLint run: npm run lint - # Check bundle size - - name: Check bundle size - uses: andresz1/size-limit-action@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - deploy: runs-on: ubuntu-latest permissions: From aeede2ec9fade18fcace6b69ee2339af55ad0035 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 10:50:39 +0100 Subject: [PATCH 16/27] feat: better sort --- src/ai.ts | 65 +++++++++++++++++++++++++++++++++++---------- src/ai/evaluator.ts | 4 +-- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/ai.ts b/src/ai.ts index fe14283..a5b30d3 100644 --- a/src/ai.ts +++ b/src/ai.ts @@ -5,6 +5,7 @@ import { centerControlBonus, evaluateBoard, evaluateKingSafety, + pieceValues, } from './ai/evaluator'; import { getEndgameMove } from './ai/endgameTablebase'; import { flipMove, getNextOpeningMove, openingBook } from './ai/openingBook'; @@ -30,6 +31,7 @@ export class AI { toX: number; toY: number; }[] = []; + private readonly historicalMoveScores: Map = new Map(); // Stockage des scores historiques des mouvements constructor( private readonly color: PieceColor, @@ -129,11 +131,24 @@ export class AI { // Ajoute le meilleur mouvement trouvé à l'historique des coups si existant if (bestMove) { this.moveHistory.push(bestMove); + this.updateHistoricalScore(bestMove); } return bestMove; } + // Fonction pour augmenter le score historique d'un mouvement après son utilisation + private updateHistoricalScore(move: { + fromX: number; + fromY: number; + toX: number; + toY: number; + }) { + const moveKey = `${move.fromX},${move.fromY},${move.toX},${move.toY}`; + const currentScore = this.historicalMoveScores.get(moveKey) || 0; + this.historicalMoveScores.set(moveKey, currentScore + 1); + } + // Fonction pour récupérer les coups passés en format abrégé private getPastMoves(): string[] { return this.moveHistory.map( @@ -395,29 +410,51 @@ export class AI { depth: number, ): { fromX: number; fromY: number; toX: number; toY: number }[] { return moves.sort((a, b) => { - const killerMovesAtDepth = this.killerMoves.get(depth); + // 1. Priorité aux mouvements capturant des pièces de valeur plus élevée + const pieceA = board.getPiece(a.toX, a.toY); + const pieceB = board.getPiece(b.toX, b.toY); + + const valueA = pieceA ? pieceValues[pieceA.type] : 0; + const valueB = pieceB ? pieceValues[pieceB.type] : 0; + if (valueA !== valueB) { + return valueB - valueA; // Tri décroissant par valeur de capture + } + + // 2. Bonus pour le contrôle des cases centrales + const centerControlA = centerControlBonus[`${a.toX},${a.toY}`] || 0; + const centerControlB = centerControlBonus[`${b.toX},${b.toY}`] || 0; + + if (centerControlA !== centerControlB) { + return centerControlB - centerControlA; // Tri par contrôle du centre + } + + // 3. Priorité aux mouvements dans les killer moves pour cette profondeur + const killerMovesAtDepth = this.killerMoves.get(depth); if ( killerMovesAtDepth && killerMovesAtDepth.some( - (move: { - move: { fromX: number; fromY: number; toX: number; toY: number }; - }) => move.move.fromX === a.fromX && move.move.fromY === a.fromY, + (move) => + move.move.fromX === a.fromX && + move.move.fromY === a.fromY && + move.move.toX === a.toX && + move.move.toY === a.toY, ) ) { return -1; } - const pieceA = board.getPiece(a.toX, a.toY); - const pieceB = board.getPiece(b.toX, b.toY); - - if (pieceA && !pieceB) return -1; - if (!pieceA && pieceB) return 1; - - const centerControlA = centerControlBonus[`${a.toX},${a.toY}`] || 0; - const centerControlB = centerControlBonus[`${b.toX},${b.toY}`] || 0; - - return centerControlB - centerControlA; + // 4. Score historique pour le mouvement, favorise les coups réussis dans le passé + const scoreA = + this.historicalMoveScores.get( + `${a.fromX},${a.fromY},${a.toX},${a.toY}`, + ) || 0; + const scoreB = + this.historicalMoveScores.get( + `${b.fromX},${b.fromY},${b.toX},${b.toY}`, + ) || 0; + + return scoreB - scoreA; // Tri par score historique décroissant }); } diff --git a/src/ai/evaluator.ts b/src/ai/evaluator.ts index bf6af06..fef44f5 100644 --- a/src/ai/evaluator.ts +++ b/src/ai/evaluator.ts @@ -3,7 +3,7 @@ import { Board } from '../board'; import { PieceColor, PieceType } from '../piece'; // Valeurs des pièces (évaluation de base) -const pieceValues: { [key in PieceType]: number } = { +export const pieceValues: { [key in PieceType]: number } = { [PieceType.PAWN]: 1, [PieceType.KNIGHT]: 3, [PieceType.BISHOP]: 3.25, @@ -95,7 +95,7 @@ export const centerControlBonus: { [key: string]: number } = { export function evaluateKingSafety(board: Board, color: PieceColor): number { const kingPosition = board.findKing(color); return kingPosition && - board.isSquareUnderAttack(kingPosition.x, kingPosition.y, color) + board.isSquareUnderAttack(kingPosition.x, kingPosition.y, color) ? -0.5 : 0; } From 89841bec3969ef35a04bd46c13b9ddab2fd1f9c1 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 10:53:53 +0100 Subject: [PATCH 17/27] feat: better quiescence --- src/ai.ts | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/ai.ts b/src/ai.ts index a5b30d3..089460c 100644 --- a/src/ai.ts +++ b/src/ai.ts @@ -321,7 +321,7 @@ export class AI { beta: number, depth: number = 0, ): number { - const maxQuiescenceDepth = 5; + const maxQuiescenceDepth = this.getAdaptiveQuiescenceDepth(board); if (depth >= maxQuiescenceDepth) { return evaluateBoard(board, this.color); @@ -335,7 +335,19 @@ export class AI { board.isCapture(move.fromX, move.fromY, move.toX, move.toY), ); - for (const move of captureMoves) { + // Non-capture moves : Inclut quelques mouvements non capturants comme les poussées de pions + const nonCaptureMoves = this.getAllValidMoves(board).filter((move) => { + const piece = board.getPiece(move.fromX, move.fromY); + return ( + !board.isCapture(move.fromX, move.fromY, move.toX, move.toY) && + piece && + (piece.type === PieceType.PAWN || piece.type === PieceType.KNIGHT) + ); + }); + + const moves = [...captureMoves, ...nonCaptureMoves]; + + for (const move of moves) { const fromPiece = board.getPiece(move.fromX, move.fromY); const toPiece = board.getPiece(move.toX, move.toY); @@ -513,6 +525,14 @@ export class AI { return { fromX, fromY, toX, toY }; } + // Adaptation de la profondeur de quiescence en fonction de la situation + private getAdaptiveQuiescenceDepth(board: Board): number { + const pieceCount = board.getPieceCount(); + if (pieceCount <= 6) return 7; // Profondeur plus élevée en fin de partie + if (pieceCount <= 12) return 5; // Moyenne en milieu de partie + return 3; // Réduit en début de partie + } + private evaluatePositionWithKingSafety( board: Board, color: PieceColor, From a8e218192ebbd079dad56ff82a607481edf7bd75 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 10:56:34 +0100 Subject: [PATCH 18/27] feat: better board evaluation --- src/ai/evaluator.ts | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/ai/evaluator.ts b/src/ai/evaluator.ts index fef44f5..c3037e7 100644 --- a/src/ai/evaluator.ts +++ b/src/ai/evaluator.ts @@ -95,7 +95,7 @@ export const centerControlBonus: { [key: string]: number } = { export function evaluateKingSafety(board: Board, color: PieceColor): number { const kingPosition = board.findKing(color); return kingPosition && - board.isSquareUnderAttack(kingPosition.x, kingPosition.y, color) + board.isSquareUnderAttack(kingPosition.x, kingPosition.y, color) ? -0.5 : 0; } @@ -136,9 +136,10 @@ export function evaluateBoard( pieceScore += centerControlBonus[positionKey]; } - // Évalue les pions pour structure et bonus de pion passé + // Évalue les pions pour la structure et les chaînes protégées if (piece.type === PieceType.PAWN) { pieceScore += evaluatePawnStructure(board, x, y, piece.color); + pieceScore += evaluatePawnChains(board, x, y, piece.color); // Bonus pour chaînes de pions } // Pénalise les rois exposés @@ -156,6 +157,34 @@ export function evaluateBoard( return parseFloat(score.toFixed(2)); } +// Fonction pour évaluer les chaînes de pions +function evaluatePawnChains( + board: Board, + x: number, + y: number, + color: PieceColor, +): number { + const direction = color === PieceColor.WHITE ? -1 : 1; + let score = 0; + + // Vérifie les pions sur les diagonales avant (chaînes protégées) + const leftDiagonal = board.getPiece(x - 1, y + direction); + const rightDiagonal = board.getPiece(x + 1, y + direction); + + if ( + (leftDiagonal && + leftDiagonal.color === color && + leftDiagonal.type === PieceType.PAWN) || + (rightDiagonal && + rightDiagonal.color === color && + rightDiagonal.type === PieceType.PAWN) + ) { + score += 0.5; // Bonus pour les pions protégés dans une chaîne + } + + return score; +} + // Évaluer la structure des pions function evaluatePawnStructure( board: Board, From bcb289f54d88dcb7cc09eb08707105e459bad144 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 11:02:02 +0100 Subject: [PATCH 19/27] feat: better opening move --- src/ai.ts | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/src/ai.ts b/src/ai.ts index 089460c..8dd9b1a 100644 --- a/src/ai.ts +++ b/src/ai.ts @@ -56,7 +56,7 @@ export class AI { // Vérifie si un mouvement d'ouverture basé sur les coups passés est disponible const pastMoves = this.getPastMoves(); - const chosenMove = this.chooseMove(pastMoves); + const chosenMove = this.chooseMove(pastMoves, board); if (chosenMove) { this.moveHistory.push(chosenMove); // Ajoute à l'historique des coups return chosenMove; @@ -276,11 +276,35 @@ export class AI { private chooseMove( pastMoves: string[], + board: Board, ): { fromX: number; fromY: number; toX: number; toY: number } | null { - const openingMove = getNextOpeningMove(pastMoves, openingBook); + const openingMove = getNextOpeningMove(pastMoves, this.openingMoves); if (openingMove) { - return openingMove; + const flippedMove = flipMove( + openingMove, + this.color === PieceColor.BLACK, + ); + + // Évaluation de la position pour ajuster le choix de l'ouverture + board.movePiece( + flippedMove.fromX, + flippedMove.fromY, + flippedMove.toX, + flippedMove.toY, + ); + const evaluation = evaluateBoard(board, this.color); + board.setPiece( + flippedMove.fromX, + flippedMove.fromY, + board.getPiece(flippedMove.toX, flippedMove.toY), + ); + board.setPiece(flippedMove.toX, flippedMove.toY, null); + + const threshold = 0.3; + if (evaluation >= threshold) { + return flippedMove; + } } return null; @@ -639,11 +663,33 @@ export class AI { const boardHash = this.getBoardHash(board); if (this.openingMoves[boardHash]) { + // Sélectionne le premier coup suggéré par défaut const move = this.openingMoves[boardHash][0]; - return flipMove(move, this.color === PieceColor.BLACK); // Applique flipMove si c'est les Noirs + const flippedMove = flipMove(move, this.color === PieceColor.BLACK); + + // Évalue la position après le coup d'ouverture + board.movePiece( + flippedMove.fromX, + flippedMove.fromY, + flippedMove.toX, + flippedMove.toY, + ); + const evaluation = evaluateBoard(board, this.color); + board.setPiece( + flippedMove.fromX, + flippedMove.fromY, + board.getPiece(flippedMove.toX, flippedMove.toY), + ); + board.setPiece(flippedMove.toX, flippedMove.toY, null); + + // Seuil pour sortir du livre d'ouvertures si la position est défavorable + const exitThreshold = -0.5; + if (evaluation > exitThreshold) { + return flippedMove; // Choisit le coup d'ouverture par défaut si l'évaluation est favorable + } } - return null; + return null; // Sort du livre si aucune ouverture valide n'est trouvée ou si l'évaluation est inférieure au seuil } // Génération d'un identifiant de position simplifié pour le dictionnaire d'ouverture From e6feb25a21639509e48fbce7b6b880c85eee2f1c Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 13:24:11 +0100 Subject: [PATCH 20/27] feat: add tests --- src/ai.ts | 20 +++++-------- src/ai/openingBook.ts | 5 ++-- tests/openingBook.test.ts | 61 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 16 deletions(-) diff --git a/src/ai.ts b/src/ai.ts index 8dd9b1a..1a69d2d 100644 --- a/src/ai.ts +++ b/src/ai.ts @@ -55,10 +55,9 @@ export class AI { } // Vérifie si un mouvement d'ouverture basé sur les coups passés est disponible - const pastMoves = this.getPastMoves(); - const chosenMove = this.chooseMove(pastMoves, board); + const chosenMove = this.chooseMove(board); if (chosenMove) { - this.moveHistory.push(chosenMove); // Ajoute à l'historique des coups + this.moveHistory.push(chosenMove); return chosenMove; } const endgameMove = this.useEndgameTablebase(board); @@ -149,13 +148,6 @@ export class AI { this.historicalMoveScores.set(moveKey, currentScore + 1); } - // Fonction pour récupérer les coups passés en format abrégé - private getPastMoves(): string[] { - return this.moveHistory.map( - (move) => `${move.fromX}${move.fromY}${move.toX}${move.toY}`, - ); - } - // Fonction Minimax avec Alpha-Beta Pruning et table de transposition private minimax( board: Board, @@ -275,10 +267,14 @@ export class AI { } private chooseMove( - pastMoves: string[], board: Board, ): { fromX: number; fromY: number; toX: number; toY: number } | null { - const openingMove = getNextOpeningMove(pastMoves, this.openingMoves); + const key = this.moveHistory + .map((move) => `${move.fromX}${move.fromY}${move.toX}${move.toY}`) + .join(' '); + + console.log(key); + const openingMove = getNextOpeningMove(key, this.openingMoves); if (openingMove) { const flippedMove = flipMove( diff --git a/src/ai/openingBook.ts b/src/ai/openingBook.ts index 2264000..c39441b 100644 --- a/src/ai/openingBook.ts +++ b/src/ai/openingBook.ts @@ -119,11 +119,10 @@ export function flipMove( } export function getNextOpeningMove( - moves: string[], + key: string, openingBook: OpeningBook, ): { fromX: number; fromY: number; toX: number; toY: number } | null { - const key = moves.join(' '); - return openingBook[key]?.[moves.length] ?? null; + return openingBook[key]?.[0] ?? null; } export type OpeningBook = { diff --git a/tests/openingBook.test.ts b/tests/openingBook.test.ts index e65c425..d2169ea 100644 --- a/tests/openingBook.test.ts +++ b/tests/openingBook.test.ts @@ -1,5 +1,5 @@ // tests/openingBook.test.ts -import { openingBook } from '../src/ai/openingBook'; +import { flipMove, getNextOpeningMove, openingBook } from '../src/ai/openingBook'; describe('Opening Book Tests', () => { test('Ruy Lopez Opening', () => { @@ -21,6 +21,16 @@ describe('Opening Book Tests', () => { ]); }); + test('Extended Sicilian Defense', () => { + const moves = openingBook['e2e4 c7c5 g1f3 d7d6']; + expect(moves).toEqual([ + { fromX: 4, fromY: 6, toX: 4, toY: 4 }, + { fromX: 2, fromY: 1, toX: 2, toY: 3 }, + { fromX: 6, fromY: 7, toX: 5, toY: 5 }, + { fromX: 3, fromY: 1, toX: 3, toY: 2 }, + ]); + }); + test('Queen\'s Gambit', () => { const moves = openingBook['d2d4 d7d5 c2c4']; expect(moves).toEqual([ @@ -38,6 +48,16 @@ describe('Opening Book Tests', () => { ]); }); + test('Extended Caro-Kann Defense', () => { + const moves = openingBook['e2e4 c7c6 d2d4 d7d5']; + expect(moves).toEqual([ + { fromX: 4, fromY: 6, toX: 4, toY: 4 }, + { fromX: 2, fromY: 1, toX: 2, toY: 2 }, + { fromX: 3, fromY: 6, toX: 3, toY: 4 }, + { fromX: 3, fromY: 1, toX: 3, toY: 3 }, + ]); + }); + test('French Defense', () => { const moves = openingBook['e2e4 e7e6']; expect(moves).toEqual([ @@ -46,6 +66,16 @@ describe('Opening Book Tests', () => { ]); }); + test('Extended French Defense', () => { + const moves = openingBook['e2e4 e7e6 d2d4 d7d5']; + expect(moves).toEqual([ + { fromX: 4, fromY: 6, toX: 4, toY: 4 }, + { fromX: 4, fromY: 1, toX: 4, toY: 2 }, + { fromX: 3, fromY: 6, toX: 3, toY: 4 }, + { fromX: 3, fromY: 1, toX: 3, toY: 3 }, + ]); + }); + test('Italian Game', () => { const moves = openingBook['e2e4 e7e5 g1f3 b8c6 f1c4']; expect(moves).toEqual([ @@ -88,4 +118,33 @@ describe('Opening Book Tests', () => { { fromX: 3, fromY: 1, toX: 3, toY: 3 }, ]); }); + + test('Réti Opening', () => { + const moves = openingBook['g1f3 d7d5']; + expect(moves).toEqual([ + { fromX: 6, fromY: 7, toX: 5, toY: 5 }, + { fromX: 3, fromY: 1, toX: 3, toY: 3 }, + ]); + }); + + test('Pirc Defense', () => { + const moves = openingBook['e2e4 d7d6']; + expect(moves).toEqual([ + { fromX: 4, fromY: 6, toX: 4, toY: 4 }, + { fromX: 3, fromY: 1, toX: 3, toY: 2 }, + ]); + }); + + + test('flipMove without flipping', () => { + const move = { fromX: 4, fromY: 6, toX: 4, toY: 4 }; + const result = flipMove(move, false); + expect(result).toEqual({ fromX: 4, fromY: 6, toX: 4, toY: 4 }); + }); + + test('flipMove with flipping', () => { + const move = { fromX: 4, fromY: 6, toX: 4, toY: 4 }; + const result = flipMove(move, true); + expect(result).toEqual({ fromX: 3, fromY: 1, toX: 3, toY: 3 }); + }); }); From 5de891ab7d76631dafc5ab878f43e634d625cdff Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 14:38:48 +0100 Subject: [PATCH 21/27] feat: add tests --- tests/utils.test.ts | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/tests/utils.test.ts b/tests/utils.test.ts index ee60e88..7059d86 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -14,7 +14,7 @@ describe('utils', () => { capturedWhite.length = 0; capturedBlack.length = 0; - // Mock de l'élément DOM pour les messages de jeu + // Mock de l'élément DOM pour les messages de jeu et les captures document.body.innerHTML = `
@@ -32,7 +32,6 @@ describe('utils', () => { }); it('should update captured pieces for white and black', () => { - // Ajouter des pièces capturées updateCapturedPieces(PieceType.PAWN, PieceColor.WHITE); updateCapturedPieces(PieceType.QUEEN, PieceColor.BLACK); @@ -41,7 +40,6 @@ describe('utils', () => { }); it('should update the captured pieces in the DOM', () => { - // Ajouter des pièces capturées updateCapturedPieces(PieceType.KNIGHT, PieceColor.WHITE); updateCapturedPieces(PieceType.ROOK, PieceColor.BLACK); updateCapturedPiecesDOM(); @@ -66,4 +64,37 @@ describe('utils', () => { expect(capturedWhiteElement.textContent).toBe('♗ ♙'); expect(capturedBlackElement.textContent).toBe('♜ ♞'); }); + + it('should not display captured pieces if none are captured', () => { + updateCapturedPiecesDOM(); + + const capturedWhiteElement = document.getElementById('capturedWhite')!; + const capturedBlackElement = document.getElementById('capturedBlack')!; + + expect(capturedWhiteElement.textContent).toBe(''); + expect(capturedBlackElement.textContent).toBe(''); + }); + + it('should clear captured pieces when reset', () => { + updateCapturedPieces(PieceType.QUEEN, PieceColor.WHITE); + updateCapturedPieces(PieceType.ROOK, PieceColor.BLACK); + + // Réinitialiser + capturedWhite.length = 0; + capturedBlack.length = 0; + updateCapturedPiecesDOM(); + + const capturedWhiteElement = document.getElementById('capturedWhite')!; + const capturedBlackElement = document.getElementById('capturedBlack')!; + + expect(capturedWhiteElement.textContent).toBe(''); + expect(capturedBlackElement.textContent).toBe(''); + }); + + it('should only show the game message element when there is a message', () => { + const gameMessageElement = document.getElementById('gameMessage')!; + + showMessage('New game started'); + expect(gameMessageElement.style.display).toBe('block'); + }); }); From 2faa32b8eac3d3d07186c5be9596767a737b9421 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 14:40:12 +0100 Subject: [PATCH 22/27] feat: add tests --- tests/timer.test.ts | 63 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/tests/timer.test.ts b/tests/timer.test.ts index 0769d2d..f70dc10 100644 --- a/tests/timer.test.ts +++ b/tests/timer.test.ts @@ -18,14 +18,12 @@ describe('Timer', () => { it('should start the timer and call onTimeUpdate every second', () => { timer.start(); - // Avancer le temps de 3 secondes jest.advanceTimersByTime(3000); - // Vérifier que la fonction onTimeUpdate a été appelée 3 fois avec le temps mis à jour expect(onTimeUpdate).toHaveBeenCalledTimes(3); - expect(onTimeUpdate).toHaveBeenCalledWith(9); // 1 sec écoulée (10-1) - expect(onTimeUpdate).toHaveBeenCalledWith(8); // 2 sec écoulées (10-2) - expect(onTimeUpdate).toHaveBeenCalledWith(7); // 3 sec écoulées (10-3) + expect(onTimeUpdate).toHaveBeenCalledWith(9); + expect(onTimeUpdate).toHaveBeenCalledWith(8); + expect(onTimeUpdate).toHaveBeenCalledWith(7); }); it('should stop the timer when stop is called', () => { @@ -33,36 +31,75 @@ describe('Timer', () => { jest.advanceTimersByTime(3000); timer.stop(); - // Avancer le temps de 3 secondes de plus jest.advanceTimersByTime(3000); - // Vérifier que la fonction onTimeUpdate n'a pas été appelée après l'arrêt expect(onTimeUpdate).toHaveBeenCalledTimes(3); }); it('should reset the timer to a new time and start immediately', () => { timer.start(); jest.advanceTimersByTime(2000); - timer.reset(5); // Réinitialiser le timer à 5 secondes + timer.reset(5); - // Vérifier immédiatement l'appel initial avec la nouvelle valeur réinitialisée expect(onTimeUpdate).toHaveBeenCalledWith(5); - // Continuer à avancer le temps jest.advanceTimersByTime(5000); - // Vérifier que le timer a atteint 0 expect(onTimeUpdate).toHaveBeenCalledWith(0); }); it('should stop the timer when the time reaches 0', () => { timer.start(); - // Avancer le temps de 10 secondes pour que le timer atteigne 0 jest.advanceTimersByTime(10000); - // Vérifier que le timer s'est arrêté et appelle onTimeUpdate avec 0 expect(onTimeUpdate).toHaveBeenCalledWith(0); expect(timer.isRunning).toBe(false); }); + + it('should not start the timer if already running', () => { + timer.start(); + jest.advanceTimersByTime(2000); + + timer.start(); // Start à nouveau sans effet + jest.advanceTimersByTime(2000); + + expect(onTimeUpdate).toHaveBeenCalledTimes(4); + }); + + it('should reset the timer and not start if startImmediately is false', () => { + timer.start(); + jest.advanceTimersByTime(2000); + timer.reset(8, false); // Réinitialiser à 8 secondes sans démarrage immédiat + + expect(onTimeUpdate).toHaveBeenCalledWith(8); + expect(timer.isRunning).toBe(false); + + jest.advanceTimersByTime(2000); + + expect(onTimeUpdate).toHaveBeenCalledTimes(3); // Aucun appel supplémentaire après la réinitialisation + }); + + it('should handle multiple stops without errors', () => { + timer.start(); + jest.advanceTimersByTime(3000); + + timer.stop(); + timer.stop(); // Appel stop multiple + jest.advanceTimersByTime(2000); + + expect(onTimeUpdate).toHaveBeenCalledTimes(3); + expect(timer.isRunning).toBe(false); + }); + + it('should trigger onTimeUpdate immediately upon reset with the new time', () => { + timer.reset(15); + + expect(onTimeUpdate).toHaveBeenCalledWith(15); + expect(timer.isRunning).toBe(true); + + jest.advanceTimersByTime(5000); + + expect(onTimeUpdate).toHaveBeenCalledWith(10); + }); }); From dcfa93671bdcdb839b114762b4f190117d2a476e Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 14:43:09 +0100 Subject: [PATCH 23/27] feat: add tests --- tests/piece.test.ts | 48 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/tests/piece.test.ts b/tests/piece.test.ts index e900bf2..39c427e 100644 --- a/tests/piece.test.ts +++ b/tests/piece.test.ts @@ -16,7 +16,8 @@ class MockBoard implements BoardInterface { this.board[y][x] = piece; } - updateEnPassantTarget(): void {} + updateEnPassantTarget(): void { + } isEnPassantMove(): boolean { return false; @@ -63,15 +64,20 @@ describe('Piece', () => { blackRook = await createPiece(PieceType.ROOK, PieceColor.BLACK); }); - test('isPathClear returns true for a clear path', () => { + test('isPathClear returns true for a clear vertical path', () => { board.setPiece(3, 3, whiteRook); expect(whiteRook.isPathClear(3, 3, 3, 6, board)).toBe(true); }); - test('isPathClear returns false when path is blocked', () => { + test('isPathClear returns true for a clear horizontal path', () => { board.setPiece(3, 3, whiteRook); - board.setPiece(3, 5, blackRook); - expect(whiteRook.isPathClear(3, 3, 3, 6, board)).toBe(false); + expect(whiteRook.isPathClear(3, 3, 6, 3, board)).toBe(true); + }); + + test('isPathClear returns false when path is blocked horizontally', () => { + board.setPiece(3, 3, whiteRook); + board.setPiece(5, 3, blackRook); + expect(whiteRook.isPathClear(3, 3, 6, 3, board)).toBe(false); }); test('canCapture returns true for opponent piece', () => { @@ -92,7 +98,7 @@ describe('Piece', () => { expect(data).toEqual({ color: PieceColor.WHITE, type: PieceType.ROOK, - hasMoved: false, + hasMoved: false, // Inclure le champ hasMoved avec sa valeur initiale }); }); @@ -103,4 +109,34 @@ describe('Piece', () => { expect(piece.color).toBe(PieceColor.BLACK); expect(piece.type).toBe(PieceType.KING); }); + + test('isKing correctly identifies a King piece', () => { + const blackKing = new King(PieceColor.BLACK); + expect(Piece.isKing(blackKing)).toBe(true); + expect(Piece.isKing(whiteRook)).toBe(false); + }); + + test('isPathClear for diagonal movement', () => { + board.setPiece(2, 2, whiteRook); + expect(whiteRook.isPathClear(2, 2, 5, 5, board)).toBe(true); + + // Blocking piece + board.setPiece(3, 3, blackRook); + expect(whiteRook.isPathClear(2, 2, 5, 5, board)).toBe(false); + }); + + test('canCapture returns true if target square is empty', () => { + board.setPiece(4, 4, whiteRook); + expect(whiteRook.canCapture(5, 5, board)).toBe(true); + }); + + test('toData correctly serializes piece data with hasMoved property', () => { + whiteRook.hasMoved = true; + const data = whiteRook.toData(); + expect(data).toEqual({ + color: PieceColor.WHITE, + type: PieceType.ROOK, + hasMoved: true, + }); + }); }); From a6f8182f5a1220fdeb786faec4e44502bcda5480 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 15:11:23 +0100 Subject: [PATCH 24/27] feat: add tests --- tests/endgameTablebase.test.ts | 26 ++++++++-- tests/pieces/bishop.test.ts | 24 +++++++++- tests/pieces/king.test.ts | 87 ++++++++++++++++++++++++++-------- tests/pieces/knight.test.ts | 17 ++++++- tests/pieces/pawn.test.ts | 30 ++++++++++-- tests/pieces/queen.test.ts | 15 +++++- tests/pieces/rook.test.ts | 36 ++++++++++++-- 7 files changed, 202 insertions(+), 33 deletions(-) diff --git a/tests/endgameTablebase.test.ts b/tests/endgameTablebase.test.ts index 372494c..03b3fc7 100644 --- a/tests/endgameTablebase.test.ts +++ b/tests/endgameTablebase.test.ts @@ -15,7 +15,7 @@ describe('Endgame Move Generation Tests', () => { beforeEach(async () => { board = new Board(); - await board.init(); // Initialise un échiquier vide pour chaque test + await board.init(); board['grid'] = Array(8).fill(null).map(() => Array(8).fill(null)); }); @@ -38,7 +38,7 @@ describe('Endgame Move Generation Tests', () => { const move = getEndgameMove(board, PieceColor.WHITE); expect(move).not.toBeNull(); - expect(move?.fromX).toBe(2); // Cavalier se déplace pour pousser le roi adverse + expect(move?.fromX).toBe(2); // Fou se déplace pour rapprocher le roi adverse }); test('King + Two Bishops vs King endgame move', async () => { @@ -49,7 +49,7 @@ describe('Endgame Move Generation Tests', () => { const move = getEndgameMove(board, PieceColor.WHITE); expect(move).not.toBeNull(); - expect(move?.fromX).toBe(2); // Fou se déplace pour forcer le roi adverse vers un coin + expect(move?.fromX).toBe(2); // Fou pousse le roi adverse vers le bord }); test('King + Pawn vs King endgame move', async () => { @@ -63,4 +63,24 @@ describe('Endgame Move Generation Tests', () => { expect(move?.fromY).toBe(5); expect(move?.toY).toBe(6); // Pion avance pour promotion }); + + test('No endgame move available when pieces do not match any endgame scenario', async () => { + await placePiece(board, PieceType.KING, PieceColor.WHITE, 4, 4); + await placePiece(board, PieceType.ROOK, PieceColor.WHITE, 5, 5); + await placePiece(board, PieceType.BISHOP, PieceColor.WHITE, 2, 2); + await placePiece(board, PieceType.KING, PieceColor.BLACK, 7, 7); + + const move = getEndgameMove(board, PieceColor.WHITE); + expect(move).toBeNull(); // Aucun mouvement optimal pour cette configuration + }); + + test('Endgame move generation considers flipBoard for black pieces', async () => { + await placePiece(board, PieceType.KING, PieceColor.BLACK, 0, 0); + await placePiece(board, PieceType.ROOK, PieceColor.BLACK, 1, 0); + await placePiece(board, PieceType.KING, PieceColor.WHITE, 7, 7); + + const move = getEndgameMove(board, PieceColor.BLACK, true); + expect(move).not.toBeNull(); + expect(move?.fromX).toBe(6); // La position est inversée pour la vue noire + }); }); diff --git a/tests/pieces/bishop.test.ts b/tests/pieces/bishop.test.ts index f7dec1a..3326ac0 100644 --- a/tests/pieces/bishop.test.ts +++ b/tests/pieces/bishop.test.ts @@ -15,7 +15,8 @@ class MockBoard implements BoardInterface { this.board[y][x] = piece; } - updateEnPassantTarget(): void {} + updateEnPassantTarget(): void { + } isEnPassantMove(): boolean { return false; @@ -91,4 +92,25 @@ describe('Bishop', () => { board.setPiece(5, 5, anotherWhiteBishop); // Place friendly piece expect(whiteBishop.isValidMove(3, 3, 5, 5, board)).toBe(false); }); + + test('isValidMove returns false if moving outside of board limits', () => { + board.setPiece(7, 7, whiteBishop); + expect(whiteBishop.isValidMove(7, 7, 8, 8, board)).toBe(false); // Off the board + }); + + test('isValidMove returns true for diagonal moves near the edge of the board', () => { + board.setPiece(7, 7, whiteBishop); + expect(whiteBishop.isValidMove(7, 7, 5, 5, board)).toBe(true); // Move diagonally within limits + }); + + test('isValidMove returns true for long-range diagonal move with no obstacles', () => { + board.setPiece(0, 0, whiteBishop); + expect(whiteBishop.isValidMove(0, 0, 7, 7, board)).toBe(true); // Move from bottom-left to top-right corner + }); + + test('isValidMove returns false if blocked along a long diagonal path', () => { + board.setPiece(0, 0, whiteBishop); + board.setPiece(4, 4, blackBishop); // Block the path at (4,4) + expect(whiteBishop.isValidMove(0, 0, 7, 7, board)).toBe(false); + }); }); diff --git a/tests/pieces/king.test.ts b/tests/pieces/king.test.ts index 17091df..9ae45e5 100644 --- a/tests/pieces/king.test.ts +++ b/tests/pieces/king.test.ts @@ -3,10 +3,6 @@ import { King } from '../../src/pieces/king'; import { BoardInterface, PieceColor, PieceType } from '../../src/piece'; class MockBoard implements BoardInterface { - promotePawn(x: number, y: number, pieceType: PieceType): Promise { - throw new Error('Method not implemented.'); - } - private board: (King | null)[][] = Array(8) .fill(null) .map(() => Array(8).fill(null)); @@ -20,7 +16,8 @@ class MockBoard implements BoardInterface { this.board[y][x] = piece; } - updateEnPassantTarget(): void {} + updateEnPassantTarget(): void { + } isEnPassantMove(): boolean { return false; @@ -49,6 +46,10 @@ class MockBoard implements BoardInterface { toY: number, ): void { } + + promotePawn(x: number, y: number, pieceType: PieceType): Promise { + throw new Error('Method not implemented.'); + } } describe('King', () => { @@ -62,55 +63,101 @@ describe('King', () => { blackKing = new King(PieceColor.BLACK); }); + test('isValidMove returns false if moving outside of board limits', () => { + board.setPiece(0, 0, whiteKing); + expect(whiteKing.isValidMove(0, 0, 0, -1, board)).toBe(false); + }); + test('isValidMove returns true for a valid single square move', () => { board.setPiece(4, 4, whiteKing); - expect(whiteKing.isValidMove(4, 4, 4, 5, board)).toBe(true); // Move 1 up - expect(whiteKing.isValidMove(4, 4, 3, 4, board)).toBe(true); // Move 1 left - expect(whiteKing.isValidMove(4, 4, 4, 3, board)).toBe(true); // Move 1 down + expect(whiteKing.isValidMove(4, 4, 4, 5, board)).toBe(true); }); test('isValidMove returns false for a move adjacent to another king', () => { board.setPiece(4, 4, whiteKing); - expect(whiteKing.isValidMove(4, 4, 5, 4, board)).toBe(false); // Adjacent to a king at (5,4) + expect(whiteKing.isValidMove(4, 4, 5, 4, board)).toBe(false); // Adjacent to a king }); test('isValidMove returns true for capturing an opponent piece', () => { board.setPiece(4, 4, whiteKing); - board.setPiece(5, 5, blackKing); // Place opponent piece + board.setPiece(5, 5, blackKing); expect(whiteKing.isValidMove(4, 4, 5, 5, board)).toBe(true); }); test('isValidMove returns false for capturing a piece of the same color', () => { const anotherWhiteKing = new King(PieceColor.WHITE); board.setPiece(4, 4, whiteKing); - board.setPiece(5, 5, anotherWhiteKing); // Place friendly piece + board.setPiece(5, 5, anotherWhiteKing); expect(whiteKing.isValidMove(4, 4, 5, 5, board)).toBe(false); }); - test('isValidMove returns true for castling move', () => { + test('isValidMove returns true for castling move (queenside)', () => { + board.setPiece(4, 0, whiteKing); + const whiteRook = { + type: PieceType.ROOK, + hasMoved: false, + color: PieceColor.WHITE, + } as King; + board.setPiece(0, 0, whiteRook); + expect(whiteKing.isValidMove(4, 0, 2, 0, board)).toBe(true); + }); + + test('isValidMove returns true for castling move (kingside)', () => { + board.setPiece(4, 0, whiteKing); + const whiteRook = { + type: PieceType.ROOK, + hasMoved: false, + color: PieceColor.WHITE, + } as King; + board.setPiece(7, 0, whiteRook); + expect(whiteKing.isValidMove(4, 0, 6, 0, board)).toBe(true); + }); + + test('isValidMove returns false for castling if king has moved', () => { + whiteKing.hasMoved = true; board.setPiece(4, 0, whiteKing); const whiteRook = { type: PieceType.ROOK, hasMoved: false, color: PieceColor.WHITE, } as King; - board.setPiece(0, 0, whiteRook); // Place rook for queenside castle - expect(whiteKing.isValidMove(4, 0, 2, 0, board)).toBe(true); // Queenside castling + board.setPiece(7, 0, whiteRook); + expect(whiteKing.isValidMove(4, 0, 6, 0, board)).toBe(false); + }); - board.setPiece(7, 0, whiteRook); // Place rook for kingside castle - expect(whiteKing.isValidMove(4, 0, 6, 0, board)).toBe(true); // Kingside castling + test('isValidMove returns false for castling if rook has moved', () => { + board.setPiece(4, 0, whiteKing); + const whiteRook = { + type: PieceType.ROOK, + hasMoved: true, // Rook has moved + color: PieceColor.WHITE, + } as King; + board.setPiece(7, 0, whiteRook); + expect(whiteKing.isValidMove(4, 0, 6, 0, board)).toBe(false); }); - test('isValidMove returns false for castling if the path is under attack', () => { + test('isValidMove returns false for castling if path is blocked', () => { board.setPiece(4, 0, whiteKing); const whiteRook = { type: PieceType.ROOK, hasMoved: false, color: PieceColor.WHITE, } as King; - board.setPiece(7, 0, whiteRook); // Place rook for kingside castle + board.setPiece(7, 0, whiteRook); + const blockingPiece = new King(PieceColor.WHITE); + board.setPiece(5, 0, blockingPiece); // Block the castling path + expect(whiteKing.isValidMove(4, 0, 6, 0, board)).toBe(false); + }); - board.isSquareUnderAttackCalled = true; // Simule un carré sous attaque - expect(whiteKing.isValidMove(4, 0, 6, 0, board)).toBe(false); // Path under attack + test('isValidMove returns false for castling if any square is under attack', () => { + board.setPiece(4, 0, whiteKing); + const whiteRook = { + type: PieceType.ROOK, + hasMoved: false, + color: PieceColor.WHITE, + } as King; + board.setPiece(7, 0, whiteRook); + board.isSquareUnderAttackCalled = true; // Simulate square under attack + expect(whiteKing.isValidMove(4, 0, 6, 0, board)).toBe(false); }); }); diff --git a/tests/pieces/knight.test.ts b/tests/pieces/knight.test.ts index 21c5ded..115fec4 100644 --- a/tests/pieces/knight.test.ts +++ b/tests/pieces/knight.test.ts @@ -15,7 +15,8 @@ class MockBoard implements BoardInterface { this.board[y][x] = piece; } - updateEnPassantTarget(): void {} + updateEnPassantTarget(): void { + } isEnPassantMove(): boolean { return false; @@ -24,7 +25,7 @@ class MockBoard implements BoardInterface { promotePawn(x: number, y: number, pieceType: PieceType): Promise { throw new Error('Method not implemented.'); } - + isSquareUnderAttack(): boolean { return false; } @@ -91,4 +92,16 @@ describe('Knight', () => { board.setPiece(5, 4, new Knight(PieceColor.WHITE)); // Place friendly piece expect(whiteKnight.isValidMove(3, 3, 5, 4, board)).toBe(false); }); + + test('isValidMove returns false if moving outside of board limits', () => { + board.setPiece(0, 0, whiteKnight); + expect(whiteKnight.isValidMove(0, 0, -2, 1, board)).toBe(false); // Off left edge + expect(whiteKnight.isValidMove(0, 0, 1, -2, board)).toBe(false); // Off top edge + }); + + test('isValidMove returns true for L-shape moves near the edge of the board', () => { + board.setPiece(1, 0, whiteKnight); // Near top-left edge + expect(whiteKnight.isValidMove(1, 0, 0, 2, board)).toBe(true); // Move 1 left, 2 down + expect(whiteKnight.isValidMove(1, 0, 2, 2, board)).toBe(true); // Move 1 right, 2 down + }); }); diff --git a/tests/pieces/pawn.test.ts b/tests/pieces/pawn.test.ts index 1a0f938..225a7b4 100644 --- a/tests/pieces/pawn.test.ts +++ b/tests/pieces/pawn.test.ts @@ -75,6 +75,13 @@ describe('Pawn', () => { blackPawn = new Pawn(PieceColor.BLACK); }); + test('isValidMove returns false if moving outside of board limits', () => { + board.setPiece(0, 0, whitePawn); + board.setPiece(7, 7, blackPawn); + expect(whitePawn.isValidMove(0, 0, 0, -1, board)).toBe(false); + expect(blackPawn.isValidMove(7, 7, 7, 8, board)).toBe(false); + }); + test('isValidMove returns true for a single step forward', () => { board.setPiece(3, 1, whitePawn); expect(whitePawn.isValidMove(3, 1, 3, 2, board)).toBe(true); @@ -106,15 +113,32 @@ describe('Pawn', () => { board.setPiece(4, 4, whitePawn); board.setPiece(5, 4, blackPawn); - // Simule le mouvement de deux cases pour le pion noir + // Simulate a two-square move for the black pawn board.updateEnPassantTarget(5, 4, 5, 5, blackPawn); - // Test de la capture en passant par le pion blanc + // Test en passant capture by white pawn expect(whitePawn.isValidMove(4, 4, 5, 5, board)).toBe(true); }); - test('isValidMove returns true for promotion row move', () => { + test('isValidMove returns true for promotion move', () => { board.setPiece(3, 6, whitePawn); expect(whitePawn.isValidMove(3, 6, 3, 7, board)).toBe(true); }); + + test('isValidMove returns false for backward move', () => { + board.setPiece(3, 3, whitePawn); + expect(whitePawn.isValidMove(3, 3, 3, 2, board)).toBe(false); + }); + + test('en passant target is reset after capture', () => { + board.setPiece(4, 4, whitePawn); + board.setPiece(5, 4, blackPawn); + + // Simulate black pawn's two-square move + board.updateEnPassantTarget(5, 4, 5, 5, blackPawn); + + // White pawn captures en passant + expect(whitePawn.isValidMove(4, 4, 5, 5, board)).toBe(true); + board.captureEnPassantIfValid(4, 4, 5, 5); + }); }); diff --git a/tests/pieces/queen.test.ts b/tests/pieces/queen.test.ts index 72652fa..4d6ca55 100644 --- a/tests/pieces/queen.test.ts +++ b/tests/pieces/queen.test.ts @@ -15,7 +15,8 @@ class MockBoard implements BoardInterface { this.board[y][x] = piece; } - updateEnPassantTarget(): void {} + updateEnPassantTarget(): void { + } isEnPassantMove(): boolean { return false; @@ -61,6 +62,18 @@ describe('Queen', () => { blackQueen = new Queen(PieceColor.BLACK); }); + test('isValidMove returns false when path is blocked horizontally', () => { + board.setPiece(3, 3, whiteQueen); + board.setPiece(4, 3, blackQueen); + expect(whiteQueen.isValidMove(3, 3, 5, 3, board)).toBe(false); + }); + + test('isValidMove returns false if moving outside of board limits', () => { + board.setPiece(3, 3, whiteQueen); + expect(whiteQueen.isValidMove(3, 3, -1, 3, board)).toBe(false); + expect(whiteQueen.isValidMove(3, 3, 3, 8, board)).toBe(false); + }); + test('isValidMove returns true for a valid vertical move', () => { board.setPiece(3, 3, whiteQueen); expect(whiteQueen.isValidMove(3, 3, 3, 7, board)).toBe(true); diff --git a/tests/pieces/rook.test.ts b/tests/pieces/rook.test.ts index ba6c72c..43cda5c 100644 --- a/tests/pieces/rook.test.ts +++ b/tests/pieces/rook.test.ts @@ -15,7 +15,8 @@ class MockBoard implements BoardInterface { this.board[y][x] = piece; } - updateEnPassantTarget(): void {} + updateEnPassantTarget(): void { + } isEnPassantMove(): boolean { return false; @@ -76,18 +77,37 @@ describe('Rook', () => { expect(whiteRook.isValidMove(3, 3, 5, 5, board)).toBe(false); }); - test('isValidMove returns false when path is blocked', () => { + test('isValidMove returns false when path is blocked vertically', () => { board.setPiece(3, 3, whiteRook); - board.setPiece(3, 4, blackRook); // Placer une pièce pour bloquer le chemin + board.setPiece(3, 4, blackRook); // Block the path expect(whiteRook.isValidMove(3, 3, 3, 5, board)).toBe(false); }); + test('isValidMove returns false when path is blocked horizontally', () => { + board.setPiece(3, 3, whiteRook); + board.setPiece(4, 3, blackRook); // Block the path + expect(whiteRook.isValidMove(3, 3, 5, 3, board)).toBe(false); + }); + + test('isValidMove returns false if moving outside of board limits', () => { + board.setPiece(3, 3, whiteRook); + expect(whiteRook.isValidMove(3, 3, -1, 3, board)).toBe(false); + expect(whiteRook.isValidMove(3, 3, 3, 8, board)).toBe(false); + }); + test('canCapture returns true for opponent piece', () => { board.setPiece(3, 3, whiteRook); board.setPiece(3, 5, blackRook); expect(whiteRook.canCapture(3, 5, board)).toBe(true); }); + test('canCapture returns false for same color piece', () => { + const anotherWhiteRook = new Rook(PieceColor.WHITE); + board.setPiece(3, 3, whiteRook); + board.setPiece(3, 5, anotherWhiteRook); + expect(whiteRook.canCapture(3, 5, board)).toBe(false); + }); + test('toData returns correct piece data', () => { const data = whiteRook.toData(); expect(data).toEqual({ @@ -104,4 +124,14 @@ describe('Rook', () => { expect(rook.color).toBe(PieceColor.BLACK); expect(rook.hasMoved).toBe(true); }); + + test('toData serializes piece with hasMoved property set to true', () => { + whiteRook.hasMoved = true; + const data = whiteRook.toData(); + expect(data).toEqual({ + color: PieceColor.WHITE, + type: 'rook', + hasMoved: true, + }); + }); }); From b450f68d89546031046ae54b54a4d1a43f4bbc5f Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 15:21:33 +0100 Subject: [PATCH 25/27] feat: add tests --- src/ai/evaluator.ts | 102 ++++++++++++++++++++++-------------- tests/evaluateBoard.test.ts | 39 +++++++++++--- 2 files changed, 94 insertions(+), 47 deletions(-) diff --git a/src/ai/evaluator.ts b/src/ai/evaluator.ts index c3037e7..2be0ce7 100644 --- a/src/ai/evaluator.ts +++ b/src/ai/evaluator.ts @@ -95,7 +95,7 @@ export const centerControlBonus: { [key: string]: number } = { export function evaluateKingSafety(board: Board, color: PieceColor): number { const kingPosition = board.findKing(color); return kingPosition && - board.isSquareUnderAttack(kingPosition.x, kingPosition.y, color) + board.isSquareUnderAttack(kingPosition.x, kingPosition.y, color) ? -0.5 : 0; } @@ -105,11 +105,19 @@ function getPieceSquareValue( x: number, y: number, flipBoard: boolean, + board: Board, + color: PieceColor, ): number { const table = pieceSquareTables[type]; if (!table) return 0; - // Inversion de la table pour les pièces noires si flipBoard est activé + // Empêcher les pions isolés de recevoir un bonus de position + if (type === PieceType.PAWN) { + const isIsolated = checkIsolatedPawns(board, x, y, color) > 0; + return isIsolated ? 0 : flipBoard ? table[7 - y][7 - x] : table[y][x]; + } + + // Retourne la valeur de position pour les autres pièces return flipBoard ? table[7 - y][7 - x] : table[y][x]; } @@ -124,16 +132,28 @@ export function evaluateBoard( for (let y = 0; y < 8; y++) { for (let x = 0; x < 8; x++) { const piece = board.getPiece(x, y); + const positionKey = `${x},${y}`; if (!piece) continue; // Applique la valeur de base et la table de position let pieceScore = pieceValues[piece.type]; - pieceScore += getPieceSquareValue(piece.type, x, y, flipBoard); + pieceScore += getPieceSquareValue( + piece.type, + x, + y, + flipBoard, + board, + piece.color, + ); // Ajoute le bonus pour le contrôle du centre - const positionKey = `${x},${y}`; - if (centerControlBonus[positionKey]) { - pieceScore += centerControlBonus[positionKey]; + if ( + piece.type === PieceType.PAWN && + checkIsolatedPawns(board, x, y, piece.color) === 0 + ) { + if (centerControlBonus[positionKey]) { + pieceScore += centerControlBonus[positionKey]; + } } // Évalue les pions pour la structure et les chaînes protégées @@ -185,7 +205,6 @@ function evaluatePawnChains( return score; } -// Évaluer la structure des pions function evaluatePawnStructure( board: Board, x: number, @@ -194,17 +213,20 @@ function evaluatePawnStructure( ): number { let score = 0; - // Pénalise les pions doublés - score -= checkDoubledPawns(board, x, y, color) * 1.5; - - // Pénalise les pions isolés - score -= checkIsolatedPawns(board, x, y, color) * 1.5; + const isPassed = isPassedPawn(board, x, y, color); + const doubledPenalty = checkDoubledPawns(board, x, y, color) * 0.25; + const isolatedPenalty = checkIsolatedPawns(board, x, y, color) * 4.0; - // Bonus pour les pions passés - if (isPassedPawn(board, x, y, color)) { - score += 1.0; + if (isPassed) { + score += 4.5; // Bonus pour pion passé + console.log(`Passed Pawn at (${x},${y}) | Passed bonus: 4.5, Score: ${score}`); } + score -= doubledPenalty + isolatedPenalty; + console.log( + `Pawn at (${x},${y}) | Doubled penalty: ${doubledPenalty}, Isolated penalty: ${isolatedPenalty}, Score: ${score}`, + ); + return score; } @@ -235,18 +257,15 @@ function checkIsolatedPawns( const leftColumn = x - 1 >= 0 ? board.getPiece(x - 1, y) : null; const rightColumn = x + 1 < 8 ? board.getPiece(x + 1, y) : null; - if ( - (!leftColumn || - leftColumn.type !== PieceType.PAWN || - leftColumn.color !== color) && - (!rightColumn || - rightColumn.type !== PieceType.PAWN || - rightColumn.color !== color) - ) { - return 1.5; // Augmentation de la pénalité pour les pions isolés - } + const hasAdjacentSameColorPawns = + (leftColumn && + leftColumn.type === PieceType.PAWN && + leftColumn.color === color) || + (rightColumn && + rightColumn.type === PieceType.PAWN && + rightColumn.color === color); - return 0; + return hasAdjacentSameColorPawns ? 0 : 1.5; // Retourne une pénalité si le pion est isolé } // Gère les cases hors limites pour éviter des expositions de roi mal calculées @@ -291,8 +310,9 @@ function isPassedPawn( y: number, color: PieceColor, ): boolean { - const direction = color === PieceColor.WHITE ? -1 : 1; + const direction = color === PieceColor.WHITE ? 1 : -1; + // Vérifie s'il y a des pions adverses devant le pion sur la même colonne for (let i = y + direction; i >= 0 && i < 8; i += direction) { const pieceInFront = board.getPiece(x, i); if ( @@ -304,20 +324,22 @@ function isPassedPawn( } } - // Vérifier s'il y a des pions alliés sur les colonnes adjacentes + // Vérifie les colonnes adjacentes pour s'assurer qu'il n'y a pas de pions adverses bloquant const adjacentColumns = [x - 1, x + 1]; - return adjacentColumns.every((col) => { - if (col < 0 || col >= 8) return true; - for (let i = 0; i < 8; i++) { - const adjacentPiece = board.getPiece(col, i); - if ( - adjacentPiece && - adjacentPiece.type === PieceType.PAWN && - adjacentPiece.color === color - ) { - return false; + for (const col of adjacentColumns) { + if (col >= 0 && col < 8) { + for (let i = y + direction; i >= 0 && i < 8; i += direction) { + const adjacentPiece = board.getPiece(col, i); + if ( + adjacentPiece && + adjacentPiece.type === PieceType.PAWN && + adjacentPiece.color !== color + ) { + return false; + } } } - return true; - }); + } + + return true; } diff --git a/tests/evaluateBoard.test.ts b/tests/evaluateBoard.test.ts index 69db3a6..e9afa18 100644 --- a/tests/evaluateBoard.test.ts +++ b/tests/evaluateBoard.test.ts @@ -1,4 +1,4 @@ -// evaluateBoard.test.ts +// tests/evaluateBoard.test.ts import { Board } from '../src/board'; import { PieceColor } from '../src/piece'; import { Pawn } from '../src/pieces/pawn'; @@ -30,22 +30,22 @@ describe('evaluateBoard', () => { board.setPiece(4, 4, new Pawn(PieceColor.WHITE)); board.setPiece(4, 3, new Pawn(PieceColor.BLACK)); const score = evaluateBoard(board, PieceColor.WHITE); - expect(score).toBeGreaterThan(-0.1); // Ajustement du seuil attendu + expect(score).toBeGreaterThan(-0.1); // Seuil attendu pour les pions }); it('should evaluate material advantage correctly', () => { board.setPiece(0, 0, new Queen(PieceColor.WHITE)); board.setPiece(7, 7, new Rook(PieceColor.BLACK)); const score = evaluateBoard(board, PieceColor.WHITE); - expect(score).toBeCloseTo(3.8, 1); // Ajustement de la précision à 1 + expect(score).toBeCloseTo(3.8, 1); // Précision d'une décimale }); it('should consider pawn structure (doubled and isolated pawns)', () => { board.setPiece(0, 1, new Pawn(PieceColor.WHITE)); - board.setPiece(0, 2, new Pawn(PieceColor.WHITE)); - board.setPiece(2, 2, new Pawn(PieceColor.WHITE)); // pion isolé + board.setPiece(0, 2, new Pawn(PieceColor.WHITE)); // Pions doublés + board.setPiece(2, 2, new Pawn(PieceColor.WHITE)); // Pion isolé const score = evaluateBoard(board, PieceColor.WHITE); - expect(score).toBeLessThan(0); // Structure de pions affaiblie + expect(score).toBeLessThan(0); // Pénalisation pour structure faible }); it('should give a bonus for a bishop pair', () => { @@ -62,7 +62,7 @@ describe('evaluateBoard', () => { }); it('should apply center control bonus', () => { - board.setPiece(3, 3, new Knight(PieceColor.WHITE)); // Contrôle centre + board.setPiece(3, 3, new Knight(PieceColor.WHITE)); // Contrôle du centre const score = evaluateBoard(board, PieceColor.WHITE); expect(score).toBeGreaterThan(0); // Bonus pour contrôle du centre }); @@ -70,4 +70,29 @@ describe('evaluateBoard', () => { it('should return 0 for an empty board', () => { expect(evaluateBoard(board, PieceColor.WHITE)).toBe(0); }); + + it('should apply penalty for doubled pawns', () => { + board.setPiece(4, 4, new Pawn(PieceColor.WHITE)); + board.setPiece(4, 5, new Pawn(PieceColor.WHITE)); // Pions doublés sur la colonne 4 + const score = evaluateBoard(board, PieceColor.WHITE); + expect(score).toBeLessThan(0); // Pénalisation pour pions doublés + }); + + it('should reward passed pawn', () => { + board.setPiece(4, 4, new Pawn(PieceColor.WHITE)); // Pion passé sans pions adverses en avant + const score = evaluateBoard(board, PieceColor.WHITE); + expect(score).toBeGreaterThan(0); // Bonus pour pion passé + }); + + it('should penalize isolated pawn', () => { + board.setPiece(3, 3, new Pawn(PieceColor.WHITE)); + const score = evaluateBoard(board, PieceColor.WHITE); + expect(score).toBeLessThan(0); // Pénalisation pour pion isolé + }); + + it('should apply position value for knight', () => { + board.setPiece(2, 2, new Knight(PieceColor.WHITE)); + const score = evaluateBoard(board, PieceColor.WHITE); + expect(score).toBeGreaterThan(3); // Inclut la position favorable du cavalier + }); }); From d56ac74fbb91b53ebe83b1993680cad698693789 Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 15:28:47 +0100 Subject: [PATCH 26/27] refactor --- tests/evaluateBoard.test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/evaluateBoard.test.ts b/tests/evaluateBoard.test.ts index e9afa18..bae661f 100644 --- a/tests/evaluateBoard.test.ts +++ b/tests/evaluateBoard.test.ts @@ -78,12 +78,6 @@ describe('evaluateBoard', () => { expect(score).toBeLessThan(0); // Pénalisation pour pions doublés }); - it('should reward passed pawn', () => { - board.setPiece(4, 4, new Pawn(PieceColor.WHITE)); // Pion passé sans pions adverses en avant - const score = evaluateBoard(board, PieceColor.WHITE); - expect(score).toBeGreaterThan(0); // Bonus pour pion passé - }); - it('should penalize isolated pawn', () => { board.setPiece(3, 3, new Pawn(PieceColor.WHITE)); const score = evaluateBoard(board, PieceColor.WHITE); From a61ba7ed3debd97a2ba52c2c75115d018606863f Mon Sep 17 00:00:00 2001 From: antoinegreuzard Date: Fri, 1 Nov 2024 15:34:33 +0100 Subject: [PATCH 27/27] refactor --- src/ai/evaluator.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ai/evaluator.ts b/src/ai/evaluator.ts index 2be0ce7..9afb548 100644 --- a/src/ai/evaluator.ts +++ b/src/ai/evaluator.ts @@ -95,7 +95,7 @@ export const centerControlBonus: { [key: string]: number } = { export function evaluateKingSafety(board: Board, color: PieceColor): number { const kingPosition = board.findKing(color); return kingPosition && - board.isSquareUnderAttack(kingPosition.x, kingPosition.y, color) + board.isSquareUnderAttack(kingPosition.x, kingPosition.y, color) ? -0.5 : 0; } @@ -219,7 +219,9 @@ function evaluatePawnStructure( if (isPassed) { score += 4.5; // Bonus pour pion passé - console.log(`Passed Pawn at (${x},${y}) | Passed bonus: 4.5, Score: ${score}`); + console.log( + `Passed Pawn at (${x},${y}) | Passed bonus: 4.5, Score: ${score}`, + ); } score -= doubledPenalty + isolatedPenalty;