tryChangeTurn(move) {
     if (this.isLastMove(move)) {
-      this.turn = (this.turn == 'w' ? 'b' : 'w');
+      this.turn = C.GetOppTurn(this.turn);
       this.movesCount++;
       this.subTurn = 1;
     }
 
   playVisual(move, r) {
     move.vanish.forEach(v => {
-      this.g_pieces[v.x][v.y].remove();
+      if (this.g_pieces[v.x][v.y]) //can be null (e.g. Apocalypse)
+        this.g_pieces[v.x][v.y].remove();
       this.g_pieces[v.x][v.y] = null;
     });
     let chessboard =
 
   animateMoving(start, end, drag, segments, cb) {
     let initPiece = this.getDomPiece(start.x, start.y);
+    if (!initPiece) { //TODO: shouldn't occur!
+      cb();
+      return;
+    }
     // NOTE: cloning often not required, but light enough, and simpler
     let movingPiece = initPiece.cloneNode();
     initPiece.style.opacity = "0";
 
   {name: 'Avalam', desc: 'Build towers'},
   {name: 'Avalanche', desc: 'Pawnfalls'},
   {name: 'Balaklava', desc: 'Meet the Mammoth'},
+  {name: "Balanced", desc: "Balanced chess"},
   {name: 'Bario', desc: 'A quantum story'},
-  {name: "Balanced", desc: "balanced chess"},
   {name: 'Baroque', desc: 'Exotic captures'},
   {name: "Benedict", desc: "Change colors"},
   {name: 'Berolina', desc: 'Pawns move diagonally'},
 
 piece.black.rook {
-  background-image: url('/variants/Alapo/pieces/black_SQUARE.svg');
+  background-image: url('/pieces/Alapo/black_SQUARE.svg');
 }
 piece.black.bishop {
-  background-image: url('/variants/Alapo/pieces/black_TRIANGLE.svg');
+  background-image: url('/pieces/Alapo/black_TRIANGLE.svg');
 }
 piece.black.bishop_inv {
-  background-image: url('/variants/Alapo/pieces/black_TRIANGLE_inv.svg');
+  background-image: url('/pieces/Alapo/black_TRIANGLE_inv.svg');
 }
 piece.black.queen {
-  background-image: url('/variants/Alapo/pieces/black_CIRCLE.svg');
+  background-image: url('/pieces/Alapo/black_CIRCLE.svg');
 }
 piece.black.babyrook {
-  background-image: url('/variants/Alapo/pieces/black_square.svg');
+  background-image: url('/pieces/Alapo/black_square.svg');
 }
 piece.black.babybishop {
-  background-image: url('/variants/Alapo/pieces/black_triangle.svg');
+  background-image: url('/pieces/Alapo/black_triangle.svg');
 }
 piece.black.babybishop_inv {
-  background-image: url('/variants/Alapo/pieces/black_triangle_inv.svg');
+  background-image: url('/pieces/Alapo/black_triangle_inv.svg');
 }
 piece.black.babyqueen {
-  background-image: url('/variants/Alapo/pieces/black_circle.svg');
+  background-image: url('/pieces/Alapo/black_circle.svg');
 }
 
 piece.white.rook {
-  background-image: url('/variants/Alapo/pieces/white_SQUARE.svg');
+  background-image: url('/pieces/Alapo/white_SQUARE.svg');
 }
 piece.white.bishop {
-  background-image: url('/variants/Alapo/pieces/white_TRIANGLE.svg');
+  background-image: url('/pieces/Alapo/white_TRIANGLE.svg');
 }
 piece.white.bishop_inv {
-  background-image: url('/variants/Alapo/pieces/white_TRIANGLE_inv.svg');
+  background-image: url('/pieces/Alapo/white_TRIANGLE_inv.svg');
 }
 piece.white.queen {
-  background-image: url('/variants/Alapo/pieces/white_CIRCLE.svg');
+  background-image: url('/pieces/Alapo/white_CIRCLE.svg');
 }
 piece.white.babyrook {
-  background-image: url('/variants/Alapo/pieces/white_square.svg');
+  background-image: url('/pieces/Alapo/white_square.svg');
 }
 piece.white.babybishop {
-  background-image: url('/variants/Alapo/pieces/white_triangle.svg');
+  background-image: url('/pieces/Alapo/white_triangle.svg');
 }
 piece.white.babybishop_inv {
-  background-image: url('/variants/Alapo/pieces/white_triangle_inv.svg');
+  background-image: url('/pieces/Alapo/white_triangle_inv.svg');
 }
 piece.white.babyqueen {
-  background-image: url('/variants/Alapo/pieces/white_circle.svg');
+  background-image: url('/pieces/Alapo/white_circle.svg');
 }
 
-import GoRules from "/variants/Weiqi/class.js";
+import WeiqiRules from "/variants/Weiqi/class.js";
 import Move from "/utils/Move.js";
 import PiPo from "/utils/PiPo.js";
 import {ArrayFun} from "/utils/array.js";
 
     if (height == 5)
       return [];
     let moves = [];
-    for (let s of this.pieces()['b'].moves[0].steps) {
+    for (let s of this.pieces(this.turn, x, y)['b'].both[0].steps) {
       const [i, j] = [x + s[0], y + s[1]];
       if (
         this.onBoard(i, j) &&
 
 piece.white.stack {
-  background-image: url('/variants/Avalam/pieces/white_stack.svg');
+  background-image: url('/pieces/Avalam/white_stack.svg');
 }
 piece.white.stack2 {
-  background-image: url('/variants/Avalam/pieces/white_stack2.svg');
+  background-image: url('/pieces/Avalam/white_stack2.svg');
 }
 piece.white.stack3 {
-  background-image: url('/variants/Avalam/pieces/white_stack3.svg');
+  background-image: url('/pieces/Avalam/white_stack3.svg');
 }
 piece.white.stack4 {
-  background-image: url('/variants/Avalam/pieces/white_stack4.svg');
+  background-image: url('/pieces/Avalam/white_stack4.svg');
 }
 piece.white.stack5 {
-  background-image: url('/variants/Avalam/pieces/white_stack5.svg');
+  background-image: url('/pieces/Avalam/white_stack5.svg');
 }
 
 piece.black.stack {
-  background-image: url('/variants/Avalam/pieces/black_stack.svg');
+  background-image: url('/pieces/Avalam/black_stack.svg');
 }
 piece.black.stack2 {
-  background-image: url('/variants/Avalam/pieces/black_stack2.svg');
+  background-image: url('/pieces/Avalam/black_stack2.svg');
 }
 piece.black.stack3 {
-  background-image: url('/variants/Avalam/pieces/black_stack3.svg');
+  background-image: url('/pieces/Avalam/black_stack3.svg');
 }
 piece.black.stack4 {
-  background-image: url('/variants/Avalam/pieces/black_stack4.svg');
+  background-image: url('/pieces/Avalam/black_stack4.svg');
 }
 piece.black.stack5 {
-  background-image: url('/variants/Avalam/pieces/black_stack5.svg');
+  background-image: url('/pieces/Avalam/black_stack5.svg');
 }
 
 .board-sq {
 
   static get Options() {
     return {
       select: C.Options.select,
+      input: [
+        {
+          label: "Balanced",
+          variable: "balanced",
+          type: "checkbox",
+          defaut: false
+        }
+      ],
       styles: [
         "atomic",
         "cannibal",
     return false;
   }
 
-  postPlay(move) {
+  tryChangeTurn(move) {
     const color = this.turn;
     const oppCol = C.GetOppTurn(color);
-    this.promotion = (
-      this.subTurn == 2 &&
-      move.end.x == (oppCol == 'w' ? 0 : this.size.x - 1) &&
-      move.vanish[0].p == 'p'
-    );
-    if (this.subTurn == 0) {
-      this.subTurn++;
-      if (!this.atLeastOneMove(color)) {
-        move.result = "1/2"; //avoid re-computation
-        this.turn = oppCol;
+    const incrementTurn = () => {
+      if (this.options["balanced"] && this.movesCount == 0) {
+        // No pawn push on move 1:
+        return true;
       }
-    }
-    else if (this.subTurn == 2) {
-      this.turn = oppCol;
-      this.subTurn = this.promotion ? 0 : 1;
-    }
-    else { //subTurn == 1, usual case
+      this.promotion = (
+        this.subTurn == 2 &&
+        move.end.x == (oppCol == 'w' ? 0 : this.size.x - 1) &&
+        move.vanish[0].p == 'p'
+      );
+      if (this.subTurn == 0) {
+        this.subTurn++;
+        if (!this.atLeastOneMove(color)) {
+          move.result = "1/2"; //avoid re-computation
+          return true;
+        }
+        return false;
+      }
+      if (this.subTurn == 2) {
+        this.subTurn = (this.promotion ? 0 : 1);
+        return true;
+      }
+      // subTurn == 1, usual case
       const kingCapture = this.searchKingPos(oppCol).length == 0;
       if (kingCapture)
         move.result = (color == 'w' ? "1-0" : "0-1");
-      if (!kingCapture && this.atLeastOnePawnPush(oppCol))
-        this.subTurn++;
-      else {
-        this.turn = oppCol;
-        this.subTurn = this.promotion ? 0 : 1;
+      if (kingCapture || !this.atLeastOnePawnPush(oppCol)) {
+        this.subTurn = (this.promotion ? 0 : 1);
+        return true;
       }
+      // A pawn push is possible: usual case
+      this.subTurn++;
+      return false;
+    };
+    if (incrementTurn()) {
+      this.turn = oppCol;
+      this.movesCount++;
     }
   }
 
 
 <p>
   After each normal move, push an opponent pawn one square forward.
   If the pawn promotes, its owner will select into which piece on next turn.
+  In balanced Avalanche, white has no pawn push at the first move.
 </p>
 
 <p>The goal is either to checkmate or to capture the enemy king.</p>
 
 
   pieces(color, x, y) {
     let res = super.pieces(color, x, y);
-    const knightSpec = res['n'];
+    const knightSpecMoves = res['n'].both;
     delete res['n'];
     res['m'] = {
       "class": "mammoth",
         }
       ]
     };
-    ['p', 'r', 'b', 'm', 'q'].forEach(p => res[p].moves = knightSpec.moves);
+    ['p', 'r', 'b', 'm', 'q'].forEach(p => {
+      if (!res[p].moves)
+        res[p].moves = [];
+      Array.prototype.push.apply(res[p].moves, knightSpecMoves);
+    });
     return res;
   }
 
     return {
       fen: s.b.join("") + "/pppppppp/8/8/8/8/PPPPPPPP/" +
            s.w.join("").toUpperCase(),
-      o: {}
+      o: {flags: s.flags}
     };
   }
 
 
 @import url("/base_pieces.css");
 
 piece.white.mammoth {
-  background-image: url('/variants/Balaklava/pieces/white_mammoth.svg');
+  background-image: url('/pieces/Balaklava/white_mammoth.svg');
 }
 piece.black.mammoth {
-  background-image: url('/variants/Balaklava/pieces/black_mammoth.svg');
+  background-image: url('/pieces/Balaklava/black_mammoth.svg');
 }
 
-import AbstractSpecialCaptureRules from "/variants/_SpecialCaptures.js";
+import AbstractSpecialCaptureRules from "/variants/_SpecialCaptures/class.js";
 import {FenUtil} from "/utils/setupPieces.js";
 import {Random} from "/utils/alea.js";
 
     const piece = this.getPiece(x, y);
     const color = this.getColor(x, y);
     const oppCol = C.GetOppTurn(color);
-    const adjacentSteps = this.pieces()['k'].moves[0].steps;
+    const adjacentSteps = this.pieces()['k'].both[0].steps;
     for (let step of adjacentSteps) {
       const [i, j] = [x + step[0], this.getY(y + step[1])];
       if (
 
 @import url("/variants/_SpecialCaptures/style.css");
 
 piece.white.immobilizer {
-  background-image: url('/variants/Baroque/pieces/white_immobilizer.svg');
+  background-image: url('/pieces/Baroque/white_immobilizer.svg');
 }
 piece.black.immobilizer {
-  background-image: url('/variants/Baroque/pieces/black_immobilizer.svg');
+  background-image: url('/pieces/Baroque/black_immobilizer.svg');
 }
 
 @import url("/base_pieces.css");
 
 piece.black.cleopatra {
-  background-image: url('/variants/Benedict/pieces/black_cleopatra.svg');
+  background-image: url('/pieces/Benedict/black_cleopatra.svg');
 }
 piece.white.cleopatra {
-  background-image: url('/variants/Benedict/pieces/white_cleopatra.svg');
+  background-image: url('/pieces/Benedict/white_cleopatra.svg');
 }
 
 @import url("/base_pieces.css");
 
 piece.egg {
-  background-image: url('/variants/Chakart/pieces/egg.svg');
+  background-image: url('/pieces/Chakart/egg.svg');
 }
 
 piece.mushroom {
-  background-image: url('/variants/Chakart/pieces/mushroom.svg');
+  background-image: url('/pieces/Chakart/mushroom.svg');
 }
 
 piece.banana {
-  background-image: url('/variants/Chakart/pieces/banana.svg');
+  background-image: url('/pieces/Chakart/banana.svg');
 }
 
 piece.bomb {
-  background-image: url('/variants/Chakart/pieces/bomb.svg');
+  background-image: url('/pieces/Chakart/bomb.svg');
 }
 
 piece.white.invisible {
 }
 
 piece.remote-capture {
-  background-image: url('/variants/Chakart/pieces/shell.svg');
+  background-image: url('/pieces/Chakart/shell.svg');
 }
 
 piece.white.mystery {
 
 @import url("/base_pieces.css");
 
 piece.checkered.pawn {
-  background-image: url('/variants/Checkered/pieces/cp.svg');
+  background-image: url('/pieces/Checkered/cp.svg');
 }
 piece.checkered.rook {
-  background-image: url('/variants/Checkered/pieces/cr.svg');
+  background-image: url('/pieces/Checkered/cr.svg');
 }
 piece.checkered.knight {
-  background-image: url('/variants/Checkered/pieces/cn.svg');
+  background-image: url('/pieces/Checkered/cn.svg');
 }
 piece.checkered.bishop {
-  background-image: url('/variants/Checkered/pieces/cb.svg');
+  background-image: url('/pieces/Checkered/cb.svg');
 }
 piece.checkered.queen {
-  background-image: url('/variants/Checkered/pieces/cq.svg');
+  background-image: url('/pieces/Checkered/cq.svg');
 }
 
 div.info-text {
 
 }
 
 piece.white.stone {
-  background-image: url('/variants/Weiqi/pieces/black_stone.svg');
+  background-image: url('/pieces/Weiqi/black_stone.svg');
 }
 piece.black.stone, piece.white.stone.one-color {
-  background-image: url('/variants/Weiqi/pieces/white_stone.svg');
+  background-image: url('/pieces/Weiqi/white_stone.svg');
 }
 
 button.pass-btn {
 
 @import url("/base_pieces.css");
 
 piece.black.antiking {
-  background-image: url('/variants/_Antiking/pieces/black_antiking.svg');
+  background-image: url('/pieces/_Antiking/black_antiking.svg');
 }
 piece.white.antiking {
-  background-image: url('/variants/_Antiking/pieces/white_antiking.svg');
+  background-image: url('/pieces/_Antiking/white_antiking.svg');
 }
 
 piece.black.pawn {
-  background-image: url('/variants/_Berolina/pieces/black_pawn.svg');
+  background-image: url('/pieces/_Berolina/black_pawn.svg');
 }
 piece.white.pawn {
-  background-image: url('/variants/_Berolina/pieces/white_pawn.svg');
+  background-image: url('/pieces/_Berolina/white_pawn.svg');
 }
 
         [i, j] = [i + step[0], this.getY(j + step[1])];
         while (this.onBoard(i, j) && this.board[i][j] == "") {
           let mv = this.getBasicMove([x, y], [i, j]);
-          Array.prorotype.push.apply(mv.vanish, vanished);
+          Array.prototype.push.apply(mv.vanish, vanished);
           moves.push(mv);
           [i, j] = [i + step[0], this.getY(j + step[1])];
         }
   getChameleonCaptures(moves, pushPullType, onlyOneJump) {
     const [x, y] = [moves[0].start.x, moves[0].start.y];
     moves = moves.concat(
-      this.getKnightCaptures([x, y], "asChameleon", onlyOneJump));
+      this.getLeaperCaptures([x, y], "asChameleon", onlyOneJump));
     // No "king capture" because king cannot remain under check
     this.addPincerCaptures(moves, "asChameleon");
     this.addCoordinatorCaptures(moves, "asChameleon");
     moves.forEach(m => {
       const [ex, ey] = [m.end.x, m.end.y];
       const step = [
-        ex != x ? (ex - x) / Math.abs(ex - x) : 0,
-        ey != y ? (ey - y) / Math.abs(ey - y) : 0
+        ex != sx ? (ex - sx) / Math.abs(ex - sx) : 0,
+        ey != sy ? (ey - sy) / Math.abs(ey - sy) : 0
       ];
       let vanishPull, vanishPush;
       if (type != "pull") {
         }
       }
       if (capturingPullDir[step[0] + "." + step[1]]) {
-        const [bi, bj] = [x - step[0], this.getY(y - step[1])];
+        const [bi, bj] = [sx - step[0], this.getY(sy - step[1])];
         vanishPull =
           new PiPo({x: bi, y: bj, p: this.getPiece(bi, bj), c: oppCol});
       }
 
 .piece.white.push-action {
-  background-image: url('/variants/_SpecialCaptures/pieces/capture_push.svg');
+  background-image: url('/pieces/_SpecialCaptures/capture_push.svg');
 }
 .piece.white.pull-action {
-  background-image: url('/variants/_SpecialCaptures/pieces/capture_pull.svg');
+  background-image: url('/pieces/_SpecialCaptures/capture_pull.svg');
 }