-import { ArrayFun } from "@/utils/array";
-import { randInt } from "@/utils/alea";
+import { randInt, sample } from "@/utils/alea";
import { ChessRules, PiPo, Move } from "@/base_rules";
export class EightpiecesRules extends ChessRules {
}
}
- static GenRandInitFen(randomness) {
- if (randomness == 0)
- // Deterministic:
+ static GenRandInitFen(options) {
+ if (options.randomness == 0)
return "jfsqkbnr/pppppppp/8/8/8/8/PPPPPPPP/JDSQKBNR w 0 ahah - -";
- let pieces = { w: new Array(8), b: new Array(8) };
- let flags = "";
- // Shuffle pieces on first (and last rank if randomness == 2)
- for (let c of ["w", "b"]) {
- if (c == 'b' && randomness == 1) {
- const lancerIdx = pieces['w'].findIndex(p => {
- return Object.keys(V.LANCER_DIRS).includes(p);
- });
- pieces['b'] =
- pieces['w'].slice(0, lancerIdx)
- .concat(['g'])
- .concat(pieces['w'].slice(lancerIdx + 1));
- flags += flags;
- break;
- }
-
- let positions = ArrayFun.range(8);
-
- // Get random squares for bishop and sentry
- let randIndex = 2 * randInt(4);
- let bishopPos = positions[randIndex];
- // The sentry must be on a square of different color
- let randIndex_tmp = 2 * randInt(4) + 1;
- let sentryPos = positions[randIndex_tmp];
- if (c == 'b') {
- // Check if white sentry is on the same color as ours.
- // If yes: swap bishop and sentry positions.
- // NOTE: test % 2 == 1 because there are 7 slashes.
- if ((pieces['w'].indexOf('s') - sentryPos) % 2 == 1)
- [bishopPos, sentryPos] = [sentryPos, bishopPos];
+ const baseFen = ChessRules.GenRandInitFen(options);
+ const fenParts = baseFen.split(' ');
+ const posParts = fenParts[0].split('/');
+
+ // Replace one bishop by sentry, so that sentries on different colors
+ // Also replace one random rook by jailer,
+ // and one random knight by lancer (facing north/south)
+ let pieceLine = { b: posParts[0], w: posParts[7].toLowerCase() };
+ let posBlack = { r: -1, n: -1, b: -1 };
+ const mapP = { r: 'j', n: 'l', b: 's' };
+ ['w', 'b'].forEach(c => {
+ ['r', 'n', 'b'].forEach(p => {
+ let pl = pieceLine[c];
+ let pos = -1;
+ if (options.randomness == 2 || c == 'b')
+ pos = (randInt(2) == 0 ? pl.indexOf(p) : pl.lastIndexOf(p));
+ else pos = posBlack[p];
+ pieceLine[c] =
+ pieceLine[c].substr(0, pos) + mapP[p] + pieceLine[c].substr(pos+1);
+ if (options.randomness == 1 && c == 'b') posBlack[p] = pos;
+ });
+ });
+ // Rename 'l' into 'g' (black) or 'c' (white)
+ pieceLine['w'] = pieceLine['w'].replace('l', 'c');
+ pieceLine['b'] = pieceLine['b'].replace('l', 'g');
+ if (options.randomness == 2) {
+ const ws = pieceLine['w'].indexOf('s');
+ const bs = pieceLine['b'].indexOf('s');
+ if (ws % 2 != bs % 2) {
+ // Fix sentry: should be on different colors.
+ // => move sentry on other bishop for random color
+ const c = sample(['w', 'b'], 1);
+ pieceLine[c] = pieceLine[c]
+ .replace('b', 't'); //tmp
+ .replace('s', 'b');
+ .replace('t', 's');
}
- positions.splice(Math.max(randIndex, randIndex_tmp), 1);
- positions.splice(Math.min(randIndex, randIndex_tmp), 1);
-
- // Get random squares for knight and lancer
- randIndex = randInt(6);
- const knightPos = positions[randIndex];
- positions.splice(randIndex, 1);
- randIndex = randInt(5);
- const lancerPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- // Get random square for queen
- randIndex = randInt(4);
- const queenPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- // Rook, jailer and king positions are now almost fixed,
- // only the ordering rook->jailer or jailer->rook must be decided.
- let rookPos = positions[0];
- let jailerPos = positions[2];
- const kingPos = positions[1];
- flags += V.CoordToColumn(rookPos) + V.CoordToColumn(jailerPos);
- if (Math.random() < 0.5) [rookPos, jailerPos] = [jailerPos, rookPos];
-
- pieces[c][rookPos] = "r";
- pieces[c][knightPos] = "n";
- pieces[c][bishopPos] = "b";
- pieces[c][queenPos] = "q";
- pieces[c][kingPos] = "k";
- pieces[c][sentryPos] = "s";
- // Lancer faces north for white, and south for black:
- pieces[c][lancerPos] = c == 'w' ? 'c' : 'g';
- pieces[c][jailerPos] = "j";
}
+
return (
- pieces["b"].join("") +
- "/pppppppp/8/8/8/8/PPPPPPPP/" +
- pieces["w"].join("").toUpperCase() +
- " w 0 " + flags + " - -"
+ pieceLine['b'] + "/" +
+ posParts.slice(1, 7).join('/') + "/" +
+ pieceLine['w'].toUpperCase() + " " +
+ fenParts.slice(1, 5).join(' ') + " -"
);
}
return null;
}
- // Because of the lancers, getPiece() could be wrong:
- // use board[x][y][1] instead (always valid).
- getBasicMove([sx, sy], [ex, ey], tr) {
- const initColor = this.getColor(sx, sy);
- const initPiece = this.board[sx][sy].charAt(1);
- let mv = new Move({
- appear: [
- new PiPo({
- x: ex,
- y: ey,
- c: tr ? tr.c : initColor,
- p: tr ? tr.p : initPiece
- })
- ],
- vanish: [
- new PiPo({
- x: sx,
- y: sy,
- c: initColor,
- p: initPiece
- })
- ]
- });
-
- // The opponent piece disappears if we take it
- if (this.board[ex][ey] != V.EMPTY) {
- mv.vanish.push(
- new PiPo({
- x: ex,
- y: ey,
- c: this.getColor(ex, ey),
- p: this.board[ex][ey].charAt(1)
- })
- );
- }
-
- return mv;
- }
-
canIplay(side, [x, y]) {
return (
(this.subTurn == 1 && this.turn == side && this.getColor(x, y) == side)
}
return true;
});
- } else if (this.subTurn == 2) {
+ }
+ else if (this.subTurn == 2) {
// Put back the sentinel on board:
const color = this.turn;
moves.forEach(m => {
getPotentialKingMoves(sq) {
const moves = this.getSlideNJumpMoves(
- sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
- "oneStep"
- );
+ sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1);
return (
this.subTurn == 1
? moves.concat(this.getCastleMoves(sq))
const oppCol = V.GetOppCol(color);
const sliderAttack = (allowedSteps, lancer) => {
const deltaX = x2 - x1,
- absDeltaX = Math.abs(deltaX);
- const deltaY = y2 - y1,
+ deltaY = y2 - y1;
+ const absDeltaX = Math.abs(deltaX),
absDeltaY = Math.abs(deltaY);
const step = [ deltaX / absDeltaX || 0, deltaY / absDeltaY || 0 ];
if (
}
let sq = [ x1 + step[0], y1 + step[1] ];
while (sq[0] != x2 || sq[1] != y2) {
- if (
- // NOTE: no need to check OnBoard in this special case
- (!lancer && this.board[sq[0]][sq[1]] != V.EMPTY) ||
- (!!lancer && this.getColor(sq[0], sq[1]) == oppCol)
- ) {
- return false;
+ // NOTE: no need to check OnBoard in this special case
+ if (this.board[sq[0]][sq[1]] != V.EMPTY) {
+ const p = this.getPiece(sq[0], sq[1]);
+ const pc = this.getColor(sq[0], sq[1]);
+ if (
+ // Enemy sentry on the way will be gone:
+ (p != V.SENTRY || pc != oppCol) &&
+ // Lancer temporarily "changed color":
+ (!lancer || pc == color)
+ ) {
+ return false;
+ }
}
sq[0] += step[0];
sq[1] += step[1];