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)
 (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];
                {
                        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)
                        {
                                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 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);
                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]));
                        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
                                if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
                                {
                                        // Two squares jump
@@ -316,10 +317,16 @@ class ChessRules
                                }
                        }
                        // Captures
                                }
                        }
                        // 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]));
                                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]));
                                moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
+                       }
                }
 
                if (x+shift == lastRank)
                }
 
                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 (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}));
                                        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}));
                                        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");
        }
 
                        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;
        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);
                                );
                        }
                        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',
                        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)
        {
 {
        initVariables(fen)
        {
-               super.initVariables();
+               super.initVariables(fen);
                // Also init reserves (used by the interface to show landing pieces)
                const V = VariantRules;
                this.reserve =
                // 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,
                        }
                };
                                [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:
        }
 
        // 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;
                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;
        }
 
                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()
        {
        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()
        {
        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)
        {
        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)
        {
        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
        }
 
        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()
        {
        // TODO: verify this assertion
        atLeastOneMove()
        {
@@ -46,13 +77,24 @@ class ExtinctionRules extends ChessRules
        updateVariables(move)
        {
                super.updateVariables(move);
        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);
                        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]++;
        }
                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 "*";
                }
 
-               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()
        {
        }
 
        // 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();
        }
 }
                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++)
                {
                // 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);
                        {
                                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
 {
 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);
        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:
                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)
                        {
                        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 = [];
        {
                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]];
                        : 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:
                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;
                        {
                                if (oneStep)
                                        continue outerLoop;
@@ -173,7 +172,8 @@ class ZenRules extends ChessRules
        getPotentialQueenMoves(sq)
        {
                const V = VariantRules;
        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);
        }
                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.