import { ChessRules, Move, PiPo } from "@/base_rules";
+import { randInt } from "@/utils/alea";
export class SittuyinRules extends ChessRules {
+
+ static get Options() {
+ return null;
+ }
+
static get HasFlags() {
return false;
}
return false;
}
+ static get Monochrome() {
+ return true;
+ }
+
+ static get Notoodark() {
+ return true;
+ }
+
+ static get Lines() {
+ return ChessRules.Lines.concat([
+ [[0, 0], [8, 8]],
+ [[0, 8], [8, 0]]
+ ]);
+ }
+
static get PawnSpecs() {
return Object.assign(
{},
ChessRules.PawnSpecs,
{
- twoSquares: false,
// Promotions are handled differently here
promotions: [V.QUEEN]
}
}
getPotentialMovesFrom([x, y]) {
- if (this.movesCount <= 1) {
- const color = this.turn;
- const p = V.RESERVE_PIECES[y];
- if (this.reserve[color][p] == 0) return [];
- const iBound =
- p != V.ROOK
- ? (color == 'w' ? [4, 7] : [0, 3])
- : (color == 'w' ? [7, 7] : [0, 0]);
- const jBound = (i) => {
- if (color == 'w' && i == 4) return [4, 7];
- if (color == 'b' && i == 3) return [0, 3];
- return [0, 7];
- };
- let moves = [];
- for (let i = iBound[0]; i <= iBound[1]; i++) {
- const jb = jBound(i);
- for (let j = jb[0]; j <= jb[1]; j++) {
- if (this.board[i][j] == V.EMPTY) {
- let mv = new Move({
- appear: [
- new PiPo({
- x: i,
- y: j,
- c: color,
- p: p
- })
- ],
- vanish: [],
- start: { x: x, y: y },
- end: { x: i, y: j }
- });
- moves.push(mv);
- }
+ if (this.movesCount >= 2) return super.getPotentialMovesFrom([x, y]);
+ // Only reserve moves are allowed for now:
+ if (V.OnBoard(x, y)) return [];
+ const color = this.turn;
+ const p = V.RESERVE_PIECES[y];
+ if (this.reserve[color][p] == 0) return [];
+ const iBound =
+ p != V.ROOK
+ ? (color == 'w' ? [4, 7] : [0, 3])
+ : (color == 'w' ? [7, 7] : [0, 0]);
+ const jBound = (i) => {
+ if (color == 'w' && i == 4) return [4, 7];
+ if (color == 'b' && i == 3) return [0, 3];
+ return [0, 7];
+ };
+ let moves = [];
+ for (let i = iBound[0]; i <= iBound[1]; i++) {
+ const jb = jBound(i);
+ for (let j = jb[0]; j <= jb[1]; j++) {
+ if (this.board[i][j] == V.EMPTY) {
+ let mv = new Move({
+ appear: [
+ new PiPo({
+ x: i,
+ y: j,
+ c: color,
+ p: p
+ })
+ ],
+ vanish: [],
+ start: { x: x, y: y },
+ end: { x: i, y: j }
+ });
+ moves.push(mv);
}
}
- return moves;
}
- return super.getPotentialMovesFrom([x, y]);
+ return moves;
}
getPotentialPawnMoves([x, y]) {
const color = this.turn;
- const [sizeX, sizeY] = [V.size.x, V.size.y];
const shiftX = V.PawnSpecs.directions[color];
let moves = [];
- // NOTE: next condition is generally true (no pawn on last rank)
- if (x + shiftX >= 0 && x + shiftX < sizeX) {
- if (this.board[x + shiftX][y] == V.EMPTY) {
+ if (x + shiftX >= 0 && x + shiftX < 8) {
+ if (this.board[x + shiftX][y] == V.EMPTY)
// One square forward
moves.push(this.getBasicMove([x, y], [x + shiftX, y]));
- }
// Captures
- if (V.PawnSpecs.canCapture) {
- for (let shiftY of [-1, 1]) {
- if (
- y + shiftY >= 0 &&
- y + shiftY < sizeY
- ) {
- if (
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- moves.push(this.getBasicMove([x, y], [x + shiftX, y + shiftY]));
- }
- }
+ for (let shiftY of [-1, 1]) {
+ if (
+ y + shiftY >= 0 && y + shiftY < 8 &&
+ this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+ this.canTake([x, y], [x + shiftX, y + shiftY])
+ ) {
+ moves.push(this.getBasicMove([x, y], [x + shiftX, y + shiftY]));
}
}
}
(color == 'b' && ((y >= 4 && x == y) || (y <= 3 && x == 7 - y)))
)
) {
- // Add potential promotions
const addPromotion = ([xx, yy], moveTo) => {
- moves.push(
- new Move({
- appear: [
- new PiPo({
- x: !!moveTo ? xx : x,
- y: yy, //yy == y if !!moveTo
- c: color,
- p: V.QUEEN
- })
- ],
- vanish: [
- new PiPo({
- x: x,
- y: y,
- c: color,
- p: V.PAWN
- })
- ],
- start: { x: x, y: y },
- end: { x: xx, y: yy }
- })
- );
+ // The promoted pawn shouldn't attack anything,
+ // and the promotion shouldn't discover a rook attack on anything.
+ const finalSquare = (!moveTo ? [x, y] : [xx, yy]);
+ let validP = true;
+ for (let step of V.steps[V.BISHOP]) {
+ const [i, j] = [finalSquare[0] + step[0], finalSquare[1] + step[1]];
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) != color
+ ) {
+ validP = false;
+ break;
+ }
+ }
+ if (validP && !!moveTo) {
+ // Also check rook discovered attacks on the enemy king
+ let found = {
+ "0,-1": 0,
+ "0,1": 0,
+ "1,0": 0,
+ "-1,0": 0
+ };
+ // TODO: check opposite steps one after another, which could
+ // save some time (no need to explore the other line).
+ for (let step of V.steps[V.ROOK]) {
+ let [i, j] = [x + step[0], y + step[1]];
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ i += step[0];
+ j += step[1];
+ }
+ if (V.OnBoard(i, j)) {
+ const colIJ = this.getColor(i, j);
+ const pieceIJ = this.getPiece(i, j);
+ if (colIJ != color && pieceIJ == V.KING)
+ found[step[0] + "," + step[1]] = -1;
+ else if (colIJ == color && pieceIJ == V.ROOK)
+ found[step[0] + "," + step[1]] = 1;
+ }
+ }
+ if (
+ (found["0,-1"] * found["0,1"] < 0) ||
+ (found["-1,0"] * found["1,0"] < 0)
+ ) {
+ validP = false;
+ }
+ }
+ if (validP) {
+ moves.push(
+ new Move({
+ appear: [
+ new PiPo({
+ x: !!moveTo ? xx : x,
+ y: yy, //yy == y if !!moveTo
+ c: color,
+ p: V.QUEEN
+ })
+ ],
+ vanish: [
+ new PiPo({
+ x: x,
+ y: y,
+ c: color,
+ p: V.PAWN
+ })
+ ],
+ start: { x: x, y: y },
+ end: { x: xx, y: yy }
+ })
+ );
+ }
};
// In-place promotion always possible:
addPromotion([x - shiftX, y]);
getPotentialBishopMoves(sq) {
const forward = (this.turn == 'w' ? -1 : 1);
return this.getSlideNJumpMoves(
- sq,
- V.steps[V.BISHOP].concat([ [forward, 0] ]),
- "oneStep"
- );
+ sq, V.steps[V.BISHOP].concat([ [forward, 0] ]), 1);
}
getPotentialQueenMoves(sq) {
- return this.getSlideNJumpMoves(
- sq,
- V.steps[V.BISHOP],
- "oneStep"
- );
+ return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 1);
+ }
+
+ getAllValidMoves() {
+ if (this.movesCount >= 2) return super.getAllValidMoves();
+ const color = this.turn;
+ let moves = [];
+ for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
+ moves = moves.concat(
+ this.getPotentialMovesFrom([V.size.x + (color == "w" ? 0 : 1), i])
+ );
+ }
+ return this.filterValid(moves);
}
isAttackedByBishop(sq, color) {
const forward = (this.turn == 'w' ? 1 : -1);
return this.isAttackedBySlideNJump(
- sq,
- color,
- V.BISHOP,
- V.steps[V.BISHOP].concat([ [forward, 0] ]),
- "oneStep"
- );
+ sq, color, V.BISHOP, V.steps[V.BISHOP].concat([ [forward, 0] ]), 1);
}
isAttackedByQueen(sq, color) {
return this.isAttackedBySlideNJump(
- sq,
- color,
- V.QUEEN,
- V.steps[V.BISHOP],
- "oneStep"
- );
+ sq, color, V.QUEEN, V.steps[V.BISHOP], 1);
}
underCheck(color) {
};
}
+ getComputerMove() {
+ if (this.movesCount >= 2) return super.getComputerMove();
+ // Play a random "initialization move"
+ let res = [];
+ for (let i=0; i<8; i++) {
+ const moves = this.getAllValidMoves();
+ const moveIdx = randInt(moves.length);
+ this.play(moves[moveIdx]);
+ res.push(moves[moveIdx]);
+ }
+ for (let i=7; i>=0; i--) this.undo(res[i]);
+ return res;
+ }
+
getNotation(move) {
// Do not note placement moves (complete move would be too long)
if (move.vanish.length == 0) return "";
}
return super.getNotation(move);
}
+
};