-#Enhancements
-
-In Hall: at loading, if something in a tab and nothing in another (e.g. live/corr challenges),
-show the other even if settings say to show the empty one.
-
-tabs "running" and "completed" for MyGames page (default to running if any and my turn)
-"load more" option for completed games (act on corr games).
-
-"load more" option for problems as well: similar to news page.
-Also on corr challenges.
-
# New variants
Landing pieces from empty board:
https://www.chessvariants.com/diffsetup.dir/unachess.html
return f.charCodeAt() <= 90 ? "w" + f.toLowerCase() : "b" + f;
}
- // Check if FEN describe a board situation correctly
+ // Check if FEN describes a board situation correctly
static IsGoodFen(fen) {
const fenParsed = V.ParseFen(fen);
// 1) Check position
i = y;
do {
if (
- this.isAttacked([x, i], [oppCol]) ||
+ this.isAttacked([x, i], oppCol) ||
(this.board[x][i] != V.EMPTY &&
// NOTE: next check is enough, because of chessboard constraints
(this.getColor(x, i) != c ||
return false;
}
- // Check if pieces of color in 'colors' are attacking (king) on square x,y
- isAttacked(sq, colors) {
+ // Check if pieces of given color are attacking (king) on square x,y
+ isAttacked(sq, color) {
return (
- this.isAttackedByPawn(sq, colors) ||
- this.isAttackedByRook(sq, colors) ||
- this.isAttackedByKnight(sq, colors) ||
- this.isAttackedByBishop(sq, colors) ||
- this.isAttackedByQueen(sq, colors) ||
- this.isAttackedByKing(sq, colors)
+ this.isAttackedByPawn(sq, color) ||
+ this.isAttackedByRook(sq, color) ||
+ this.isAttackedByKnight(sq, color) ||
+ this.isAttackedByBishop(sq, color) ||
+ this.isAttackedByQueen(sq, color) ||
+ this.isAttackedByKing(sq, color)
);
}
// Generic method for non-pawn pieces ("sliding or jumping"):
- // is x,y attacked by a piece of color in array 'colors' ?
- isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
+ // is x,y attacked by a piece of given color ?
+ isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
for (let step of steps) {
let rx = x + step[0],
ry = y + step[1];
}
if (
V.OnBoard(rx, ry) &&
- this.getPiece(rx, ry) === piece &&
- colors.includes(this.getColor(rx, ry))
+ this.getPiece(rx, ry) == piece &&
+ this.getColor(rx, ry) == color
) {
return true;
}
return false;
}
- // Is square x,y attacked by 'colors' pawns ?
- isAttackedByPawn([x, y], colors) {
- for (let c of colors) {
- const pawnShift = c == "w" ? 1 : -1;
- if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
- for (let i of [-1, 1]) {
- if (
- y + i >= 0 &&
- y + i < V.size.y &&
- this.getPiece(x + pawnShift, y + i) == V.PAWN &&
- this.getColor(x + pawnShift, y + i) == c
- ) {
- return true;
- }
+ // Is square x,y attacked by 'color' pawns ?
+ isAttackedByPawn([x, y], color) {
+ const pawnShift = (color == "w" ? 1 : -1);
+ if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
+ for (let i of [-1, 1]) {
+ if (
+ y + i >= 0 &&
+ y + i < V.size.y &&
+ this.getPiece(x + pawnShift, y + i) == V.PAWN &&
+ this.getColor(x + pawnShift, y + i) == color
+ ) {
+ return true;
}
}
}
return false;
}
- // Is square x,y attacked by 'colors' rooks ?
- isAttackedByRook(sq, colors) {
- return this.isAttackedBySlideNJump(sq, colors, V.ROOK, V.steps[V.ROOK]);
+ // Is square x,y attacked by 'color' rooks ?
+ isAttackedByRook(sq, color) {
+ return this.isAttackedBySlideNJump(sq, color, V.ROOK, V.steps[V.ROOK]);
}
- // Is square x,y attacked by 'colors' knights ?
- isAttackedByKnight(sq, colors) {
+ // Is square x,y attacked by 'color' knights ?
+ isAttackedByKnight(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.KNIGHT,
V.steps[V.KNIGHT],
"oneStep"
);
}
- // Is square x,y attacked by 'colors' bishops ?
- isAttackedByBishop(sq, colors) {
- return this.isAttackedBySlideNJump(sq, colors, V.BISHOP, V.steps[V.BISHOP]);
+ // Is square x,y attacked by 'color' bishops ?
+ isAttackedByBishop(sq, color) {
+ return this.isAttackedBySlideNJump(sq, color, V.BISHOP, V.steps[V.BISHOP]);
}
- // Is square x,y attacked by 'colors' queens ?
- isAttackedByQueen(sq, colors) {
+ // Is square x,y attacked by 'color' queens ?
+ isAttackedByQueen(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.QUEEN,
V.steps[V.ROOK].concat(V.steps[V.BISHOP])
);
}
- // Is square x,y attacked by 'colors' king(s) ?
- isAttackedByKing(sq, colors) {
+ // Is square x,y attacked by 'color' king(s) ?
+ isAttackedByKing(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.KING,
V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
"oneStep"
// Game over
const color = this.turn;
// No valid move: stalemate or checkmate?
- if (!this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]))
+ if (!this.isAttacked(this.kingPos[color], V.GetOppCol(color)))
return "1/2";
// OK, checkmate
- return color == "w" ? "0-1" : "1-0";
+ return (color == "w" ? "0-1" : "1-0");
}
///////////////
"Mate any piece (v2)": "Mate any piece (v2)",
"Mate the knight": "Mate the knight",
"Middle battle": "Middle battle",
+ "Move like a knight (v1)": "Move like a knight (v1)",
+ "Move like a knight (v2)": "Move like a knight (v2)",
"Move twice": "Move twice",
"Neverending rows": "Neverending rows",
"Pawns move diagonally": "Pawns move diagonally",
"Shoot pieces": "Shoot pieces",
"Squares disappear": "Squares disappear",
"Standard rules": "Standard rules",
- "The knight transfers its powers": "The knight transfers its powers",
"Unidentified pieces": "Unidentified pieces"
};
"Mate any piece (v2)": "Matar cualquier pieza (v2)",
"Mate the knight": "Matar el caballo",
"Middle battle": "Batalla media",
+ "Move like a knight (v1)": "Moverse como un caballo (v1)",
+ "Move like a knight (v2)": "Moverse como un caballo (v2)",
"Move twice": "Mover dos veces",
"Neverending rows": "Filas interminables",
"Pawns move diagonally": "Peones se mueven en diagonal",
"Shoot pieces": "Tirar de las piezas",
"Squares disappear": "Las casillas desaparecen",
"Standard rules": "Reglas estandar",
- "The knight transfers its powers": "El caballo transfiere sus poderes",
"Unidentified pieces": "Piezas no identificadas"
};
"Mate any piece (v2)": "Matez n'importe quelle pièce (v2)",
"Mate the knight": "Matez le cavalier",
"Middle battle": "Bataille du milieu",
+ "Move like a knight (v1)": "Bouger comme un cavalier (v1)",
+ "Move like a knight (v2)": "Bouger comme un cavalier (v2)",
"Move twice": "Jouer deux coups",
"Neverending rows": "Rangées sans fin",
"Pawns move diagonally": "Les pions vont en diagonale",
"Shoot pieces": "Tirez sur les pièces",
"Squares disappear": "Les cases disparaissent",
"Standard rules": "Règles usuelles",
- "The knight transfers its powers": "Le cavalier transfère ses pouvoirs",
"Unidentified pieces": "Pièces non identifiées"
};
--- /dev/null
+p.boxed
+ | Any piece guarded by a friendly knight can also move like a knight.
+
+p.
+ In addition to its normal abilities, a piece guarded by a knight can move like him.
+ On the following diagram, 1.Nf4 would checkmate because it guard the g6 queen.
+ If it is black to play, then 1...Rxe2 is forbidden because of the knight
+ immunity exception. Exceptions to the orthodox rules are the following:
+
+ul
+ li Knights cannot capture or be captured.
+ li Kings cannot be knight-relayed.
+ li Pawns cannot give check on last rank or promote with knight-relay.
+
+p These oddities excepted, orthodox rules apply.
+
+figure.diagram-container
+ .diagram
+ | fen:7k/8/6Q1/1n6/8/2r5/4N3/K7:
+
+h3 Source
+
+p
+ | These are the original N-relay or Knight-relay rules, described for example
+ a(href="https://www.chessvariants.com/diffmove.dir/nrelay.html") on this page
+ | . See also Knightrelay2.
+
+p Inventor: Mannis Charosh (1972)
--- /dev/null
+p.boxed
+ | Cualquier parte protegida por un caballo también puede moverse como un caballo.
+
+p.
+ Además de sus capacidades normales, una pieza defendida por un caballo puede
+ muévete como él.
+ En el siguiente diagrama, 1.Nf4 serÃa jaque mate porque protege a la dama en g6.
+ Si son las negras para jugar, entonces 1...Rxe2 es posible gracias al caballo c8.
+
+ Si son las negras para jugar, entonces 1...Txe2 está prohibido debido a
+ excepción de la inmunidad del caballo. Las excepciones a las reglas
+ ortodoxas son las siguientes:
+
+ul
+ li Los caballos no pueden capturar ni ser capturados.
+ li Los reyes no pueden ser retransmitidos por un caballo.
+ li.
+ Los peones no dan jaque en la última fila ni se promocionan
+ por relevo de caballo.
+
+figure.diagram-container
+ .diagram
+ | fen:7k/8/6Q1/1n6/8/2r5/4N3/K7:
+
+p Salvo estas rarezas, se aplican las reglas ortodoxas.
+
+h3 Fuente
+
+p
+ | Esta es la regla de origen de N-relay o Knight-relay, descrita por ejemplo
+ a(href="https://www.chessvariants.com/diffmove.dir/nrelay.html") en esta página
+ | . Ver también Knightrelay2.
+
+p Inventor: Mannis Charosh (1972)
--- /dev/null
+p.boxed
+ | Toute pièce protégée par un cavalier peut aussi se déplacer comme un cavalier.
+
+p.
+ En plus de ses capacités normales, une pièce défendue par un cavalier peut se
+ déplacer comme lui.
+ Sur le diagramme suivant, 1.Nf4 ferait mat car il protège la dame en g6.
+ Si c'est aux noirs de jouer, alors 1...Txe2 est interdit à cause de
+ l'exception d'immunité du cavalier.
+ Les exceptions aux règles orthodoxes sont les suivantes :
+
+ul
+ li Les cavaliers ne peuvent capturer ou être capturés.
+ li Les rois ne peuvent pas être relayés par un cavalier.
+ li.
+ Les pions ne donnent pas échec sur la dernière rangée ni se promeuvent
+ par relais de cavalier.
+
+figure.diagram-container
+ .diagram
+ | fen:7k/8/6Q1/1n6/8/2r5/4N3/K7:
+
+p Ces bizarreries exceptées, les règles orthodoxes s'appliquent.
+
+h3 Source
+
+p
+ | Il s'agit de la règle d'origine du N-relay ou Knight-relay, décrite par exemple
+ a(href="https://www.chessvariants.com/diffmove.dir/nrelay.html") sur cette page
+ | . Voir aussi Knightrelay2.
+
+p Inventeur : Mannis Charosh (1972)
);
}
- isAttacked(sq, colors) {
+ isAttacked(sq, color) {
return (
- super.isAttacked(sq, colors) || this.isAttackedByAntiking(sq, colors)
+ super.isAttacked(sq, color) ||
+ this.isAttackedByAntiking(sq, color)
);
}
- isAttackedByKing([x, y], colors) {
- if (this.getPiece(x, y) == V.ANTIKING) return false; //antiking is not attacked by king
+ isAttackedByKing([x, y], color) {
+ // Antiking is not attacked by king:
+ if (this.getPiece(x, y) == V.ANTIKING) return false;
return this.isAttackedBySlideNJump(
[x, y],
- colors,
+ color,
V.KING,
V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
"oneStep"
);
}
- isAttackedByAntiking([x, y], colors) {
- if ([V.KING, V.ANTIKING].includes(this.getPiece(x, y))) return false; //(anti)king is not attacked by antiking
+ isAttackedByAntiking([x, y], color) {
+ // (Anti)King is not attacked by antiking
+ if ([V.KING, V.ANTIKING].includes(this.getPiece(x, y))) return false;
return this.isAttackedBySlideNJump(
[x, y],
- colors,
+ color,
V.ANTIKING,
V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
"oneStep"
underCheck(color) {
const oppCol = V.GetOppCol(color);
let res =
- this.isAttacked(this.kingPos[color], [oppCol]) ||
- !this.isAttacked(this.antikingPos[color], [oppCol]);
+ this.isAttacked(this.kingPos[color], oppCol) ||
+ !this.isAttacked(this.antikingPos[color], oppCol);
return res;
}
getCheckSquares(color) {
let res = super.getCheckSquares(color);
- if (!this.isAttacked(this.antikingPos[color], [V.GetOppCol(color)]))
+ if (!this.isAttacked(this.antikingPos[color], V.GetOppCol(color)))
res.push(JSON.parse(JSON.stringify(this.antikingPos[color])));
return res;
}
const color = this.turn;
const oppCol = V.GetOppCol(color);
if (
- !this.isAttacked(this.kingPos[color], [oppCol]) &&
- this.isAttacked(this.antikingPos[color], [oppCol])
+ !this.isAttacked(this.kingPos[color], oppCol) &&
+ this.isAttacked(this.antikingPos[color], oppCol)
) {
return "1/2";
}
return moves.concat(this.getCastleMoves([x, y]));
}
- isAttacked(sq, colors) {
+ isAttacked(sq, color) {
if (
this.getPiece(sq[0], sq[1]) == V.KING &&
- this.isAttackedByKing(sq, colors)
- )
- return false; //king cannot take...
+ this.isAttackedByKing(sq, color)
+ ) {
+ // A king next to the enemy king is immune to attacks
+ return false;
+ }
return (
- this.isAttackedByPawn(sq, colors) ||
- this.isAttackedByRook(sq, colors) ||
- this.isAttackedByKnight(sq, colors) ||
- this.isAttackedByBishop(sq, colors) ||
- this.isAttackedByQueen(sq, colors)
+ this.isAttackedByPawn(sq, color) ||
+ this.isAttackedByRook(sq, color) ||
+ this.isAttackedByKnight(sq, color) ||
+ this.isAttackedByBishop(sq, color) ||
+ this.isAttackedByQueen(sq, color)
+ // No "attackedByKing": it cannot take
);
}
// If opponent king disappeared, move is valid
else if (this.kingPos[oppCol][0] < 0) res = false;
// Otherwise, if we remain under check, move is not valid
- else res = this.isAttacked(this.kingPos[color], [oppCol]);
+ else res = this.isAttacked(this.kingPos[color], oppCol);
return res;
}
let res = [];
if (
this.kingPos[color][0] >= 0 && //king might have exploded
- this.isAttacked(this.kingPos[color], [V.GetOppCol(color)])
+ this.isAttacked(this.kingPos[color], V.GetOppCol(color))
) {
res = [JSON.parse(JSON.stringify(this.kingPos[color]))];
}
return color == "w" ? "0-1" : "1-0";
if (this.atLeastOneMove())
return "*";
- if (!this.isAttacked(kp, [V.GetOppCol(color)])) return "1/2";
+ if (!this.isAttacked(kp, V.GetOppCol(color))) return "1/2";
return color == "w" ? "0-1" : "1-0"; //checkmate
}
};
// isAttacked() is OK because the immobilizer doesn't take
- isAttackedByPawn([x, y], colors) {
+ isAttackedByPawn([x, y], color) {
// Square (x,y) must be surroundable by two enemy pieces,
// and one of them at least should be a pawn (moving).
const dirs = [
const [i2, j2] = [x + dir[0], y + dir[1]]; //"after"
if (V.OnBoard(i1, j1) && V.OnBoard(i2, j2)) {
if (
- (this.board[i1][j1] != V.EMPTY &&
- colors.includes(this.getColor(i1, j1)) &&
- this.board[i2][j2] == V.EMPTY) ||
- (this.board[i2][j2] != V.EMPTY &&
- colors.includes(this.getColor(i2, j2)) &&
- this.board[i1][j1] == V.EMPTY)
+ (
+ this.board[i1][j1] != V.EMPTY &&
+ this.getColor(i1, j1) == color &&
+ this.board[i2][j2] == V.EMPTY
+ )
+ ||
+ (
+ this.board[i2][j2] != V.EMPTY &&
+ this.getColor(i2, j2) == color &&
+ this.board[i1][j1] == V.EMPTY
+ )
) {
// Search a movable enemy pawn landing on the empty square
for (let step of steps) {
}
if (
V.OnBoard(i3, j3) &&
- colors.includes(this.getColor(i3, j3)) &&
+ this.getColor(i3, j3) == color &&
this.getPiece(i3, j3) == V.PAWN &&
!this.isImmobilized([i3, j3])
) {
return false;
}
- isAttackedByRook([x, y], colors) {
+ isAttackedByRook([x, y], color) {
// King must be on same column or row,
// and a rook should be able to reach a capturing square
- // colors contains only one element, giving the oppCol and thus king position
- const sameRow = x == this.kingPos[colors[0]][0];
- const sameColumn = y == this.kingPos[colors[0]][1];
+ const sameRow = x == this.kingPos[color][0];
+ const sameColumn = y == this.kingPos[color][1];
if (sameRow || sameColumn) {
// Look for the enemy rook (maximum 1)
for (let i = 0; i < V.size.x; i++) {
for (let j = 0; j < V.size.y; j++) {
if (
this.board[i][j] != V.EMPTY &&
- colors.includes(this.getColor(i, j)) &&
+ this.getColor(i, j) == color &&
this.getPiece(i, j) == V.ROOK
) {
if (this.isImmobilized([i, j])) return false; //because only one rook
return false;
}
- isAttackedByKnight([x, y], colors) {
+ isAttackedByKnight([x, y], color) {
// Square (x,y) must be on same line as a knight,
// and there must be empty square(s) behind.
const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
j -= step[1];
}
if (V.OnBoard(i, j)) {
- if (colors.includes(this.getColor(i, j))) {
+ if (this.getColor(i, j) == color) {
if (
this.getPiece(i, j) == V.KNIGHT &&
!this.isImmobilized([i, j])
return false;
}
- isAttackedByBishop([x, y], colors) {
+ isAttackedByBishop([x, y], color) {
// We cheat a little here: since this function is used exclusively for
// the king, it's enough to check the immediate surrounding of the square.
const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
if (
V.OnBoard(i, j) &&
this.board[i][j] != V.EMPTY &&
- colors.includes(this.getColor(i, j)) &&
+ this.getColor(i, j) == color &&
this.getPiece(i, j) == V.BISHOP
) {
return true; //bishops are never immobilized
return false;
}
- isAttackedByQueen([x, y], colors) {
+ isAttackedByQueen([x, y], color) {
// Square (x,y) must be adjacent to a queen, and the queen must have
// some free space in the opposite direction from (x,y)
const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
const sq1 = [x + step[0], y + step[1]];
if (
this.board[sq1[0]][sq1[1]] != V.EMPTY &&
- colors.includes(this.getColor(sq1[0], sq1[1])) &&
+ this.getColor(sq1[0], sq1[1]) == color &&
this.getPiece(sq1[0], sq1[1]) == V.QUEEN &&
!this.isImmobilized(sq1)
) {
return false;
}
- isAttackedByKing([x, y], colors) {
+ isAttackedByKing([x, y], color) {
const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
for (let step of steps) {
let rx = x + step[0],
if (
V.OnBoard(rx, ry) &&
this.getPiece(rx, ry) === V.KING &&
- colors.includes(this.getColor(rx, ry)) &&
+ this.getColor(rx, ry) == color &&
!this.isImmobilized([rx, ry])
) {
return true;
return moves;
}
- isAttackedByPawn([x, y], colors) {
- for (let c of colors) {
- let pawnShift = c == "w" ? 1 : -1;
- if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
- if (
- this.getPiece(x + pawnShift, y) == V.PAWN &&
- this.getColor(x + pawnShift, y) == c
- ) {
- return true;
- }
+ isAttackedByPawn([x, y], color) {
+ let pawnShift = (color == "w" ? 1 : -1);
+ if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
+ if (
+ this.getPiece(x + pawnShift, y) == V.PAWN &&
+ this.getColor(x + pawnShift, y) == color
+ ) {
+ return true;
}
}
return false;
-import { ChessRules } from "@/base_rules";
+import { ChessRules, Move, PiPo } from "@/base_rules";
export const VariantRules = class CheckeredRules extends ChessRules {
static board2fen(b) {
return moves;
}
+ // Same as in base_rules but with an array given to isAttacked:
+ getCastleMoves([x, y]) {
+ const c = this.getColor(x, y);
+ if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c])
+ return []; //x isn't first rank, or king has moved (shortcut)
+
+ // Castling ?
+ const oppCol = V.GetOppCol(c);
+ let moves = [];
+ let i = 0;
+ // King, then rook:
+ const finalSquares = [
+ [2, 3],
+ [V.size.y - 2, V.size.y - 3]
+ ];
+ castlingCheck: for (
+ let castleSide = 0;
+ castleSide < 2;
+ castleSide++ //large, then small
+ ) {
+ if (this.castleFlags[c][castleSide] >= V.size.y) continue;
+ // If this code is reached, rooks and king are on initial position
+
+ // Nothing on the path of the king ? (and no checks)
+ const finDist = finalSquares[castleSide][0] - y;
+ let step = finDist / Math.max(1, Math.abs(finDist));
+ i = y;
+ do {
+ if (
+ this.isAttacked([x, i], [oppCol]) ||
+ (this.board[x][i] != V.EMPTY &&
+ // NOTE: next check is enough, because of chessboard constraints
+ (this.getColor(x, i) != c ||
+ ![V.KING, V.ROOK].includes(this.getPiece(x, i))))
+ ) {
+ continue castlingCheck;
+ }
+ i += step;
+ } while (i != finalSquares[castleSide][0]);
+
+ // Nothing on the path to the rook?
+ step = castleSide == 0 ? -1 : 1;
+ const rookPos = this.castleFlags[c][castleSide];
+ for (i = y + step; i != rookPos; i += step) {
+ if (this.board[x][i] != V.EMPTY) continue castlingCheck;
+ }
+
+ // Nothing on final squares, except maybe king and castling rook?
+ for (i = 0; i < 2; i++) {
+ if (
+ this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
+ this.getPiece(x, finalSquares[castleSide][i]) != V.KING &&
+ finalSquares[castleSide][i] != rookPos
+ ) {
+ continue castlingCheck;
+ }
+ }
+
+ // If this code is reached, castle is valid
+ moves.push(
+ new Move({
+ appear: [
+ new PiPo({ x: x, y: finalSquares[castleSide][0], p: V.KING, c: c }),
+ new PiPo({ x: x, y: finalSquares[castleSide][1], p: V.ROOK, c: c })
+ ],
+ vanish: [
+ new PiPo({ x: x, y: y, p: V.KING, c: c }),
+ new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c })
+ ],
+ end:
+ Math.abs(y - rookPos) <= 2
+ ? { x: x, y: rookPos }
+ : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
+ })
+ );
+ }
+
+ return moves;
+ }
+
canIplay(side, [x, y]) {
return side == this.turn && [side, "c"].includes(this.getColor(x, y));
}
return false;
}
+ // colors: array, generally 'w' and 'c' or 'b' and 'c'
+ isAttacked(sq, colors) {
+ return (
+ this.isAttackedByPawn(sq, colors) ||
+ this.isAttackedByRook(sq, colors) ||
+ this.isAttackedByKnight(sq, colors) ||
+ this.isAttackedByBishop(sq, colors) ||
+ this.isAttackedByQueen(sq, colors) ||
+ this.isAttackedByKing(sq, colors)
+ );
+ }
+
isAttackedByPawn([x, y], colors) {
for (let c of colors) {
- const color = c == "c" ? this.turn : c;
+ const color = (c == "c" ? this.turn : c);
let pawnShift = color == "w" ? 1 : -1;
if (x + pawnShift >= 0 && x + pawnShift < 8) {
for (let i of [-1, 1]) {
return false;
}
+ isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
+ for (let step of steps) {
+ let rx = x + step[0],
+ ry = y + step[1];
+ while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
+ rx += step[0];
+ ry += step[1];
+ }
+ if (
+ V.OnBoard(rx, ry) &&
+ this.getPiece(rx, ry) === piece &&
+ colors.includes(this.getColor(rx, ry))
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ isAttackedByRook(sq, colors) {
+ return this.isAttackedBySlideNJump(sq, colors, V.ROOK, V.steps[V.ROOK]);
+ }
+
+ isAttackedByKnight(sq, colors) {
+ return this.isAttackedBySlideNJump(
+ sq,
+ colors,
+ V.KNIGHT,
+ V.steps[V.KNIGHT],
+ "oneStep"
+ );
+ }
+
+ isAttackedByBishop(sq, colors) {
+ return this.isAttackedBySlideNJump(sq, colors, V.BISHOP, V.steps[V.BISHOP]);
+ }
+
+ isAttackedByQueen(sq, colors) {
+ return this.isAttackedBySlideNJump(
+ sq,
+ colors,
+ V.QUEEN,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+ );
+ }
+
+ isAttackedByKing(sq, colors) {
+ return this.isAttackedBySlideNJump(
+ sq,
+ colors,
+ V.KING,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
+ }
+
underCheck(color) {
return this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"]);
}
});
}
- isAttackedByPawn([x, y], colors) {
- const pawnShift = 1;
- const attackerRow = V.ComputeX(x + pawnShift);
- for (let c of colors) {
- for (let i of [-1, 1]) {
- if (
- y + i >= 0 &&
- y + i < V.size.y &&
- this.getPiece(attackerRow, y + i) == V.PAWN &&
- this.getColor(attackerRow, y + i) == c
- ) {
- return true;
- }
+ isAttackedByPawn([x, y], color) {
+ // pawn shift is always 1 (all pawns go the same way)
+ const attackerRow = V.ComputeX(x + 1);
+ for (let i of [-1, 1]) {
+ if (
+ y + i >= 0 &&
+ y + i < V.size.y &&
+ this.getPiece(attackerRow, y + i) == V.PAWN &&
+ this.getColor(attackerRow, y + i) == color
+ ) {
+ return true;
}
}
return false;
}
- isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
+ isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
for (let step of steps) {
let rx = V.ComputeX(x + step[0]),
ry = y + step[1];
}
if (
V.OnBoard(rx, ry) &&
- this.getPiece(rx, ry) === piece &&
- colors.includes(this.getColor(rx, ry))
+ this.getPiece(rx, ry) == piece &&
+ this.getColor(rx, ry) == color
) {
return true;
}
return moves;
}
- isAttackedByPawn([x, y], colors) {
- for (let c of colors) {
- let pawnShift = c == "w" ? 1 : -1;
- if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
- for (let i of [-1, 1]) {
- const nextFile = V.ComputeY(y + i);
- if (
- this.getPiece(x + pawnShift, nextFile) == V.PAWN &&
- this.getColor(x + pawnShift, nextFile) == c
- ) {
- return true;
- }
+ isAttackedByPawn([x, y], color) {
+ let pawnShift = (color == "w" ? 1 : -1);
+ if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
+ for (let i of [-1, 1]) {
+ const nextFile = V.ComputeY(y + i);
+ if (
+ this.getPiece(x + pawnShift, nextFile) == V.PAWN &&
+ this.getColor(x + pawnShift, nextFile) == color
+ ) {
+ return true;
}
}
}
return false;
}
- isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
+ isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
for (let step of steps) {
let rx = x + step[0],
ry = V.ComputeY(y + step[1]);
}
if (
V.OnBoard(rx, ry) &&
- this.getPiece(rx, ry) === piece &&
- colors.includes(this.getColor(rx, ry))
+ this.getPiece(rx, ry) == piece &&
+ this.getColor(rx, ry) == color
) {
return true;
}
i = y;
do {
if (
- this.isAttacked([x, i], [oppCol]) ||
+ this.isAttacked([x, i], oppCol) ||
(this.board[x][i] != V.EMPTY &&
(this.getColor(x, i) != c ||
![V.KING, V.ROOK, V.JAILER].includes(this.getPiece(x, i))))
this.sentryPush.pop();
}
- isAttacked(sq, colors) {
+ isAttacked(sq, color) {
return (
- super.isAttacked(sq, colors) ||
- this.isAttackedByLancer(sq, colors) ||
- this.isAttackedBySentry(sq, colors)
+ super.isAttacked(sq, color) ||
+ this.isAttackedByLancer(sq, color) ||
+ this.isAttackedBySentry(sq, color)
);
}
- isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
+ isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
for (let step of steps) {
let rx = x + step[0],
ry = y + step[1];
}
if (
V.OnBoard(rx, ry) &&
- this.getPiece(rx, ry) === piece &&
- colors.includes(this.getColor(rx, ry)) &&
+ this.getPiece(rx, ry) == piece &&
+ this.getColor(rx, ry) == color &&
!this.isImmobilized([rx, ry])
) {
return true;
return false;
}
- isAttackedByPawn([x, y], colors) {
- for (let c of colors) {
- const pawnShift = c == "w" ? 1 : -1;
- if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
- for (let i of [-1, 1]) {
- if (
- y + i >= 0 &&
- y + i < V.size.y &&
- this.getPiece(x + pawnShift, y + i) == V.PAWN &&
- this.getColor(x + pawnShift, y + i) == c &&
- !this.isImmobilized([x + pawnShift, y + i])
- ) {
- return true;
- }
+ isAttackedByPawn([x, y], color) {
+ const pawnShift = (color == "w" ? 1 : -1);
+ if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
+ for (let i of [-1, 1]) {
+ if (
+ y + i >= 0 &&
+ y + i < V.size.y &&
+ this.getPiece(x + pawnShift, y + i) == V.PAWN &&
+ this.getColor(x + pawnShift, y + i) == color &&
+ !this.isImmobilized([x + pawnShift, y + i])
+ ) {
+ return true;
}
}
}
return false;
}
- isAttackedByLancer([x, y], colors) {
+ isAttackedByLancer([x, y], color) {
for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
// If in this direction there are only enemy pieces and empty squares,
// and we meet a lancer: can he reach us?
V.OnBoard(coord.x, coord.y) &&
(
this.board[coord.x][coord.y] == V.EMPTY ||
- colors.includes(this.getColor(coord.x, coord.y))
+ this.getColor(coord.x, coord.y) == color
)
) {
if (
return false;
}
- isAttackedBySentry([x, y], colors) {
+ isAttackedBySentry([x, y], color) {
// Attacked by sentry means it can self-take our king.
// Just check diagonals of enemy sentry(ies), and if it reaches
// one of our pieces: can I self-take?
- const color = V.GetOppCol(colors[0]);
+ const myColor = V.GetOppCol(color);
let candidates = [];
for (let i=0; i<V.size.x; i++) {
for (let j=0; j<V.size.y; j++) {
if (
this.getPiece(i,j) == V.SENTRY &&
- colors.includes(this.getColor(i,j)) &&
+ this.getColor(i,j) == color &&
!this.isImmobilized([i, j])
) {
for (let step of V.steps[V.BISHOP]) {
}
if (
V.OnBoard(sq[0], sq[1]) &&
- this.getColor(sq[0], sq[1]) == color
+ this.getColor(sq[0], sq[1]) == myColor
) {
candidates.push([ sq[0], sq[1] ]);
}
return filteredMoves.filter(m => m.vanish.length == 1);
}
- isAttackedByKnight(sq, colors) {
+ isAttackedByKnight(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.KNIGHT,
V.steps[V.KNIGHT]
);
);
}
- isAttacked(sq, colors) {
+ isAttacked(sq, color) {
return (
- super.isAttacked(sq, colors) ||
- this.isAttackedByMarshall(sq, colors) ||
- this.isAttackedByCardinal(sq, colors)
+ super.isAttacked(sq, color) ||
+ this.isAttackedByMarshall(sq, color) ||
+ this.isAttackedByCardinal(sq, color)
);
}
- isAttackedByMarshall(sq, colors) {
+ isAttackedByMarshall(sq, color) {
return (
- this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.ROOK]) ||
+ this.isAttackedBySlideNJump(sq, color, V.MARSHALL, V.steps[V.ROOK]) ||
this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.MARSHALL,
V.steps[V.KNIGHT],
"oneStep"
);
}
- isAttackedByCardinal(sq, colors) {
+ isAttackedByCardinal(sq, color) {
return (
- this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.BISHOP]) ||
+ this.isAttackedBySlideNJump(sq, color, V.CARDINAL, V.steps[V.BISHOP]) ||
this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.CARDINAL,
V.steps[V.KNIGHT],
"oneStep"
return moves;
}
- isAttacked(sq, colors) {
+ isAttacked(sq, color) {
return (
- super.isAttacked(sq, colors) ||
- this.isAttackedByGrasshopper(sq, colors)
+ super.isAttacked(sq, color) ||
+ this.isAttackedByGrasshopper(sq, color)
);
}
- isAttackedByGrasshopper([x, y], colors) {
+ isAttackedByGrasshopper([x, y], color) {
// Reversed process: is there an adjacent obstacle,
// and a grasshopper next in the same line?
for (const step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
if (
V.OnBoard(i, j) &&
this.getPiece(i, j) == V.GRASSHOPPER &&
- colors.includes(this.getColor(i, j))
+ this.getColor(i, j) == color
) {
return true;
}
return super.getPotentialKnightMoves(sq).concat(super.getCastleMoves(sq));
}
- isAttacked(sq, colors) {
+ isAttacked(sq, color) {
return (
- this.isAttackedByCommoner(sq, colors) ||
- this.isAttackedByPawn(sq, colors) ||
- this.isAttackedByRook(sq, colors) ||
- this.isAttackedByBishop(sq, colors) ||
- this.isAttackedByQueen(sq, colors) ||
- this.isAttackedByKing(sq, colors)
+ this.isAttackedByCommoner(sq, color) ||
+ this.isAttackedByPawn(sq, color) ||
+ this.isAttackedByRook(sq, color) ||
+ this.isAttackedByBishop(sq, color) ||
+ this.isAttackedByQueen(sq, color) ||
+ this.isAttackedByKing(sq, color)
);
}
- isAttackedByKing(sq, colors) {
+ isAttackedByKing(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.KING,
V.steps[V.KNIGHT],
"oneStep"
);
}
- isAttackedByCommoner(sq, colors) {
+ isAttackedByCommoner(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.COMMONER,
V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
"oneStep"
--- /dev/null
+import { ChessRules } from "@/base_rules";
+
+export const VariantRules = class Knightrelay1Rules extends ChessRules {
+ static get HasEnpassant() {
+ return false;
+ }
+
+ getPotentialMovesFrom([x, y]) {
+ let moves = super.getPotentialMovesFrom([x, y]);
+
+ // Expand possible moves if guarded by a knight, and is not a king:
+ const piece = this.getPiece(x,y);
+ if (![V.KNIGHT,V.KING].includes(piece)) {
+ const color = this.turn;
+ let guardedByKnight = false;
+ for (const step of V.steps[V.KNIGHT]) {
+ if (
+ V.OnBoard(x+step[0],y+step[1]) &&
+ this.getPiece(x+step[0],y+step[1]) == V.KNIGHT &&
+ this.getColor(x+step[0],y+step[1]) == color
+ ) {
+ guardedByKnight = true;
+ break;
+ }
+ }
+ if (guardedByKnight) {
+ const lastRank = (color == "w" ? 0 : V.size.x - 1);
+ for (const step of V.steps[V.KNIGHT]) {
+ if (
+ V.OnBoard(x+step[0],y+step[1]) &&
+ this.getColor(x+step[0],y+step[1]) != color &&
+ // Pawns cannot promote by knight-relay
+ (piece != V.PAWN || x+step[0] != lastRank)
+ ) {
+ moves.push(this.getBasicMove([x,y], [x+step[0],y+step[1]]));
+ }
+ }
+ }
+ }
+
+ // Forbid captures of knights (invincible in this variant)
+ return moves.filter(m => {
+ return (
+ m.vanish.length == 1 ||
+ m.appear.length == 2 ||
+ m.vanish[1].p != V.KNIGHT
+ );
+ });
+ }
+
+ getPotentialKnightMoves(sq) {
+ // Knights don't capture:
+ return super.getPotentialKnightMoves(sq).filter(m => m.vanish.length == 1);
+ }
+
+ isAttacked(sq, color) {
+ if (super.isAttacked(sq, color)) return true;
+
+ // Check if a (non-knight) piece at knight distance
+ // is guarded by a knight (and thus attacking)
+ // --> Except for pawns targetting last rank.
+ const x = sq[0],
+ y = sq[1];
+ // Last rank for me, that is to say oppCol of color:
+ const lastRank = (color == 'w' ? V.size.x - 1 : 0);
+ for (const step of V.steps[V.KNIGHT]) {
+ if (
+ V.OnBoard(x+step[0],y+step[1]) &&
+ this.getColor(x+step[0],y+step[1]) == color
+ ) {
+ const piece = this.getPiece(x+step[0],y+step[1]);
+ if (piece != V.KNIGHT && (piece != V.PAWN || x != lastRank)) {
+ for (const step2 of V.steps[V.KNIGHT]) {
+ const xx = x+step[0]+step2[0],
+ yy = y+step[1]+step2[1];
+ if (
+ V.OnBoard(xx,yy) &&
+ this.getColor(xx,yy) == color &&
+ this.getPiece(xx,yy) == V.KNIGHT
+ ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ isAttackedByKnight(sq, color) {
+ // Knights don't attack
+ return false;
+ }
+
+ static get VALUES() {
+ return {
+ p: 1,
+ r: 5,
+ n: 7, //the knight is valuable
+ b: 3,
+ q: 9,
+ k: 1000
+ };
+ }
+
+ static get SEARCH_DEPTH() {
+ return 2;
+ }
+
+ getNotation(move) {
+ if (move.appear.length == 2 && move.appear[0].p == V.KING)
+ // Castle
+ return move.end.y < move.start.y ? "0-0-0" : "0-0";
+
+ // Translate final and initial square
+ const initSquare = V.CoordsToSquare(move.start);
+ const finalSquare = V.CoordsToSquare(move.end);
+ const piece = this.getPiece(move.start.x, move.start.y);
+
+ // Since pieces and pawns could move like knight, indicate start and end squares
+ let notation =
+ piece.toUpperCase() +
+ initSquare +
+ (move.vanish.length > move.appear.length ? "x" : "") +
+ finalSquare
+
+ if (
+ piece == V.PAWN &&
+ move.appear.length > 0 &&
+ move.appear[0].p != V.PAWN
+ ) {
+ // Promotion
+ notation += "=" + move.appear[0].p.toUpperCase();
+ }
+
+ return notation;
+ }
+};
import { ChessRules } from "@/base_rules";
-export const VariantRules = class KnightrelayRules extends ChessRules {
+export const VariantRules = class Knightrelay2Rules extends ChessRules {
getPotentialMovesFrom([x, y]) {
let moves = super.getPotentialMovesFrom([x, y]);
return moves;
}
- isAttacked(sq, colors) {
- if (super.isAttacked(sq, colors))
- return true;
+ isAttacked(sq, color) {
+ if (super.isAttacked(sq, color)) return true;
// Check if a (non-knight) piece at knight distance
// is guarded by a knight (and thus attacking)
for (const step of V.steps[V.KNIGHT]) {
if (
V.OnBoard(x+step[0],y+step[1]) &&
- colors.includes(this.getColor(x+step[0],y+step[1])) &&
+ this.getColor(x+step[0],y+step[1]) == color &&
this.getPiece(x+step[0],y+step[1]) != V.KNIGHT
) {
for (const step2 of V.steps[V.KNIGHT]) {
yy = y+step[1]+step2[1];
if (
V.OnBoard(xx,yy) &&
- colors.includes(this.getColor(xx,yy)) &&
+ this.getColor(xx,yy) == color &&
this.getPiece(xx,yy) == V.KNIGHT
) {
return true;
});
}
- isAttackedByPawn([x, y], colors) {
- const pawnShift = 1;
- if (x + pawnShift < V.size.x) {
- for (let c of colors) {
+ isAttackedByPawn([x, y], color) {
+ // Pawns can capture forward and backward:
+ for (let pawnShift of [-1, 1]) {
+ if (0 < x + pawnShift && x + pawnShift < V.size.x) {
for (let i of [-1, 1]) {
if (
y + i >= 0 &&
y + i < V.size.y &&
this.getPiece(x + pawnShift, y + i) == V.PAWN &&
- this.getColor(x + pawnShift, y + i) == c
+ this.getColor(x + pawnShift, y + i) == color
) {
return true;
}
return false;
}
- isAttackedByKnight(sq, colors) {
+ isAttackedByKnight(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.KNIGHT,
V.steps[V.KNIGHT]
);
);
}
- isAttackedByBishop(sq, colors) {
+ isAttackedByBishop(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.BISHOP,
V.ElephantSteps,
"oneStep"
);
}
- isAttackedByQueen(sq, colors) {
+ isAttackedByQueen(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.QUEEN,
V.steps[V.BISHOP],
"oneStep"
// 2 enemy units or more: I lose
return getScoreLost();
// I don't have any piece, my opponent have one: can I take it?
- if (this.isAttacked(piecesLeft[oppCol].square, [color]))
+ if (this.isAttacked(piecesLeft[oppCol].square, color))
// Yes! But I still need to take it
return "*";
// No :(
);
}
- isAttacked(sq, colors) {
+ isAttacked(sq, color) {
return (
- super.isAttacked(sq, colors) ||
- this.isAttackedByCamel(sq, colors) ||
- this.isAttackedByWildebeest(sq, colors)
+ super.isAttacked(sq, color) ||
+ this.isAttackedByCamel(sq, color) ||
+ this.isAttackedByWildebeest(sq, color)
);
}
- isAttackedByCamel(sq, colors) {
+ isAttackedByCamel(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.CAMEL,
V.steps[V.CAMEL],
"oneStep"
);
}
- isAttackedByWildebeest(sq, colors) {
+ isAttackedByWildebeest(sq, color) {
return this.isAttackedBySlideNJump(
sq,
- colors,
+ color,
V.WILDEBEEST,
V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]),
"oneStep"
return this.getJumpMoves(sq, V.steps[V.KING]);
}
- isAttackedByJump([x, y], colors, piece, steps) {
+ isAttackedByJump([x, y], color, piece, steps) {
for (let step of steps) {
const sq = this.getSquareAfter([x,y], step);
if (
sq &&
- this.getPiece(sq[0], sq[1]) === piece &&
- colors.includes(this.getColor(sq[0], sq[1]))
+ this.getPiece(sq[0], sq[1]) == piece &&
+ this.getColor(sq[0], sq[1]) == color
) {
return true;
}
return false;
}
- isAttackedByPawn([x, y], colors) {
- for (let c of colors) {
- const pawnShift = c == "w" ? 1 : -1;
- for (let i of [-1, 1]) {
- const sq = this.getSquareAfter([x,y], [pawnShift,i]);
- if (
- sq &&
- this.getPiece(sq[0], sq[1]) == V.PAWN &&
- this.getColor(sq[0], sq[1]) == c
- ) {
- return true;
- }
+ isAttackedByPawn([x, y], color) {
+ const pawnShift = (color == "w" ? 1 : -1);
+ for (let i of [-1, 1]) {
+ const sq = this.getSquareAfter([x,y], [pawnShift,i]);
+ if (
+ sq &&
+ this.getPiece(sq[0], sq[1]) == V.PAWN &&
+ this.getColor(sq[0], sq[1]) == color
+ ) {
+ return true;
}
}
return false;
}
- isAttackedByRook(sq, colors) {
- return this.isAttackedByJump(sq, colors, V.ROOK, V.steps[V.ROOK]);
+ isAttackedByRook(sq, color) {
+ return this.isAttackedByJump(sq, color, V.ROOK, V.steps[V.ROOK]);
}
- isAttackedByKnight(sq, colors) {
+ isAttackedByKnight(sq, color) {
// NOTE: knight attack is not symmetric in this variant:
// steps order need to be reversed.
return this.isAttackedByJump(
sq,
- colors,
+ color,
V.KNIGHT,
V.steps[V.KNIGHT].map(s => s.reverse())
);
}
- isAttackedByBishop(sq, colors) {
- return this.isAttackedByJump(sq, colors, V.BISHOP, V.steps[V.BISHOP]);
+ isAttackedByBishop(sq, color) {
+ return this.isAttackedByJump(sq, color, V.BISHOP, V.steps[V.BISHOP]);
}
- isAttackedByQueen(sq, colors) {
+ isAttackedByQueen(sq, color) {
return this.isAttackedByJump(
sq,
- colors,
+ color,
V.QUEEN,
V.steps[V.ROOK].concat(V.steps[V.BISHOP])
);
}
- isAttackedByKing(sq, colors) {
- return this.isAttackedByJump(sq, colors, V.KING, V.steps[V.KING]);
+ isAttackedByKing(sq, color) {
+ return this.isAttackedByJump(sq, color, V.KING, V.steps[V.KING]);
}
// NOTE: altering move in getBasicMove doesn't work and wouldn't be logical.
cursor: this.cursor
},
success: (res) => {
- if (res.games.length > 0) {
- const L = res.games.length;
+ const L = res.games.length;
+ if (L > 0) {
this.cursor = res.games[L - 1].created;
let moreGames = res.games.map(g => {
const vname = this.getVname(g.vid);
credentials: true,
data: { cursor: this.cursor },
success: (res2) => {
- if (res2.games.length > 0) {
- const L = res2.games.length;
+ const L = res2.games.length;
+ if (L > 0) {
this.cursor = res2.games[L - 1].created;
let completedGames = res2.games;
completedGames.forEach(g => g.type = "corr");
credentials: true,
data: { cursor: this.cursor },
success: (res) => {
- if (res.games.length > 0) {
- const L = res.games.length;
+ const L = res.games.length;
+ if (L > 0) {
this.cursor = res.games[L - 1].created;
let moreGames = res.games;
moreGames.forEach(g => g.type = "corr");
this.newsList = res.newsList;
const L = res.newsList.length;
if (L > 0) this.cursor = res.newsList[L - 1].added;
+ else this.hasMore = false;
}
}
);
{
data: { cursor: this.cursor },
success: (res) => {
- if (res.newsList.length > 0) {
+ const L = res.newsList.length;
+ if (L > 0) {
this.newsList = this.newsList.concat(res.newsList);
- const L = res.newsList.length;
- if (L > 0) this.cursor = res.newsList[L - 1].added;
+ this.cursor = res.newsList[L - 1].added;
} else this.hasMore = false;
}
}
td {{ p.vname }}
td {{ firstChars(p.instruction) }}
td {{ p.id }}
+ button#loadMoreBtn(
+ v-if="hasMore"
+ @click="loadMore()"
+ )
+ | {{ st.tr["Load more"] }}
BaseGame(
ref="basegame"
v-if="showOne"
loadedVar: 0, //corresponding to loaded V
selectedVar: 0, //to filter problems based on variant
problems: [],
+ // timestamp of oldest showed problem:
+ cursor: Number.MAX_SAFE_INTEGER,
+ // hasMore == TRUE: a priori there could be more problems to load
+ hasMore: true,
onlyMines: false,
showOne: false,
infoMsg: "",
"/problems",
"GET",
{
+ data: { cursor: this.cursor },
success: (res) => {
- // Show newest problem first:
- this.problems = res.problems.sort((p1, p2) => p2.added - p1.added);
- if (this.st.variants.length > 0)
- this.problems.forEach(p => this.setVname(p));
- // Retrieve all problems' authors' names
- let names = {};
- this.problems.forEach(p => {
- if (p.uid != this.st.user.id) names[p.uid] = "";
- else p.uname = this.st.user.name;
- });
+ // The returned list is sorted from most recent to oldest
+ this.problems = res.problems;
+ const L = res.problems.length;
+ if (L > 0) this.cursor = res.problems[L - 1].added;
+ else this.hasMore = false;
const showOneIfPid = () => {
const pid = this.$route.query["id"];
if (!!pid) this.showProblem(this.problems.find(p => p.id == pid));
};
- if (Object.keys(names).length > 0) {
- ajax(
- "/users",
- "GET",
- {
- data: { ids: Object.keys(names).join(",") },
- success: (res2) => {
- res2.users.forEach(u => {
- names[u.id] = u.name;
- });
- this.problems.forEach(p => {
- if (!p.uname)
- p.uname = names[p.uid];
- });
- showOneIfPid();
- }
- }
- );
- } else showOneIfPid();
+ this.decorate(this.problems, showOneIfPid);
}
}
);
setVname: function(prob) {
prob.vname = this.st.variants.find(v => v.id == prob.vid).name;
},
+ // Add vname and user names:
+ decorate: function(problems, callback) {
+ if (this.st.variants.length > 0)
+ problems.forEach(p => this.setVname(p));
+ // Retrieve all problems' authors' names
+ let names = {};
+ problems.forEach(p => {
+ if (p.uid != this.st.user.id) names[p.uid] = "";
+ else p.uname = this.st.user.name;
+ });
+ if (Object.keys(names).length > 0) {
+ ajax(
+ "/users",
+ "GET",
+ {
+ data: { ids: Object.keys(names).join(",") },
+ success: (res2) => {
+ res2.users.forEach(u => {
+ names[u.id] = u.name;
+ });
+ problems.forEach(p => {
+ if (!p.uname)
+ p.uname = names[p.uid];
+ });
+ if (!!callback) callback();
+ }
+ }
+ );
+ } else if (!!callback) callback();
+ },
firstChars: function(text) {
let preparedText = text
// Replace line jumps and <br> by spaces
}
);
}
+ },
+ loadMore: function() {
+ ajax(
+ "/problems",
+ "GET",
+ {
+ data: { cursor: this.cursor },
+ success: (res) => {
+ const L = res.problems.length;
+ if (L > 0) {
+ this.decorate(res.problems);
+ this.problems = this.problems.concat(res.problems);
+ this.cursor = res.problems[L - 1].added;
+ } else this.hasMore = false;
+ }
+ }
+ );
}
}
};
table#tProblems
max-height: 100%
+button#loadMoreBtn
+ display: block
+ margin: 0 auto
+
#controls
margin: 0
width: 100%
('Hidden', 'Unidentified pieces'),
('Hiddenqueen', 'Queen disguised as a pawn'),
('Knightmate', 'Mate the knight'),
- ('Knightrelay', 'The knight transfers its powers'),
+ ('Knightrelay1', 'Move like a knight (v1)'),
+ ('Knightrelay2', 'Move like a knight (v2)'),
('Losers', 'Get strong at self-mate'),
('Magnetic', 'Laws of attraction'),
('Marseille', 'Move twice'),
"WHERE added < " + cursor + " " +
"ORDER BY added DESC " +
"LIMIT 10"; //TODO: 10 currently hard-coded
- db.all(query, (err,newsList) => {
+ db.all(query, (err, newsList) => {
cb(err, newsList);
});
});
"FROM News " +
"ORDER BY added DESC " +
"LIMIT 1";
- db.get(query, (err,ts) => {
+ db.get(query, (err, ts) => {
cb(err, ts);
});
});
});
},
- getAll: function(cb) {
+ getNext: function(cursor, cb) {
db.serialize(function() {
const query =
"SELECT * " +
- "FROM Problems";
- db.all(query, (err,problems) => {
+ "FROM Problems " +
+ "WHERE added < " + cursor + " " +
+ "ORDER BY added DESC " +
+ "LIMIT 20"; //TODO: 20 is arbitrary
+ db.all(query, (err, problems) => {
cb(err, problems);
});
});
"SELECT * " +
"FROM Problems " +
"WHERE id = " + id;
- db.get(query, (err,problem) => {
+ db.get(query, (err, problem) => {
cb(err, problem);
});
});
"instruction = ?," +
"solution = ? " +
"WHERE id = " + prob.id + " AND uid = " + uid;
- db.run(query, [prob.instruction,prob.solution]);
+ db.run(query, [prob.instruction, prob.solution]);
});
},
router.get("/news", access.ajax, (req,res) => {
const cursor = req.query["cursor"];
- if (!!cursor.match(/^[0-9]+$/)) {
+ if (!!cursor && !!cursor.match(/^[0-9]+$/)) {
NewsModel.getNext(cursor, (err, newsList) => {
res.json(err || { newsList: newsList });
});
router.get("/problems", access.ajax, (req,res) => {
const probId = req.query["pid"];
- if (probId && probId.match(/^[0-9]+$/)) {
- ProblemModel.getOne(req.query["pid"], (err,problem) => {
+ const cursor = req.query["cursor"];
+ if (!!probId && !!probId.match(/^[0-9]+$/)) {
+ ProblemModel.getOne(req.query["pid"], (err, problem) => {
res.json(err || {problem: problem});
});
- } else {
- ProblemModel.getAll((err,problems) => {
+ } else if (!!cursor && !!cursor.match(/^[0-9]+$/)) {
+ ProblemModel.getNext(cursor, (err, problems) => {
res.json(err || { problems: problems });
});
}