From a6abf094c35a26019e47fea21302c4be32ff030b Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Tue, 27 Nov 2018 18:28:36 +0100 Subject: [PATCH] Some fixes. Loser implemented. Draft Extinction,Crazyhouse,Switching --- TODO | 2 + public/javascripts/base_rules.js | 25 ++-- public/javascripts/variants/Antiking.js | 2 +- public/javascripts/variants/Crazyhouse.js | 95 ++++++++++++++ public/javascripts/variants/Extinction.js | 86 +++++++++++++ public/javascripts/variants/Grand.js | 8 +- public/javascripts/variants/Loser.js | 144 ++++++++++++++++++++++ public/javascripts/variants/Magnetic.js | 15 --- public/javascripts/variants/Switching.js | 10 ++ variants.js | 26 ++-- views/rules/Grand.pug | 2 +- views/rules/Loser.pug | 42 +++++++ views/rules/Wildebeest.pug | 2 +- 13 files changed, 416 insertions(+), 43 deletions(-) create mode 100644 public/javascripts/variants/Crazyhouse.js create mode 100644 public/javascripts/variants/Extinction.js create mode 100644 public/javascripts/variants/Loser.js create mode 100644 public/javascripts/variants/Switching.js create mode 100644 views/rules/Loser.pug diff --git a/TODO b/TODO index 23b3412c..bcda2e84 100644 --- a/TODO +++ b/TODO @@ -1,2 +1,4 @@ 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) diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js index 6cab3496..f1188ab5 100644 --- a/public/javascripts/base_rules.js +++ b/public/javascripts/base_rules.js @@ -492,9 +492,9 @@ class ChessRules const oppCol = this.getOppCol(color); let potentialMoves = []; const [sizeX,sizeY] = VariantRules.size; - for (var i=0; i= VariantRules.THRESHOLD_MATE); + this.undo(moves1[i]); + if (finish) + return moves1[i]; + } // Rank moves using a min-max at depth 2 for (let i=0; i= 3 && Math.abs(moves1[0].eval) < VariantRules.THRESHOLD_MATE) { - // TODO: show current analyzed move for depth 3, allow stopping eval (return moves1[0]) for (let i=0; i { + + moves.push(...); //concat... just appear + }); + return moves; + } + + // TODO: condition "if this is reserve" --> special square !!! coordinates ?? + getPossibleMovesFrom(sq) + { + // Assuming color is right (already checked) + return this.filterValid( this.getPotentialMovesFrom(sq) ); + } + + // TODO: add reserve moves + getAllValidMoves() + { + + } + + // TODO: also + atLeastOneMove() + { + + } + + // TODO: update reserve + updateVariables(move) + { + } + unupdateVariables(move) + { + } + + static get SEARCH_DEPTH() { return 2; } //high branching factor + + getNotation(move) + { + if (move.vanish.length > 0) + return super.getNotation(move); + // Rebirth: + const piece = + (move.appear[0].p != VariantRules.PAWN ? move.appear.p.toUpperCase() : ""); + const finalSquare = + String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x); + return piece + "@" + finalSquare; + } +} diff --git a/public/javascripts/variants/Extinction.js b/public/javascripts/variants/Extinction.js new file mode 100644 index 00000000..db330d9e --- /dev/null +++ b/public/javascripts/variants/Extinction.js @@ -0,0 +1,86 @@ +class ExtinctionRules extends ChessRules +{ + initVariables(fen) + { + super.initVariables(fen); + const V = VariantRules; + this.material = + { + "w": + { + [V.KING]: 1, + [V.QUEEN]: 1, + [V.ROOK]: 2, + [V.KNIGHT]: 2, + [V.BISHOP]: 2, + [V.PAWN]: 8 + }, + "b": + { + [V.KING]: 1, + [V.QUEEN]: 1, + [V.ROOK]: 2, + [V.KNIGHT]: 2, + [V.BISHOP]: 2, + [V.PAWN]: 8 + } + }; + } + + // TODO: verify this assertion + atLeastOneMove() + { + return true; //always at least one possible move + } + + underCheck(move) + { + return false; //there is no check + } + + getCheckSquares(move) + { + return []; + } + + updateVariables(move) + { + super.updateVariables(move); + if (move.vanish.length==2 && move.appear.length==1) + this.material[move.vanish[1].c][move.vanish[1].p]--; + } + + unupdateVariables(move) + { + super.unupdateVariables(move); + if (move.vanish.length==2 && move.appear.length==1) + this.material[move.vanish[1].c][move.vanish[1].p]++; + } + + checkGameOver() + { + if (this.checkRepetition()) + return "1/2"; + + if (this.atLeastOneMove()) // game not over? + { + const color = this.turn; + if (Object.keys(this.material[color]).some( + p => { return this.material[color][p] == 0; })) + { + return this.checkGameEnd(); + } + return "*"; + } + + return this.checkGameEnd(); + } + + // Very negative (resp. positive) if white (reps. black) pieces set is incomplete + evalPosition() + { + if (this.missAkind()) + return (this.turn=="w"?-1:1) * VariantRules.INFINITY; + return super.evalPosition(); + } +} diff --git a/public/javascripts/variants/Grand.js b/public/javascripts/variants/Grand.js index 1409bcc5..844d62c7 100644 --- a/public/javascripts/variants/Grand.js +++ b/public/javascripts/variants/Grand.js @@ -171,9 +171,9 @@ class GrandRules extends ChessRules || this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.KNIGHT], "oneStep"); } - play(move, ingame) + updateVariables(move) { - super.play(move, ingame); + super.updateVariables(move); if (move.vanish.length==2 && move.appear.length==1 && move.vanish[1].p != VariantRules.PAWN) { @@ -185,9 +185,9 @@ class GrandRules extends ChessRules } } - undo(move) + unupdateVariables(move) { - super.undo(move); + super.unupdateVariables(move); if (move.vanish.length==2 && move.appear.length==1 && move.vanish[1].p != VariantRules.PAWN) { diff --git a/public/javascripts/variants/Loser.js b/public/javascripts/variants/Loser.js new file mode 100644 index 00000000..0c484c12 --- /dev/null +++ b/public/javascripts/variants/Loser.js @@ -0,0 +1,144 @@ +class LoserRules extends ChessRules +{ + initVariables(fen) + { + // No castling, hence no flags + const epSq = this.moves.length > 0 ? this.getEpSquare(this.lastMove) : undefined; + this.epSquares = [ epSq ]; + } + + setFlags(fen) { } + + getPotentialPawnMoves([x,y]) + { + let moves = super.getPotentialPawnMoves([x,y]); + + // Complete with promotion(s) into king, if possible + const color = this.turn; + const V = VariantRules; + const [sizeX,sizeY] = VariantRules.size; + const shift = (color == "w" ? -1 : 1); + const lastRank = (color == "w" ? 0 : sizeX-1); + if (x+shift == lastRank) + { + let p = V.KING; + // Normal move + 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) + moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p})); + if (y 0) + { + for (let k=0; k 0) + return true; + } + } + } + } + } + return false; + } + + // Trim all non-capturing moves + static KeepCaptures(moves) + { + return moves.filter(m => { return m.vanish.length == 2; }); + } + + getPossibleMovesFrom(sq) + { + let moves = this.filterValid( this.getPotentialMovesFrom(sq) ); + // This is called from interface: we need to know if a capture is possible + if (this.atLeastOneCapture()) + moves = VariantRules.KeepCaptures(moves); + return moves; + } + + getAllValidMoves() + { + let moves = super.getAllValidMoves(); + if (moves.some(m => { return m.vanish.length == 2; })) + moves = VariantRules.KeepCaptures(moves); + return moves; + } + + underCheck(move) + { + return false; //No notion of check + } + + getCheckSquares(move) + { + 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(); + } + + checkGameEnd() + { + // No valid move: you win! + return this.turn == "w" ? "1-0" : "0-1"; + } + + static get VALUES() { //experimental... + return { + 'p': 1, + 'r': 7, + 'n': 3, + 'b': 3, + 'q': 5, + 'k': 5 + }; + } + + static get SEARCH_DEPTH() { return 4; } + + evalPosition() + { + return - super.evalPosition(); //better with less material + } +} diff --git a/public/javascripts/variants/Magnetic.js b/public/javascripts/variants/Magnetic.js index e126f1ce..844cc689 100644 --- a/public/javascripts/variants/Magnetic.js +++ b/public/javascripts/variants/Magnetic.js @@ -216,19 +216,4 @@ class MagneticRules extends ChessRules static get THRESHOLD_MATE() { return 500; //checkmates evals may be slightly below 1000 } - - getComputerMove() - { - let moves1 = this.getAllValidMoves(); - // Can I mate in 1 ? - for (let i of _.shuffle(_.range(moves1.length))) - { - this.play(moves1[i]); - const finish = (Math.abs(this.evalPosition()) >= VariantRules.THRESHOLD_MATE); - this.undo(moves1[i]); - if (finish) - return moves1[i]; - } - return super.getComputerMove(moves1); - } } diff --git a/public/javascripts/variants/Switching.js b/public/javascripts/variants/Switching.js new file mode 100644 index 00000000..a7d67879 --- /dev/null +++ b/public/javascripts/variants/Switching.js @@ -0,0 +1,10 @@ +//https://www.chessvariants.com/diffmove.dir/switching.html +class SwitchingRules extends ChessRules +{ + //TODO: + // Move completion: promote switched pawns (as in Magnetic) + + // To every piece potential moves: add switchings + + // Prevent king switching if under check +} diff --git a/variants.js b/variants.js index 175c4269..8a00fe00 100644 --- a/variants.js +++ b/variants.js @@ -1,15 +1,15 @@ module.exports = [ - { "name" : "Checkered", "description" : "Shared pieces" }, - { "name" : "Zen", "description" : "Reverse captures" }, - { "name" : "Atomic", "description" : "Explosive captures" }, - { "name" : "Chess960", "description" : "Standard rules" }, - { "name" : "Antiking", "description" : "Keep antiking in check" }, - { "name" : "Magnetic", "description" : "Laws of attraction" }, - { "name" : "Alice", "description" : "Both sides of the mirror" }, - { "name" : "Grand", "description" : "Big board" }, - { "name" : "Wildebeest", "description" : "Balanced sliders & leapers" }, -// { "name" : "Loser", "description" : "Lose all pieces" }, -// { "name" : "Crazyhouse", "description" : "Captures reborn" }, -// { "name" : "Switching", "description" : "Exchange pieces positions" }, -// { "name" : "Absorption", "description" : "Capture enhance movements" }, + { "name": "Checkered", "description": "Shared pieces" }, + { "name": "Zen", "description": "Reverse captures" }, + { "name": "Atomic", "description": "Explosive captures" }, + { "name": "Chess960", "description": "Standard rules" }, + { "name": "Antiking", "description": "Keep antiking in check" }, + { "name": "Magnetic", "description": "Laws of attraction" }, + { "name": "Alice", "description": "Both sides of the mirror" }, + { "name": "Grand", "description": "Big board" }, + { "name": "Wildebeest", "description": "Balanced sliders & leapers" }, + { "name": "Loser", "description": "Lose all pieces" }, + { "name": "Crazyhouse", "description": "Captures reborn" }, + { "name": "Switching", "description": "Exchange pieces positions" }, + { "name": "Extinction", "description": "Capture all of a kind" }, ]; diff --git a/views/rules/Grand.pug b/views/rules/Grand.pug index d5ca2016..67b849fe 100644 --- a/views/rules/Grand.pug +++ b/views/rules/Grand.pug @@ -47,6 +47,6 @@ p. h3 Credits p - | Grand chess page on + | Grand chess page on a(href="https://www.chessvariants.com/large.dir/freeling.html") chessvariants.com | . diff --git a/views/rules/Loser.pug b/views/rules/Loser.pug new file mode 100644 index 00000000..3b6b1b62 --- /dev/null +++ b/views/rules/Loser.pug @@ -0,0 +1,42 @@ +p.boxed + | Win by losing all your pieces. Capture is mandatory. + +h3 Specifications + +ul + li Chessboard: standard. + li Material: standard. + li Non-capturing moves: standard (when allowed). + li Special moves: no castle. + li Captures: standard. + li End of game: stalemate or lose all material. + +h3 Basics + +p. + The goal is to lose all pieces, or get stalemated like on the following diagram. + The king has no royal status: it can be taken as any other piece. + Thus, there is no castle rule, no checks. + +figure.diagram-container + .diagram + | fen:6nB/6P1/8/4p3/2p1P3/2P5/8/8: + figcaption White cannot move: 1-0. + +h3 Special moves + +p. + Castling is not possible, but en-passant captures are allowed. + Pawns may promote into king (so you can potentially have several kings on the board). + +h3 End of the game + +p You can win by losing all material or be stalemated. + +h3 Credits + +p + | This is a popular variant, played in many places on the web. + | A starting point can be the + a(href="https://en.wikipedia.org/wiki/Losing_Chess") wikipedia page + | . diff --git a/views/rules/Wildebeest.pug b/views/rules/Wildebeest.pug index cb883f9d..482ab6b5 100644 --- a/views/rules/Wildebeest.pug +++ b/views/rules/Wildebeest.pug @@ -45,6 +45,6 @@ p. h3 Credits p - | Wildebeest page on + | Wildebeest page on a(href="https://www.chessvariants.com/large.dir/wildebeest.html") chessvariants.com | . -- 2.44.0