Improve Crazyhouse; still not debugged
authorBenjamin Auder <benjamin.auder@somewhere>
Thu, 29 Nov 2018 03:30:27 +0000 (04:30 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Thu, 29 Nov 2018 03:30:27 +0000 (04:30 +0100)
TODO
public/javascripts/base_rules.js
public/javascripts/components/game.js
public/javascripts/variants/Crazyhouse.js
public/javascripts/variants/Loser.js
public/javascripts/variants/Switching.js
views/rules/Checkered.pug

diff --git a/TODO b/TODO
index 27b3f8d..d491a90 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,11 +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)
 Button to show all pieces that can move (next to expert mode; change icons)
-
-Bugs, limitations:
- - switching chess when castling: show 2 promotion kings (??)
- - Crazyhouse: TODO = keep track of promoted pawns.
- - Checkered: implement stage 2 ?!
+Style (crazyhouse): ghost image of reserve pieces are initially translated
index 360f60c..e886273 100644 (file)
@@ -726,7 +726,7 @@ class ChessRules
 //             if (!ingame) this.states.push(JSON.stringify(this.board));
 
                if (!!ingame)
-                       move.notation = this.getNotation(move);
+                       move.notation = [this.getNotation(move), this.getLongNotation(move)];
 
                move.flags = JSON.stringify(this.flags); //save flags (for undo)
                this.updateVariables(move);
@@ -1110,6 +1110,16 @@ class ChessRules
                }
        }
 
+       // Complete the usual notation, may be required for de-ambiguification
+       getLongNotation(move)
+       {
+               const startSquare =
+                       String.fromCharCode(97 + move.start.y) + (VariantRules.size[0]-move.start.x);
+               const finalSquare =
+                       String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+               return startSquare + finalSquare; //not encoding move. But short+long is enough
+       }
+
        // The score is already computed when calling this function
        getPGN(mycolor, score, fenStart, mode)
        {
@@ -1124,14 +1134,24 @@ class ChessRules
                pgn += '[Fen "' + fenStart + '"]<br>';
                pgn += '[Result "' + score + '"]<br><br>';
 
+               // Standard PGN
                for (let i=0; i<this.moves.length; i++)
                {
                        if (i % 2 == 0)
                                pgn += ((i/2)+1) + ".";
-                       pgn += this.moves[i].notation + " ";
+                       pgn += this.moves[i].notation[0] + " ";
                }
+               pgn += score + "<br><br>";
 
+               // "Complete moves" PGN (helping in ambiguous cases)
+               for (let i=0; i<this.moves.length; i++)
+               {
+                       if (i % 2 == 0)
+                               pgn += ((i/2)+1) + ".";
+                       pgn += this.moves[i].notation[1] + " ";
+               }
                pgn += score;
+
                return pgn;
        }
 }
index f5bef8e..6d05a2a 100644 (file)
@@ -242,13 +242,14 @@ Vue.component('my-game', {
                        elementArray.push(gameDiv);
                        if (!!this.vr.reserve)
                        {
+                               const shiftIdx = (this.mycolor=="w" ? 0 : 1);
                                let myReservePiecesArray = [];
                                for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
                                {
                                        myReservePiecesArray.push(h('div',
                                        {
                                                'class': {'board':true, ['board'+sizeY]:true},
-                                               attrs: { id: this.getSquareId({x:sizeX,y:i}) }
+                                               attrs: { id: this.getSquareId({x:sizeX+shiftIdx,y:i}) }
                                        },
                                        [
                                                h('img',
@@ -272,7 +273,7 @@ Vue.component('my-game', {
                                        oppReservePiecesArray.push(h('div',
                                        {
                                                'class': {'board':true, ['board'+sizeY]:true},
-                                               attrs: { id: this.getSquareId({x:sizeX,y:i}) }
+                                               attrs: { id: this.getSquareId({x:sizeX+(1-shiftIdx),y:i}) }
                                        },
                                        [
                                                h('img',
@@ -754,6 +755,8 @@ Vue.component('my-game', {
                                this.possibleMoves = this.mode!="idle" && this.vr.canIplay(this.mycolor,startSquare)
                                        ? this.vr.getPossibleMovesFrom(startSquare)
                                        : [];
+                               console.log(this.possibleMoves);
+                               console.log(this.vr.promoted);
                                e.target.parentNode.appendChild(this.selectedPiece);
                        }
                },
index 9558fe4..3733765 100644 (file)
@@ -24,14 +24,10 @@ class CrazyhouseRules extends ChessRules
                                [V.QUEEN]: 0,
                        }
                };
-               // 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]--;
-               });
-               // TODO: keep track of promoted pawns ==> give a pawn if captured.
+               const [sizeX,sizeY] = VariantRules.size;
+               this.promoted = doubleArray(sizeX, sizeY, false);
+               // May be a continuation: adjust numbers of pieces in reserve + promoted pieces
+               this.moves.forEach(m => { this.updateVariables(m); });
        }
 
        getColor(i,j)
@@ -86,7 +82,7 @@ class CrazyhouseRules extends ChessRules
                                                        })
                                                ],
                                                vanish: [],
-                                               start: {x:sizeX, y:y}, //a bit artificial...
+                                               start: {x:x, y:y}, //a bit artificial...
                                                end: {x:i, y:j}
                                        });
                                        moves.push(mv);
@@ -99,10 +95,13 @@ class CrazyhouseRules extends ChessRules
        getPotentialMovesFrom([x,y])
        {
                const sizeX = VariantRules.size[0];
-               if (x < sizeX)
-                       return super.getPotentialMovesFrom([x,y]);
-               // Reserves, outside of board: x == sizeX
-               return this.getReserveMoves([x,y]);
+               if (x >= sizeX)
+               {
+                       // Reserves, outside of board: x == sizeX
+                       return this.getReserveMoves([x,y]);
+               }
+               // Standard moves
+               return super.getPotentialMovesFrom([x,y]);
        }
 
        getAllValidMoves()
@@ -111,7 +110,7 @@ class CrazyhouseRules extends ChessRules
                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]));
+                       moves = moves.concat(this.getReserveMoves([sizeX+(color=="w"?0:1),i]));
                return this.filterValid(moves);
        }
 
@@ -135,25 +134,101 @@ class CrazyhouseRules extends ChessRules
        updateVariables(move)
        {
                super.updateVariables(move);
+               if (move.vanish.length == 2 && move.appear.length == 2)
+                       return; //skip castle
                const color = this.turn;
-               if (move.vanish.length==2)
-                       this.reserve[color][move.vanish[1].p]++;
-               if (move.vanish.length==0)
+               const V = VariantRules;
+               // Three types of move:
+               //   1. Rebirth: just update material
+               //   2. Standard move:
+               //     a. check if a promoted piece is moving
+               //     b. check if it's a promotion (mutually exclusive)
+               //   3. Capture:
+               //     a. check if a promoted piece is captured (and mark move)
+               //     b. check if a promoted piece is moving
+               //     c. check if it's a promotion (mutually exclusive with b)
+               if (move.vanish.length == 0)
                        this.reserve[color][move.appear[0].p]--;
+               else if (move.vanish.length == 1)
+               {
+                       if (this.promoted[move.start.x][move.start.y])
+                       {
+                               this.promoted[move.start.x][move.start.y] = false;
+                               this.promoted[move.end.x][move.end.y] = true;
+                       }
+                       else if (move.vanish[0].p == V.PAWN && move.appear[0].p != V.PAWN)
+                               this.promoted[move.end.x][move.end.y] = true;
+               }
+               else //capture
+               {
+                       if (this.promoted[move.end.x][move.end.y])
+                       {
+                               move.capturePromoted = true; //required for undo
+                               this.reserve[color][VariantRules.PAWN]++;
+                               this.promoted[move.end.x][move.end.y] = false;
+                       }
+                       else
+                               this.reserve[color][move.vanish[1].p]++;
+                       if (this.promoted[move.start.x][move.start.y])
+                       {
+                               this.promoted[move.start.x][move.start.y] = false;
+                               this.promoted[move.end.x][move.end.y] = true;
+                       }
+                       else if (move.vanish[0].p == V.PAWN && move.appear[0].p != V.PAWN)
+                               this.promoted[move.end.x][move.end.y] = true;
+               }
        }
 
        unupdateVariables(move)
        {
                super.unupdateVariables(move);
                const color = this.turn;
-               if (move.vanish.length==2)
-                       this.reserve[color][move.vanish[1].p]--;
-               if (move.vanish.length==0)
+               const V = VariantRules;
+               if (move.vanish.length == 0)
                        this.reserve[color][move.appear[0].p]++;
+               else if (move.vanish.length == 1)
+               {
+                       if (this.promoted[move.end.x][move.end.y])
+                       {
+                               this.promoted[move.end.x][move.end.y] = false;
+                               if (move.vanish[0].p != V.PAWN || move.appear[0].p == V.PAWN)
+                               {
+                                       // Not a promotion (= promoted piece creation)
+                                       this.promoted[move.start.x][move.start.y] = true;
+                               }
+                       }
+               }
+               else //capture
+               {
+                       if (this.promoted[move.end.x][move.end.y])
+                       {
+                               this.promoted[move.end.x][move.end.y] = !!move.capturePromoted;
+                               if (move.vanish[0].p != V.PAWN || move.appear[0].p == V.PAWN)
+                                       this.promoted[move.start.x][move.start.y] = true;
+                       }
+                       // Un-update material:
+                       if (move.capturePromoted)
+                               this.reserve[color][VariantRules.PAWN]--;
+                       else
+                               this.reserve[color][move.vanish[1].p]--;
+               }
        }
 
        static get SEARCH_DEPTH() { return 2; } //high branching factor
 
+       evalPosition()
+       {
+               let evaluation = super.evalPosition();
+               // Add reserves:
+               for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
+               {
+                       const p = VariantRules.RESERVE_PIECES[i];
+                       evaluation += this.reserve["w"][p] * VariantRules.VALUES[p];
+                       evaluation -= this.reserve["b"][p] * VariantRules.VALUES[p];
+               }
+               return evaluation;
+       }
+
        getNotation(move)
        {
                if (move.vanish.length > 0)
@@ -165,4 +240,13 @@ class CrazyhouseRules extends ChessRules
                        String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
                return piece + "@" + finalSquare;
        }
+
+       getLongNotation(move)
+       {
+               if (move.vanish.length > 0)
+                       return super.getLongNotation(move);
+               const finalSquare =
+                       String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+               return "@" + finalSquare;
+       }
 }
index 0c484c1..0ed3edc 100644 (file)
@@ -2,7 +2,8 @@ class LoserRules extends ChessRules
 {
        initVariables(fen)
        {
-               // No castling, hence no flags
+               // No castling, hence no flags; but flags defined for compatibility
+               this.flags = "-";
                const epSq = this.moves.length > 0 ? this.getEpSquare(this.lastMove) : undefined;
                this.epSquares = [ epSq ];
        }
@@ -102,21 +103,10 @@ class LoserRules extends ChessRules
                return [];
        }
 
-       play(move, ingame)
-       {
-               if (!!ingame)
-                       move.notation = this.getNotation(move);
-               this.moves.push(move);
-               this.epSquares.push( this.getEpSquare(move) );
-               VariantRules.PlayOnBoard(this.board, move);
-       }
-
-       undo(move)
-       {
-               VariantRules.UndoOnBoard(this.board, move);
-               this.epSquares.pop();
-               this.moves.pop();
-       }
+       // Unused:
+       updateVariables(move) { }
+       unupdateVariables(move) { }
+       parseFlags(flags) { }
 
        checkGameEnd()
        {
index 3ddf503..af934f3 100644 (file)
@@ -6,6 +6,9 @@ class SwitchingRules extends ChessRules
                const c = this.getColor(x1,y1); //same as color at square 2
                const p1 = this.getPiece(x1,y1);
                const p2 = this.getPiece(x2,y2);
+               const V = VariantRules;
+               if (p1 == V.KING && p2 == V.ROOK)
+                       return []; //avoid duplicate moves (potential conflict with castle)
                let move = new Move({
                        appear: [
                                new PiPo({x:x2,y:y2,c:c,p:p1}),
@@ -21,7 +24,6 @@ class SwitchingRules extends ChessRules
                // 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) || (p2==V.PAWN && x1==lastRank))
                {
index 23ea0f0..cf34766 100644 (file)
@@ -50,14 +50,21 @@ ul
 
 h2.stageDelimiter Stage 2
 
-p.warn This stage is unimplemented for now.
+p.warn This stage is not (and probably will never be) implemented.
 
 p.
        During the game one of the two players can decide to take control of the checkered pieces.
        They thus become autonomous and vulnerable to being captured - stage 2 begins.
-       The other player is in charge of both the white and black pieces, and tries to eliminate checkered pieces.
+       The other player is in charge of both the white and black pieces, and tries to
+       eliminate checkered pieces.
        The checkered side wins by checkmating either the white or black king.
 
+h4 Variant of stage 2
+p.
+       An observer could decide to join the game by taking the checkered pieces at any moment.
+       It then becomes a chess game with three players, with some subtelties to be resolved.
+       It was tested in some (real life) games organised by the variant creator.
+
 h3 Special moves
 
 span Checkered pawns can...
@@ -69,5 +76,9 @@ ul
 h3 Credits
 
 ul
-       li The rules of Checkered Chess were thought up by Patrick Bernier and developed with the help of Benjamin Auder.
-       li Thanks also to Olive Martin, Christian Poisson, Bevis Martin, Laurent Nouhaud and Frédéric Fradet.
+       li.
+               The rules of Checkered Chess were thought up by Patrick Bernier and developed
+               with the help of Benjamin Auder.
+       li.
+               Thanks also to Olive Martin, Christian Poisson, Bevis Martin, Laurent Nouhaud
+               and Frédéric Fradet.