+ // Check if FEN describe a position
+ static IsGoodFen(fen)
+ {
+ const fenParsed = V.ParseFen(fen);
+ // 1) Check position
+ if (!V.IsGoodPosition(fenParsed.position))
+ return false;
+ // 2) Check turn
+ if (!fenParsed.turn || !V.IsGoodTurn(fenParsed.turn))
+ return false;
+ // 3) Check moves count
+ if (!fenParsed.movesCount || !(parseInt(fenParsed.movesCount) >= 0))
+ return false;
+ // 4) Check flags
+ if (V.HasFlags && (!fenParsed.flags || !V.IsGoodFlags(fenParsed.flags)))
+ return false;
+ // 5) Check 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;
+ const rows = position.split("/");
+ 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
+ {
+ const num = parseInt(row[i]);
+ if (isNaN(num))
+ return false;
+ sumElts += num;
+ }
+ }
+ if (sumElts != V.size.y)
+ return false;
+ }
+ return true;
+ }
+
+ // For FEN checking
+ static IsGoodTurn(turn)
+ {
+ return ["w","b"].includes(turn);
+ }
+
+ // For FEN checking
+ 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;
+ }
+ return true;
+ }
+
+ // 3 --> d (column number to letter)
+ static CoordToColumn(colnum)
+ {
+ return String.fromCharCode(97 + colnum);
+ }
+
+ // d --> 3 (column letter to number)
+ static ColumnToCoord(column)
+ {
+ return column.charCodeAt(0) - 97;
+ }
+
+ // a4 --> {x:3,y:0}
+ static SquareToCoords(sq)
+ {
+ return {
+ // NOTE: column is always one char => max 26 columns
+ // row is counted from black side => subtraction
+ x: V.size.x - parseInt(sq.substr(1)),
+ y: sq[0].charCodeAt() - 97
+ };
+ }
+
+ // {x:0,y:4} --> e8
+ static CoordsToSquare(coords)
+ {
+ return V.CoordToColumn(coords.y) + (V.size.x - coords.x);
+ }
+
+ // Aggregates flags into one object
+ aggregateFlags()
+ {
+ return this.castleFlags;
+ }
+
+ // Reverse operation
+ disaggregateFlags(flags)
+ {
+ this.castleFlags = flags;
+ }
+
+ // En-passant square, if any
+ getEpSquare(moveOrSquare)
+ {
+ if (!moveOrSquare)
+ return undefined;
+ if (typeof moveOrSquare === "string")
+ {
+ const square = moveOrSquare;
+ 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];
+ // TODO: next conditions are first for Atomic, and third for Checkered
+ if (move.appear.length > 0 && move.appear[0].p == V.PAWN && ["w","b"].includes(move.appear[0].c) && Math.abs(sx - ex) == 2)
+ {
+ return {
+ x: (sx + ex)/2,
+ y: sy
+ };
+ }
+ return undefined; //default
+ }
+
+ // Can thing on square1 take thing on square2
+ canTake([x1,y1], [x2,y2])
+ {
+ return this.getColor(x1,y1) !== this.getColor(x2,y2);
+ }