From 375ecdd1387e729f85ed114e82253469e4849869 Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Mon, 24 Dec 2018 13:50:44 +0100 Subject: [PATCH] Prepare some more variants (unfinished) --- LICENSE | 5 ++ public/javascripts/base_rules.js | 93 ++++++++++------------- public/javascripts/components/game.js | 48 +++++++----- public/javascripts/variant.js | 3 + public/javascripts/variants/Berolina.js | 93 +++++++++++++++++++++++ public/javascripts/variants/Dark.js | 67 ++++++++++++++++ public/javascripts/variants/Grand.js | 7 +- public/javascripts/variants/Magnetic.js | 61 --------------- public/javascripts/variants/Wildebeest.js | 7 +- public/stylesheets/variant.sass | 3 + views/variant.pug | 5 +- 11 files changed, 249 insertions(+), 143 deletions(-) create mode 100644 LICENSE create mode 100644 public/javascripts/variants/Berolina.js create mode 100644 public/javascripts/variants/Dark.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..695f90a7 --- /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. diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js index 7f27e855..d85ff869 100644 --- a/public/javascripts/base_rules.js +++ b/public/javascripts/base_rules.js @@ -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 { - // 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 { 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', diff --git a/public/javascripts/variant.js b/public/javascripts/variant.js index a7d18200..819bf90f 100644 --- a/public/javascripts/variant.js +++ b/public/javascripts/variant.js @@ -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 index 00000000..c556f517 --- /dev/null +++ b/public/javascripts/variants/Berolina.js @@ -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 index 00000000..9cf50513 --- /dev/null +++ b/public/javascripts/variants/Dark.js @@ -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; diff --git a/public/javascripts/variants/Grand.js b/public/javascripts/variants/Grand.js index 4271a693..7248f578 100644 --- a/public/javascripts/variants/Grand.js +++ b/public/javascripts/variants/Grand.js @@ -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); } diff --git a/public/javascripts/variants/Magnetic.js b/public/javascripts/variants/Magnetic.js index c4995dd3..8480d799 100644 --- a/public/javascripts/variants/Magnetic.js +++ b/public/javascripts/variants/Magnetic.js @@ -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 { - // 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