Switching chess almost OK, Extinction seems OK, Crazyhouse advanced draft but to...
authorBenjamin Auder <benjamin.auder@somewhere>
Wed, 28 Nov 2018 02:07:29 +0000 (03:07 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Wed, 28 Nov 2018 02:07:29 +0000 (03:07 +0100)
TODO
public/javascripts/base_rules.js
public/javascripts/components/game.js
public/javascripts/variants/Crazyhouse.js
public/javascripts/variants/Extinction.js
public/javascripts/variants/Magnetic.js
public/javascripts/variants/Switching.js
public/javascripts/variants/Zen.js
views/rules/Crazyhouse.pug [new file with mode: 0644]
views/rules/Extinction.pug [new file with mode: 0644]
views/rules/Switching.pug [new file with mode: 0644]

diff --git a/TODO b/TODO
index bcda2e8..fe7d847 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,3 +2,5 @@ For animation, moves should contains "moving" and "fading" maybe...
 (But it's really just for Magnetic chess)
 setInterval "CRON" task in sockets.js to check connected clients
 (every 1hour maybe, or more)
+Systematically show init+dest squares in PGN, maybe after short notation
+(2 moves list, second for de-ambiguification)
index f1188ab..360f60c 100644 (file)
@@ -276,7 +276,8 @@ class ChessRules
                {
                        let i = x + step[0];
                        let j = y + step[1];
-                       while (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == VariantRules.EMPTY)
+                       while (i>=0 && i<sizeX && j>=0 && j<sizeY
+                               && this.board[i][j] == VariantRules.EMPTY)
                        {
                                moves.push(this.getBasicMove([x,y], [i,j]));
                                if (oneStep !== undefined)
@@ -296,7 +297,7 @@ class ChessRules
                const color = this.turn;
                let moves = [];
                const V = VariantRules;
-               const [sizeX,sizeY] = VariantRules.size;
+               const [sizeX,sizeY] = V.size;
                const shift = (color == "w" ? -1 : 1);
                const firstRank = (color == 'w' ? sizeX-1 : 0);
                const startRank = (color == "w" ? sizeX-2 : 1);
@@ -308,7 +309,7 @@ class ChessRules
                        if (this.board[x+shift][y] == V.EMPTY)
                        {
                                moves.push(this.getBasicMove([x,y], [x+shift,y]));
-                               // Next condition because variants with pawns on 1st rank generally allow them to jump
+                               // Next condition because variants with pawns on 1st rank allow them to jump
                                if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
                                {
                                        // Two squares jump
@@ -316,10 +317,16 @@ class ChessRules
                                }
                        }
                        // Captures
-                       if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+                       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]));
-                       if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+                       }
+                       if (y<sizeY-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]));
+                       }
                }
 
                if (x+shift == lastRank)
@@ -331,10 +338,16 @@ class ChessRules
                                if (this.board[x+shift][y] == V.EMPTY)
                                        moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
                                // Captures
-                               if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+                               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:p}));
-                               if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+                               }
+                               if (y<sizeY-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:p}));
+                               }
                        });
                }
 
@@ -603,7 +616,8 @@ class ChessRules
                        V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
        }
 
-       // Generic method for non-pawn pieces ("sliding or jumping"): is x,y attacked by piece != color ?
+       // Generic method for non-pawn pieces ("sliding or jumping"):
+       // is x,y attacked by piece !of color in colors?
        isAttackedBySlideNJump([x,y], colors, piece, steps, oneStep)
        {
                const [sizeX,sizeY] = VariantRules.size;
index 86f2521..2897b1c 100644 (file)
@@ -240,23 +240,39 @@ Vue.component('my-game', {
                                );
                        }
                        elementArray.push(gameDiv);
-       //                      if (!!vr.reserve)
-       //                      {
-       //                              let reserve = h('div',
-       //                                      {'class':{'game':true}}, [
-       //                                              h('div',
-       //                                                      { 'class': { 'row': true }},
-       //                                                      [
-       //                                                              h('div',
-       //                                                                      {'class':{'board':true}},
-       //                                                                      [h('img',{'class':{"piece":true},attrs:{"src":"/images/pieces/wb.svg"}})]
-       //                                                              )
-       //                                                      ]
-       //                                              )
-       //                                      ],
-       //                              );
-       //                              elementArray.push(reserve);
-       //                      }
+                       if (!!this.vr.reserve) //TODO: table, show counts for reserve pieces
+                               //<tr style="padding:0">
+                               //    <td style="padding:0;font-size:10px">3</td>
+                       {
+                               let reservePiecesArray = [];
+                               for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
+                               {
+                                       reservePiecesArray.push(h('img',
+                                               {
+                                                       'class': {"piece":true},
+                                                       attrs: {
+                                                               "src": "/images/pieces/" +
+                                                                       this.vr.getReservePpath(this.mycolor,i) + ".svg",
+                                                               id: this.getSquareId({x:sizeX,y:i}),
+                                                       }
+                                               })
+                                       );
+                               }
+                               let reserve = h('div',
+                                       {'class':{'game':true}}, [
+                                               h('div',
+                                                       { 'class': { 'row': true }},
+                                                       [
+                                                               h('div',
+                                                                       {'class':{'board':true, ['board'+sizeY]:true}},
+                                                                       reservePiecesArray
+                                                               )
+                                                       ]
+                                               )
+                                       ],
+                               );
+                               elementArray.push(reserve);
+                       }
                        const eogMessage = this.getEndgameMessage(this.score);
                        const modalEog = [
                                h('input',
index 0ee4bd3..9cb0768 100644 (file)
@@ -2,7 +2,7 @@ class CrazyhouseRules extends ChessRules
 {
        initVariables(fen)
        {
-               super.initVariables();
+               super.initVariables(fen);
                // Also init reserves (used by the interface to show landing pieces)
                const V = VariantRules;
                this.reserve =
@@ -24,59 +24,111 @@ class CrazyhouseRules extends ChessRules
                                [V.QUEEN]: 0,
                        }
                };
-               // It may be a continuation: adjust numbers of pieces according to captures + rebirths
-               // TODO
+               // May be a continuation: adjust numbers of pieces according to captures + rebirths
+               this.moves.forEach(m => {
+                       if (m.vanish.length == 2)
+                               this.reserve[m.appear[0].c][m.vanish[1].p]++;
+                       else if (m.vanish.length == 0)
+                               this.reserve[m.appear[0].c][m.appear[0].p]--;
+               });
        }
 
        // Used by the interface:
-       getReservePieces(color)
+       getReservePpath(color, index)
        {
-               return {
-                       [color+V.PAWN]: this.reserve[color][V.PAWN],
-                       [color+V.ROOK]: this.reserve[color][V.ROOK],
-                       [color+V.KNIGHT]: this.reserve[color][V.KNIGHT],
-                       [color+V.BISHOP]: this.reserve[color][V.BISHOP],
-                       [color+V.QUEEN]: this.reserve[color][V.QUEEN],
-               };
+               return color + VariantRules.RESERVE_PIECES[index];
        }
 
-       getPotentialMovesFrom([x,y])
+       // Put an ordering on reserve pieces
+       static get RESERVE_PIECES() {
+               const V = VariantRules;
+               return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
+       }
+
+       getReserveMoves([x,y])
        {
-               let moves = super.getPotentialMovesFrom([x,y]);
-               // Add landing moves:
                const color = this.turn;
-               Object.keys(this.reserve[color]).forEach(p => {
-
-                       moves.push(...); //concat... just appear
-               });
+               const p = VariantRules.RESERVE_PIECES[y];
+               if (this.reserve[color][p] == 0)
+                       return [];
+               let moves = [];
+               for (let i=0; i<sizeX; i++)
+               {
+                       for (let j=0; j<sizeY; j++)
+                       {
+                               if (this.board[i][j] != VariantRules.EMPTY)
+                               {
+                                       let mv = new Move({
+                                               appear: [
+                                                       new PiPo({
+                                                               x: i,
+                                                               y: j,
+                                                               c: color,
+                                                               p: p
+                                                       })
+                                               ]
+                                       });
+                                       moves.push(mv);
+                               }
+                       }
+               }
                return moves;
        }
 
-       // TODO: condition "if this is reserve" --> special square !!! coordinates ??
-       getPossibleMovesFrom(sq)
+       getPotentialMovesFrom([x,y])
        {
-               // Assuming color is right (already checked)
-               return this.filterValid( this.getPotentialMovesFrom(sq) );
+               const sizeX = VariantRules.size[0];
+               if (x < sizeX)
+                       return super.getPotentialMovesFrom([x,y]);
+               // Reserves, outside of board: x == sizeX
+               return this.getReserveMoves([x,y]);
        }
 
-       // TODO: add reserve moves
        getAllValidMoves()
        {
-
+               let moves = super.getAllValidMoves();
+               const color = this.turn;
+               const sizeX = VariantRules.size[0];
+               for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
+                       moves = moves.concat(this.getReserveMoves([sizeX,i]));
+               return this.filterValid(moves);
        }
 
-       // TODO: also
        atLeastOneMove()
        {
-
+               if (!super.atLeastOneMove())
+               {
+                       const sizeX = VariantRules.size[0];
+                       // Scan for reserve moves
+                       for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
+                       {
+                               let moves = this.filterValid(this.getReserveMoves([sizeX,i]));
+                               if (moves.length > 0)
+                                       return true;
+                       }
+                       return false;
+               }
+               return true;
        }
 
-       // TODO: update reserve
        updateVariables(move)
        {
+               super.updateVariables(move);
+               const color = this.turn;
+               if (move.vanish.length==2)
+                       this.reserve[color][move.appear[0].p]++;
+               if (move.vanish.length==0)
+                       this.reserve[color][move.appear[0].p]--;
        }
+
        unupdateVariables(move)
        {
+               super.unupdateVariables(move);
+               const color = this.turn;
+               if (move.vanish.length==2)
+                       this.reserve[color][move.appear[0].p]--;
+               if (move.vanish.length==0)
+                       this.reserve[color][move.appear[0].p]++;
        }
 
        static get SEARCH_DEPTH() { return 2; } //high branching factor
index db330d9..9be4b0d 100644 (file)
@@ -27,6 +27,37 @@ class ExtinctionRules extends ChessRules
                };
        }
 
+       getPotentialPawnMoves([x,y])
+       {
+               let moves = super.getPotentialPawnMoves([x,y]);
+               // Add potential promotions into king
+               const color = this.turn;
+               const V = VariantRules;
+               const [sizeX,sizeY] = V.size;
+               const shift = (color == "w" ? -1 : 1);
+               const lastRank = (color == "w" ? 0 : sizeX-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}));
+                       // 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<sizeY-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;
+       }
+
        // TODO: verify this assertion
        atLeastOneMove()
        {
@@ -46,13 +77,24 @@ class ExtinctionRules extends ChessRules
        updateVariables(move)
        {
                super.updateVariables(move);
-               if (move.vanish.length==2 && move.appear.length==1)
+               // Treat the promotion case: (not the capture part)
+               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][VariantRules.PAWN]--;
+               }
+               if (move.vanish.length==2 && move.appear.length==1) //capture
                        this.material[move.vanish[1].c][move.vanish[1].p]--;
        }
 
        unupdateVariables(move)
        {
                super.unupdateVariables(move);
+               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][VariantRules.PAWN]++;
+               }
                if (move.vanish.length==2 && move.appear.length==1)
                        this.material[move.vanish[1].c][move.vanish[1].p]++;
        }
@@ -73,14 +115,23 @@ class ExtinctionRules extends ChessRules
                        return "*";
                }
 
-               return this.checkGameEnd();
+               return this.checkGameEnd(); //NOTE: currently unreachable...
+       }
+
+       checkGameEnd()
+       {
+               return this.turn == "w" ? "0-1" : "1-0";
        }
 
        // Very negative (resp. positive) if white (reps. black) pieces set is incomplete
        evalPosition()
        {
-               if (this.missAkind())
-                       return (this.turn=="w"?-1:1) * VariantRules.INFINITY;
+               const color = this.turn;
+               if (Object.keys(this.material[color]).some(
+                       p => { return this.material[color][p] == 0; }))
+               {
+                       return (color=="w"?-1:1) * VariantRules.INFINITY;
+               }
                return super.evalPosition();
        }
 }
index 844cc68..03912e7 100644 (file)
@@ -112,7 +112,8 @@ class MagneticRules extends ChessRules
                // 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)
+                       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);
index a7d6787..cc2febd 100644 (file)
@@ -1,10 +1,73 @@
-//https://www.chessvariants.com/diffmove.dir/switching.html
 class SwitchingRules extends ChessRules
 {
-       //TODO:
-       // Move completion: promote switched pawns (as in Magnetic)
+       // Build switch move between squares x1,y1 and x2,y2
+       getSwitchMove_s([x1,y1],[x2,y2])
+       {
 
-       // To every piece potential moves: add switchings
+               const c = this.getColor(x1,y1); //same as color at square 2
+               const p1 = this.getPiece(x1,y1);
+               const p2 = this.getPiece(x2,y2);
+               let move = new Move({
+                       appear: [
+                               new PiPo({x:x2,y:y2,c:c,p:p1}),
+                               new PiPo({x:x1,y:y1,c:c,p:p2})
+                       ],
+                       vanish: [
+                               new PiPo({x:x1,y:y1,c:c,p:p1}),
+                               new PiPo({x:x2,y:y2,c:c,p:p2})
+                       ],
+                       start: {x:x1,y:y1},
+                       end: {x:x2,y:y2}
+               });
+               // Move completion: promote switched pawns (as in Magnetic)
+               const sizeX = VariantRules.size[0];
+               const lastRank = (c == "w" ? 0 : sizeX-1);
+               const V = VariantRules;
+               let moves = [];
+               if (p1==V.PAWN && x2==lastRank) //TODO: also the case p2==V.PAWN and x1==lastRank! see Magnetic chess
+               {
+                       move.appear[0].p = V.ROOK;
+                       moves.push(move);
+                       for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN])
+                       {
+                               let cmove = JSON.parse(JSON.stringify(move));
+                               cmove.appear[0].p = piece;
+                               moves.push(cmove);
+                       }
+               }
+               else //other cases
+                       moves.push(move);
+               return moves;
+       }
 
-       // Prevent king switching if under check
+       getPotentialMovesFrom([x,y])
+       {
+               let moves = super.getPotentialMovesFrom([x,y]);
+               // Add switches:
+               const V = VariantRules;
+               const color = this.turn;
+               const piece = this.getPiece(x,y);
+               const [sizeX,sizeY] = V.size;
+               const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
+               const kp = this.kingPos[color];
+               const oppCol = this.getOppCol(color);
+               for (let step of steps)
+               {
+                       let [i,j] = [x+step[0],y+step[1]];
+                       if (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j]!=V.EMPTY
+                               && this.getColor(i,j)==color && this.getPiece(i,j)!=piece
+                               // No switching under check (theoretically non-king pieces could, but not)
+                               && !this.isAttacked(kp, [oppCol]))
+                       {
+                               let switchMove_s = this.getSwitchMove_s([x,y],[i,j]);
+                               if (switchMove_s.length == 1)
+                                       moves.push(switchMove_s[0]);
+                               else //promotion
+                                       moves = moves.concat(switchMove_s);
+                       }
+               }
+               return moves;
+       }
+
+       static get SEARCH_DEPTH() { return 2; } //branching factor is quite high
 }
index 17c922d..31dfccd 100644 (file)
@@ -10,14 +10,14 @@ class ZenRules extends ChessRules
        getSlideNJumpMoves([x,y], steps, oneStep)
        {
                const color = this.getColor(x,y);
-               var moves = [];
-               let [sizeX,sizeY] = VariantRules.size;
+               let moves = [];
+               const [sizeX,sizeY] = VariantRules.size;
                outerLoop:
-               for (var loop=0; loop<steps.length; loop++)
+               for (let loop=0; loop<steps.length; loop++)
                {
-                       var step = steps[loop];
-                       var i = x + step[0];
-                       var j = y + step[1];
+                       const step = steps[loop];
+                       let i = x + step[0];
+                       let j = y + step[1];
                        while (i>=0 && i<sizeX && j>=0 && j<sizeY
                                && this.board[i][j] == VariantRules.EMPTY)
                        {
@@ -38,22 +38,21 @@ class ZenRules extends ChessRules
        {
                const color = this.getColor(x,y);
                var moves = [];
-               var V = VariantRules;
-               var steps = asA != V.PAWN
-                       ? V.steps[asA]
+               const V = VariantRules;
+               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]];
-               var oneStep = (asA==V.KNIGHT || asA==V.PAWN); //we don't capture king
-               let [sizeX,sizeY] = V.size;
-               let lastRank = (color == 'w' ? 0 : sizeY-1);
-               let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
+               const oneStep = (asA==V.KNIGHT || asA==V.PAWN); //we don't capture king
+               const [sizeX,sizeY] = V.size;
+               const lastRank = (color == 'w' ? 0 : sizeY-1);
+               const promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
                outerLoop:
-               for (var loop=0; loop<steps.length; loop++)
+               for (let loop=0; loop<steps.length; loop++)
                {
-                       var step = steps[loop];
-                       var i = x + step[0];
-                       var j = y + step[1];
-                       while (i>=0 && i<sizeX && j>=0 && j<sizeY
-                               && this.board[i][j] == V.EMPTY)
+                       const step = steps[loop];
+                       let i = x + step[0];
+                       let j = y + step[1];
+                       while (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == V.EMPTY)
                        {
                                if (oneStep)
                                        continue outerLoop;
@@ -173,7 +172,8 @@ class ZenRules extends ChessRules
        getPotentialQueenMoves(sq)
        {
                const V = VariantRules;
-               let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.ROOK.concat(V.steps[V.BISHOP])]);
+               let noCaptures =
+                       this.getSlideNJumpMoves(sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
                let captures = this.findCaptures(sq);
                return noCaptures.concat(captures);
        }
diff --git a/views/rules/Crazyhouse.pug b/views/rules/Crazyhouse.pug
new file mode 100644 (file)
index 0000000..3928c30
--- /dev/null
@@ -0,0 +1,33 @@
+p.boxed
+       | Every captured piece can be re-used by the capturer, landing it anywhere instead of moving a piece.
+
+h3 Specifications
+
+ul
+       li Chessboard: standard.
+       li Material: standard.
+       li Non-capturing moves: standard.
+       li Special moves: standard + rebirth.
+       li Captures: standard.
+       li End of game: standard.
+
+h3 Basics
+
+p
+       | Orthodox rules apply, with only one change:
+       | every time you capture a piece, your "reserve" of waiting pieces is augmented
+       | with this figure. At every move, you may choose to land one of your reserve
+       | pieces anywhere on the board (except last rank for pawns),
+       | instead of playing a regular move.
+
+p.
+       Note: when a promoted pawn is captured, capturing it put a pawn in the reserve,
+       not the promoted piece. This is to allow to gain material on last rank without
+       fear of giving a queen to the opponent.
+
+h3 Credits
+
+p
+       | This variant is very popular, a possible starting point is 
+       a(href="https://www.chessvariants.com/other.dir/crazyhouse.html") lichess.org
+       | .
diff --git a/views/rules/Extinction.pug b/views/rules/Extinction.pug
new file mode 100644 (file)
index 0000000..5672586
--- /dev/null
@@ -0,0 +1,34 @@
+p.boxed
+       | Win by eliminating all opponent pieces of the same type.
+
+h3 Specifications
+
+ul
+       li Chessboard: standard.
+       li Material: standard.
+       li Non-capturing moves: standard.
+       li Special moves: standard.
+       li Captures: standard.
+       li End of game: pieces extinction.
+
+h3 Basics
+
+p
+       | Standard rules apply, but the game ends when all pieces of a kind disappeared.
+       | Kings are treated as normal pieces (no royal power), but may castle -
+       | without any concern about checks.
+       | Pawns may promote into king.
+       | If all pieces of a kind disappear, the game is lost; except it's a pawns extinction
+       | which results in a non-pawn extinction by capturing and promoting on the last rank.
+
+h3 End of game
+
+p.
+       Win by eliminating all enemy pawns, or rooks, or knights, or bishops, or queen(s),
+       or king(s) (there may be several if promotions happened).
+
+h3 Credits
+
+p
+       a(href="https://www.chessvariants.com/winning.dir/extinction.html") Extinction chess 
+       | on chessvariants.com.
diff --git a/views/rules/Switching.pug b/views/rules/Switching.pug
new file mode 100644 (file)
index 0000000..3104734
--- /dev/null
@@ -0,0 +1,32 @@
+p.boxed
+       | In addition to standard moves, a piece can be exchanged with an adjacent friendly unit.
+
+h3 Specifications
+
+ul
+       li Chessboard: standard.
+       li Material: standard.
+       li Non-capturing moves: standard + switch.
+       li Special moves: standard.
+       li Captures: standard.
+       li End of game: standard.
+
+h3 Basics
+
+p
+       | Instead of a normal move, a piece may be exchanged with an adjacent friendly one.
+       | Switching can move pawns until rank 1 or final rank. In the first case a 2-squares,
+       | jump is possible, and in the second a promotion occurs.
+       | Switching must involves two different units.
+       | Switching while the king is under check is not allowed.
+
+p.
+       Note: if the king and rook are on two adjacent squares, castling and switching
+       from the king are triggered in the same way. Castling takes priority:
+       if you wanna switch, use the rook.
+
+h3 Credits
+
+p
+       a(href="https://www.chessvariants.com/diffmove.dir/switching.html") Switching chess 
+       | on chessvariants.com.