diff --git a/README.md b/README.md index fdb4969..964e9af 100644 --- a/README.md +++ b/README.md @@ -89,4 +89,6 @@ La position de toutes les pièces sont échangées aléatoirement. ### La parrure Une reine est dégradée en pion ### Tricherie -Regarde les trois prochaines cartes de la pioche \ No newline at end of file +Regarde les trois prochaines cartes de la pioche +### Tout ou rien +Une pièce choisie ne peut maintenant se déplacer que si elle capture \ No newline at end of file diff --git a/engine/core/movement/generateMoves.js b/engine/core/movement/generateMoves.js new file mode 100644 index 0000000..706f863 --- /dev/null +++ b/engine/core/movement/generateMoves.js @@ -0,0 +1,50 @@ +// Utilities for movement generation +// The movement modules receive a context object with at least: +// - board: instance exposing getWidth(), getHeight(), getCell(x,y) and setPiece(x,y,piece) +// - from: { x, y } +// - piece: the moving piece (with getColor()) + +/** + * Walk in linear directions (array of dx,dy) until blocked. + * Returns an array of move objects: { x, y, capture: boolean } + * + * Options: + * - maxSteps (default: Infinity) + * - allowCapture=true + * - allowEmpty=true + */ +export function generateLinearMoves({ board, from, piece, directions, maxSteps = Infinity, allowCapture = true, allowEmpty = true }) { + const moves = []; + const w = board.getWidth(); + const h = board.getHeight(); + const color = piece.getColor(); + + for (const [dx, dy] of directions) { + let steps = 0; + let x = from.x + dx; + let y = from.y + dy; + + while (x >= 0 && x < w && y >= 0 && y < h && steps < maxSteps) { + const cell = board.getCell(x, y); + const target = cell?.piece ?? null; + + if (target == null) { + if (allowEmpty) moves.push({ x, y, capture: false }); + // continue along this direction + } else { + // occupied + if (target.getColor() !== color) { + if (allowCapture) moves.push({ x, y, capture: true }); + } + // stop after encountering any piece + break; + } + + steps++; + x += dx; + y += dy; + } + } + + return moves; +} diff --git a/engine/core/movement/index.js b/engine/core/movement/index.js new file mode 100644 index 0000000..fa17b81 --- /dev/null +++ b/engine/core/movement/index.js @@ -0,0 +1,13 @@ +// Central export for movement modules +import {RookMove, BishopMove, KnightMove, QueenMove, KingMove, PawnMove} from './moves.js'; +import { generateLinearMoves } from './generateMoves.js'; + +export default { + RookMove, + BishopMove, + KnightMove, + QueenMove, + KingMove, + PawnMove, + generateLinearMoves, +}; \ No newline at end of file diff --git a/engine/core/movement/moves.js b/engine/core/movement/moves.js new file mode 100644 index 0000000..26843f6 --- /dev/null +++ b/engine/core/movement/moves.js @@ -0,0 +1,172 @@ +import { generateLinearMoves } from './generateMoves.js'; + +class RookMove { + /** + * Return possible moves for a rook-like piece from `from` on the given `board`. + * The move format is { x, y, capture: boolean } + * + * ctx: { board, from: {x,y}, piece } + */ + static moves(ctx) { + const orthogonals = [ + [1, 0], + [-1, 0], + [0, 1], + [0, -1], + ]; + + return generateLinearMoves({ + board: ctx.board, + from: ctx.from, + piece: ctx.piece, + directions: orthogonals, + }); + } +} + +class BishopMove { + /** + * Return possible moves for a bishop-like piece from `from` on the given `board`. + * The move format is { x, y, capture: boolean } + * + * ctx: { board, from: {x,y}, piece } + */ + static moves(ctx) { + const diagonals = [ + [1, 1], + [1, -1], + [-1, 1], + [-1, -1], + ]; + + return generateLinearMoves({ + board: ctx.board, + from: ctx.from, + piece: ctx.piece, + directions: diagonals, + }); + } +} + +class KnightMove { + /** + * Return possible moves for a knight-like piece from `from` on the given `board`. + * The move format is { x, y, capture: boolean } + * + * ctx: { board, from: {x,y}, piece } + */ + static moves(ctx) { + const knightMoves = [ + [2, 1], [2, -1], [-2, 1], [-2, -1], + [1, 2], [1, -2], [-1, 2], [-1, -2], + ]; + + return generateLinearMoves({ + board: ctx.board, + from: ctx.from, + piece: ctx.piece, + directions: knightMoves, + maxSteps: 1, + allowEmpty: true, + allowCapture: true, + }); + + } +} + +class QueenMove { + /** + * Return possible moves for a queen-like piece from `from` on the given `board`. + * The move format is { x, y, capture: boolean } + * + * ctx: { board, from: {x,y}, piece } + */ + static moves(ctx) { + const directions = [ + [1, 0], [-1, 0], [0, 1], [0, -1], // orthogonals + [1, 1], [1, -1], [-1, 1], [-1, -1], // diagonals + ]; + + return generateLinearMoves({ + board: ctx.board, + from: ctx.from, + piece: ctx.piece, + directions: directions, + }); + } +} + +class KingMove { + /** + * Return possible moves for a king-like piece from `from` on the given `board`. + * The move format is { x, y, capture: boolean } + * + * ctx: { board, from: {x,y}, piece } + */ + static moves(ctx) { + const directions = [ + [1, 0], [-1, 0], [0, 1], [0, -1], // orthogonals + [1, 1], [1, -1], [-1, 1], [-1, -1], // diagonals + ]; + + return generateLinearMoves({ + board: ctx.board, + from: ctx.from, + piece: ctx.piece, + directions: directions, + maxSteps: 1, + }); + } +} + +class PawnMove { + /** + * Return possible moves for a pawn-like piece from `from` on the given `board`. + * The move format is { x, y, capture: boolean } + * + * ctx: { board, from: {x,y}, piece } + */ + static moves(ctx) { + const color = ctx.piece.getColor(); + const forward = (color === 'white') ? -1 : 1; // assuming y=0 is top + + const moves = []; + const w = ctx.board.getWidth(); + const h = ctx.board.getHeight(); + const x = ctx.from.x; + const y = ctx.from.y; + + // Forward move + if (y + forward >= 0 && y + forward < h) { + const forwardCell = ctx.board.getCell(x, y + forward); + if (forwardCell?.piece == null) { + moves.push({ x: x, y: y + forward, capture: false }); + + // Double step from starting position + const startingRow = (color === 'white') ? h - 2 : 1; + if (!ctx.piece.hasMoved) { + const doubleForwardCell = ctx.board.getCell(x, y + 2 * forward); + if (doubleForwardCell?.piece == null) { + moves.push({ x: x, y: y + 2 * forward, capture: false }); + } + } + + } + } + + // Captures + for (const dx of [-1, 1]) { + if (x + dx >= 0 && x + dx < w && y + forward >= 0 && y + forward < h) { + const diagCell = ctx.board.getCell(x + dx, y + forward); + const target = diagCell?.piece ?? null; + if (target != null && target.getColor() !== color) { + moves.push({ x: x + dx, y: y + forward, capture: true }); + } + } + } + + return moves; + } +} + +export { RookMove, BishopMove, KnightMove, QueenMove, KingMove, PawnMove }; diff --git a/engine/core/piece.js b/engine/core/piece.js index 0504954..a91329b 100644 --- a/engine/core/piece.js +++ b/engine/core/piece.js @@ -22,6 +22,8 @@ class Piece { this.color = color; /** @type {PieceType} */ this.type = type; + /** @type {boolean} */ + this.hasMoved = false; } /**