Prepare some more variants (unfinished)
authorBenjamin Auder <benjamin.auder@somewhere>
Mon, 24 Dec 2018 12:50:44 +0000 (13:50 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Mon, 24 Dec 2018 12:50:44 +0000 (13:50 +0100)
LICENSE [new file with mode: 0644]
public/javascripts/base_rules.js
public/javascripts/components/game.js
public/javascripts/variant.js
public/javascripts/variants/Berolina.js [new file with mode: 0644]
public/javascripts/variants/Dark.js [new file with mode: 0644]
public/javascripts/variants/Grand.js
public/javascripts/variants/Magnetic.js
public/javascripts/variants/Wildebeest.js
public/stylesheets/variant.sass
views/variant.pug

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..695f90a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,5 @@
+Copyright 2018 Benjamin AUder
+
+Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
index 7f27e85..d85ff86 100644 (file)
@@ -599,74 +599,65 @@ class ChessRules
                const color = this.turn;
                let moves = [];
                const [sizeX,sizeY] = [V.size.x,V.size.y];
-               const shift = (color == "w" ? -1 : 1);
+               const shiftX = (color == "w" ? -1 : 1);
                const firstRank = (color == 'w' ? sizeX-1 : 0);
                const startRank = (color == "w" ? sizeX-2 : 1);
                const lastRank = (color == "w" ? 0 : sizeX-1);
+               const pawnColor = this.getColor(x,y); //can be different for checkered
 
-               if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank)
+               if (x+shiftX >= 0 && x+shiftX < sizeX) //TODO: always true
                {
-                       // Normal moves
-                       if (this.board[x+shift][y] == V.EMPTY)
+                       const finalPieces = x + shiftX == lastRank
+                               ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
+                               : [V.PAWN]
+                       // One square forward
+                       if (this.board[x+shiftX][y] == V.EMPTY)
                        {
-                               moves.push(this.getBasicMove([x,y], [x+shift,y]));
-                               // 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)
+                               for (let piece of finalPieces)
+                               {
+                                       moves.push(this.getBasicMove([x,y], [x+shiftX,y],
+                                               {c:pawnColor,p:piece}));
+                               }
+                               // Next condition because pawns on 1st rank can generally jump
+                               if ([startRank,firstRank].includes(x)
+                                       && this.board[x+2*shiftX][y] == V.EMPTY)
                                {
                                        // Two squares jump
-                                       moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
+                                       moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
                                }
                        }
                        // Captures
-                       if (y>0 && this.board[x+shift][y-1] != V.EMPTY
-                               && this.canTake([x,y], [x+shift,y-1]))
-                       {
-                               moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
-                       }
-                       if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
-                               && this.canTake([x,y], [x+shift,y+1]))
+                       for (let shiftY of [-1,1])
                        {
-                               moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
-                       }
-               }
-
-               if (x+shift == lastRank)
-               {
-                       // Promotion
-                       const pawnColor = this.getColor(x,y); //can be different for checkered
-                       let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
-                       promotionPieces.forEach(p => {
-                               // Normal move
-                               if (this.board[x+shift][y] == V.EMPTY)
-                                       moves.push(this.getBasicMove([x,y], [x+shift,y], {c:pawnColor,p:p}));
-                               // Captures
-                               if (y>0 && this.board[x+shift][y-1] != V.EMPTY
-                                       && this.canTake([x,y], [x+shift,y-1]))
+                               if (y + shiftY >= 0 && y + shiftY < sizeY
+                                       && this.board[x+shiftX][y+shiftY] != V.EMPTY
+                                       && this.canTake([x,y], [x+shiftX,y+shiftY]))
                                {
-                                       moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:pawnColor,p:p}));
-                               }
-                               if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
-                                       && this.canTake([x,y], [x+shift,y+1]))
-                               {
-                                       moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:pawnColor,p:p}));
+                                       for (let piece of finalPieces)
+                                       {
+                                               moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
+                                                       {c:pawnColor,p:piece}));
+                                       }
                                }
-                       });
+                       }
                }
 
-               // En passant
-               const Lep = this.epSquares.length;
-               const epSquare = this.epSquares[Lep-1]; //always at least one element
-               if (!!epSquare && epSquare.x == x+shift && Math.abs(epSquare.y - y) == 1)
+               if (V.HasEnpassant)
                {
-                       const epStep = epSquare.y - y;
-                       let enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
-                       enpassantMove.vanish.push({
-                               x: x,
-                               y: y+epStep,
-                               p: 'p',
-                               c: this.getColor(x,y+epStep)
-                       });
-                       moves.push(enpassantMove);
+                       // En passant
+                       const Lep = this.epSquares.length;
+                       const epSquare = this.epSquares[Lep-1]; //always at least one element
+                       if (!!epSquare && epSquare.x == x+shiftX && Math.abs(epSquare.y - y) == 1)
+                       {
+                               let enpassantMove = this.getBasicMove([x,y], [epSquare.x,epSquare.y]);
+                               enpassantMove.vanish.push({
+                                       x: x,
+                                       y: epSquare.y,
+                                       p: 'p',
+                                       c: this.getColor(x,epSquare.y)
+                               });
+                               moves.push(enpassantMove);
+                       }
                }
 
                return moves;
index 88b193f..b308aa8 100644 (file)
@@ -63,7 +63,7 @@ Vue.component('my-game', {
                        },
                        [h('i', { 'class': { "material-icons": true } }, "accessibility")])
                );
-               if (["idle","chat","computer"].includes(this.mode))
+               if (variant!="Dark" && ["idle","chat","computer"].includes(this.mode))
                {
                        actionArray.push(
                                h('button',
@@ -80,7 +80,7 @@ Vue.component('my-game', {
                                [h('i', { 'class': { "material-icons": true } }, "computer")])
                        );
                }
-               if (["idle","chat","friend"].includes(this.mode))
+               if (variant!="Dark" && ["idle","chat","friend"].includes(this.mode))
                {
                        actionArray.push(
                                h('button',
@@ -298,7 +298,8 @@ Vue.component('my-game', {
                                                _.range(sizeY).map(j => {
                                                        let cj = (this.mycolor=='w' ? j : sizeY-j-1);
                                                        let elems = [];
-                                                       if (this.vr.board[ci][cj] != VariantRules.EMPTY)
+                                                       if (this.vr.board[ci][cj] != VariantRules.EMPTY && (variant!="Dark"
+                                                               || this.score!="*" || this.vr.isEnlightened(ci,cj,this.mycolor)))
                                                        {
                                                                elems.push(
                                                                        h(
@@ -342,6 +343,8 @@ Vue.component('my-game', {
                                                                                'light-square': (i+j)%2==0,
                                                                                'dark-square': (i+j)%2==1,
                                                                                [this.color]: true,
+                                                                               'in-shadow': variant=="Dark" && this.score=="*"
+                                                                                       && !this.vr.isEnlightened(ci,cj,this.mycolor),
                                                                                'highlight': showLight && !!lm && _.isMatch(lm.end, {x:ci,y:cj}),
                                                                                'incheck': showLight && incheckSq[ci][cj],
                                                                        },
@@ -919,24 +922,27 @@ Vue.component('my-game', {
                                        )
                                );
                        }
-                       // Show current FEN
-                       elementArray.push(
-                               h('div',
-                                       {
-                                               attrs: { id: "fen-div" },
-                                               "class": { "section-content": true },
-                                       },
-                                       [
-                                               h('p',
-                                                       {
-                                                               attrs: { id: "fen-string" },
-                                                               domProps: { innerHTML: this.vr.getBaseFen() },
-                                                               "class": { "text-center": true },
-                                                       }
-                                               )
-                                       ]
-                               )
-                       );
+                       if (variant != "Dark" || this.score!="*")
+                       {
+                               // Show current FEN
+                               elementArray.push(
+                                       h('div',
+                                               {
+                                                       attrs: { id: "fen-div" },
+                                                       "class": { "section-content": true },
+                                               },
+                                               [
+                                                       h('p',
+                                                               {
+                                                                       attrs: { id: "fen-string" },
+                                                                       domProps: { innerHTML: this.vr.getBaseFen() },
+                                                                       "class": { "text-center": true },
+                                                               }
+                                                       )
+                                               ]
+                                       )
+                               );
+                       }
                }
                return h(
                        'div',
index a7d1820..819bf90 100644 (file)
@@ -21,5 +21,8 @@ new Vue({
                        if (!!menuToggle)
                                menuToggle.checked = false;
                },
+               notDark: function() {
+                       return variant != "Dark";
+               },
        },
 });
diff --git a/public/javascripts/variants/Berolina.js b/public/javascripts/variants/Berolina.js
new file mode 100644 (file)
index 0000000..c556f51
--- /dev/null
@@ -0,0 +1,93 @@
+class BerolinaRules extends ChessRules
+{
+       // En-passant after 2-sq jump
+       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,ex,sy] = [move.start.x,move.end.x,move.start.y];
+               if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) == 2)
+               {
+                       return {
+                               x: ex,
+                               y: (move.end.y + sy)/2
+                       };
+               }
+               return undefined; //default
+       }
+
+       // Special pawn rules: promotions to captured friendly pieces,
+       // optional on ranks 8-9 and mandatory on rank 10.
+       getPotentialPawnMoves([x,y])
+       {
+               const color = this.turn;
+               let moves = [];
+               const [sizeX,sizeY] = [V.size.x,V.size.y];
+               const shiftX = (color == "w" ? -1 : 1);
+               const firstRank = (color == 'w' ? sizeX-1 : 0);
+               const startRank = (color == "w" ? sizeX-2 : 1);
+               const lastRank = (color == "w" ? 0 : sizeX-1);
+
+               if (x+shiftX >= 0 && x+shiftX < sizeX) //TODO: always true
+               {
+                       const finalPieces = x + shiftX == lastRank
+                               ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
+                               : [V.PAWN]
+                       // One square diagonally
+                       for (let shiftY of [-1,1])
+                       {
+                               if (this.board[x+shiftX][y+shiftY] == V.EMPTY)
+                               {
+                                       for (let piece of finalPieces)
+                                       {
+                                               moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
+                                                       {c:pawnColor,p:piece}));
+                                       }
+                                       if (x == startRank && this.board[x+2*shiftX][y] == V.EMPTY)
+                                       {
+                                               // Two squares jump
+                                               moves.push(this.getBasicMove([x,y], [x+2*shiftX,y+2*shiftY]);
+                                       }
+                               }
+                       }
+                       // Capture
+                       if (this.board[x+shiftX][y] != V.EMPTY
+                               && this.canTake([x,y], [x+shiftX,y]))
+                       {
+                               for (let piece of finalPieces)
+                               {
+                                       moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
+                                               {c:pawnColor,p:piece}));
+                               }
+                       }
+               }
+
+               // En passant
+               const Lep = this.epSquares.length;
+               const epSquare = this.epSquares[Lep-1]; //always at least one element
+               if (!!epSquare && epSquare.x == x+shiftX && epSquare.y == y)
+               {
+                       let enpassantMove = this.getBasicMove([x,y], [x+shift,y]);
+                       enpassantMove.vanish.push({
+                               x: epSquare.x,
+                               y: y,
+                               p: 'p',
+                               c: this.getColor(epSquare.x,y)
+                       });
+                       moves.push(enpassantMove);
+               }
+
+               return moves;
+       }
+}
+
+const VariantRules = BerolinaRules;
diff --git a/public/javascripts/variants/Dark.js b/public/javascripts/variants/Dark.js
new file mode 100644 (file)
index 0000000..9cf5051
--- /dev/null
@@ -0,0 +1,67 @@
+class Chess960Rules extends ChessRules
+{
+       // Standard rules, in the shadow
+       setOtherVariables(fen)
+       {
+               super.setOtherVariables(fen);
+               const [sizeX,sizeY] = {V.size.x,V.size.y};
+               this.enlightened = {
+                       "w": doubleArray(sizeX,sizeY,false),
+                       "b": doubleArray(sizeX,sizeY,false)
+               };
+               setup enlightened: squares reachable by each side (TODO: one side would be enough)
+       }
+
+       isEnlightened(x, y, color)
+       {
+               //TODO: artificlaly change turn
+       }
+
+       getAllPotentialMoves()
+       {
+               let moves = []; //TODO
+       }
+
+       atLeastOneMove()
+       {
+               if (this.kingPos[this.turn][0] < 0)
+                       return false;
+               return true; //TODO: is it right?
+       }
+
+       underCheck(move)
+       {
+               return false; //there is no check
+       }
+
+       getCheckSquares(move)
+       {
+               const c = this.getOppCol(this.turn); //opponent
+               const saveKingPos = this.kingPos[c]; //king might be taken
+               this.play(move);
+               // The only way to be "under check" is to have lost the king (thus game over)
+               let res = this.kingPos[c][0] < 0
+                       ? [JSON.parse(JSON.stringify(saveKingPos))]
+                       : [];
+               this.undo(move);
+               return res;
+       }
+
+       // NOTE: no (un)updateVariables() because no computer mode
+       // --> but isEnlightened() should have its variable updated
+       // --> in fact an array is enough (no need for a function)
+       // recomputed after every play/undo (although there are no undo here for now)
+
+       checkGameEnd()
+       {
+               // No valid move: our king disappeared
+               return this.turn == "w" ? "0-1" : "1-0";
+       }
+
+       static get THRESHOLD_MATE()
+       {
+               return 500; //checkmates evals may be slightly below 1000
+       }
+}
+
+const VariantRules = DarkRules;
index 4271a69..7248f57 100644 (file)
@@ -218,13 +218,12 @@ class GrandRules extends ChessRules
                                // TODO: some redundant checks
                                if (epsq.x == x+shift && Math.abs(epsq.y - y) == 1)
                                {
-                                       let epStep = epsq.y - y;
-                                       var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
+                                       var enpassantMove = this.getBasicMove([x,y], [x+shift,epsq.y]);
                                        enpassantMove.vanish.push({
                                                x: x,
-                                               y: y+epStep,
+                                               y: epsq.y,
                                                p: 'p',
-                                               c: this.getColor(x,y+epStep)
+                                               c: this.getColor(x,epsq.y)
                                        });
                                        moves.push(enpassantMove);
                                }
index c4995dd..8480d79 100644 (file)
@@ -16,67 +16,6 @@ class MagneticRules extends ChessRules
                return moves;
        }
 
-       getPotentialPawnMoves([x,y])
-       {
-               const color = this.turn;
-               let moves = [];
-               const [sizeX,sizeY] = [V.size.x,V.size.y];
-               const shift = (color == "w" ? -1 : 1);
-               const firstRank = (color == 'w' ? sizeX-1 : 0);
-               const startRank = (color == "w" ? sizeX-2 : 1);
-               const lastRank = (color == "w" ? 0 : sizeX-1);
-
-               if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank)
-               {
-                       // Normal moves
-                       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 allow them to jump
-                               if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
-                               {
-                                       // Two squares jump
-                                       moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
-                               }
-                       }
-                       // Captures
-                       if (y>0 && this.board[x+shift][y-1] != V.EMPTY
-                               && this.canTake([x,y], [x+shift,y-1]))
-                       {
-                               moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
-                       }
-                       if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
-                               && this.canTake([x,y], [x+shift,y+1]))
-                       {
-                               moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
-                       }
-               }
-
-               if (x+shift == lastRank)
-               {
-                       // Promotion
-                       const pawnColor = this.getColor(x,y); //can be different for checkered
-                       let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
-                       promotionPieces.forEach(p => {
-                               // Normal move
-                               if (this.board[x+shift][y] == V.EMPTY)
-                                       moves.push(this.getBasicMove([x,y], [x+shift,y], {c:pawnColor,p:p}));
-                               // Captures
-                               if (y>0 && this.board[x+shift][y-1] != V.EMPTY
-                                       && this.canTake([x,y], [x+shift,y-1]))
-                               {
-                                       moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:pawnColor,p:p}));
-                               }
-                               if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
-                                       && this.canTake([x,y], [x+shift,y+1]))
-                               {
-                                       moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:pawnColor,p:p}));
-                               }
-                       });
-               }
-               return moves; //no en-passant
-       }
-
        // Complete a move with magnetic actions
        // TODO: job is done multiple times for (normal) promotions.
        applyMagneticLaws(move)
index 783eb44..ef3779a 100644 (file)
@@ -159,13 +159,12 @@ class WildebeestRules extends ChessRules
                                // TODO: some redundant checks
                                if (epsq.x == x+shift && Math.abs(epsq.y - y) == 1)
                                {
-                                       let epStep = epsq.y - y;
-                                       var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
+                                       var enpassantMove = this.getBasicMove([x,y], [x+shift,epsq.y]);
                                        enpassantMove.vanish.push({
                                                x: x,
-                                               y: y+epStep,
+                                               y: epsq.y,
                                                p: 'p',
-                                               c: this.getColor(x,y+epStep)
+                                               c: this.getColor(x,epsq.y)
                                        });
                                        moves.push(enpassantMove);
                                }
index ecb1bc9..0ebad8c 100644 (file)
@@ -221,6 +221,9 @@ img.ghost
 .highlight
   background-color: #00cc66 !important
 
+.in-shadow
+  opacity: 0.5
+
 .incheck
   background-color: #cc3300 !important
 
index 23e7076..7f01b91 100644 (file)
@@ -39,7 +39,7 @@ block content
                                                        =translations["Rules"]
                                                a(href="#play" @click="setDisplay('play')")
                                                        =translations["Play"]
-                                               a(href="#problems" @click="setDisplay('problems')") 
+                                               a(href="#problems" v-if="notDark()" @click="setDisplay('problems')") 
                                                        =translations["Problems"]
                                        #flagMenu.clickable(
                                                        onClick="document.getElementById('modalLang').checked=true")
@@ -51,7 +51,8 @@ block content
                .row
                        my-rules(v-show="display=='rules'")
                        my-game(v-show="display=='play'" v-bind:problem="problem")
-                       my-problems(v-show="display=='problems'" v-on:show-problem="showProblem($event)")
+                       my-problems(v-if="notDark()" v-show="display=='problems'"
+                               v-on:show-problem="showProblem($event)")
 
 block javascripts
        script(src="/javascripts/utils/misc.js")