From 9234226104764b91df9d677fb360ad538b98510c Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Mon, 10 Dec 2018 16:12:58 +0100 Subject: [PATCH] Some code cleaning + clarifying (TODO: work on variables names) --- README.md | 3 +- app.js | 3 +- public/images/Hexagonal_chess.svg | 4 +- public/images/pieces/{LICENSE => README} | 0 public/images/tmp_checkered/README | 2 + public/javascripts/base_rules.js | 23 +++++-- public/javascripts/components/game.js | 66 ++++++++++++------- public/javascripts/components/rules.js | 4 +- .../javascripts/components/variantSummary.js | 1 + public/javascripts/index.js | 5 ++ public/javascripts/utils/misc.js | 3 +- public/javascripts/variants/Alice.js | 29 ++++---- public/javascripts/variants/Antiking.js | 17 ++--- public/javascripts/variants/Atomic.js | 13 ++-- public/javascripts/variants/Checkered.js | 9 ++- public/javascripts/variants/Crazyhouse.js | 4 +- public/javascripts/variants/Extinction.js | 2 +- public/javascripts/variants/Grand.js | 37 +++++++---- public/javascripts/variants/Loser.js | 19 ++++-- public/javascripts/variants/Magnetic.js | 2 +- public/javascripts/variants/Wildebeest.js | 48 ++++++++------ public/javascripts/variants/Zen.js | 27 +++----- public/stylesheets/index.sass | 19 ++++++ public/stylesheets/variant.sass | 15 ++++- views/index.pug | 52 +++++++++------ views/layout.pug | 28 ++++---- views/rules/Alice.pug | 13 ++-- views/rules/Antiking.pug | 14 ++-- views/rules/Atomic.pug | 16 +++-- views/rules/Checkered.pug | 24 +++++-- views/rules/Crazyhouse.pug | 3 +- views/rules/Grand.pug | 6 +- views/rules/Magnetic.pug | 9 ++- views/rules/Switching.pug | 3 +- views/rules/Wildebeest.pug | 3 +- views/rules/Zen.pug | 13 ++-- views/variant.pug | 7 +- 37 files changed, 345 insertions(+), 201 deletions(-) rename public/images/pieces/{LICENSE => README} (100%) create mode 100644 public/images/tmp_checkered/README diff --git a/README.md b/README.md index c5cb5081..3147c1fc 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Pieces images where found at various locations. ## Installation (for developers) 0. Install git-fat https://github.com/jedbrown/git-fat - 1. Rename public/javascripts/utils/socket\_url.js.dist into socket\_url.js and adjust its content. + 1. Rename public/javascripts/utils/socket\_url.js.dist into socket\_url.js + and adjust its content. 2. git fat init && git fat pull 3. npm i && npm start diff --git a/app.js b/app.js index 7a8b98f3..e32ef39e 100644 --- a/app.js +++ b/app.js @@ -19,7 +19,8 @@ if (app.get('env') === 'development') } else { - app.set('trust proxy', true); //http://dev.rdybarra.com/2016/06/23/Production-Logging-With-Morgan-In-Express/ + // http://dev.rdybarra.com/2016/06/23/Production-Logging-With-Morgan-In-Express/ + app.set('trust proxy', true); // In prod, only log error responses (https://github.com/expressjs/morgan) app.use(logger('combined', { skip: function (req, res) { return res.statusCode < 400 } diff --git a/public/images/Hexagonal_chess.svg b/public/images/Hexagonal_chess.svg index fe8626c2..7bc0fb4b 100644 --- a/public/images/Hexagonal_chess.svg +++ b/public/images/Hexagonal_chess.svg @@ -1,6 +1,6 @@ - + - \ No newline at end of file + diff --git a/public/images/pieces/LICENSE b/public/images/pieces/README similarity index 100% rename from public/images/pieces/LICENSE rename to public/images/pieces/README diff --git a/public/images/tmp_checkered/README b/public/images/tmp_checkered/README new file mode 100644 index 00000000..aac1f828 --- /dev/null +++ b/public/images/tmp_checkered/README @@ -0,0 +1,2 @@ +Initial PNG images designed by Patrick Bernier, +from a model found online (cannot remember where, sorry) diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js index 860495a4..5c1ef97c 100644 --- a/public/javascripts/base_rules.js +++ b/public/javascripts/base_rules.js @@ -1,3 +1,6 @@ +// (Orthodox) Chess rules are defined in ChessRules class. +// Variants generally inherit from it, and modify some parts. + class PiPo //Piece+Position { // o: {piece[p], color[c], posX[x], posY[y]} @@ -61,7 +64,7 @@ class ChessRules { this.INIT_COL_KING = {'w':-1, 'b':-1}; this.INIT_COL_ROOK = {'w':[-1,-1], 'b':[-1,-1]}; - this.kingPos = {'w':[-1,-1], 'b':[-1,-1]}; //respective squares of white and black king + this.kingPos = {'w':[-1,-1], 'b':[-1,-1]}; //squares of white and black king const fenParts = fen.split(" "); const position = fenParts[0].split("/"); for (let i=0; i moves1[i].eval) || (color=="b" && eval2 < moves1[i].eval)) + if ((color=="w" && eval2 > moves1[i].eval) + || (color=="b" && eval2 < moves1[i].eval)) + { moves1[i].eval = eval2; + } this.undo(moves1[i]); } moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); }); @@ -1137,7 +1145,8 @@ class ChessRules const d = new Date(); const opponent = mode=="human" ? "Anonymous" : "Computer"; pgn += '[Variant "' + variant + '"]
'; - pgn += '[Date "' + d.getFullYear() + '-' + (d.getMonth()+1) + '-' + zeroPad(d.getDate()) + '"]
'; + pgn += '[Date "' + d.getFullYear() + '-' + (d.getMonth()+1) + + '-' + zeroPad(d.getDate()) + '"]
'; pgn += '[White "' + (mycolor=='w'?'Myself':opponent) + '"]
'; pgn += '[Black "' + (mycolor=='b'?'Myself':opponent) + '"]
'; pgn += '[FenStart "' + fenStart + '"]
'; diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js index 44db7dd8..7b238f5c 100644 --- a/public/javascripts/components/game.js +++ b/public/javascripts/components/game.js @@ -1,13 +1,14 @@ +// Game logic on a variant page Vue.component('my-game', { data: function() { return { vr: null, //object to check moves, store them, FEN.. mycolor: "w", possibleMoves: [], //filled after each valid click/dragstart - choices: [], //promotion pieces, or checkered captures... (contain possible pieces) + choices: [], //promotion pieces, or checkered captures... (as moves) start: {}, //pixels coordinates + id of starting square (click or drag) selectedPiece: null, //moving piece (or clicked piece) - conn: null, //socket messages + conn: null, //socket connection score: "*", //'*' means 'unfinished' mode: "idle", //human, friend, computer or idle (when not playing) oppid: "", //opponent ID in case of HH game @@ -16,7 +17,7 @@ Vue.component('my-game', { fenStart: "", incheck: [], pgnTxt: "", - expert: getCookie("expert") === "1" ? true : false, + expert: (getCookie("expert") === "1" ? true : false), gameId: "", //used to limit computer moves' time }; }, @@ -127,7 +128,6 @@ Vue.component('my-game', { { on: { click: this.toggleExpertMode }, attrs: { "aria-label": 'Toggle expert mode' }, - style: { "padding-top": "0", "margin-top": "0" }, 'class': { "tooltip":true, "topindicator": true, @@ -273,10 +273,12 @@ Vue.component('my-game', { actionArray = actionArray.concat([ h('button', { - style: { "margin-left": "30px" }, on: { click: e => this.undo() }, attrs: { "aria-label": 'Undo' }, - "class": { "small": smallScreen }, + "class": { + "small": smallScreen, + "marginleft": true, + }, }, [h('i', { 'class': { "material-icons": true } }, "fast_rewind")]), h('button', @@ -295,10 +297,12 @@ Vue.component('my-game', { [ h('button', { - style: { "margin-left": "30px" }, on: { click: this.undoInGame }, attrs: { "aria-label": 'Undo' }, - "class": { "small": smallScreen }, + "class": { + "small": smallScreen, + "marginleft": true, + }, }, [h('i', { 'class': { "material-icons": true } }, "undo")] ), @@ -334,7 +338,7 @@ Vue.component('my-game', { } }), h('sup', - {style: { "padding-left":"40%"} }, + {"class": { "reserve-count": true } }, [ this.vr.reserve[this.mycolor][VariantRules.RESERVE_PIECES[i]] ] ) ])); @@ -358,21 +362,25 @@ Vue.component('my-game', { } }), h('sup', - {style: { "padding-left":"40%"} }, + {"class": { "reserve-count": true } }, [ this.vr.reserve[oppCol][VariantRules.RESERVE_PIECES[i]] ] ) ])); } let reserves = h('div', { - 'class':{'game':true}, - style: {"margin-bottom": "20px"}, + 'class':{ + 'game': true, + "reserve-div": true, + }, }, [ h('div', { - 'class': { 'row': true }, - style: {"margin-bottom": "15px"}, + 'class': { + 'row': true, + "reserve-row-1": true, + }, }, myReservePiecesArray ), @@ -605,7 +613,8 @@ Vue.component('my-game', { this.myid = continuation ? localStorage.getItem("myid") : getRandString(); if (!continuation) { - // HACK: play a small silent sound to allow "new game" sound later if tab not focused + // HACK: play a small silent sound to allow "new game" sound later + // if tab not focused (TODO: does it really work ?!) new Audio("/sounds/silent.mp3").play().then(() => {}).catch(err => {}); } this.conn = new WebSocket(url + "/?sid=" + this.myid + "&page=" + variant); @@ -631,7 +640,8 @@ Vue.component('my-game', { switch (data.code) { case "newgame": //opponent found - this.newGame("human", data.fen, data.color, data.oppid); //oppid: opponent socket ID + // oppid: opponent socket ID + this.newGame("human", data.fen, data.color, data.oppid); break; case "newmove": //..he played! this.play(data.move, "animate"); @@ -717,7 +727,8 @@ Vue.component('my-game', { // Prepare and trigger download link let downloadAnchor = document.getElementById("download"); downloadAnchor.setAttribute("download", "game.pgn"); - downloadAnchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(content); + downloadAnchor.href = "data:text/plain;charset=utf-8," + + encodeURIComponent(content); downloadAnchor.click(); }, endGame: function(score) { @@ -884,7 +895,8 @@ Vue.component('my-game', { }, playComputerMove: function() { const timeStart = Date.now(); - const nbMoves = this.vr.moves.length; //using played moves to know if search finished + // We use moves' count to know if search finished: + const nbMoves = this.vr.moves.length; const gameId = this.gameId; //to know if game was reset before timer end setTimeout( () => { @@ -943,7 +955,8 @@ Vue.component('my-game', { const iCanPlay = this.mode!="idle" && (this.mode=="friend" || this.vr.canIplay(this.mycolor,startSquare)); this.possibleMoves = iCanPlay ? this.vr.getPossibleMovesFrom(startSquare) : []; - // Next line add moving piece just after current image (required for Crazyhouse reserve) + // Next line add moving piece just after current image + // (required for Crazyhouse reserve) e.target.parentNode.insertBefore(this.selectedPiece, e.target.nextSibling); } }, @@ -966,16 +979,20 @@ Vue.component('my-game', { return; e = e || window.event; // Read drop target (or parentElement, parentNode... if type == "img") - this.selectedPiece.style.zIndex = -3000; //HACK to find square from final coordinates + this.selectedPiece.style.zIndex = -3000; //HACK to find square from final coords const [offsetX,offsetY] = !!e.clientX ? [e.clientX,e.clientY] : [e.changedTouches[0].pageX, e.changedTouches[0].pageY]; let landing = document.elementFromPoint(offsetX, offsetY); this.selectedPiece.style.zIndex = 3000; - while (landing.tagName == "IMG") //classList.contains(piece) fails because of mark/highlight + // Next condition: classList.contains(piece) fails because of marks + while (landing.tagName == "IMG") landing = landing.parentNode; - if (this.start.id == landing.id) //a click: selectedPiece and possibleMoves already filled + if (this.start.id == landing.id) + { + // A click: selectedPiece and possibleMoves are already filled return; + } // OK: process move attempt let endSquare = this.getSquareFromId(landing.id); let moves = this.findMatchingMoves(endSquare); @@ -1006,7 +1023,7 @@ Vue.component('my-game', { let translation = {x:rectEnd.x-rectStart.x, y:rectEnd.y-rectStart.y}; let movingPiece = document.querySelector("#" + this.getSquareId(move.start) + " > img.piece"); - // HACK for animation (with positive translate, image slides "under background"...) + // HACK for animation (with positive translate, image slides "under background") // Possible improvement: just alter squares on the piece's way... squares = document.getElementsByClassName("board"); for (let i=0; i { diff --git a/public/javascripts/components/rules.js b/public/javascripts/components/rules.js index 718021d0..fd05d44d 100644 --- a/public/javascripts/components/rules.js +++ b/public/javascripts/components/rules.js @@ -1,3 +1,4 @@ +// Load rules on variant page Vue.component('my-rules', { data: function() { return { content: "" }; @@ -16,7 +17,7 @@ Vue.component('my-rules', { }; xhr.open("GET", "/rules/" + variant, true); xhr.setRequestHeader('X-Requested-With', "XMLHttpRequest"); - xhr.send(null); //TODO: or just xhr.send() ? + xhr.send(); }, methods: { drawDiag: function(fen) { @@ -51,7 +52,6 @@ Vue.component('my-rules', { boardDiv += "
"; for (let j=startY; j>=0 && j"; if (markArray.length>0 && markArray[i][j]) diff --git a/public/javascripts/components/variantSummary.js b/public/javascripts/components/variantSummary.js index ed9b7b64..620f810f 100644 --- a/public/javascripts/components/variantSummary.js +++ b/public/javascripts/components/variantSummary.js @@ -1,3 +1,4 @@ +// Show a variant summary on index Vue.component('my-variant-summary', { props: ['vobj'], template: ` diff --git a/public/javascripts/index.js b/public/javascripts/index.js index 007c9fe2..29e48f74 100644 --- a/public/javascripts/index.js +++ b/public/javascripts/index.js @@ -1,3 +1,4 @@ +// Javascript for index page: mostly counters updating new Vue({ el: "#indexPage", data: { @@ -79,5 +80,9 @@ new Vue({ document.getElementById("modal-b4welcome").checked = false; document.getElementById("modal-welcome").checked = true; }, + markAsVisited: function() { + setCookie('visited', '1'); + document.getElementById('modal-welcome').checked = false; + }, }, }); diff --git a/public/javascripts/utils/misc.js b/public/javascripts/utils/misc.js index 89a545cf..0b68ac45 100644 --- a/public/javascripts/utils/misc.js +++ b/public/javascripts/utils/misc.js @@ -1,5 +1,4 @@ // Source: https://www.quirksmode.org/js/cookies.html - function setCookie(name,value) { var date = new Date(); @@ -22,9 +21,9 @@ function getCookie(name) { return null; } +// Random (enough) string for socket and game IDs function getRandString() { - // Random enough (for socket and game IDs) return (Date.now().toString(36) + Math.random().toString(36).substr(2, 7)) .toUpperCase(); } diff --git a/public/javascripts/variants/Alice.js b/public/javascripts/variants/Alice.js index b286bf66..220fbd40 100644 --- a/public/javascripts/variants/Alice.js +++ b/public/javascripts/variants/Alice.js @@ -175,7 +175,9 @@ class AliceRules extends ChessRules if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) == color) { const mirrorSide = - (Object.keys(VariantRules.ALICE_CODES).includes(this.getPiece(i,j)) ? 1 : 2); + Object.keys(VariantRules.ALICE_CODES).includes(this.getPiece(i,j)) + ? 1 + : 2; Array.prototype.push.apply(potentialMoves, this.getPotentialMovesFrom([i,j], sideBoard[mirrorSide-1])); } @@ -291,20 +293,17 @@ class AliceRules extends ChessRules } static get VALUES() { - return { - 'p': 1, - 's': 1, - 'r': 5, - 'u': 5, - 'n': 3, - 'o': 3, - 'b': 3, - 'c': 3, - 'q': 9, - 't': 9, - 'k': 1000, - 'l': 1000 - }; + return Object.assign( + ChessRules.VALUES, + { + 's': 1, + 'u': 5, + 'o': 3, + 'c': 3, + 't': 9, + 'l': 1000, + } + ); } getNotation(move) diff --git a/public/javascripts/variants/Antiking.js b/public/javascripts/variants/Antiking.js index b22cbd75..014a9c89 100644 --- a/public/javascripts/variants/Antiking.js +++ b/public/javascripts/variants/Antiking.js @@ -1,6 +1,5 @@ class AntikingRules extends ChessRules { - // Path to pieces static getPpath(b) { return b[1]=='a' ? "Antiking/"+b : b; @@ -142,17 +141,11 @@ class AntikingRules extends ChessRules return color == "w" ? "0-1" : "1-0"; } - // Pieces values (TODO: use Object.assign() + ChessRules.VALUES ?) static get VALUES() { - return { - 'p': 1, - 'r': 5, - 'n': 3, - 'b': 3, - 'q': 9, - 'k': 1000, - 'a': 1000 - }; + return Object.assign( + ChessRules.VALUES, + { 'a': 1000 } + ); } static GenRandInitFen() @@ -206,7 +199,7 @@ class AntikingRules extends ChessRules let fen = pieces["b"].join("") + "/" + ranks23_black + "/8/8/" + ranks23_white + "/" + pieces["w"].join("").toUpperCase() + - " 1111"; //add flags + " 1111"; return fen; } } diff --git a/public/javascripts/variants/Atomic.js b/public/javascripts/variants/Atomic.js index 21fbedb2..28ee1f26 100644 --- a/public/javascripts/variants/Atomic.js +++ b/public/javascripts/variants/Atomic.js @@ -8,7 +8,7 @@ class AtomicRules extends ChessRules moves.forEach(m => { if (m.vanish.length > 1 && m.appear.length <= 1) //avoid castles { - // Explosion! TODO: drop moves which explode our king here + // Explosion! TODO(?): drop moves which explode our king here let steps = [ [-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1] ]; for (let step of steps) { @@ -17,7 +17,8 @@ class AtomicRules extends ChessRules if (x>=0 && x<8 && y>=0 && y<8 && this.board[x][y] != VariantRules.EMPTY && this.getPiece(x,y) != VariantRules.PAWN) { - m.vanish.push(new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y})); + m.vanish.push( + new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y})); } } m.end = {x:m.appear[0].x, y:m.appear[0].y}; @@ -47,8 +48,11 @@ class AtomicRules extends ChessRules isAttacked(sq, colors) { - if (this.getPiece(sq[0],sq[1]) == VariantRules.KING && this.isAttackedByKing(sq, colors)) + if (this.getPiece(sq[0],sq[1]) == VariantRules.KING + && this.isAttackedByKing(sq, colors)) + { return false; //king cannot take... + } return (this.isAttackedByPawn(sq, colors) || this.isAttackedByRook(sq, colors) || this.isAttackedByKnight(sq, colors) @@ -145,7 +149,6 @@ class AtomicRules extends ChessRules return color == "w" ? "0-1" : "1-0"; if (!this.isAttacked(kp, [this.getOppCol(color)])) return "1/2"; - // Checkmate - return color == "w" ? "0-1" : "1-0"; + return color == "w" ? "0-1" : "1-0"; //checkmate } } diff --git a/public/javascripts/variants/Checkered.js b/public/javascripts/variants/Checkered.js index b9e7223b..019e1e2c 100644 --- a/public/javascripts/variants/Checkered.js +++ b/public/javascripts/variants/Checkered.js @@ -103,7 +103,8 @@ class CheckeredRules extends ChessRules canIplay(side, [x,y]) { - return ((side=='w' && this.moves.length%2==0) || (side=='b' && this.moves.length%2==1)) + return ((side=='w' && this.moves.length%2==0) + || (side=='b' && this.moves.length%2==1)) && [side,'c'].includes(this.getColor(x,y)); } @@ -166,7 +167,8 @@ class CheckeredRules extends ChessRules this.play(move); const color = this.turn; this.moves.push(move); //artifically change turn, for checkered pawns (TODO) - const kingAttacked = this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c']); + const kingAttacked = this.isAttacked( + this.kingPos[color], [this.getOppCol(color),'c']); let res = kingAttacked ? [ JSON.parse(JSON.stringify(this.kingPos[color])) ] //need to duplicate! : [ ]; @@ -259,7 +261,8 @@ class CheckeredRules extends ChessRules { // Capture let startColumn = String.fromCharCode(97 + move.start.y); - notation = startColumn + "x" + finalSquare + "=" + move.appear[0].p.toUpperCase(); + notation = startColumn + "x" + finalSquare + + "=" + move.appear[0].p.toUpperCase(); } else //no capture { diff --git a/public/javascripts/variants/Crazyhouse.js b/public/javascripts/variants/Crazyhouse.js index 88193edb..b297f1fb 100644 --- a/public/javascripts/variants/Crazyhouse.js +++ b/public/javascripts/variants/Crazyhouse.js @@ -51,7 +51,7 @@ class CrazyhouseRules extends ChessRules return color + VariantRules.RESERVE_PIECES[index]; } - // Put an ordering on reserve pieces + // Ordering on reserve pieces static get RESERVE_PIECES() { const V = VariantRules; return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]; @@ -97,7 +97,7 @@ class CrazyhouseRules extends ChessRules const sizeX = VariantRules.size[0]; if (x >= sizeX) { - // Reserves, outside of board: x == sizeX + // Reserves, outside of board: x == sizeX(+1) return this.getReserveMoves([x,y]); } // Standard moves diff --git a/public/javascripts/variants/Extinction.js b/public/javascripts/variants/Extinction.js index 9be4b0d7..f0ebeabb 100644 --- a/public/javascripts/variants/Extinction.js +++ b/public/javascripts/variants/Extinction.js @@ -123,13 +123,13 @@ class ExtinctionRules extends ChessRules return this.turn == "w" ? "0-1" : "1-0"; } - // Very negative (resp. positive) if white (reps. black) pieces set is incomplete evalPosition() { const color = this.turn; if (Object.keys(this.material[color]).some( p => { return this.material[color][p] == 0; })) { + // Very negative (resp. positive) if white (reps. black) pieces set is incomplete return (color=="w"?-1:1) * VariantRules.INFINITY; } return super.evalPosition(); diff --git a/public/javascripts/variants/Grand.js b/public/javascripts/variants/Grand.js index 844d62c7..9e1504d9 100644 --- a/public/javascripts/variants/Grand.js +++ b/public/javascripts/variants/Grand.js @@ -1,4 +1,5 @@ -//https://www.chessvariants.com/large.dir/freeling.html +// NOTE: initial setup differs from the original; see +// https://www.chessvariants.com/large.dir/freeling.html class GrandRules extends ChessRules { static getPpath(b) @@ -84,10 +85,16 @@ class GrandRules extends ChessRules } } // 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])); - if (y0 && 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})); - if (y0 && 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 (y0 && 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 (y0 && 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])); - if (y0 && 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})); - if (y { return 2*i+1; }); let bishop2Pos = positions[randIndexes_tmp[0]]; let camel2Pos = positions[randIndexes_tmp[1]]; - // Remove chosen squares - for (let idx of randIndexes.concat(randIndexes_tmp).sort((a,b) => { return b-a; })) + for (let idx of randIndexes.concat(randIndexes_tmp) + .sort((a,b) => { return b-a; })) //largest indices first + { positions.splice(idx, 1); + } - // Get random squares for knights let randIndex = _.random(6); let knight1Pos = positions[randIndex]; positions.splice(randIndex, 1); @@ -208,22 +221,19 @@ class WildebeestRules extends ChessRules let knight2Pos = positions[randIndex]; positions.splice(randIndex, 1); - // Get random square for queen randIndex = _.random(4); let queenPos = positions[randIndex]; positions.splice(randIndex, 1); - // ...random square for wildebeest + // Random square for wildebeest randIndex = _.random(3); let wildebeestPos = positions[randIndex]; positions.splice(randIndex, 1); - // Rooks and king positions are now fixed, because of the ordering rook-king-rook let rook1Pos = positions[0]; let kingPos = positions[1]; let rook2Pos = positions[2]; - // Finally put the shuffled pieces in the board array pieces[c][rook1Pos] = 'r'; pieces[c][knight1Pos] = 'n'; pieces[c][bishop1Pos] = 'b'; @@ -236,9 +246,9 @@ class WildebeestRules extends ChessRules pieces[c][knight2Pos] = 'n'; pieces[c][rook2Pos] = 'r'; } - let fen = pieces[0].join("") + + let fen = pieces["b"].join("") + "/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" + - pieces[1].join("").toUpperCase() + + pieces["w"].join("").toUpperCase() + " 1111"; return fen; } diff --git a/public/javascripts/variants/Zen.js b/public/javascripts/variants/Zen.js index 31dfccd1..c1f48147 100644 --- a/public/javascripts/variants/Zen.js +++ b/public/javascripts/variants/Zen.js @@ -6,7 +6,7 @@ class ZenRules extends ChessRules return undefined; } - // TODO: some duplicated code in 2 next functions + // TODO(?): some duplicated code in 2 next functions getSlideNJumpMoves([x,y], steps, oneStep) { const color = this.getColor(x,y); @@ -83,21 +83,12 @@ class ZenRules extends ChessRules // Find possible captures from a square: look in every direction! findCaptures(sq) { - var moves = []; + let moves = []; - // PAWN Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.PAWN)); - - // ROOK Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.ROOK)); - - // KNIGHT Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.KNIGHT)); - - // BISHOP Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.BISHOP)); - - // QUEEN Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.QUEEN)); return moves; @@ -149,22 +140,24 @@ class ZenRules extends ChessRules getPotentialRookMoves(sq) { - let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.ROOK]); + let noCaptures = this.getSlideNJumpMoves( + sq, VariantRules.steps[VariantRules.ROOK]); let captures = this.findCaptures(sq); return noCaptures.concat(captures); } getPotentialKnightMoves(sq) { - let noCaptures = this.getSlideNJumpMoves(sq, - VariantRules.steps[VariantRules.KNIGHT], "oneStep"); + let noCaptures = this.getSlideNJumpMoves( + sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep"); let captures = this.findCaptures(sq); return noCaptures.concat(captures); } getPotentialBishopMoves(sq) { - let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.BISHOP]); + let noCaptures = this.getSlideNJumpMoves( + sq, VariantRules.steps[VariantRules.BISHOP]); let captures = this.findCaptures(sq); return noCaptures.concat(captures); } @@ -172,8 +165,8 @@ class ZenRules extends ChessRules 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); } diff --git a/public/stylesheets/index.sass b/public/stylesheets/index.sass index 6f5fedfe..f67ed08b 100644 --- a/public/stylesheets/index.sass +++ b/public/stylesheets/index.sass @@ -16,6 +16,9 @@ .card > h3.section.red color: #cc3300 +.main-title + font-style: italic + #welcome, #help max-height: 100vh max-width: 90vw @@ -37,6 +40,22 @@ #welcome ul > li font-family: monospace +.read-this + color: blue + text-decoration: underline + cursor: pointer + +.emphasis + font-style: italic + color: purple + +.disable-msg + cursor: pointer + color: darkred + +.smallfont + font-size: 0.8em + table.list-table width: 300px margin: 0 auto diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass index d23e83f0..c98807f3 100644 --- a/public/stylesheets/variant.sass +++ b/public/stylesheets/variant.sass @@ -55,6 +55,18 @@ figure.diagram-container > .diagram float: right margin: 0 20px 10px 0 +.marginleft + margin-left: 30px + +.reserve-count + padding-left: 40% + +.reserve-div + margin-bottom: 20px + +.reserve-row-1 + margin-bottom: 15px + .connected background-color: green @@ -62,7 +74,8 @@ figure.diagram-container > .diagram background-color: red .expert-switch - padding: 5px 10px + padding: 0 10px 5px 10px + margin: 0 .expert-mode, button.expert-mode:hover background-color: #ffcc99 diff --git a/views/index.pug b/views/index.pug index 6e8cc0b4..ba5393cc 100644 --- a/views/index.pug +++ b/views/index.pug @@ -7,9 +7,10 @@ block content .container#indexPage .row .col-sm-12 - h1.text-center(style="font-style:italic") Welcome to v[ariant] chess club ! + h1.text-center.main-title Welcome to v[ariant] chess club ! h2.text-center - span.help(onClick="document.getElementById('modal-help').checked=true") Help ? + span.help(onClick="document.getElementById('modal-help').checked=true") + | Help ? a(href="/demo.webm") Demo ! input#modal-help.modal(type="checkbox") div(role="dialog") @@ -17,9 +18,13 @@ block content label.modal-close(for="modal-help") h3.blue.section Tips p.section - span.conditional-jump On a variant page, read the rules by clicking on the title "<Variant> rules". - span.conditional-jump Try playing against a human: click the leftmost "new game" button :) - | ...then, while waiting you can play against a (rather weak) bot or a friend. + span.conditional-jump + | On a variant page, read the rules by clicking on the title + | "<Variant> rules". + span.conditional-jump + | Try playing against a human: click the leftmost "new game" button :) + | ...then, while waiting you can play against a (rather weak) bot + | or a friend. // TODO? On the index page, try typing the first letters of a variant. h3.blue.section Comments p.section. @@ -27,20 +32,21 @@ block content Games are untimed, and played anonymously. #[br] No chat, to rather focus on the moves :) h3.red.section Bug report - p.section. - If you find a bug in a game, please follow this procedure: #[br] - 1. stop playing: click on the resign button; #[br] - 2. click on the PGN to download it; #[br] - 3. send an email to - #[a(href="mailto:contact@vchess.club?subject=[vchess.club] bug report") contact@vchess.club] - with relevant comments and the PGN attached. Thank you! + p.section + | If you find a bug in a game, please follow this procedure: #[br] + | 1. stop playing: click on the resign button; #[br] + | 2. click on the PGN to download it; #[br] + | 3. send an email to + a(href="mailto:contact@vchess.club?subject=[vchess.club] bug report") + | contact@vchess.club + | with relevant comments and the PGN attached. Thank you! input#modal-b4welcome.modal(type="checkbox") div(role="dialog") #b4welcome.card.text-center label.modal-close(for="modal-b4welcome") h3.blue.section First visit? p Please - span(style="color:blue;text-decoration:underline;cursor:pointer" @click="showWelcomeMsg") read this + span.read-this(@click="showWelcomeMsg") read this span  before playing ☺ input#modal-welcome.modal(type="checkbox") div(role="dialog") @@ -55,7 +61,7 @@ block content As suggested by the picture, a variant setup generally looks more or less like a chessboard with regular pieces (otherwise it's no longer a variant but a whole new game!). - p(style="font-style:italic;color:purple") However... + p.emphasis However... p Each variant has its own new rules, which can involve table.list-table tbody @@ -71,21 +77,27 @@ block content td ...and so on .section p. - Example: imagine that a capture is an atomic explosion, wiping all adjacent squares - – except the pawns, which as cockroaches can resist this kind of event. + Example: imagine that a capture is an atomic explosion, wiping all + adjacent squares – except the pawns, which as cockroaches can + resist this kind of event. p Also state a goal: make the opponent's king explode. p → Congrats, you defined Atomic chess! (Playable here) .section - p(style="font-style:italic;color:purple") OK, this all sounds interesting, but why would that be fun? + p.emphasis OK, this all sounds interesting, but why would that be fun? p. Because all games here start with a random setup: no more boring openings memorization, you have to rely on your chess skills only :) p Moreover, I claim that the chosen variants here are fun to play :P + - + var wikipediaUrl = "https://en.wikipedia.org/wiki/" + + "List_of_chess_variants#/media/File:Hexagonal_chess.svg"; p. For informations about hundreds (if not thousands!) of variants, you - can visit the excellent #[a(href="https://www.chessvariants.com/") chessvariants] website. - p(style="cursor:pointer;color:darkred" onClick="setCookie('visited','1');document.getElementById('modal-welcome').checked=false") Click here to not show this message next time - p(style="font-size:0.8em") Image credit: #[a(href="https://en.wikipedia.org/wiki/List_of_chess_variants#/media/File:Hexagonal_chess.svg") Wikpedia] + can visit the excellent + #[a(href="https://www.chessvariants.com/") chessvariants] website. + p.disable-msg(@click="markAsVisited") + | Click here to not show this message next time + p.smallfont Image credit: #[a(href=wikipediaUrl) Wikipedia] .row my-variant-summary( v-for="(v,idx) in sortedCounts", diff --git a/views/layout.pug b/views/layout.pug index 10b1ae9d..0416766d 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -4,18 +4,22 @@ html(lang="fr") head meta(charset="UTF-8") title vchess - #{title} - meta(name="viewport", content="width=device-width, initial-scale=1") - meta(name="msapplication-config", content="/images/favicon/browserconfig.xml") - meta(name="theme-color", content="#ffffff") - link(rel="stylesheet", href="//cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.0/mini-default.min.css") - link(rel="stylesheet", href="//fonts.googleapis.com/css?family=Open+Sans:400,700") - link(rel="apple-touch-icon", sizes="180x180", href="/images/favicon/apple-touch-icon.png") - link(rel="icon", type="image/png", sizes="32x32", href="/images/favicon/favicon-32x32.png") - link(rel="icon", type="image/png", sizes="16x16", href="/images/favicon/favicon-16x16.png") - link(rel="manifest", href="/images/favicon/manifest.json") - link(rel="mask-icon", href="/images/favicon/safari-pinned-tab.svg", color="#5bbad5") - link(rel="shortcut icon", href="/images/favicon/favicon.ico") - link(rel="stylesheet", href="/stylesheets/layout.css") + meta(name="viewport" content="width=device-width, initial-scale=1") + meta(name="msapplication-config" content="/images/favicon/browserconfig.xml") + meta(name="theme-color" content="#ffffff") + link(rel="stylesheet" + href="//cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.0/mini-default.min.css") + link(rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:400,700") + link(rel="apple-touch-icon" sizes="180x180" + href="/images/favicon/apple-touch-icon.png") + link(rel="icon" type="image/png" sizes="32x32" + href="/images/favicon/favicon-32x32.png") + link(rel="icon" type="image/png" sizes="16x16" + href="/images/favicon/favicon-16x16.png") + link(rel="manifest" href="/images/favicon/manifest.json") + link(rel="mask-icon" href="/images/favicon/safari-pinned-tab.svg" color="#5bbad5") + link(rel="shortcut icon" href="/images/favicon/favicon.ico") + link(rel="stylesheet" href="/stylesheets/layout.css") block css body diff --git a/views/rules/Alice.pug b/views/rules/Alice.pug index e23d97a7..7c5fca41 100644 --- a/views/rules/Alice.pug +++ b/views/rules/Alice.pug @@ -1,5 +1,6 @@ p.boxed - | Every move played ends up on another board (the "other side of the mirror"). So there are two boards. All pieces start on board 1. + | Every move played ends up on another board (the "other side of the mirror"). + | So there are two boards. All pieces start on board 1. h3 Specifications @@ -14,7 +15,8 @@ ul h3 Basics p - | Two boards are used in this variant. Pieces from board 2 are represented on the main board, upside down. + | Two boards are used in this variant. Pieces from board 2 are represented on + | the main board, upside down. | Any move played must be valid on the board it is played on. | In addition, the final square should not be occupied by a piece from the other board | (thus allowing to represent all on one board). @@ -35,7 +37,9 @@ figure.diagram-container h3 End of the game -p As in the orthodox game, win by checkmating the king. It shouldn't be able to escape the check, not even by moving to the other board. +p + | As in the orthodox game, win by checkmating the king. It shouldn't be able to + | escape the check, not even by moving to the other board. p Note: en-passant and castle occur as they do in the standard game. @@ -45,5 +49,6 @@ p | Alice chess pages on a(href="https://www.chessvariants.com/other.dir/alice.html") chessvariants.com | and on - a(href="https://www.schemingmind.com/journalarticle.aspx?article_id=9") schemingmind.com + a(href="https://www.schemingmind.com/journalarticle.aspx?article_id=9") + | schemingmind.com | . diff --git a/views/rules/Antiking.pug b/views/rules/Antiking.pug index 28e264b7..8272e2b0 100644 --- a/views/rules/Antiking.pug +++ b/views/rules/Antiking.pug @@ -1,6 +1,6 @@ p.boxed - | You have a king and an antiking. King must stay away from checks, but antiking must always stay in check. - | Antiking captures his own kind. + | You have a king and an antiking. King must stay away from checks, but antiking + | must always stay in check. Antiking captures his own kind. h3 Specifications @@ -16,7 +16,8 @@ h3 Basics p | The additional piece is a royal figure, thus cannot be captured. - | It captures the pieces of his color (to help checkmate opponent antiking, but by doing so it also make standard checkmate more difficult...). + | It captures the pieces of his color (to help checkmate opponent antiking, + | but by doing so it also make standard checkmate more difficult...). | It should always remains under check (if it cannot, game is over). figure.diagram-container @@ -34,12 +35,15 @@ p ...Or maybe do both at the same time? p Note 1: athough antiking captures his color, it doesn't check his king. -p Note 2: since it would allow a basic tactic (keep antiking touching opponent's king), kings do not attack antikings. +p + | Note 2: since it would allow a basic tactic (keep antiking touching opponent's + | king), kings do not attack antikings. p Note 3: an antiking does not check opponent's antiking. h3 Credits p - a(href="https://www.chessvariants.com/diffobjective.dir/anti-king-chess.html") Antiking chess + a(href="https://www.chessvariants.com/diffobjective.dir/anti-king-chess.html") + | Antiking chess | on chessvariants.com. diff --git a/views/rules/Atomic.pug b/views/rules/Atomic.pug index 008a35ce..7b776685 100644 --- a/views/rules/Atomic.pug +++ b/views/rules/Atomic.pug @@ -1,5 +1,6 @@ p.boxed - | All captures result in an "explosion" through which all surrounding white and black pieces other than pawns are removed from play. + | All captures result in an "explosion" through which all surrounding + | pieces other than pawns are removed from the board. h3 Specifications @@ -14,7 +15,8 @@ ul h3 Basics p - | When a piece captures an opponent figure on some square S, all pieces sitting on a square reachable by a king move from S are removed. + | When a piece captures an opponent figure on some square S, all pieces sitting + | on a square reachable by a king move from S are removed. | The pawns, however, remain: they have to be taken directly to disappear. figure.diagram-container @@ -31,10 +33,14 @@ ol p Explosions have priority: a checkmate followed by a king explosion loses. -p Note: since suicide is forbidden, a king can touch the opponent king - and become immune to checks. +p + | Note: since suicide is forbidden, a king can touch the opponent king - + | and become immune to checks. h3 Credits p - | Many resources can be found on the web (this variation is played on lichess and FICS, among others). - | This game was played first in 1995 at the German Internet Chess Server (GICS) according to Wikipedia. + | Many resources can be found on the web (this variation is played on lichess and + | FICS, among others). + | This game was played first in 1995 at the German Internet Chess Server (GICS) + | according to Wikipedia. diff --git a/views/rules/Checkered.pug b/views/rules/Checkered.pug index cf347665..96e4475e 100644 --- a/views/rules/Checkered.pug +++ b/views/rules/Checkered.pug @@ -1,5 +1,6 @@ p.boxed - | The capture of an enemy piece produces a new "checkered" piece belonging to both players. + | The capture of an enemy piece produces a new "checkered" piece belonging + | to both players. figure.showPieces.center-align img(src="/images/tmp_checkered/cp.png") @@ -27,9 +28,15 @@ h2.stageDelimiter Stage 1 h3 Basics ol - li Each capture produces a new piece, taking on nature of the capturing or captured one. - li The new piece arising from a capture has a new color: "checkered", as illustrated above. - li All checkered pieces belong to the player in turn and can capture the opponents pieces. + li + | Each capture produces a new piece, taking on nature of + | the capturing or captured one. + li + | The new piece arising from a capture has a new color: + | "checkered", as illustrated above. + li + | All checkered pieces belong to the player in turn and can + | capture the opponents pieces. span Remarks: ul @@ -40,7 +47,8 @@ figure.diagram-container .diagram | fen:2kr4/pp6/2p5/4ss1r/1P2ns1P/2Np4/P1P1P1BP/R2o1RK1: figcaption. - Black plays Rxh4=P. (Checkered pawn to) h5 is allowed then, because piece's nature changed. + Black plays Rxh4=P. (Checkered pawn to) h5 is allowed then, + because piece's nature changed. h3 Pawn moves @@ -53,7 +61,8 @@ h2.stageDelimiter Stage 2 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. + 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. @@ -61,7 +70,8 @@ p. h4 Variant of stage 2 p. - An observer could decide to join the game by taking the checkered pieces at any moment. + 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. diff --git a/views/rules/Crazyhouse.pug b/views/rules/Crazyhouse.pug index 3928c309..7a3d91b7 100644 --- a/views/rules/Crazyhouse.pug +++ b/views/rules/Crazyhouse.pug @@ -1,5 +1,6 @@ p.boxed - | Every captured piece can be re-used by the capturer, landing it anywhere instead of moving a piece. + | Every captured piece can be re-used by the capturer, + | landing it anywhere instead of moving a piece. h3 Specifications diff --git a/views/rules/Grand.pug b/views/rules/Grand.pug index 67b849fe..acafca81 100644 --- a/views/rules/Grand.pug +++ b/views/rules/Grand.pug @@ -1,5 +1,6 @@ p.boxed - | Two new pieces: marshall and cardinal. Bigger board. Orthodox rules with a few adaptations. + | Two new pieces: marshall and cardinal. Bigger board. + | Orthodox rules with a few adaptations. h3 Specifications @@ -42,7 +43,8 @@ p As in the orthodox game, win by checkmating the king. p. Note: I changed the author's starting position, to facilitate random start. - Thus the castling rule was introduced compared to the rules described on chessvariants.com. + Thus the castling rule was introduced compared to the rules described + on chessvariants.com. h3 Credits diff --git a/views/rules/Magnetic.pug b/views/rules/Magnetic.pug index ac90eeb6..7a0ac2ec 100644 --- a/views/rules/Magnetic.pug +++ b/views/rules/Magnetic.pug @@ -1,5 +1,6 @@ p.boxed - | Each piece has a charge generating a magnetic field, attracting enemy pieces while repelling others. + | Each piece has a charge generating a magnetic field, + | attracting enemy pieces while repelling others. h3 Specifications @@ -14,8 +15,10 @@ ul h3 Basics p - | Every piece has a charge generating a magnetic field, except the two kings which have a neutral charge. - | Pieces of the same color have let's say a positive charge, while the others have a negative charge. + | Every piece has a charge generating a magnetic field, except the two kings + | which have a neutral charge. + | Pieces of the same color have let's say a positive charge, + | while the others have a negative charge. | So, after each move some pieces are attracted while others are repelled. figure.diagram-container diff --git a/views/rules/Switching.pug b/views/rules/Switching.pug index 3104734b..4d1dcd52 100644 --- a/views/rules/Switching.pug +++ b/views/rules/Switching.pug @@ -1,5 +1,6 @@ p.boxed - | In addition to standard moves, a piece can be exchanged with an adjacent friendly unit. + | In addition to standard moves, a piece can be exchanged + | with an adjacent friendly unit. h3 Specifications diff --git a/views/rules/Wildebeest.pug b/views/rules/Wildebeest.pug index 8a14fb2f..dbfa3f52 100644 --- a/views/rules/Wildebeest.pug +++ b/views/rules/Wildebeest.pug @@ -1,5 +1,6 @@ p.boxed - | Two new pieces: camel and wildebeest. Bigger board. Orthodox rules with a few adaptations. + | Two new pieces: camel and wildebeest. Bigger board. + | Orthodox rules with a few adaptations. h3 Specifications diff --git a/views/rules/Zen.pug b/views/rules/Zen.pug index 41140433..bc5f9f58 100644 --- a/views/rules/Zen.pug +++ b/views/rules/Zen.pug @@ -1,6 +1,7 @@ p.boxed | Zen chess only change one thing to the standard rules: - | a piece A captures an opponent piece B if and only if B can take A according to the orthodox rules. + | a piece A captures an opponent piece B if and only if B can take A + | according to the orthodox rules. figure.diagram-container .diagram @@ -26,16 +27,20 @@ figure.diagram-container | fen:8/8/8/3r1k2/8/8/3K4/8: figcaption The white king can take the rook -p However, the king is attacked in the same way as in regular chess - and it's the only exception. +p. + However, the king is attacked in the same way as in regular chess - + and it's the only exception. figure.diagram-container .diagram | fen:r7/2n5/1q6/5k2/8/8/K7/8: - figcaption The king cannot take on a8 because it's guarded by the knight: it's checkmate + figcaption. + The king cannot take on a8 because it's guarded by the knight: it's checkmate h3 Credits p. - Very few resources about this variation: #[a(href="http://play.chessvariants.org/erf/ZenChess.html") this webpage] + Very few resources about this variation: + #[a(href="http://play.chessvariants.org/erf/ZenChess.html") this webpage] and #[a(href="http://www.pathguy.com/chess/ZenChess.htm") this one]. Ed Friedlander developed the Zen Chess applet from the link above. diff --git a/views/variant.pug b/views/variant.pug index 8ca9db49..561663c7 100644 --- a/views/variant.pug +++ b/views/variant.pug @@ -1,14 +1,15 @@ extends layout block css - link(rel="stylesheet", href="//fonts.googleapis.com/icon?family=Material+Icons") - link(rel="stylesheet", href="/stylesheets/variant.css") + link(rel="stylesheet" href="//fonts.googleapis.com/icon?family=Material+Icons") + link(rel="stylesheet" href="/stylesheets/variant.css") block content .container#variantPage .row .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 - h4.rulesTitle.text-center(v-on:click="displayRules=!displayRules") #{variant} Rules + h4.rulesTitle.text-center(v-on:click="displayRules=!displayRules") + | #{variant} Rules my-rules(v-show="displayRules") .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 my-game -- 2.44.0