+ setOtherVariables(fen) {
+ super.setOtherVariables(fen);
+ this.subTurn = 1;
+ // Local stack of "action moves"
+ this.amoves = [];
+ const amove = V.ParseFen(fen).amove;
+ if (cmove == "-") this.amoves.push(null);
+ else {
+ const amoveParts = amove.split("/");
+ let amove = {
+ // No need for start & end
+ appear: [],
+ vanish: []
+ };
+ [0, 1].map(i => {
+ amoveParts[0].split(".").forEach(av => {
+ // Format is "bpe3"
+ const xy = V.SquareToCoords(av.substr(2));
+ move[i == 0 ? "appear" : "vanish"].push(
+ new PiPo({
+ x: xy.x,
+ y: xy.y,
+ c: av[0],
+ p: av[1]
+ })
+ );
+ });
+ });
+ this.amoves.push(move);
+ }
+ }
+
+ static ParseFen(fen) {
+ return Object.assign(
+ ChessRules.ParseFen(fen),
+ { cmove: fen.split(" ")[4] }
+ );
+ }
+
+ static IsGoodFen(fen) {
+ if (!ChessRules.IsGoodFen(fen)) return false;
+ const fenParts = fen.split(" ");
+ if (fenParts.length != 6) return false;
+ if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/))
+ return false;
+ return true;
+ }
+
+ getAmove(move) {
+ if (move.appear.length == 2 && move.vanish.length == 2)
+ return { appear: move.appear, vanish: move.vanish };
+ return null;
+ }
+
+ // TODO: this.firstMove + rooks location in setOtherVariables
+ // only rooks location in FEN (firstMove is forgotten if quit game and come back)
+ doClick(square) {
+ // If subTurn == 2 && square is the final square of last move,
+ // then return an empty move
+ if (
+ this.subTurn == 2 &&
+ square.x == this.firstMove.end.x &&
+ square.y == this.firstMove.end.y
+ ) {
+ return {
+ appear: [],
+ vanish: []
+ };
+ }
+ return null;
+ }
+
+ canTake() {
+ // Captures don't occur (only pulls & pushes)
+ return false;
+ }
+
+ // "pa" : piece (as a square) doing this push/pull action
+ getActionMoves([sx, sy], [ex, ey], pa) {
+ const color = this.getColor(sx, sy);
+ const lastRank = (color == 'w' ? 0 : 7);
+ const piece = this.getPiece(sx, sy);
+ let moves = [];
+ if (ex == lastRank && piece == V.PAWN) {
+ // Promotion by push or pull
+ V.PawnSpecs.promotions.forEach(p => {
+ let move = super.getBasicMove([sx, sy], [ex, ey], { c: color, p: p });
+ moves.push(move);
+ });
+ } else moves.push(super.getBasicMove([sx, sy], [ex, ey]));
+ const actionType =
+ (
+ Math.abs(pa[0] - sx) < Math.abs(pa[0] - ex) ||
+ Math.abs(pa[1] - sy) < Math.abs(pa[1] - ey)
+ )
+ ? "push"
+ : "pull";
+ moves.forEach(m => m.action = [{ by: pa, type: actionType }]);
+ return moves;
+ }
+
+ // TODO: if type is given, consider only actions of this type
+ getPactions(sq, color, type) {
+ const [x, y] = sq;
+ let moves = [];
+ let squares = {};
+ if (!by) {
+ const oppCol = V.GetOppCol(color);
+ // Look in all directions for a "color" piece
+ for (let step of V.steps[V.KNIGHT]) {
+ const xx = x + step[0],
+ yy = y + step[1];
+ if (
+ V.OnBoard(xx, yy) &&
+ this.getPiece(xx, yy) == V.KNIGHT &&
+ this.getColor(xx, yy) == color
+ ) {
+ const px = x - step[0],
+ py = y - step[1];
+ if (V.OnBoard(px, py)) {
+ if (this.board[px][py] == V.EMPTY) {
+ const hash = "s" + px + py;
+ if (!squares[hash]) {
+ squares[hash] = true;
+ Array.prototype.push.apply(
+ moves,
+ this.getActionMoves([x, y], [px, py], [xx, yy])
+ );
+ }
+ else { //add piece doing action
+ }
+ }
+ } else {
+ const hash = "s" + xx + yy;
+ if (!squares[hash]) {
+ squares[hash] = true;
+ moves.push(
+ new Move({
+ start: { x: x, y: y },
+ end: { x: xx, y: yy },
+ appear: [],
+ vanish: [
+ new PiPo({
+ x: x,
+ y: y,
+ p: this.getPiece(x, y),
+ c: oppCol
+ })
+ ]
+ })
+ );
+ }
+ }
+ }
+ }
+ for (let step in V.steps[V.ROOK]) {
+ // (+ if color is ours, pawn pushes) king, rook and queen
+ // --> pawns special case can push from a little distance if on 2nd rank (or 1st rank)
+ }
+ for (let step in V.steps[V.BISHOP]) {
+ // King, bishop, queen, and possibly pawns attacks (if color is enemy)
+ }
+ }
+ return moves;
+ }
+