"devDependencies": {
"@vue/cli-plugin-eslint": "^3.12.1",
"@vue/cli-service": "^4.2.2",
- "@vue/eslint-config-prettier": "^4.0.1",
"ajv": "^6.11.0",
"apply-loader": "^2.0.0",
"babel-eslint": "^10.0.3",
},
"extends": [
"plugin:vue/essential",
- "@vue/prettier"
+ "eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
+ },
+ "globals": {
+ "V": "readonly"
+ },
+ "rules": {
+ "consistent-return": 2,
+ "indent": [
+ "error",
+ 2,
+ {
+ "SwitchCase": 1,
+ "VariableDeclarator": "first",
+ "FunctionExpression": {
+ "parameters": "first"
+ },
+ "CallExpression": {
+ "arguments": "first"
+ },
+ "flatTernaryExpressions": true
+ }
+ ],
+ "no-else-return" : [
+ 1,
+ {
+ "allowElseIf": false
+ }
+ ],
+ "semi" : [1, "always"]
}
},
"postcss": {
ContactForm,
Language,
Settings,
- UpsertUser,
+ UpsertUser
},
data: function() {
return {
- st: store.state,
+ st: store.state
};
},
computed: {
flagImage: function() {
return `/images/flags/${this.st.lang}.svg`;
- },
+ }
},
mounted: function() {
let dialogs = document.querySelectorAll("div[role='dialog']");
},
methods: {
hideDrawer: function(e) {
- if (e.target.innerText == "Forum")
- return; //external link
+ if (e.target.innerText == "Forum") return; //external link
e.preventDefault(); //TODO: why is this needed?
document.getElementsByClassName("drawer")[0].checked = false;
- },
- },
+ }
+ }
};
</script>
import { ArrayFun } from "@/utils/array";
import { randInt, shuffle } from "@/utils/alea";
-export const PiPo = class PiPo //Piece+Position
-{
+export const PiPo = class PiPo {
+ //Piece+Position
// o: {piece[p], color[c], posX[x], posY[y]}
- constructor(o)
- {
+ constructor(o) {
this.p = o.p;
this.c = o.c;
this.x = o.x;
this.y = o.y;
}
-}
+};
// TODO: for animation, moves should contains "moving" and "fading" maybe...
-export const Move = class Move
-{
+export const Move = class Move {
// o: {appear, vanish, [start,] [end,]}
// appear,vanish = arrays of PiPo
// start,end = coordinates to apply to trigger move visually (think castle)
- constructor(o)
- {
+ constructor(o) {
this.appear = o.appear;
this.vanish = o.vanish;
- this.start = !!o.start ? o.start : {x:o.vanish[0].x, y:o.vanish[0].y};
- this.end = !!o.end ? o.end : {x:o.appear[0].x, y:o.appear[0].y};
+ this.start = o.start ? o.start : { x: o.vanish[0].x, y: o.vanish[0].y };
+ this.end = o.end ? o.end : { x: o.appear[0].x, y: o.appear[0].y };
}
-}
+};
// NOTE: x coords = top to bottom; y = left to right (from white player perspective)
-export const ChessRules = class ChessRules
-{
+export const ChessRules = class ChessRules {
//////////////
// MISC UTILS
- static get HasFlags() { return true; } //some variants don't have flags
+ static get HasFlags() {
+ return true;
+ } //some variants don't have flags
- static get HasEnpassant() { return true; } //some variants don't have ep.
+ static get HasEnpassant() {
+ return true;
+ } //some variants don't have ep.
// Path to pieces
- static getPpath(b)
- {
+ static getPpath(b) {
return b; //usual pieces in pieces/ folder
}
// Turn "wb" into "B" (for FEN)
- static board2fen(b)
- {
- return (b[0]=='w' ? b[1].toUpperCase() : b[1]);
+ static board2fen(b) {
+ return b[0] == "w" ? b[1].toUpperCase() : b[1];
}
// Turn "p" into "bp" (for board)
- static fen2board(f)
- {
- return (f.charCodeAt()<=90 ? "w"+f.toLowerCase() : "b"+f);
+ static fen2board(f) {
+ return f.charCodeAt() <= 90 ? "w" + f.toLowerCase() : "b" + f;
}
// Check if FEN describe a board situation correctly
- static IsGoodFen(fen)
- {
+ static IsGoodFen(fen) {
const fenParsed = V.ParseFen(fen);
// 1) Check position
- if (!V.IsGoodPosition(fenParsed.position))
- return false;
+ if (!V.IsGoodPosition(fenParsed.position)) return false;
// 2) Check turn
- if (!fenParsed.turn || !V.IsGoodTurn(fenParsed.turn))
- return false;
+ if (!fenParsed.turn || !V.IsGoodTurn(fenParsed.turn)) return false;
// 3) Check moves count
if (!fenParsed.movesCount || !(parseInt(fenParsed.movesCount) >= 0))
return false;
if (V.HasFlags && (!fenParsed.flags || !V.IsGoodFlags(fenParsed.flags)))
return false;
// 5) Check enpassant
- if (V.HasEnpassant &&
- (!fenParsed.enpassant || !V.IsGoodEnpassant(fenParsed.enpassant)))
- {
+ if (
+ V.HasEnpassant &&
+ (!fenParsed.enpassant || !V.IsGoodEnpassant(fenParsed.enpassant))
+ ) {
return false;
}
return true;
}
// Is position part of the FEN a priori correct?
- static IsGoodPosition(position)
- {
- if (position.length == 0)
- return false;
+ static IsGoodPosition(position) {
+ if (position.length == 0) return false;
const rows = position.split("/");
- if (rows.length != V.size.x)
- return false;
- for (let row of rows)
- {
+ if (rows.length != V.size.x) return false;
+ for (let row of rows) {
let sumElts = 0;
- for (let i=0; i<row.length; i++)
- {
- if (V.PIECES.includes(row[i].toLowerCase()))
- sumElts++;
- else
- {
+ for (let i = 0; i < row.length; i++) {
+ if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
+ else {
const num = parseInt(row[i]);
- if (isNaN(num))
- return false;
+ if (isNaN(num)) return false;
sumElts += num;
}
}
- if (sumElts != V.size.y)
- return false;
+ if (sumElts != V.size.y) return false;
}
return true;
}
// For FEN checking
- static IsGoodTurn(turn)
- {
- return ["w","b"].includes(turn);
+ static IsGoodTurn(turn) {
+ return ["w", "b"].includes(turn);
}
// For FEN checking
- static IsGoodFlags(flags)
- {
+ static IsGoodFlags(flags) {
return !!flags.match(/^[01]{4,4}$/);
}
- static IsGoodEnpassant(enpassant)
- {
- if (enpassant != "-")
- {
- const ep = V.SquareToCoords(fenParsed.enpassant);
- if (isNaN(ep.x) || !V.OnBoard(ep))
- return false;
+ static IsGoodEnpassant(enpassant) {
+ if (enpassant != "-") {
+ const ep = V.SquareToCoords(enpassant);
+ if (isNaN(ep.x) || !V.OnBoard(ep)) return false;
}
return true;
}
// 3 --> d (column number to letter)
- static CoordToColumn(colnum)
- {
+ static CoordToColumn(colnum) {
return String.fromCharCode(97 + colnum);
}
// d --> 3 (column letter to number)
- static ColumnToCoord(column)
- {
+ static ColumnToCoord(column) {
return column.charCodeAt(0) - 97;
}
// a4 --> {x:3,y:0}
- static SquareToCoords(sq)
- {
+ static SquareToCoords(sq) {
return {
// NOTE: column is always one char => max 26 columns
// row is counted from black side => subtraction
}
// {x:0,y:4} --> e8
- static CoordsToSquare(coords)
- {
+ static CoordsToSquare(coords) {
return V.CoordToColumn(coords.y) + (V.size.x - coords.x);
}
// Aggregates flags into one object
- aggregateFlags()
- {
+ aggregateFlags() {
return this.castleFlags;
}
// Reverse operation
- disaggregateFlags(flags)
- {
+ disaggregateFlags(flags) {
this.castleFlags = flags;
}
// En-passant square, if any
- getEpSquare(moveOrSquare)
- {
- if (!moveOrSquare)
- return undefined;
- if (typeof moveOrSquare === "string")
- {
+ getEpSquare(moveOrSquare) {
+ if (!moveOrSquare) return undefined;
+ if (typeof moveOrSquare === "string") {
const square = moveOrSquare;
- if (square == "-")
- return undefined;
+ if (square == "-") return undefined;
return V.SquareToCoords(square);
}
// Argument is a move:
const move = moveOrSquare;
- const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
+ const [sx, sy, ex] = [move.start.x, move.start.y, move.end.x];
// NOTE: next conditions are first for Atomic, and last for Checkered
- if (move.appear.length > 0 && Math.abs(sx - ex) == 2
- && move.appear[0].p == V.PAWN && ["w","b"].includes(move.appear[0].c))
- {
+ if (
+ move.appear.length > 0 &&
+ Math.abs(sx - ex) == 2 &&
+ move.appear[0].p == V.PAWN &&
+ ["w", "b"].includes(move.appear[0].c)
+ ) {
return {
- x: (sx + ex)/2,
+ x: (sx + ex) / 2,
y: sy
};
}
}
// Can thing on square1 take thing on square2
- canTake([x1,y1], [x2,y2])
- {
- return this.getColor(x1,y1) !== this.getColor(x2,y2);
+ canTake([x1, y1], [x2, y2]) {
+ return this.getColor(x1, y1) !== this.getColor(x2, y2);
}
// Is (x,y) on the chessboard?
- static OnBoard(x,y)
- {
- return (x>=0 && x<V.size.x && y>=0 && y<V.size.y);
+ static OnBoard(x, y) {
+ return x >= 0 && x < V.size.x && y >= 0 && y < V.size.y;
}
// Used in interface: 'side' arg == player color
- canIplay(side, [x,y])
- {
- return (this.turn == side && this.getColor(x,y) == side);
+ canIplay(side, [x, y]) {
+ return this.turn == side && this.getColor(x, y) == side;
}
// On which squares is color under check ? (for interface)
- getCheckSquares(color)
- {
+ getCheckSquares(color) {
return this.isAttacked(this.kingPos[color], [V.GetOppCol(color)])
? [JSON.parse(JSON.stringify(this.kingPos[color]))] //need to duplicate!
: [];
// FEN UTILS
// Setup the initial random (assymetric) position
- static GenRandInitFen()
- {
- let pieces = { "w": new Array(8), "b": new Array(8) };
+ static GenRandInitFen() {
+ let pieces = { w: new Array(8), b: new Array(8) };
// Shuffle pieces on first and last rank
- for (let c of ["w","b"])
- {
+ for (let c of ["w", "b"]) {
let positions = ArrayFun.range(8);
// Get random squares for bishops
let randIndex_tmp = 2 * randInt(4) + 1;
const bishop2Pos = positions[randIndex_tmp];
// Remove chosen squares
- positions.splice(Math.max(randIndex,randIndex_tmp), 1);
- positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+ positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+ positions.splice(Math.min(randIndex, randIndex_tmp), 1);
// Get random squares for knights
randIndex = randInt(6);
const rook2Pos = positions[2];
// Finally put the shuffled pieces in the board array
- pieces[c][rook1Pos] = 'r';
- pieces[c][knight1Pos] = 'n';
- pieces[c][bishop1Pos] = 'b';
- pieces[c][queenPos] = 'q';
- pieces[c][kingPos] = 'k';
- pieces[c][bishop2Pos] = 'b';
- pieces[c][knight2Pos] = 'n';
- pieces[c][rook2Pos] = 'r';
+ pieces[c][rook1Pos] = "r";
+ pieces[c][knight1Pos] = "n";
+ pieces[c][bishop1Pos] = "b";
+ pieces[c][queenPos] = "q";
+ pieces[c][kingPos] = "k";
+ pieces[c][bishop2Pos] = "b";
+ pieces[c][knight2Pos] = "n";
+ pieces[c][rook2Pos] = "r";
}
- return pieces["b"].join("") +
+ return (
+ pieces["b"].join("") +
"/pppppppp/8/8/8/8/PPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
- " w 0 1111 -"; //add turn + flags + enpassant
+ " w 0 1111 -"
+ ); //add turn + flags + enpassant
}
// "Parse" FEN: just return untransformed string data
- static ParseFen(fen)
- {
+ static ParseFen(fen) {
const fenParts = fen.split(" ");
- let res =
- {
+ let res = {
position: fenParts[0],
turn: fenParts[1],
- movesCount: fenParts[2],
+ movesCount: fenParts[2]
};
let nextIdx = 3;
- if (V.HasFlags)
- Object.assign(res, {flags: fenParts[nextIdx++]});
- if (V.HasEnpassant)
- Object.assign(res, {enpassant: fenParts[nextIdx]});
+ if (V.HasFlags) Object.assign(res, { flags: fenParts[nextIdx++] });
+ if (V.HasEnpassant) Object.assign(res, { enpassant: fenParts[nextIdx] });
return res;
}
// Return current fen (game state)
- getFen()
- {
- return this.getBaseFen() + " " +
- this.getTurnFen() + " " + this.movesCount +
- (V.HasFlags ? (" " + this.getFlagsFen()) : "") +
- (V.HasEnpassant ? (" " + this.getEnpassantFen()) : "");
+ getFen() {
+ return (
+ this.getBaseFen() +
+ " " +
+ this.getTurnFen() +
+ " " +
+ this.movesCount +
+ (V.HasFlags ? " " + this.getFlagsFen() : "") +
+ (V.HasEnpassant ? " " + this.getEnpassantFen() : "")
+ );
}
// Position part of the FEN string
- getBaseFen()
- {
+ getBaseFen() {
let position = "";
- for (let i=0; i<V.size.x; i++)
- {
+ for (let i = 0; i < V.size.x; i++) {
let emptyCount = 0;
- for (let j=0; j<V.size.y; j++)
- {
- if (this.board[i][j] == V.EMPTY)
- emptyCount++;
- else
- {
- if (emptyCount > 0)
- {
+ for (let j = 0; j < V.size.y; j++) {
+ if (this.board[i][j] == V.EMPTY) emptyCount++;
+ else {
+ if (emptyCount > 0) {
// Add empty squares in-between
position += emptyCount;
emptyCount = 0;
position += V.board2fen(this.board[i][j]);
}
}
- if (emptyCount > 0)
- {
+ if (emptyCount > 0) {
// "Flush remainder"
position += emptyCount;
}
- if (i < V.size.x - 1)
- position += "/"; //separate rows
+ if (i < V.size.x - 1) position += "/"; //separate rows
}
return position;
}
- getTurnFen()
- {
+ getTurnFen() {
return this.turn;
}
// Flags part of the FEN string
- getFlagsFen()
- {
+ getFlagsFen() {
let flags = "";
// Add castling flags
- for (let i of ['w','b'])
- {
- for (let j=0; j<2; j++)
- flags += (this.castleFlags[i][j] ? '1' : '0');
+ for (let i of ["w", "b"]) {
+ for (let j = 0; j < 2; j++) flags += this.castleFlags[i][j] ? "1" : "0";
}
return flags;
}
// Enpassant part of the FEN string
- getEnpassantFen()
- {
+ getEnpassantFen() {
const L = this.epSquares.length;
- if (!this.epSquares[L-1])
- return "-"; //no en-passant
- return V.CoordsToSquare(this.epSquares[L-1]);
+ if (!this.epSquares[L - 1]) return "-"; //no en-passant
+ return V.CoordsToSquare(this.epSquares[L - 1]);
}
// Turn position fen into double array ["wb","wp","bk",...]
- static GetBoard(position)
- {
+ static GetBoard(position) {
const rows = position.split("/");
let board = ArrayFun.init(V.size.x, V.size.y, "");
- for (let i=0; i<rows.length; i++)
- {
+ for (let i = 0; i < rows.length; i++) {
let j = 0;
- for (let indexInRow = 0; indexInRow < rows[i].length; indexInRow++)
- {
+ for (let indexInRow = 0; indexInRow < rows[i].length; indexInRow++) {
const character = rows[i][indexInRow];
const num = parseInt(character);
- if (!isNaN(num))
- j += num; //just shift j
- else //something at position i,j
- board[i][j++] = V.fen2board(character);
+ if (!isNaN(num)) j += num;
+ //just shift j
+ //something at position i,j
+ else board[i][j++] = V.fen2board(character);
}
}
return board;
}
// Extract (relevant) flags from fen
- setFlags(fenflags)
- {
+ setFlags(fenflags) {
// white a-castle, h-castle, black a-castle, h-castle
- this.castleFlags = {'w': [true,true], 'b': [true,true]};
- if (!fenflags)
- return;
- for (let i=0; i<4; i++)
- this.castleFlags[i < 2 ? 'w' : 'b'][i%2] = (fenflags.charAt(i) == '1');
+ this.castleFlags = { w: [true, true], b: [true, true] };
+ if (!fenflags) return;
+ for (let i = 0; i < 4; i++)
+ this.castleFlags[i < 2 ? "w" : "b"][i % 2] = fenflags.charAt(i) == "1";
}
//////////////////
// INITIALIZATION
- constructor(fen)
- {
+ constructor(fen) {
this.re_init(fen);
}
// Fen string fully describes the game state
- re_init(fen)
- {
+ re_init(fen) {
const fenParsed = V.ParseFen(fen);
this.board = V.GetBoard(fenParsed.position);
this.turn = fenParsed.turn[0]; //[0] to work with MarseilleRules
}
// Scan board for kings and rooks positions
- scanKingsRooks(fen)
- {
- this.INIT_COL_KING = {'w':-1, 'b':-1};
- this.INIT_COL_ROOK = {'w':[-1,-1], 'b':[-1,-1]};
- this.kingPos = {'w':[-1,-1], 'b':[-1,-1]}; //squares of white and black king
+ scanKingsRooks(fen) {
+ this.INIT_COL_KING = { w: -1, b: -1 };
+ this.INIT_COL_ROOK = { w: [-1, -1], b: [-1, -1] };
+ this.kingPos = { w: [-1, -1], b: [-1, -1] }; //squares of white and black king
const fenRows = V.ParseFen(fen).position.split("/");
- for (let i=0; i<fenRows.length; i++)
- {
+ for (let i = 0; i < fenRows.length; i++) {
let k = 0; //column index on board
- for (let j=0; j<fenRows[i].length; j++)
- {
- switch (fenRows[i].charAt(j))
- {
- case 'k':
- this.kingPos['b'] = [i,k];
- this.INIT_COL_KING['b'] = k;
+ for (let j = 0; j < fenRows[i].length; j++) {
+ switch (fenRows[i].charAt(j)) {
+ case "k":
+ this.kingPos["b"] = [i, k];
+ this.INIT_COL_KING["b"] = k;
break;
- case 'K':
- this.kingPos['w'] = [i,k];
- this.INIT_COL_KING['w'] = k;
+ case "K":
+ this.kingPos["w"] = [i, k];
+ this.INIT_COL_KING["w"] = k;
break;
- case 'r':
- if (this.INIT_COL_ROOK['b'][0] < 0)
- this.INIT_COL_ROOK['b'][0] = k;
- else
- this.INIT_COL_ROOK['b'][1] = k;
+ case "r":
+ if (this.INIT_COL_ROOK["b"][0] < 0) this.INIT_COL_ROOK["b"][0] = k;
+ else this.INIT_COL_ROOK["b"][1] = k;
break;
- case 'R':
- if (this.INIT_COL_ROOK['w'][0] < 0)
- this.INIT_COL_ROOK['w'][0] = k;
- else
- this.INIT_COL_ROOK['w'][1] = k;
+ case "R":
+ if (this.INIT_COL_ROOK["w"][0] < 0) this.INIT_COL_ROOK["w"][0] = k;
+ else this.INIT_COL_ROOK["w"][1] = k;
break;
- default:
+ default: {
const num = parseInt(fenRows[i].charAt(j));
- if (!isNaN(num))
- k += (num-1);
+ if (!isNaN(num)) k += num - 1;
+ }
}
k++;
}
}
// Some additional variables from FEN (variant dependant)
- setOtherVariables(fen)
- {
+ setOtherVariables(fen) {
// Set flags and enpassant:
const parsedFen = V.ParseFen(fen);
- if (V.HasFlags)
- this.setFlags(parsedFen.flags);
- if (V.HasEnpassant)
- {
- const epSq = parsedFen.enpassant != "-"
- ? V.SquareToCoords(parsedFen.enpassant)
- : undefined;
- this.epSquares = [ epSq ];
+ if (V.HasFlags) this.setFlags(parsedFen.flags);
+ if (V.HasEnpassant) {
+ const epSq =
+ parsedFen.enpassant != "-"
+ ? V.SquareToCoords(parsedFen.enpassant)
+ : undefined;
+ this.epSquares = [epSq];
}
// Search for king and rooks positions:
this.scanKingsRooks(fen);
/////////////////////
// GETTERS & SETTERS
- static get size()
- {
- return {x:8, y:8};
+ static get size() {
+ return { x: 8, y: 8 };
}
// Color of thing on suqare (i,j). 'undefined' if square is empty
- getColor(i,j)
- {
+ getColor(i, j) {
return this.board[i][j].charAt(0);
}
// Piece type on square (i,j). 'undefined' if square is empty
- getPiece(i,j)
- {
+ getPiece(i, j) {
return this.board[i][j].charAt(1);
}
// Get opponent color
- static GetOppCol(color)
- {
- return (color=="w" ? "b" : "w");
+ static GetOppCol(color) {
+ return color == "w" ? "b" : "w";
}
// Pieces codes (for a clearer code)
- static get PAWN() { return 'p'; }
- static get ROOK() { return 'r'; }
- static get KNIGHT() { return 'n'; }
- static get BISHOP() { return 'b'; }
- static get QUEEN() { return 'q'; }
- static get KING() { return 'k'; }
+ static get PAWN() {
+ return "p";
+ }
+ static get ROOK() {
+ return "r";
+ }
+ static get KNIGHT() {
+ return "n";
+ }
+ static get BISHOP() {
+ return "b";
+ }
+ static get QUEEN() {
+ return "q";
+ }
+ static get KING() {
+ return "k";
+ }
// For FEN checking:
- static get PIECES()
- {
- return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN,V.KING];
+ static get PIECES() {
+ return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.KING];
}
// Empty square
- static get EMPTY() { return ""; }
+ static get EMPTY() {
+ return "";
+ }
// Some pieces movements
- static get steps()
- {
+ static get steps() {
return {
- 'r': [ [-1,0],[1,0],[0,-1],[0,1] ],
- 'n': [ [-1,-2],[-1,2],[1,-2],[1,2],[-2,-1],[-2,1],[2,-1],[2,1] ],
- 'b': [ [-1,-1],[-1,1],[1,-1],[1,1] ],
+ r: [
+ [-1, 0],
+ [1, 0],
+ [0, -1],
+ [0, 1]
+ ],
+ n: [
+ [-1, -2],
+ [-1, 2],
+ [1, -2],
+ [1, 2],
+ [-2, -1],
+ [-2, 1],
+ [2, -1],
+ [2, 1]
+ ],
+ b: [
+ [-1, -1],
+ [-1, 1],
+ [1, -1],
+ [1, 1]
+ ]
};
}
// MOVES GENERATION
// All possible moves from selected square (assumption: color is OK)
- getPotentialMovesFrom([x,y])
- {
- switch (this.getPiece(x,y))
- {
+ getPotentialMovesFrom([x, y]) {
+ switch (this.getPiece(x, y)) {
case V.PAWN:
- return this.getPotentialPawnMoves([x,y]);
+ return this.getPotentialPawnMoves([x, y]);
case V.ROOK:
- return this.getPotentialRookMoves([x,y]);
+ return this.getPotentialRookMoves([x, y]);
case V.KNIGHT:
- return this.getPotentialKnightMoves([x,y]);
+ return this.getPotentialKnightMoves([x, y]);
case V.BISHOP:
- return this.getPotentialBishopMoves([x,y]);
+ return this.getPotentialBishopMoves([x, y]);
case V.QUEEN:
- return this.getPotentialQueenMoves([x,y]);
+ return this.getPotentialQueenMoves([x, y]);
case V.KING:
- return this.getPotentialKingMoves([x,y]);
+ return this.getPotentialKingMoves([x, y]);
}
+ return []; //never reached
}
// Build a regular move from its initial and destination squares.
// tr: transformation
- getBasicMove([sx,sy], [ex,ey], tr)
- {
+ getBasicMove([sx, sy], [ex, ey], tr) {
let mv = new Move({
appear: [
new PiPo({
x: ex,
y: ey,
- c: !!tr ? tr.c : this.getColor(sx,sy),
- p: !!tr ? tr.p : this.getPiece(sx,sy)
+ c: tr ? tr.c : this.getColor(sx, sy),
+ p: tr ? tr.p : this.getPiece(sx, sy)
})
],
vanish: [
new PiPo({
x: sx,
y: sy,
- c: this.getColor(sx,sy),
- p: this.getPiece(sx,sy)
+ c: this.getColor(sx, sy),
+ p: this.getPiece(sx, sy)
})
]
});
// The opponent piece disappears if we take it
- if (this.board[ex][ey] != V.EMPTY)
- {
+ if (this.board[ex][ey] != V.EMPTY) {
mv.vanish.push(
new PiPo({
x: ex,
y: ey,
- c: this.getColor(ex,ey),
- p: this.getPiece(ex,ey)
+ c: this.getColor(ex, ey),
+ p: this.getPiece(ex, ey)
})
);
}
// Generic method to find possible moves of non-pawn pieces:
// "sliding or jumping"
- getSlideNJumpMoves([x,y], steps, oneStep)
- {
- const color = this.getColor(x,y);
+ getSlideNJumpMoves([x, y], steps, oneStep) {
let moves = [];
- outerLoop:
- for (let step of steps)
- {
+ outerLoop: for (let step of steps) {
let i = x + step[0];
let j = y + step[1];
- while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [i,j]));
- if (oneStep !== undefined)
- continue outerLoop;
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ moves.push(this.getBasicMove([x, y], [i, j]));
+ if (oneStep !== undefined) continue outerLoop;
i += step[0];
j += step[1];
}
- if (V.OnBoard(i,j) && this.canTake([x,y], [i,j]))
- moves.push(this.getBasicMove([x,y], [i,j]));
+ if (V.OnBoard(i, j) && this.canTake([x, y], [i, j]))
+ moves.push(this.getBasicMove([x, y], [i, j]));
}
return moves;
}
// What are the pawn moves from square x,y ?
- getPotentialPawnMoves([x,y])
- {
+ getPotentialPawnMoves([x, y]) {
const color = this.turn;
let moves = [];
- const [sizeX,sizeY] = [V.size.x,V.size.y];
- const shiftX = (color == "w" ? -1 : 1);
- const firstRank = (color == 'w' ? sizeX-1 : 0);
- const startRank = (color == "w" ? sizeX-2 : 1);
- const lastRank = (color == "w" ? 0 : sizeX-1);
- const pawnColor = this.getColor(x,y); //can be different for checkered
+ const [sizeX, sizeY] = [V.size.x, V.size.y];
+ const shiftX = color == "w" ? -1 : 1;
+ const firstRank = color == "w" ? sizeX - 1 : 0;
+ const startRank = color == "w" ? sizeX - 2 : 1;
+ const lastRank = color == "w" ? 0 : sizeX - 1;
+ const pawnColor = this.getColor(x, y); //can be different for checkered
// NOTE: next condition is generally true (no pawn on last rank)
- if (x+shiftX >= 0 && x+shiftX < sizeX)
- {
- const finalPieces = x + shiftX == lastRank
- ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
- : [V.PAWN]
+ if (x + shiftX >= 0 && x + shiftX < sizeX) {
+ const finalPieces =
+ x + shiftX == lastRank
+ ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
+ : [V.PAWN];
// One square forward
- if (this.board[x+shiftX][y] == V.EMPTY)
- {
- for (let piece of finalPieces)
- {
- moves.push(this.getBasicMove([x,y], [x+shiftX,y],
- {c:pawnColor,p:piece}));
+ if (this.board[x + shiftX][y] == V.EMPTY) {
+ for (let piece of finalPieces) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y], {
+ c: pawnColor,
+ p: piece
+ })
+ );
}
// Next condition because pawns on 1st rank can generally jump
- if ([startRank,firstRank].includes(x)
- && this.board[x+2*shiftX][y] == V.EMPTY)
- {
+ if (
+ [startRank, firstRank].includes(x) &&
+ this.board[x + 2 * shiftX][y] == V.EMPTY
+ ) {
// Two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
+ moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
}
}
// Captures
- for (let shiftY of [-1,1])
- {
- if (y + shiftY >= 0 && y + shiftY < sizeY
- && this.board[x+shiftX][y+shiftY] != V.EMPTY
- && this.canTake([x,y], [x+shiftX,y+shiftY]))
- {
- for (let piece of finalPieces)
- {
- moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
- {c:pawnColor,p:piece}));
+ for (let shiftY of [-1, 1]) {
+ if (
+ y + shiftY >= 0 &&
+ y + shiftY < sizeY &&
+ this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+ this.canTake([x, y], [x + shiftX, y + shiftY])
+ ) {
+ for (let piece of finalPieces) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+ c: pawnColor,
+ p: piece
+ })
+ );
}
}
}
}
- if (V.HasEnpassant)
- {
+ if (V.HasEnpassant) {
// En passant
const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep-1]; //always at least one element
- if (!!epSquare && epSquare.x == x+shiftX && Math.abs(epSquare.y - y) == 1)
- {
- let enpassantMove = this.getBasicMove([x,y], [epSquare.x,epSquare.y]);
+ const epSquare = this.epSquares[Lep - 1]; //always at least one element
+ if (
+ !!epSquare &&
+ epSquare.x == x + shiftX &&
+ Math.abs(epSquare.y - y) == 1
+ ) {
+ let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
enpassantMove.vanish.push({
x: x,
y: epSquare.y,
- p: 'p',
- c: this.getColor(x,epSquare.y)
+ p: "p",
+ c: this.getColor(x, epSquare.y)
});
moves.push(enpassantMove);
}
}
// What are the rook moves from square x,y ?
- getPotentialRookMoves(sq)
- {
+ getPotentialRookMoves(sq) {
return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]);
}
// What are the knight moves from square x,y ?
- getPotentialKnightMoves(sq)
- {
+ getPotentialKnightMoves(sq) {
return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep");
}
// What are the bishop moves from square x,y ?
- getPotentialBishopMoves(sq)
- {
+ getPotentialBishopMoves(sq) {
return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]);
}
// What are the queen moves from square x,y ?
- getPotentialQueenMoves(sq)
- {
- return this.getSlideNJumpMoves(sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
+ getPotentialQueenMoves(sq) {
+ return this.getSlideNJumpMoves(
+ sq,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+ );
}
// What are the king moves from square x,y ?
- getPotentialKingMoves(sq)
- {
+ getPotentialKingMoves(sq) {
// Initialize with normal moves
- let moves = this.getSlideNJumpMoves(sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+ let moves = this.getSlideNJumpMoves(
+ sq,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
return moves.concat(this.getCastleMoves(sq));
}
- getCastleMoves([x,y])
- {
- const c = this.getColor(x,y);
- if (x != (c=="w" ? V.size.x-1 : 0) || y != this.INIT_COL_KING[c])
+ 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;
- const finalSquares = [ [2,3], [V.size.y-2,V.size.y-3] ]; //king, then rook
- castlingCheck:
- for (let castleSide=0; castleSide < 2; castleSide++) //large, then small
- {
- if (!this.castleFlags[c][castleSide])
- continue;
+ const finalSquares = [
+ [2, 3],
+ [V.size.y - 2, V.size.y - 3]
+ ]; //king, then rook
+ castlingCheck: for (
+ let castleSide = 0;
+ castleSide < 2;
+ castleSide++ //large, then small
+ ) {
+ if (!this.castleFlags[c][castleSide]) 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)))))
- {
+ 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]);
+ } while (i != finalSquares[castleSide][0]);
// Nothing on the path to the rook?
- step = (castleSide == 0 ? -1 : 1);
- for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step)
- {
- if (this.board[x][i] != V.EMPTY)
- continue castlingCheck;
+ step = castleSide == 0 ? -1 : 1;
+ for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step) {
+ if (this.board[x][i] != V.EMPTY) continue castlingCheck;
}
const rookPos = this.INIT_COL_ROOK[c][castleSide];
// 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)
- {
+ 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)}
- }) );
+ 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;
// MOVES VALIDATION
// For the interface: possible moves for the current turn from square sq
- getPossibleMovesFrom(sq)
- {
- return this.filterValid( this.getPotentialMovesFrom(sq) );
+ getPossibleMovesFrom(sq) {
+ return this.filterValid(this.getPotentialMovesFrom(sq));
}
// TODO: promotions (into R,B,N,Q) should be filtered only once
- filterValid(moves)
- {
- if (moves.length == 0)
- return [];
+ filterValid(moves) {
+ if (moves.length == 0) return [];
const color = this.turn;
return moves.filter(m => {
this.play(m);
// Search for all valid moves considering current turn
// (for engine and game end)
- getAllValidMoves()
- {
+ getAllValidMoves() {
const color = this.turn;
const oppCol = V.GetOppCol(color);
let potentialMoves = [];
- for (let i=0; i<V.size.x; i++)
- {
- for (let j=0; j<V.size.y; j++)
- {
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
// Next condition "!= oppCol" to work with checkered variant
- if (this.board[i][j] != V.EMPTY && this.getColor(i,j) != oppCol)
- {
- Array.prototype.push.apply(potentialMoves,
- this.getPotentialMovesFrom([i,j]));
+ if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
+ Array.prototype.push.apply(
+ potentialMoves,
+ this.getPotentialMovesFrom([i, j])
+ );
}
}
}
}
// Stop at the first move found
- atLeastOneMove()
- {
+ atLeastOneMove() {
const color = this.turn;
const oppCol = V.GetOppCol(color);
- 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 && this.getColor(i,j) != oppCol)
- {
- const moves = this.getPotentialMovesFrom([i,j]);
- if (moves.length > 0)
- {
- for (let k=0; k<moves.length; k++)
- {
- if (this.filterValid([moves[k]]).length > 0)
- return true;
+ 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 && this.getColor(i, j) != oppCol) {
+ const moves = this.getPotentialMovesFrom([i, j]);
+ if (moves.length > 0) {
+ for (let k = 0; k < moves.length; k++) {
+ if (this.filterValid([moves[k]]).length > 0) return true;
}
}
}
}
// Check if pieces of color in 'colors' are attacking (king) on square x,y
- 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));
+ 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)
+ );
}
// Is square x,y attacked by 'colors' pawns ?
- 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])
- {
- if (y+i>=0 && y+i<V.size.y && this.getPiece(x+pawnShift,y+i)==V.PAWN
- && this.getColor(x+pawnShift,y+i)==c)
- {
+ 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]) {
+ 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 'colors' rooks ?
- isAttackedByRook(sq, colors)
- {
+ isAttackedByRook(sq, colors) {
return this.isAttackedBySlideNJump(sq, colors, V.ROOK, V.steps[V.ROOK]);
}
// Is square x,y attacked by 'colors' knights ?
- isAttackedByKnight(sq, colors)
- {
- return this.isAttackedBySlideNJump(sq, colors,
- V.KNIGHT, V.steps[V.KNIGHT], "oneStep");
+ isAttackedByKnight(sq, colors) {
+ return this.isAttackedBySlideNJump(
+ sq,
+ colors,
+ V.KNIGHT,
+ V.steps[V.KNIGHT],
+ "oneStep"
+ );
}
// Is square x,y attacked by 'colors' bishops ?
- isAttackedByBishop(sq, colors)
- {
+ isAttackedByBishop(sq, colors) {
return this.isAttackedBySlideNJump(sq, colors, V.BISHOP, V.steps[V.BISHOP]);
}
// Is square x,y attacked by 'colors' queens ?
- isAttackedByQueen(sq, colors)
- {
- return this.isAttackedBySlideNJump(sq, colors, V.QUEEN,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
+ isAttackedByQueen(sq, colors) {
+ return this.isAttackedBySlideNJump(
+ sq,
+ colors,
+ V.QUEEN,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+ );
}
// Is square x,y attacked by 'colors' king(s) ?
- isAttackedByKing(sq, colors)
- {
- return this.isAttackedBySlideNJump(sq, colors, V.KING,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+ isAttackedByKing(sq, colors) {
+ return this.isAttackedBySlideNJump(
+ sq,
+ colors,
+ V.KING,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
}
// 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)
- {
- 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)
- {
+ 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)))
- {
+ if (
+ V.OnBoard(rx, ry) &&
+ this.getPiece(rx, ry) === piece &&
+ colors.includes(this.getColor(rx, ry))
+ ) {
return true;
}
}
}
// Is color under check after his move ?
- underCheck(color)
- {
+ underCheck(color) {
return this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]);
}
// MOVES PLAYING
// Apply a move on board
- static PlayOnBoard(board, move)
- {
- for (let psq of move.vanish)
- board[psq.x][psq.y] = V.EMPTY;
- for (let psq of move.appear)
- board[psq.x][psq.y] = psq.c + psq.p;
+ static PlayOnBoard(board, move) {
+ for (let psq of move.vanish) board[psq.x][psq.y] = V.EMPTY;
+ for (let psq of move.appear) board[psq.x][psq.y] = psq.c + psq.p;
}
// Un-apply the played move
- static UndoOnBoard(board, move)
- {
- for (let psq of move.appear)
- board[psq.x][psq.y] = V.EMPTY;
- for (let psq of move.vanish)
- board[psq.x][psq.y] = psq.c + psq.p;
+ static UndoOnBoard(board, move) {
+ for (let psq of move.appear) board[psq.x][psq.y] = V.EMPTY;
+ for (let psq of move.vanish) board[psq.x][psq.y] = psq.c + psq.p;
}
// After move is played, update variables + flags
- updateVariables(move)
- {
+ updateVariables(move) {
let piece = undefined;
let c = undefined;
- if (move.vanish.length >= 1)
- {
+ if (move.vanish.length >= 1) {
// Usual case, something is moved
piece = move.vanish[0].p;
c = move.vanish[0].c;
- }
- else
- {
+ } else {
// Crazyhouse-like variants
piece = move.appear[0].p;
c = move.appear[0].c;
}
- if (c == "c") //if (!["w","b"].includes(c))
- {
+ if (c == "c") {
+ //if (!["w","b"].includes(c))
// 'c = move.vanish[0].c' doesn't work for Checkered
c = V.GetOppCol(this.turn);
}
- const firstRank = (c == "w" ? V.size.x-1 : 0);
+ const firstRank = c == "w" ? V.size.x - 1 : 0;
// Update king position + flags
- if (piece == V.KING && move.appear.length > 0)
- {
+ if (piece == V.KING && move.appear.length > 0) {
this.kingPos[c][0] = move.appear[0].x;
this.kingPos[c][1] = move.appear[0].y;
- if (V.HasFlags)
- this.castleFlags[c] = [false,false];
+ if (V.HasFlags) this.castleFlags[c] = [false, false];
return;
}
- if (V.HasFlags)
- {
+ if (V.HasFlags) {
// Update castling flags if rooks are moved
const oppCol = V.GetOppCol(c);
- const oppFirstRank = (V.size.x-1) - firstRank;
- if (move.start.x == firstRank //our rook moves?
- && this.INIT_COL_ROOK[c].includes(move.start.y))
- {
- const flagIdx = (move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1);
+ const oppFirstRank = V.size.x - 1 - firstRank;
+ if (
+ move.start.x == firstRank && //our rook moves?
+ this.INIT_COL_ROOK[c].includes(move.start.y)
+ ) {
+ const flagIdx = move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1;
this.castleFlags[c][flagIdx] = false;
- }
- else if (move.end.x == oppFirstRank //we took opponent rook?
- && this.INIT_COL_ROOK[oppCol].includes(move.end.y))
- {
- const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1);
+ } else if (
+ move.end.x == oppFirstRank && //we took opponent rook?
+ this.INIT_COL_ROOK[oppCol].includes(move.end.y)
+ ) {
+ const flagIdx = move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1;
this.castleFlags[oppCol][flagIdx] = false;
}
}
// After move is undo-ed *and flags resetted*, un-update other variables
// TODO: more symmetry, by storing flags increment in move (?!)
- unupdateVariables(move)
- {
+ unupdateVariables(move) {
// (Potentially) Reset king position
- const c = this.getColor(move.start.x,move.start.y);
- if (this.getPiece(move.start.x,move.start.y) == V.KING)
+ const c = this.getColor(move.start.x, move.start.y);
+ if (this.getPiece(move.start.x, move.start.y) == V.KING)
this.kingPos[c] = [move.start.x, move.start.y];
}
- play(move)
- {
+ play(move) {
// DEBUG:
-// if (!this.states) this.states = [];
-// const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen();
-// this.states.push(stateFen);
-
- if (V.HasFlags)
- move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo)
- if (V.HasEnpassant)
- this.epSquares.push( this.getEpSquare(move) );
+ // if (!this.states) this.states = [];
+ // const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen();
+ // this.states.push(stateFen);
+
+ if (V.HasFlags) move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo)
+ if (V.HasEnpassant) this.epSquares.push(this.getEpSquare(move));
V.PlayOnBoard(this.board, move);
this.turn = V.GetOppCol(this.turn);
this.movesCount++;
this.updateVariables(move);
}
- undo(move)
- {
- if (V.HasEnpassant)
- this.epSquares.pop();
- if (V.HasFlags)
- this.disaggregateFlags(JSON.parse(move.flags));
+ undo(move) {
+ if (V.HasEnpassant) this.epSquares.pop();
+ if (V.HasFlags) this.disaggregateFlags(JSON.parse(move.flags));
V.UndoOnBoard(this.board, move);
this.turn = V.GetOppCol(this.turn);
this.movesCount--;
this.unupdateVariables(move);
// DEBUG:
-// const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen();
-// if (stateFen != this.states[this.states.length-1]) debugger;
-// this.states.pop();
+ // const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen();
+ // if (stateFen != this.states[this.states.length-1]) debugger;
+ // this.states.pop();
}
///////////////
// END OF GAME
// What is the score ? (Interesting if game is over)
- getCurrentScore()
- {
- if (this.atLeastOneMove()) // game not over
+ getCurrentScore() {
+ if (this.atLeastOneMove())
+ // game not over
return "*";
// Game over
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";
}
///////////////
// ENGINE PLAY
// Pieces values
- static get VALUES()
- {
+ static get VALUES() {
return {
- 'p': 1,
- 'r': 5,
- 'n': 3,
- 'b': 3,
- 'q': 9,
- 'k': 1000
+ p: 1,
+ r: 5,
+ n: 3,
+ b: 3,
+ q: 9,
+ k: 1000
};
}
// "Checkmate" (unreachable eval)
- static get INFINITY() { return 9999; }
+ static get INFINITY() {
+ return 9999;
+ }
// At this value or above, the game is over
- static get THRESHOLD_MATE() { return V.INFINITY; }
+ static get THRESHOLD_MATE() {
+ return V.INFINITY;
+ }
// Search depth: 2 for high branching factor, 4 for small (Loser chess, eg.)
- static get SEARCH_DEPTH() { return 3; }
+ static get SEARCH_DEPTH() {
+ return 3;
+ }
// NOTE: works also for extinction chess because depth is 3...
- getComputerMove()
- {
+ getComputerMove() {
const maxeval = V.INFINITY;
const color = this.turn;
// Some variants may show a bigger moves list to the human (Switching),
// thus the argument "computer" below (which is generally ignored)
let moves1 = this.getAllValidMoves("computer");
- if (moves1.length == 0) //TODO: this situation should not happen
+ if (moves1.length == 0)
+ //TODO: this situation should not happen
return null;
// Can I mate in 1 ? (for Magnetic & Extinction)
- for (let i of shuffle(ArrayFun.range(moves1.length)))
- {
+ for (let i of shuffle(ArrayFun.range(moves1.length))) {
this.play(moves1[i]);
- let finish = (Math.abs(this.evalPosition()) >= V.THRESHOLD_MATE);
- if (!finish)
- {
+ let finish = Math.abs(this.evalPosition()) >= V.THRESHOLD_MATE;
+ if (!finish) {
const score = this.getCurrentScore();
- if (["1-0","0-1"].includes(score))
- finish = true;
+ if (["1-0", "0-1"].includes(score)) finish = true;
}
this.undo(moves1[i]);
- if (finish)
- return moves1[i];
+ if (finish) return moves1[i];
}
// Rank moves using a min-max at depth 2
- for (let i=0; i<moves1.length; i++)
- {
+ for (let i = 0; i < moves1.length; i++) {
// Initial self evaluation is very low: "I'm checkmated"
- moves1[i].eval = (color=="w" ? -1 : 1) * maxeval;
+ moves1[i].eval = (color == "w" ? -1 : 1) * maxeval;
this.play(moves1[i]);
const score1 = this.getCurrentScore();
let eval2 = undefined;
- if (score1 == "*")
- {
+ if (score1 == "*") {
// Initial enemy evaluation is very low too, for him
- eval2 = (color=="w" ? 1 : -1) * maxeval;
+ eval2 = (color == "w" ? 1 : -1) * maxeval;
// Second half-move:
let moves2 = this.getAllValidMoves("computer");
- for (let j=0; j<moves2.length; j++)
- {
+ for (let j = 0; j < moves2.length; j++) {
this.play(moves2[j]);
const score2 = this.getCurrentScore();
- const evalPos = score2 == "*"
- ? this.evalPosition()
- : (score2=="1/2" ? 0 : (score2=="1-0" ? 1 : -1) * maxeval);
- if ((color == "w" && evalPos < eval2)
- || (color=="b" && evalPos > eval2))
- {
+ let evalPos = 0; //1/2 value
+ switch (score2) {
+ case "*":
+ evalPos = this.evalPosition();
+ break;
+ case "1-0":
+ evalPos = maxeval;
+ break;
+ case "0-1":
+ evalPos = -maxeval;
+ break;
+ }
+ if (
+ (color == "w" && evalPos < eval2) ||
+ (color == "b" && evalPos > eval2)
+ ) {
eval2 = evalPos;
}
this.undo(moves2[j]);
}
- }
- else
- eval2 = (score1=="1/2" ? 0 : (score1=="1-0" ? 1 : -1) * maxeval);
- if ((color=="w" && eval2 > moves1[i].eval)
- || (color=="b" && eval2 < moves1[i].eval))
- {
+ } else eval2 = score1 == "1/2" ? 0 : (score1 == "1-0" ? 1 : -1) * maxeval;
+ if (
+ (color == "w" && eval2 > moves1[i].eval) ||
+ (color == "b" && eval2 < moves1[i].eval)
+ ) {
moves1[i].eval = eval2;
}
this.undo(moves1[i]);
}
- moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
+ moves1.sort((a, b) => {
+ return (color == "w" ? 1 : -1) * (b.eval - a.eval);
+ });
let candidates = [0]; //indices of candidates moves
- for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
+ for (let j = 1; j < moves1.length && moves1[j].eval == moves1[0].eval; j++)
candidates.push(j);
let currentBest = moves1[candidates[randInt(candidates.length)]];
// Skip depth 3+ if we found a checkmate (or if we are checkmated in 1...)
- if (V.SEARCH_DEPTH >= 3 && Math.abs(moves1[0].eval) < V.THRESHOLD_MATE)
- {
+ if (V.SEARCH_DEPTH >= 3 && Math.abs(moves1[0].eval) < V.THRESHOLD_MATE) {
// From here, depth >= 3: may take a while, so we control time
const timeStart = Date.now();
- for (let i=0; i<moves1.length; i++)
- {
- if (Date.now()-timeStart >= 5000) //more than 5 seconds
+ for (let i = 0; i < moves1.length; i++) {
+ if (Date.now() - timeStart >= 5000)
+ //more than 5 seconds
return currentBest; //depth 2 at least
this.play(moves1[i]);
// 0.1 * oldEval : heuristic to avoid some bad moves (not all...)
- moves1[i].eval = 0.1*moves1[i].eval +
- this.alphabeta(V.SEARCH_DEPTH-1, -maxeval, maxeval);
+ moves1[i].eval =
+ 0.1 * moves1[i].eval +
+ this.alphabeta(V.SEARCH_DEPTH - 1, -maxeval, maxeval);
this.undo(moves1[i]);
}
- moves1.sort( (a,b) => {
- return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
- }
- else
- return currentBest;
-// console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; }));
+ moves1.sort((a, b) => {
+ return (color == "w" ? 1 : -1) * (b.eval - a.eval);
+ });
+ } else return currentBest;
+ // console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; }));
candidates = [0];
- for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
+ for (let j = 1; j < moves1.length && moves1[j].eval == moves1[0].eval; j++)
candidates.push(j);
return moves1[candidates[randInt(candidates.length)]];
}
- alphabeta(depth, alpha, beta)
- {
+ alphabeta(depth, alpha, beta) {
const maxeval = V.INFINITY;
const color = this.turn;
const score = this.getCurrentScore();
if (score != "*")
- return (score=="1/2" ? 0 : (score=="1-0" ? 1 : -1) * maxeval);
- if (depth == 0)
- return this.evalPosition();
+ return score == "1/2" ? 0 : (score == "1-0" ? 1 : -1) * maxeval;
+ if (depth == 0) return this.evalPosition();
const moves = this.getAllValidMoves("computer");
- let v = color=="w" ? -maxeval : maxeval;
- if (color == "w")
- {
- for (let i=0; i<moves.length; i++)
- {
+ let v = color == "w" ? -maxeval : maxeval;
+ if (color == "w") {
+ for (let i = 0; i < moves.length; i++) {
this.play(moves[i]);
- v = Math.max(v, this.alphabeta(depth-1, alpha, beta));
+ v = Math.max(v, this.alphabeta(depth - 1, alpha, beta));
this.undo(moves[i]);
alpha = Math.max(alpha, v);
- if (alpha >= beta)
- break; //beta cutoff
+ if (alpha >= beta) break; //beta cutoff
}
- }
- else //color=="b"
- {
- for (let i=0; i<moves.length; i++)
- {
+ } //color=="b"
+ else {
+ for (let i = 0; i < moves.length; i++) {
this.play(moves[i]);
- v = Math.min(v, this.alphabeta(depth-1, alpha, beta));
+ v = Math.min(v, this.alphabeta(depth - 1, alpha, beta));
this.undo(moves[i]);
beta = Math.min(beta, v);
- if (alpha >= beta)
- break; //alpha cutoff
+ if (alpha >= beta) break; //alpha cutoff
}
}
return v;
}
- evalPosition()
- {
+ evalPosition() {
let evaluation = 0;
// Just count material for now
- 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)
- {
- const sign = this.getColor(i,j) == "w" ? 1 : -1;
- evaluation += sign * V.VALUES[this.getPiece(i,j)];
+ 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) {
+ const sign = this.getColor(i, j) == "w" ? 1 : -1;
+ evaluation += sign * V.VALUES[this.getPiece(i, j)];
}
}
}
// Context: just before move is played, turn hasn't changed
// TODO: un-ambiguous notation (switch on piece type, check directions...)
- 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");
+ 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 square
const finalSquare = V.CoordsToSquare(move.end);
const piece = this.getPiece(move.start.x, move.start.y);
- if (piece == V.PAWN)
- {
+ if (piece == V.PAWN) {
// Pawn move
let notation = "";
- if (move.vanish.length > move.appear.length)
- {
+ if (move.vanish.length > move.appear.length) {
// Capture
const startColumn = V.CoordToColumn(move.start.y);
notation = startColumn + "x" + finalSquare;
- }
- else //no capture
- notation = finalSquare;
- if (move.appear.length > 0 && move.appear[0].p != V.PAWN) //promotion
+ } //no capture
+ else notation = finalSquare;
+ if (move.appear.length > 0 && move.appear[0].p != V.PAWN)
+ //promotion
notation += "=" + move.appear[0].p.toUpperCase();
return notation;
}
-
- else
- {
- // Piece movement
- return piece.toUpperCase() +
- (move.vanish.length > move.appear.length ? "x" : "") + finalSquare;
- }
- }
-}
+ // Piece movement
+ return (
+ piece.toUpperCase() +
+ (move.vanish.length > move.appear.length ? "x" : "") +
+ finalSquare
+ );
+ }
+};
<template lang="pug">
-div#baseGame(tabindex=-1 @click="focusBg()"
- @keydown="handleKeys($event)" @wheel="handleScroll($event)")
+div#baseGame(
+ tabindex=-1
+ @click="focusBg()"
+ @keydown="handleKeys($event)"
+ @wheel="handleScroll($event)"
+)
input#modalEog.modal(type="checkbox")
- div#eogDiv(role="dialog" data-checkbox="modalEog")
+ div#eogDiv(
+ role="dialog"
+ data-checkbox="modalEog"
+ )
.card.text-center
label.modal-close(for="modalEog")
h3.section {{ endgameMessage }}
input#modalAdjust.modal(type="checkbox")
- div#adjuster(role="dialog" data-checkbox="modalAdjust")
+ div#adjuster(
+ role="dialog"
+ data-checkbox="modalAdjust"
+ )
.card.text-center
label.modal-close(for="modalAdjust")
label(for="boardSize") {{ st.tr["Board size"] }}
- input#boardSize.slider(type="range" min="0" max="100" value="50"
- @input="adjustBoard()")
+ input#boardSize.slider(
+ type="range"
+ min="0"
+ max="100"
+ value="50"
+ @input="adjustBoard()"
+ )
#gameContainer
#boardContainer
- Board(:vr="vr" :last-move="lastMove" :analyze="analyze"
- :user-color="game.mycolor" :orientation="orientation"
- :vname="game.vname" :incheck="incheck" @play-move="play")
+ Board(
+ :vr="vr"
+ :last-move="lastMove"
+ :analyze="analyze"
+ :user-color="game.mycolor"
+ :orientation="orientation"
+ :vname="game.vname"
+ :incheck="incheck"
+ @play-move="play"
+ )
#turnIndicator(v-if="game.vname=='Dark' && game.score=='*'")
- | {{ turn }}
+ | {{ st.tr[vr.turn + " to move"] }}
#controls
button(@click="gotoBegin()") <<
button(@click="undo()") <
a#download(href="#")
button(@click="download()") {{ st.tr["Download"] }} PGN
button(onClick="doClick('modalAdjust')") ⤢
- button(v-if="game.vname!='Dark' && game.mode!='analyze'"
- @click="analyzePosition()")
+ button(
+ v-if="game.vname!='Dark' && game.mode!='analyze'"
+ @click="analyzePosition()"
+ )
| {{ st.tr["Analyse"] }}
// NOTE: rather ugly hack to avoid showing twice "rules" link...
- button(v-if="!$route.path.match('/variants/')" @click="showRules()")
+ button(
+ v-if="!$route.path.match('/variants/')"
+ @click="showRules()"
+ )
| {{ st.tr["Rules"] }}
#movesList
- MoveList(v-if="showMoves" :score="game.score" :message="game.scoreMsg"
- :firstNum="firstMoveNumber" :moves="moves" :cursor="cursor"
- @goto-move="gotoMove")
+ MoveList(
+ v-if="showMoves"
+ :score="game.score"
+ :message="game.scoreMsg"
+ :firstNum="firstMoveNumber"
+ :moves="moves"
+ :cursor="cursor"
+ @goto-move="gotoMove"
+ )
.clearer
</template>
import { processModalClick } from "@/utils/modalClick";
import { getScoreMessage } from "@/utils/scoring";
export default {
- name: 'my-base-game',
+ name: "my-base-game",
components: {
Board,
- MoveList,
+ MoveList
},
// "vr": VariantRules object, describing the game state + rules
- props: ["vr","game"],
+ props: ["vr", "game"],
data: function() {
return {
st: store.state,
cursor: -1, //index of the move just played
lastMove: null,
firstMoveNumber: 0, //for printing
- incheck: [], //for Board
+ incheck: [] //for Board
};
},
watch: {
this.re_setVariables();
},
// Received a new move to play:
- "game.moveToPlay": function(newMove) {
- if (!!newMove) //if stop + launch new game, get undefined move
- this.play(newMove, "receive");
+ "game.moveToPlay": function(move) {
+ if (move) this.play(move, "receive");
},
// ...Or to undo (corr game, move not validated)
"game.moveToUndo": function(move) {
- if (!!move)
- this.undo(move);
- },
+ if (move) this.undo(move);
+ }
},
computed: {
showMoves: function() {
return this.game.vname != "Dark" || this.game.score != "*";
},
- turn: function() {
- let color = "";
- const L = this.moves.length;
- if (L == 0 || this.moves[L-1].color == "b")
- color = "White";
- else //if (this.moves[L-1].color == "w")
- color = "Black";
- return this.st.tr[color + " to move"];
- },
analyze: function() {
- return this.game.mode=="analyze" ||
+ return (
+ this.game.mode == "analyze" ||
// From Board viewpoint, a finished Dark game == analyze (TODO: unclear)
- (this.game.vname == "Dark" && this.game.score != "*");
- },
+ (this.game.vname == "Dark" && this.game.score != "*")
+ );
+ }
},
created: function() {
- if (!!this.game.fenStart)
- this.re_setVariables();
+ if (this.game.fenStart) this.re_setVariables();
},
mounted: function() {
- [document.getElementById("eogDiv"),document.getElementById("adjuster")]
- .forEach(elt => elt.addEventListener("click", processModalClick));
+ [
+ document.getElementById("eogDiv"),
+ document.getElementById("adjuster")
+ ].forEach(elt => elt.addEventListener("click", processModalClick));
// Take full width on small screens:
let boardSize = parseInt(localStorage.getItem("boardSize"));
- if (!boardSize)
- {
- boardSize = (window.innerWidth >= 768
- ? 0.75 * Math.min(window.innerWidth, window.innerHeight)
- : window.innerWidth);
+ if (!boardSize) {
+ boardSize =
+ window.innerWidth >= 768
+ ? 0.75 * Math.min(window.innerWidth, window.innerHeight)
+ : window.innerWidth;
}
- const movesWidth = (window.innerWidth >= 768 ? 280 : 0);
+ const movesWidth = window.innerWidth >= 768 ? 280 : 0;
document.getElementById("boardContainer").style.width = boardSize + "px";
let gameContainer = document.getElementById("gameContainer");
- gameContainer.style.width = (boardSize + movesWidth) + "px";
- document.getElementById("boardSize").value = (boardSize * 100) / (window.innerWidth - movesWidth);
+ gameContainer.style.width = boardSize + movesWidth + "px";
+ document.getElementById("boardSize").value =
+ (boardSize * 100) / (window.innerWidth - movesWidth);
// timeout to avoid calling too many time the adjust method
let timeoutLaunched = false;
- window.addEventListener("resize", (e) => {
- if (!timeoutLaunched)
- {
+ window.addEventListener("resize", () => {
+ if (!timeoutLaunched) {
timeoutLaunched = true;
- setTimeout( () => {
+ setTimeout(() => {
this.adjustBoard();
timeoutLaunched = false;
}, 500);
},
adjustBoard: function() {
const boardContainer = document.getElementById("boardContainer");
- if (!boardContainer)
- return; //no board on page
+ if (!boardContainer) return; //no board on page
const k = document.getElementById("boardSize").value;
- const movesWidth = (window.innerWidth >= 768 ? 280 : 0);
+ const movesWidth = window.innerWidth >= 768 ? 280 : 0;
const minBoardWidth = 240; //TODO: these 240 and 280 are arbitrary...
// Value of 0 is board min size; 100 is window.width [- movesWidth]
- const boardSize = minBoardWidth +
- k * (window.innerWidth - (movesWidth+minBoardWidth)) / 100;
+ const boardSize =
+ minBoardWidth +
+ (k * (window.innerWidth - (movesWidth + minBoardWidth))) / 100;
localStorage.setItem("boardSize", boardSize);
boardContainer.style.width = boardSize + "px";
document.getElementById("gameContainer").style.width =
- (boardSize + movesWidth) + "px";
+ boardSize + movesWidth + "px";
},
handleKeys: function(e) {
- if ([32,37,38,39,40].includes(e.keyCode))
- e.preventDefault();
- switch (e.keyCode)
- {
+ if ([32, 37, 38, 39, 40].includes(e.keyCode)) e.preventDefault();
+ switch (e.keyCode) {
case 37:
this.undo();
break;
},
handleScroll: function(e) {
// NOTE: since game.mode=="analyze" => no score, next condition is enough
- if (this.game.score != "*")
- {
+ if (this.game.score != "*") {
e.preventDefault();
- if (e.deltaY < 0)
- this.undo();
- else if (e.deltaY > 0)
- this.play();
+ if (e.deltaY < 0) this.undo();
+ else if (e.deltaY > 0) this.play();
}
},
showRules: function() {
// Post-processing: decorate each move with color + current FEN:
// (to be able to jump to any position quickly)
let vr_tmp = new V(this.game.fenStart); //vr is already at end of game
- this.firstMoveNumber =
- Math.floor(V.ParseFen(this.game.fenStart).movesCount / 2);
+ this.firstMoveNumber = Math.floor(
+ V.ParseFen(this.game.fenStart).movesCount / 2
+ );
this.moves.forEach(move => {
// NOTE: this is doing manually what play() function below achieve,
// but in a lighter "fast-forward" way
vr_tmp.play(move);
move.fen = vr_tmp.getFen();
});
- if ((this.moves.length > 0 && this.moves[0].color == "b") ||
- (this.moves.length == 0 && this.vr_tmp.turn == "b"))
- {
+ if (
+ (this.moves.length > 0 && this.moves[0].color == "b") ||
+ (this.moves.length == 0 && vr_tmp.turn == "b")
+ ) {
// 'end' is required for Board component to check lastMove for e.p.
- this.moves.unshift({color: "w", notation: "...", end: {x:-1,y:-1}});
+ this.moves.unshift({
+ color: "w",
+ notation: "...",
+ end: { x: -1, y: -1 }
+ });
}
const L = this.moves.length;
- this.cursor = L-1;
- this.lastMove = (L > 0 ? this.moves[L-1] : null);
+ this.cursor = L - 1;
+ this.lastMove = L > 0 ? this.moves[L - 1] : null;
this.incheck = this.vr.getCheckSquares(this.vr.turn);
},
analyzePosition: function() {
- const newUrl = "/analyse/" + this.game.vname +
- "/?fen=" + this.vr.getFen().replace(/ /g, "_");
- if (this.game.type == "live")
- this.$router.push(newUrl); //open in same tab: against cheating...
- else
- window.open("#" + newUrl); //open in a new tab: more comfortable
+ const newUrl =
+ "/analyse/" +
+ this.game.vname +
+ "/?fen=" +
+ this.vr.getFen().replace(/ /g, "_");
+ if (this.game.type == "live") this.$router.push(newUrl);
+ //open in same tab: against cheating...
+ else window.open("#" + newUrl); //open in a new tab: more comfortable
},
download: function() {
const content = this.getPgn();
// Prepare and trigger download link
let downloadAnchor = document.getElementById("download");
downloadAnchor.setAttribute("download", "game.pgn");
- downloadAnchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(content);
+ downloadAnchor.href =
+ "data:text/plain;charset=utf-8," + encodeURIComponent(content);
downloadAnchor.click();
},
getPgn: function() {
pgn += '[Result "' + this.game.score + '"]\n\n';
let counter = 1;
let i = 0;
- while (i < this.moves.length)
- {
- pgn += (counter++) + ".";
- for (let color of ["w","b"])
- {
+ while (i < this.moves.length) {
+ pgn += counter++ + ".";
+ for (let color of ["w", "b"]) {
let move = "";
while (i < this.moves.length && this.moves[i].color == color)
move += this.moves[i++].notation + ",";
- move = move.slice(0,-1); //remove last comma
+ move = move.slice(0, -1); //remove last comma
pgn += move + (i < this.moves.length ? " " : "");
}
}
this.endgameMessage = message;
let modalBox = document.getElementById("modalEog");
modalBox.checked = true;
- setTimeout(() => { modalBox.checked = false; }, 2000);
+ setTimeout(() => {
+ modalBox.checked = false;
+ }, 2000);
},
animateMove: function(move, callback) {
let startSquare = document.getElementById(getSquareId(move.start));
// TODO: error "flush nextTick callbacks" when observer reloads page:
// this late check is not a fix!
- if (!startSquare)
- return;
+ if (!startSquare) return;
let endSquare = document.getElementById(getSquareId(move.end));
let rectStart = startSquare.getBoundingClientRect();
let rectEnd = endSquare.getBoundingClientRect();
- let translation = {x:rectEnd.x-rectStart.x, y:rectEnd.y-rectStart.y};
- let movingPiece =
- document.querySelector("#" + getSquareId(move.start) + " > img.piece");
- if (!movingPiece) //TODO: shouldn't happen
+ let translation = {
+ x: rectEnd.x - rectStart.x,
+ y: rectEnd.y - rectStart.y
+ };
+ let movingPiece = document.querySelector(
+ "#" + getSquareId(move.start) + " > img.piece"
+ );
+ if (!movingPiece)
+ //TODO: shouldn't happen
return;
// HACK for animation (with positive translate, image slides "under background")
// Possible improvement: just alter squares on the piece's way...
const squares = document.getElementsByClassName("board");
- for (let i=0; i<squares.length; i++)
- {
+ for (let i = 0; i < squares.length; i++) {
let square = squares.item(i);
- if (square.id != getSquareId(move.start))
- square.style.zIndex = "-1";
+ if (square.id != getSquareId(move.start)) square.style.zIndex = "-1";
}
- movingPiece.style.transform = "translate(" + translation.x + "px," +
- translation.y + "px)";
+ movingPiece.style.transform =
+ "translate(" + translation.x + "px," + translation.y + "px)";
movingPiece.style.transitionDuration = "0.2s";
movingPiece.style.zIndex = "3000";
- setTimeout( () => {
- for (let i=0; i<squares.length; i++)
+ setTimeout(() => {
+ for (let i = 0; i < squares.length; i++)
squares.item(i).style.zIndex = "auto";
movingPiece.style = {}; //required e.g. for 0-0 with KR swap
callback();
const navigate = !move;
// Forbid playing outside analyze mode, except if move is received.
// Sufficient condition because Board already knows which turn it is.
- if (!navigate && this.game.mode!="analyze" && !receive
- && (this.game.score != "*" || this.cursor < this.moves.length-1))
- {
+ if (
+ !navigate &&
+ this.game.mode != "analyze" &&
+ !receive &&
+ (this.game.score != "*" || this.cursor < this.moves.length - 1)
+ ) {
return;
}
const doPlayMove = () => {
- if (!!receive && this.cursor < this.moves.length-1)
- this.gotoEnd(); //required to play the move
- if (navigate)
- {
- if (this.cursor == this.moves.length-1)
- return; //no more moves
- move = this.moves[this.cursor+1];
- }
- else
- {
+ if (!!receive && this.cursor < this.moves.length - 1) this.gotoEnd(); //required to play the move
+ if (navigate) {
+ if (this.cursor == this.moves.length - 1) return; //no more moves
+ move = this.moves[this.cursor + 1];
+ } else {
move.color = this.vr.turn;
move.notation = this.vr.getNotation(move);
}
this.cursor++;
this.lastMove = move;
if (this.st.settings.sound == 2)
- new Audio("/sounds/move.mp3").play().catch(err => {});
- if (!navigate)
- {
+ new Audio("/sounds/move.mp3").play().catch(() => {});
+ if (!navigate) {
move.fen = this.vr.getFen();
// Stack move on movesList at current cursor
- if (this.cursor == this.moves.length)
- this.moves.push(move);
- else
- this.moves = this.moves.slice(0,this.cursor).concat([move]);
+ if (this.cursor == this.moves.length) this.moves.push(move);
+ else this.moves = this.moves.slice(0, this.cursor).concat([move]);
}
// Is opponent in check?
this.incheck = this.vr.getCheckSquares(this.vr.turn);
const score = this.vr.getCurrentScore();
- if (score != "*")
- {
+ if (score != "*") {
const message = getScoreMessage(score);
if (this.game.mode != "analyze")
this.$emit("gameover", score, message);
- else //just show score on screen (allow undo)
- this.showEndgameMsg(score + " . " + message);
+ //just show score on screen (allow undo)
+ else this.showEndgameMsg(score + " . " + message);
}
- if (!navigate && this.game.mode!="analyze")
+ if (!navigate && this.game.mode != "analyze")
this.$emit("newmove", move); //post-processing (e.g. computer play)
};
if (!!receive && this.game.vname != "Dark")
this.animateMove(move, doPlayMove);
- else
- doPlayMove();
+ else doPlayMove();
},
undo: function(move) {
const navigate = !move;
- if (navigate)
- {
- if (this.cursor < 0)
- return; //no more moves
+ if (navigate) {
+ if (this.cursor < 0) return; //no more moves
move = this.moves[this.cursor];
}
this.vr.undo(move);
this.cursor--;
- this.lastMove = (this.cursor >= 0 ? this.moves[this.cursor] : undefined);
+ this.lastMove = this.cursor >= 0 ? this.moves[this.cursor] : undefined;
if (this.st.settings.sound == 2)
- new Audio("/sounds/undo.mp3").play().catch(err => {});
+ new Audio("/sounds/undo.mp3").play().catch(() => {});
this.incheck = this.vr.getCheckSquares(this.vr.turn);
- if (!navigate)
- this.moves.pop();
+ if (!navigate) this.moves.pop();
},
gotoMove: function(index) {
this.vr.re_init(this.moves[index].fen);
this.lastMove = this.moves[index];
},
gotoBegin: function() {
- if (this.cursor == -1)
- return;
+ if (this.cursor == -1) return;
this.vr.re_init(this.game.fenStart);
- if (this.moves.length > 0 && this.moves[0].notation == "...")
- {
+ if (this.moves.length > 0 && this.moves[0].notation == "...") {
this.cursor = 0;
this.lastMove = this.moves[0];
- }
- else
- {
+ } else {
this.cursor = -1;
this.lastMove = null;
}
},
gotoEnd: function() {
- if (this.cursor == this.moves.length - 1)
- return;
- this.gotoMove(this.moves.length-1);
+ if (this.cursor == this.moves.length - 1) return;
+ this.gotoMove(this.moves.length - 1);
},
flip: function() {
this.orientation = V.GetOppCol(this.orientation);
- },
- },
+ }
+ }
};
</script>
import { ArrayFun } from "@/utils/array";
import { store } from "@/store";
export default {
- name: 'my-board',
+ name: "my-board",
// Last move cannot be guessed from here, and is required to highlight squares
// vr: object to check moves, print board...
// userColor is left undefined for an external observer
- props: ["vr","lastMove","analyze","incheck","orientation","userColor","vname"],
- data: function () {
+ props: [
+ "vr",
+ "lastMove",
+ "analyze",
+ "incheck",
+ "orientation",
+ "userColor",
+ "vname"
+ ],
+ data: function() {
return {
possibleMoves: [], //filled after each valid click/dragstart
choices: [], //promotion pieces, or checkered captures... (as moves)
selectedPiece: null, //moving piece (or clicked piece)
start: {}, //pixels coordinates + id of starting square (click or drag)
- settings: store.state.settings,
+ settings: store.state.settings
};
},
render(h) {
- if (!this.vr)
- {
+ if (!this.vr) {
// Return empty div of class 'game' to avoid error when setting size
- return h("div",
- {
- "class": {
- "game": true,
- },
- });
+ return h("div", {
+ class: {
+ game: true
+ }
+ });
}
- const [sizeX,sizeY] = [V.size.x,V.size.y];
+ const [sizeX, sizeY] = [V.size.x, V.size.y];
// Precompute hints squares to facilitate rendering
let hintSquares = ArrayFun.init(sizeX, sizeY, false);
- this.possibleMoves.forEach(m => { hintSquares[m.end.x][m.end.y] = true; });
+ this.possibleMoves.forEach(m => {
+ hintSquares[m.end.x][m.end.y] = true;
+ });
// Also precompute in-check squares
let incheckSq = ArrayFun.init(sizeX, sizeY, false);
- this.incheck.forEach(sq => { incheckSq[sq[0]][sq[1]] = true; });
+ this.incheck.forEach(sq => {
+ incheckSq[sq[0]][sq[1]] = true;
+ });
// Create board element (+ reserves if needed by variant or mode)
const lm = this.lastMove;
const showLight = this.settings.highlight && this.vname != "Dark";
const gameDiv = h(
- 'div',
+ "div",
{
- 'class': {
- 'game': true,
- 'clearer': true,
- },
+ class: {
+ game: true,
+ clearer: true
+ }
},
[...Array(sizeX).keys()].map(i => {
- let ci = (this.orientation=='w' ? i : sizeX-i-1);
+ let ci = this.orientation == "w" ? i : sizeX - i - 1;
return h(
- 'div',
+ "div",
{
- 'class': {
- 'row': true,
+ class: {
+ row: true
},
- style: { 'opacity': this.choices.length>0?"0.5":"1" },
+ style: { opacity: this.choices.length > 0 ? "0.5" : "1" }
},
[...Array(sizeY).keys()].map(j => {
- let cj = (this.orientation=='w' ? j : sizeY-j-1);
+ let cj = this.orientation == "w" ? j : sizeY - j - 1;
let elems = [];
- if (this.vr.board[ci][cj] != V.EMPTY && (this.vname!="Dark"
- || this.analyze || (!!this.userColor
- && this.vr.enlightened[this.userColor][ci][cj])))
- {
+ if (
+ this.vr.board[ci][cj] != V.EMPTY &&
+ (this.vname != "Dark" ||
+ this.analyze ||
+ (!!this.userColor &&
+ this.vr.enlightened[this.userColor][ci][cj]))
+ ) {
elems.push(
- h(
- 'img',
- {
- 'class': {
- 'piece': true,
- 'ghost': !!this.selectedPiece
- && this.selectedPiece.parentNode.id == "sq-"+ci+"-"+cj,
- },
- attrs: {
- src: "/images/pieces/" +
- V.getPpath(this.vr.board[ci][cj]) + ".svg",
- },
+ h("img", {
+ class: {
+ piece: true,
+ ghost:
+ !!this.selectedPiece &&
+ this.selectedPiece.parentNode.id == "sq-" + ci + "-" + cj
+ },
+ attrs: {
+ src:
+ "/images/pieces/" +
+ V.getPpath(this.vr.board[ci][cj]) +
+ ".svg"
}
- )
+ })
);
}
- if (this.settings.hints && hintSquares[ci][cj])
- {
+ if (this.settings.hints && hintSquares[ci][cj]) {
elems.push(
- h(
- 'img',
- {
- 'class': {
- 'mark-square': true,
- },
- attrs: {
- src: "/images/mark.svg",
- },
+ h("img", {
+ class: {
+ "mark-square": true
+ },
+ attrs: {
+ src: "/images/mark.svg"
}
- )
+ })
);
}
return h(
- 'div',
+ "div",
{
- 'class': {
- 'board': true,
- ['board'+sizeY]: true,
- 'light-square': (i+j)%2==0,
- 'dark-square': (i+j)%2==1,
+ class: {
+ board: true,
+ ["board" + sizeY]: true,
+ "light-square": (i + j) % 2 == 0,
+ "dark-square": (i + j) % 2 == 1,
[this.settings.bcolor]: true,
- 'in-shadow': this.vname=="Dark" && !this.analyze
- && (!this.userColor
- || !this.vr.enlightened[this.userColor][ci][cj]),
- 'highlight': showLight && !!lm && lm.end.x == ci && lm.end.y == cj,
- 'incheck': showLight && incheckSq[ci][cj],
+ "in-shadow":
+ this.vname == "Dark" &&
+ !this.analyze &&
+ (!this.userColor ||
+ !this.vr.enlightened[this.userColor][ci][cj]),
+ highlight:
+ showLight && !!lm && lm.end.x == ci && lm.end.y == cj,
+ incheck: showLight && incheckSq[ci][cj]
},
attrs: {
- id: getSquareId({x:ci,y:cj}),
- },
+ id: getSquareId({ x: ci, y: cj })
+ }
},
elems
);
);
let elementArray = [gameDiv];
const playingColor = this.userColor || "w"; //default for an observer
- if (!!this.vr.reserve)
- {
- const shiftIdx = (playingColor=="w" ? 0 : 1);
+ if (this.vr.reserve) {
+ const shiftIdx = playingColor == "w" ? 0 : 1;
let myReservePiecesArray = [];
- for (let i=0; i<V.RESERVE_PIECES.length; i++)
- {
- myReservePiecesArray.push(h('div',
- {
- 'class': {'board':true, ['board'+sizeY]:true},
- attrs: { id: getSquareId({x:sizeX+shiftIdx,y:i}) }
- },
- [
- h('img',
- {
- 'class': {"piece":true, "reserve":true},
- attrs: {
- "src": "/images/pieces/" +
- this.vr.getReservePpath(playingColor,i) + ".svg",
- }
- }),
- h('sup',
- {"class": { "reserve-count": true } },
- [ this.vr.reserve[playingColor][V.RESERVE_PIECES[i]] ]
+ for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
+ myReservePiecesArray.push(
+ h(
+ "div",
+ {
+ class: { board: true, ["board" + sizeY]: true },
+ attrs: { id: getSquareId({ x: sizeX + shiftIdx, y: i }) }
+ },
+ [
+ h("img", {
+ class: { piece: true, reserve: true },
+ attrs: {
+ src:
+ "/images/pieces/" +
+ this.vr.getReservePpath(playingColor, i) +
+ ".svg"
+ }
+ }),
+ h("sup", { class: { "reserve-count": true } }, [
+ this.vr.reserve[playingColor][V.RESERVE_PIECES[i]]
+ ])
+ ]
)
- ]));
+ );
}
let oppReservePiecesArray = [];
const oppCol = V.GetOppCol(playingColor);
- for (let i=0; i<V.RESERVE_PIECES.length; i++)
- {
- oppReservePiecesArray.push(h('div',
- {
- 'class': {'board':true, ['board'+sizeY]:true},
- attrs: { id: getSquareId({x:sizeX+(1-shiftIdx),y:i}) }
- },
- [
- h('img',
- {
- 'class': {"piece":true, "reserve":true},
- attrs: {
- "src": "/images/pieces/" +
- this.vr.getReservePpath(oppCol,i) + ".svg",
- }
- }),
- h('sup',
- {"class": { "reserve-count": true } },
- [ this.vr.reserve[oppCol][V.RESERVE_PIECES[i]] ]
+ for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
+ oppReservePiecesArray.push(
+ h(
+ "div",
+ {
+ class: { board: true, ["board" + sizeY]: true },
+ attrs: { id: getSquareId({ x: sizeX + (1 - shiftIdx), y: i }) }
+ },
+ [
+ h("img", {
+ class: { piece: true, reserve: true },
+ attrs: {
+ src:
+ "/images/pieces/" +
+ this.vr.getReservePpath(oppCol, i) +
+ ".svg"
+ }
+ }),
+ h("sup", { class: { "reserve-count": true } }, [
+ this.vr.reserve[oppCol][V.RESERVE_PIECES[i]]
+ ])
+ ]
)
- ]));
+ );
}
- let reserves = h('div',
+ let reserves = h(
+ "div",
{
- 'class':{
- 'game': true,
- "reserve-div": true,
- },
+ class: {
+ game: true,
+ "reserve-div": true
+ }
},
[
- h('div',
+ h(
+ "div",
{
- 'class': {
- 'row': true,
- "reserve-row-1": true,
- },
+ class: {
+ row: true,
+ "reserve-row-1": true
+ }
},
myReservePiecesArray
),
- h('div',
- { 'class': { 'row': true }},
- oppReservePiecesArray
- )
+ h("div", { class: { row: true } }, oppReservePiecesArray)
]
);
elementArray.push(reserves);
}
const boardElt = document.querySelector(".game");
- if (this.choices.length > 0 && !!boardElt) //no choices to show at first drawing
- {
+ if (this.choices.length > 0 && !!boardElt) {
+ //no choices to show at first drawing
const squareWidth = boardElt.offsetWidth / sizeY;
const offset = [boardElt.offsetTop, boardElt.offsetLeft];
const choices = h(
- 'div',
+ "div",
{
- attrs: { "id": "choices" },
- 'class': { 'row': true },
+ attrs: { id: "choices" },
+ class: { row: true },
style: {
- "top": (offset[0] + (sizeY/2)*squareWidth-squareWidth/2) + "px",
- "left": (offset[1] + squareWidth*(sizeY - this.choices.length)/2) + "px",
- "width": (this.choices.length * squareWidth) + "px",
- "height": squareWidth + "px",
- },
+ top: offset[0] + (sizeY / 2) * squareWidth - squareWidth / 2 + "px",
+ left:
+ offset[1] +
+ (squareWidth * (sizeY - this.choices.length)) / 2 +
+ "px",
+ width: this.choices.length * squareWidth + "px",
+ height: squareWidth + "px"
+ }
},
- this.choices.map(m => { //a "choice" is a move
- return h('div',
+ this.choices.map(m => {
+ //a "choice" is a move
+ return h(
+ "div",
{
- 'class': {
- 'board': true,
- ['board'+sizeY]: true,
+ class: {
+ board: true,
+ ["board" + sizeY]: true
},
style: {
- 'width': (100/this.choices.length) + "%",
- 'padding-bottom': (100/this.choices.length) + "%",
- },
+ width: 100 / this.choices.length + "%",
+ "padding-bottom": 100 / this.choices.length + "%"
+ }
},
- [h('img',
- {
- attrs: { "src": '/images/pieces/' +
- V.getPpath(m.appear[0].c+m.appear[0].p) + '.svg' },
- 'class': { 'choice-piece': true },
- on: {
- "click": e => { this.play(m); this.choices=[]; },
+ [
+ h("img", {
+ attrs: {
+ src:
+ "/images/pieces/" +
+ V.getPpath(m.appear[0].c + m.appear[0].p) +
+ ".svg"
},
+ class: { "choice-piece": true },
+ on: {
+ click: () => {
+ this.play(m);
+ this.choices = [];
+ }
+ }
})
]
);
}
let onEvents = {};
// NOTE: click = mousedown + mouseup
- if ('ontouchstart' in window)
- {
+ if ("ontouchstart" in window) {
onEvents = {
on: {
touchstart: this.mousedown,
touchmove: this.mousemove,
- touchend: this.mouseup,
- },
+ touchend: this.mouseup
+ }
};
- }
- else
- {
+ } else {
onEvents = {
on: {
mousedown: this.mousedown,
mousemove: this.mousemove,
- mouseup: this.mouseup,
- },
+ mouseup: this.mouseup
+ }
};
}
- return h(
- 'div',
- onEvents,
- elementArray
- );
+ return h("div", onEvents, elementArray);
},
methods: {
mousedown: function(e) {
// Abort if a piece is already being processed, or target is not a piece.
// NOTE: just looking at classList[0] because piece is the first assigned class
- if (!!this.selectedPiece || e.target.classList[0] != "piece")
- return;
+ if (!!this.selectedPiece || e.target.classList[0] != "piece") return;
e.preventDefault(); //disable native drag & drop
let parent = e.target.parentNode; //the surrounding square
// Next few lines to center the piece on mouse cursor
let rect = parent.getBoundingClientRect();
this.start = {
- x: rect.x + rect.width/2,
- y: rect.y + rect.width/2,
- id: parent.id,
+ x: rect.x + rect.width / 2,
+ y: rect.y + rect.width / 2,
+ id: parent.id
};
this.selectedPiece = e.target.cloneNode();
- let spStyle = this.selectedPiece.style
+ let spStyle = this.selectedPiece.style;
spStyle.position = "absolute";
spStyle.top = 0;
spStyle.display = "inline-block";
spStyle.zIndex = 3000;
const startSquare = getSquareFromId(parent.id);
this.possibleMoves = [];
- const color = (this.analyze ? this.vr.turn : this.userColor);
- if (this.vr.canIplay(color,startSquare))
+ const color = this.analyze ? this.vr.turn : this.userColor;
+ if (this.vr.canIplay(color, startSquare))
this.possibleMoves = this.vr.getPossibleMovesFrom(startSquare);
// Next line add moving piece just after current image
// (required for Crazyhouse reserve)
parent.insertBefore(this.selectedPiece, e.target.nextSibling);
},
mousemove: function(e) {
- if (!this.selectedPiece)
- return;
+ if (!this.selectedPiece) return;
// There is an active element: move it around
- const [offsetX,offsetY] = !!e.clientX
- ? [e.clientX,e.clientY] //desktop browser
+ const [offsetX, offsetY] = e.clientX
+ ? [e.clientX, e.clientY] //desktop browser
: [e.changedTouches[0].pageX, e.changedTouches[0].pageY]; //smartphone
- this.selectedPiece.style.left = (offsetX-this.start.x) + "px";
- this.selectedPiece.style.top = (offsetY-this.start.y) + "px";
+ this.selectedPiece.style.left = offsetX - this.start.x + "px";
+ this.selectedPiece.style.top = offsetY - this.start.y + "px";
},
mouseup: function(e) {
- if (!this.selectedPiece)
- return;
+ if (!this.selectedPiece) return;
// There is an active element: obtain the move from start and end squares
this.selectedPiece.style.zIndex = -3000; //HACK to find square from final coords
- const [offsetX,offsetY] = !!e.clientX
- ? [e.clientX,e.clientY]
+ const [offsetX, offsetY] = e.clientX
+ ? [e.clientX, e.clientY]
: [e.changedTouches[0].pageX, e.changedTouches[0].pageY];
let landing = document.elementFromPoint(offsetX, offsetY);
this.selectedPiece.style.zIndex = 3000;
// Next condition: classList.contains(piece) fails because of marks
- while (landing.tagName == "IMG")
- landing = landing.parentNode;
- if (this.start.id == landing.id) //one or multi clicks on same piece
+ while (landing.tagName == "IMG") landing = landing.parentNode;
+ if (this.start.id == landing.id)
+ //one or multi clicks on same piece
return;
// OK: process move attempt, landing is a square node
let endSquare = getSquareFromId(landing.id);
let moves = this.findMatchingMoves(endSquare);
this.possibleMoves = [];
- if (moves.length > 1)
- this.choices = moves;
- else if (moves.length==1)
- this.play(moves[0]);
+ if (moves.length > 1) this.choices = moves;
+ else if (moves.length == 1) this.play(moves[0]);
// Else: impossible move
this.selectedPiece.parentNode.removeChild(this.selectedPiece);
delete this.selectedPiece;
// Run through moves list and return the matching set (if promotions...)
let moves = [];
this.possibleMoves.forEach(function(m) {
- if (endSquare[0] == m.end.x && endSquare[1] == m.end.y)
- moves.push(m);
+ if (endSquare[0] == m.end.x && endSquare[1] == m.end.y) moves.push(m);
});
return moves;
},
play: function(move) {
- this.$emit('play-move', move);
- },
- },
+ this.$emit("play-move", move);
+ }
+ }
};
</script>
props: ["challenges"],
data: function() {
return {
- st: store.state,
+ st: store.state
};
},
computed: {
sortedChallenges: function() {
// Show in order: challenges I sent, challenges I received, other challenges
- let minAdded = Number.MAX_SAFE_INTEGER
- let maxAdded = 0
+ let minAdded = Number.MAX_SAFE_INTEGER;
+ let maxAdded = 0;
let augmentedChalls = this.challenges.map(c => {
let priority = 0;
- if (!!c.to && c.to == this.st.user.name)
- priority = 1;
+ if (!!c.to && c.to == this.st.user.name) priority = 1;
else if (c.from.sid == this.st.user.sid || c.from.id == this.st.user.id)
priority = 2;
- if (c.added < minAdded)
- minAdded = c.added;
- if (c.added > maxAdded)
- maxAdded = c.added
- return Object.assign({}, c, {priority: priority});
+ if (c.added < minAdded) minAdded = c.added;
+ if (c.added > maxAdded) maxAdded = c.added;
+ return Object.assign({}, c, { priority: priority });
});
const deltaAdded = maxAdded - minAdded;
- return augmentedChalls.sort((c1,c2) => {
+ return augmentedChalls.sort((c1, c2) => {
return c2.priority - c1.priority + (c2.added - c1.added) / deltaAdded;
});
- },
- },
+ }
+ }
};
</script>
export default {
name: "my-chat",
// Prop 'pastChats' for corr games where chats are on server
- props: ["players","pastChats","newChat"],
+ props: ["players", "pastChats", "newChat"],
data: function() {
return {
st: store.state,
- chats: [], //chat messages after human game
+ chats: [] //chat messages after human game
};
},
watch: {
newChat: function(chat) {
if (chat.msg != "")
- this.chats.unshift({msg:chat.msg, name:chat.name || "@nonymous"});
- },
+ this.chats.unshift({ msg: chat.msg, name: chat.name || "@nonymous" });
+ }
},
methods: {
classObject: function(chat) {
return {
"my-chatmsg": chat.name == this.st.user.name,
- "opp-chatmsg": !!this.players && this.players.some(
- p => p.name == chat.name && p.name != this.st.user.name)
+ "opp-chatmsg":
+ !!this.players &&
+ this.players.some(
+ p => p.name == chat.name && p.name != this.st.user.name
+ )
};
},
sendChat: function() {
let chatInput = document.getElementById("inputChat");
const chatTxt = chatInput.value.trim();
- if (chatTxt == "")
- return; //nothing to send
+ if (chatTxt == "") return; //nothing to send
chatInput.value = "";
- const chat = {msg:chatTxt, name: this.st.user.name || "@nonymous"};
+ const chat = { msg: chatTxt, name: this.st.user.name || "@nonymous" };
this.$emit("mychat", chat);
this.chats.unshift(chat);
- },
- },
+ }
+ }
};
</script>
export default {
name: "my-computer-game",
components: {
- BaseGame,
+ BaseGame
},
// gameInfo: fen + mode + vname
// mode: "auto" (game comp vs comp) or "versus" (normal)
// Web worker to play computer moves without freezing interface:
timeStart: undefined, //time when computer starts thinking
compThink: false, //avoid asking a new move while one is being searched
- compWorker: null,
+ compWorker: null
};
},
watch: {
this.launchGame();
},
"gameInfo.score": function(newScore) {
- if (newScore != "*")
- {
+ if (newScore != "*") {
this.game.score = newScore; //user action
- if (!this.compThink)
- this.$emit("game-stopped"); //otherwise wait for comp
+ if (!this.compThink) this.$emit("game-stopped"); //otherwise wait for comp
}
- },
+ }
},
// Modal end of game, and then sub-components
created: function() {
this.compWorker = new Worker();
this.compWorker.onmessage = e => {
let compMove = e.data;
- if (!compMove)
- {
+ if (!compMove) {
this.compThink = false;
this.$emit("game-stopped"); //no more moves: mate or stalemate
return; //after game ends, no more moves, nothing to do
}
- if (!Array.isArray(compMove))
- compMove = [compMove]; //to deal with MarseilleRules
+ if (!Array.isArray(compMove)) compMove = [compMove]; //to deal with MarseilleRules
// Small delay for the bot to appear "more human"
- const delay = Math.max(500-(Date.now()-this.timeStart), 0);
+ const delay = Math.max(500 - (Date.now() - this.timeStart), 0);
setTimeout(() => {
- if (this.currentUrl != document.location.href)
- return; //page change
+ if (this.currentUrl != document.location.href) return; //page change
// NOTE: Dark and 2-moves are incompatible
- const animate = (this.gameInfo.vname != "Dark");
- const animDelay = (animate ? 250 : 0);
+ const animate = this.gameInfo.vname != "Dark";
+ const animDelay = animate ? 250 : 0;
let moveIdx = 0;
let self = this;
(function executeMove() {
self.$set(self.game, "moveToPlay", compMove[moveIdx++]);
- if (moveIdx >= compMove.length)
- {
+ if (moveIdx >= compMove.length) {
self.compThink = false;
- if (self.game.score != "*") //user action
+ if (self.game.score != "*")
+ //user action
self.$emit("game-stopped");
- }
- else
- setTimeout(executeMove, 500 + animDelay);
+ } else setTimeout(executeMove, 500 + animDelay);
})();
}, delay);
- }
- if (!!this.gameInfo.fen)
- this.launchGame();
+ };
+ if (this.gameInfo.fen) this.launchGame();
},
methods: {
launchGame: function() {
- this.compWorker.postMessage(["scripts",this.gameInfo.vname]);
- this.compWorker.postMessage(["init",this.gameInfo.fen]);
+ this.compWorker.postMessage(["scripts", this.gameInfo.vname]);
+ this.compWorker.postMessage(["init", this.gameInfo.fen]);
this.vr = new V(this.gameInfo.fen);
- const mycolor = (Math.random() < 0.5 ? "w" : "b");
- let players = [{name:"Myself"},{name:"Computer"}];
- if (mycolor == "b")
- players = players.reverse();
+ const mycolor = Math.random() < 0.5 ? "w" : "b";
+ let players = [{ name: "Myself" }, { name: "Computer" }];
+ if (mycolor == "b") players = players.reverse();
this.currentUrl = document.location.href; //to avoid playing outside page
// NOTE: fen and fenStart are redundant in game object
- this.game = Object.assign({},
- this.gameInfo,
- {
- fenStart: this.gameInfo.fen,
- players: players,
- mycolor: mycolor,
- score: "*",
- });
- this.compWorker.postMessage(["init",this.gameInfo.fen]);
+ this.game = Object.assign({}, this.gameInfo, {
+ fenStart: this.gameInfo.fen,
+ players: players,
+ mycolor: mycolor,
+ score: "*"
+ });
+ this.compWorker.postMessage(["init", this.gameInfo.fen]);
if (mycolor != "w" || this.gameInfo.mode == "auto")
this.playComputerMove();
},
this.compWorker.postMessage(["askmove"]);
},
processMove: function(move) {
- if (this.game.score != "*")
- return;
+ if (this.game.score != "*") return;
// Send the move to web worker (including his own moves)
- this.compWorker.postMessage(["newmove",move]);
+ this.compWorker.postMessage(["newmove", move]);
// subTurn condition for Marseille (and Avalanche) rules
- if ((!this.vr.subTurn || this.vr.subTurn <= 1)
- && (this.gameInfo.mode == "auto" || this.vr.turn != this.game.mycolor))
- {
+ if (
+ (!this.vr.subTurn || this.vr.subTurn <= 1) &&
+ (this.gameInfo.mode == "auto" || this.vr.turn != this.game.mycolor)
+ ) {
this.playComputerMove();
}
},
this.game.score = score;
this.game.scoreMsg = scoreMsg;
this.$emit("game-over", score); //bubble up to Rules.vue
- },
- },
+ }
+ }
};
</script>
return {
enterTime: Number.MAX_SAFE_INTEGER, //for a basic anti-bot strategy
st: store.state,
- infoMsg: "",
+ infoMsg: ""
};
},
methods: {
trySetEnterTime: function(event) {
- if (!!event.target.checked)
- {
+ if (event.target.checked) {
this.enterTime = Date.now();
this.infoMsg = "";
}
trySendMessage: function() {
// Basic anti-bot strategy:
const exitTime = Date.now();
- if (exitTime - this.enterTime < 5000)
- return;
+ if (exitTime - this.enterTime < 5000) return;
let email = document.getElementById("userEmail");
let subject = document.getElementById("mailSubject");
let content = document.getElementById("mailContent");
- const error = checkNameEmail({email: email});
- if (!!error)
- return alert(error);
- if (content.value.trim().length == 0)
- return alert(this.st.tr["Empty message"]);
- if (subject.value.trim().length == 0 && !confirm(this.st.tr["No subject. Send anyway?"]))
+ let error = checkNameEmail({ email: email });
+ if (!error && content.value.trim().length == 0)
+ error = this.st.tr["Empty message"];
+ if (error) {
+ alert(error);
+ return;
+ }
+ if (
+ subject.value.trim().length == 0 &&
+ !confirm(this.st.tr["No subject. Send anyway?"])
+ )
return;
// Message sending:
{
email: email.value,
subject: subject.value,
- content: content.value,
+ content: content.value
},
() => {
this.infoMsg = "Email sent!";
content.value = "";
}
);
- },
- },
+ }
+ }
};
</script>
import { GameStorage } from "@/utils/gameStorage";
export default {
name: "my-game-list",
- props: ["games","showBoth"],
+ props: ["games", "showBoth"],
data: function() {
return {
st: store.state,
- showCadence: true,
+ showCadence: true
};
},
mounted: function() {
// timeout to avoid calling too many time the adjust method
let timeoutLaunched = false;
- window.addEventListener("resize", (e) => {
- if (!timeoutLaunched)
- {
+ window.addEventListener("resize", () => {
+ if (!timeoutLaunched) {
timeoutLaunched = true;
- setTimeout( () => {
+ setTimeout(() => {
this.showCadence = window.innerWidth >= 425; //TODO: arbitrary
timeoutLaunched = false;
}, 500);
computed: {
sortedGames: function() {
// Show in order: games where it's my turn, my running games, my games, other games
- let minCreated = Number.MAX_SAFE_INTEGER
- let maxCreated = 0
+ let minCreated = Number.MAX_SAFE_INTEGER;
+ let maxCreated = 0;
let augmentedGames = this.games.map(g => {
let priority = 0;
- if (g.players.some(p => p.uid == this.st.user.id || p.sid == this.st.user.sid))
- {
+ if (
+ g.players.some(
+ p => p.uid == this.st.user.id || p.sid == this.st.user.sid
+ )
+ ) {
priority++;
- if (g.score == "*")
- {
+ if (g.score == "*") {
priority++;
- const myColor = g.players[0].uid == this.st.user.id
- || g.players[0].sid == this.st.user.sid
- ? "w"
- : "b";
+ const myColor =
+ g.players[0].uid == this.st.user.id ||
+ g.players[0].sid == this.st.user.sid
+ ? "w"
+ : "b";
// I play in this game, so g.fen will be defined
- if (!!g.fen.match(" " + myColor + " "))
- priority++;
+ if (g.fen.match(" " + myColor + " ")) priority++;
}
}
- if (g.created < minCreated)
- minCreated = g.created;
- if (g.created > maxCreated)
- maxCreated = g.created;
- return Object.assign({}, g, {priority: priority, myTurn: priority==3});
+ if (g.created < minCreated) minCreated = g.created;
+ if (g.created > maxCreated) maxCreated = g.created;
+ return Object.assign({}, g, {
+ priority: priority,
+ myTurn: priority == 3
+ });
});
const deltaCreated = maxCreated - minCreated;
- return augmentedGames.sort((g1,g2) => {
- return g2.priority - g1.priority +
- (g2.created - g1.created) / deltaCreated;
+ return augmentedGames.sort((g1, g2) => {
+ return (
+ g2.priority - g1.priority + (g2.created - g1.created) / deltaCreated
+ );
});
- },
+ }
},
methods: {
player_s: function(g) {
if (this.showBoth)
- return (g.players[0].name || "@nonymous") + " - " + (g.players[1].name || "@nonymous");
- if (this.st.user.sid == g.players[0].sid || this.st.user.id == g.players[0].uid)
+ return (
+ (g.players[0].name || "@nonymous") +
+ " - " +
+ (g.players[1].name || "@nonymous")
+ );
+ if (
+ this.st.user.sid == g.players[0].sid ||
+ this.st.user.id == g.players[0].uid
+ )
return g.players[1].name || "@nonymous";
return g.players[0].name || "@nonymous";
},
deleteGame: function(game, e) {
- if (game.score != "*")
- {
- if (confirm(this.st.tr["Remove game?"]))
- GameStorage.remove(game.id);
+ if (game.score != "*") {
+ if (confirm(this.st.tr["Remove game?"])) GameStorage.remove(game.id);
e.stopPropagation();
}
- },
- },
+ }
+ }
};
</script>
name: "my-language",
data: function() {
return {
- st: store.state,
+ st: store.state
};
},
mounted: function() {
// NOTE: better style would be in pug directly, but how?
document.querySelectorAll("#langSelect > option").forEach(opt => {
- if (opt.value == this.st.lang)
- opt.selected = true;
+ if (opt.value == this.st.lang) opt.selected = true;
});
},
methods: {
setLanguage: function(e) {
localStorage["lang"] = e.target.value;
store.setLanguage(e.target.value);
- },
- },
+ }
+ }
};
</script>
<script>
import { store } from "@/store";
export default {
- name: 'my-move-list',
- props: ["moves","cursor","score","message","firstNum"],
+ name: "my-move-list",
+ props: ["moves", "cursor", "score", "message", "firstNum"],
watch: {
cursor: function(newCursor) {
- if (window.innerWidth <= 767)
- return; //moves list is below: scrolling would hide chessboard
+ if (window.innerWidth <= 767) return; //moves list is below: scrolling would hide chessboard
// Count grouped moves until the cursor (if multi-moves):
let groupsCount = -1;
let curCol = undefined;
- for (let i=0; i<newCursor; i++)
- {
+ for (let i = 0; i < newCursor; i++) {
const m = this.moves[i];
- if (m.color != curCol)
- {
+ if (m.color != curCol) {
groupsCount++;
curCol = m.color;
}
}
// $nextTick to wait for table > tr to be rendered
- this.$nextTick( () => {
- let rows = document.querySelectorAll('#movesList tr');
- if (rows.length > 0)
- {
- rows[Math.floor(Math.max(groupsCount,0)/2)].scrollIntoView({
+ this.$nextTick(() => {
+ let rows = document.querySelectorAll("#movesList tr");
+ if (rows.length > 0) {
+ rows[Math.floor(Math.max(groupsCount, 0) / 2)].scrollIntoView({
behavior: "auto",
- block: "nearest",
+ block: "nearest"
});
}
});
- },
+ }
},
render(h) {
- if (this.moves.length == 0)
- return;
+ if (this.moves.length == 0) return h("div");
let tableContent = [];
let moveCounter = 0;
let tableRow = undefined;
let moveCells = undefined;
let curCellContent = "";
let firstIndex = 0;
- for (let i=0; i<this.moves.length; i++)
- {
- if (this.moves[i].color == "w")
- {
- if (i == 0 || i>0 && this.moves[i-1].color=="b")
- {
- if (!!tableRow)
- {
+ for (let i = 0; i < this.moves.length; i++) {
+ if (this.moves[i].color == "w") {
+ if (i == 0 || (i > 0 && this.moves[i - 1].color == "b")) {
+ if (tableRow) {
tableRow.children = moveCells;
tableContent.push(tableRow);
}
moveCells = [
- h(
- "td",
- { domProps: { innerHTML: (++moveCounter) + "." } }
- )
+ h("td", { domProps: { innerHTML: ++moveCounter + "." } })
];
- tableRow = h(
- "tr",
- { }
- );
+ tableRow = h("tr", {});
curCellContent = "";
firstIndex = i;
}
}
// Next condition is fine because even if the first move is black,
// there will be the "..." which count as white move.
- else if (this.moves[i].color == "b" && this.moves[i-1].color == "w")
+ else if (this.moves[i].color == "b" && this.moves[i - 1].color == "w")
firstIndex = i;
curCellContent += this.moves[i].notation;
- if (i < this.moves.length-1 && this.moves[i+1].color == this.moves[i].color)
+ if (
+ i < this.moves.length - 1 &&
+ this.moves[i + 1].color == this.moves[i].color
+ )
curCellContent += ",";
- else //color change
- {
+ //color change
+ else {
moveCells.push(
- h(
- "td",
- {
- domProps: { innerHTML: curCellContent },
- on: { click: () => this.gotoMove(i) },
- "class": { "highlight-lm":
- this.cursor >= firstIndex && this.cursor <= i },
+ h("td", {
+ domProps: { innerHTML: curCellContent },
+ on: { click: () => this.gotoMove(i) },
+ class: {
+ "highlight-lm": this.cursor >= firstIndex && this.cursor <= i
}
- )
+ })
);
curCellContent = "";
}
}
// Complete last row, which might not be full:
- if (moveCells.length-1 == 1)
- {
- moveCells.push(
- h(
- "td",
- { domProps: { innerHTML: "" } }
- )
- );
+ if (moveCells.length - 1 == 1) {
+ moveCells.push(h("td", { domProps: { innerHTML: "" } }));
}
tableRow.children = moveCells;
tableContent.push(tableRow);
let rootElements = [];
- if (!!this.score && this.score != "*")
- {
- const scoreDiv = h("div",
+ if (!!this.score && this.score != "*") {
+ const scoreDiv = h(
+ "div",
{
id: "scoreInfo",
style: {
- display: this.score!="*" ? "block" : "none",
- },
+ display: this.score != "*" ? "block" : "none"
+ }
},
- [
- h("p", this.score),
- h("p", store.state.tr[this.message]),
- ]
+ [h("p", this.score), h("p", store.state.tr[this.message])]
);
rootElements.push(scoreDiv);
}
h(
"table",
{
- "class": {
- "moves-list": true,
- },
+ class: {
+ "moves-list": true
+ }
},
tableContent
)
);
- return h(
- "div",
- { },
- rootElements);
+ return h("div", {}, rootElements);
},
methods: {
gotoMove: function(index) {
this.$emit("goto-move", index);
- },
- },
+ }
+ }
};
</script>
name: "my-settings",
data: function() {
return {
- st: store.state,
+ st: store.state
};
},
methods: {
updateSettings: function(event) {
- const propName =
- event.target.id.substr(3).replace(/^\w/, c => c.toLowerCase())
- let value = (["bcolor","sound"].includes(propName)
+ const propName = event.target.id
+ .substr(3)
+ .replace(/^\w/, c => c.toLowerCase());
+ let value = ["bcolor", "sound"].includes(propName)
? event.target.value
- : event.target.checked);
- if (propName == "sound")
- value = parseInt(value);
+ : event.target.checked;
+ if (propName == "sound") value = parseInt(value);
store.updateSetting(propName, value);
- },
- },
+ }
+ }
};
</script>
import { checkNameEmail } from "@/data/userCheck";
import { ajax } from "@/utils/ajax";
export default {
- name: 'my-upsert-user',
+ name: "my-upsert-user",
data: function() {
return {
nameOrEmail: "", //for login
logStage: "Login", //or Register
infoMsg: "",
enterTime: Number.MAX_SAFE_INTEGER, //for a basic anti-bot strategy
- st: store.state,
+ st: store.state
};
},
watch: {
nameOrEmail: function(newValue) {
- if (newValue.indexOf('@') >= 0)
- {
+ if (newValue.indexOf("@") >= 0) {
this.st.user.email = newValue;
this.st.user.name = "";
- }
- else
- {
+ } else {
this.st.user.name = newValue;
this.st.user.email = "";
}
- },
+ }
},
computed: {
submitMessage: function() {
- switch (this.stage)
- {
+ switch (this.stage) {
case "Login":
return "Go";
case "Register":
case "Update":
return "Apply";
}
+ return "Never reached";
},
stage: function() {
return this.st.user.id > 0 ? "Update" : this.logStage;
- },
+ }
},
methods: {
trySetEnterTime: function(event) {
- if (!!event.target.checked)
- {
+ if (event.target.checked) {
this.infoMsg = "";
this.enterTime = Date.now();
}
},
toggleStage: function() {
// Loop login <--> register (update is for logged-in users)
- this.logStage = (this.logStage == "Login" ? "Register" : "Login");
+ this.logStage = this.logStage == "Login" ? "Register" : "Login";
},
ajaxUrl: function() {
- switch (this.stage)
- {
+ switch (this.stage) {
case "Login":
return "/sendtoken";
case "Register":
case "Update":
return "/update";
}
+ return "Never reached";
},
ajaxMethod: function() {
- switch (this.stage)
- {
+ switch (this.stage) {
case "Login":
return "GET";
case "Register":
case "Update":
return "PUT";
}
+ return "Never reached";
},
infoMessage: function() {
- switch (this.stage)
- {
+ switch (this.stage) {
case "Login":
return "Connection token sent. Check your emails!";
case "Register":
case "Update":
return "Modifications applied!";
}
+ return "Never reached";
},
onSubmit: function() {
// Basic anti-bot strategy:
const exitTime = Date.now();
- if (this.stage == "Register" && exitTime - this.enterTime < 5000)
- return;
+ if (this.stage == "Register" && exitTime - this.enterTime < 5000) return;
let error = undefined;
- if (this.stage == 'Login')
- {
- const type = (this.nameOrEmail.indexOf('@') >= 0 ? "email" : "name");
- error = checkNameEmail({[type]: this.nameOrEmail});
+ if (this.stage == "Login") {
+ const type = this.nameOrEmail.indexOf("@") >= 0 ? "email" : "name";
+ error = checkNameEmail({ [type]: this.nameOrEmail });
+ } else error = checkNameEmail(this.st.user);
+ if (error) {
+ alert(error);
+ return;
}
- else
- error = checkNameEmail(this.st.user);
- if (!!error)
- return alert(error);
this.infoMsg = "Processing... Please wait";
- ajax(this.ajaxUrl(), this.ajaxMethod(),
- this.stage == "Login" ? { nameOrEmail: this.nameOrEmail } : this.st.user,
- res => {
+ ajax(
+ this.ajaxUrl(),
+ this.ajaxMethod(),
+ this.stage == "Login"
+ ? { nameOrEmail: this.nameOrEmail }
+ : this.st.user,
+ () => {
this.infoMsg = this.infoMessage();
- if (this.stage != "Update")
- this.nameOrEmail = "";
+ if (this.stage != "Update") this.nameOrEmail = "";
},
err => {
this.infoMsg = "";
doLogout: function() {
document.getElementById("modalUser").checked = false;
this.$router.push("/logout");
- },
- },
+ }
+ }
};
</script>
import { extractTime } from "@/utils/timeControl";
-export function checkChallenge(c)
-{
+export function checkChallenge(c) {
const vid = parseInt(c.vid);
- if (isNaN(vid) || vid <= 0)
- return "Please select a variant";
+ if (isNaN(vid) || vid <= 0) return "Please select a variant";
const tc = extractTime(c.cadence);
- if (!tc)
- return "Wrong time control";
+ if (!tc) return "Wrong time control";
// Basic alphanumeric check for opponent name
- if (!!c.to)
- {
- // NOTE: slightly redundant (see data/userCheck.js)
- if (!c.to.match(/^[\w]+$/))
- return "Wrong characters in opponent name";
+ if (c.to) {
+ // NOTE: slightly redundant (see data/userCheck.js)
+ if (!c.to.match(/^[\w]+$/)) return "Wrong characters in opponent name";
}
// Allow custom FEN (and check it) only for individual challenges
- if (c.fen.length > 0 && !!c.to)
- {
- if (!V.IsGoodFen(c.fen))
- return "Bad FEN string";
- }
- else
- c.fen = "";
+ if (c.fen.length > 0 && !!c.to) {
+ if (!V.IsGoodFen(c.fen)) return "Bad FEN string";
+ } else c.fen = "";
+
+ return "";
}
-export function checkProblem(p)
-{
+export function checkProblem(p) {
const vid = parseInt(p.vid);
- if (isNaN(vid) || vid <= 0)
- return "Please select a variant";
+ if (isNaN(vid) || vid <= 0) return "Please select a variant";
- if (!V.IsGoodFen(p.fen))
- return "Bad FEN string";
+ if (!V.IsGoodFen(p.fen)) return "Bad FEN string";
+
+ return "";
}
-export function checkNameEmail(o)
-{
- if (typeof o.name === "string")
- {
- if (o.name.length == 0)
- return "Empty name";
- if (!o.name.match(/^[\w]+$/))
- return "Bad characters in name";
+export function checkNameEmail(o) {
+ if (typeof o.name === "string") {
+ if (o.name.length == 0) return "Empty name";
+ if (!o.name.match(/^[\w]+$/)) return "Bad characters in name";
}
- if (typeof o.email === "string")
- {
- if (o.email.length == 0)
- return "Empty email";
- if (!o.email.match(/^[\w.+-]+@[\w.+-]+$/))
- return "Bad characters in email";
+ if (typeof o.email === "string") {
+ if (o.email.length == 0) return "Empty email";
+ if (!o.email.match(/^[\w.+-]+@[\w.+-]+$/)) return "Bad characters in email";
}
+ return "";
}
return h(App);
},
created: function() {
- window.doClick = (elemId) => { document.getElementById(elemId).click() };
- document.addEventListener("keydown", (e) => {
- if (e.code === "Escape")
- {
+ window.doClick = elemId => {
+ document.getElementById(elemId).click();
+ };
+ document.addEventListener("keydown", e => {
+ if (e.code === "Escape") {
let modalBoxes = document.querySelectorAll("[id^='modal']");
modalBoxes.forEach(m => {
- if (m.checked && m.id != "modalWelcome")
- m.checked = false;
+ if (m.checked && m.id != "modalWelcome") m.checked = false;
});
}
});
store.initialize();
- },
+ }
}).$mount("#app");
// Logic to play a computer move in a web worker
-onmessage = async function(e)
-{
- switch (e.data[0])
- {
- case "scripts":
+onmessage = async function(e) {
+ switch (e.data[0]) {
+ case "scripts": {
const vModule = await import("@/variants/" + e.data[1] + ".js");
self.V = vModule.VariantRules;
break;
- case "init":
+ }
+ case "init": {
const fen = e.data[1];
self.vr = new self.V(fen);
break;
+ }
case "newmove":
self.vr.play(e.data[1]);
break;
- case "askmove":
+ case "askmove": {
const compMove = self.vr.getComputerMove();
postMessage(compMove);
break;
+ }
}
-}
+};
Vue.use(Router);
function loadView(view) {
- return () => import(/* webpackChunkName: "view-[request]" */ `@/views/${view}.vue`)
+ return () =>
+ import(/* webpackChunkName: "view-[request]" */ `@/views/${view}.vue`);
}
-import { ajax } from "@/utils/ajax";
-import { store } from "@/store";
-
const router = new Router({
routes: [
{
path: "/",
name: "hall",
- component: Hall,
+ component: Hall
},
{
path: "/variants",
name: "variants",
- component: loadView("Variants"),
+ component: loadView("Variants")
},
{
path: "/variants/:vname([a-zA-Z0-9]+)",
name: "rules",
- component: loadView("Rules"),
+ component: loadView("Rules")
},
{
path: "/authenticate/:token",
name: "authenticate",
- component: loadView("Auth"),
+ component: loadView("Auth")
},
{
path: "/logout",
name: "logout",
- component: loadView("Logout"),
+ component: loadView("Logout")
},
{
path: "/problems",
name: "myproblems",
- component: loadView("Problems"),
+ component: loadView("Problems")
},
{
path: "/mygames",
name: "mygames",
- component: loadView("MyGames"),
+ component: loadView("MyGames")
},
{
path: "/game/:id([a-zA-Z0-9]+)",
name: "game",
- component: loadView("Game"),
+ component: loadView("Game")
},
{
path: "/analyse/:vname([a-zA-Z0-9]+)",
name: "analyse",
- component: loadView("Analyse"),
+ component: loadView("Analyse")
},
{
path: "/about",
name: "about",
- component: loadView("About"),
+ component: loadView("About")
},
{
path: "/news",
name: "news",
- component: loadView("News"),
- },
+ component: loadView("News")
+ }
]
});
import { getRandString } from "./utils/alea";
// Global store: see https://medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87
-export const store =
-{
+export const store = {
state: {
variants: [],
tr: {},
user: {},
settings: {},
- lang: "",
+ lang: ""
},
socketCloseListener: null,
initialize() {
- ajax("/variants", "GET", res => { this.state.variants = res.variantArray; });
+ ajax("/variants", "GET", res => {
+ this.state.variants = res.variantArray;
+ });
let mysid = localStorage.getItem("mysid");
- if (!mysid)
- {
+ if (!mysid) {
mysid = getRandString();
localStorage.setItem("mysid", mysid); //done only once (unless user clear browser data)
}
name: localStorage.getItem("myname") || "", //"" for "anonymous"
email: "", //unknown yet
notify: false, //email notifications
- sid: mysid,
+ sid: mysid
};
// Slow verification through the server:
// NOTE: still superficial identity usurpation possible, but difficult.
ajax("/whoami", "GET", res => {
this.state.user.id = res.id;
const storedId = localStorage.getItem("myid");
- if (res.id > 0 && !storedId) //user cleared localStorage
+ if (res.id > 0 && !storedId)
+ //user cleared localStorage
localStorage.setItem("myid", res.id);
- else if (res.id == 0 && !!storedId) //user cleared cookie
+ else if (res.id == 0 && !!storedId)
+ //user cleared cookie
localStorage.removeItem("myid");
this.state.user.name = res.name;
const storedName = localStorage.getItem("myname");
- if (!!res.name && !storedName) //user cleared localStorage
+ if (!!res.name && !storedName)
+ //user cleared localStorage
localStorage.setItem("myname", res.name);
- else if (!res.name && !!storedName) //user cleared cookie
+ else if (!res.name && !!storedName)
+ //user cleared cookie
localStorage.removeItem("myname");
this.state.user.email = res.email;
this.state.user.notify = res.notify;
bcolor: localStorage.getItem("bcolor") || "lichess",
sound: parseInt(localStorage.getItem("sound")) || 1,
hints: localStorage.getItem("hints") == "true",
- highlight: localStorage.getItem("highlight") == "true",
+ highlight: localStorage.getItem("highlight") == "true"
};
- const supportedLangs = ["en","es","fr"];
- this.state.lang = localStorage["lang"] ||
- (supportedLangs.includes(navigator.language)
- ? navigator.language
- : "en");
+ const supportedLangs = ["en", "es", "fr"];
+ this.state.lang =
+ localStorage["lang"] ||
+ (supportedLangs.includes(navigator.language) ? navigator.language : "en");
this.setTranslations();
},
updateSetting: function(propName, value) {
setLanguage(lang) {
this.state.lang = lang;
this.setTranslations();
- },
+ }
};
-export const translations =
-{
- "Abort": "Abort",
- "About": "About",
+export const translations = {
+ Abort: "Abort",
+ About: "About",
"Accept draw?": "Accept draw?",
- "All": "All",
- "Analyse": "Analyse",
- "Analyse in Dark mode makes no sense!": "Analyse in Dark mode makes no sense!",
+ All: "All",
+ Analyse: "Analyse",
+ "Analyse in Dark mode makes no sense!":
+ "Analyse in Dark mode makes no sense!",
"Are you sure?": "Are you sure?",
"Authentication successful!": "Authentication successful!",
- "Apply": "Apply",
+ Apply: "Apply",
"Back to list": "Back to list",
"Black to move": "Black to move",
"Black win": "Black win",
"Board colors": "Board colors",
"Board size": "Board size",
- "blue": "blue",
- "brown": "brown",
- "Cadence": "Cadence",
- "Challenge": "Challenge",
+ blue: "blue",
+ brown: "brown",
+ Cadence: "Cadence",
+ Challenge: "Challenge",
"Challenge declined": "Challenge declined",
"Chat here": "Chat here",
- "Connection token sent. Check your emails!": "Connection token sent. Check your emails!",
- "Contact": "Contact",
+ "Connection token sent. Check your emails!":
+ "Connection token sent. Check your emails!",
+ Contact: "Contact",
"Correspondance challenges": "Correspondance challenges",
"Correspondance games": "Correspondance games",
"Database error:": "Database error:",
- "Delete": "Delete",
- "Download": "Download",
- "Draw": "Draw",
+ Delete: "Delete",
+ Download: "Download",
+ Draw: "Draw",
"Draw offer only in your turn": "Draw offer only in your turn",
- "Edit": "Edit",
- "Email": "Email",
+ Edit: "Edit",
+ Email: "Email",
"Email sent!": "Email sent!",
"Empty message": "Empty message",
"Error while loading database:": "Error while loading database:",
"Example game": "Example game",
- "From": "From",
+ From: "From",
"Game retrieval failed:": "Game retrieval failed:",
"Game removal failed:": "Game removal failed:",
- "Go": "Go",
- "green": "green",
- "Hall": "Hall",
+ Go: "Go",
+ green: "green",
+ Hall: "Hall",
"Highlight last move and checks?": "Highlight last move and checks?",
- "Instructions": "Instructions",
- "Language": "Language",
+ Instructions: "Instructions",
+ Language: "Language",
"Live challenges": "Live challenges",
"Live games": "Live games",
"Load more": "Load more",
- "Login": "Login",
- "Logout": "Logout",
+ Login: "Login",
+ Logout: "Logout",
"Logout successful!": "Logout successful!",
"Modifications applied!": "Modifications applied!",
"Move played:": "Move played:",
"My games": "My games",
"My problems": "My problems",
"Name or Email": "Name or Email",
- "New connexion detected: tab now offline": "New connexion detected: tab now offline",
+ "New connexion detected: tab now offline":
+ "New connexion detected: tab now offline",
"New correspondance game:": "New correspondance game:",
"New game": "New game",
"New problem": "New problem",
- "News": "News",
+ News: "News",
"No subject. Send anyway?": "No subject. Send anyway?",
- "None": "None",
+ None: "None",
"Notifications by email": "Notifications by email",
- "Number": "Number",
- "Observe": "Observe",
+ Number: "Number",
+ Observe: "Observe",
"Offer draw?": "Offer draw?",
"Opponent action": "Opponent action",
"Play sounds?": "Play sounds?",
"Play with?": "Play with?",
- "Players": "Players",
- "Please log in to accept corr challenges": "Please log in to accept corr challenges",
- "Please log in to play correspondance games": "Please log in to play correspondance games",
+ Players: "Players",
+ "Please log in to accept corr challenges":
+ "Please log in to accept corr challenges",
+ "Please log in to play correspondance games":
+ "Please log in to play correspondance games",
"Please select a variant": "Please select a variant",
- "Practice": "Practice",
+ Practice: "Practice",
"Prefix?": "Prefix?",
"Processing... Please wait": "Processing... Please wait",
- "Problems": "Problems",
+ Problems: "Problems",
"participant(s):": "participant(s):",
- "Register": "Register",
- "Registration complete! Please check your emails": "Registration complete! Please check your emails",
+ Register: "Register",
+ "Registration complete! Please check your emails":
+ "Registration complete! Please check your emails",
"Remove game?": "Remove game?",
- "Resign": "Resign",
+ Resign: "Resign",
"Resign the game?": "Resign the game?",
- "Result": "Result",
- "Rules": "Rules",
- "Send": "Send",
+ Result: "Result",
+ Rules: "Rules",
+ Send: "Send",
"Self-challenge is forbidden": "Self-challenge is forbidden",
"Send challenge": "Send challenge",
- "Settings": "Settings",
+ Settings: "Settings",
"Show possible moves?": "Show possible moves?",
"Show solution": "Show solution",
- "Social": "Social",
- "Solution": "Solution",
+ Social: "Social",
+ Solution: "Solution",
"Stop game": "Stop game",
- "Subject": "Subject",
+ Subject: "Subject",
"Terminate game?": "Terminate game?",
"Three repetitions": "Three repetitions",
- "Time": "Time",
- "To": "To",
- "Unknown": "Unknown",
- "Update": "Update",
+ Time: "Time",
+ To: "To",
+ Unknown: "Unknown",
+ Update: "Update",
"User name": "User name",
- "Variant": "Variant",
- "Variants": "Variants",
- "Versus": "Versus",
+ Variant: "Variant",
+ Variants: "Variants",
+ Versus: "Versus",
"White to move": "White to move",
"White win": "White win",
"Who's there?": "Who's there?",
"Pawns move diagonally": "Pawns move diagonally",
"Reverse captures": "Reverse captures",
"Shared pieces": "Shared pieces",
- "Standard rules": "Standard rules",
+ "Standard rules": "Standard rules"
};
-export const translations =
-{
- "Abort": "Terminar",
- "About": "Acerca de",
+export const translations = {
+ Abort: "Terminar",
+ About: "Acerca de",
"Accept draw?": "¿Acceptar tablas?",
- "All": "Todos",
- "Analyse": "Analizar",
- "Analyse in Dark mode makes no sense!": "¡Analizar en modo Dark no tiene sentido!",
- "Apply": "Aplicar",
+ All: "Todos",
+ Analyse: "Analizar",
+ "Analyse in Dark mode makes no sense!":
+ "¡Analizar en modo Dark no tiene sentido!",
+ Apply: "Aplicar",
"Are you sure?": "¿Está usted seguro?",
"Authentication successful!": "¡Autenticación exitosa!",
"Back to list": "Volver a la lista",
- "Black": "Negras",
+ Black: "Negras",
"Black to move": "Juegan las negras",
"Black win": "Las negras gagnan",
"Board colors": "Colores del tablero",
"Board size": "Tamaño del tablero",
- "blue": "azul",
- "brown": "marrón",
- "Cadence": "Cadencia",
- "Challenge": "Desafiar",
+ blue: "azul",
+ brown: "marrón",
+ Cadence: "Cadencia",
+ Challenge: "Desafiar",
"Challenge declined": "DesafÃo rechazado",
"Chat here": "Chat aquÃ",
- "Connection token sent. Check your emails!": "Token de conexión enviado. ¡Revisa tus correos!",
- "Contact": "Contacto",
+ "Connection token sent. Check your emails!":
+ "Token de conexión enviado. ¡Revisa tus correos!",
+ Contact: "Contacto",
"Correspondance challenges": "DesafÃos por correspondencia",
"Correspondance games": "Partidas por correspondencia",
"Database error:": "Error de la base de datos:",
- "Delete": "Borrar",
- "Download": "Descargar",
- "Draw": "Tablas",
+ Delete: "Borrar",
+ Download: "Descargar",
+ Draw: "Tablas",
"Draw offer only in your turn": "Oferta de tablas solo en tu turno",
- "Edit": "Editar",
- "Email": "Email",
+ Edit: "Editar",
+ Email: "Email",
"Email sent!": "¡Email enviado!",
"Empty message": "Mensaje vacio",
"Error while loading database:": "Error al cargar la base de datos:",
"Example game": "Ejemplo de partida",
- "From": "De",
+ From: "De",
"Game retrieval failed:": "La recuperación de la partida falló:",
"Game removal failed:": "La eliminación de la partida falló:",
- "Go": "Go",
- "green": "verde",
- "Hall": "Salón",
+ Go: "Go",
+ green: "verde",
+ Hall: "Salón",
"Highlight last move and checks?": "¿Resaltar el último movimiento y jaques?",
- "Instructions": "Instrucciones",
- "Language": "Idioma",
+ Instructions: "Instrucciones",
+ Language: "Idioma",
"Live challenges": "DesafÃos en vivo",
"Live games": "Partidas en vivo",
"Load more": "Cargar más",
- "Login": "Login",
- "Logout": "Logout",
+ Login: "Login",
+ Logout: "Logout",
"Logout successful!": "¡Desconexión exitosa!",
"Modifications applied!": "¡Modificaciones aplicadas!",
"Move played:": "Movimiento jugado:",
"My games": "Mis partidas",
"My problems": "Mis problemas",
"Name or Email": "Nombre o Email",
- "New connexion detected: tab now offline": "Nueva conexión detectada: pestaña ahora desconectada",
+ "New connexion detected: tab now offline":
+ "Nueva conexión detectada: pestaña ahora desconectada",
"New correspondance game:": "Nueva partida por correspondencia:",
"New game": "Nueva partida",
"New problem": "Nuevo problema",
- "News": "Noticias",
+ News: "Noticias",
"No subject. Send anyway?": "Sin asunto. ¿Enviar sin embargo?",
- "None": "Ninguno",
+ None: "Ninguno",
"Notifications by email": "Notificaciones por email",
- "Number": "Número",
+ Number: "Número",
"Offer draw?": "¿Ofrecer tablas?",
- "Observe": "Observar",
+ Observe: "Observar",
"Opponent action": "Acción del adversario",
"Play sounds?": "¿Permitir sonidos?",
"Play with?": "¿Jugar con?",
- "Players": "Jugadores",
- "Please log in to accept corr challenges": "Inicia sesión para aceptar los desafÃos por correspondencia",
- "Please log in to play correspondance games": "Inicia sesión para jugar partidas por correspondancia",
+ Players: "Jugadores",
+ "Please log in to accept corr challenges":
+ "Inicia sesión para aceptar los desafÃos por correspondencia",
+ "Please log in to play correspondance games":
+ "Inicia sesión para jugar partidas por correspondancia",
"Please select a variant": "Por favor seleccione una variante",
- "Practice": "Práctica",
+ Practice: "Práctica",
"Prefix?": "¿Prefijo?",
"Processing... Please wait": "Procesando... por favor espere",
- "Problems": "Problemas",
+ Problems: "Problemas",
"participant(s):": "participante(s):",
- "Register": "Registrarse",
- "Registration complete! Please check your emails": "¡Registro completo! Por favor revise sus correos electrónicos",
+ Register: "Registrarse",
+ "Registration complete! Please check your emails":
+ "¡Registro completo! Por favor revise sus correos electrónicos",
"Remove game?": "¿Eliminar la partida?",
- "Resign": "Abandonar",
+ Resign: "Abandonar",
"Resign the game?": "¿Abandonar la partida?",
- "Result": "Resultado",
- "Rules": "Reglas",
- "Send": "Enviar",
+ Result: "Resultado",
+ Rules: "Reglas",
+ Send: "Enviar",
"Self-challenge is forbidden": "Auto desafÃo está prohibido",
"Send challenge": "Enviar desafÃo",
- "Settings": "Configuraciones",
+ Settings: "Configuraciones",
"Show possible moves?": "¿Mostrar posibles movimientos?",
"Show solution": "Mostrar la solución",
- "Social": "Social",
- "Solution": "Solución",
+ Social: "Social",
+ Solution: "Solución",
"Stop game": "Terminar la partida",
- "Subject": "Asunto",
+ Subject: "Asunto",
"Terminate game?": "¿Terminar la partida?",
"Three repetitions": "Tres repeticiones",
- "Time": "Tiempo",
- "To": "A",
- "Unknown": "Desconocido",
- "Update": "Actualización",
+ Time: "Tiempo",
+ To: "A",
+ Unknown: "Desconocido",
+ Update: "Actualización",
"User name": "Nombre de usuario",
- "Variant": "Variante",
- "Variants": "Variantes",
- "Versus": "Contra",
- "White": "Blancas",
+ Variant: "Variante",
+ Variants: "Variantes",
+ Versus: "Contra",
+ White: "Blancas",
"White to move": "Juegan las blancas",
"White win": "Las blancas gagnan",
"Who's there?": "¿Quién está ah�",
"Pawns move diagonally": "Peones se mueven en diagonal",
"Reverse captures": "Capturas invertidas",
"Shared pieces": "Piezas compartidas",
- "Standard rules": "Reglas estandar",
+ "Standard rules": "Reglas estandar"
};
-export const translations =
-{
- "Abort": "Arrêter",
- "About": "À propos",
+export const translations = {
+ Abort: "Arrêter",
+ About: "À propos",
"Accept draw?": "Accepter la nulle ?",
- "All": "Tous",
- "Analyse": "Analyser",
- "Analyse in Dark mode makes no sense!": "Analyser en mode Dark n'a pas de sens !",
- "Apply": "Appliquer",
+ All: "Tous",
+ Analyse: "Analyser",
+ "Analyse in Dark mode makes no sense!":
+ "Analyser en mode Dark n'a pas de sens !",
+ Apply: "Appliquer",
"Authentication successful!": "Authentification réussie !",
"Are you sure?": "Étes vous sûr?",
"Back to list": "Retour à la liste",
- "Black": "Noirs",
+ Black: "Noirs",
"Black to move": "Trait aux noirs",
"Black win": "Les noirs gagnent",
"Board colors": "Couleurs de l'échiquier",
"Board size": "Taille de l'échiquier",
- "blue": "bleu",
- "brown": "marron",
- "Cadence": "Cadence",
- "Challenge": "Défier",
+ blue: "bleu",
+ brown: "marron",
+ Cadence: "Cadence",
+ Challenge: "Défier",
"Challenge declined": "Défi refusé",
"Chat here": "Chattez ici",
- "Connection token sent. Check your emails!": "Token de connection envoyé. Allez voir vos emails !",
- "Contact": "Contact",
+ "Connection token sent. Check your emails!":
+ "Token de connection envoyé. Allez voir vos emails !",
+ Contact: "Contact",
"Correspondance challenges": "Défis par correspondance",
"Correspondance games": "Parties par correspondance",
"Database error:": "Erreur de base de données :",
- "Delete": "Supprimer",
- "Download": "Télécharger",
- "Draw": "Nulle",
- "Draw offer only in your turn": "Proposition de nulle seulement sur votre temps",
- "Edit": "Éditer",
- "Email": "Email",
+ Delete: "Supprimer",
+ Download: "Télécharger",
+ Draw: "Nulle",
+ "Draw offer only in your turn":
+ "Proposition de nulle seulement sur votre temps",
+ Edit: "Éditer",
+ Email: "Email",
"Email sent!": "Email envoyé !",
"Empty message": "Message vide",
- "Error while loading database:": "Erreur lors du chargement de la base de données :",
+ "Error while loading database:":
+ "Erreur lors du chargement de la base de données :",
"Example game": "Partie exemple",
- "From": "De",
+ From: "De",
"Game retrieval failed:": "Échec de la récupération de la partie :",
"Game removal failed:": "Échec de la suppresion de la partie :",
- "Go": "Go",
- "green": "vert",
- "Hall": "Salon",
- "Highlight last move and checks?": "Mettre en valeur le dernier coup et les échecs ?",
- "Instructions": "Instructions",
- "Language": "Langue",
+ Go: "Go",
+ green: "vert",
+ Hall: "Salon",
+ "Highlight last move and checks?":
+ "Mettre en valeur le dernier coup et les échecs ?",
+ Instructions: "Instructions",
+ Language: "Langue",
"Live challenges": "Défis en direct",
"Live games": "Parties en direct",
"Load more": "Charger plus",
- "Login": "Login",
- "Logout": "Logout",
+ Login: "Login",
+ Logout: "Logout",
"Logout successful!": "Déconnection réussie !",
"Modifications applied!": "Modifications effectuées !",
"Move played:": "Coup joué :",
"My games": "Mes parties",
"My problems": "Mes problèmes",
"Name or Email": "Nom ou Email",
- "New connexion detected: tab now offline": "Nouvelle connexion détectée : onglet désormais hors ligne",
+ "New connexion detected: tab now offline":
+ "Nouvelle connexion détectée : onglet désormais hors ligne",
"New correspondance game:": "Nouvelle partie par corespondance :",
"New game": "Nouvelle partie",
"New problem": "Nouveau problème",
- "News": "Nouvelles",
+ News: "Nouvelles",
"No subject. Send anyway?": "Pas de sujet. Envoyer quand-même ??",
- "None": "Aucun",
+ None: "Aucun",
"Notifications by email": "Notifications par email",
- "Number": "Numéro",
+ Number: "Numéro",
"Offer draw?": "Proposer nulle ?",
- "Observe": "Observer",
+ Observe: "Observer",
"Opponent action": "Action de l'adversaire",
"Play sounds?": "Jouer les sons ?",
"Play with?": "Jouer avec ?",
- "Players": "Joueurs",
- "Please log in to accept corr challenges": "Identifiez vous pour accepter des défis par correspondance",
- "Please log in to play correspondance games": "Identifiez vous pour jouer des parties par correspondance",
+ Players: "Joueurs",
+ "Please log in to accept corr challenges":
+ "Identifiez vous pour accepter des défis par correspondance",
+ "Please log in to play correspondance games":
+ "Identifiez vous pour jouer des parties par correspondance",
"Please select a variant": "Sélectionnez une variante SVP",
- "Practice": "Pratiquer",
+ Practice: "Pratiquer",
"Prefix?": "Préfixe ?",
"Processing... Please wait": "Traitement en cours... Attendez SVP",
- "Problems": "Problèmes",
+ Problems: "Problèmes",
"participant(s):": "participant(s) :",
- "Register": "S'enregistrer",
- "Registration complete! Please check your emails": "Enregistrement terminé ! Allez voir vos emails",
+ Register: "S'enregistrer",
+ "Registration complete! Please check your emails":
+ "Enregistrement terminé ! Allez voir vos emails",
"Remove game?": "Supprimer la partie ?",
- "Resign": "Abandonner",
+ Resign: "Abandonner",
"Resign the game?": "Abandonner la partie ?",
- "Result": "Résultat",
- "Rules": "Règles",
- "Send": "Envoyer",
+ Result: "Résultat",
+ Rules: "Règles",
+ Send: "Envoyer",
"Self-challenge is forbidden": "Interdit de s'auto-défier",
"Send challenge": "Envoyer défi",
- "Settings": "Réglages",
+ Settings: "Réglages",
"Show possible moves?": "Montrer les coups possibles ?",
"Show solution": "Montrer la solution",
- "Social": "Social",
- "Solution": "Solution",
+ Social: "Social",
+ Solution: "Solution",
"Stop game": "Arrêter la partie",
- "Subject": "Sujet",
+ Subject: "Sujet",
"Terminate game?": "Stopper la partie ?",
"Three repetitions": "Triple répétition",
- "Time": "Temps",
- "To": "À",
- "Unknown": "Inconnu",
- "Update": "Mise à jour",
+ Time: "Temps",
+ To: "À",
+ Unknown: "Inconnu",
+ Update: "Mise à jour",
"User name": "Nom d'utilisateur",
- "Variant": "Variante",
- "Variants": "Variantes",
- "Versus": "Contre",
- "White": "Blancs",
+ Variant: "Variante",
+ Variants: "Variantes",
+ Versus: "Contre",
+ White: "Blancs",
"White to move": "Trait aux blancs",
"White win": "Les blancs gagnent",
"Who's there?": "Qui est là ?",
"Pawns move diagonally": "Les pions vont en diagonale",
"Reverse captures": "Captures inversées",
"Shared pieces": "Pièces partagées",
- "Standard rules": "Règles usuelles",
+ "Standard rules": "Règles usuelles"
};
// Problem: fetch() does not set req.xhr... see access/ajax() security especially for /whoami
// From JSON (encoded string values!) to "arg1=...&arg2=..."
-function toQueryString(data)
-{
+function toQueryString(data) {
let data_str = "";
Object.keys(data).forEach(k => {
data_str += k + "=" + encodeURIComponent(data[k]) + "&";
}
// data, error: optional
-export function ajax(url, method, data, success, error)
-{
+export function ajax(url, method, data, success, error) {
let xhr = new XMLHttpRequest();
- if (data === undefined || typeof(data) === "function") //no data
- {
+ if (data === undefined || typeof data === "function") {
+ //no data
error = success;
success = data;
data = {};
}
- if (!success)
- success = () => {}; //by default, do nothing
+ if (!success) success = () => {}; //by default, do nothing
if (!error)
- error = errmsg => { alert(errmsg); };
+ error = errmsg => {
+ alert(errmsg);
+ };
xhr.onreadystatechange = function() {
- if (this.readyState == 4 && this.status == 200)
- {
+ if (this.readyState == 4 && this.status == 200) {
let res_json = "";
try {
res_json = JSON.parse(xhr.responseText);
} catch (e) {
- // Plain text (e.g. for rules retrieval)
- return success(xhr.responseText);
+ // Plain text (e.g. for rules retrieval) (TODO: no more plain text in current version)
+ success(xhr.responseText);
}
- if (!res_json.errmsg && !res_json.errno)
- success(res_json);
- else
- {
- if (!!res_json.errmsg)
- error(res_json.errmsg);
- else
- error(res_json.code + ". errno = " + res_json.errno);
+ if (res_json) {
+ if (!res_json.errmsg && !res_json.errno) success(res_json);
+ else {
+ if (res_json.errmsg) error(res_json.errmsg);
+ else error(res_json.code + ". errno = " + res_json.errno);
+ }
}
}
};
- if (["GET","DELETE"].includes(method) && !!data)
- {
+ if (["GET", "DELETE"].includes(method) && !!data) {
// Append query params to URL
url += "/?" + toQueryString(data);
}
xhr.open(method, params.serverUrl + url, true);
- xhr.setRequestHeader('X-Requested-With', "XMLHttpRequest");
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
// Next line to allow cross-domain cookies in dev mode
- if (params.cors)
- xhr.withCredentials = true;
- if (["POST","PUT"].includes(method))
- {
+ if (params.cors) xhr.withCredentials = true;
+ if (["POST", "PUT"].includes(method)) {
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.send(JSON.stringify(data));
- }
- else
- xhr.send();
+ } else xhr.send();
}
// Random (enough) string for socket and game IDs
-export function getRandString()
-{
- return (Date.now().toString(36) + Math.random().toString(36).substr(2, 7))
- .toUpperCase();
+export function getRandString() {
+ return (
+ Date.now().toString(36) +
+ Math.random()
+ .toString(36)
+ .substr(2, 7)
+ ).toUpperCase();
}
-export function randInt(min, max)
-{
- if (!max)
- {
+export function randInt(min, max) {
+ if (!max) {
max = min;
min = 0;
}
- return Math.floor(Math.random() * (max - min) ) + min;
+ return Math.floor(Math.random() * (max - min)) + min;
}
// Inspired by https://github.com/jashkenas/underscore/blob/master/underscore.js
-export function sample (arr, n)
-{
+export function sample(arr, n) {
n = n || 1;
let cpArr = arr.map(e => e);
- for (let index = 0; index < n; index++)
- {
+ for (let index = 0; index < n; index++) {
const rand = randInt(index, arr.length);
const temp = cpArr[index];
cpArr[index] = cpArr[rand];
return cpArr.slice(0, n);
}
-export function shuffle(arr)
-{
+export function shuffle(arr) {
return sample(arr, arr.length);
}
// Remove item(s) in array (if present)
-export const ArrayFun =
-{
- remove: function(arr, rfun, all)
- {
+export const ArrayFun = {
+ remove: function(arr, rfun, all) {
const index = arr.findIndex(rfun);
- if (index >= 0)
- {
+ if (index >= 0) {
arr.splice(index, 1);
- if (!!all)
- {
+ if (all) {
// Reverse loop because of the splice below
- for (let i=arr.length-1; i>=index; i--)
- {
- if (rfun(arr[i]))
- arr.splice(i, 1);
+ for (let i = arr.length - 1; i >= index; i--) {
+ if (rfun(arr[i])) arr.splice(i, 1);
}
}
}
},
// Double array intialization
- init: function(size1, size2, initElem)
- {
- return [...Array(size1)].map(e => Array(size2).fill(initElem));
+ init: function(size1, size2, initElem) {
+ return [...Array(size1)].map(() => Array(size2).fill(initElem));
},
- range: function(max)
- {
+ range: function(max) {
return [...Array(max).keys()];
- },
+ }
};
// Source: https://www.quirksmode.org/js/cookies.html
-export function setCookie(name, value)
-{
+export function setCookie(name, value) {
var date = new Date();
- date.setTime(date.getTime()+(183*24*60*60*1000)); //6 months
- var expires = "; expires="+date.toGMTString();
- document.cookie = name+"="+value+expires+"; path=/";
+ date.setTime(date.getTime() + 183 * 24 * 60 * 60 * 1000); //6 months
+ var expires = "; expires=" + date.toGMTString();
+ document.cookie = name + "=" + value + expires + "; path=/";
}
export function getCookie(name, defaut) {
var nameEQ = name + "=";
- var ca = document.cookie.split(';');
- for (var i=0;i < ca.length;i++)
- {
+ var ca = document.cookie.split(";");
+ for (var i = 0; i < ca.length; i++) {
var c = ca[i];
- while (c.charAt(0)==' ')
- c = c.substring(1,c.length);
- if (c.indexOf(nameEQ) == 0)
- return c.substring(nameEQ.length,c.length);
+ while (c.charAt(0) == " ") c = c.substring(1, c.length);
+ if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return defaut; //cookie not found
}
-function zeroPad(x)
-{
- return (x<10 ? "0" : "") + x;
+function zeroPad(x) {
+ return (x < 10 ? "0" : "") + x;
}
-export function getDate(d)
-{
- return d.getFullYear() + '-' + zeroPad(d.getMonth()+1) + '-' + zeroPad(d.getDate());
+export function getDate(d) {
+ return (
+ d.getFullYear() +
+ "-" +
+ zeroPad(d.getMonth() + 1) +
+ "-" +
+ zeroPad(d.getDate())
+ );
}
-export function getTime(d)
-{
- return zeroPad(d.getHours()) + ":" + zeroPad(d.getMinutes()) + ":" +
- zeroPad(d.getSeconds());
+export function getTime(d) {
+ return (
+ zeroPad(d.getHours()) +
+ ":" +
+ zeroPad(d.getMinutes()) +
+ ":" +
+ zeroPad(d.getSeconds())
+ );
}
-function padDigits(x)
-{
- if (x < 10)
- return "0" + x;
+function padDigits(x) {
+ if (x < 10) return "0" + x;
return x;
}
-export function ppt(t)
-{
+export function ppt(t) {
// "Pretty print" an amount of time given in seconds
const dayInSeconds = 60 * 60 * 24;
const hourInSeconds = 60 * 60;
const days = Math.floor(t / dayInSeconds);
- const hours = Math.floor(t%dayInSeconds / hourInSeconds);
- const minutes = Math.floor(t%hourInSeconds / 60);
+ const hours = Math.floor((t % dayInSeconds) / hourInSeconds);
+ const minutes = Math.floor((t % hourInSeconds) / 60);
const seconds = Math.floor(t % 60);
let res = "";
- if (days > 0)
- res += days + "d ";
- if (days <= 3 && hours > 0) //NOTE: 3 is arbitrary
+ if (days > 0) res += days + "d ";
+ if (days <= 3 && hours > 0)
+ //NOTE: 3 is arbitrary
res += hours + "h ";
if (days == 0 && minutes > 0)
- res += (hours > 0 ? padDigits(minutes) + "m " : minutes + ":");
- if (days == 0 && hours == 0)
- {
+ res += hours > 0 ? padDigits(minutes) + "m " : minutes + ":";
+ if (days == 0 && hours == 0) {
res += padDigits(seconds);
- if (minutes == 0)
- res += "s"; //seconds indicator, since this is the only number printed
+ if (minutes == 0) res += "s"; //seconds indicator, since this is the only number printed
}
return res.trim(); //remove potential last space
}
import { ajax } from "@/utils/ajax";
import { store } from "@/store";
-function dbOperation(callback)
-{
+function dbOperation(callback) {
let db = null;
let DBOpenRequest = window.indexedDB.open("vchess", 4);
alert(store.state.tr["Database error:"] + " " + event.target.errorCode);
};
- DBOpenRequest.onsuccess = function(event) {
+ DBOpenRequest.onsuccess = function() {
db = DBOpenRequest.result;
callback(db);
db.close();
DBOpenRequest.onupgradeneeded = function(event) {
let db = event.target.result;
db.onerror = function(event) {
- alert(store.state.tr["Error while loading database:"] + " " + event.target.errorCode);
+ alert(
+ store.state.tr["Error while loading database:"] +
+ " " +
+ event.target.errorCode
+ );
};
// Create objectStore for vchess->games
let objectStore = db.createObjectStore("games", { keyPath: "id" });
objectStore.createIndex("score", "score"); //to search by game result
- }
+ };
}
-export const GameStorage =
-{
+export const GameStorage = {
// Optional callback to get error status
- add: function(game, callback)
- {
- dbOperation((db) => {
+ add: function(game, callback) {
+ dbOperation(db => {
let transaction = db.transaction("games", "readwrite");
- if (callback)
- {
+ if (callback) {
transaction.oncomplete = function() {
callback({}); //everything's fine
- }
+ };
transaction.onerror = function() {
- callback({errmsg: store.state.tr["Game retrieval failed:"] + " " + transaction.error});
+ callback({
+ errmsg:
+ store.state.tr["Game retrieval failed:"] + " " + transaction.error
+ });
};
}
let objectStore = transaction.objectStore("games");
// TODO: also option to takeback a move ?
// obj: chat, move, fen, clocks, score[Msg], initime, ...
- update: function(gameId, obj)
- {
- if (Number.isInteger(gameId) || !isNaN(parseInt(gameId)))
- {
+ update: function(gameId, obj) {
+ if (Number.isInteger(gameId) || !isNaN(parseInt(gameId))) {
// corr: only move, fen and score
- ajax(
- "/games",
- "PUT",
- {
- gid: gameId,
- newObj:
- {
- // Some fields may be undefined:
- chat: obj.chat,
- move: obj.move,
- fen: obj.fen,
- score: obj.score,
- scoreMsg: obj.scoreMsg,
- drawOffer: obj.drawOffer,
- }
+ ajax("/games", "PUT", {
+ gid: gameId,
+ newObj: {
+ // Some fields may be undefined:
+ chat: obj.chat,
+ move: obj.move,
+ fen: obj.fen,
+ score: obj.score,
+ scoreMsg: obj.scoreMsg,
+ drawOffer: obj.drawOffer
}
- );
- }
- else
- {
+ });
+ } else {
// live
- dbOperation((db) => {
- let objectStore = db.transaction("games", "readwrite").objectStore("games");
+ dbOperation(db => {
+ let objectStore = db
+ .transaction("games", "readwrite")
+ .objectStore("games");
objectStore.get(gameId).onsuccess = function(event) {
const game = event.target.result;
Object.keys(obj).forEach(k => {
- if (k == "move")
- game.moves.push(obj[k]);
- else
- game[k] = obj[k];
+ if (k == "move") game.moves.push(obj[k]);
+ else game[k] = obj[k];
});
objectStore.put(game); //save updated data
- }
+ };
});
}
},
// Retrieve all local games (running, completed, imported...)
- getAll: function(callback)
- {
- dbOperation((db) => {
- let objectStore = db.transaction('games').objectStore('games');
+ getAll: function(callback) {
+ dbOperation(db => {
+ let objectStore = db.transaction("games").objectStore("games");
let games = [];
objectStore.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
// if there is still another cursor to go, keep running this code
- if (cursor)
- {
+ if (cursor) {
games.push(cursor.value);
cursor.continue();
- }
- else
- callback(games);
- }
+ } else callback(games);
+ };
});
},
// Retrieve any game from its identifiers (locally or on server)
// NOTE: need callback because result is obtained asynchronously
- get: function(gameId, callback)
- {
+ get: function(gameId, callback) {
// corr games identifiers are integers
- if (Number.isInteger(gameId) || !isNaN(parseInt(gameId)))
- {
- ajax("/games", "GET", {gid:gameId}, res => {
+ if (Number.isInteger(gameId) || !isNaN(parseInt(gameId))) {
+ ajax("/games", "GET", { gid: gameId }, res => {
let game = res.game;
game.moves.forEach(m => {
m.squares = JSON.parse(m.squares);
});
callback(game);
});
- }
- else //local game
- {
- dbOperation((db) => {
- let objectStore = db.transaction('games').objectStore('games');
+ } //local game
+ else {
+ dbOperation(db => {
+ let objectStore = db.transaction("games").objectStore("games");
objectStore.get(gameId).onsuccess = function(event) {
callback(event.target.result);
- }
+ };
});
}
},
- getCurrent: function(callback)
- {
- dbOperation((db) => {
- let objectStore = db.transaction('games').objectStore('games');
+ getCurrent: function(callback) {
+ dbOperation(db => {
+ let objectStore = db.transaction("games").objectStore("games");
objectStore.get("*").onsuccess = function(event) {
callback(event.target.result);
};
},
// Delete a game in indexedDB
- remove: function(gameId, callback)
- {
- dbOperation((db) => {
+ remove: function(gameId, callback) {
+ dbOperation(db => {
let transaction = db.transaction(["games"], "readwrite");
- if (callback)
- {
+ if (callback) {
transaction.oncomplete = function() {
callback({}); //everything's fine
- }
+ };
transaction.onerror = function() {
- callback({errmsg: store.state.tr["Game removal failed:"] + " " + transaction.error});
+ callback({
+ errmsg:
+ store.state.tr["Game removal failed:"] + " " + transaction.error
+ });
};
}
transaction.objectStore("games").delete(gameId);
});
- },
+ }
};
-export function processModalClick(e)
-{
+export function processModalClick(e) {
// Close a modal when click on it but outside focused element
const data = e.target.dataset;
- if (!!data.checkbox)
- document.getElementById(data.checkbox).checked = false;
+ if (data.checkbox) document.getElementById(data.checkbox).checked = false;
}
import { ArrayFun } from "@/utils/array";
// Turn (human) marks into coordinates
-function getMarkArray(marks)
-{
- if (!marks || marks == "-")
- return [];
+function getMarkArray(marks) {
+ if (!marks || marks == "-") return [];
let markArray = ArrayFun.init(V.size.x, V.size.y, false);
const squares = marks.split(",");
- for (let i=0; i<squares.length; i++)
- {
+ for (let i = 0; i < squares.length; i++) {
const coords = V.SquareToCoords(squares[i]);
markArray[coords.x][coords.y] = true;
}
}
// Turn (human) shadow indications into coordinates
-function getShadowArray(shadow)
-{
- if (!shadow || shadow == "-")
- return [];
+function getShadowArray(shadow) {
+ if (!shadow || shadow == "-") return [];
let shadowArray = ArrayFun.init(V.size.x, V.size.y, false);
const squares = shadow.split(",");
- for (let i=0; i<squares.length; i++)
- {
+ for (let i = 0; i < squares.length; i++) {
const rownum = V.size.x - parseInt(squares[i]);
- if (!isNaN(rownum))
- {
+ if (!isNaN(rownum)) {
// Shadow a full row
- for (let i=0; i<V.size.y; i++)
- shadowArray[rownum][i] = true;
+ for (let i = 0; i < V.size.y; i++) shadowArray[rownum][i] = true;
continue;
}
- if (squares[i].length == 1)
- {
+ if (squares[i].length == 1) {
// Shadow a full column
const colnum = V.ColumnToCoord(squares[i]);
- for (let i=0; i<V.size.x; i++)
- shadowArray[i][colnum] = true;
+ for (let i = 0; i < V.size.x; i++) shadowArray[i][colnum] = true;
continue;
}
- if (squares[i].indexOf("-") >= 0)
- {
+ if (squares[i].indexOf("-") >= 0) {
// Shadow a range of squares, horizontally or vertically
const firstLastSq = squares[i].split("-");
- const range =
- [
+ const range = [
V.SquareToCoords(firstLastSq[0]),
V.SquareToCoords(firstLastSq[1])
];
- const step =
- [
+ const step = [
range[1].x == range[0].x
? 0
: (range[1].x - range[0].x) / Math.abs(range[1].x - range[0].x),
: (range[1].y - range[0].y) / Math.abs(range[1].y - range[0].y)
];
// Convention: range always from smaller to larger number
- for (let x=range[0].x, y=range[0].y; x <= range[1].x && y <= range[1].y;
- x += step[0], y += step[1])
- {
+ for (
+ let x = range[0].x, y = range[0].y;
+ x <= range[1].x && y <= range[1].y;
+ x += step[0], y += step[1]
+ ) {
shadowArray[x][y] = true;
}
continue;
// args: object with position (mandatory), and
// orientation, marks, shadow (optional)
-export function getDiagram(args)
-{
+export function getDiagram(args) {
// Obtain the array of pieces images names:
const board = V.GetBoard(args.position);
const orientation = args.orientation || "w";
const markArray = getMarkArray(args.marks);
const shadowArray = getShadowArray(args.shadow);
let boardDiv = "";
- const [startX,startY,inc] = orientation == 'w'
- ? [0, 0, 1]
- : [V.size.x-1, V.size.y-1, -1];
- for (let i=startX; i>=0 && i<V.size.x; i+=inc)
- {
+ const [startX, startY, inc] =
+ orientation == "w" ? [0, 0, 1] : [V.size.x - 1, V.size.y - 1, -1];
+ for (let i = startX; i >= 0 && i < V.size.x; i += inc) {
boardDiv += "<div class='row'>";
- for (let j=startY; j>=0 && j<V.size.y; j+=inc)
- {
- boardDiv += "<div class='board board" + V.size.y + " " +
- ((i+j)%2==0 ? "light-square-diag" : "dark-square-diag") +
+ for (let j = startY; j >= 0 && j < V.size.y; j += inc) {
+ boardDiv +=
+ "<div class='board board" +
+ V.size.y +
+ " " +
+ ((i + j) % 2 == 0 ? "light-square-diag" : "dark-square-diag") +
(shadowArray.length > 0 && shadowArray[i][j] ? " in-shadow" : "") +
"'>";
- if (board[i][j] != V.EMPTY)
- {
- boardDiv += "<img " +
- "src='/images/pieces/" + V.getPpath(board[i][j]) + ".svg' " +
+ if (board[i][j] != V.EMPTY) {
+ boardDiv +=
+ "<img " +
+ "src='/images/pieces/" +
+ V.getPpath(board[i][j]) +
+ ".svg' " +
"class='piece'/>";
}
if (markArray.length > 0 && markArray[i][j])
// Default score message if none provided
export function getScoreMessage(score) {
let eogMessage = "Undefined"; //not translated: unused
- switch (score)
- {
+ switch (score) {
case "1-0":
eogMessage = "White win";
break;
// Get the identifier of a HTML square from its numeric coordinates o.x,o.y.
-export function getSquareId(o)
-{
+export function getSquareId(o) {
// NOTE: a separator is required to allow any size of board
- return "sq-" + o.x + "-" + o.y;
+ return "sq-" + o.x + "-" + o.y;
}
// Inverse function
-export function getSquareFromId(id)
-{
- const idParts = id.split('-');
+export function getSquareFromId(id) {
+ const idParts = id.split("-");
return [parseInt(idParts[1]), parseInt(idParts[2])];
}
-function timeUnitToSeconds(value, unit)
-{
+function timeUnitToSeconds(value, unit) {
let seconds = value;
- switch (unit)
- {
- case 'd':
- seconds *= 24;
- case 'h':
- seconds *= 60;
- case 'm':
+ switch (unit) {
+ case "d":
+ seconds *= 86400; //24*60*60
+ break;
+ case "h":
+ seconds *= 3600;
+ break;
+ case "m":
seconds *= 60;
+ break;
}
return seconds;
}
-function isLargerUnit(unit1, unit2)
-{
- return (unit1 == 'd' && unit2 != 'd')
- || (unit1 == 'h' && ['s','m'].includes(unit2))
- || (unit1 == 'm' && unit2 == 's');
+function isLargerUnit(unit1, unit2) {
+ return (
+ (unit1 == "d" && unit2 != "d") ||
+ (unit1 == "h" && ["s", "m"].includes(unit2)) ||
+ (unit1 == "m" && unit2 == "s")
+ );
}
-export function extractTime(cadence)
-{
- let tcParts = cadence.replace(/ /g,"").split('+');
+export function extractTime(cadence) {
+ let tcParts = cadence.replace(/ /g, "").split("+");
// Concatenate usual time control suffixes, in case of none is provided
tcParts[0] += "m";
tcParts[1] += "s";
const mainTimeArray = tcParts[0].match(/^([0-9]+)([smhd]+)$/);
- if (!mainTimeArray)
- return null;
+ if (!mainTimeArray) return null;
const mainTimeValue = parseInt(mainTimeArray[1]);
const mainTimeUnit = mainTimeArray[2][0];
const mainTime = timeUnitToSeconds(mainTimeValue, mainTimeUnit);
let increment = 0;
- if (tcParts.length >= 2)
- {
+ if (tcParts.length >= 2) {
const incrementArray = tcParts[1].match(/^([0-9]+)([smhd]+)$/);
- if (!incrementArray)
- return null;
+ if (!incrementArray) return null;
const incrementValue = parseInt(incrementArray[1]);
const incrementUnit = incrementArray[2][0];
// Increment unit cannot be larger than main unit:
- if (isLargerUnit(incrementUnit, mainTimeUnit))
- return null;
+ if (isLargerUnit(incrementUnit, mainTimeUnit)) return null;
increment = timeUnitToSeconds(incrementValue, incrementUnit);
}
- return {mainTime:mainTime, increment:increment};
+ return { mainTime: mainTime, increment: increment };
}
import { ChessRules } from "@/base_rules";
-import { ArrayFun} from "@/utils/array";
+import { ArrayFun } from "@/utils/array";
// NOTE: alternative implementation, probably cleaner = use only 1 board
// TODO? atLeastOneMove() would be more efficient if rewritten here (less sideBoard computations)
-export const VariantRules = class AliceRules extends ChessRules
-{
- static get ALICE_PIECES()
- {
+export const VariantRules = class AliceRules extends ChessRules {
+ static get ALICE_PIECES() {
return {
- 's': 'p',
- 't': 'q',
- 'u': 'r',
- 'c': 'b',
- 'o': 'n',
- 'l': 'k',
+ s: "p",
+ t: "q",
+ u: "r",
+ c: "b",
+ o: "n",
+ l: "k"
};
}
- static get ALICE_CODES()
- {
+ static get ALICE_CODES() {
return {
- 'p': 's',
- 'q': 't',
- 'r': 'u',
- 'b': 'c',
- 'n': 'o',
- 'k': 'l',
+ p: "s",
+ q: "t",
+ r: "u",
+ b: "c",
+ n: "o",
+ k: "l"
};
}
- static getPpath(b)
- {
+ static getPpath(b) {
return (Object.keys(this.ALICE_PIECES).includes(b[1]) ? "Alice/" : "") + b;
}
- static get PIECES()
- {
+ static get PIECES() {
return ChessRules.PIECES.concat(Object.keys(V.ALICE_PIECES));
}
- setOtherVariables(fen)
- {
+ setOtherVariables(fen) {
super.setOtherVariables(fen);
const rows = V.ParseFen(fen).position.split("/");
- if (this.kingPos["w"][0] < 0 || this.kingPos["b"][0] < 0)
- {
+ if (this.kingPos["w"][0] < 0 || this.kingPos["b"][0] < 0) {
// INIT_COL_XXX won't be required if Alice kings are found (means 'king moved')
- for (let i=0; i<rows.length; i++)
- {
+ for (let i = 0; i < rows.length; i++) {
let k = 0; //column index on board
- for (let j=0; j<rows[i].length; j++)
- {
- switch (rows[i].charAt(j))
- {
- case 'l':
- this.kingPos['b'] = [i,k];
+ for (let j = 0; j < rows[i].length; j++) {
+ switch (rows[i].charAt(j)) {
+ case "l":
+ this.kingPos["b"] = [i, k];
break;
- case 'L':
- this.kingPos['w'] = [i,k];
+ case "L":
+ this.kingPos["w"] = [i, k];
break;
- default:
+ default: {
const num = parseInt(rows[i].charAt(j));
- if (!isNaN(num))
- k += (num-1);
+ if (!isNaN(num)) k += num - 1;
+ }
}
k++;
}
}
// Return the (standard) color+piece notation at a square for a board
- getSquareOccupation(i, j, mirrorSide)
- {
- const piece = this.getPiece(i,j);
- if (mirrorSide==1 && Object.keys(V.ALICE_CODES).includes(piece))
+ getSquareOccupation(i, j, mirrorSide) {
+ const piece = this.getPiece(i, j);
+ if (mirrorSide == 1 && Object.keys(V.ALICE_CODES).includes(piece))
return this.board[i][j];
- else if (mirrorSide==2 && Object.keys(V.ALICE_PIECES).includes(piece))
- return this.getColor(i,j) + V.ALICE_PIECES[piece];
+ if (mirrorSide == 2 && Object.keys(V.ALICE_PIECES).includes(piece))
+ return this.getColor(i, j) + V.ALICE_PIECES[piece];
return "";
}
// Build board of the given (mirror)side
- getSideBoard(mirrorSide)
- {
+ getSideBoard(mirrorSide) {
// Build corresponding board from complete board
let sideBoard = ArrayFun.init(V.size.x, V.size.y, "");
- for (let i=0; i<V.size.x; i++)
- {
- for (let j=0; j<V.size.y; j++)
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++)
sideBoard[i][j] = this.getSquareOccupation(i, j, mirrorSide);
}
return sideBoard;
}
// NOTE: castle & enPassant https://www.chessvariants.com/other.dir/alice.html
- getPotentialMovesFrom([x,y], sideBoard)
- {
+ getPotentialMovesFrom([x, y], sideBoard) {
const pieces = Object.keys(V.ALICE_CODES);
const codes = Object.keys(V.ALICE_PIECES);
- const mirrorSide = (pieces.includes(this.getPiece(x,y)) ? 1 : 2);
- if (!sideBoard)
- sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
- const color = this.getColor(x,y);
+ const mirrorSide = pieces.includes(this.getPiece(x, y)) ? 1 : 2;
+ if (!sideBoard) sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
+ const color = this.getColor(x, y);
// Search valid moves on sideBoard
const saveBoard = this.board;
- this.board = sideBoard[mirrorSide-1];
- const moves = super.getPotentialMovesFrom([x,y])
- .filter(m => {
- // Filter out king moves which result in under-check position on
- // current board (before mirror traversing)
- let aprioriValid = true;
- if (m.appear[0].p == V.KING)
- {
- this.play(m);
- if (this.underCheck(color, sideBoard))
- aprioriValid = false;
- this.undo(m);
- }
- return aprioriValid;
- });
+ this.board = sideBoard[mirrorSide - 1];
+ const moves = super.getPotentialMovesFrom([x, y]).filter(m => {
+ // Filter out king moves which result in under-check position on
+ // current board (before mirror traversing)
+ let aprioriValid = true;
+ if (m.appear[0].p == V.KING) {
+ this.play(m);
+ if (this.underCheck(color, sideBoard)) aprioriValid = false;
+ this.undo(m);
+ }
+ return aprioriValid;
+ });
this.board = saveBoard;
// Finally filter impossible moves
const res = moves.filter(m => {
- if (m.appear.length == 2) //castle
- {
+ if (m.appear.length == 2) {
+ //castle
// appear[i] must be an empty square on the other board
- for (let psq of m.appear)
- {
- if (this.getSquareOccupation(psq.x,psq.y,3-mirrorSide) != V.EMPTY)
+ for (let psq of m.appear) {
+ if (this.getSquareOccupation(psq.x, psq.y, 3 - mirrorSide) != V.EMPTY)
return false;
}
- }
- else if (this.board[m.end.x][m.end.y] != V.EMPTY)
- {
+ } else if (this.board[m.end.x][m.end.y] != V.EMPTY) {
// Attempt to capture
- const piece = this.getPiece(m.end.x,m.end.y);
- if ((mirrorSide==1 && codes.includes(piece))
- || (mirrorSide==2 && pieces.includes(piece)))
- {
+ const piece = this.getPiece(m.end.x, m.end.y);
+ if (
+ (mirrorSide == 1 && codes.includes(piece)) ||
+ (mirrorSide == 2 && pieces.includes(piece))
+ ) {
return false;
}
}
// If the move is computed on board1, m.appear change for Alice pieces.
- if (mirrorSide==1)
- {
- m.appear.forEach(psq => { //forEach: castling taken into account
+ if (mirrorSide == 1) {
+ m.appear.forEach(psq => {
+ //forEach: castling taken into account
psq.p = V.ALICE_CODES[psq.p]; //goto board2
});
- }
- else //move on board2: mark vanishing pieces as Alice
- {
+ } //move on board2: mark vanishing pieces as Alice
+ else {
m.vanish.forEach(psq => {
psq.p = V.ALICE_CODES[psq.p];
});
}
// Fix en-passant captures
- if (m.vanish[0].p == V.PAWN && m.vanish.length == 2
- && this.board[m.end.x][m.end.y] == V.EMPTY)
- {
- m.vanish[1].c = V.GetOppCol(this.getColor(x,y));
+ if (
+ m.vanish[0].p == V.PAWN &&
+ m.vanish.length == 2 &&
+ this.board[m.end.x][m.end.y] == V.EMPTY
+ ) {
+ m.vanish[1].c = V.GetOppCol(this.getColor(x, y));
// In the special case of en-passant, if
// - board1 takes board2 : vanish[1] --> Alice
// - board2 takes board1 : vanish[1] --> normal
let van = m.vanish[1];
- if (mirrorSide==1 && codes.includes(this.getPiece(van.x,van.y)))
+ if (mirrorSide == 1 && codes.includes(this.getPiece(van.x, van.y)))
van.p = V.ALICE_CODES[van.p];
- else if (mirrorSide==2 && pieces.includes(this.getPiece(van.x,van.y)))
+ else if (
+ mirrorSide == 2 &&
+ pieces.includes(this.getPiece(van.x, van.y))
+ )
van.p = V.ALICE_PIECES[van.p];
}
return true;
return res;
}
- filterValid(moves, sideBoard)
- {
- if (moves.length == 0)
- return [];
- if (!sideBoard)
- sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
+ filterValid(moves, sideBoard) {
+ if (moves.length == 0) return [];
+ if (!sideBoard) sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
const color = this.turn;
return moves.filter(m => {
this.playSide(m, sideBoard); //no need to track flags
});
}
- getAllValidMoves()
- {
+ getAllValidMoves() {
const color = this.turn;
- const oppCol = V.GetOppCol(color);
let potentialMoves = [];
const sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
- for (var i=0; i<V.size.x; i++)
- {
- for (var j=0; j<V.size.y; j++)
- {
- if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == color)
- {
- Array.prototype.push.apply(potentialMoves,
- this.getPotentialMovesFrom([i,j], sideBoard));
+ for (var i = 0; i < V.size.x; i++) {
+ for (var j = 0; j < V.size.y; j++) {
+ if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == color) {
+ Array.prototype.push.apply(
+ potentialMoves,
+ this.getPotentialMovesFrom([i, j], sideBoard)
+ );
}
}
}
}
// Play on sideboards [TODO: only one sideBoard required]
- playSide(move, sideBoard)
- {
+ playSide(move, sideBoard) {
const pieces = Object.keys(V.ALICE_CODES);
move.vanish.forEach(psq => {
- const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
- sideBoard[mirrorSide-1][psq.x][psq.y] = V.EMPTY;
+ const mirrorSide = pieces.includes(psq.p) ? 1 : 2;
+ sideBoard[mirrorSide - 1][psq.x][psq.y] = V.EMPTY;
});
move.appear.forEach(psq => {
- const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
- const piece = (mirrorSide == 1 ? psq.p : V.ALICE_PIECES[psq.p]);
- sideBoard[mirrorSide-1][psq.x][psq.y] = psq.c + piece;
- if (piece == V.KING)
- this.kingPos[psq.c] = [psq.x,psq.y];
+ const mirrorSide = pieces.includes(psq.p) ? 1 : 2;
+ const piece = mirrorSide == 1 ? psq.p : V.ALICE_PIECES[psq.p];
+ sideBoard[mirrorSide - 1][psq.x][psq.y] = psq.c + piece;
+ if (piece == V.KING) this.kingPos[psq.c] = [psq.x, psq.y];
});
}
// Undo on sideboards
- undoSide(move, sideBoard)
- {
+ undoSide(move, sideBoard) {
const pieces = Object.keys(V.ALICE_CODES);
move.appear.forEach(psq => {
- const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
- sideBoard[mirrorSide-1][psq.x][psq.y] = V.EMPTY;
+ const mirrorSide = pieces.includes(psq.p) ? 1 : 2;
+ sideBoard[mirrorSide - 1][psq.x][psq.y] = V.EMPTY;
});
move.vanish.forEach(psq => {
- const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
- const piece = (mirrorSide == 1 ? psq.p : V.ALICE_PIECES[psq.p]);
- sideBoard[mirrorSide-1][psq.x][psq.y] = psq.c + piece;
- if (piece == V.KING)
- this.kingPos[psq.c] = [psq.x,psq.y];
+ const mirrorSide = pieces.includes(psq.p) ? 1 : 2;
+ const piece = mirrorSide == 1 ? psq.p : V.ALICE_PIECES[psq.p];
+ sideBoard[mirrorSide - 1][psq.x][psq.y] = psq.c + piece;
+ if (piece == V.KING) this.kingPos[psq.c] = [psq.x, psq.y];
});
}
// sideBoard: arg containing both boards (see getAllValidMoves())
- underCheck(color, sideBoard)
- {
+ underCheck(color, sideBoard) {
const kp = this.kingPos[color];
- const mirrorSide = (sideBoard[0][kp[0]][kp[1]] != V.EMPTY ? 1 : 2);
+ const mirrorSide = sideBoard[0][kp[0]][kp[1]] != V.EMPTY ? 1 : 2;
let saveBoard = this.board;
- this.board = sideBoard[mirrorSide-1];
+ this.board = sideBoard[mirrorSide - 1];
let res = this.isAttacked(kp, [V.GetOppCol(color)]);
this.board = saveBoard;
return res;
}
- getCheckSquares(color)
- {
+ getCheckSquares(color) {
const pieces = Object.keys(V.ALICE_CODES);
const kp = this.kingPos[color];
- const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2);
+ const mirrorSide = pieces.includes(this.getPiece(kp[0], kp[1])) ? 1 : 2;
let sideBoard = this.getSideBoard(mirrorSide);
let saveBoard = this.board;
this.board = sideBoard;
let res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color)])
- ? [ JSON.parse(JSON.stringify(this.kingPos[color])) ]
- : [ ];
+ ? [JSON.parse(JSON.stringify(this.kingPos[color]))]
+ : [];
this.board = saveBoard;
return res;
}
- updateVariables(move)
- {
+ updateVariables(move) {
super.updateVariables(move); //standard king
const piece = move.vanish[0].p;
const c = move.vanish[0].c;
// "l" = Alice king
- if (piece == "l")
- {
+ if (piece == "l") {
this.kingPos[c][0] = move.appear[0].x;
this.kingPos[c][1] = move.appear[0].y;
- this.castleFlags[c] = [false,false];
+ this.castleFlags[c] = [false, false];
}
}
- unupdateVariables(move)
- {
+ unupdateVariables(move) {
super.unupdateVariables(move);
const c = move.vanish[0].c;
- if (move.vanish[0].p == "l")
- this.kingPos[c] = [move.start.x, move.start.y];
+ if (move.vanish[0].p == "l") this.kingPos[c] = [move.start.x, move.start.y];
}
- getCurrentScore()
- {
- if (this.atLeastOneMove()) // game not over
+ getCurrentScore() {
+ if (this.atLeastOneMove())
+ // game not over
return "*";
const pieces = Object.keys(V.ALICE_CODES);
const color = this.turn;
const kp = this.kingPos[color];
- const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2);
+ const mirrorSide = pieces.includes(this.getPiece(kp[0], kp[1])) ? 1 : 2;
let sideBoard = this.getSideBoard(mirrorSide);
let saveBoard = this.board;
this.board = sideBoard;
let res = "*";
if (!this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]))
res = "1/2";
- else
- res = (color == "w" ? "0-1" : "1-0");
+ else res = color == "w" ? "0-1" : "1-0";
this.board = saveBoard;
return res;
}
- static get VALUES()
- {
- return Object.assign(
- ChessRules.VALUES,
- {
- 's': 1,
- 'u': 5,
- 'o': 3,
- 'c': 3,
- 't': 9,
- 'l': 1000,
- }
- );
+ static get VALUES() {
+ return Object.assign(ChessRules.VALUES, {
+ s: 1,
+ u: 5,
+ o: 3,
+ c: 3,
+ t: 9,
+ l: 1000
+ });
}
- getNotation(move)
- {
- if (move.appear.length == 2 && move.appear[0].p == V.KING)
- {
- if (move.end.y < move.start.y)
- return "0-0-0";
- else
- return "0-0";
+ getNotation(move) {
+ if (move.appear.length == 2 && move.appear[0].p == V.KING) {
+ if (move.end.y < move.start.y) return "0-0-0";
+ return "0-0";
}
const finalSquare = V.CoordsToSquare(move.end);
const piece = this.getPiece(move.start.x, move.start.y);
- const captureMark = (move.vanish.length > move.appear.length ? "x" : "");
+ const captureMark = move.vanish.length > move.appear.length ? "x" : "";
let pawnMark = "";
- if (["p","s"].includes(piece) && captureMark.length == 1)
+ if (["p", "s"].includes(piece) && captureMark.length == 1)
pawnMark = V.CoordToColumn(move.start.y); //start column
// Piece or pawn movement
let notation = piece.toUpperCase() + pawnMark + captureMark + finalSquare;
- if (['s','p'].includes(piece) && !['s','p'].includes(move.appear[0].p))
- {
+ if (["s", "p"].includes(piece) && !["s", "p"].includes(move.appear[0].p)) {
// Promotion
notation += "=" + move.appear[0].p.toUpperCase();
}
return notation;
}
-}
+};
import { ChessRules } from "@/base_rules";
-import { ArrayFun} from "@/utils/array";
+import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class AntikingRules extends ChessRules
-{
- static getPpath(b)
- {
- return b[1]=='a' ? "Antiking/"+b : b;
+export const VariantRules = class AntikingRules extends ChessRules {
+ static getPpath(b) {
+ return b[1] == "a" ? "Antiking/" + b : b;
}
- static get ANTIKING() { return 'a'; }
+ static get ANTIKING() {
+ return "a";
+ }
- static get PIECES()
- {
+ static get PIECES() {
return ChessRules.PIECES.concat([V.ANTIKING]);
}
- setOtherVariables(fen)
- {
+ setOtherVariables(fen) {
super.setOtherVariables(fen);
- this.antikingPos = {'w':[-1,-1], 'b':[-1,-1]};
+ this.antikingPos = { w: [-1, -1], b: [-1, -1] };
const rows = V.ParseFen(fen).position.split("/");
- for (let i=0; i<rows.length; i++)
- {
+ for (let i = 0; i < rows.length; i++) {
let k = 0;
- for (let j=0; j<rows[i].length; j++)
- {
- switch (rows[i].charAt(j))
- {
- case 'a':
- this.antikingPos['b'] = [i,k];
+ for (let j = 0; j < rows[i].length; j++) {
+ switch (rows[i].charAt(j)) {
+ case "a":
+ this.antikingPos["b"] = [i, k];
break;
- case 'A':
- this.antikingPos['w'] = [i,k];
+ case "A":
+ this.antikingPos["w"] = [i, k];
break;
- default:
+ default: {
const num = parseInt(rows[i].charAt(j));
- if (!isNaN(num))
- k += (num-1);
+ if (!isNaN(num)) k += num - 1;
+ }
}
k++;
}
}
}
- canTake([x1,y1], [x2,y2])
- {
- const piece1 = this.getPiece(x1,y1);
- const piece2 = this.getPiece(x2,y2);
- const color1 = this.getColor(x1,y1);
- const color2 = this.getColor(x2,y2);
- return piece2 != "a" &&
- ((piece1 != "a" && color1 != color2) || (piece1 == "a" && color1 == color2));
+ canTake([x1, y1], [x2, y2]) {
+ const piece1 = this.getPiece(x1, y1);
+ const piece2 = this.getPiece(x2, y2);
+ const color1 = this.getColor(x1, y1);
+ const color2 = this.getColor(x2, y2);
+ return (
+ piece2 != "a" &&
+ ((piece1 != "a" && color1 != color2) ||
+ (piece1 == "a" && color1 == color2))
+ );
}
- getPotentialMovesFrom([x,y])
- {
- switch (this.getPiece(x,y))
- {
+ getPotentialMovesFrom([x, y]) {
+ switch (this.getPiece(x, y)) {
case V.ANTIKING:
- return this.getPotentialAntikingMoves([x,y]);
+ return this.getPotentialAntikingMoves([x, y]);
default:
- return super.getPotentialMovesFrom([x,y]);
+ return super.getPotentialMovesFrom([x, y]);
}
}
- getPotentialAntikingMoves(sq)
- {
- return this.getSlideNJumpMoves(sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+ getPotentialAntikingMoves(sq) {
+ return this.getSlideNJumpMoves(
+ sq,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
}
- isAttacked(sq, colors)
- {
- return (super.isAttacked(sq, colors) || this.isAttackedByAntiking(sq, colors));
+ isAttacked(sq, colors) {
+ return (
+ super.isAttacked(sq, colors) || this.isAttackedByAntiking(sq, colors)
+ );
}
- isAttackedByKing([x,y], colors)
- {
- if (this.getPiece(x,y) == V.ANTIKING)
- return false; //antiking is not attacked by king
- return this.isAttackedBySlideNJump([x,y], colors, V.KING,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+ isAttackedByKing([x, y], colors) {
+ if (this.getPiece(x, y) == V.ANTIKING) return false; //antiking is not attacked by king
+ return this.isAttackedBySlideNJump(
+ [x, y],
+ colors,
+ 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
- return this.isAttackedBySlideNJump([x,y], colors, V.ANTIKING,
- 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
+ return this.isAttackedBySlideNJump(
+ [x, y],
+ colors,
+ V.ANTIKING,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
}
- underCheck(color)
- {
+ underCheck(color) {
const oppCol = V.GetOppCol(color);
- let res = this.isAttacked(this.kingPos[color], [oppCol])
- || !this.isAttacked(this.antikingPos[color], [oppCol]);
+ let res =
+ this.isAttacked(this.kingPos[color], [oppCol]) ||
+ !this.isAttacked(this.antikingPos[color], [oppCol]);
return res;
}
- getCheckSquares(color)
- {
+ getCheckSquares(color) {
let res = super.getCheckSquares(color);
if (!this.isAttacked(this.antikingPos[color], [V.GetOppCol(color)]))
res.push(JSON.parse(JSON.stringify(this.antikingPos[color])));
return res;
}
- updateVariables(move)
- {
+ updateVariables(move) {
super.updateVariables(move);
const piece = move.vanish[0].p;
const c = move.vanish[0].c;
// Update antiking position
- if (piece == V.ANTIKING)
- {
+ if (piece == V.ANTIKING) {
this.antikingPos[c][0] = move.appear[0].x;
this.antikingPos[c][1] = move.appear[0].y;
}
}
- unupdateVariables(move)
- {
+ unupdateVariables(move) {
super.unupdateVariables(move);
const c = move.vanish[0].c;
if (move.vanish[0].p == V.ANTIKING)
this.antikingPos[c] = [move.start.x, move.start.y];
}
- getCurrentScore()
- {
- if (this.atLeastOneMove()) // game not over
+ getCurrentScore() {
+ if (this.atLeastOneMove())
+ // game not over
return "*";
const color = this.turn;
const oppCol = V.GetOppCol(color);
- if (!this.isAttacked(this.kingPos[color], [oppCol])
- && this.isAttacked(this.antikingPos[color], [oppCol]))
- {
+ if (
+ !this.isAttacked(this.kingPos[color], [oppCol]) &&
+ this.isAttacked(this.antikingPos[color], [oppCol])
+ ) {
return "1/2";
}
return color == "w" ? "0-1" : "1-0";
}
static get VALUES() {
- return Object.assign(
- ChessRules.VALUES,
- { 'a': 1000 }
- );
+ return Object.assign(ChessRules.VALUES, { a: 1000 });
}
- static GenRandInitFen()
- {
- let pieces = { "w": new Array(8), "b": new Array(8) };
- let antikingPos = { "w": -1, "b": -1 };
- for (let c of ["w","b"])
- {
+ static GenRandInitFen() {
+ let pieces = { w: new Array(8), b: new Array(8) };
+ let antikingPos = { w: -1, b: -1 };
+ for (let c of ["w", "b"]) {
let positions = ArrayFun.range(8);
// Get random squares for bishops, but avoid corners; because,
// if an antiking blocks a cornered bishop, it can never be checkmated
- let randIndex = 2 * randInt(1,4);
+ let randIndex = 2 * randInt(1, 4);
const bishop1Pos = positions[randIndex];
let randIndex_tmp = 2 * randInt(3) + 1;
const bishop2Pos = positions[randIndex_tmp];
- positions.splice(Math.max(randIndex,randIndex_tmp), 1);
- positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+ positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+ positions.splice(Math.min(randIndex, randIndex_tmp), 1);
randIndex = randInt(6);
const knight1Pos = positions[randIndex];
// Random squares for antikings
antikingPos[c] = randInt(8);
- pieces[c][rook1Pos] = 'r';
- pieces[c][knight1Pos] = 'n';
- pieces[c][bishop1Pos] = 'b';
- pieces[c][queenPos] = 'q';
- pieces[c][kingPos] = 'k';
- pieces[c][bishop2Pos] = 'b';
- pieces[c][knight2Pos] = 'n';
- pieces[c][rook2Pos] = 'r';
+ pieces[c][rook1Pos] = "r";
+ pieces[c][knight1Pos] = "n";
+ pieces[c][bishop1Pos] = "b";
+ pieces[c][queenPos] = "q";
+ pieces[c][kingPos] = "k";
+ pieces[c][bishop2Pos] = "b";
+ pieces[c][knight2Pos] = "n";
+ pieces[c][rook2Pos] = "r";
}
- const ranks23_black = "pppppppp/" + (antikingPos["w"]>0?antikingPos["w"]:"")
- + "A" + (antikingPos["w"]<7?7-antikingPos["w"]:"");
- const ranks23_white = (antikingPos["b"]>0?antikingPos["b"]:"") + "a"
- + (antikingPos["b"]<7?7-antikingPos["b"]:"") + "/PPPPPPPP";
- return pieces["b"].join("") + "/" + ranks23_black +
+ const ranks23_black =
+ "pppppppp/" +
+ (antikingPos["w"] > 0 ? antikingPos["w"] : "") +
+ "A" +
+ (antikingPos["w"] < 7 ? 7 - antikingPos["w"] : "");
+ const ranks23_white =
+ (antikingPos["b"] > 0 ? antikingPos["b"] : "") +
+ "a" +
+ (antikingPos["b"] < 7 ? 7 - antikingPos["b"] : "") +
+ "/PPPPPPPP";
+ return (
+ pieces["b"].join("") +
+ "/" +
+ ranks23_black +
"/8/8/" +
- ranks23_white + "/" + pieces["w"].join("").toUpperCase() +
- " w 0 1111 -";
+ ranks23_white +
+ "/" +
+ pieces["w"].join("").toUpperCase() +
+ " w 0 1111 -"
+ );
}
-}
+};
import { ChessRules, PiPo } from "@/base_rules";
-export const VariantRules = class AtomicRules extends ChessRules
-{
- getPotentialMovesFrom([x,y])
- {
- let moves = super.getPotentialMovesFrom([x,y]);
+export const VariantRules = class AtomicRules extends ChessRules {
+ getPotentialMovesFrom([x, y]) {
+ let moves = super.getPotentialMovesFrom([x, y]);
// Handle explosions
moves.forEach(m => {
- if (m.vanish.length > 1 && m.appear.length <= 1) //avoid castles
- {
+ if (m.vanish.length > 1 && m.appear.length <= 1) {
+ //avoid castles
// Explosion! OPTION (TODO?): drop moves which explode our king here
- let steps = [ [-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1] ];
- for (let step of steps)
- {
+ let steps = [
+ [-1, -1],
+ [-1, 0],
+ [-1, 1],
+ [0, -1],
+ [0, 1],
+ [1, -1],
+ [1, 0],
+ [1, 1]
+ ];
+ for (let step of steps) {
let x = m.end.x + step[0];
let y = m.end.y + step[1];
- if (V.OnBoard(x,y) && this.board[x][y] != V.EMPTY
- && this.getPiece(x,y) != V.PAWN)
- {
+ if (
+ V.OnBoard(x, y) &&
+ this.board[x][y] != V.EMPTY &&
+ this.getPiece(x, y) != V.PAWN
+ ) {
m.vanish.push(
- new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y}));
+ new PiPo({
+ p: this.getPiece(x, y),
+ c: this.getColor(x, y),
+ x: x,
+ y: y
+ })
+ );
}
}
- m.end = {x:m.appear[0].x, y:m.appear[0].y};
+ m.end = { x: m.appear[0].x, y: m.appear[0].y };
m.appear.pop(); //Nothin appears in this case
}
});
return moves;
}
- getPotentialKingMoves([x,y])
- {
+ getPotentialKingMoves([x, y]) {
// King cannot capture:
let moves = [];
const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
- for (let step of steps)
- {
+ for (let step of steps) {
const i = x + step[0];
const j = y + step[1];
- if (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
- moves.push(this.getBasicMove([x,y], [i,j]));
+ if (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY)
+ moves.push(this.getBasicMove([x, y], [i, j]));
}
- return moves.concat(this.getCastleMoves([x,y]));
+ return moves.concat(this.getCastleMoves([x, y]));
}
- isAttacked(sq, colors)
- {
- if (this.getPiece(sq[0],sq[1]) == V.KING && this.isAttackedByKing(sq, colors))
+ isAttacked(sq, colors) {
+ if (
+ this.getPiece(sq[0], sq[1]) == V.KING &&
+ this.isAttackedByKing(sq, colors)
+ )
return false; //king cannot take...
- return (this.isAttackedByPawn(sq, colors)
- || this.isAttackedByRook(sq, colors)
- || this.isAttackedByKnight(sq, colors)
- || this.isAttackedByBishop(sq, colors)
- || this.isAttackedByQueen(sq, colors));
+ return (
+ this.isAttackedByPawn(sq, colors) ||
+ this.isAttackedByRook(sq, colors) ||
+ this.isAttackedByKnight(sq, colors) ||
+ this.isAttackedByBishop(sq, colors) ||
+ this.isAttackedByQueen(sq, colors)
+ );
}
- updateVariables(move)
- {
+ updateVariables(move) {
super.updateVariables(move);
- const color = move.vanish[0].c;
- if (move.appear.length == 0) //capture
- {
- const firstRank = {"w": 7, "b": 0};
- for (let c of ["w","b"])
- {
+ if (move.appear.length == 0) {
+ //capture
+ const firstRank = { w: 7, b: 0 };
+ for (let c of ["w", "b"]) {
// Did we explode king of color c ? (TODO: remove move earlier)
- if (Math.abs(this.kingPos[c][0]-move.end.x) <= 1
- && Math.abs(this.kingPos[c][1]-move.end.y) <= 1)
- {
- this.kingPos[c] = [-1,-1];
- this.castleFlags[c] = [false,false];
- }
- else
- {
+ if (
+ Math.abs(this.kingPos[c][0] - move.end.x) <= 1 &&
+ Math.abs(this.kingPos[c][1] - move.end.y) <= 1
+ ) {
+ this.kingPos[c] = [-1, -1];
+ this.castleFlags[c] = [false, false];
+ } else {
// Now check if init rook(s) exploded
- if (Math.abs(move.end.x-firstRank[c]) <= 1)
- {
- if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][0]) <= 1)
+ if (Math.abs(move.end.x - firstRank[c]) <= 1) {
+ if (Math.abs(move.end.y - this.INIT_COL_ROOK[c][0]) <= 1)
this.castleFlags[c][0] = false;
- if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][1]) <= 1)
+ if (Math.abs(move.end.y - this.INIT_COL_ROOK[c][1]) <= 1)
this.castleFlags[c][1] = false;
}
}
}
}
- unupdateVariables(move)
- {
+ unupdateVariables(move) {
super.unupdateVariables(move);
const c = move.vanish[0].c;
const oppCol = V.GetOppCol(c);
- if ([this.kingPos[c][0],this.kingPos[oppCol][0]].some(e => { return e < 0; }))
- {
+ if (
+ [this.kingPos[c][0], this.kingPos[oppCol][0]].some(e => {
+ return e < 0;
+ })
+ ) {
// There is a chance that last move blowed some king away..
- for (let psq of move.vanish)
- {
- if (psq.p == 'k')
- this.kingPos[psq.c==c ? c : oppCol] = [psq.x, psq.y];
+ for (let psq of move.vanish) {
+ if (psq.p == "k")
+ this.kingPos[psq.c == c ? c : oppCol] = [psq.x, psq.y];
}
}
}
- underCheck(color)
- {
+ underCheck(color) {
const oppCol = V.GetOppCol(color);
let res = undefined;
// If our king disappeared, move is not valid
- if (this.kingPos[color][0] < 0)
- res = true;
+ if (this.kingPos[color][0] < 0) res = true;
// If opponent king disappeared, move is valid
- else if (this.kingPos[oppCol][0] < 0)
- res = false;
+ 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;
}
- getCheckSquares(color)
- {
- let res = [ ];
- if (this.kingPos[color][0] >= 0 //king might have exploded
- && this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]))
- {
- res = [ JSON.parse(JSON.stringify(this.kingPos[color])) ]
+ getCheckSquares(color) {
+ let res = [];
+ if (
+ this.kingPos[color][0] >= 0 && //king might have exploded
+ this.isAttacked(this.kingPos[color], [V.GetOppCol(color)])
+ ) {
+ res = [JSON.parse(JSON.stringify(this.kingPos[color]))];
}
return res;
}
- getCurrentScore()
- {
+ getCurrentScore() {
const color = this.turn;
const kp = this.kingPos[color];
- if (kp[0] < 0) //king disappeared
+ if (kp[0] < 0)
+ //king disappeared
return color == "w" ? "0-1" : "1-0";
- if (this.atLeastOneMove()) // game not over
+ if (this.atLeastOneMove())
+ // game not over
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
}
-}
+};
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class BaroqueRules extends ChessRules
-{
- static get HasFlags() { return false; }
+export const VariantRules = class BaroqueRules extends ChessRules {
+ static get HasFlags() {
+ return false;
+ }
- static get HasEnpassant() { return false; }
+ static get HasEnpassant() {
+ return false;
+ }
- static getPpath(b)
- {
- if (b[1] == "m") //'m' for Immobilizer (I is too similar to 1)
+ static getPpath(b) {
+ if (b[1] == "m")
+ //'m' for Immobilizer (I is too similar to 1)
return "Baroque/" + b;
return b; //usual piece
}
- static get PIECES()
- {
+ static get PIECES() {
return ChessRules.PIECES.concat([V.IMMOBILIZER]);
}
// No castling, but checks, so keep track of kings
- setOtherVariables(fen)
- {
- this.kingPos = {'w':[-1,-1], 'b':[-1,-1]};
+ setOtherVariables(fen) {
+ this.kingPos = { w: [-1, -1], b: [-1, -1] };
const fenParts = fen.split(" ");
const position = fenParts[0].split("/");
- for (let i=0; i<position.length; i++)
- {
+ for (let i = 0; i < position.length; i++) {
let k = 0;
- for (let j=0; j<position[i].length; j++)
- {
- switch (position[i].charAt(j))
- {
- case 'k':
- this.kingPos['b'] = [i,k];
+ for (let j = 0; j < position[i].length; j++) {
+ switch (position[i].charAt(j)) {
+ case "k":
+ this.kingPos["b"] = [i, k];
break;
- case 'K':
- this.kingPos['w'] = [i,k];
+ case "K":
+ this.kingPos["w"] = [i, k];
break;
- default:
- let num = parseInt(position[i].charAt(j));
- if (!isNaN(num))
- k += (num-1);
+ default: {
+ const num = parseInt(position[i].charAt(j));
+ if (!isNaN(num)) k += num - 1;
+ }
}
k++;
}
}
}
- static get IMMOBILIZER() { return 'm'; }
+ static get IMMOBILIZER() {
+ return "m";
+ }
// Although other pieces keep their names here for coding simplicity,
// keep in mind that:
// - a "rook" is a coordinator, capturing by coordinating with the king
// - a "queen" is a withdrawer, capturing by moving away from pieces
// Is piece on square (x,y) immobilized?
- isImmobilized([x,y])
- {
- const piece = this.getPiece(x,y);
- const color = this.getColor(x,y);
+ isImmobilized([x, y]) {
+ const piece = this.getPiece(x, y);
+ const color = this.getColor(x, y);
const oppCol = V.GetOppCol(color);
const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
- outerLoop:
- for (let step of adjacentSteps)
- {
- const [i,j] = [x+step[0],y+step[1]];
- if (V.OnBoard(i,j) && this.board[i][j] != V.EMPTY
- && this.getColor(i,j) == oppCol)
- {
- const oppPiece = this.getPiece(i,j);
- if (oppPiece == V.IMMOBILIZER)
- {
+ for (let step of adjacentSteps) {
+ const [i, j] = [x + step[0], y + step[1]];
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == oppCol
+ ) {
+ const oppPiece = this.getPiece(i, j);
+ if (oppPiece == V.IMMOBILIZER) {
// Moving is impossible only if this immobilizer is not neutralized
- for (let step2 of adjacentSteps)
- {
- const [i2,j2] = [i+step2[0],j+step2[1]];
- if (i2 == x && j2 == y)
- continue; //skip initial piece!
- if (V.OnBoard(i2,j2) && this.board[i2][j2] != V.EMPTY
- && this.getColor(i2,j2) == color)
- {
- if ([V.BISHOP,V.IMMOBILIZER].includes(this.getPiece(i2,j2)))
+ for (let step2 of adjacentSteps) {
+ const [i2, j2] = [i + step2[0], j + step2[1]];
+ if (i2 == x && j2 == y) continue; //skip initial piece!
+ if (
+ V.OnBoard(i2, j2) &&
+ this.board[i2][j2] != V.EMPTY &&
+ this.getColor(i2, j2) == color
+ ) {
+ if ([V.BISHOP, V.IMMOBILIZER].includes(this.getPiece(i2, j2)))
return false;
}
}
return true; //immobilizer isn't neutralized
}
// Chameleons can't be immobilized twice, because there is only one immobilizer
- if (oppPiece == V.BISHOP && piece == V.IMMOBILIZER)
- return true;
+ if (oppPiece == V.BISHOP && piece == V.IMMOBILIZER) return true;
}
}
return false;
}
- getPotentialMovesFrom([x,y])
- {
+ getPotentialMovesFrom([x, y]) {
// Pre-check: is thing on this square immobilized?
- if (this.isImmobilized([x,y]))
- return [];
- switch (this.getPiece(x,y))
- {
+ if (this.isImmobilized([x, y])) return [];
+ switch (this.getPiece(x, y)) {
case V.IMMOBILIZER:
- return this.getPotentialImmobilizerMoves([x,y]);
+ return this.getPotentialImmobilizerMoves([x, y]);
default:
- return super.getPotentialMovesFrom([x,y]);
+ return super.getPotentialMovesFrom([x, y]);
}
}
- getSlideNJumpMoves([x,y], steps, oneStep)
- {
- const color = this.getColor(x,y);
- const piece = this.getPiece(x,y);
+ getSlideNJumpMoves([x, y], steps, oneStep) {
+ const piece = this.getPiece(x, y);
let moves = [];
- outerLoop:
- for (let step of steps)
- {
+ outerLoop: for (let step of steps) {
let i = x + step[0];
let j = y + step[1];
- while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [i,j]));
- if (oneStep !== undefined)
- continue outerLoop;
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ moves.push(this.getBasicMove([x, y], [i, j]));
+ if (oneStep !== undefined) continue outerLoop;
i += step[0];
j += step[1];
}
// Only king can take on occupied square:
- if (piece==V.KING && V.OnBoard(i,j) && this.canTake([x,y], [i,j]))
- moves.push(this.getBasicMove([x,y], [i,j]));
+ if (piece == V.KING && V.OnBoard(i, j) && this.canTake([x, y], [i, j]))
+ moves.push(this.getBasicMove([x, y], [i, j]));
}
return moves;
}
// Modify capturing moves among listed pawn moves
- addPawnCaptures(moves, byChameleon)
- {
+ addPawnCaptures(moves, byChameleon) {
const steps = V.steps[V.ROOK];
const color = this.turn;
const oppCol = V.GetOppCol(color);
moves.forEach(m => {
- if (!!byChameleon && m.start.x!=m.end.x && m.start.y!=m.end.y)
- return; //chameleon not moving as pawn
+ if (!!byChameleon && m.start.x != m.end.x && m.start.y != m.end.y) return; //chameleon not moving as pawn
// Try capturing in every direction
- for (let step of steps)
- {
- const sq2 = [m.end.x+2*step[0],m.end.y+2*step[1]];
- if (V.OnBoard(sq2[0],sq2[1]) && this.board[sq2[0]][sq2[1]] != V.EMPTY
- && this.getColor(sq2[0],sq2[1]) == color)
- {
+ for (let step of steps) {
+ const sq2 = [m.end.x + 2 * step[0], m.end.y + 2 * step[1]];
+ if (
+ V.OnBoard(sq2[0], sq2[1]) &&
+ this.board[sq2[0]][sq2[1]] != V.EMPTY &&
+ this.getColor(sq2[0], sq2[1]) == color
+ ) {
// Potential capture
- const sq1 = [m.end.x+step[0],m.end.y+step[1]];
- if (this.board[sq1[0]][sq1[1]] != V.EMPTY
- && this.getColor(sq1[0],sq1[1]) == oppCol)
- {
- const piece1 = this.getPiece(sq1[0],sq1[1]);
- if (!byChameleon || piece1 == V.PAWN)
- {
- m.vanish.push(new PiPo({
- x:sq1[0],
- y:sq1[1],
- c:oppCol,
- p:piece1
- }));
+ const sq1 = [m.end.x + step[0], m.end.y + step[1]];
+ if (
+ this.board[sq1[0]][sq1[1]] != V.EMPTY &&
+ this.getColor(sq1[0], sq1[1]) == oppCol
+ ) {
+ const piece1 = this.getPiece(sq1[0], sq1[1]);
+ if (!byChameleon || piece1 == V.PAWN) {
+ m.vanish.push(
+ new PiPo({
+ x: sq1[0],
+ y: sq1[1],
+ c: oppCol,
+ p: piece1
+ })
+ );
}
}
}
}
// "Pincer"
- getPotentialPawnMoves([x,y])
- {
- let moves = super.getPotentialRookMoves([x,y]);
+ getPotentialPawnMoves([x, y]) {
+ let moves = super.getPotentialRookMoves([x, y]);
this.addPawnCaptures(moves);
return moves;
}
- addRookCaptures(moves, byChameleon)
- {
+ addRookCaptures(moves, byChameleon) {
const color = this.turn;
const oppCol = V.GetOppCol(color);
const kp = this.kingPos[color];
moves.forEach(m => {
// Check piece-king rectangle (if any) corners for enemy pieces
- if (m.end.x == kp[0] || m.end.y == kp[1])
- return; //"flat rectangle"
+ if (m.end.x == kp[0] || m.end.y == kp[1]) return; //"flat rectangle"
const corner1 = [m.end.x, kp[1]];
const corner2 = [kp[0], m.end.y];
- for (let [i,j] of [corner1,corner2])
- {
- if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol)
- {
- const piece = this.getPiece(i,j);
- if (!byChameleon || piece == V.ROOK)
- {
- m.vanish.push( new PiPo({
- x:i,
- y:j,
- p:piece,
- c:oppCol
- }) );
+ for (let [i, j] of [corner1, corner2]) {
+ if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == oppCol) {
+ const piece = this.getPiece(i, j);
+ if (!byChameleon || piece == V.ROOK) {
+ m.vanish.push(
+ new PiPo({
+ x: i,
+ y: j,
+ p: piece,
+ c: oppCol
+ })
+ );
}
}
}
}
// Coordinator
- getPotentialRookMoves(sq)
- {
+ getPotentialRookMoves(sq) {
let moves = super.getPotentialQueenMoves(sq);
this.addRookCaptures(moves);
return moves;
}
// Long-leaper
- getKnightCaptures(startSquare, byChameleon)
- {
+ getKnightCaptures(startSquare, byChameleon) {
// Look in every direction for captures
const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
const color = this.turn;
const oppCol = V.GetOppCol(color);
let moves = [];
- const [x,y] = [startSquare[0],startSquare[1]];
- const piece = this.getPiece(x,y); //might be a chameleon!
- outerLoop:
- for (let step of steps)
- {
- let [i,j] = [x+step[0], y+step[1]];
- while (V.OnBoard(i,j) && this.board[i][j]==V.EMPTY)
- {
+ const [x, y] = [startSquare[0], startSquare[1]];
+ const piece = this.getPiece(x, y); //might be a chameleon!
+ outerLoop: for (let step of steps) {
+ 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) || this.getColor(i,j)==color
- || (!!byChameleon && this.getPiece(i,j)!=V.KNIGHT))
- {
+ if (
+ !V.OnBoard(i, j) ||
+ this.getColor(i, j) == color ||
+ (!!byChameleon && this.getPiece(i, j) != V.KNIGHT)
+ ) {
continue;
}
// last(thing), cur(thing) : stop if "cur" is our color, or beyond board limits,
// or if "last" isn't empty and cur neither. Otherwise, if cur is empty then
// add move until cur square; if cur is occupied then stop if !!byChameleon and
// the square not occupied by a leaper.
- let last = [i,j];
- let cur = [i+step[0],j+step[1]];
- let vanished = [ new PiPo({x:x,y:y,c:color,p:piece}) ];
- while (V.OnBoard(cur[0],cur[1]))
- {
- if (this.board[last[0]][last[1]] != V.EMPTY)
- {
- const oppPiece = this.getPiece(last[0],last[1]);
- if (!!byChameleon && oppPiece != V.KNIGHT)
- continue outerLoop;
+ let last = [i, j];
+ let cur = [i + step[0], j + step[1]];
+ let vanished = [new PiPo({ x: x, y: y, c: color, p: piece })];
+ while (V.OnBoard(cur[0], cur[1])) {
+ if (this.board[last[0]][last[1]] != V.EMPTY) {
+ const oppPiece = this.getPiece(last[0], last[1]);
+ if (!!byChameleon && oppPiece != V.KNIGHT) continue outerLoop;
// Something to eat:
- vanished.push( new PiPo({x:last[0],y:last[1],c:oppCol,p:oppPiece}) );
+ vanished.push(
+ new PiPo({ x: last[0], y: last[1], c: oppCol, p: oppPiece })
+ );
}
- if (this.board[cur[0]][cur[1]] != V.EMPTY)
- {
- if (this.getColor(cur[0],cur[1]) == color
- || this.board[last[0]][last[1]] != V.EMPTY) //TODO: redundant test
- {
+ if (this.board[cur[0]][cur[1]] != V.EMPTY) {
+ if (
+ this.getColor(cur[0], cur[1]) == color ||
+ this.board[last[0]][last[1]] != V.EMPTY
+ ) {
+ //TODO: redundant test
continue outerLoop;
}
+ } else {
+ moves.push(
+ new Move({
+ appear: [new PiPo({ x: cur[0], y: cur[1], c: color, p: piece })],
+ vanish: JSON.parse(JSON.stringify(vanished)), //TODO: required?
+ start: { x: x, y: y },
+ end: { x: cur[0], y: cur[1] }
+ })
+ );
}
- else
- {
- moves.push(new Move({
- appear: [ new PiPo({x:cur[0],y:cur[1],c:color,p:piece}) ],
- vanish: JSON.parse(JSON.stringify(vanished)), //TODO: required?
- start: {x:x,y:y},
- end: {x:cur[0],y:cur[1]}
- }));
- }
- last = [last[0]+step[0],last[1]+step[1]];
- cur = [cur[0]+step[0],cur[1]+step[1]];
+ last = [last[0] + step[0], last[1] + step[1]];
+ cur = [cur[0] + step[0], cur[1] + step[1]];
}
}
return moves;
}
// Long-leaper
- getPotentialKnightMoves(sq)
- {
+ getPotentialKnightMoves(sq) {
return super.getPotentialQueenMoves(sq).concat(this.getKnightCaptures(sq));
}
- getPotentialBishopMoves([x,y])
- {
- let moves = super.getPotentialQueenMoves([x,y])
- .concat(this.getKnightCaptures([x,y],"asChameleon"));
+ getPotentialBishopMoves([x, y]) {
+ let moves = super
+ .getPotentialQueenMoves([x, y])
+ .concat(this.getKnightCaptures([x, y], "asChameleon"));
// No "king capture" because king cannot remain under check
this.addPawnCaptures(moves, "asChameleon");
this.addRookCaptures(moves, "asChameleon");
let mergedMoves = {};
moves.forEach(m => {
const key = m.end.x + V.size.x * m.end.y;
- if (!mergedMoves[key])
- mergedMoves[key] = m;
- else
- {
- for (let i=1; i<m.vanish.length; i++)
+ if (!mergedMoves[key]) mergedMoves[key] = m;
+ else {
+ for (let i = 1; i < m.vanish.length; i++)
mergedMoves[key].vanish.push(m.vanish[i]);
}
});
// Finally return an array
moves = [];
- Object.keys(mergedMoves).forEach(k => { moves.push(mergedMoves[k]); });
+ Object.keys(mergedMoves).forEach(k => {
+ moves.push(mergedMoves[k]);
+ });
return moves;
}
// Withdrawer
- addQueenCaptures(moves, byChameleon)
- {
- if (moves.length == 0)
- return;
- const [x,y] = [moves[0].start.x,moves[0].start.y];
+ addQueenCaptures(moves, byChameleon) {
+ if (moves.length == 0) return;
+ const [x, y] = [moves[0].start.x, moves[0].start.y];
const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
let capturingDirections = [];
const color = this.turn;
const oppCol = V.GetOppCol(color);
adjacentSteps.forEach(step => {
- const [i,j] = [x+step[0],y+step[1]];
- if (V.OnBoard(i,j) && this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol
- && (!byChameleon || this.getPiece(i,j) == V.QUEEN))
- {
+ const [i, j] = [x + step[0], y + step[1]];
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == oppCol &&
+ (!byChameleon || this.getPiece(i, j) == V.QUEEN)
+ ) {
capturingDirections.push(step);
}
});
moves.forEach(m => {
const step = [
- m.end.x!=x ? (m.end.x-x)/Math.abs(m.end.x-x) : 0,
- m.end.y!=y ? (m.end.y-y)/Math.abs(m.end.y-y) : 0
+ m.end.x != x ? (m.end.x - x) / Math.abs(m.end.x - x) : 0,
+ m.end.y != y ? (m.end.y - y) / Math.abs(m.end.y - y) : 0
];
// NOTE: includes() and even _.isEqual() functions fail...
// TODO: this test should be done only once per direction
- if (capturingDirections.some(dir =>
- { return (dir[0]==-step[0] && dir[1]==-step[1]); }))
- {
- const [i,j] = [x-step[0],y-step[1]];
- m.vanish.push(new PiPo({
- x:i,
- y:j,
- p:this.getPiece(i,j),
- c:oppCol
- }));
+ if (
+ capturingDirections.some(dir => {
+ return dir[0] == -step[0] && dir[1] == -step[1];
+ })
+ ) {
+ const [i, j] = [x - step[0], y - step[1]];
+ m.vanish.push(
+ new PiPo({
+ x: i,
+ y: j,
+ p: this.getPiece(i, j),
+ c: oppCol
+ })
+ );
}
});
}
- getPotentialQueenMoves(sq)
- {
+ getPotentialQueenMoves(sq) {
let moves = super.getPotentialQueenMoves(sq);
this.addQueenCaptures(moves);
return moves;
}
- getPotentialImmobilizerMoves(sq)
- {
+ getPotentialImmobilizerMoves(sq) {
// Immobilizer doesn't capture
return super.getPotentialQueenMoves(sq);
}
- getPotentialKingMoves(sq)
- {
- return this.getSlideNJumpMoves(sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+ getPotentialKingMoves(sq) {
+ return this.getSlideNJumpMoves(
+ sq,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
}
// isAttacked() is OK because the immobilizer doesn't take
- isAttackedByPawn([x,y], colors)
- {
+ isAttackedByPawn([x, y], colors) {
// Square (x,y) must be surroundable by two enemy pieces,
// and one of them at least should be a pawn (moving).
- const dirs = [ [1,0],[0,1] ];
+ const dirs = [
+ [1, 0],
+ [0, 1]
+ ];
const steps = V.steps[V.ROOK];
- for (let dir of dirs)
- {
- const [i1,j1] = [x-dir[0],y-dir[1]]; //"before"
- 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))
- {
+ for (let dir of dirs) {
+ const [i1, j1] = [x - dir[0], y - dir[1]]; //"before"
+ 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)
+ ) {
// Search a movable enemy pawn landing on the empty square
- for (let step of steps)
- {
- let [ii,jj] = (this.board[i1][j1]==V.EMPTY ? [i1,j1] : [i2,j2]);
- let [i3,j3] = [ii+step[0],jj+step[1]];
- while (V.OnBoard(i3,j3) && this.board[i3][j3]==V.EMPTY)
- {
+ for (let step of steps) {
+ let [ii, jj] = this.board[i1][j1] == V.EMPTY ? [i1, j1] : [i2, j2];
+ let [i3, j3] = [ii + step[0], jj + step[1]];
+ while (V.OnBoard(i3, j3) && this.board[i3][j3] == V.EMPTY) {
i3 += step[0];
j3 += step[1];
}
- if (V.OnBoard(i3,j3) && colors.includes(this.getColor(i3,j3))
- && this.getPiece(i3,j3) == V.PAWN && !this.isImmobilized([i3,j3]))
- {
+ if (
+ V.OnBoard(i3, j3) &&
+ colors.includes(this.getColor(i3, j3)) &&
+ this.getPiece(i3, j3) == V.PAWN &&
+ !this.isImmobilized([i3, j3])
+ ) {
return true;
}
}
return false;
}
- isAttackedByRook([x,y], colors)
- {
+ isAttackedByRook([x, y], colors) {
// 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]);
- if (sameRow || sameColumn)
- {
+ const sameRow = x == this.kingPos[colors[0]][0];
+ const sameColumn = y == this.kingPos[colors[0]][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.getPiece(i,j) == V.ROOK)
- {
- if (this.isImmobilized([i,j]))
- return false; //because only one rook
+ 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.getPiece(i, j) == V.ROOK
+ ) {
+ if (this.isImmobilized([i, j])) return false; //because only one rook
// Can it reach a capturing square?
// Easy but quite suboptimal way (TODO): generate all moves (turn is OK)
- const moves = this.getPotentialMovesFrom([i,j]);
- for (let move of moves)
- {
- if (sameRow && move.end.y == y || sameColumn && move.end.x == x)
+ const moves = this.getPotentialMovesFrom([i, j]);
+ for (let move of moves) {
+ if (
+ (sameRow && move.end.y == y) ||
+ (sameColumn && move.end.x == x)
+ )
return true;
}
}
return false;
}
- isAttackedByKnight([x,y], colors)
- {
+ isAttackedByKnight([x, y], colors) {
// 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]);
- outerLoop:
- for (let step of steps)
- {
- const [i0,j0] = [x+step[0],y+step[1]];
- if (V.OnBoard(i0,j0) && this.board[i0][j0] == V.EMPTY)
- {
+ outerLoop: for (let step of steps) {
+ const [i0, j0] = [x + step[0], y + step[1]];
+ if (V.OnBoard(i0, j0) && this.board[i0][j0] == V.EMPTY) {
// Try in opposite direction:
- let [i,j] = [x-step[0],y-step[1]];
- while (V.OnBoard(i,j))
- {
- while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
- {
+ let [i, j] = [x - step[0], y - step[1]];
+ while (V.OnBoard(i, j)) {
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
i -= step[0];
j -= step[1];
}
- if (V.OnBoard(i,j))
- {
- if (colors.includes(this.getColor(i,j)))
- {
- if (this.getPiece(i,j) == V.KNIGHT && !this.isImmobilized([i,j]))
+ if (V.OnBoard(i, j)) {
+ if (colors.includes(this.getColor(i, j))) {
+ if (
+ this.getPiece(i, j) == V.KNIGHT &&
+ !this.isImmobilized([i, j])
+ )
return true;
continue outerLoop;
}
// [else] Our color, could be captured *if there was an empty space*
- if (this.board[i+step[0]][j+step[1]] != V.EMPTY)
+ if (this.board[i + step[0]][j + step[1]] != V.EMPTY)
continue outerLoop;
i -= step[0];
j -= step[1];
return false;
}
- isAttackedByBishop([x,y], colors)
- {
+ isAttackedByBishop([x, y], colors) {
// We cheat a little here: since this function is used exclusively for king,
// it's enough to check the immediate surrounding of the square.
const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
- for (let step of adjacentSteps)
- {
- const [i,j] = [x+step[0],y+step[1]];
- if (V.OnBoard(i,j) && this.board[i][j]!=V.EMPTY
- && colors.includes(this.getColor(i,j)) && this.getPiece(i,j) == V.BISHOP)
- {
+ for (let step of adjacentSteps) {
+ const [i, j] = [x + step[0], y + step[1]];
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ colors.includes(this.getColor(i, j)) &&
+ this.getPiece(i, j) == V.BISHOP
+ ) {
return true; //bishops are never immobilized
}
}
return false;
}
- isAttackedByQueen([x,y], colors)
- {
+ isAttackedByQueen([x, y], colors) {
// 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]);
- for (let step of adjacentSteps)
- {
- const sq2 = [x+2*step[0],y+2*step[1]];
- if (V.OnBoard(sq2[0],sq2[1]) && this.board[sq2[0]][sq2[1]] == V.EMPTY)
- {
- 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.getPiece(sq1[0],sq1[1]) == V.QUEEN
- && !this.isImmobilized(sq1))
- {
+ for (let step of adjacentSteps) {
+ const sq2 = [x + 2 * step[0], y + 2 * step[1]];
+ if (V.OnBoard(sq2[0], sq2[1]) && this.board[sq2[0]][sq2[1]] == V.EMPTY) {
+ 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.getPiece(sq1[0], sq1[1]) == V.QUEEN &&
+ !this.isImmobilized(sq1)
+ ) {
return true;
}
}
return false;
}
- static get VALUES()
- {
+ static get VALUES() {
return {
- 'p': 1,
- 'r': 2,
- 'n': 5,
- 'b': 3,
- 'q': 3,
- 'm': 5,
- 'k': 1000
+ p: 1,
+ r: 2,
+ n: 5,
+ b: 3,
+ q: 3,
+ m: 5,
+ k: 1000
};
}
- static get SEARCH_DEPTH() { return 2; }
+ static get SEARCH_DEPTH() {
+ return 2;
+ }
- static GenRandInitFen()
- {
- let pieces = { "w": new Array(8), "b": new Array(8) };
+ static GenRandInitFen() {
+ let pieces = { w: new Array(8), b: new Array(8) };
// Shuffle pieces on first and last rank
- for (let c of ["w","b"])
- {
+ for (let c of ["w", "b"]) {
let positions = ArrayFun.range(8);
// Get random squares for every piece, totally freely
positions.splice(randIndex, 1);
const immobilizerPos = positions[0];
- pieces[c][bishop1Pos] = 'b';
- pieces[c][bishop2Pos] = 'b';
- pieces[c][knight1Pos] = 'n';
- pieces[c][knight2Pos] = 'n';
- pieces[c][queenPos] = 'q';
- pieces[c][kingPos] = 'k';
- pieces[c][rookPos] = 'r';
- pieces[c][immobilizerPos] = 'm';
+ pieces[c][bishop1Pos] = "b";
+ pieces[c][bishop2Pos] = "b";
+ pieces[c][knight1Pos] = "n";
+ pieces[c][knight2Pos] = "n";
+ pieces[c][queenPos] = "q";
+ pieces[c][kingPos] = "k";
+ pieces[c][rookPos] = "r";
+ pieces[c][immobilizerPos] = "m";
}
- return pieces["b"].join("") +
+ return (
+ pieces["b"].join("") +
"/pppppppp/8/8/8/8/PPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
- " w 0";
+ " w 0"
+ );
}
- getNotation(move)
- {
+ getNotation(move) {
const initialSquare = V.CoordsToSquare(move.start);
const finalSquare = V.CoordsToSquare(move.end);
let notation = undefined;
- if (move.appear[0].p == V.PAWN)
- {
+ if (move.appear[0].p == V.PAWN) {
// Pawn: generally ambiguous short notation, so we use full description
notation = "P" + initialSquare + finalSquare;
- }
- else if (move.appear[0].p == V.KING)
- notation = "K" + (move.vanish.length>1 ? "x" : "") + finalSquare;
- else
- notation = move.appear[0].p.toUpperCase() + finalSquare;
- if (move.vanish.length > 1 && move.appear[0].p != V.KING)
- notation += "X"; //capture mark (not describing what is captured...)
+ } else if (move.appear[0].p == V.KING)
+ notation = "K" + (move.vanish.length > 1 ? "x" : "") + finalSquare;
+ else notation = move.appear[0].p.toUpperCase() + finalSquare;
+ if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X"; //capture mark (not describing what is captured...)
return notation;
}
-}
+};
import { ChessRules } from "@/base_rules";
-export const VariantRules = class BerolinaRules extends ChessRules
-{
+export const VariantRules = class BerolinaRules extends ChessRules {
// En-passant after 2-sq jump
- getEpSquare(moveOrSquare)
- {
- if (!moveOrSquare)
- return undefined;
- if (typeof moveOrSquare === "string")
- {
+ getEpSquare(moveOrSquare) {
+ if (!moveOrSquare) return undefined;
+ if (typeof moveOrSquare === "string") {
const square = moveOrSquare;
- if (square == "-")
- return undefined;
+ if (square == "-") return undefined;
// Enemy pawn initial column must be given too:
let res = [];
const epParts = square.split(",");
}
// Argument is a move:
const move = moveOrSquare;
- const [sx,ex,sy] = [move.start.x,move.end.x,move.start.y];
- if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) == 2)
- {
- return
- [
+ const [sx, ex, sy] = [move.start.x, move.end.x, move.start.y];
+ if (this.getPiece(sx, sy) == V.PAWN && Math.abs(sx - ex) == 2) {
+ return [
{
- x: (ex + sx)/2,
- y: (move.end.y + sy)/2
+ x: (ex + sx) / 2,
+ y: (move.end.y + sy) / 2
},
move.end.y
];
}
// Special pawns movements
- getPotentialPawnMoves([x,y])
- {
+ getPotentialPawnMoves([x, y]) {
const color = this.turn;
let moves = [];
- const [sizeX,sizeY] = [V.size.x,V.size.y];
- const shiftX = (color == "w" ? -1 : 1);
- const firstRank = (color == 'w' ? sizeX-1 : 0);
- const startRank = (color == "w" ? sizeX-2 : 1);
- const lastRank = (color == "w" ? 0 : sizeX-1);
- const finalPieces = x + shiftX == lastRank
- ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
- : [V.PAWN];
+ const [sizeX, sizeY] = [V.size.x, V.size.y];
+ const shiftX = color == "w" ? -1 : 1;
+ const startRank = color == "w" ? sizeX - 2 : 1;
+ const lastRank = color == "w" ? 0 : sizeX - 1;
+ const finalPieces =
+ x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN];
// One square diagonally
- for (let shiftY of [-1,1])
- {
- if (this.board[x+shiftX][y+shiftY] == V.EMPTY)
- {
- for (let piece of finalPieces)
- {
- moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
- {c:color,p:piece}));
+ for (let shiftY of [-1, 1]) {
+ if (this.board[x + shiftX][y + shiftY] == V.EMPTY) {
+ for (let piece of finalPieces) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+ c: color,
+ p: piece
+ })
+ );
}
- if (x == startRank && y+2*shiftY>=0 && y+2*shiftY<sizeY
- && this.board[x+2*shiftX][y+2*shiftY] == V.EMPTY)
- {
+ if (
+ x == startRank &&
+ y + 2 * shiftY >= 0 &&
+ y + 2 * shiftY < sizeY &&
+ this.board[x + 2 * shiftX][y + 2 * shiftY] == V.EMPTY
+ ) {
// Two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shiftX,y+2*shiftY]));
+ moves.push(
+ this.getBasicMove([x, y], [x + 2 * shiftX, y + 2 * shiftY])
+ );
}
}
}
// Capture
- if (this.board[x+shiftX][y] != V.EMPTY
- && this.canTake([x,y], [x+shiftX,y]))
- {
+ if (
+ this.board[x + shiftX][y] != V.EMPTY &&
+ this.canTake([x, y], [x + shiftX, y])
+ ) {
for (let piece of finalPieces)
- moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece}));
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
+ );
}
// En passant
const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep-1]; //always at least one element
- if (!!epSquare && epSquare[0].x == x+shiftX && epSquare[0].y == y
- && Math.abs(epSquare[1] - y) == 1)
- {
- let enpassantMove = this.getBasicMove([x,y], [x+shiftX,y]);
+ const epSquare = this.epSquares[Lep - 1]; //always at least one element
+ if (
+ !!epSquare &&
+ epSquare[0].x == x + shiftX &&
+ epSquare[0].y == y &&
+ Math.abs(epSquare[1] - y) == 1
+ ) {
+ let enpassantMove = this.getBasicMove([x, y], [x + shiftX, y]);
enpassantMove.vanish.push({
x: x,
y: epSquare[1],
- p: 'p',
- c: this.getColor(x,epSquare[1])
+ p: "p",
+ c: this.getColor(x, epSquare[1])
});
moves.push(enpassantMove);
}
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)
- {
+ 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;
}
}
return false;
}
- getNotation(move)
- {
+ getNotation(move) {
const piece = this.getPiece(move.start.x, move.start.y);
- if (piece == V.PAWN)
- {
+ if (piece == V.PAWN) {
// Pawn move
const finalSquare = V.CoordsToSquare(move.end);
let notation = "";
- if (move.vanish.length == 2) //capture
+ if (move.vanish.length == 2)
+ //capture
notation = "Px" + finalSquare;
- else
- {
+ else {
// No capture: indicate the initial square for potential ambiguity
const startSquare = V.CoordsToSquare(move.start);
notation = startSquare + finalSquare;
}
- if (move.appear[0].p != V.PAWN) //promotion
+ if (move.appear[0].p != V.PAWN)
+ //promotion
notation += "=" + move.appear[0].p.toUpperCase();
return notation;
}
return super.getNotation(move); //all other pieces are orthodox
}
-}
+};
import { ChessRules } from "@/base_rules";
-export const VariantRules = class CheckeredRules extends ChessRules
-{
- static getPpath(b)
- {
- return b[0]=='c' ? "Checkered/"+b : b;
+export const VariantRules = class CheckeredRules extends ChessRules {
+ static getPpath(b) {
+ return b[0] == "c" ? "Checkered/" + b : b;
}
- static board2fen(b)
- {
+ static board2fen(b) {
const checkered_codes = {
- 'p': 's',
- 'q': 't',
- 'r': 'u',
- 'b': 'c',
- 'n': 'o',
+ p: "s",
+ q: "t",
+ r: "u",
+ b: "c",
+ n: "o"
};
- if (b[0]=="c")
- return checkered_codes[b[1]];
+ if (b[0] == "c") return checkered_codes[b[1]];
return ChessRules.board2fen(b);
}
- static fen2board(f)
- {
+ static fen2board(f) {
// Tolerate upper-case versions of checkered pieces (why not?)
const checkered_pieces = {
- 's': 'p',
- 'S': 'p',
- 't': 'q',
- 'T': 'q',
- 'u': 'r',
- 'U': 'r',
- 'c': 'b',
- 'C': 'b',
- 'o': 'n',
- 'O': 'n',
+ s: "p",
+ S: "p",
+ t: "q",
+ T: "q",
+ u: "r",
+ U: "r",
+ c: "b",
+ C: "b",
+ o: "n",
+ O: "n"
};
if (Object.keys(checkered_pieces).includes(f))
- return 'c'+checkered_pieces[f];
+ return "c" + checkered_pieces[f];
return ChessRules.fen2board(f);
}
- static get PIECES()
- {
- return ChessRules.PIECES.concat(['s','t','u','c','o']);
+ static get PIECES() {
+ return ChessRules.PIECES.concat(["s", "t", "u", "c", "o"]);
}
- setOtherVariables(fen)
- {
+ setOtherVariables(fen) {
super.setOtherVariables(fen);
// Local stack of non-capturing checkered moves:
this.cmoves = [];
const cmove = fen.split(" ")[5];
- if (cmove == "-")
- this.cmoves.push(null);
- else
- {
+ if (cmove == "-") this.cmoves.push(null);
+ else {
this.cmoves.push({
- start: ChessRules.SquareToCoords(cmove.substr(0,2)),
- end: ChessRules.SquareToCoords(cmove.substr(2)),
+ start: ChessRules.SquareToCoords(cmove.substr(0, 2)),
+ end: ChessRules.SquareToCoords(cmove.substr(2))
});
}
}
- static IsGoodFen(fen)
- {
- if (!ChessRules.IsGoodFen(fen))
- return false;
+ static IsGoodFen(fen) {
+ if (!ChessRules.IsGoodFen(fen)) return false;
const fenParts = fen.split(" ");
- if (fenParts.length != 6)
- return false;
+ if (fenParts.length != 6) return false;
if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/))
return false;
return true;
}
- static IsGoodFlags(flags)
- {
+ static IsGoodFlags(flags) {
// 4 for castle + 16 for pawns
return !!flags.match(/^[01]{20,20}$/);
}
- setFlags(fenflags)
- {
+ setFlags(fenflags) {
super.setFlags(fenflags); //castleFlags
- this.pawnFlags =
- {
- "w": [...Array(8).fill(true)], //pawns can move 2 squares?
- "b": [...Array(8).fill(true)],
+ this.pawnFlags = {
+ w: [...Array(8).fill(true)], //pawns can move 2 squares?
+ b: [...Array(8).fill(true)]
};
- if (!fenflags)
- return;
+ if (!fenflags) return;
const flags = fenflags.substr(4); //skip first 4 digits, for castle
- for (let c of ['w','b'])
- {
- for (let i=0; i<8; i++)
- this.pawnFlags[c][i] = (flags.charAt((c=='w'?0:8)+i) == '1');
+ for (let c of ["w", "b"]) {
+ for (let i = 0; i < 8; i++)
+ this.pawnFlags[c][i] = flags.charAt((c == "w" ? 0 : 8) + i) == "1";
}
}
- aggregateFlags()
- {
+ aggregateFlags() {
return [this.castleFlags, this.pawnFlags];
}
- disaggregateFlags(flags)
- {
+ disaggregateFlags(flags) {
this.castleFlags = flags[0];
this.pawnFlags = flags[1];
}
- getCmove(move)
- {
- if (move.appear[0].c == 'c' && move.vanish.length == 1)
- return {start: move.start, end: move.end};
+ getCmove(move) {
+ if (move.appear[0].c == "c" && move.vanish.length == 1)
+ return { start: move.start, end: move.end };
return null;
}
- canTake([x1,y1], [x2,y2])
- {
- const color1 = this.getColor(x1,y1);
- const color2 = this.getColor(x2,y2);
+ canTake([x1, y1], [x2, y2]) {
+ const color1 = this.getColor(x1, y1);
+ const color2 = this.getColor(x2, y2);
// Checkered aren't captured
- return color1 != color2 && color2 != 'c' && (color1 != 'c' || color2 != this.turn);
+ return (
+ color1 != color2 &&
+ color2 != "c" &&
+ (color1 != "c" || color2 != this.turn)
+ );
}
// Post-processing: apply "checkerization" of standard moves
- getPotentialMovesFrom([x,y])
- {
- let standardMoves = super.getPotentialMovesFrom([x,y]);
+ getPotentialMovesFrom([x, y]) {
+ let standardMoves = super.getPotentialMovesFrom([x, y]);
const lastRank = this.turn == "w" ? 0 : 7;
- if (this.getPiece(x,y) == V.KING)
- return standardMoves; //king has to be treated differently (for castles)
+ if (this.getPiece(x, y) == V.KING) return standardMoves; //king has to be treated differently (for castles)
let moves = [];
standardMoves.forEach(m => {
- if (m.vanish[0].p == V.PAWN)
- {
- if (Math.abs(m.end.x-m.start.x)==2 && !this.pawnFlags[this.turn][m.start.y])
+ if (m.vanish[0].p == V.PAWN) {
+ if (
+ Math.abs(m.end.x - m.start.x) == 2 &&
+ !this.pawnFlags[this.turn][m.start.y]
+ )
return; //skip forbidden 2-squares jumps
- if (this.board[m.end.x][m.end.y] == V.EMPTY && m.vanish.length==2
- && this.getColor(m.start.x,m.start.y) == 'c')
- {
+ if (
+ this.board[m.end.x][m.end.y] == V.EMPTY &&
+ m.vanish.length == 2 &&
+ this.getColor(m.start.x, m.start.y) == "c"
+ ) {
return; //checkered pawns cannot take en-passant
}
}
- if (m.vanish.length == 1)
- moves.push(m); //no capture
- else
- {
+ if (m.vanish.length == 1) moves.push(m);
+ //no capture
+ else {
// A capture occured (m.vanish.length == 2)
m.appear[0].c = "c";
moves.push(m);
- if (m.appear[0].p != m.vanish[1].p //avoid promotions (already treated):
- && (m.vanish[0].p != V.PAWN || m.end.x != lastRank))
- {
+ if (
+ m.appear[0].p != m.vanish[1].p && //avoid promotions (already treated):
+ (m.vanish[0].p != V.PAWN || m.end.x != lastRank)
+ ) {
// Add transformation into captured piece
let m2 = JSON.parse(JSON.stringify(m));
m2.appear[0].p = m.vanish[1].p;
return moves;
}
- canIplay(side, [x,y])
- {
- return (side == this.turn && [side,'c'].includes(this.getColor(x,y)));
+ canIplay(side, [x, y]) {
+ return side == this.turn && [side, "c"].includes(this.getColor(x, y));
}
// Does m2 un-do m1 ? (to disallow undoing checkered moves)
- oppositeMoves(m1, m2)
- {
- return (!!m1 && m2.appear[0].c == 'c'
- && m2.appear.length == 1 && m2.vanish.length == 1
- && m1.start.x == m2.end.x && m1.end.x == m2.start.x
- && m1.start.y == m2.end.y && m1.end.y == m2.start.y);
+ oppositeMoves(m1, m2) {
+ return (
+ !!m1 &&
+ m2.appear[0].c == "c" &&
+ m2.appear.length == 1 &&
+ m2.vanish.length == 1 &&
+ m1.start.x == m2.end.x &&
+ m1.end.x == m2.start.x &&
+ m1.start.y == m2.end.y &&
+ m1.end.y == m2.start.y
+ );
}
- filterValid(moves)
- {
- if (moves.length == 0)
- return [];
+ filterValid(moves) {
+ if (moves.length == 0) return [];
const color = this.turn;
return moves.filter(m => {
const L = this.cmoves.length; //at least 1: init from FEN
- if (this.oppositeMoves(this.cmoves[L-1], m))
- return false;
+ if (this.oppositeMoves(this.cmoves[L - 1], m)) return false;
this.play(m);
const res = !this.underCheck(color);
this.undo(m);
});
}
- isAttackedByPawn([x,y], colors)
- {
- for (let c of colors)
- {
- 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])
- {
- if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==V.PAWN
- && this.getColor(x+pawnShift,y+i)==c)
- {
+ isAttackedByPawn([x, y], colors) {
+ for (let c of colors) {
+ 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]) {
+ if (
+ y + i >= 0 &&
+ y + i < 8 &&
+ this.getPiece(x + pawnShift, y + i) == V.PAWN &&
+ this.getColor(x + pawnShift, y + i) == c
+ ) {
return true;
}
}
return false;
}
- underCheck(color)
- {
- return this.isAttacked(this.kingPos[color], [V.GetOppCol(color),'c']);
+ underCheck(color) {
+ return this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"]);
}
- getCheckSquares(color)
- {
+ getCheckSquares(color) {
// Artifically change turn, for checkered pawns
this.turn = V.GetOppCol(color);
- const kingAttacked = this.isAttacked(
- this.kingPos[color], [V.GetOppCol(color),'c']);
+ const kingAttacked = this.isAttacked(this.kingPos[color], [
+ V.GetOppCol(color),
+ "c"
+ ]);
let res = kingAttacked
? [JSON.parse(JSON.stringify(this.kingPos[color]))] //need to duplicate!
: [];
return res;
}
- updateVariables(move)
- {
+ updateVariables(move) {
super.updateVariables(move);
// Does this move turn off a 2-squares pawn flag?
- const secondRank = [1,6];
+ const secondRank = [1, 6];
if (secondRank.includes(move.start.x) && move.vanish[0].p == V.PAWN)
- this.pawnFlags[move.start.x==6 ? "w" : "b"][move.start.y] = false;
+ this.pawnFlags[move.start.x == 6 ? "w" : "b"][move.start.y] = false;
}
- getCurrentScore()
- {
- if (this.atLeastOneMove()) // game not over
+ getCurrentScore() {
+ if (this.atLeastOneMove())
+ // game not over
return "*";
const color = this.turn;
// Artifically change turn, for checkered pawns
this.turn = V.GetOppCol(this.turn);
- const res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color),'c'])
- ? (color == "w" ? "0-1" : "1-0")
+ const res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"])
+ ? color == "w"
+ ? "0-1"
+ : "1-0"
: "1/2";
this.turn = V.GetOppCol(this.turn);
return res;
}
- evalPosition()
- {
+ evalPosition() {
let evaluation = 0;
//Just count material for now, considering checkered neutral (...)
- 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)
- {
- const sqColor = this.getColor(i,j);
- const sign = sqColor == "w" ? 1 : (sqColor=="b" ? -1 : 0);
- evaluation += sign * V.VALUES[this.getPiece(i,j)];
+ 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) {
+ const sqColor = this.getColor(i, j);
+ const sign = sqColor == "w" ? 1 : sqColor == "b" ? -1 : 0;
+ evaluation += sign * V.VALUES[this.getPiece(i, j)];
}
}
}
return evaluation;
}
- static GenRandInitFen()
- {
+ static GenRandInitFen() {
const randFen = ChessRules.GenRandInitFen();
// Add 16 pawns flags + empty cmove:
return randFen.replace(" w 0 1111", " w 0 11111111111111111111 -");
}
- static ParseFen(fen)
- {
- const fenParsed = ChessRules.ParseFen(fen);
- return Object.assign({},
- ChessRules.ParseFen(fen),
- {cmove: fen.split(" ")[5]});
+ static ParseFen(fen) {
+ return Object.assign({}, ChessRules.ParseFen(fen), {
+ cmove: fen.split(" ")[5]
+ });
}
- getFen()
- {
+ getFen() {
const L = this.cmoves.length;
- const cmoveFen = (!this.cmoves[L-1]
+ const cmoveFen = !this.cmoves[L - 1]
? "-"
- : ChessRules.CoordsToSquare(this.cmoves[L-1].start)
- + ChessRules.CoordsToSquare(this.cmoves[L-1].end));
+ : ChessRules.CoordsToSquare(this.cmoves[L - 1].start) +
+ ChessRules.CoordsToSquare(this.cmoves[L - 1].end);
return super.getFen() + " " + cmoveFen;
}
- getFlagsFen()
- {
+ getFlagsFen() {
let fen = super.getFlagsFen();
// Add pawns flags
- for (let c of ['w','b'])
- {
- for (let i=0; i<8; i++)
- fen += this.pawnFlags[c][i] ? '1' : '0';
+ for (let c of ["w", "b"]) {
+ for (let i = 0; i < 8; i++) fen += this.pawnFlags[c][i] ? "1" : "0";
}
return fen;
}
// TODO (design): this cmove update here or in (un)updateVariables ?
- play(move)
- {
- this.cmoves.push( this.getCmove(move) );
+ play(move) {
+ this.cmoves.push(this.getCmove(move));
super.play(move);
}
- undo(move)
- {
+ undo(move) {
this.cmoves.pop();
super.undo(move);
}
- getNotation(move)
- {
- if (move.appear.length == 2)
- {
+ getNotation(move) {
+ if (move.appear.length == 2) {
// Castle
- if (move.end.y < move.start.y)
- return "0-0-0";
- else
- return "0-0";
+ if (move.end.y < move.start.y) return "0-0-0";
+ return "0-0";
}
// Translate final square
const finalSquare = V.CoordsToSquare(move.end);
const piece = this.getPiece(move.start.x, move.start.y);
- if (piece == V.PAWN)
- {
+ if (piece == V.PAWN) {
// Pawn move
let notation = "";
- if (move.vanish.length > 1)
- {
+ if (move.vanish.length > 1) {
// Capture
const startColumn = V.CoordToColumn(move.start.y);
- notation = startColumn + "x" + finalSquare +
- "=" + move.appear[0].p.toUpperCase();
- }
- else //no capture
- {
+ notation =
+ startColumn +
+ "x" +
+ finalSquare +
+ "=" +
+ move.appear[0].p.toUpperCase();
+ } //no capture
+ else {
notation = finalSquare;
- if (move.appear.length > 0 && piece != move.appear[0].p) //promotion
+ if (move.appear.length > 0 && piece != move.appear[0].p)
+ //promotion
notation += "=" + move.appear[0].p.toUpperCase();
}
return notation;
}
-
- else
- {
- // Piece movement
- return piece.toUpperCase() + (move.vanish.length > 1 ? "x" : "") + finalSquare
- + (move.vanish.length > 1 ? "=" + move.appear[0].p.toUpperCase() : "");
- }
+ // Piece movement
+ return (
+ piece.toUpperCase() +
+ (move.vanish.length > 1 ? "x" : "") +
+ finalSquare +
+ (move.vanish.length > 1 ? "=" + move.appear[0].p.toUpperCase() : "")
+ );
}
-}
+};
import { ChessRules } from "@/base_rules";
-export const VariantRules = class Chess960Rules extends ChessRules
-{
+export const VariantRules = class Chess960Rules extends ChessRules {
// Standard rules
-}
+};
import { ChessRules, PiPo, Move } from "@/base_rules";
-import { ArrayFun} from "@/utils/array";
+import { ArrayFun } from "@/utils/array";
-export const VariantRules = class CrazyhouseRules extends ChessRules
-{
- static IsGoodFen(fen)
- {
- if (!ChessRules.IsGoodFen(fen))
- return false;
+export const VariantRules = class CrazyhouseRules extends ChessRules {
+ static IsGoodFen(fen) {
+ if (!ChessRules.IsGoodFen(fen)) return false;
const fenParsed = V.ParseFen(fen);
// 5) Check reserves
if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{10,10}$/))
return false;
// 6) Check promoted array
- if (!fenParsed.promoted)
- return false;
- if (fenParsed.promoted == "-")
- return true; //no promoted piece on board
+ if (!fenParsed.promoted) return false;
+ if (fenParsed.promoted == "-") return true; //no promoted piece on board
const squares = fenParsed.promoted.split(",");
- for (let square of squares)
- {
+ for (let square of squares) {
const c = V.SquareToCoords(square);
if (c.y < 0 || c.y > V.size.y || isNaN(c.x) || c.x < 0 || c.x > V.size.x)
return false;
return true;
}
- static ParseFen(fen)
- {
+ static ParseFen(fen) {
const fenParts = fen.split(" ");
- return Object.assign(
- ChessRules.ParseFen(fen),
- {
- reserve: fenParts[5],
- promoted: fenParts[6],
- }
- );
+ return Object.assign(ChessRules.ParseFen(fen), {
+ reserve: fenParts[5],
+ promoted: fenParts[6]
+ });
}
- static GenRandInitFen()
- {
+ static GenRandInitFen() {
return ChessRules.GenRandInitFen() + " 0000000000 -";
}
- getFen()
- {
- return super.getFen() + " " + this.getReserveFen() + " " + this.getPromotedFen();
+ getFen() {
+ return (
+ super.getFen() + " " + this.getReserveFen() + " " + this.getPromotedFen()
+ );
}
- getReserveFen()
- {
+ getReserveFen() {
let counts = new Array(10);
- for (let i=0; i<V.PIECES.length-1; i++) //-1: no king reserve
- {
+ for (
+ let i = 0;
+ i < V.PIECES.length - 1;
+ i++ //-1: no king reserve
+ ) {
counts[i] = this.reserve["w"][V.PIECES[i]];
- counts[5+i] = this.reserve["b"][V.PIECES[i]];
+ counts[5 + i] = this.reserve["b"][V.PIECES[i]];
}
return counts.join("");
}
- getPromotedFen()
- {
+ getPromotedFen() {
let res = "";
- for (let i=0; i<V.size.x; i++)
- {
- for (let j=0; j<V.size.y; j++)
- {
- if (this.promoted[i][j])
- res += V.CoordsToSquare({x:i,y:j});
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
+ if (this.promoted[i][j]) res += V.CoordsToSquare({ x: i, y: j });
}
}
- if (res.length > 0)
- res = res.slice(0,-1); //remove last comma
- else
- res = "-";
+ if (res.length > 0) res = res.slice(0, -1);
+ //remove last comma
+ else res = "-";
return res;
}
- setOtherVariables(fen)
- {
+ setOtherVariables(fen) {
super.setOtherVariables(fen);
const fenParsed = V.ParseFen(fen);
// Also init reserves (used by the interface to show landable pieces)
- this.reserve =
- {
- "w":
- {
+ this.reserve = {
+ w: {
[V.PAWN]: parseInt(fenParsed.reserve[0]),
[V.ROOK]: parseInt(fenParsed.reserve[1]),
[V.KNIGHT]: parseInt(fenParsed.reserve[2]),
[V.BISHOP]: parseInt(fenParsed.reserve[3]),
- [V.QUEEN]: parseInt(fenParsed.reserve[4]),
+ [V.QUEEN]: parseInt(fenParsed.reserve[4])
},
- "b":
- {
+ b: {
[V.PAWN]: parseInt(fenParsed.reserve[5]),
[V.ROOK]: parseInt(fenParsed.reserve[6]),
[V.KNIGHT]: parseInt(fenParsed.reserve[7]),
[V.BISHOP]: parseInt(fenParsed.reserve[8]),
- [V.QUEEN]: parseInt(fenParsed.reserve[9]),
+ [V.QUEEN]: parseInt(fenParsed.reserve[9])
}
};
this.promoted = ArrayFun.init(V.size.x, V.size.y, false);
- if (fenParsed.promoted != "-")
- {
- for (let square of fenParsed.promoted.split(","))
- {
- const [x,y] = V.SquareToCoords(square);
- promoted[x][y] = true;
+ if (fenParsed.promoted != "-") {
+ for (let square of fenParsed.promoted.split(",")) {
+ const [x, y] = V.SquareToCoords(square);
+ this.promoted[x][y] = true;
}
}
}
- getColor(i,j)
- {
- if (i >= V.size.x)
- return (i==V.size.x ? "w" : "b");
+ getColor(i, j) {
+ if (i >= V.size.x) return i == V.size.x ? "w" : "b";
return this.board[i][j].charAt(0);
}
- getPiece(i,j)
- {
- if (i >= V.size.x)
- return V.RESERVE_PIECES[j];
+ getPiece(i, j) {
+ if (i >= V.size.x) return V.RESERVE_PIECES[j];
return this.board[i][j].charAt(1);
}
// Used by the interface:
- getReservePpath(color, index)
- {
+ getReservePpath(color, index) {
return color + V.RESERVE_PIECES[index];
}
// Ordering on reserve pieces
- static get RESERVE_PIECES()
- {
- return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
+ static get RESERVE_PIECES() {
+ return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
}
- getReserveMoves([x,y])
- {
+ getReserveMoves([x, y]) {
const color = this.turn;
const p = V.RESERVE_PIECES[y];
- if (this.reserve[color][p] == 0)
- return [];
+ if (this.reserve[color][p] == 0) return [];
let moves = [];
- const pawnShift = (p==V.PAWN ? 1 : 0);
- for (let i=pawnShift; i<V.size.x-pawnShift; i++)
- {
- for (let j=0; j<V.size.y; j++)
- {
- if (this.board[i][j] == V.EMPTY)
- {
+ const pawnShift = p == V.PAWN ? 1 : 0;
+ for (let i = pawnShift; i < V.size.x - pawnShift; i++) {
+ for (let j = 0; j < V.size.y; j++) {
+ if (this.board[i][j] == V.EMPTY) {
let mv = new Move({
appear: [
new PiPo({
})
],
vanish: [],
- start: {x:x, y:y}, //a bit artificial...
- end: {x:i, y:j}
+ start: { x: x, y: y }, //a bit artificial...
+ end: { x: i, y: j }
});
moves.push(mv);
}
return moves;
}
- getPotentialMovesFrom([x,y])
- {
- if (x >= V.size.x)
- {
+ getPotentialMovesFrom([x, y]) {
+ if (x >= V.size.x) {
// Reserves, outside of board: x == sizeX(+1)
- return this.getReserveMoves([x,y]);
+ return this.getReserveMoves([x, y]);
}
// Standard moves
- return super.getPotentialMovesFrom([x,y]);
+ return super.getPotentialMovesFrom([x, y]);
}
- getAllValidMoves()
- {
+ getAllValidMoves() {
let moves = super.getAllValidMoves();
const color = this.turn;
- for (let i=0; i<V.RESERVE_PIECES.length; i++)
- moves = moves.concat(this.getReserveMoves([V.size.x+(color=="w"?0:1),i]));
+ for (let i = 0; i < V.RESERVE_PIECES.length; i++)
+ moves = moves.concat(
+ this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
+ );
return this.filterValid(moves);
}
- atLeastOneMove()
- {
- if (!super.atLeastOneMove())
- {
- const color = this.turn;
+ atLeastOneMove() {
+ if (!super.atLeastOneMove()) {
// Search one reserve move
- for (let i=0; i<V.RESERVE_PIECES.length; i++)
- {
+ for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
let moves = this.filterValid(
- this.getReserveMoves([V.size.x+(this.turn=="w"?0:1), i]) );
- if (moves.length > 0)
- return true;
+ this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i])
+ );
+ if (moves.length > 0) return true;
}
return false;
}
return true;
}
- updateVariables(move)
- {
+ updateVariables(move) {
super.updateVariables(move);
- if (move.vanish.length == 2 && move.appear.length == 2)
- return; //skip castle
+ if (move.vanish.length == 2 && move.appear.length == 2) return; //skip castle
const color = move.appear[0].c;
- if (move.vanish.length == 0)
- {
+ if (move.vanish.length == 0) {
this.reserve[color][move.appear[0].p]--;
return;
}
move.movePromoted = this.promoted[move.start.x][move.start.y];
- move.capturePromoted = this.promoted[move.end.x][move.end.y]
+ move.capturePromoted = this.promoted[move.end.x][move.end.y];
this.promoted[move.start.x][move.start.y] = false;
- this.promoted[move.end.x][move.end.y] = move.movePromoted
- || (move.vanish[0].p == V.PAWN && move.appear[0].p != V.PAWN);
- if (move.capturePromoted)
- this.reserve[color][V.PAWN]++;
- else if (move.vanish.length == 2)
- this.reserve[color][move.vanish[1].p]++;
+ this.promoted[move.end.x][move.end.y] =
+ move.movePromoted ||
+ (move.vanish[0].p == V.PAWN && move.appear[0].p != V.PAWN);
+ if (move.capturePromoted) this.reserve[color][V.PAWN]++;
+ else if (move.vanish.length == 2) this.reserve[color][move.vanish[1].p]++;
}
- unupdateVariables(move)
- {
+ unupdateVariables(move) {
super.unupdateVariables(move);
- if (move.vanish.length == 2 && move.appear.length == 2)
- return;
+ if (move.vanish.length == 2 && move.appear.length == 2) return;
const color = this.turn;
- if (move.vanish.length == 0)
- {
+ if (move.vanish.length == 0) {
this.reserve[color][move.appear[0].p]++;
return;
}
- if (move.movePromoted)
- this.promoted[move.start.x][move.start.y] = true;
+ if (move.movePromoted) this.promoted[move.start.x][move.start.y] = true;
this.promoted[move.end.x][move.end.y] = move.capturePromoted;
- if (move.capturePromoted)
- this.reserve[color][V.PAWN]--;
- else if (move.vanish.length == 2)
- this.reserve[color][move.vanish[1].p]--;
+ if (move.capturePromoted) this.reserve[color][V.PAWN]--;
+ else if (move.vanish.length == 2) this.reserve[color][move.vanish[1].p]--;
}
- static get SEARCH_DEPTH() { return 2; } //high branching factor
+ static get SEARCH_DEPTH() {
+ return 2;
+ } //high branching factor
- evalPosition()
- {
+ evalPosition() {
let evaluation = super.evalPosition();
// Add reserves:
- for (let i=0; i<V.RESERVE_PIECES.length; i++)
- {
+ for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
const p = V.RESERVE_PIECES[i];
evaluation += this.reserve["w"][p] * V.VALUES[p];
evaluation -= this.reserve["b"][p] * V.VALUES[p];
return evaluation;
}
- getNotation(move)
- {
- if (move.vanish.length > 0)
- return super.getNotation(move);
+ getNotation(move) {
+ if (move.vanish.length > 0) return super.getNotation(move);
// Rebirth:
const piece =
- (move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "");
+ move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
return piece + "@" + V.CoordsToSquare(move.end);
}
- getLongNotation(move)
- {
- if (move.vanish.length > 0)
- return super.getLongNotation(move);
+ getLongNotation(move) {
+ if (move.vanish.length > 0) return super.getLongNotation(move);
return "@" + V.CoordsToSquare(move.end);
}
-}
+};
import { ChessRules } from "@/base_rules";
-import { ArrayFun} from "@/utils/array";
+import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class DarkRules extends ChessRules
-{
+export const VariantRules = class DarkRules extends ChessRules {
// Standard rules, in the shadow
- setOtherVariables(fen)
- {
+ setOtherVariables(fen) {
super.setOtherVariables(fen);
- const [sizeX,sizeY] = [V.size.x,V.size.y];
+ const [sizeX, sizeY] = [V.size.x, V.size.y];
this.enlightened = {
- "w": ArrayFun.init(sizeX,sizeY),
- "b": ArrayFun.init(sizeX,sizeY)
+ w: ArrayFun.init(sizeX, sizeY),
+ b: ArrayFun.init(sizeX, sizeY)
};
// Setup enlightened: squares reachable by each side
// (TODO: one side would be enough ?)
this.updateEnlightened();
}
- updateEnlightened()
- {
- for (let i=0; i<V.size.x; i++)
- {
- for (let j=0; j<V.size.y; j++)
- {
+ updateEnlightened() {
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
this.enlightened["w"][i][j] = false;
this.enlightened["b"][i][j] = false;
}
}
- const pawnShift = {"w":-1, "b":1};
+ const pawnShift = { w: -1, b: 1 };
// Initialize with pieces positions (which are seen)
- 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)
- {
- const color = this.getColor(i,j);
+ 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) {
+ const color = this.getColor(i, j);
this.enlightened[color][i][j] = true;
// Add potential squares visible by "impossible pawn capture"
- if (this.getPiece(i,j) == V.PAWN)
- {
- for (let shiftY of [-1,1])
- {
- if (V.OnBoard(i+pawnShift[color],j+shiftY)
- && this.board[i+pawnShift[color]][j+shiftY] == V.EMPTY)
- {
- this.enlightened[color][i+pawnShift[color]][j+shiftY] = true;
+ if (this.getPiece(i, j) == V.PAWN) {
+ for (let shiftY of [-1, 1]) {
+ if (
+ V.OnBoard(i + pawnShift[color], j + shiftY) &&
+ this.board[i + pawnShift[color]][j + shiftY] == V.EMPTY
+ ) {
+ this.enlightened[color][i + pawnShift[color]][
+ j + shiftY
+ ] = true;
}
}
}
}
// Has to be redefined to avoid an infinite loop
- getAllValidMoves()
- {
+ getAllValidMoves() {
const color = this.turn;
- const oppCol = V.GetOppCol(color);
let potentialMoves = [];
- 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 && this.getColor(i,j) == color)
- Array.prototype.push.apply(potentialMoves, this.getPotentialMovesFrom([i,j]));
+ 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 && this.getColor(i, j) == color)
+ Array.prototype.push.apply(
+ potentialMoves,
+ this.getPotentialMovesFrom([i, j])
+ );
}
}
return potentialMoves; //because there are no checks
}
- atLeastOneMove()
- {
- if (this.kingPos[this.turn][0] < 0)
- return false;
+ atLeastOneMove() {
+ if (this.kingPos[this.turn][0] < 0) return false;
return true; //TODO: is it right?
}
- underCheck(color)
- {
+ underCheck() {
return false; //there is no check
}
- getCheckSquares(color)
- {
+ getCheckSquares() {
return [];
}
- updateVariables(move)
- {
+ updateVariables(move) {
super.updateVariables(move);
- if (move.vanish.length >= 2 && move.vanish[1].p == V.KING)
- {
+ if (move.vanish.length >= 2 && move.vanish[1].p == V.KING) {
// We took opponent king ! (because if castle vanish[1] is a rook)
- this.kingPos[this.turn] = [-1,-1];
+ this.kingPos[this.turn] = [-1, -1];
}
// Update lights for both colors:
this.updateEnlightened();
}
- unupdateVariables(move)
- {
+ unupdateVariables(move) {
super.unupdateVariables(move);
const c = move.vanish[0].c;
const oppCol = V.GetOppCol(c);
- if (this.kingPos[oppCol][0] < 0)
- {
+ if (this.kingPos[oppCol][0] < 0) {
// Last move took opponent's king
- for (let psq of move.vanish)
- {
- if (psq.p == 'k')
- {
+ for (let psq of move.vanish) {
+ if (psq.p == "k") {
this.kingPos[oppCol] = [psq.x, psq.y];
break;
}
this.updateEnlightened();
}
- getCurrentScore()
- {
+ getCurrentScore() {
const color = this.turn;
const kp = this.kingPos[color];
- if (kp[0] < 0) //king disappeared
- return (color == "w" ? "0-1" : "1-0");
- if (this.atLeastOneMove()) // game not over
+ if (kp[0] < 0)
+ //king disappeared
+ return color == "w" ? "0-1" : "1-0";
+ if (this.atLeastOneMove())
+ // game not over
return "*";
return "1/2"; //no moves but kings still there (seems impossible)
}
- static get THRESHOLD_MATE()
- {
+ static get THRESHOLD_MATE() {
return 500; //checkmates evals may be slightly below 1000
}
// In this special situation, we just look 1 half move ahead
- getComputerMove()
- {
+ getComputerMove() {
const maxeval = V.INFINITY;
const color = this.turn;
const oppCol = V.GetOppCol(color);
- const pawnShift = (color == "w" ? -1 : 1);
+ const pawnShift = color == "w" ? -1 : 1;
// Do not cheat: the current enlightment is all we can see
const myLight = JSON.parse(JSON.stringify(this.enlightened[color]));
// Can a slider on (i,j) apparently take my king?
// NOTE: inaccurate because assume yes if some squares are shadowed
- const sliderTake = ([i,j], piece) => {
+ const sliderTake = ([i, j], piece) => {
const kp = this.kingPos[color];
let step = undefined;
- if (piece == V.BISHOP)
- {
- if (Math.abs(kp[0] - i) == Math.abs(kp[1] - j))
- {
- step =
- [
- (i-kp[0]) / Math.abs(i-kp[0]),
- (j-kp[1]) / Math.abs(j-kp[1])
+ if (piece == V.BISHOP) {
+ if (Math.abs(kp[0] - i) == Math.abs(kp[1] - j)) {
+ step = [
+ (i - kp[0]) / Math.abs(i - kp[0]),
+ (j - kp[1]) / Math.abs(j - kp[1])
];
}
+ } else if (piece == V.ROOK) {
+ if (kp[0] == i) step = [0, (j - kp[1]) / Math.abs(j - kp[1])];
+ else if (kp[1] == j) step = [(i - kp[0]) / Math.abs(i - kp[0]), 0];
}
- else if (piece == V.ROOK)
- {
- if (kp[0] == i)
- step = [0, (j-kp[1]) / Math.abs(j-kp[1])];
- else if (kp[1] == j)
- step = [(i-kp[0]) / Math.abs(i-kp[0]), 0];
- }
- if (!step)
- return false;
+ if (!step) return false;
// Check for obstacles
let obstacle = false;
for (
- let x=kp[0]+step[0], y=kp[1]+step[1];
+ let x = kp[0] + step[0], y = kp[1] + step[1];
x != i && y != j;
- x += step[0], y += step[1])
- {
- if (myLight[x][y] && this.board[x][y] != V.EMPTY)
- {
+ x += step[0], y += step[1]
+ ) {
+ if (myLight[x][y] && this.board[x][y] != V.EMPTY) {
obstacle = true;
break;
}
}
- if (!obstacle)
- return true;
+ if (!obstacle) return true;
return false;
};
// Do I see something which can take my king ?
const kingThreats = () => {
const kp = this.kingPos[color];
- for (let i=0; i<V.size.x; i++)
- {
- for (let j=0; j<V.size.y; j++)
- {
- if (myLight[i][j] && this.board[i][j] != V.EMPTY
- && this.getColor(i,j) != color)
- {
- switch (this.getPiece(i,j))
- {
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
+ if (
+ myLight[i][j] &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) != color
+ ) {
+ switch (this.getPiece(i, j)) {
case V.PAWN:
- if (kp[0] + pawnShift == i && Math.abs(kp[1]-j) == 1)
+ if (kp[0] + pawnShift == i && Math.abs(kp[1] - j) == 1)
return true;
break;
case V.KNIGHT:
- if ((Math.abs(kp[0] - i) == 2 && Math.abs(kp[1] - j) == 1) ||
- (Math.abs(kp[0] - i) == 1 && Math.abs(kp[1] - j) == 2))
- {
+ if (
+ (Math.abs(kp[0] - i) == 2 && Math.abs(kp[1] - j) == 1) ||
+ (Math.abs(kp[0] - i) == 1 && Math.abs(kp[1] - j) == 2)
+ ) {
return true;
}
break;
return true;
break;
case V.BISHOP:
- if (sliderTake([i,j], V.BISHOP))
- return true;
+ if (sliderTake([i, j], V.BISHOP)) return true;
break;
case V.ROOK:
- if (sliderTake([i,j], V.ROOK))
- return true;
+ if (sliderTake([i, j], V.ROOK)) return true;
break;
case V.QUEEN:
- if (sliderTake([i,j], V.BISHOP) || sliderTake([i,j], V.ROOK))
+ if (sliderTake([i, j], V.BISHOP) || sliderTake([i, j], V.ROOK))
return true;
break;
}
};
let moves = this.getAllValidMoves();
- for (let move of moves)
- {
+ for (let move of moves) {
this.play(move);
- if (this.kingPos[oppCol][0] >= 0 && kingThreats())
- {
+ if (this.kingPos[oppCol][0] >= 0 && kingThreats()) {
// We didn't take opponent king, and our king will be captured: bad
move.eval = -maxeval;
}
this.undo(move);
- if (!!move.eval)
- continue;
+ if (move.eval) continue;
move.eval = 0; //a priori...
// Can I take something ? If yes, do it if it seems good...
- if (move.vanish.length == 2 && move.vanish[1].c != color) //avoid castle
- {
+ if (move.vanish.length == 2 && move.vanish[1].c != color) {
+ //avoid castle
const myPieceVal = V.VALUES[move.appear[0].p];
const hisPieceVal = V.VALUES[move.vanish[1].p];
- if (myPieceVal <= hisPieceVal)
- move.eval = hisPieceVal - myPieceVal + 2; //favor captures
- else
- {
+ if (myPieceVal <= hisPieceVal) move.eval = hisPieceVal - myPieceVal + 2;
+ //favor captures
+ else {
// Taking a pawn with minor piece,
// or minor piece or pawn with a rook,
// or anything but a queen with a queen,
// or anything with a king.
// ==> Do it at random, although
// this is clearly inferior to what a human can deduce...
- move.eval = (Math.random() < 0.5 ? 1 : -1);
+ move.eval = Math.random() < 0.5 ? 1 : -1;
}
}
}
// TODO: also need to implement the case when an opponent piece (in light)
// is threatening something - maybe not the king, but e.g. pawn takes rook.
- moves.sort((a,b) => b.eval - a.eval);
+ moves.sort((a, b) => b.eval - a.eval);
let candidates = [0];
- for (let j=1; j<moves.length && moves[j].eval == moves[0].eval; j++)
+ for (let j = 1; j < moves.length && moves[j].eval == moves[0].eval; j++)
candidates.push(j);
return moves[candidates[randInt(candidates.length)]];
}
-}
+};
import { ChessRules } from "@/base_rules";
-export const VariantRules = class ExtinctionRules extends ChessRules
-{
- setOtherVariables(fen)
- {
+export const VariantRules = class ExtinctionRules extends ChessRules {
+ setOtherVariables(fen) {
super.setOtherVariables(fen);
const pos = V.ParseFen(fen).position;
// NOTE: no need for safety "|| []", because each piece type must be present
// (otherwise game is already over!)
- this.material =
- {
- "w":
- {
+ this.material = {
+ w: {
[V.KING]: pos.match(/K/g).length,
[V.QUEEN]: pos.match(/Q/g).length,
[V.ROOK]: pos.match(/R/g).length,
[V.BISHOP]: pos.match(/B/g).length,
[V.PAWN]: pos.match(/P/g).length
},
- "b":
- {
+ b: {
[V.KING]: pos.match(/k/g).length,
[V.QUEEN]: pos.match(/q/g).length,
[V.ROOK]: pos.match(/r/g).length,
};
}
- getPotentialPawnMoves([x,y])
- {
- let moves = super.getPotentialPawnMoves([x,y]);
+ getPotentialPawnMoves([x, y]) {
+ let moves = super.getPotentialPawnMoves([x, y]);
// Add potential promotions into king
const color = this.turn;
- const shift = (color == "w" ? -1 : 1);
- const lastRank = (color == "w" ? 0 : V.size.x-1);
+ const shift = color == "w" ? -1 : 1;
+ const lastRank = color == "w" ? 0 : V.size.x - 1;
- if (x+shift == lastRank)
- {
+ if (x + shift == lastRank) {
// Normal move
- if (this.board[x+shift][y] == V.EMPTY)
- moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:V.KING}));
+ if (this.board[x + shift][y] == V.EMPTY)
+ moves.push(
+ this.getBasicMove([x, y], [x + shift, y], { c: color, p: V.KING })
+ );
// Captures
- if (y>0 && this.board[x+shift][y-1] != V.EMPTY
- && this.canTake([x,y], [x+shift,y-1]))
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:V.KING}));
+ if (
+ y > 0 &&
+ this.board[x + shift][y - 1] != V.EMPTY &&
+ this.canTake([x, y], [x + shift, y - 1])
+ ) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shift, y - 1], { c: color, p: V.KING })
+ );
}
- if (y<V.size.y-1 && this.board[x+shift][y+1] != V.EMPTY
- && this.canTake([x,y], [x+shift,y+1]))
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:V.KING}));
+ if (
+ y < V.size.y - 1 &&
+ this.board[x + shift][y + 1] != V.EMPTY &&
+ this.canTake([x, y], [x + shift, y + 1])
+ ) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shift, y + 1], { c: color, p: V.KING })
+ );
}
}
}
// TODO: verify this assertion
- atLeastOneMove()
- {
+ atLeastOneMove() {
return true; //always at least one possible move
}
- underCheck(color)
- {
+ underCheck() {
return false; //there is no check
}
- getCheckSquares(color)
- {
+ getCheckSquares() {
return [];
}
- updateVariables(move)
- {
+ updateVariables(move) {
super.updateVariables(move);
// Treat the promotion case: (not the capture part)
- if (move.appear[0].p != move.vanish[0].p)
- {
+ if (move.appear[0].p != move.vanish[0].p) {
this.material[move.appear[0].c][move.appear[0].p]++;
this.material[move.appear[0].c][V.PAWN]--;
}
- if (move.vanish.length==2 && move.appear.length==1) //capture
+ if (move.vanish.length == 2 && move.appear.length == 1)
+ //capture
this.material[move.vanish[1].c][move.vanish[1].p]--;
}
- unupdateVariables(move)
- {
+ unupdateVariables(move) {
super.unupdateVariables(move);
- if (move.appear[0].p != move.vanish[0].p)
- {
+ if (move.appear[0].p != move.vanish[0].p) {
this.material[move.appear[0].c][move.appear[0].p]--;
this.material[move.appear[0].c][V.PAWN]++;
}
- if (move.vanish.length==2 && move.appear.length==1)
+ if (move.vanish.length == 2 && move.appear.length == 1)
this.material[move.vanish[1].c][move.vanish[1].p]++;
}
- getCurrentScore()
- {
- if (this.atLeastOneMove()) // game not over?
- {
+ getCurrentScore() {
+ if (this.atLeastOneMove()) {
+ // game not over?
const color = this.turn;
- if (Object.keys(this.material[color]).some(
- p => { return this.material[color][p] == 0; }))
- {
- return (this.turn == "w" ? "0-1" : "1-0");
+ if (
+ Object.keys(this.material[color]).some(p => {
+ return this.material[color][p] == 0;
+ })
+ ) {
+ return this.turn == "w" ? "0-1" : "1-0";
}
return "*";
}
- return (this.turn == "w" ? "0-1" : "1-0"); //NOTE: currently unreachable...
+ return this.turn == "w" ? "0-1" : "1-0"; //NOTE: currently unreachable...
}
- evalPosition()
- {
+ evalPosition() {
const color = this.turn;
- if (Object.keys(this.material[color]).some(
- p => { return this.material[color][p] == 0; }))
- {
+ if (
+ Object.keys(this.material[color]).some(p => {
+ return this.material[color][p] == 0;
+ })
+ ) {
// Very negative (resp. positive) if white (reps. black) pieces set is incomplete
- return (color=="w"?-1:1) * V.INFINITY;
+ return (color == "w" ? -1 : 1) * V.INFINITY;
}
return super.evalPosition();
}
-}
+};
// NOTE: initial setup differs from the original; see
// https://www.chessvariants.com/large.dir/freeling.html
-export const VariantRules = class GrandRules extends ChessRules
-{
- static getPpath(b)
- {
- return ([V.MARSHALL,V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b;
+export const VariantRules = class GrandRules extends ChessRules {
+ static getPpath(b) {
+ return ([V.MARSHALL, V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b;
}
- static IsGoodFen(fen)
- {
- if (!ChessRules.IsGoodFen(fen))
- return false;
+ static IsGoodFen(fen) {
+ if (!ChessRules.IsGoodFen(fen)) return false;
const fenParsed = V.ParseFen(fen);
// 5) Check captures
if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{14,14}$/))
return true;
}
- static IsGoodEnpassant(enpassant)
- {
- if (enpassant != "-")
- {
+ static IsGoodEnpassant(enpassant) {
+ if (enpassant != "-") {
const squares = enpassant.split(",");
- if (squares.length > 2)
- return false;
- for (let sq of squares)
- {
+ if (squares.length > 2) return false;
+ for (let sq of squares) {
const ep = V.SquareToCoords(sq);
- if (isNaN(ep.x) || !V.OnBoard(ep))
- return false;
+ if (isNaN(ep.x) || !V.OnBoard(ep)) return false;
}
}
return true;
}
- static ParseFen(fen)
- {
+ static ParseFen(fen) {
const fenParts = fen.split(" ");
- return Object.assign(
- ChessRules.ParseFen(fen),
- { captured: fenParts[5] }
- );
+ return Object.assign(ChessRules.ParseFen(fen), { captured: fenParts[5] });
}
- getFen()
- {
+ getFen() {
return super.getFen() + " " + this.getCapturedFen();
}
- getCapturedFen()
- {
+ getCapturedFen() {
let counts = [...Array(14).fill(0)];
let i = 0;
- for (let j=0; j<V.PIECES.length; j++)
- {
- if (V.PIECES[j] == V.KING) //no king captured
+ for (let j = 0; j < V.PIECES.length; j++) {
+ if (V.PIECES[j] == V.KING)
+ //no king captured
continue;
counts[i] = this.captured["w"][V.PIECES[i]];
- counts[7+i] = this.captured["b"][V.PIECES[i]];
+ counts[7 + i] = this.captured["b"][V.PIECES[i]];
i++;
}
return counts.join("");
}
- setOtherVariables(fen)
- {
+ setOtherVariables(fen) {
super.setOtherVariables(fen);
const fenParsed = V.ParseFen(fen);
// Initialize captured pieces' counts from FEN
- this.captured =
- {
- "w":
- {
+ this.captured = {
+ w: {
[V.PAWN]: parseInt(fenParsed.captured[0]),
[V.ROOK]: parseInt(fenParsed.captured[1]),
[V.KNIGHT]: parseInt(fenParsed.captured[2]),
[V.BISHOP]: parseInt(fenParsed.captured[3]),
[V.QUEEN]: parseInt(fenParsed.captured[4]),
[V.MARSHALL]: parseInt(fenParsed.captured[5]),
- [V.CARDINAL]: parseInt(fenParsed.captured[6]),
+ [V.CARDINAL]: parseInt(fenParsed.captured[6])
},
- "b":
- {
+ b: {
[V.PAWN]: parseInt(fenParsed.captured[7]),
[V.ROOK]: parseInt(fenParsed.captured[8]),
[V.KNIGHT]: parseInt(fenParsed.captured[9]),
[V.BISHOP]: parseInt(fenParsed.captured[10]),
[V.QUEEN]: parseInt(fenParsed.captured[11]),
[V.MARSHALL]: parseInt(fenParsed.captured[12]),
- [V.CARDINAL]: parseInt(fenParsed.captured[13]),
+ [V.CARDINAL]: parseInt(fenParsed.captured[13])
}
};
}
- static get size() { return {x:10,y:10}; }
+ static get size() {
+ return { x: 10, y: 10 };
+ }
- static get MARSHALL() { return 'm'; } //rook+knight
- static get CARDINAL() { return 'c'; } //bishop+knight
+ static get MARSHALL() {
+ return "m";
+ } //rook+knight
+ static get CARDINAL() {
+ return "c";
+ } //bishop+knight
- static get PIECES()
- {
- return ChessRules.PIECES.concat([V.MARSHALL,V.CARDINAL]);
+ static get PIECES() {
+ return ChessRules.PIECES.concat([V.MARSHALL, V.CARDINAL]);
}
// There may be 2 enPassant squares (if pawn jump 3 squares)
- getEnpassantFen()
- {
+ getEnpassantFen() {
const L = this.epSquares.length;
- if (!this.epSquares[L-1])
- return "-"; //no en-passant
+ if (!this.epSquares[L - 1]) return "-"; //no en-passant
let res = "";
- this.epSquares[L-1].forEach(sq => {
+ this.epSquares[L - 1].forEach(sq => {
res += V.CoordsToSquare(sq) + ",";
});
- return res.slice(0,-1); //remove last comma
+ return res.slice(0, -1); //remove last comma
}
// En-passant after 2-sq or 3-sq jumps
- getEpSquare(moveOrSquare)
- {
- if (!moveOrSquare)
- return undefined;
- if (typeof moveOrSquare === "string")
- {
+ getEpSquare(moveOrSquare) {
+ if (!moveOrSquare) return undefined;
+ if (typeof moveOrSquare === "string") {
const square = moveOrSquare;
- if (square == "-")
- return undefined;
+ if (square == "-") return undefined;
let res = [];
square.split(",").forEach(sq => {
res.push(V.SquareToCoords(sq));
}
// Argument is a move:
const move = moveOrSquare;
- const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
- if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) >= 2)
- {
- const step = (ex-sx) / Math.abs(ex-sx);
- let res = [{
- x: sx + step,
- y: sy
- }];
- if (sx + 2*step != ex) //3-squares move
- {
+ const [sx, sy, ex] = [move.start.x, move.start.y, move.end.x];
+ if (this.getPiece(sx, sy) == V.PAWN && Math.abs(sx - ex) >= 2) {
+ const step = (ex - sx) / Math.abs(ex - sx);
+ let res = [
+ {
+ x: sx + step,
+ y: sy
+ }
+ ];
+ if (sx + 2 * step != ex) {
+ //3-squares move
res.push({
- x: sx + 2*step,
+ x: sx + 2 * step,
y: sy
});
}
return undefined; //default
}
- getPotentialMovesFrom([x,y])
- {
- switch (this.getPiece(x,y))
- {
+ getPotentialMovesFrom([x, y]) {
+ switch (this.getPiece(x, y)) {
case V.MARSHALL:
- return this.getPotentialMarshallMoves([x,y]);
+ return this.getPotentialMarshallMoves([x, y]);
case V.CARDINAL:
- return this.getPotentialCardinalMoves([x,y]);
+ return this.getPotentialCardinalMoves([x, y]);
default:
- return super.getPotentialMovesFrom([x,y])
+ return super.getPotentialMovesFrom([x, y]);
}
}
// Special pawn rules: promotions to captured friendly pieces,
// optional on ranks 8-9 and mandatory on rank 10.
- getPotentialPawnMoves([x,y])
- {
+ getPotentialPawnMoves([x, y]) {
const color = this.turn;
let moves = [];
- const [sizeX,sizeY] = [V.size.x,V.size.y];
- const shiftX = (color == "w" ? -1 : 1);
- const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]);
- const lastRanks = (color == "w" ? [0,1,2] : [sizeX-1,sizeX-2,sizeX-3]);
- const promotionPieces =
- [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN,V.MARSHALL,V.CARDINAL];
+ const [sizeX, sizeY] = [V.size.x, V.size.y];
+ const shiftX = color == "w" ? -1 : 1;
+ const startRanks = color == "w" ? [sizeX - 2, sizeX - 3] : [1, 2];
+ const lastRanks =
+ color == "w" ? [0, 1, 2] : [sizeX - 1, sizeX - 2, sizeX - 3];
+ const promotionPieces = [
+ V.ROOK,
+ V.KNIGHT,
+ V.BISHOP,
+ V.QUEEN,
+ V.MARSHALL,
+ V.CARDINAL
+ ];
// Always x+shiftX >= 0 && x+shiftX < sizeX, because no pawns on last rank
let finalPieces = undefined;
- if (lastRanks.includes(x + shiftX))
- {
+ if (lastRanks.includes(x + shiftX)) {
finalPieces = promotionPieces.filter(p => this.captured[color][p] > 0);
- if (x + shiftX != lastRanks[0])
- finalPieces.push(V.PAWN);
- }
- else
- finalPieces = [V.PAWN];
- if (this.board[x+shiftX][y] == V.EMPTY)
- {
+ if (x + shiftX != lastRanks[0]) finalPieces.push(V.PAWN);
+ } else finalPieces = [V.PAWN];
+ if (this.board[x + shiftX][y] == V.EMPTY) {
// One square forward
for (let piece of finalPieces)
- moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece}));
- if (startRanks.includes(x))
- {
- if (this.board[x+2*shiftX][y] == V.EMPTY)
- {
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
+ );
+ if (startRanks.includes(x)) {
+ if (this.board[x + 2 * shiftX][y] == V.EMPTY) {
// Two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
- if (x==startRanks[0] && this.board[x+3*shiftX][y] == V.EMPTY)
- {
+ moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
+ if (x == startRanks[0] && this.board[x + 3 * shiftX][y] == V.EMPTY) {
// Three squares jump
- moves.push(this.getBasicMove([x,y], [x+3*shiftX,y]));
+ moves.push(this.getBasicMove([x, y], [x + 3 * shiftX, y]));
}
}
}
}
// Captures
- for (let shiftY of [-1,1])
- {
- if (y + shiftY >= 0 && y + shiftY < sizeY
- && this.board[x+shiftX][y+shiftY] != V.EMPTY
- && this.canTake([x,y], [x+shiftX,y+shiftY]))
- {
- for (let piece of finalPieces)
- {
- moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
- {c:color,p:piece}));
+ for (let shiftY of [-1, 1]) {
+ if (
+ y + shiftY >= 0 &&
+ y + shiftY < sizeY &&
+ this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+ this.canTake([x, y], [x + shiftX, y + shiftY])
+ ) {
+ for (let piece of finalPieces) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+ c: color,
+ p: piece
+ })
+ );
}
}
}
// En passant
const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep-1];
- if (!!epSquare)
- {
- for (let epsq of epSquare)
- {
+ const epSquare = this.epSquares[Lep - 1];
+ if (epSquare) {
+ for (let epsq of epSquare) {
// TODO: some redundant checks
- if (epsq.x == x+shiftX && Math.abs(epsq.y - y) == 1)
- {
- var enpassantMove = this.getBasicMove([x,y], [epsq.x,epsq.y]);
+ if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
+ var enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
// WARNING: the captured pawn may be diagonally behind us,
// if it's a 3-squares jump and we take on 1st passing square
- const px = (this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX);
+ const px = this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX;
enpassantMove.vanish.push({
x: px,
y: epsq.y,
- p: 'p',
- c: this.getColor(px,epsq.y)
+ p: "p",
+ c: this.getColor(px, epsq.y)
});
moves.push(enpassantMove);
}
// TODO: different castle?
- getPotentialMarshallMoves(sq)
- {
+ getPotentialMarshallMoves(sq) {
return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
- this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep"));
+ this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
+ );
}
- getPotentialCardinalMoves(sq)
- {
+ getPotentialCardinalMoves(sq) {
return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
- this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep"));
+ this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
+ );
}
- isAttacked(sq, colors)
- {
- return super.isAttacked(sq, colors)
- || this.isAttackedByMarshall(sq, colors)
- || this.isAttackedByCardinal(sq, colors);
+ isAttacked(sq, colors) {
+ return (
+ super.isAttacked(sq, colors) ||
+ this.isAttackedByMarshall(sq, colors) ||
+ this.isAttackedByCardinal(sq, colors)
+ );
}
- isAttackedByMarshall(sq, colors)
- {
- return this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.ROOK])
- || this.isAttackedBySlideNJump(
- sq, colors, V.MARSHALL, V.steps[V.KNIGHT], "oneStep");
+ isAttackedByMarshall(sq, colors) {
+ return (
+ this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.ROOK]) ||
+ this.isAttackedBySlideNJump(
+ sq,
+ colors,
+ V.MARSHALL,
+ V.steps[V.KNIGHT],
+ "oneStep"
+ )
+ );
}
- isAttackedByCardinal(sq, colors)
- {
- return this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.BISHOP])
- || this.isAttackedBySlideNJump(
- sq, colors, V.CARDINAL, V.steps[V.KNIGHT], "oneStep");
+ isAttackedByCardinal(sq, colors) {
+ return (
+ this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.BISHOP]) ||
+ this.isAttackedBySlideNJump(
+ sq,
+ colors,
+ V.CARDINAL,
+ V.steps[V.KNIGHT],
+ "oneStep"
+ )
+ );
}
- updateVariables(move)
- {
+ updateVariables(move) {
super.updateVariables(move);
- if (move.vanish.length == 2 && move.appear.length == 1)
- {
+ if (move.vanish.length == 2 && move.appear.length == 1) {
// Capture: update this.captured
this.captured[move.vanish[1].c][move.vanish[1].p]++;
}
- if (move.vanish[0].p != move.appear[0].p)
- {
+ if (move.vanish[0].p != move.appear[0].p) {
// Promotion: update this.captured
this.captured[move.vanish[0].c][move.appear[0].p]--;
}
}
- unupdateVariables(move)
- {
+ unupdateVariables(move) {
super.unupdateVariables(move);
if (move.vanish.length == 2 && move.appear.length == 1)
this.captured[move.vanish[1].c][move.vanish[1].p]--;
this.captured[move.vanish[0].c][move.appear[0].p]++;
}
- static get VALUES()
- {
+ static get VALUES() {
return Object.assign(
ChessRules.VALUES,
- {'c': 5, 'm': 7} //experimental
+ { c: 5, m: 7 } //experimental
);
}
- static get SEARCH_DEPTH() { return 2; }
+ static get SEARCH_DEPTH() {
+ return 2;
+ }
// TODO: this function could be generalized and shared better (how ?!...)
- static GenRandInitFen()
- {
- let pieces = { "w": new Array(10), "b": new Array(10) };
+ static GenRandInitFen() {
+ let pieces = { w: new Array(10), b: new Array(10) };
// Shuffle pieces on first and last rank
- for (let c of ["w","b"])
- {
+ for (let c of ["w", "b"]) {
let positions = ArrayFun.range(10);
// Get random squares for bishops
let randIndex_tmp = 2 * randInt(5) + 1;
let bishop2Pos = positions[randIndex_tmp];
// Remove chosen squares
- positions.splice(Math.max(randIndex,randIndex_tmp), 1);
- positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+ positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+ positions.splice(Math.min(randIndex, randIndex_tmp), 1);
// Get random squares for knights
randIndex = randInt(8);
let rook2Pos = positions[2];
// Finally put the shuffled pieces in the board array
- pieces[c][rook1Pos] = 'r';
- pieces[c][knight1Pos] = 'n';
- pieces[c][bishop1Pos] = 'b';
- pieces[c][queenPos] = 'q';
- pieces[c][marshallPos] = 'm';
- pieces[c][cardinalPos] = 'c';
- pieces[c][kingPos] = 'k';
- pieces[c][bishop2Pos] = 'b';
- pieces[c][knight2Pos] = 'n';
- pieces[c][rook2Pos] = 'r';
+ pieces[c][rook1Pos] = "r";
+ pieces[c][knight1Pos] = "n";
+ pieces[c][bishop1Pos] = "b";
+ pieces[c][queenPos] = "q";
+ pieces[c][marshallPos] = "m";
+ pieces[c][cardinalPos] = "c";
+ pieces[c][kingPos] = "k";
+ pieces[c][bishop2Pos] = "b";
+ pieces[c][knight2Pos] = "n";
+ pieces[c][rook2Pos] = "r";
}
- return pieces["b"].join("") +
+ return (
+ pieces["b"].join("") +
"/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
- " w 0 1111 - 00000000000000";
+ " w 0 1111 - 00000000000000"
+ );
}
-}
+};
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class LosersRules extends ChessRules
-{
- static get HasFlags() { return false; }
+export const VariantRules = class LosersRules extends ChessRules {
+ static get HasFlags() {
+ return false;
+ }
- getPotentialPawnMoves([x,y])
- {
- let moves = super.getPotentialPawnMoves([x,y]);
+ getPotentialPawnMoves([x, y]) {
+ let moves = super.getPotentialPawnMoves([x, y]);
// Complete with promotion(s) into king, if possible
const color = this.turn;
- const shift = (color == "w" ? -1 : 1);
- const lastRank = (color == "w" ? 0 : V.size.x-1);
- if (x+shift == lastRank)
- {
+ const shift = color == "w" ? -1 : 1;
+ const lastRank = color == "w" ? 0 : V.size.x - 1;
+ if (x + shift == lastRank) {
// Normal move
- if (this.board[x+shift][y] == V.EMPTY)
- moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:V.KING}));
+ if (this.board[x + shift][y] == V.EMPTY)
+ moves.push(
+ this.getBasicMove([x, y], [x + shift, y], { c: color, p: V.KING })
+ );
// Captures
- if (y>0 && this.canTake([x,y], [x+shift,y-1])
- && this.board[x+shift][y-1] != V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:V.KING}));
+ if (
+ y > 0 &&
+ this.canTake([x, y], [x + shift, y - 1]) &&
+ this.board[x + shift][y - 1] != V.EMPTY
+ ) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shift, y - 1], { c: color, p: V.KING })
+ );
}
- if (y<V.size.y-1 && this.canTake([x,y], [x+shift,y+1])
- && this.board[x+shift][y+1] != V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:V.KING}));
+ if (
+ y < V.size.y - 1 &&
+ this.canTake([x, y], [x + shift, y + 1]) &&
+ this.board[x + shift][y + 1] != V.EMPTY
+ ) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shift, y + 1], { c: color, p: V.KING })
+ );
}
}
return moves;
}
- getPotentialKingMoves(sq)
- {
+ getPotentialKingMoves(sq) {
// No castle:
- return this.getSlideNJumpMoves(sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+ return this.getSlideNJumpMoves(
+ sq,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
}
// Stop at the first capture found (if any)
- atLeastOneCapture()
- {
+ atLeastOneCapture() {
const color = this.turn;
const oppCol = V.GetOppCol(color);
- 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 && this.getColor(i,j) != oppCol)
- {
- const moves = this.getPotentialMovesFrom([i,j]);
- if (moves.length > 0)
- {
- for (let k=0; k<moves.length; k++)
- {
- if (moves[k].vanish.length==2 && this.filterValid([moves[k]]).length > 0)
+ 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 && this.getColor(i, j) != oppCol) {
+ const moves = this.getPotentialMovesFrom([i, j]);
+ if (moves.length > 0) {
+ for (let k = 0; k < moves.length; k++) {
+ if (
+ moves[k].vanish.length == 2 &&
+ this.filterValid([moves[k]]).length > 0
+ )
return true;
}
}
}
// Trim all non-capturing moves
- static KeepCaptures(moves)
- {
- return moves.filter(m => { return m.vanish.length == 2; });
+ static KeepCaptures(moves) {
+ return moves.filter(m => {
+ return m.vanish.length == 2;
+ });
}
- getPossibleMovesFrom(sq)
- {
- let moves = this.filterValid( this.getPotentialMovesFrom(sq) );
+ getPossibleMovesFrom(sq) {
+ let moves = this.filterValid(this.getPotentialMovesFrom(sq));
// This is called from interface: we need to know if a capture is possible
- if (this.atLeastOneCapture())
- moves = V.KeepCaptures(moves);
+ if (this.atLeastOneCapture()) moves = V.KeepCaptures(moves);
return moves;
}
- getAllValidMoves()
- {
+ getAllValidMoves() {
let moves = super.getAllValidMoves();
- if (moves.some(m => { return m.vanish.length == 2; }))
+ if (
+ moves.some(m => {
+ return m.vanish.length == 2;
+ })
+ )
moves = V.KeepCaptures(moves);
return moves;
}
- underCheck(color)
- {
+ underCheck() {
return false; //No notion of check
}
- getCheckSquares(move)
- {
+ getCheckSquares() {
return [];
}
// No variables update because no royal king + no castling
- updateVariables(move) { }
- unupdateVariables(move) { }
+ updateVariables() {}
+ unupdateVariables() {}
- getCurrentScore()
- {
- if (this.atLeastOneMove()) // game not over
+ getCurrentScore() {
+ if (this.atLeastOneMove())
+ // game not over
return "*";
// No valid move: the side who cannot move wins
- return (this.turn == "w" ? "1-0" : "0-1");
+ return this.turn == "w" ? "1-0" : "0-1";
}
- static get VALUES()
- {
+ static get VALUES() {
// Experimental...
return {
- 'p': 1,
- 'r': 7,
- 'n': 3,
- 'b': 3,
- 'q': 5,
- 'k': 5
+ p: 1,
+ r: 7,
+ n: 3,
+ b: 3,
+ q: 5,
+ k: 5
};
}
- static get SEARCH_DEPTH() { return 4; }
+ static get SEARCH_DEPTH() {
+ return 4;
+ }
- evalPosition()
- {
- return - super.evalPosition(); //better with less material
+ evalPosition() {
+ return -super.evalPosition(); //better with less material
}
- static GenRandInitFen()
- {
- let pieces = { "w": new Array(8), "b": new Array(8) };
+ static GenRandInitFen() {
+ let pieces = { w: new Array(8), b: new Array(8) };
// Shuffle pieces on first and last rank
- for (let c of ["w","b"])
- {
+ for (let c of ["w", "b"]) {
let positions = ArrayFun.range(8);
// Get random squares for bishops
let randIndex_tmp = 2 * randInt(4) + 1;
let bishop2Pos = positions[randIndex_tmp];
// Remove chosen squares
- positions.splice(Math.max(randIndex,randIndex_tmp), 1);
- positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+ positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+ positions.splice(Math.min(randIndex, randIndex_tmp), 1);
// Get random squares for knights
randIndex = randInt(6);
let rook2Pos = positions[1];
// Finally put the shuffled pieces in the board array
- pieces[c][rook1Pos] = 'r';
- pieces[c][knight1Pos] = 'n';
- pieces[c][bishop1Pos] = 'b';
- pieces[c][queenPos] = 'q';
- pieces[c][kingPos] = 'k';
- pieces[c][bishop2Pos] = 'b';
- pieces[c][knight2Pos] = 'n';
- pieces[c][rook2Pos] = 'r';
+ pieces[c][rook1Pos] = "r";
+ pieces[c][knight1Pos] = "n";
+ pieces[c][bishop1Pos] = "b";
+ pieces[c][queenPos] = "q";
+ pieces[c][kingPos] = "k";
+ pieces[c][bishop2Pos] = "b";
+ pieces[c][knight2Pos] = "n";
+ pieces[c][rook2Pos] = "r";
}
- return pieces["b"].join("") +
+ return (
+ pieces["b"].join("") +
"/pppppppp/8/8/8/8/PPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
- " w 0 -"; //en-passant allowed, but no flags
+ " w 0 -"
+ ); //en-passant allowed, but no flags
}
-}
+};
import { ChessRules, PiPo } from "@/base_rules";
-export const VariantRules = class MagneticRules extends ChessRules
-{
- static get HasEnpassant() { return false; }
+export const VariantRules = class MagneticRules extends ChessRules {
+ static get HasEnpassant() {
+ return false;
+ }
- getPotentialMovesFrom([x,y])
- {
- let standardMoves = super.getPotentialMovesFrom([x,y]);
+ getPotentialMovesFrom([x, y]) {
+ let standardMoves = super.getPotentialMovesFrom([x, y]);
let moves = [];
standardMoves.forEach(m => {
let newMove_s = this.applyMagneticLaws(m);
- if (newMove_s.length == 1)
- moves.push(newMove_s[0]);
- else //promotion
- moves = moves.concat(newMove_s);
+ if (newMove_s.length == 1) moves.push(newMove_s[0]);
+ //promotion
+ else moves = moves.concat(newMove_s);
});
return moves;
}
// Complete a move with magnetic actions
// TODO: job is done multiple times for (normal) promotions.
- applyMagneticLaws(move)
- {
- if (move.appear[0].p == V.KING && move.appear.length==1)
- return [move]; //kings are not charged
- const aIdx = (move.appear[0].p != V.KING ? 0 : 1); //if castling, rook is charged
- const [x,y] = [move.appear[aIdx].x, move.appear[aIdx].y];
+ applyMagneticLaws(move) {
+ if (move.appear[0].p == V.KING && move.appear.length == 1) return [move]; //kings are not charged
+ const aIdx = move.appear[0].p != V.KING ? 0 : 1; //if castling, rook is charged
+ const [x, y] = [move.appear[aIdx].x, move.appear[aIdx].y];
const color = this.turn;
- const lastRank = (color=="w" ? 0 : 7);
+ const lastRank = color == "w" ? 0 : 7;
const standardMove = JSON.parse(JSON.stringify(move));
this.play(standardMove);
- for (let step of [[-1,0],[1,0],[0,-1],[0,1]])
- {
- let [i,j] = [x+step[0],y+step[1]];
- while (V.OnBoard(i,j))
- {
- if (this.board[i][j] != V.EMPTY)
- {
+ for (let step of [
+ [-1, 0],
+ [1, 0],
+ [0, -1],
+ [0, 1]
+ ]) {
+ let [i, j] = [x + step[0], y + step[1]];
+ while (V.OnBoard(i, j)) {
+ if (this.board[i][j] != V.EMPTY) {
// Found something. Same color or not?
- if (this.getColor(i,j) != color)
- {
+ if (this.getColor(i, j) != color) {
// Attraction
- if ((Math.abs(i-x)>=2 || Math.abs(j-y)>=2) && this.getPiece(i,j) != V.KING)
- {
+ if (
+ (Math.abs(i - x) >= 2 || Math.abs(j - y) >= 2) &&
+ this.getPiece(i, j) != V.KING
+ ) {
move.vanish.push(
new PiPo({
- p:this.getPiece(i,j),
- c:this.getColor(i,j),
- x:i,
- y:j
+ p: this.getPiece(i, j),
+ c: this.getColor(i, j),
+ x: i,
+ y: j
})
);
move.appear.push(
new PiPo({
- p:this.getPiece(i,j),
- c:this.getColor(i,j),
- x:x+step[0],
- y:y+step[1]
+ p: this.getPiece(i, j),
+ c: this.getColor(i, j),
+ x: x + step[0],
+ y: y + step[1]
})
);
}
- }
- else
- {
+ } else {
// Repulsion
- if (this.getPiece(i,j) != V.KING)
- {
+ if (this.getPiece(i, j) != V.KING) {
// Push it until we meet an obstacle or edge of the board
- let [ii,jj] = [i+step[0],j+step[1]];
- while (V.OnBoard(ii,jj))
- {
- if (this.board[ii][jj] != V.EMPTY)
- break;
+ let [ii, jj] = [i + step[0], j + step[1]];
+ while (V.OnBoard(ii, jj)) {
+ if (this.board[ii][jj] != V.EMPTY) break;
ii += step[0];
jj += step[1];
}
ii -= step[0];
jj -= step[1];
- if (Math.abs(ii-i)>=1 || Math.abs(jj-j)>=1)
- {
+ if (Math.abs(ii - i) >= 1 || Math.abs(jj - j) >= 1) {
move.vanish.push(
new PiPo({
- p:this.getPiece(i,j),
- c:this.getColor(i,j),
- x:i,
- y:j
+ p: this.getPiece(i, j),
+ c: this.getColor(i, j),
+ x: i,
+ y: j
})
);
move.appear.push(
new PiPo({
- p:this.getPiece(i,j),
- c:this.getColor(i,j),
- x:ii,
- y:jj
+ p: this.getPiece(i, j),
+ c: this.getColor(i, j),
+ x: ii,
+ y: jj
})
);
}
this.undo(standardMove);
let moves = [];
// Scan move for pawn (max 1) on 8th rank
- for (let i=1; i<move.appear.length; i++)
- {
- if (move.appear[i].p==V.PAWN && move.appear[i].c==color
- && move.appear[i].x==lastRank)
- {
+ for (let i = 1; i < move.appear.length; i++) {
+ if (
+ move.appear[i].p == V.PAWN &&
+ move.appear[i].c == color &&
+ move.appear[i].x == lastRank
+ ) {
move.appear[i].p = V.ROOK;
moves.push(move);
- for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN])
- {
+ for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN]) {
let cmove = JSON.parse(JSON.stringify(move));
cmove.appear[i].p = piece;
moves.push(cmove);
break;
}
}
- if (moves.length == 0) //no pawn on 8th rank
+ if (moves.length == 0)
+ //no pawn on 8th rank
moves.push(move);
return moves;
}
- atLeastOneMove()
- {
- if (this.kingPos[this.turn][0] < 0)
- return false;
+ atLeastOneMove() {
+ if (this.kingPos[this.turn][0] < 0) return false;
return true; //TODO: is it right?
}
- underCheck(color)
- {
+ underCheck() {
return false; //there is no check
}
- getCheckSquares(move)
- {
+ getCheckSquares() {
return [];
}
- updateVariables(move)
- {
+ updateVariables(move) {
super.updateVariables(move);
const c = move.vanish[0].c;
- if (move.vanish.length >= 2 && move.vanish[1].p == V.KING)
- {
+ if (move.vanish.length >= 2 && move.vanish[1].p == V.KING) {
// We took opponent king !
const oppCol = V.GetOppCol(c);
- this.kingPos[oppCol] = [-1,-1];
- this.castleFlags[oppCol] = [false,false];
+ this.kingPos[oppCol] = [-1, -1];
+ this.castleFlags[oppCol] = [false, false];
}
// Did we magnetically move our (init) rooks or opponents' ones ?
- const firstRank = (c == "w" ? 7 : 0);
+ const firstRank = c == "w" ? 7 : 0;
const oppFirstRank = 7 - firstRank;
const oppCol = V.GetOppCol(c);
move.vanish.forEach(psq => {
if (psq.x == firstRank && this.INIT_COL_ROOK[c].includes(psq.y))
- this.castleFlags[c][psq.y==this.INIT_COL_ROOK[c][0] ? 0 : 1] = false;
- else if (psq.x == oppFirstRank && this.INIT_COL_ROOK[oppCol].includes(psq.y))
- this.castleFlags[oppCol][psq.y==this.INIT_COL_ROOK[oppCol][0] ? 0 : 1] = false;
+ this.castleFlags[c][psq.y == this.INIT_COL_ROOK[c][0] ? 0 : 1] = false;
+ else if (
+ psq.x == oppFirstRank &&
+ this.INIT_COL_ROOK[oppCol].includes(psq.y)
+ )
+ this.castleFlags[oppCol][
+ psq.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1
+ ] = false;
});
}
- unupdateVariables(move)
- {
+ unupdateVariables(move) {
super.unupdateVariables(move);
const c = move.vanish[0].c;
const oppCol = V.GetOppCol(c);
- if (this.kingPos[oppCol][0] < 0)
- {
+ if (this.kingPos[oppCol][0] < 0) {
// Last move took opponent's king
- for (let psq of move.vanish)
- {
- if (psq.p == 'k')
- {
+ for (let psq of move.vanish) {
+ if (psq.p == "k") {
this.kingPos[oppCol] = [psq.x, psq.y];
break;
}
}
}
- getCurrentScore()
- {
+ getCurrentScore() {
const color = this.turn;
const kp = this.kingPos[color];
- if (kp[0] < 0) //king disappeared
- return (color == "w" ? "0-1" : "1-0");
- if (this.atLeastOneMove()) // game not over
+ if (kp[0] < 0)
+ //king disappeared
+ return color == "w" ? "0-1" : "1-0";
+ if (this.atLeastOneMove())
+ // game not over
return "*";
return "1/2"; //no moves but kings still there
}
- static get THRESHOLD_MATE()
- {
+ static get THRESHOLD_MATE() {
return 500; //checkmates evals may be slightly below 1000
}
-}
+};
import { ChessRules } from "@/base_rules";
import { randInt } from "@/utils/alea";
-export const VariantRules = class MarseilleRules extends ChessRules
-{
- static IsGoodEnpassant(enpassant)
- {
- if (enpassant != "-")
- {
+export const VariantRules = class MarseilleRules extends ChessRules {
+ static IsGoodEnpassant(enpassant) {
+ if (enpassant != "-") {
const squares = enpassant.split(",");
- if (squares.length > 2)
- return false;
- for (let sq of squares)
- {
+ if (squares.length > 2) return false;
+ for (let sq of squares) {
const ep = V.SquareToCoords(sq);
- if (isNaN(ep.x) || !V.OnBoard(ep))
- return false;
+ if (isNaN(ep.x) || !V.OnBoard(ep)) return false;
}
}
return true;
}
- getTurnFen()
- {
+ getTurnFen() {
return this.turn + this.subTurn;
}
// There may be 2 enPassant squares (if 2 pawns jump 2 squares in same turn)
- getEnpassantFen()
- {
+ getEnpassantFen() {
const L = this.epSquares.length;
- if (this.epSquares[L-1].every(epsq => epsq === undefined))
- return "-"; //no en-passant
+ if (this.epSquares[L - 1].every(epsq => epsq === undefined)) return "-"; //no en-passant
let res = "";
- this.epSquares[L-1].forEach(epsq => {
- if (!!epsq)
- res += V.CoordsToSquare(epsq) + ",";
+ this.epSquares[L - 1].forEach(epsq => {
+ if (epsq) res += V.CoordsToSquare(epsq) + ",";
});
- return res.slice(0,-1); //remove last comma
+ return res.slice(0, -1); //remove last comma
}
- setOtherVariables(fen)
- {
+ setOtherVariables(fen) {
const parsedFen = V.ParseFen(fen);
this.setFlags(parsedFen.flags);
- if (parsedFen.enpassant == "-")
- this.epSquares = [ [undefined] ];
- else
- {
+ if (parsedFen.enpassant == "-") this.epSquares = [[undefined]];
+ else {
let res = [];
const squares = parsedFen.enpassant.split(",");
- for (let sq of squares)
- res.push(V.SquareToCoords(sq));
- this.epSquares = [ res ];
+ for (let sq of squares) res.push(V.SquareToCoords(sq));
+ this.epSquares = [res];
}
this.scanKingsRooks(fen);
// Extract subTurn from turn indicator: "w" (first move), or
// "w1" or "w2" white subturn 1 or 2, and same for black
const fullTurn = V.ParseFen(fen).turn;
this.turn = fullTurn[0];
- this.subTurn = (fullTurn[1] || 0); //"w0" = special code for first move in game
+ this.subTurn = fullTurn[1] || 0; //"w0" = special code for first move in game
}
- getPotentialPawnMoves([x,y])
- {
+ getPotentialPawnMoves([x, y]) {
const color = this.turn;
let moves = [];
- const [sizeX,sizeY] = [V.size.x,V.size.y];
- const shiftX = (color == "w" ? -1 : 1);
- const firstRank = (color == 'w' ? sizeX-1 : 0);
- const startRank = (color == "w" ? sizeX-2 : 1);
- const lastRank = (color == "w" ? 0 : sizeX-1);
- const finalPieces = x + shiftX == lastRank
- ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
- : [V.PAWN];
+ const [sizeX, sizeY] = [V.size.x, V.size.y];
+ const shiftX = color == "w" ? -1 : 1;
+ const firstRank = color == "w" ? sizeX - 1 : 0;
+ const startRank = color == "w" ? sizeX - 2 : 1;
+ const lastRank = color == "w" ? 0 : sizeX - 1;
+ const finalPieces =
+ x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN];
// One square forward
- if (this.board[x+shiftX][y] == V.EMPTY)
- {
- for (let piece of finalPieces)
- {
- moves.push(this.getBasicMove([x,y], [x+shiftX,y],
- {c:color,p:piece}));
+ if (this.board[x + shiftX][y] == V.EMPTY) {
+ for (let piece of finalPieces) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
+ );
}
// Next condition because pawns on 1st rank can generally jump
- if ([startRank,firstRank].includes(x)
- && this.board[x+2*shiftX][y] == V.EMPTY)
- {
+ if (
+ [startRank, firstRank].includes(x) &&
+ this.board[x + 2 * shiftX][y] == V.EMPTY
+ ) {
// Two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
+ moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
}
}
// Captures
- for (let shiftY of [-1,1])
- {
- if (y + shiftY >= 0 && y + shiftY < sizeY
- && this.board[x+shiftX][y+shiftY] != V.EMPTY
- && this.canTake([x,y], [x+shiftX,y+shiftY]))
- {
- for (let piece of finalPieces)
- {
- moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
- {c:color,p:piece}));
+ for (let shiftY of [-1, 1]) {
+ if (
+ y + shiftY >= 0 &&
+ y + shiftY < sizeY &&
+ this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+ this.canTake([x, y], [x + shiftX, y + shiftY])
+ ) {
+ for (let piece of finalPieces) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+ c: color,
+ p: piece
+ })
+ );
}
}
}
// OK on subturn 2 only if enPassant was played at subturn 1
// (and if there are two e.p. squares available).
const Lep = this.epSquares.length;
- const epSquares = this.epSquares[Lep-1]; //always at least one element
+ const epSquares = this.epSquares[Lep - 1]; //always at least one element
let epSqs = [];
epSquares.forEach(sq => {
- if (!!sq)
- epSqs.push(sq);
+ if (sq) epSqs.push(sq);
});
- if (epSqs.length == 0)
- return moves;
+ if (epSqs.length == 0) return moves;
const oppCol = V.GetOppCol(color);
- for (let sq of epSqs)
- {
- if (this.subTurn == 1 || (epSqs.length == 2 &&
- // Was this en-passant capture already played at subturn 1 ?
- // (Or maybe the opponent filled the en-passant square with a piece)
- this.board[epSqs[0].x][epSqs[0].y] != V.EMPTY))
- {
- if (sq.x == x+shiftX && Math.abs(sq.y - y) == 1
+ for (let sq of epSqs) {
+ if (
+ this.subTurn == 1 ||
+ (epSqs.length == 2 &&
+ // Was this en-passant capture already played at subturn 1 ?
+ // (Or maybe the opponent filled the en-passant square with a piece)
+ this.board[epSqs[0].x][epSqs[0].y] != V.EMPTY)
+ ) {
+ if (
+ sq.x == x + shiftX &&
+ Math.abs(sq.y - y) == 1 &&
// Add condition "enemy pawn must be present"
- && this.getPiece(x,sq.y) == V.PAWN && this.getColor(x,sq.y) == oppCol)
- {
- let epMove = this.getBasicMove([x,y], [sq.x,sq.y]);
+ this.getPiece(x, sq.y) == V.PAWN &&
+ this.getColor(x, sq.y) == oppCol
+ ) {
+ let epMove = this.getBasicMove([x, y], [sq.x, sq.y]);
epMove.vanish.push({
x: x,
y: sq.y,
- p: 'p',
+ p: "p",
c: oppCol
});
moves.push(epMove);
return moves;
}
- play(move)
- {
+ play(move) {
move.flags = JSON.stringify(this.aggregateFlags());
move.turn = this.turn + this.subTurn;
V.PlayOnBoard(this.board, move);
const epSq = this.getEpSquare(move);
- if (this.subTurn == 0) //first move in game
- {
+ if (this.subTurn == 0) {
+ //first move in game
this.turn = "b";
this.subTurn = 1;
this.epSquares.push([epSq]);
}
// Does this move give check on subturn 1? If yes, skip subturn 2
- else if (this.subTurn==1 && this.underCheck(V.GetOppCol(this.turn)))
- {
+ else if (this.subTurn == 1 && this.underCheck(V.GetOppCol(this.turn))) {
this.turn = V.GetOppCol(this.turn);
this.epSquares.push([epSq]);
move.checkOnSubturn1 = true;
- }
- else
- {
- if (this.subTurn == 2)
- {
+ } else {
+ if (this.subTurn == 2) {
this.turn = V.GetOppCol(this.turn);
- let lastEpsq = this.epSquares[this.epSquares.length-1];
+ let lastEpsq = this.epSquares[this.epSquares.length - 1];
lastEpsq.push(epSq);
- }
- else
- this.epSquares.push([epSq]);
+ } else this.epSquares.push([epSq]);
this.subTurn = 3 - this.subTurn;
}
this.updateVariables(move);
}
- undo(move)
- {
+ undo(move) {
this.disaggregateFlags(JSON.parse(move.flags));
V.UndoOnBoard(this.board, move);
- if (move.turn[1] == '0' || move.checkOnSubturn1 || this.subTurn == 2)
+ if (move.turn[1] == "0" || move.checkOnSubturn1 || this.subTurn == 2)
this.epSquares.pop();
- else //this.subTurn == 1
- {
- let lastEpsq = this.epSquares[this.epSquares.length-1];
+ //this.subTurn == 1
+ else {
+ let lastEpsq = this.epSquares[this.epSquares.length - 1];
lastEpsq.pop();
}
this.turn = move.turn[0];
// NOTE: GenRandInitFen() is OK,
// since at first move turn indicator is just "w"
- static get VALUES()
- {
+ static get VALUES() {
return {
- 'p': 1,
- 'r': 5,
- 'n': 3,
- 'b': 3,
- 'q': 7, //slightly less than in orthodox game
- 'k': 1000
+ p: 1,
+ r: 5,
+ n: 3,
+ b: 3,
+ q: 7, //slightly less than in orthodox game
+ k: 1000
};
}
// No alpha-beta here, just adapted min-max at depth 2(+1)
- getComputerMove()
- {
- if (this.subTurn == 2)
- return null; //TODO: imperfect interface setup
+ getComputerMove() {
+ if (this.subTurn == 2) return null; //TODO: imperfect interface setup
const maxeval = V.INFINITY;
const color = this.turn;
// Search best (half) move for opponent turn
const getBestMoveEval = () => {
- const turnBefore = this.turn + this.subTurn;
let score = this.getCurrentScore();
- if (score != "*")
- {
- if (score == "1/2")
- return 0;
+ if (score != "*") {
+ if (score == "1/2") return 0;
return maxeval * (score == "1-0" ? 1 : -1);
}
let moves = this.getAllValidMoves();
- let res = (oppCol == "w" ? -maxeval : maxeval);
- for (let m of moves)
- {
+ let res = oppCol == "w" ? -maxeval : maxeval;
+ for (let m of moves) {
this.play(m);
score = this.getCurrentScore();
// Now turn is oppCol,2 if m doesn't give check
// Otherwise it's color,1. In both cases the next test makes sense
- if (score != "*")
- {
+ if (score != "*") {
if (score == "1/2")
- res = (oppCol == "w" ? Math.max(res, 0) : Math.min(res, 0));
- else
- {
+ res = oppCol == "w" ? Math.max(res, 0) : Math.min(res, 0);
+ else {
// Found a mate
this.undo(m);
return maxeval * (score == "1-0" ? 1 : -1);
}
}
const evalPos = this.evalPosition();
- res = (oppCol == "w" ? Math.max(res, evalPos) : Math.min(res, evalPos));
+ res = oppCol == "w" ? Math.max(res, evalPos) : Math.min(res, evalPos);
this.undo(m);
}
return res;
let moves11 = this.getAllValidMoves();
let doubleMoves = [];
// Rank moves using a min-max at depth 2
- for (let i=0; i<moves11.length; i++)
- {
+ for (let i = 0; i < moves11.length; i++) {
this.play(moves11[i]);
- if (this.turn != color)
- {
+ if (this.turn != color) {
// We gave check with last move: search the best opponent move
- doubleMoves.push({moves:[moves11[i]], eval:getBestMoveEval()});
- }
- else
- {
+ doubleMoves.push({ moves: [moves11[i]], eval: getBestMoveEval() });
+ } else {
let moves12 = this.getAllValidMoves();
- for (let j=0; j<moves12.length; j++)
- {
+ for (let j = 0; j < moves12.length; j++) {
this.play(moves12[j]);
doubleMoves.push({
- moves:[moves11[i],moves12[j]],
- eval:getBestMoveEval()});
+ moves: [moves11[i], moves12[j]],
+ eval: getBestMoveEval()
+ });
this.undo(moves12[j]);
}
}
this.undo(moves11[i]);
}
- doubleMoves.sort( (a,b) => {
- return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
+ doubleMoves.sort((a, b) => {
+ return (color == "w" ? 1 : -1) * (b.eval - a.eval);
+ });
let candidates = [0]; //indices of candidates moves
- for (let i=1;
- i<doubleMoves.length && doubleMoves[i].eval == doubleMoves[0].eval;
- i++)
- {
+ for (
+ let i = 1;
+ i < doubleMoves.length && doubleMoves[i].eval == doubleMoves[0].eval;
+ i++
+ ) {
candidates.push(i);
}
const selected = doubleMoves[randInt(candidates.length)].moves;
- if (selected.length == 1)
- return selected[0];
+ if (selected.length == 1) return selected[0];
return selected;
}
-}
+};
import { randInt } from "@/utils/alea";
import { ArrayFun } from "@/utils/array";
-export const VariantRules = class UpsidedownRules extends ChessRules
-{
- static get HasFlags() { return false; }
+export const VariantRules = class UpsidedownRules extends ChessRules {
+ static get HasFlags() {
+ return false;
+ }
- static get HasEnpassant() { return false; }
+ static get HasEnpassant() {
+ return false;
+ }
- getPotentialKingMoves(sq)
- {
+ getPotentialKingMoves(sq) {
// No castle
- return this.getSlideNJumpMoves(sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+ return this.getSlideNJumpMoves(
+ sq,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
}
- static GenRandInitFen()
- {
- let pieces = { "w": new Array(8), "b": new Array(8) };
- for (let c of ["w","b"])
- {
+ static GenRandInitFen() {
+ let pieces = { w: new Array(8), b: new Array(8) };
+ for (let c of ["w", "b"]) {
let positions = ArrayFun.range(8);
let randIndex = randInt(8);
// At least a knight must be next to the king:
let knight1Pos = undefined;
- if (kingPos == 0)
- knight1Pos = 1;
- else if (kingPos == V.size.y-1)
- knight1Pos = V.size.y-2;
- else
- knight1Pos = kingPos + (Math.random() < 0.5 ? 1 : -1);
+ if (kingPos == 0) knight1Pos = 1;
+ else if (kingPos == V.size.y - 1) knight1Pos = V.size.y - 2;
+ else knight1Pos = kingPos + (Math.random() < 0.5 ? 1 : -1);
// Search for knight1Pos index in positions and remove it
const knight1Index = positions.indexOf(knight1Pos);
positions.splice(knight1Index, 1);
const bishop1Pos = positions[randIndex];
let randIndex_tmp = 2 * randInt(3) + 1;
const bishop2Pos = positions[randIndex_tmp];
- positions.splice(Math.max(randIndex,randIndex_tmp), 1);
- positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+ positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+ positions.splice(Math.min(randIndex, randIndex_tmp), 1);
randIndex = randInt(4);
const knight2Pos = positions[randIndex];
const rook1Pos = positions[0];
const rook2Pos = positions[1];
- pieces[c][rook1Pos] = 'r';
- pieces[c][knight1Pos] = 'n';
- pieces[c][bishop1Pos] = 'b';
- pieces[c][queenPos] = 'q';
- pieces[c][kingPos] = 'k';
- pieces[c][bishop2Pos] = 'b';
- pieces[c][knight2Pos] = 'n';
- pieces[c][rook2Pos] = 'r';
+ pieces[c][rook1Pos] = "r";
+ pieces[c][knight1Pos] = "n";
+ pieces[c][bishop1Pos] = "b";
+ pieces[c][queenPos] = "q";
+ pieces[c][kingPos] = "k";
+ pieces[c][bishop2Pos] = "b";
+ pieces[c][knight2Pos] = "n";
+ pieces[c][rook2Pos] = "r";
}
- return pieces["w"].join("").toUpperCase() +
+ return (
+ pieces["w"].join("").toUpperCase() +
"/PPPPPPPP/8/8/8/8/pppppppp/" +
pieces["b"].join("") +
- " w 0"; //no castle, no en-passant
+ " w 0"
+ ); //no castle, no en-passant
}
-}
+};
import { ArrayFun } from "@/utils/array";
import { sample, randInt } from "@/utils/alea";
-export const VariantRules = class WildebeestRules extends ChessRules
-{
- static getPpath(b)
- {
- return ([V.CAMEL,V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b;
+export const VariantRules = class WildebeestRules extends ChessRules {
+ static getPpath(b) {
+ return ([V.CAMEL, V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b;
}
- static get size() { return {x:10,y:11}; }
+ static get size() {
+ return { x: 10, y: 11 };
+ }
- static get CAMEL() { return 'c'; }
- static get WILDEBEEST() { return 'w'; }
+ static get CAMEL() {
+ return "c";
+ }
+ static get WILDEBEEST() {
+ return "w";
+ }
- static get PIECES()
- {
- return ChessRules.PIECES.concat([V.CAMEL,V.WILDEBEEST]);
+ static get PIECES() {
+ return ChessRules.PIECES.concat([V.CAMEL, V.WILDEBEEST]);
}
- static get steps()
- {
+ static get steps() {
return Object.assign(
ChessRules.steps, //add camel moves:
- {'c': [ [-3,-1],[-3,1],[-1,-3],[-1,3],[1,-3],[1,3],[3,-1],[3,1] ]}
+ {
+ c: [
+ [-3, -1],
+ [-3, 1],
+ [-1, -3],
+ [-1, 3],
+ [1, -3],
+ [1, 3],
+ [3, -1],
+ [3, 1]
+ ]
+ }
);
}
- static IsGoodEnpassant(enpassant)
- {
- if (enpassant != "-")
- {
+ static IsGoodEnpassant(enpassant) {
+ if (enpassant != "-") {
const squares = enpassant.split(",");
- if (squares.length > 2)
- return false;
- for (let sq of squares)
- {
+ if (squares.length > 2) return false;
+ for (let sq of squares) {
const ep = V.SquareToCoords(sq);
- if (isNaN(ep.x) || !V.OnBoard(ep))
- return false;
+ if (isNaN(ep.x) || !V.OnBoard(ep)) return false;
}
}
return true;
}
// There may be 2 enPassant squares (if pawn jump 3 squares)
- getEnpassantFen()
- {
+ getEnpassantFen() {
const L = this.epSquares.length;
- if (!this.epSquares[L-1])
- return "-"; //no en-passant
+ if (!this.epSquares[L - 1]) return "-"; //no en-passant
let res = "";
- this.epSquares[L-1].forEach(sq => {
+ this.epSquares[L - 1].forEach(sq => {
res += V.CoordsToSquare(sq) + ",";
});
- return res.slice(0,-1); //remove last comma
+ return res.slice(0, -1); //remove last comma
}
// En-passant after 2-sq or 3-sq jumps
- getEpSquare(moveOrSquare)
- {
- if (!moveOrSquare)
- return undefined;
- if (typeof moveOrSquare === "string")
- {
+ getEpSquare(moveOrSquare) {
+ if (!moveOrSquare) return undefined;
+ if (typeof moveOrSquare === "string") {
const square = moveOrSquare;
- if (square == "-")
- return undefined;
+ if (square == "-") return undefined;
let res = [];
square.split(",").forEach(sq => {
res.push(V.SquareToCoords(sq));
}
// Argument is a move:
const move = moveOrSquare;
- const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
- if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) >= 2)
- {
- const step = (ex-sx) / Math.abs(ex-sx);
- let res = [{
- x: sx + step,
- y: sy
- }];
- if (sx + 2*step != ex) //3-squares move
- {
+ const [sx, sy, ex] = [move.start.x, move.start.y, move.end.x];
+ if (this.getPiece(sx, sy) == V.PAWN && Math.abs(sx - ex) >= 2) {
+ const step = (ex - sx) / Math.abs(ex - sx);
+ let res = [
+ {
+ x: sx + step,
+ y: sy
+ }
+ ];
+ if (sx + 2 * step != ex) {
+ //3-squares move
res.push({
- x: sx + 2*step,
+ x: sx + 2 * step,
y: sy
});
}
return undefined; //default
}
- getPotentialMovesFrom([x,y])
- {
- switch (this.getPiece(x,y))
- {
+ getPotentialMovesFrom([x, y]) {
+ switch (this.getPiece(x, y)) {
case V.CAMEL:
- return this.getPotentialCamelMoves([x,y]);
+ return this.getPotentialCamelMoves([x, y]);
case V.WILDEBEEST:
- return this.getPotentialWildebeestMoves([x,y]);
+ return this.getPotentialWildebeestMoves([x, y]);
default:
- return super.getPotentialMovesFrom([x,y])
+ return super.getPotentialMovesFrom([x, y]);
}
}
// Pawns jump 2 or 3 squares, and promote to queen or wildebeest
- getPotentialPawnMoves([x,y])
- {
+ getPotentialPawnMoves([x, y]) {
const color = this.turn;
let moves = [];
- const [sizeX,sizeY] = [V.size.x,V.size.y];
- const shiftX = (color == "w" ? -1 : 1);
- const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]);
- const lastRank = (color == "w" ? 0 : sizeX-1);
- const finalPieces = x + shiftX == lastRank
- ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
- : [V.PAWN];
+ const [sizeX, sizeY] = [V.size.x, V.size.y];
+ const shiftX = color == "w" ? -1 : 1;
+ const startRanks = color == "w" ? [sizeX - 2, sizeX - 3] : [1, 2];
+ const lastRank = color == "w" ? 0 : sizeX - 1;
+ const finalPieces =
+ x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN];
- if (this.board[x+shiftX][y] == V.EMPTY)
- {
+ if (this.board[x + shiftX][y] == V.EMPTY) {
// One square forward
for (let piece of finalPieces)
- moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece}));
- if (startRanks.includes(x))
- {
- if (this.board[x+2*shiftX][y] == V.EMPTY)
- {
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
+ );
+ if (startRanks.includes(x)) {
+ if (this.board[x + 2 * shiftX][y] == V.EMPTY) {
// Two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
- if (x==startRanks[0] && this.board[x+3*shiftX][y] == V.EMPTY)
- {
+ moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
+ if (x == startRanks[0] && this.board[x + 3 * shiftX][y] == V.EMPTY) {
// Three squares jump
- moves.push(this.getBasicMove([x,y], [x+3*shiftX,y]));
+ moves.push(this.getBasicMove([x, y], [x + 3 * shiftX, y]));
}
}
}
}
// Captures
- for (let shiftY of [-1,1])
- {
- if (y + shiftY >= 0 && y + shiftY < sizeY
- && this.board[x+shiftX][y+shiftY] != V.EMPTY
- && this.canTake([x,y], [x+shiftX,y+shiftY]))
- {
- for (let piece of finalPieces)
- {
- moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
- {c:color,p:piece}));
+ for (let shiftY of [-1, 1]) {
+ if (
+ y + shiftY >= 0 &&
+ y + shiftY < sizeY &&
+ this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+ this.canTake([x, y], [x + shiftX, y + shiftY])
+ ) {
+ for (let piece of finalPieces) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+ c: color,
+ p: piece
+ })
+ );
}
}
}
// En passant
const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep-1];
- if (!!epSquare)
- {
- for (let epsq of epSquare)
- {
+ const epSquare = this.epSquares[Lep - 1];
+ if (epSquare) {
+ for (let epsq of epSquare) {
// TODO: some redundant checks
- if (epsq.x == x+shiftX && Math.abs(epsq.y - y) == 1)
- {
- var enpassantMove = this.getBasicMove([x,y], [epsq.x,epsq.y]);
+ if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
+ var enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
// WARNING: the captured pawn may be diagonally behind us,
// if it's a 3-squares jump and we take on 1st passing square
- const px = (this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX);
+ const px = this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX;
enpassantMove.vanish.push({
x: px,
y: epsq.y,
- p: 'p',
- c: this.getColor(px,epsq.y)
+ p: "p",
+ c: this.getColor(px, epsq.y)
});
moves.push(enpassantMove);
}
// TODO: wildebeest castle
- getPotentialCamelMoves(sq)
- {
+ getPotentialCamelMoves(sq) {
return this.getSlideNJumpMoves(sq, V.steps[V.CAMEL], "oneStep");
}
- getPotentialWildebeestMoves(sq)
- {
+ getPotentialWildebeestMoves(sq) {
return this.getSlideNJumpMoves(
- sq, V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
+ sq,
+ V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]),
+ "oneStep"
+ );
}
- isAttacked(sq, colors)
- {
- return super.isAttacked(sq, colors)
- || this.isAttackedByCamel(sq, colors)
- || this.isAttackedByWildebeest(sq, colors);
+ isAttacked(sq, colors) {
+ return (
+ super.isAttacked(sq, colors) ||
+ this.isAttackedByCamel(sq, colors) ||
+ this.isAttackedByWildebeest(sq, colors)
+ );
}
- isAttackedByCamel(sq, colors)
- {
- return this.isAttackedBySlideNJump(sq, colors,
- V.CAMEL, V.steps[V.CAMEL], "oneStep");
+ isAttackedByCamel(sq, colors) {
+ return this.isAttackedBySlideNJump(
+ sq,
+ colors,
+ V.CAMEL,
+ V.steps[V.CAMEL],
+ "oneStep"
+ );
}
- isAttackedByWildebeest(sq, colors)
- {
- return this.isAttackedBySlideNJump(sq, colors, V.WILDEBEEST,
- V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
+ isAttackedByWildebeest(sq, colors) {
+ return this.isAttackedBySlideNJump(
+ sq,
+ colors,
+ V.WILDEBEEST,
+ V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]),
+ "oneStep"
+ );
}
- getCurrentScore()
- {
- if (this.atLeastOneMove()) // game not over
+ getCurrentScore() {
+ if (this.atLeastOneMove())
+ // game not over
return "*";
// No valid move: game is lost (stalemate is a win)
- return (this.turn == "w" ? "0-1" : "1-0");
+ return this.turn == "w" ? "0-1" : "1-0";
}
static get VALUES() {
return Object.assign(
ChessRules.VALUES,
- {'c': 3, 'w': 7} //experimental
+ { c: 3, w: 7 } //experimental
);
}
- static get SEARCH_DEPTH() { return 2; }
+ static get SEARCH_DEPTH() {
+ return 2;
+ }
- static GenRandInitFen()
- {
- let pieces = { "w": new Array(10), "b": new Array(10) };
- for (let c of ["w","b"])
- {
+ static GenRandInitFen() {
+ let pieces = { w: new Array(10), b: new Array(10) };
+ for (let c of ["w", "b"]) {
let positions = ArrayFun.range(11);
// Get random squares for bishops + camels (different colors)
- let randIndexes = sample(ArrayFun.range(6), 2)
- .map(i => { return 2*i; });
+ let randIndexes = sample(ArrayFun.range(6), 2).map(i => {
+ return 2 * i;
+ });
let bishop1Pos = positions[randIndexes[0]];
let camel1Pos = positions[randIndexes[1]];
// The second bishop (camel) must be on a square of different color
- let randIndexes_tmp = sample(ArrayFun.range(5), 2)
- .map(i => { return 2*i+1; });
+ let randIndexes_tmp = sample(ArrayFun.range(5), 2).map(i => {
+ return 2 * i + 1;
+ });
let bishop2Pos = positions[randIndexes_tmp[0]];
let camel2Pos = positions[randIndexes_tmp[1]];
- for (let idx of randIndexes.concat(randIndexes_tmp)
- .sort((a,b) => { return b-a; })) //largest indices first
- {
+ for (let idx of randIndexes.concat(randIndexes_tmp).sort((a, b) => {
+ return b - a;
+ })) {
+ //largest indices first
positions.splice(idx, 1);
}
let kingPos = positions[1];
let rook2Pos = positions[2];
- pieces[c][rook1Pos] = 'r';
- pieces[c][knight1Pos] = 'n';
- pieces[c][bishop1Pos] = 'b';
- pieces[c][queenPos] = 'q';
- pieces[c][camel1Pos] = 'c';
- pieces[c][camel2Pos] = 'c';
- pieces[c][wildebeestPos] = 'w';
- pieces[c][kingPos] = 'k';
- pieces[c][bishop2Pos] = 'b';
- pieces[c][knight2Pos] = 'n';
- pieces[c][rook2Pos] = 'r';
+ pieces[c][rook1Pos] = "r";
+ pieces[c][knight1Pos] = "n";
+ pieces[c][bishop1Pos] = "b";
+ pieces[c][queenPos] = "q";
+ pieces[c][camel1Pos] = "c";
+ pieces[c][camel2Pos] = "c";
+ pieces[c][wildebeestPos] = "w";
+ pieces[c][kingPos] = "k";
+ pieces[c][bishop2Pos] = "b";
+ pieces[c][knight2Pos] = "n";
+ pieces[c][rook2Pos] = "r";
}
- return pieces["b"].join("") +
+ return (
+ pieces["b"].join("") +
"/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
- " w 0 1111 -";
+ " w 0 1111 -"
+ );
}
-}
+};
import { ChessRules } from "@/base_rules";
-export const VariantRules = class ZenRules extends ChessRules
-{
+export const VariantRules = class ZenRules extends ChessRules {
// NOTE: enPassant, if enabled, would need to redefine carefully getEpSquare
- static get HasEnpassant() { return false; }
+ static get HasEnpassant() {
+ return false;
+ }
// TODO(?): some duplicated code in 2 next functions
- getSlideNJumpMoves([x,y], steps, oneStep)
- {
- const color = this.getColor(x,y);
+ getSlideNJumpMoves([x, y], steps, oneStep) {
let moves = [];
- outerLoop:
- for (let loop=0; loop<steps.length; loop++)
- {
+ outerLoop: for (let loop = 0; loop < steps.length; loop++) {
const step = steps[loop];
let i = x + step[0];
let j = y + step[1];
- while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [i,j]));
- if (!!oneStep)
- continue outerLoop;
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ moves.push(this.getBasicMove([x, y], [i, j]));
+ if (oneStep) continue outerLoop;
i += step[0];
j += step[1];
}
// follow steps from x,y until something is met.
// if met piece is opponent and same movement (asA): eat it!
- findCaptures_aux([x,y], asA)
- {
- const color = this.getColor(x,y);
+ findCaptures_aux([x, y], asA) {
+ const color = this.getColor(x, y);
var moves = [];
- const steps = asA != V.PAWN
- ? (asA==V.QUEEN ? V.steps[V.ROOK].concat(V.steps[V.BISHOP]) : V.steps[asA])
- : color=='w' ? [[-1,-1],[-1,1]] : [[1,-1],[1,1]];
- const oneStep = (asA==V.KNIGHT || asA==V.PAWN); //we don't capture king
- const lastRank = (color == 'w' ? 0 : V.size.x-1);
- const promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
- outerLoop:
- for (let loop=0; loop<steps.length; loop++)
- {
+ const steps =
+ asA != V.PAWN
+ ? asA == V.QUEEN
+ ? V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+ : V.steps[asA]
+ : color == "w"
+ ? [
+ [-1, -1],
+ [-1, 1]
+ ]
+ : [
+ [1, -1],
+ [1, 1]
+ ];
+ const oneStep = asA == V.KNIGHT || asA == V.PAWN; //we don't capture king
+ const lastRank = color == "w" ? 0 : V.size.x - 1;
+ const promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
+ outerLoop: for (let loop = 0; loop < steps.length; loop++) {
const step = steps[loop];
let i = x + step[0];
let j = y + step[1];
- while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
- {
- if (oneStep)
- continue outerLoop;
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ if (oneStep) continue outerLoop;
i += step[0];
j += step[1];
}
- if (V.OnBoard(i,j) && this.getColor(i,j) == V.GetOppCol(color)
- && this.getPiece(i,j) == asA)
- {
+ if (
+ V.OnBoard(i, j) &&
+ this.getColor(i, j) == V.GetOppCol(color) &&
+ this.getPiece(i, j) == asA
+ ) {
// eat!
- if (this.getPiece(x,y) == V.PAWN && i == lastRank)
- {
+ if (this.getPiece(x, y) == V.PAWN && i == lastRank) {
// Special case of promotion:
promotionPieces.forEach(p => {
- moves.push(this.getBasicMove([x,y], [i,j], {c:color,p:p}));
+ moves.push(this.getBasicMove([x, y], [i, j], { c: color, p: p }));
});
- }
- else
- {
+ } else {
// All other cases
- moves.push(this.getBasicMove([x,y], [i,j]));
+ moves.push(this.getBasicMove([x, y], [i, j]));
}
}
}
}
// Find possible captures from a square: look in every direction!
- findCaptures(sq)
- {
+ findCaptures(sq) {
let moves = [];
Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.PAWN));
return moves;
}
- getPotentialPawnMoves([x,y])
- {
- const color = this.getColor(x,y);
+ getPotentialPawnMoves([x, y]) {
+ const color = this.getColor(x, y);
let moves = [];
- const [sizeX,sizeY] = [V.size.x,V.size.y];
- const shift = (color == 'w' ? -1 : 1);
- const startRank = (color == 'w' ? sizeY-2 : 1);
- const firstRank = (color == 'w' ? sizeY-1 : 0);
- const lastRank = (color == "w" ? 0 : sizeY-1);
-
- if (x+shift != lastRank)
- {
+ const sizeY = V.size.y;
+ const shift = color == "w" ? -1 : 1;
+ const startRank = color == "w" ? sizeY - 2 : 1;
+ const firstRank = color == "w" ? sizeY - 1 : 0;
+ const lastRank = color == "w" ? 0 : sizeY - 1;
+
+ if (x + shift != lastRank) {
// Normal moves
- if (this.board[x+shift][y] == V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y]));
- if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
- {
+ if (this.board[x + shift][y] == V.EMPTY) {
+ moves.push(this.getBasicMove([x, y], [x + shift, y]));
+ if (
+ [startRank, firstRank].includes(x) &&
+ this.board[x + 2 * shift][y] == V.EMPTY
+ ) {
//two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
+ moves.push(this.getBasicMove([x, y], [x + 2 * shift, y]));
}
}
- }
-
- else //promotion
- {
- let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
+ } //promotion
+ else {
+ let promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
promotionPieces.forEach(p => {
// Normal move
- if (this.board[x+shift][y] == V.EMPTY)
- moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
+ if (this.board[x + shift][y] == V.EMPTY)
+ moves.push(
+ this.getBasicMove([x, y], [x + shift, y], { c: color, p: p })
+ );
});
}
// No en passant here
// Add "zen" captures
- Array.prototype.push.apply(moves, this.findCaptures([x,y]));
+ Array.prototype.push.apply(moves, this.findCaptures([x, y]));
return moves;
}
- getPotentialRookMoves(sq)
- {
+ getPotentialRookMoves(sq) {
let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.ROOK]);
let captures = this.findCaptures(sq);
return noCaptures.concat(captures);
}
- getPotentialKnightMoves(sq)
- {
+ getPotentialKnightMoves(sq) {
let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep");
let captures = this.findCaptures(sq);
return noCaptures.concat(captures);
}
- getPotentialBishopMoves(sq)
- {
+ getPotentialBishopMoves(sq) {
let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]);
let captures = this.findCaptures(sq);
return noCaptures.concat(captures);
}
- getPotentialQueenMoves(sq)
- {
+ getPotentialQueenMoves(sq) {
let noCaptures = this.getSlideNJumpMoves(
- sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
+ sq,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+ );
let captures = this.findCaptures(sq);
return noCaptures.concat(captures);
}
- getPotentialKingMoves(sq)
- {
+ getPotentialKingMoves(sq) {
// Initialize with normal moves
- let noCaptures = this.getSlideNJumpMoves(sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+ let noCaptures = this.getSlideNJumpMoves(
+ sq,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
let captures = this.findCaptures(sq);
return noCaptures.concat(captures).concat(this.getCastleMoves(sq));
}
- getNotation(move)
- {
+ getNotation(move) {
// Recognize special moves first
- if (move.appear.length == 2)
- {
+ if (move.appear.length == 2) {
// castle
- if (move.end.y < move.start.y)
- return "0-0-0";
- else
- return "0-0";
+ if (move.end.y < move.start.y) return "0-0-0";
+ return "0-0";
}
// Translate initial square (because pieces may fly unusually in this variant!)
let notation = "";
const piece = this.getPiece(move.start.x, move.start.y);
- if (piece == V.PAWN)
- {
+ if (piece == V.PAWN) {
// pawn move (TODO: enPassant indication)
- if (move.vanish.length > 1)
- {
+ if (move.vanish.length > 1) {
// capture
notation = initialSquare + "x" + finalSquare;
- }
- else //no capture
- notation = finalSquare;
- if (piece != move.appear[0].p) //promotion
+ } //no capture
+ else notation = finalSquare;
+ if (piece != move.appear[0].p)
+ //promotion
notation += "=" + move.appear[0].p.toUpperCase();
- }
-
- else
- {
+ } else {
// Piece movement
notation = piece.toUpperCase();
- if (move.vanish.length > 1)
- notation += initialSquare + "x";
+ if (move.vanish.length > 1) notation += initialSquare + "x";
notation += finalSquare;
}
return notation;
}
- static get VALUES()
- {
+ static get VALUES() {
return {
- 'p': 1,
- 'r': 3,
- 'n': 2,
- 'b': 2,
- 'q': 5,
- 'k': 1000
- }
+ p: 1,
+ r: 3,
+ n: 2,
+ b: 2,
+ q: 5,
+ k: 1000
+ };
}
-}
+};
<script>
import { store } from "@/store";
export default {
- name: 'my-about',
+ name: "my-about",
computed: {
content: function() {
- return require("raw-loader!@/translations/about/" + store.state.lang + ".pug")
- // Next two lines fix a weird issue after last update (2019-11)
- .replace(/\\[n"]/g, " ")
- .replace('module.exports = "', '').replace(/"$/, "");
- },
- },
+ return (
+ require("raw-loader!@/translations/about/" + store.state.lang + ".pug")
+ // Next two lines fix a weird issue after last update (2019-11)
+ .replace(/\\[n"]/g, " ")
+ .replace('module.exports = "', "")
+ .replace(/"$/, "")
+ );
+ }
+ }
};
</script>
<script>
import BaseGame from "@/components/BaseGame.vue";
import { store } from "@/store";
-import { ArrayFun } from "@/utils/array";
export default {
- name: 'my-analyse',
+ name: "my-analyse",
components: {
- BaseGame,
+ BaseGame
},
// gameRef: to find the game in (potentially remote) storage
data: function() {
return {
st: store.state,
- gameRef: { //given in URL (rid = remote ID)
+ gameRef: {
+ //given in URL (rid = remote ID)
vname: "",
fen: ""
},
game: {
- players:[{name:"Analyse"},{name:"Analyse"}],
- mode: "analyze",
+ players: [{ name: "Analyse" }, { name: "Analyse" }],
+ mode: "analyze"
},
vr: null, //"variant rules" object initialized from FEN
- curFen: "",
+ curFen: ""
//people: [], //players + observers //TODO later: interactive analyze...
};
},
watch: {
// NOTE: no watcher for $route change, because if fenStart doesn't change
// then it doesn't trigger BaseGame.re_init() and the result is weird.
- "vr.movesCount": function(fen) {
+ "vr.movesCount": function() {
this.curFen = this.vr.getFen();
this.adjustFenSize();
- },
+ }
},
created: function() {
this.gameRef.vname = this.$route.params["vname"];
- if (this.gameRef.vname == "Dark")
- {
+ if (this.gameRef.vname == "Dark") {
alert(this.st.tr["Analyse in Dark mode makes no sense!"]);
history.back(); //or this.$router.go(-1)
- }
- else
- {
+ } else {
this.gameRef.fen = this.$route.query["fen"].replace(/_/g, " ");
this.initialize();
}
gotoFen: function() {
this.gameRef.fen = this.curFen;
this.loadGame();
- },
- },
+ }
+ }
};
</script>
import { store } from "@/store";
import { ajax } from "@/utils/ajax";
export default {
- name: 'my-auth',
+ name: "my-auth",
data: function() {
return {
st: store.state,
- errmsg: "",
+ errmsg: ""
};
},
created: function() {
- ajax(
- "/authenticate",
- "GET",
- {token: this.$route.params["token"]},
- (res) => {
- if (!res.errmsg) //if not already logged in
- {
- this.st.user.id = res.id;
- this.st.user.name = res.name;
- this.st.user.email = res.email;
- this.st.user.notify = res.notify;
- localStorage["myname"] = res.name;
- localStorage["myid"] = res.id;
- }
- else
- this.errmsg = res.errmsg;
- }
+ ajax(
+ "/authenticate",
+ "GET",
+ { token: this.$route.params["token"] },
+ res => {
+ if (!res.errmsg) {
+ //if not already logged in
+ this.st.user.id = res.id;
+ this.st.user.name = res.name;
+ this.st.user.email = res.email;
+ this.st.user.notify = res.notify;
+ localStorage["myname"] = res.name;
+ localStorage["myid"] = res.id;
+ } else this.errmsg = res.errmsg;
+ }
);
- },
+ }
};
</script>
import { ppt } from "@/utils/datetime";
import { extractTime } from "@/utils/timeControl";
import { getRandString } from "@/utils/alea";
-import { ArrayFun } from "@/utils/array";
import { processModalClick } from "@/utils/modalClick";
import { getScoreMessage } from "@/utils/scoring";
import params from "@/parameters";
export default {
- name: 'my-game',
+ name: "my-game",
components: {
BaseGame,
- Chat,
+ Chat
},
// gameRef: to find the game in (potentially remote) storage
data: function() {
return {
st: store.state,
- gameRef: { //given in URL (rid = remote ID)
+ gameRef: {
+ //given in URL (rid = remote ID)
id: "",
rid: ""
},
- game: { //passed to BaseGame
- players:[{name:""},{name:""}],
+ game: {
+ //passed to BaseGame
+ players: [{ name: "" }, { name: "" }],
chats: [],
- rendered: false,
+ rendered: false
},
virtualClocks: [0, 0], //initialized with true game.clocks
vr: null, //"variant rules" object initialized from FEN
connexionString: "",
// Related to (killing of) self multi-connects:
newConnect: {},
- killed: {},
+ killed: {}
};
},
watch: {
- "$route": function(to, from) {
+ $route: function(to) {
this.gameRef.id = to.params["id"];
this.gameRef.rid = to.query["rid"];
this.loadGame();
- },
+ }
},
// NOTE: some redundant code with Hall.vue (mostly related to people array)
created: function() {
// Always add myself to players' list
const my = this.st.user;
- this.$set(this.people, my.sid, {id:my.id, name:my.name});
+ this.$set(this.people, my.sid, { id: my.id, name: my.name });
this.gameRef.id = this.$route.params["id"];
this.gameRef.rid = this.$route.query["rid"]; //may be undefined
// Initialize connection
- this.connexionString = params.socketUrl +
- "/?sid=" + this.st.user.sid +
- "&tmpId=" + getRandString() +
- "&page=" + encodeURIComponent(this.$route.path);
+ this.connexionString =
+ params.socketUrl +
+ "/?sid=" +
+ this.st.user.sid +
+ "&tmpId=" +
+ getRandString() +
+ "&page=" +
+ encodeURIComponent(this.$route.path);
this.conn = new WebSocket(this.connexionString);
this.conn.onmessage = this.socketMessageListener;
this.conn.onclose = this.socketCloseListener;
// Socket init required before loading remote game:
- const socketInit = (callback) => {
- if (!!this.conn && this.conn.readyState == 1) //1 == OPEN state
+ const socketInit = callback => {
+ if (!!this.conn && this.conn.readyState == 1)
+ //1 == OPEN state
callback();
- else //socket not ready yet (initial loading)
- {
+ //socket not ready yet (initial loading)
+ else {
// NOTE: it's important to call callback without arguments,
// otherwise first arg is Websocket object and loadGame fails.
- this.conn.onopen = () => { return callback() };
+ this.conn.onopen = () => {
+ return callback();
+ };
}
};
- if (!this.gameRef.rid) //game stored locally or on server
+ if (!this.gameRef.rid)
+ //game stored locally or on server
this.loadGame(null, () => socketInit(this.roomInit));
- else //game stored remotely: need socket to retrieve it
- {
+ //game stored remotely: need socket to retrieve it
+ else {
// NOTE: the callback "roomInit" will be lost, so we don't provide it.
// --> It will be given when receiving "fullgame" socket event.
// A more general approach would be to store it somewhere.
}
},
mounted: function() {
- document.getElementById("chatWrap").addEventListener(
- "click", processModalClick);
+ document
+ .getElementById("chatWrap")
+ .addEventListener("click", processModalClick);
},
beforeDestroy: function() {
this.send("disconnect");
this.send("pollclients");
},
send: function(code, obj) {
- if (!!this.conn)
- {
- this.conn.send(JSON.stringify(
- Object.assign(
- {code: code},
- obj,
- )
- ));
+ if (this.conn) {
+ this.conn.send(JSON.stringify(Object.assign({ code: code }, obj)));
}
},
isConnected: function(index) {
if (this.st.user.sid == player.sid || this.st.user.id == player.uid)
return true;
// Try to find a match in people:
- return Object.keys(this.people).some(sid => sid == player.sid) ||
- Object.values(this.people).some(p => p.id == player.uid);
+ return (
+ Object.keys(this.people).some(sid => sid == player.sid) ||
+ Object.values(this.people).some(p => p.id == player.uid)
+ );
},
socketMessageListener: function(msg) {
- if (!this.conn)
- return;
+ if (!this.conn) return;
const data = JSON.parse(msg.data);
- switch (data.code)
- {
+ switch (data.code) {
case "pollclients":
data.sockIds.forEach(sid => {
- this.$set(this.people, sid, {id:0, name:""});
- if (sid != this.st.user.sid)
- {
- this.send("askidentity", {target:sid});
+ this.$set(this.people, sid, { id: 0, name: "" });
+ if (sid != this.st.user.sid) {
+ this.send("askidentity", { target: sid });
// Ask potentially missed last state, if opponent and I play
- if (!!this.game.mycolor
- && this.game.type == "live" && this.game.score == "*"
- && this.game.players.some(p => p.sid == sid))
- {
- this.send("asklastate", {target:sid});
+ if (
+ !!this.game.mycolor &&
+ this.game.type == "live" &&
+ this.game.score == "*" &&
+ this.game.players.some(p => p.sid == sid)
+ ) {
+ this.send("asklastate", { target: sid });
}
}
});
break;
case "connect":
if (!this.people[data.from])
- this.$set(this.people, data.from, {name:"", id:0});
- if (!this.people[data.from].name)
- {
+ this.$set(this.people, data.from, { name: "", id: 0 });
+ if (!this.people[data.from].name) {
this.newConnect[data.from] = true; //for self multi-connects tests
- this.send("askidentity", {target:data.from});
+ this.send("askidentity", { target: data.from });
}
break;
case "disconnect":
//this.conn.close();
this.conn = null;
break;
- case "askidentity":
- {
+ case "askidentity": {
// Request for identification (TODO: anonymous shouldn't need to reply)
const me = {
// Decompose to avoid revealing email
name: this.st.user.name,
sid: this.st.user.sid,
- id: this.st.user.id,
+ id: this.st.user.id
};
- this.send("identity", {data:me, target:data.from});
+ this.send("identity", { data: me, target: data.from });
break;
}
- case "identity":
- {
+ case "identity": {
const user = data.data;
- if (!!user.name) //otherwise anonymous
- {
+ if (user.name) {
+ //otherwise anonymous
// If I multi-connect, kill current connexion if no mark (I'm older)
- if (this.newConnect[user.sid] && user.id > 0
- && user.id == this.st.user.id && user.sid != this.st.user.sid)
- {
- if (!this.killed[this.st.user.sid])
- {
- this.send("killme", {sid:this.st.user.sid});
+ if (
+ this.newConnect[user.sid] &&
+ user.id > 0 &&
+ user.id == this.st.user.id &&
+ user.sid != this.st.user.sid
+ ) {
+ if (!this.killed[this.st.user.sid]) {
+ this.send("killme", { sid: this.st.user.sid });
this.killed[this.st.user.sid] = true;
}
}
- if (user.sid != this.st.user.sid) //I already know my identity...
- {
- this.$set(this.people, user.sid,
- {
- id: user.id,
- name: user.name,
- });
+ if (user.sid != this.st.user.sid) {
+ //I already know my identity...
+ this.$set(this.people, user.sid, {
+ id: user.id,
+ name: user.name
+ });
}
}
delete this.newConnect[user.sid];
}
case "askgame":
// Send current (live) game if not asked by any of the players
- if (this.game.type == "live"
- && this.game.players.every(p => p.sid != data.from[0]))
- {
+ if (
+ this.game.type == "live" &&
+ this.game.players.every(p => p.sid != data.from[0])
+ ) {
const myGame = {
id: this.game.id,
fen: this.game.fen,
vid: this.game.vid,
cadence: this.game.cadence,
score: this.game.score,
- rid: this.st.user.sid, //useful in Hall if I'm an observer
+ rid: this.st.user.sid //useful in Hall if I'm an observer
};
- this.send("game", {data:myGame, target:data.from});
+ this.send("game", { data: myGame, target: data.from });
}
break;
case "askfullgame":
- this.send("fullgame", {data:this.game, target:data.from});
+ this.send("fullgame", { data: this.game, target: data.from });
break;
case "fullgame":
// Callback "roomInit" to poll clients only after game is loaded
break;
case "asklastate":
// Sending last state if I played a move or score != "*"
- if ((this.game.moves.length > 0 && this.vr.turn != this.game.mycolor)
- || this.game.score != "*" || this.drawOffer == "sent")
- {
+ if (
+ (this.game.moves.length > 0 && this.vr.turn != this.game.mycolor) ||
+ this.game.score != "*" ||
+ this.drawOffer == "sent"
+ ) {
// Send our "last state" informations to opponent
const L = this.game.moves.length;
- const myIdx = ["w","b"].indexOf(this.game.mycolor);
+ const myIdx = ["w", "b"].indexOf(this.game.mycolor);
const myLastate = {
// NOTE: lastMove (when defined) includes addTime
- lastMove: (L>0 ? this.game.moves[L-1] : undefined),
+ lastMove: L > 0 ? this.game.moves[L - 1] : undefined,
// Since we played a move (or abort or resign),
// only drawOffer=="sent" is possible
drawSent: this.drawOffer == "sent",
score: this.game.score,
movesCount: L,
- initime: this.game.initime[1-myIdx], //relevant only if I played
+ initime: this.game.initime[1 - myIdx] //relevant only if I played
};
- this.send("lastate", {data:myLastate, target:data.from});
+ this.send("lastate", { data: myLastate, target: data.from });
}
break;
case "lastate": //got opponent infos about last move
this.lastate = data.data;
- if (this.game.rendered) //game is rendered (Board component)
+ if (this.game.rendered)
+ //game is rendered (Board component)
this.processLastate();
//else: will be processed when game is ready
break;
- case "newmove":
- {
+ case "newmove": {
const move = data.data;
- if (!!move.cancelDrawOffer) //opponent refuses draw
- {
+ if (move.cancelDrawOffer) {
+ //opponent refuses draw
this.drawOffer = "";
// NOTE for corr games: drawOffer reset by player in turn
if (this.game.type == "live" && !!this.game.mycolor)
- GameStorage.update(this.gameRef.id, {drawOffer: ""});
+ GameStorage.update(this.gameRef.id, { drawOffer: "" });
}
this.$set(this.game, "moveToPlay", move);
break;
}
case "resign":
- this.gameOver(data.side=="b" ? "1-0" : "0-1", "Resign");
+ this.gameOver(data.side == "b" ? "1-0" : "0-1", "Resign");
break;
case "abort":
this.gameOver("?", "Abort");
},
socketCloseListener: function() {
this.conn = new WebSocket(this.connexionString);
- this.conn.addEventListener('message', this.socketMessageListener);
- this.conn.addEventListener('close', this.socketCloseListener);
+ this.conn.addEventListener("message", this.socketMessageListener);
+ this.conn.addEventListener("close", this.socketCloseListener);
},
// lastate was received, but maybe game wasn't ready yet:
processLastate: function() {
const data = this.lastate;
this.lastate = undefined; //security...
const L = this.game.moves.length;
- if (data.movesCount > L)
- {
+ if (data.movesCount > L) {
// Just got last move from him
- this.$set(this.game, "moveToPlay", Object.assign({initime: data.initime}, data.lastMove));
+ this.$set(
+ this.game,
+ "moveToPlay",
+ Object.assign({ initime: data.initime }, data.lastMove)
+ );
}
- if (data.drawSent)
- this.drawOffer = "received";
- if (data.score != "*")
- {
+ if (data.drawSent) this.drawOffer = "received";
+ if (data.score != "*") {
this.drawOffer = "";
- if (this.game.score == "*")
- this.gameOver(data.score);
+ if (this.game.score == "*") this.gameOver(data.score);
}
},
clickDraw: function() {
- if (!this.game.mycolor)
- return; //I'm just spectator
- if (["received","threerep"].includes(this.drawOffer))
- {
- if (!confirm(this.st.tr["Accept draw?"]))
- return;
- const message = (this.drawOffer == "received"
- ? "Mutual agreement"
- : "Three repetitions");
- this.send("draw", {data:message});
+ if (!this.game.mycolor) return; //I'm just spectator
+ if (["received", "threerep"].includes(this.drawOffer)) {
+ if (!confirm(this.st.tr["Accept draw?"])) return;
+ const message =
+ this.drawOffer == "received"
+ ? "Mutual agreement"
+ : "Three repetitions";
+ this.send("draw", { data: message });
this.gameOver("1/2", message);
- }
- else if (this.drawOffer == "") //no effect if drawOffer == "sent"
- {
- if (this.game.mycolor != this.vr.turn)
- return alert(this.st.tr["Draw offer only in your turn"]);
- if (!confirm(this.st.tr["Offer draw?"]))
+ } else if (this.drawOffer == "") {
+ //no effect if drawOffer == "sent"
+ if (this.game.mycolor != this.vr.turn) {
+ alert(this.st.tr["Draw offer only in your turn"]);
return;
+ }
+ if (!confirm(this.st.tr["Offer draw?"])) return;
this.drawOffer = "sent";
this.send("drawoffer");
- GameStorage.update(this.gameRef.id, {drawOffer: this.game.mycolor});
+ GameStorage.update(this.gameRef.id, { drawOffer: this.game.mycolor });
}
},
abortGame: function() {
- if (!this.game.mycolor || !confirm(this.st.tr["Terminate game?"]))
- return;
+ if (!this.game.mycolor || !confirm(this.st.tr["Terminate game?"])) return;
this.gameOver("?", "Abort");
this.send("abort");
},
- resign: function(e) {
+ resign: function() {
if (!this.game.mycolor || !confirm(this.st.tr["Resign the game?"]))
return;
- this.send("resign", {data:this.game.mycolor});
- this.gameOver(this.game.mycolor=="w" ? "0-1" : "1-0", "Resign");
+ this.send("resign", { data: this.game.mycolor });
+ this.gameOver(this.game.mycolor == "w" ? "0-1" : "1-0", "Resign");
},
// 3 cases for loading a game:
// - from indexedDB (running or completed live game I play)
// - from server (one correspondance game I play[ed] or not)
// - from remote peer (one live game I don't play, finished or not)
loadGame: function(game, callback) {
- const afterRetrieval = async (game) => {
+ const afterRetrieval = async game => {
const vModule = await import("@/variants/" + game.vname + ".js");
window.V = vModule.VariantRules;
this.vr = new V(game.fen);
- const gtype = (game.cadence.indexOf('d') >= 0 ? "corr" : "live");
+ const gtype = game.cadence.indexOf("d") >= 0 ? "corr" : "live";
const tc = extractTime(game.cadence);
const myIdx = game.players.findIndex(p => {
return p.sid == this.st.user.sid || p.uid == this.st.user.id;
});
- const mycolor = [undefined,"w","b"][myIdx+1]; //undefined for observers
- if (!game.chats)
- game.chats = []; //live games don't have chat history
- if (gtype == "corr")
- {
- if (game.players[0].color == "b")
- {
+ const mycolor = [undefined, "w", "b"][myIdx + 1]; //undefined for observers
+ if (!game.chats) game.chats = []; //live games don't have chat history
+ if (gtype == "corr") {
+ if (game.players[0].color == "b") {
// Adopt the same convention for live and corr games: [0] = white
- [ game.players[0], game.players[1] ] =
- [ game.players[1], game.players[0] ];
+ [game.players[0], game.players[1]] = [
+ game.players[1],
+ game.players[0]
+ ];
}
// corr game: needs to compute the clocks + initime
// NOTE: clocks in seconds, initime in milliseconds
game.clocks = [tc.mainTime, tc.mainTime];
- game.moves.sort((m1,m2) => m1.idx - m2.idx); //in case of
- if (game.score == "*") //otherwise no need to bother with time
- {
+ game.moves.sort((m1, m2) => m1.idx - m2.idx); //in case of
+ if (game.score == "*") {
+ //otherwise no need to bother with time
game.initime = [0, 0];
const L = game.moves.length;
- if (L >= 3)
- {
+ if (L >= 3) {
let addTime = [0, 0];
- for (let i=2; i<L; i++)
- {
- addTime[i%2] += tc.increment -
- (game.moves[i].played - game.moves[i-1].played) / 1000;
+ for (let i = 2; i < L; i++) {
+ addTime[i % 2] +=
+ tc.increment -
+ (game.moves[i].played - game.moves[i - 1].played) / 1000;
}
- for (let i=0; i<=1; i++)
- game.clocks[i] += addTime[i];
+ for (let i = 0; i <= 1; i++) game.clocks[i] += addTime[i];
}
- if (L >= 1)
- game.initime[L%2] = game.moves[L-1].played;
+ if (L >= 1) game.initime[L % 2] = game.moves[L - 1].played;
}
- const reformattedMoves = game.moves.map( (m) => {
+ const reformattedMoves = game.moves.map(m => {
const s = m.squares;
return {
appear: s.appear,
vanish: s.vanish,
start: s.start,
- end: s.end,
+ end: s.end
};
});
// Sort chat messages from newest to oldest
- game.chats.sort( (c1,c2) => { return c2.added - c1.added; });
- if (myIdx >= 0 && game.chats.length > 0)
- {
+ game.chats.sort((c1, c2) => {
+ return c2.added - c1.added;
+ });
+ if (myIdx >= 0 && game.chats.length > 0) {
// TODO: group multi-moves into an array, to deduce color from index
// and not need this (also repeated in BaseGame::re_setVariables())
let vr_tmp = new V(game.fenStart); //vr is already at end of game
- for (let i=0; i<reformattedMoves.length; i++)
- {
+ for (let i = 0; i < reformattedMoves.length; i++) {
game.moves[i].color = vr_tmp.turn;
vr_tmp.play(reformattedMoves[i]);
}
// Blue background on chat button if last chat message arrived after my last move.
let dtLastMove = 0;
- for (let midx = game.moves.length-1; midx >= 0; midx--)
- {
- if (game.moves[midx].color == mycolor)
- {
+ for (let midx = game.moves.length - 1; midx >= 0; midx--) {
+ if (game.moves[midx].color == mycolor) {
dtLastMove = game.moves[midx].played;
break;
}
// Now that we used idx and played, re-format moves as for live games
game.moves = reformattedMoves;
}
- if (gtype == "live" && game.clocks[0] < 0) //game unstarted
- {
+ if (gtype == "live" && game.clocks[0] < 0) {
+ //game unstarted
game.clocks = [tc.mainTime, tc.mainTime];
- if (game.score == "*")
- {
+ if (game.score == "*") {
game.initime[0] = Date.now();
- if (myIdx >= 0)
- {
+ if (myIdx >= 0) {
// I play in this live game; corr games don't have clocks+initime
- GameStorage.update(game.id,
- {
+ GameStorage.update(game.id, {
clocks: game.clocks,
- initime: game.initime,
+ initime: game.initime
});
}
}
}
- if (!!game.drawOffer)
- {
- if (game.drawOffer == "t") //three repetitions
+ if (game.drawOffer) {
+ if (game.drawOffer == "t")
+ //three repetitions
this.drawOffer = "threerep";
- else
- {
- if (myIdx < 0)
- this.drawOffer = "received"; //by any of the players
- else
- {
+ else {
+ if (myIdx < 0) this.drawOffer = "received";
+ //by any of the players
+ else {
// I play in this game:
- if ((game.drawOffer == "w" && myIdx==0) || (game.drawOffer=="b" && myIdx==1))
+ if (
+ (game.drawOffer == "w" && myIdx == 0) ||
+ (game.drawOffer == "b" && myIdx == 1)
+ )
this.drawOffer = "sent";
- else //all other cases
- this.drawOffer = "received";
+ //all other cases
+ else this.drawOffer = "received";
}
}
}
- if (!!game.scoreMsg)
- game.scoreMsg = this.st.tr[game.scoreMsg]; //stored in english
- this.game = Object.assign({},
+ if (game.scoreMsg) game.scoreMsg = this.st.tr[game.scoreMsg]; //stored in english
+ this.game = Object.assign(
+ {},
game,
// NOTE: assign mycolor here, since BaseGame could also be VS computer
{
mycolor: mycolor,
// opponent sid not strictly required (or available), but easier
// at least oppsid or oppid is available anyway:
- oppsid: (myIdx < 0 ? undefined : game.players[1-myIdx].sid),
- oppid: (myIdx < 0 ? undefined : game.players[1-myIdx].uid),
+ oppsid: myIdx < 0 ? undefined : game.players[1 - myIdx].sid,
+ oppid: myIdx < 0 ? undefined : game.players[1 - myIdx].uid
}
);
this.re_setClocks();
this.$nextTick(() => {
this.game.rendered = true;
// Did lastate arrive before game was rendered?
- if (!!this.lastate)
- this.processLastate();
+ if (this.lastate) this.processLastate();
});
this.repeat = {}; //reset: scan past moves' FEN:
let repIdx = 0;
let vr_tmp = new V(game.fenStart);
game.moves.forEach(m => {
vr_tmp.play(m);
- const fenObj = V.ParseFen( vr_tmp.getFen() );
+ const fenObj = V.ParseFen(vr_tmp.getFen());
repIdx = fenObj.position + "_" + fenObj.turn;
- if (!!fenObj.flags)
- repIdx += "_" + fenObj.flags;
- this.repeat[repIdx] = (!!this.repeat[repIdx]
- ? this.repeat[repIdx]+1
- : 1);
+ if (fenObj.flags) repIdx += "_" + fenObj.flags;
+ this.repeat[repIdx] = this.repeat[repIdx]
+ ? this.repeat[repIdx] + 1
+ : 1;
});
- if (this.repeat[repIdx] >= 3)
- this.drawOffer = "threerep";
- if (!!callback)
- callback();
+ if (this.repeat[repIdx] >= 3) this.drawOffer = "threerep";
+ if (callback) callback();
};
- if (!!game)
- return afterRetrieval(game);
- if (!!this.gameRef.rid)
- {
- // Remote live game: forgetting about callback func... (TODO: design)
- this.send("askfullgame", {target:this.gameRef.rid});
+ if (game) {
+ afterRetrieval(game);
+ return;
}
- else
- {
+ if (this.gameRef.rid) {
+ // Remote live game: forgetting about callback func... (TODO: design)
+ this.send("askfullgame", { target: this.gameRef.rid });
+ } else {
// Local or corr game
GameStorage.get(this.gameRef.id, afterRetrieval);
}
},
re_setClocks: function() {
- if (this.game.moves.length < 2 || this.game.score != "*")
- {
+ if (this.game.moves.length < 2 || this.game.score != "*") {
// 1st move not completed yet, or game over: freeze time
this.virtualClocks = this.game.clocks.map(s => ppt(s));
return;
}
const currentTurn = this.vr.turn;
- const colorIdx = ["w","b"].indexOf(currentTurn);
- let countdown = this.game.clocks[colorIdx] -
- (Date.now() - this.game.initime[colorIdx])/1000;
- this.virtualClocks = [0,1].map(i => {
- const removeTime = i == colorIdx
- ? (Date.now() - this.game.initime[colorIdx])/1000
- : 0;
+ const colorIdx = ["w", "b"].indexOf(currentTurn);
+ let countdown =
+ this.game.clocks[colorIdx] -
+ (Date.now() - this.game.initime[colorIdx]) / 1000;
+ this.virtualClocks = [0, 1].map(i => {
+ const removeTime =
+ i == colorIdx ? (Date.now() - this.game.initime[colorIdx]) / 1000 : 0;
return ppt(this.game.clocks[i] - removeTime);
});
let clockUpdate = setInterval(() => {
- if (countdown < 0 || this.vr.turn != currentTurn || this.game.score != "*")
- {
+ if (
+ countdown < 0 ||
+ this.vr.turn != currentTurn ||
+ this.game.score != "*"
+ ) {
clearInterval(clockUpdate);
if (countdown < 0)
- this.gameOver(this.vr.turn=="w" ? "0-1" : "1-0", this.st.tr["Time"]);
- }
- else
- this.$set(this.virtualClocks, colorIdx, ppt(Math.max(0, --countdown)));
+ this.gameOver(
+ this.vr.turn == "w" ? "0-1" : "1-0",
+ this.st.tr["Time"]
+ );
+ } else
+ this.$set(
+ this.virtualClocks,
+ colorIdx,
+ ppt(Math.max(0, --countdown))
+ );
}, 1000);
},
// Post-process a move (which was just played in BaseGame)
processMove: function(move) {
- if (this.game.type == "corr" && move.color == this.game.mycolor)
- {
- if (!confirm(this.st.tr["Move played:"] + " " + move.notation + "\n" + this.st.tr["Are you sure?"]))
- {
- return this.$set(this.game, "moveToUndo", move);
+ if (this.game.type == "corr" && move.color == this.game.mycolor) {
+ if (
+ !confirm(
+ this.st.tr["Move played:"] +
+ " " +
+ move.notation +
+ "\n" +
+ this.st.tr["Are you sure?"]
+ )
+ ) {
+ this.$set(this.game, "moveToUndo", move);
+ return;
}
}
- const colorIdx = ["w","b"].indexOf(move.color);
- const nextIdx = ["w","b"].indexOf(this.vr.turn);
+ const colorIdx = ["w", "b"].indexOf(move.color);
+ const nextIdx = ["w", "b"].indexOf(this.vr.turn);
// https://stackoverflow.com/a/38750895
- if (!!this.game.mycolor)
- {
+ if (this.game.mycolor) {
const allowed_fields = ["appear", "vanish", "start", "end"];
// NOTE: 'var' to see this variable outside this block
var filtered_move = Object.keys(move)
}
// Send move ("newmove" event) to people in the room (if our turn)
let addTime = 0;
- if (move.color == this.game.mycolor)
- {
- if (this.drawOffer == "received") //I refuse draw
+ if (move.color == this.game.mycolor) {
+ if (this.drawOffer == "received")
+ //I refuse draw
this.drawOffer = "";
- if (this.game.moves.length >= 2) //after first move
- {
+ if (this.game.moves.length >= 2) {
+ //after first move
const elapsed = Date.now() - this.game.initime[colorIdx];
// elapsed time is measured in milliseconds
- addTime = this.game.increment - elapsed/1000;
+ addTime = this.game.increment - elapsed / 1000;
}
- const sendMove = Object.assign({},
- filtered_move,
- {
- addTime: addTime,
- cancelDrawOffer: this.drawOffer=="",
- });
- this.send("newmove", {data: sendMove});
+ const sendMove = Object.assign({}, filtered_move, {
+ addTime: addTime,
+ cancelDrawOffer: this.drawOffer == ""
+ });
+ this.send("newmove", { data: sendMove });
// (Add)Time indication: useful in case of lastate infos requested
move.addTime = addTime;
- }
- else
- addTime = move.addTime; //supposed transmitted
+ } else addTime = move.addTime; //supposed transmitted
// Update current game object:
this.game.moves.push(move);
this.game.fen = move.fen;
// If repetition detected, consider that a draw offer was received:
const fenObj = V.ParseFen(move.fen);
let repIdx = fenObj.position + "_" + fenObj.turn;
- if (!!fenObj.flags)
- repIdx += "_" + fenObj.flags;
- this.repeat[repIdx] = (!!this.repeat[repIdx]
- ? this.repeat[repIdx]+1
- : 1);
- if (this.repeat[repIdx] >= 3)
- this.drawOffer = "threerep";
- else if (this.drawOffer == "threerep")
- this.drawOffer = "";
+ if (fenObj.flags) repIdx += "_" + fenObj.flags;
+ this.repeat[repIdx] = this.repeat[repIdx] ? this.repeat[repIdx] + 1 : 1;
+ if (this.repeat[repIdx] >= 3) this.drawOffer = "threerep";
+ else if (this.drawOffer == "threerep") this.drawOffer = "";
// Since corr games are stored at only one location, update should be
// done only by one player for each move:
- if (!!this.game.mycolor &&
- (this.game.type == "live" || move.color == this.game.mycolor))
- {
+ if (
+ !!this.game.mycolor &&
+ (this.game.type == "live" || move.color == this.game.mycolor)
+ ) {
let drawCode = "";
- switch (this.drawOffer)
- {
+ switch (this.drawOffer) {
case "threerep":
drawCode = "t";
break;
drawCode = this.vr.turn;
break;
}
- if (this.game.type == "corr")
- {
- GameStorage.update(this.gameRef.id,
- {
+ if (this.game.type == "corr") {
+ GameStorage.update(this.gameRef.id, {
fen: move.fen,
- move:
- {
+ move: {
squares: filtered_move,
played: Date.now(),
- idx: this.game.moves.length - 1,
+ idx: this.game.moves.length - 1
},
- drawOffer: drawCode || "n", //"n" for "None" to force reset (otherwise it's ignored)
+ drawOffer: drawCode || "n" //"n" for "None" to force reset (otherwise it's ignored)
});
- }
- else //live
- {
- GameStorage.update(this.gameRef.id,
- {
+ } //live
+ else {
+ GameStorage.update(this.gameRef.id, {
fen: move.fen,
move: filtered_move,
clocks: this.game.clocks,
initime: this.game.initime,
- drawOffer: drawCode,
+ drawOffer: drawCode
});
}
}
document.getElementById("chatBtn").classList.remove("somethingnew");
},
processChat: function(chat) {
- this.send("newchat", {data:chat});
+ this.send("newchat", { data: chat });
// NOTE: anonymous chats in corr games are not stored on server (TODO?)
if (this.game.type == "corr" && this.st.user.id > 0)
- GameStorage.update(this.gameRef.id, {chat: chat});
+ GameStorage.update(this.gameRef.id, { chat: chat });
},
gameOver: function(score, scoreMsg) {
this.game.score = score;
- this.game.scoreMsg = this.st.tr[(!!scoreMsg
- ? scoreMsg
- : getScoreMessage(score))];
+ this.game.scoreMsg = this.st.tr[
+ scoreMsg ? scoreMsg : getScoreMessage(score)
+ ];
const myIdx = this.game.players.findIndex(p => {
return p.sid == this.st.user.sid || p.uid == this.st.user.id;
});
- if (myIdx >= 0) //OK, I play in this game
- {
- GameStorage.update(this.gameRef.id,
- {score: score, scoreMsg: scoreMsg});
+ if (myIdx >= 0) {
+ //OK, I play in this game
+ GameStorage.update(this.gameRef.id, {
+ score: score,
+ scoreMsg: scoreMsg
+ });
// Notify the score to main Hall. TODO: only one player (currently double send)
- this.send("result", {gid:this.game.id, score:score});
+ this.send("result", { gid: this.game.id, score: score });
}
- },
- },
+ }
+ }
};
</script>
components: {
Chat,
GameList,
- ChallengeList,
+ ChallengeList
},
- data: function () {
+ data: function() {
return {
st: store.state,
cdisplay: "live", //or corr
fen: "",
vid: localStorage.getItem("vid") || "",
to: "", //name of challenged player (if any)
- cadence: localStorage.getItem("cadence") || "",
+ cadence: localStorage.getItem("cadence") || ""
},
newChat: "",
conn: null,
connexionString: "",
// Related to (killing of) self multi-connects:
newConnect: {},
- killed: {},
+ killed: {}
};
},
watch: {
// st.variants changes only once, at loading from [] to [...]
- "st.variants": function(variantArray) {
+ "st.variants": function() {
// Set potential challenges and games variant names:
this.challenges.concat(this.games).forEach(o => {
- if (o.vname == "")
- o.vname = this.getVname(o.vid);
+ if (o.vname == "") o.vname = this.getVname(o.vid);
});
- },
+ }
},
computed: {
anonymousCount: function() {
let count = 0;
- Object.values(this.people).forEach(p => { count += (!p.name ? 1 : 0); });
+ Object.values(this.people).forEach(p => {
+ count += !p.name ? 1 : 0;
+ });
return count;
- },
+ }
},
created: function() {
const my = this.st.user;
- this.$set(this.people, my.sid, {id:my.id, name:my.name, pages:["/"]});
+ this.$set(this.people, my.sid, { id: my.id, name: my.name, pages: ["/"] });
// Ask server for current corr games (all but mines)
ajax(
"/games",
"GET",
- {uid: this.st.user.id, excluded: true},
+ { uid: this.st.user.id, excluded: true },
response => {
- this.games = this.games.concat(response.games.map(g => {
- const type = this.classifyObject(g);
- const vname = this.getVname(g.vid);
- return Object.assign({}, g, {type: type, vname: vname});
- }));
+ this.games = this.games.concat(
+ response.games.map(g => {
+ const type = this.classifyObject(g);
+ const vname = this.getVname(g.vid);
+ return Object.assign({}, g, { type: type, vname: vname });
+ })
+ );
}
);
// Also ask for corr challenges (open + sent by/to me)
- ajax(
- "/challenges",
- "GET",
- {uid: this.st.user.id},
- response => {
- // Gather all senders names, and then retrieve full identity:
- // (TODO [perf]: some might be online...)
- let names = {};
- response.challenges.forEach(c => {
- if (c.uid != this.st.user.id)
- names[c.uid] = ""; //unknwon for now
- else if (!!c.target && c.target != this.st.user.id)
- names[c.target] = "";
- });
- const addChallenges = (newChalls) => {
- names[this.st.user.id] = this.st.user.name; //in case of
- this.challenges = this.challenges.concat(
- response.challenges.map(c => {
- const from = {name: names[c.uid], id: c.uid}; //or just name
- const type = this.classifyObject(c);
- const vname = this.getVname(c.vid);
- return Object.assign({},
- {
- type: type,
- vname: vname,
- from: from,
- to: (!!c.target ? names[c.target] : ""),
- },
- c);
- })
- );
- };
- if (Object.keys(names).length > 0)
- {
- ajax("/users",
- "GET",
- { ids: Object.keys(names).join(",") },
- response2 => {
- response2.users.forEach(u => {names[u.id] = u.name});
- addChallenges();
- }
- );
- }
- else
- addChallenges();
- }
- );
+ ajax("/challenges", "GET", { uid: this.st.user.id }, response => {
+ // Gather all senders names, and then retrieve full identity:
+ // (TODO [perf]: some might be online...)
+ let names = {};
+ response.challenges.forEach(c => {
+ if (c.uid != this.st.user.id) names[c.uid] = "";
+ //unknwon for now
+ else if (!!c.target && c.target != this.st.user.id)
+ names[c.target] = "";
+ });
+ const addChallenges = () => {
+ names[this.st.user.id] = this.st.user.name; //in case of
+ this.challenges = this.challenges.concat(
+ response.challenges.map(c => {
+ const from = { name: names[c.uid], id: c.uid }; //or just name
+ const type = this.classifyObject(c);
+ const vname = this.getVname(c.vid);
+ return Object.assign(
+ {},
+ {
+ type: type,
+ vname: vname,
+ from: from,
+ to: c.target ? names[c.target] : ""
+ },
+ c
+ );
+ })
+ );
+ };
+ if (Object.keys(names).length > 0) {
+ ajax(
+ "/users",
+ "GET",
+ { ids: Object.keys(names).join(",") },
+ response2 => {
+ response2.users.forEach(u => {
+ names[u.id] = u.name;
+ });
+ addChallenges();
+ }
+ );
+ } else addChallenges();
+ });
const connectAndPoll = () => {
this.send("connect");
this.send("pollclientsandgamers");
};
// Initialize connection
- this.connexionString = params.socketUrl +
- "/?sid=" + this.st.user.sid +
- "&tmpId=" + getRandString() +
- "&page=" + encodeURIComponent(this.$route.path);
+ this.connexionString =
+ params.socketUrl +
+ "/?sid=" +
+ this.st.user.sid +
+ "&tmpId=" +
+ getRandString() +
+ "&page=" +
+ encodeURIComponent(this.$route.path);
this.conn = new WebSocket(this.connexionString);
this.conn.onopen = connectAndPoll;
this.conn.onmessage = this.socketMessageListener;
this.conn.onclose = this.socketCloseListener;
},
mounted: function() {
- ["peopleWrap","infoDiv","newgameDiv"].forEach(eltName => {
+ ["peopleWrap", "infoDiv", "newgameDiv"].forEach(eltName => {
let elt = document.getElementById(eltName);
elt.addEventListener("click", processModalClick);
});
- document.querySelectorAll("#predefinedCadences > button").forEach(
- (b) => { b.addEventListener("click",
- () => { this.newchallenge.cadence = b.innerHTML; }
- )}
- );
+ document.querySelectorAll("#predefinedCadences > button").forEach(b => {
+ b.addEventListener("click", () => {
+ this.newchallenge.cadence = b.innerHTML;
+ });
+ });
const showCtype = localStorage.getItem("type-challenges") || "live";
const showGtype = localStorage.getItem("type-games") || "live";
- this.setDisplay('c', showCtype);
- this.setDisplay('g', showGtype);
+ this.setDisplay("c", showCtype);
+ this.setDisplay("g", showGtype);
},
beforeDestroy: function() {
this.send("disconnect");
methods: {
// Helpers:
send: function(code, obj) {
- if (!!this.conn)
- {
- this.conn.send(JSON.stringify(
- Object.assign(
- {code: code},
- obj,
- )
- ));
+ if (this.conn) {
+ this.conn.send(JSON.stringify(Object.assign({ code: code }, obj)));
}
},
getVname: function(vid) {
const variant = this.st.variants.find(v => v.id == vid);
// this.st.variants might be uninitialized (variant == null)
- return (!!variant ? variant.name : "");
+ return variant ? variant.name : "";
},
filterChallenges: function(type) {
return this.challenges.filter(c => c.type == type);
filterGames: function(type) {
return this.games.filter(g => g.type == type);
},
- classifyObject: function(o) { //challenge or game
- return (o.cadence.indexOf('d') === -1 ? "live" : "corr");
+ classifyObject: function(o) {
+ //challenge or game
+ return o.cadence.indexOf("d") === -1 ? "live" : "corr";
},
setDisplay: function(letter, type, e) {
this[letter + "display"] = type;
- localStorage.setItem("type-" + (letter == 'c' ? "challenges" : "games"), type);
- let elt = !!e
+ localStorage.setItem(
+ "type-" + (letter == "c" ? "challenges" : "games"),
+ type
+ );
+ let elt = e
? e.target
: document.getElementById("btn" + letter.toUpperCase() + type);
elt.classList.add("active");
elt.classList.remove("somethingnew"); //in case of
- if (!!elt.previousElementSibling)
+ if (elt.previousElementSibling)
elt.previousElementSibling.classList.remove("active");
- else
- elt.nextElementSibling.classList.remove("active");
+ else elt.nextElementSibling.classList.remove("active");
},
isGamer: function(sid) {
return this.people[sid].pages.some(p => p.indexOf("/game/") >= 0);
: "Observe";
},
challOrWatch: function(sid) {
- if (this.people[sid].pages.some(p => p == "/"))
- {
+ if (this.people[sid].pages.some(p => p == "/")) {
// Available, in Hall
this.newchallenge.to = this.people[sid].name;
document.getElementById("modalPeople").checked = false;
- doClick("modalNewgame");
- }
- else
- {
+ window.doClick("modalNewgame");
+ } else {
// In some game, maybe playing maybe not: show a random one
let gids = [];
this.people[sid].pages.forEach(p => {
const matchGid = p.match(/[a-zA-Z0-9]+$/);
- if (!!matchGid)
- gids.push(matchGid[0]);
+ if (matchGid) gids.push(matchGid[0]);
});
const gid = gids[Math.floor(Math.random() * gids.length)];
this.showGame(this.games.find(g => g.id == gid));
document.getElementById("peopleBtn").classList.remove("somethingnew");
},
processChat: function(chat) {
- this.send("newchat", {data:chat});
+ this.send("newchat", { data: chat });
},
// Messaging center:
socketMessageListener: function(msg) {
- if (!this.conn)
- return;
+ if (!this.conn) return;
const data = JSON.parse(msg.data);
- switch (data.code)
- {
- case "pollclientsandgamers":
- {
+ switch (data.code) {
+ case "pollclientsandgamers": {
// Since people can be both in Hall and Game,
// need to track "askIdentity" requests:
let identityAsked = {};
data.sockIds.forEach(s => {
const page = s.page || "/";
- if (s.sid != this.st.user.sid && !identityAsked[s.sid])
- {
+ if (s.sid != this.st.user.sid && !identityAsked[s.sid]) {
identityAsked[s.sid] = true;
- this.send("askidentity", {target:s.sid, page:page});
+ this.send("askidentity", { target: s.sid, page: page });
}
if (!this.people[s.sid])
- this.$set(this.people, s.sid, {id:0, name:"", pages:[page]});
+ this.$set(this.people, s.sid, { id: 0, name: "", pages: [page] });
else if (this.people[s.sid].pages.indexOf(page) < 0)
this.people[s.sid].pages.push(page);
- if (!s.page) //peer is in Hall
- this.send("askchallenge", {target:s.sid});
- else //peer is in Game
- this.send("askgame", {target:s.sid, page:page});
+ if (!s.page)
+ //peer is in Hall
+ this.send("askchallenge", { target: s.sid });
+ //peer is in Game
+ else this.send("askgame", { target: s.sid, page: page });
});
break;
}
case "connect":
- case "gconnect":
- {
+ case "gconnect": {
const page = data.page || "/";
// NOTE: player could have been polled earlier, but might have logged in then
// So it's a good idea to ask identity if he was anonymous.
// But only ask game / challenge if currently disconnected.
- if (!this.people[data.from])
- {
- this.$set(this.people, data.from, {name:"", id:0, pages:[page]});
+ if (!this.people[data.from]) {
+ this.$set(this.people, data.from, {
+ name: "",
+ id: 0,
+ pages: [page]
+ });
if (data.code == "connect")
- this.send("askchallenge", {target:data.from});
- else
- this.send("askgame", {target:data.from, page:page});
- }
- else
- {
+ this.send("askchallenge", { target: data.from });
+ else this.send("askgame", { target: data.from, page: page });
+ } else {
// append page if not already in list
if (this.people[data.from].pages.indexOf(page) < 0)
this.people[data.from].pages.push(page);
}
- if (this.people[data.from].id == 0)
- {
+ if (this.people[data.from].id == 0) {
this.newConnect[data.from] = true; //for self multi-connects tests
- this.send("askidentity", {target:data.from, page:page});
+ this.send("askidentity", { target: data.from, page: page });
}
break;
}
case "disconnect":
- case "gdisconnect":
+ case "gdisconnect": {
// If the user reloads the page twice very quickly (experienced with Firefox),
// the first reload won't have time to connect but will trigger a "close" event anyway.
// ==> Next check is required.
- if (!this.people[data.from])
- return;
+ if (!this.people[data.from]) return;
// Disconnect means no more tmpIds:
- if (data.code == "disconnect")
- {
+ if (data.code == "disconnect") {
// Remove the live challenge sent by this player:
ArrayFun.remove(this.challenges, c => c.from.sid == data.from);
- }
- else
- {
+ } else {
// Remove the matching live game if now unreachable
const gid = data.page.match(/[a-zA-Z0-9]+$/)[0];
const gidx = this.games.findIndex(g => g.id == gid);
- if (gidx >= 0)
- {
+ if (gidx >= 0) {
const game = this.games[gidx];
- if (game.type == "live" &&
- game.rids.length == 1 && game.rids[0] == data.from)
- {
+ if (
+ game.type == "live" &&
+ game.rids.length == 1 &&
+ game.rids[0] == data.from
+ ) {
this.games.splice(gidx, 1);
}
}
if (this.people[data.from].pages.length == 0)
this.$delete(this.people, data.from);
break;
+ }
case "killed":
// I logged in elsewhere:
alert(this.st.tr["New connexion detected: tab now offline"]);
//this.conn.close();
this.conn = null;
break;
- case "askidentity":
- {
+ case "askidentity": {
// Request for identification (TODO: anonymous shouldn't need to reply)
const me = {
// Decompose to avoid revealing email
name: this.st.user.name,
sid: this.st.user.sid,
- id: this.st.user.id,
+ id: this.st.user.id
};
- this.send("identity", {data:me, target:data.from});
+ this.send("identity", { data: me, target: data.from });
break;
}
- case "identity":
- {
+ case "identity": {
const user = data.data;
- if (!!user.name) //otherwise anonymous
- {
+ if (user.name) {
+ //otherwise anonymous
// If I multi-connect, kill current connexion if no mark (I'm older)
- if (this.newConnect[user.sid] && user.id > 0
- && user.id == this.st.user.id && user.sid != this.st.user.sid)
- {
- if (!this.killed[this.st.user.sid])
- {
- this.send("killme", {sid:this.st.user.sid});
+ if (
+ this.newConnect[user.sid] &&
+ user.id > 0 &&
+ user.id == this.st.user.id &&
+ user.sid != this.st.user.sid
+ ) {
+ if (!this.killed[this.st.user.sid]) {
+ this.send("killme", { sid: this.st.user.sid });
this.killed[this.st.user.sid] = true;
}
}
- if (user.sid != this.st.user.sid) //I already know my identity...
- {
- this.$set(this.people, user.sid,
- {
- id: user.id,
- name: user.name,
- pages: this.people[user.sid].pages,
- });
+ if (user.sid != this.st.user.sid) {
+ //I already know my identity...
+ this.$set(this.people, user.sid, {
+ id: user.id,
+ name: user.name,
+ pages: this.people[user.sid].pages
+ });
}
}
delete this.newConnect[user.sid];
break;
}
- case "askchallenge":
- {
+ case "askchallenge": {
// Send my current live challenge (if any)
- const cIdx = this.challenges.findIndex(c =>
- c.from.sid == this.st.user.sid && c.type == "live");
- if (cIdx >= 0)
- {
+ const cIdx = this.challenges.findIndex(
+ c => c.from.sid == this.st.user.sid && c.type == "live"
+ );
+ if (cIdx >= 0) {
const c = this.challenges[cIdx];
// NOTE: in principle, should only send targeted challenge to the target.
// But we may not know yet the identity of the target (just name),
// so cannot decide if data.from is the target or not.
- const myChallenge =
- {
+ const myChallenge = {
id: c.id,
from: this.st.user.sid,
to: c.to,
fen: c.fen,
vid: c.vid,
cadence: c.cadence,
- added: c.added,
+ added: c.added
};
- this.send("challenge", {data:myChallenge, target:data.from});
+ this.send("challenge", { data: myChallenge, target: data.from });
}
break;
}
case "challenge": //after "askchallenge"
- case "newchallenge":
- {
+ case "newchallenge": {
// NOTE about next condition: see "askchallenge" case.
const chall = data.data;
- if (!chall.to || (this.people[chall.from].id > 0 &&
- (chall.from == this.st.user.sid || chall.to == this.st.user.name)))
- {
+ if (
+ !chall.to ||
+ (this.people[chall.from].id > 0 &&
+ (chall.from == this.st.user.sid || chall.to == this.st.user.name))
+ ) {
let newChall = Object.assign({}, chall);
newChall.type = this.classifyObject(chall);
newChall.added = Date.now();
let fromValues = Object.assign({}, this.people[chall.from]);
delete fromValues["pages"]; //irrelevant in this context
- newChall.from = Object.assign({sid:chall.from}, fromValues);
+ newChall.from = Object.assign({ sid: chall.from }, fromValues);
newChall.vname = this.getVname(newChall.vid);
this.challenges.push(newChall);
- if ((newChall.type == "live" && this.cdisplay == "corr") ||
- (newChall.type == "corr" && this.cdisplay == "live"))
- {
- document.getElementById("btnC" + newChall.type).classList.add("somethingnew");
+ if (
+ (newChall.type == "live" && this.cdisplay == "corr") ||
+ (newChall.type == "corr" && this.cdisplay == "live")
+ ) {
+ document
+ .getElementById("btnC" + newChall.type)
+ .classList.add("somethingnew");
}
}
break;
}
- case "refusechallenge":
- {
+ case "refusechallenge": {
const cid = data.data;
ArrayFun.remove(this.challenges, c => c.id == cid);
alert(this.st.tr["Challenge declined"]);
break;
}
- case "deletechallenge":
- {
+ case "deletechallenge": {
// NOTE: the challenge may be already removed
const cid = data.data;
ArrayFun.remove(this.challenges, c => c.id == cid);
break;
}
case "game": //individual request
- case "newgame":
- {
+ case "newgame": {
// NOTE: it may be live or correspondance
const game = data.data;
let locGame = this.games.find(g => g.id == game.id);
- if (!locGame)
- {
+ if (!locGame) {
let newGame = game;
newGame.type = this.classifyObject(game);
newGame.vname = this.getVname(game.vid);
- if (!game.score) //if new game from Hall
+ if (!game.score)
+ //if new game from Hall
newGame.score = "*";
newGame.rids = [game.rid];
delete newGame["rid"];
this.games.push(newGame);
- if ((newGame.type == "live" && this.gdisplay == "corr") ||
- (newGame.type == "corr" && this.gdisplay == "live"))
- {
- document.getElementById("btnG" + newGame.type).classList.add("somethingnew");
+ if (
+ (newGame.type == "live" && this.gdisplay == "corr") ||
+ (newGame.type == "corr" && this.gdisplay == "live")
+ ) {
+ document
+ .getElementById("btnG" + newGame.type)
+ .classList.add("somethingnew");
}
- }
- else
- {
+ } else {
// Append rid (if not already in list)
- if (!locGame.rids.includes(game.rid))
- locGame.rids.push(game.rid);
+ if (!locGame.rids.includes(game.rid)) locGame.rids.push(game.rid);
}
break;
}
- case "result":
- {
+ case "result": {
let g = this.games.find(g => g.id == data.gid);
- if (!!g)
- g.score = data.score;
+ if (g) g.score = data.score;
break;
}
- case "startgame":
- {
+ case "startgame": {
// New game just started: data contain all information
const gameInfo = data.data;
if (this.classifyObject(gameInfo) == "live")
this.startNewGame(gameInfo);
- else
- {
- this.infoMessage = this.st.tr["New correspondance game:"] +
- " <a href='#/game/" + gameInfo.id + "'>" +
- "#/game/" + gameInfo.id + "</a>";
+ else {
+ this.infoMessage =
+ this.st.tr["New correspondance game:"] +
+ " <a href='#/game/" +
+ gameInfo.id +
+ "'>" +
+ "#/game/" +
+ gameInfo.id +
+ "</a>";
let modalBox = document.getElementById("modalInfo");
modalBox.checked = true;
}
}
},
socketCloseListener: function() {
- if (!this.conn)
- return;
+ if (!this.conn) return;
this.conn = new WebSocket(this.connexionString);
this.conn.addEventListener("message", this.socketMessageListener);
this.conn.addEventListener("close", this.socketCloseListener);
},
// Challenge lifecycle:
newChallenge: async function() {
+ let error = "";
if (this.newchallenge.vid == "")
- return alert(this.st.tr["Please select a variant"]);
- if (!!this.newchallenge.to && this.newchallenge.to == this.st.user.name)
- return alert(this.st.tr["Self-challenge is forbidden"]);
+ error = this.st.tr["Please select a variant"];
+ else if (!!this.newchallenge.to && this.newchallenge.to == this.st.user.name)
+ error = this.st.tr["Self-challenge is forbidden"];
+ if (error) {
+ alert(error);
+ return;
+ }
const vname = this.getVname(this.newchallenge.vid);
const vModule = await import("@/variants/" + vname + ".js");
window.V = vModule.VariantRules;
- if (!!this.newchallenge.cadence.match(/^[0-9]+$/))
+ if (this.newchallenge.cadence.match(/^[0-9]+$/))
this.newchallenge.cadence += "+0"; //assume minutes, no increment
- const error = checkChallenge(this.newchallenge);
- if (!!error)
- return alert(error);
const ctype = this.classifyObject(this.newchallenge);
- if (ctype == "corr" && this.st.user.id <= 0)
- return alert(this.st.tr["Please log in to play correspondance games"]);
+ error = checkChallenge(this.newchallenge);
+ if (!error && ctype == "corr" && this.st.user.id <= 0)
+ error = this.st.tr["Please log in to play correspondance games"];
+ if (error) {
+ alert(error);
+ return;
+ }
// NOTE: "from" information is not required here
let chall = Object.assign({}, this.newchallenge);
- const finishAddChallenge = (cid) => {
+ const finishAddChallenge = cid => {
chall.id = cid || "c" + getRandString();
// Remove old challenge if any (only one at a time of a given type):
- const cIdx = this.challenges.findIndex(c =>
- (c.from.sid == this.st.user.sid || c.from.id == this.st.user.id) && c.type == ctype);
- if (cIdx >= 0)
- {
+ const cIdx = this.challenges.findIndex(
+ c =>
+ (c.from.sid == this.st.user.sid || c.from.id == this.st.user.id) &&
+ c.type == ctype
+ );
+ if (cIdx >= 0) {
// Delete current challenge (will be replaced now)
- this.send("deletechallenge", {data:this.challenges[cIdx].id});
- if (ctype == "corr")
- {
- ajax(
- "/challenges",
- "DELETE",
- {id: this.challenges[cIdx].id}
- );
+ this.send("deletechallenge", { data: this.challenges[cIdx].id });
+ if (ctype == "corr") {
+ ajax("/challenges", "DELETE", { id: this.challenges[cIdx].id });
}
this.challenges.splice(cIdx, 1);
}
- this.send("newchallenge", {data:Object.assign({from:this.st.user.sid}, chall)});
+ this.send("newchallenge", {
+ data: Object.assign({ from: this.st.user.sid }, chall)
+ });
// Add new challenge:
- chall.from = { //decompose to avoid revealing email
+ chall.from = {
+ //decompose to avoid revealing email
sid: this.st.user.sid,
id: this.st.user.id,
- name: this.st.user.name,
+ name: this.st.user.name
};
chall.added = Date.now();
// NOTE: vname and type are redundant (can be deduced from cadence + vid)
localStorage.setItem("vid", chall.vid);
document.getElementById("modalNewgame").checked = false;
};
- if (ctype == "live")
- {
+ if (ctype == "live") {
// Live challenges have a random ID
finishAddChallenge(null);
- }
- else
- {
+ } else {
// Correspondance game: send challenge to server
- ajax(
- "/challenges",
- "POST",
- { chall: chall },
- response => { finishAddChallenge(response.cid); }
- );
+ ajax("/challenges", "POST", { chall: chall }, response => {
+ finishAddChallenge(response.cid);
+ });
}
},
clickChallenge: function(c) {
- const myChallenge = (c.from.sid == this.st.user.sid //live
- || (this.st.user.id > 0 && c.from.id == this.st.user.id)); //corr
- if (!myChallenge)
- {
- if (c.type == "corr" && this.st.user.id <= 0)
- return alert(this.st.tr["Please log in to accept corr challenges"]);
+ const myChallenge =
+ c.from.sid == this.st.user.sid || //live
+ (this.st.user.id > 0 && c.from.id == this.st.user.id); //corr
+ if (!myChallenge) {
+ if (c.type == "corr" && this.st.user.id <= 0) {
+ alert(this.st.tr["Please log in to accept corr challenges"]);
+ return;
+ }
c.accepted = true;
- if (!!c.to) //c.to == this.st.user.name (connected)
- {
+ if (c.to) {
+ //c.to == this.st.user.name (connected)
// TODO: if special FEN, show diagram after loading variant
c.accepted = confirm("Accept challenge?");
}
- if (c.accepted)
- {
- c.seat = { //again, avoid c.seat = st.user to not reveal email
+ if (c.accepted) {
+ c.seat = {
+ //again, avoid c.seat = st.user to not reveal email
sid: this.st.user.sid,
id: this.st.user.id,
- name: this.st.user.name,
+ name: this.st.user.name
};
this.launchGame(c);
+ } else {
+ this.send("refusechallenge", { data: c.id, target: c.from.sid });
}
- else
- {
- this.send("refusechallenge", {data:c.id, target:c.from.sid});
+ this.send("deletechallenge", { data: c.id });
+ } //my challenge
+ else {
+ if (c.type == "corr") {
+ ajax("/challenges", "DELETE", { id: c.id });
}
- this.send("deletechallenge", {data:c.id});
- }
- else //my challenge
- {
- if (c.type == "corr")
- {
- ajax(
- "/challenges",
- "DELETE",
- {id: c.id}
- );
- }
- this.send("deletechallenge", {data:c.id});
+ this.send("deletechallenge", { data: c.id });
}
// In all cases, the challenge is consumed:
ArrayFun.remove(this.challenges, ch => ch.id == c.id);
const vModule = await import("@/variants/" + c.vname + ".js");
window.V = vModule.VariantRules;
// These game informations will be shared
- let gameInfo =
- {
+ let gameInfo = {
id: getRandString(),
fen: c.fen || V.GenRandInitFen(),
players: shuffle([c.from, c.seat]), //white then black
vid: c.vid,
- cadence: c.cadence,
+ cadence: c.cadence
};
let oppsid = c.from.sid; //may not be defined if corr + offline opp
- if (!oppsid)
- {
- oppsid = Object.keys(this.people).find(sid =>
- this.people[sid].id == c.from.id);
+ if (!oppsid) {
+ oppsid = Object.keys(this.people).find(
+ sid => this.people[sid].id == c.from.id
+ );
}
const notifyNewgame = () => {
- if (!!oppsid) //opponent is online
- this.send("startgame", {data:gameInfo, target:oppsid});
+ if (oppsid)
+ //opponent is online
+ this.send("startgame", { data: gameInfo, target: oppsid });
// Send game info (only if live) to everyone except me in this tab
- this.send("newgame", {data:gameInfo});
+ this.send("newgame", { data: gameInfo });
};
- if (c.type == "live")
- {
+ if (c.type == "live") {
notifyNewgame();
this.startNewGame(gameInfo);
- }
- else //corr: game only on server
- {
+ } //corr: game only on server
+ else {
ajax(
"/games",
"POST",
- {gameInfo: gameInfo, cid: c.id}, //cid useful to delete challenge
+ { gameInfo: gameInfo, cid: c.id }, //cid useful to delete challenge
response => {
gameInfo.id = response.gameId;
notifyNewgame();
moves: [],
clocks: [-1, -1], //-1 = unstarted
initime: [0, 0], //initialized later
- score: "*",
+ score: "*"
});
GameStorage.add(game);
if (this.st.settings.sound >= 1)
- new Audio("/sounds/newgame.mp3").play().catch(err => {});
+ new Audio("/sounds/newgame.mp3").play().catch(() => {});
this.$router.push("/game/" + gameInfo.id);
- },
- },
+ }
+ }
};
</script>
import { store } from "@/store";
import { ajax } from "@/utils/ajax";
export default {
- name: 'my-logout',
+ name: "my-logout",
data: function() {
return {
st: store.state,
- errmsg: "",
+ errmsg: ""
};
},
created: function() {
localStorage.removeItem("myid");
localStorage.removeItem("myname");
ajax("/logout", "GET"); //TODO: listen for errors?
- },
+ }
};
</script>
export default {
name: "my-my-games",
components: {
- GameList,
+ GameList
},
data: function() {
return {
st: store.state,
display: "live",
liveGames: [],
- corrGames: [],
+ corrGames: []
};
},
created: function() {
- GameStorage.getAll((localGames) => {
- localGames.forEach((g) => g.type = this.classifyObject(g));
+ GameStorage.getAll(localGames => {
+ localGames.forEach(g => (g.type = this.classifyObject(g)));
this.liveGames = localGames;
});
- if (this.st.user.id > 0)
- {
- ajax("/games", "GET", {uid: this.st.user.id}, (res) => {
- res.games.forEach((g) => g.type = this.classifyObject(g));
+ if (this.st.user.id > 0) {
+ ajax("/games", "GET", { uid: this.st.user.id }, res => {
+ res.games.forEach(g => (g.type = this.classifyObject(g)));
this.corrGames = res.games;
});
}
setDisplay: function(type, e) {
this.display = type;
localStorage.setItem("type-myGames", type);
- let elt = !!e
- ? e.target
- : document.getElementById(type + "Games");
+ let elt = e ? e.target : document.getElementById(type + "Games");
elt.classList.add("active");
- if (!!elt.previousElementSibling)
+ if (elt.previousElementSibling)
elt.previousElementSibling.classList.remove("active");
- else
- elt.nextElementSibling.classList.remove("active");
+ else elt.nextElementSibling.classList.remove("active");
},
// TODO: classifyObject is redundant (see Hall.vue)
classifyObject: function(o) {
- return (o.cadence.indexOf('d') === -1 ? "live" : "corr");
+ return o.cadence.indexOf("d") === -1 ? "live" : "corr";
},
showGame: function(g) {
this.$router.push("/game/" + g.id);
- },
- },
+ }
+ }
};
</script>
@click="showModalNews"
)
| {{ st.tr["Write news"] }}
- .news(v-for="n,idx in sortedNewsList" :class="{margintop:idx>0}")
+ .news(v-for="n,idx in newsList" :class="{margintop:idx>0}")
span.ndt {{ formatDatetime(n.added) }}
div(v-if="devs.includes(st.user.id)")
button(@click="editNews(n)") {{ st.tr["Edit"] }}
st: store.state,
cursor: 0, //ID of last showed news
hasMore: true, //a priori there could be more news to load
- curnews: {id:0, content:""},
+ curnews: { id: 0, content: "" },
newsList: [],
- infoMsg: "",
+ infoMsg: ""
};
},
created: function() {
- ajax("/news", "GET", {cursor:this.cursor}, (res) => {
- this.newsList = res.newsList;
+ ajax("/news", "GET", { cursor: this.cursor }, res => {
+ this.newsList = res.newsList.sort((n1, n2) => n1.added - n2.added);
const L = res.newsList.length;
- if (L > 0)
- this.cursor = res.newsList[L-1].id;
+ if (L > 0) this.cursor = this.newsList[0].id;
});
},
mounted: function() {
- document.getElementById("newnewsDiv").addEventListener("click", processModalClick);
- },
- computed: {
- sortedNewsList: function() {
- return this.newsList.sort( (n1,n2) => n1.added - n2.added );
- },
+ document
+ .getElementById("newnewsDiv")
+ .addEventListener("click", processModalClick);
},
methods: {
formatDatetime: function(dt) {
const dtObj = new Date(dt);
const timePart = getTime(dtObj);
// Show minutes but not seconds:
- return getDate(dtObj) + " " + timePart.substr(0,timePart.lastIndexOf(":"));
+ return (
+ getDate(dtObj) + " " + timePart.substr(0, timePart.lastIndexOf(":"))
+ );
},
parseHtml: function(txt) {
return !txt.match(/<[/a-zA-Z]+>/)
const newsContent = document.getElementById("newsContent");
// https://stackoverflow.com/questions/995168/textarea-to-resize-based-on-content-length
newsContent.style.height = "1px";
- newsContent.style.height = (10+newsContent.scrollHeight)+"px";
+ newsContent.style.height = 10 + newsContent.scrollHeight + "px";
},
resetCurnews: function() {
this.curnews.id = 0;
},
showModalNews: function() {
this.resetCurnews();
- doClick('modalNews');
+ window.doClick("modalNews");
},
sendNews: function() {
const edit = this.curnews.id > 0;
this.infoMsg = "Processing... Please wait";
- ajax(
- "/news",
- edit ? "PUT" : "POST",
- {news: this.curnews},
- (res) => {
- if (edit)
- {
- let n = this.newsList.find(n => n.id == this.curnews.id);
- if (!!n)
- n.content = this.curnews.content;
- }
- else
- {
- const newNews = {
- content:this.curnews.content,
- added:Date.now(),
- uid: this.st.user.id,
- id: res.id
- };
- this.newsList = this.newsList.concat([newNews]);
- }
- document.getElementById("modalNews").checked = false;
- this.infoMsg = "";
- this.resetCurnews();
+ ajax("/news", edit ? "PUT" : "POST", { news: this.curnews }, res => {
+ if (edit) {
+ let n = this.newsList.find(n => n.id == this.curnews.id);
+ if (n) n.content = this.curnews.content;
+ } else {
+ const newNews = {
+ content: this.curnews.content,
+ added: Date.now(),
+ uid: this.st.user.id,
+ id: res.id
+ };
+ this.newsList = [newNews].concat(this.newsList);
}
- );
+ document.getElementById("modalNews").checked = false;
+ this.infoMsg = "";
+ this.resetCurnews();
+ });
},
editNews: function(n) {
this.curnews.content = n.content;
this.curnews.id = n.id;
// No need for added and uid fields: never updated
- doClick('modalNews');
+ window.doClick("modalNews");
},
deleteNews: function(n) {
- if (confirm(this.st.tr["Are you sure?"]))
- {
+ if (confirm(this.st.tr["Are you sure?"])) {
this.infoMsg = "Processing... Please wait";
- ajax("/news", "DELETE", {id:n.id}, () => {
+ ajax("/news", "DELETE", { id: n.id }, () => {
const nIdx = this.newsList.findIndex(nw => nw.id == n.id);
this.newsList.splice(nIdx, 1);
this.infoMsg = "";
}
},
loadMore: function() {
- ajax("/news", "GET", {cursor:this.cursor}, (res) => {
- if (res.newsList.length > 0)
- {
+ ajax("/news", "GET", { cursor: this.cursor }, res => {
+ if (res.newsList.length > 0) {
this.newsList = this.newsList.concat(res.newsList);
const L = res.newsList.length;
- if (L > 0)
- this.cursor = res.newsList[L-1].id;
- }
- else
- this.hasMore = false;
+ if (L > 0) this.cursor = res.newsList[L - 1].id;
+ } else this.hasMore = false;
});
- },
- },
+ }
+ }
};
</script>
.row(v-else)
.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
#controls
- button#newProblem(onClick="doClick('modalNewprob')")
+ button#newProblem(onClick="window.doClick('modalNewprob')")
| {{ st.tr["New problem"] }}
label(for="checkboxMine") {{ st.tr["My problems"] }}
input#checkboxMine(
th {{ st.tr["Instructions"] }}
th {{ st.tr["Number"] }}
tr(
- v-for="p in sortedProblems"
+ v-for="p in problems"
v-show="displayProblem(p)"
@click="setHrefPid(p)"
)
export default {
name: "my-problems",
components: {
- BaseGame,
+ BaseGame
},
data: function() {
return {
st: store.state,
emptyVar: {
vid: 0,
- vname: "",
+ vname: ""
},
// Problem currently showed, or edited:
curproblem: {
diag: "",
instruction: "",
solution: "",
- showSolution: false,
+ showSolution: false
},
loadedVar: 0, //corresponding to loaded V
selectedVar: 0, //to filter problems based on variant
infoMsg: "",
vr: null, //"variant rules" object initialized from FEN
game: {
- players:[{name:"Problem"},{name:"Problem"}],
- mode: "analyze",
- },
+ players: [{ name: "Problem" }, { name: "Problem" }],
+ mode: "analyze"
+ }
};
},
created: function() {
- ajax("/problems", "GET", (res) => {
- this.problems = res.problems;
+ ajax("/problems", "GET", 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] = ""; //unknwon for now
- else
- p.uname = this.st.user.name;
+ if (p.uid != this.st.user.id) names[p.uid] = "";
+ //unknwon for now
+ else p.uname = this.st.user.name;
});
const showOneIfPid = () => {
const pid = this.$route.query["id"];
- if (!!pid)
- this.showProblem(this.problems.find(p => p.id == pid));
+ if (pid) this.showProblem(this.problems.find(p => p.id == pid));
};
- if (Object.keys(names).length > 0)
- {
- ajax("/users",
- "GET",
- { ids: Object.keys(names).join(",") },
- res2 => {
- res2.users.forEach(u => {names[u.id] = u.name});
- this.problems.forEach(p => p.uname = names[p.uid]);
- showOneIfPid();
- }
- );
- }
- else
- showOneIfPid();
+ if (Object.keys(names).length > 0) {
+ ajax("/users", "GET", { ids: Object.keys(names).join(",") }, res2 => {
+ res2.users.forEach(u => {
+ names[u.id] = u.name;
+ });
+ this.problems.forEach(p => (p.uname = names[p.uid]));
+ showOneIfPid();
+ });
+ } else showOneIfPid();
});
},
mounted: function() {
- document.getElementById("newprobDiv").addEventListener("click", processModalClick);
+ document
+ .getElementById("newprobDiv")
+ .addEventListener("click", processModalClick);
},
watch: {
// st.variants changes only once, at loading from [] to [...]
- "st.variants": function(variantArray) {
+ "st.variants": function() {
// Set problems vname (either all are set or none)
if (this.problems.length > 0 && this.problems[0].vname == "")
this.problems.forEach(p => this.setVname(p));
},
- "$route": function(to, from) {
+ $route: function(to) {
const pid = to.query["id"];
- if (!!pid)
- this.showProblem(this.problems.find(p => p.id == pid));
- else
- this.showOne = false
- },
- },
- computed: {
- sortedProblems: function() {
- // Newest first:
- return this.problems.sort( (p1,p2) => p2.added - p1.added);
- },
+ if (pid) this.showProblem(this.problems.find(p => p.id == pid));
+ else this.showOne = false;
+ }
},
methods: {
setVname: function(prob) {
firstChars: function(text) {
let preparedText = text
// Replace line jumps and <br> by spaces
- .replace(/\n/g, " " )
- .replace(/<br\/?>/g, " " )
+ .replace(/\n/g, " ")
+ .replace(/<br\/?>/g, " ")
.replace(/<[^>]+>/g, "") //remove remaining HTML tags
.replace(/[ ]+/g, " ") //remove series of spaces by only one
.trim();
const maxLength = 32; //arbitrary...
if (preparedText.length > maxLength)
- return preparedText.substr(0,32) + "...";
+ return preparedText.substr(0, 32) + "...";
return preparedText;
},
copyProblem: function(p1, p2) {
- for (let key in p1)
- p2[key] = p1[key];
+ for (let key in p1) p2[key] = p1[key];
},
setHrefPid: function(p) {
// Change href => $route changes, watcher notices, call showProblem
},
changeVariant: function(prob) {
this.setVname(prob);
- this.loadVariant(
- prob.vid,
- () => {
- // Set FEN if possible (might not be correct yet)
- if (V.IsGoodFen(prob.fen))
- this.setDiagram(prob);
- }
- );
+ this.loadVariant(prob.vid, () => {
+ // Set FEN if possible (might not be correct yet)
+ if (V.IsGoodFen(prob.fen)) this.setDiagram(prob);
+ });
},
loadVariant: async function(vid, cb) {
// Condition: vid is a valid variant ID
const parsedFen = V.ParseFen(prob.fen);
const args = {
position: parsedFen.position,
- orientation: parsedFen.turn,
+ orientation: parsedFen.turn
};
prob.diag = getDiagram(args);
},
displayProblem: function(p) {
- return ((this.selectedVar == 0 || p.vid == this.selectedVar) &&
- ((this.onlyMines && p.uid == this.st.user.id)
- || (!this.onlyMines && p.uid != this.st.user.id)));
+ return (
+ (this.selectedVar == 0 || p.vid == this.selectedVar) &&
+ ((this.onlyMines && p.uid == this.st.user.id) ||
+ (!this.onlyMines && p.uid != this.st.user.id))
+ );
},
showProblem: function(p) {
- this.loadVariant(
- p.vid,
- () => {
- // The FEN is already checked at this stage:
- this.vr = new V(p.fen);
- this.game.vname = p.vname;
- this.game.mycolor = this.vr.turn; //diagram orientation
- this.game.fen = p.fen;
- this.$set(this.game, "fenStart", p.fen);
- this.copyProblem(p, this.curproblem);
- this.showOne = true;
- }
- );
+ this.loadVariant(p.vid, () => {
+ // The FEN is already checked at this stage:
+ this.vr = new V(p.fen);
+ this.game.vname = p.vname;
+ this.game.mycolor = this.vr.turn; //diagram orientation
+ this.game.fen = p.fen;
+ this.$set(this.game, "fenStart", p.fen);
+ this.copyProblem(p, this.curproblem);
+ this.showOne = true;
+ });
},
sendProblem: function() {
const error = checkProblem(this.curproblem);
- if (!!error)
- return alert(error);
+ if (error) {
+ alert(error);
+ return;
+ }
const edit = this.curproblem.id > 0;
this.infoMsg = "Processing... Please wait";
ajax(
"/problems",
edit ? "PUT" : "POST",
- {prob: this.curproblem},
- (ret) => {
- if (edit)
- {
+ { prob: this.curproblem },
+ ret => {
+ if (edit) {
let editedP = this.problems.find(p => p.id == this.curproblem.id);
this.copyProblem(this.curproblem, editedP);
- }
- else //new problem
- {
+ } //new problem
+ else {
let newProblem = Object.assign({}, this.curproblem);
newProblem.id = ret.id;
newProblem.uid = this.st.user.id;
);
},
editProblem: function(prob) {
- if (!prob.diag)
- this.setDiagram(prob); //possible because V is loaded at this stage
+ if (!prob.diag) this.setDiagram(prob); //possible because V is loaded at this stage
this.copyProblem(prob, this.curproblem);
- doClick('modalNewprob');
+ window.doClick("modalNewprob");
},
deleteProblem: function(prob) {
- if (confirm(this.st.tr["Are you sure?"]))
- {
- ajax("/problems", "DELETE", {id:prob.id}, () => {
+ if (confirm(this.st.tr["Are you sure?"])) {
+ ajax("/problems", "DELETE", { id: prob.id }, () => {
ArrayFun.remove(this.problems, p => p.id == prob.id);
this.backToList();
});
}
- },
- },
+ }
+ }
};
</script>
@media screen and (max-width: 767px)
#topPage
text-align: center
-
</style>
import { store } from "@/store";
import { getDiagram } from "@/utils/printDiagram";
export default {
- name: 'my-rules',
+ name: "my-rules",
components: {
- ComputerGame,
+ ComputerGame
},
data: function() {
return {
vname: "",
mode: "versus",
fen: "",
- score: "*",
+ score: "*"
}
};
},
watch: {
- "$route": function(newRoute) {
+ $route: function(newRoute) {
this.re_setVariant(newRoute.params["vname"]);
- },
+ }
},
created: function() {
// NOTE: variant cannot be set before store is initialized
},
computed: {
content: function() {
- if (!this.gameInfo.vname)
- return ""; //variant not set yet
+ if (!this.gameInfo.vname) return ""; //variant not set yet
// (AJAX) Request to get rules content (plain text, HTML)
- return require("raw-loader!@/translations/rules/" +
- this.gameInfo.vname + "/" + this.st.lang + ".pug")
- // Next two lines fix a weird issue after last update (2019-11)
- .replace(/\\n/g, " ").replace(/\\"/g, '"')
- .replace('module.exports = "', '').replace(/"$/, "")
- .replace(/(fen:)([^:]*):/g, this.replaceByDiag);
- },
+ return (
+ require("raw-loader!@/translations/rules/" +
+ this.gameInfo.vname +
+ "/" +
+ this.st.lang +
+ ".pug")
+ // Next two lines fix a weird issue after last update (2019-11)
+ .replace(/\\n/g, " ")
+ .replace(/\\"/g, '"')
+ .replace('module.exports = "', "")
+ .replace(/"$/, "")
+ .replace(/(fen:)([^:]*):/g, this.replaceByDiag)
+ );
+ }
},
methods: {
clickReadRules: function() {
- if (this.display != "rules")
- this.display = "rules";
- else if (this.gameInProgress)
- this.display = "computer";
+ if (this.display != "rules") this.display = "rules";
+ else if (this.gameInProgress) this.display = "computer";
},
parseFen(fen) {
const fenParts = fen.split(" ");
position: fenParts[0],
marks: fenParts[1],
orientation: fenParts[2],
- shadow: fenParts[3],
+ shadow: fenParts[3]
};
},
// Method to replace diagrams in loaded HTML
this.gameInfo.vname = vname;
},
startGame: function(mode) {
- if (this.gameInProgress)
- return;
+ if (this.gameInProgress) return;
this.gameInProgress = true;
this.display = "computer";
this.gameInfo.mode = mode;
this.gameInProgress = false;
},
gotoAnalyze: function() {
- this.$router.push("/analyse/" + this.gameInfo.vname
- + "/?fen=" + V.GenRandInitFen());
- },
- },
+ this.$router.push(
+ "/analyse/" + this.gameInfo.vname + "/?fen=" + V.GenRandInitFen()
+ );
+ }
+ }
};
</script>
data: function() {
return {
curPrefix: "",
- st: store.state,
+ st: store.state
};
},
computed: {
- filteredVariants: function () {
- const capitalizedPrefix = this.curPrefix.replace(/^\w/, c => c.toUpperCase());
+ filteredVariants: function() {
+ const capitalizedPrefix = this.curPrefix.replace(/^\w/, c =>
+ c.toUpperCase()
+ );
const variants = this.st.variants
- .filter( v => {
- return v.name.startsWith(capitalizedPrefix);
- })
- .map( v => {
- return {
- name: v.name,
- desc: v.description,
- };
- })
- .sort((a,b) => {
- return a.name.localeCompare(b.name);
- });
+ .filter(v => {
+ return v.name.startsWith(capitalizedPrefix);
+ })
+ .map(v => {
+ return {
+ name: v.name,
+ desc: v.description
+ };
+ })
+ .sort((a, b) => {
+ return a.name.localeCompare(b.name);
+ });
return variants;
- },
+ }
},
methods: {
getLink: function(vname) {
return "/variants/" + vname;
- },
- },
+ }
+ }
};
</script>
output: {
// Fix "window is not defined" issues with web worker.
// https://github.com/webpack/webpack/issues/6642
- globalObject: 'this',
- },
- },
+ globalObject: "this"
+ }
+ }
};