chessgroundx
Version:
Extended lichess.org Chess UI
110 lines (94 loc) • 3.93 kB
text/typescript
import * as util from './util.js';
import * as cg from './types.js';
type DropMobility = (x: number, y: number) => boolean;
const wholeBoard = () => true;
/**
*
* @param from 0-based index from given color's PoV, inclusive
* @param to 0-based index from given color's PoV, exclusive
* @param color The piece's color
* @param bd The board's dimensions
*
* Returns a function that checks if a position's rank is inside the from-to range, where from and to are indices of rank when counting from
* current "color"'s point of view (i.e. if from=to=1 and color=black the function will return true only if the position's rank is 8 in case of 8x8 board)
* from and to can be zero or negative to denote that many ranks counting from the last
*
* */
function rankRange(from: number, to: number, color: cg.Color, bd: cg.BoardDimensions): DropMobility {
if (from < 0) from += bd.height;
if (to < 0) to += bd.height;
return (_x, y) => {
if (color === 'black') y = bd.height - 1 - y;
return from <= y && y < to;
};
}
export function predrop(variant: string, bd: cg.BoardDimensions): cg.Predrop {
const mobility = builtinMobility(variant, bd);
return (boardState, piece) =>
util
.allPos(bd)
.filter(pos => boardState.pieces.get(util.pos2key(pos))?.color !== piece.color && mobility(piece)(pos[0], pos[1]))
.map(util.pos2key);
}
function builtinMobility(variant: string, bd: cg.BoardDimensions): (piece: cg.Piece) => DropMobility {
switch (variant) {
case 'crazyhouse':
case 'shouse':
case 'capahouse':
case 'gothhouse':
// pawns can't be dropped on the first or last rank
return piece => (piece.role === 'p-piece' ? rankRange(1, -1, piece.color, bd) : wholeBoard);
case 'placement':
// the "drop" is the placement phase where pieces can only be placed on the first rank
return piece => rankRange(0, 1, piece.color, bd);
case 'sittuyin':
// the "drop" is the placement phase where pieces can only be placed on the player's half
// rooks can only be dropped on the first rank
return piece => (piece.role === 'r-piece' ? rankRange(0, 1, piece.color, bd) : rankRange(0, 3, piece.color, bd));
case 'shogi':
case 'minishogi':
case 'gorogoro':
case 'gorogoroplus':
return piece => {
switch (piece.role) {
case 'p-piece': // pawns and lances can't be dropped on the last rank
case 'l-piece':
return rankRange(0, -1, piece.color, bd);
case 'n-piece': // knights can't be dropped on the last two ranks
return rankRange(0, -2, piece.color, bd);
default:
return wholeBoard;
}
};
case 'torishogi':
// swallows can't be dropped on the last rank
return piece => (piece.role === 's-piece' ? rankRange(0, -1, piece.color, bd) : wholeBoard);
case 'grandhouse':
// pawns can't be dropped on the 1st, or 8th to 10th ranks
return piece => (piece.role === 'p-piece' ? rankRange(1, 7, piece.color, bd) : wholeBoard);
case 'shogun':
// shogun only permits drops on ranks 1-5 for all pieces
return piece => rankRange(0, 5, piece.color, bd);
case 'synochess':
// Only black can drop, and the only droppable rank is the literal rank five.
return () => (_x, y) => y === 4;
case 'shinobi':
// Only white can drop, and only on their own half of the board
return () => (_x, y) => y <= 3;
case 'mansindam':
return piece => {
switch (piece.role) {
case 'p-piece': // pawns can't be dropped on the last rank
return rankRange(0, -1, piece.color, bd);
default:
return wholeBoard;
}
};
// These cases are unnecessary but is here anyway to be explicit
case 'kyotoshogi':
case 'dobutsu':
case 'chennis':
default:
return () => wholeBoard;
}
}