From: Benjamin Auder <benjamin.auder@somewhere> Date: Sat, 14 Mar 2020 20:35:33 +0000 (+0100) Subject: Hopefully Eightpieces is less buggish now X-Git-Url: https://git.auder.net/doc/html/bundles/framework/scripts/%3C?a=commitdiff_plain;h=b0a0468aa6f436f2ad4962492561ef430a3bc15c;p=vchess.git Hopefully Eightpieces is less buggish now --- diff --git a/TODO b/TODO index a993b216..6916bdf3 100644 --- a/TODO +++ b/TODO @@ -1,19 +1,4 @@ -Revise variants code by using more aggregate/disaggregate flags functions? - # New variants -8-pieces https://www.youtube.com/watch?v=XZ8K02Da7Ps&list=PLRyjH8DPuzTBiym6lA0r84P8N0HnTtZyN&index=6&t=0s -https://www.chessvariants.com/rules/8-piece-chess "Eightpieces" - -"Ball" Chess: 9x9 board, ball on center square. 2 queens ? -To take the ball when it's free you need to capture it. -To take the ball when it's used, u need to take the piece. -Goal: bring ball to final rank. -Possibles passes : soit à une pièce, soit sur une case. - --> remplace un déplacement de pièce. Par exemple pion a2 passe à cavalier a4 = 1 coup. - --> selon le mode de déplacement standard (donc tout droit pour les pions) -Pas de notion d'échec ou de mat (?) -Si une pièce est mat elle donne le ballon (?) - Landing pieces from empty board: https://www.chessvariants.com/diffsetup.dir/unachess.html @@ -36,6 +21,16 @@ Goal is still checkmate. Take(a)n(d)make : if capture a piece, take its power for the last of the turn and make a move like it. If a pawn taken: direction of the capturer. +"Ball" Chess: 9x9 board, ball on center square. 2 queens ? +To take the ball when it's free you need to capture it. +To take the ball when it's used, u need to take the piece. +Goal: bring ball to final rank. +Possibles passes : soit à une pièce, soit sur une case. + --> remplace un déplacement de pièce. Par exemple pion a2 passe à cavalier a4 = 1 coup. + --> selon le mode de déplacement standard (donc tout droit pour les pions) +Pas de notion d'échec ou de mat (?) +Si une pièce est mat elle donne le ballon (?) + Maxima, Interweave, Roccoco Synchrone Chess: allow to anticipate en-passant capture as well :) diff --git a/client/public/images/icons/SOURCE b/client/public/images/icons/SOURCE index eeb51d3e..88c25021 100644 --- a/client/public/images/icons/SOURCE +++ b/client/public/images/icons/SOURCE @@ -13,3 +13,5 @@ https://www.flaticon.com/free-icon/download_724933?term=download&page=1&position https://www.flaticon.com/free-icon/resize_512182?term=resize&page=1&position=49 https://www.flaticon.com/free-icon/clear_565313?term=delete&page=1&position=33 https://www.flaticon.com/free-icon/clear_1632708?term=delete&page=1&position=3 +https://iconscout.com/icon/discord-1 +https://www.onlinewebfonts.com/icon/154680 diff --git a/client/public/images/icons/discord.svg b/client/public/images/icons/discord.svg new file mode 100644 index 00000000..bf910158 --- /dev/null +++ b/client/public/images/icons/discord.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50" version="1.1"><path d="M41.625 10.77c-3.98-3.204-10.277-3.747-10.547-3.766a.992.992 0 0 0-.988.586 6.63 6.63 0 0 0-.305.832c2.633.445 5.867 1.34 8.793 3.156a1 1 0 1 1-1.055 1.7C32.493 10.155 26.211 10 25 10c-1.21 0-7.496.156-12.523 3.277a1 1 0 0 1-1.055-1.7c2.926-1.811 6.16-2.71 8.793-3.151-.152-.496-.29-.809-.3-.836a.987.987 0 0 0-.993-.586c-.27.02-6.567.562-10.602 3.809C6.215 12.761 2 24.152 2 34c0 .176.047.344.133.496 2.906 5.11 10.84 6.445 12.648 6.504h.031a1 1 0 0 0 .81-.41l1.827-2.516c-4.933-1.273-7.453-3.437-7.597-3.566a1 1 0 1 1 1.324-1.5C11.234 33.063 15.875 37 25 37c9.14 0 13.781-3.953 13.828-3.992a1 1 0 0 1 1.41.094.996.996 0 0 1-.09 1.406c-.144.129-2.664 2.293-7.597 3.566l1.828 2.516a1 1 0 0 0 .809.41h.03c1.81-.059 9.743-1.395 12.65-6.504.085-.152.132-.32.132-.496 0-9.848-4.215-21.238-6.375-23.23zM18.5 30c-1.934 0-3.5-1.79-3.5-4s1.566-4 3.5-4 3.5 1.79 3.5 4-1.566 4-3.5 4zm13 0c-1.934 0-3.5-1.79-3.5-4s1.566-4 3.5-4 3.5 1.79 3.5 4-1.566 4-3.5 4z" id="surface1"/><metadata><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:dc="http://purl.org/dc/elements/1.1/"><rdf:Description about="https://iconscout.com/legal#licenses" dc:title="discord,filled" dc:description="discord,filled" dc:publisher="Iconscout" dc:date="2017-12-09" dc:format="image/svg+xml" dc:language="en"><dc:creator><rdf:Bag><rdf:li>Icons8</rdf:li></rdf:Bag></dc:creator></rdf:Description></rdf:RDF></metadata></svg> \ No newline at end of file diff --git a/client/public/images/icons/github.svg b/client/public/images/icons/github.svg new file mode 100644 index 00000000..4c46ca3f --- /dev/null +++ b/client/public/images/icons/github.svg @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve"> +<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata> +<g><path d="M500,10C229.4,10,10,229.4,10,500s219.4,490,490,490s490-219.4,490-490S770.6,10,500,10z M791,791c-37.8,37.8-81.8,67.5-130.8,88.2c-12.4,5.3-25.1,9.9-37.9,13.9v-73.4c0-38.6-13.2-67-39.7-85.2c16.6-1.6,31.8-3.8,45.7-6.7c13.9-2.9,28.6-7,44-12.4c15.5-5.4,29.3-11.9,41.6-19.4c12.3-7.5,24.1-17.2,35.4-29.2c11.3-12,20.8-25.5,28.5-40.7c7.7-15.2,13.7-33.3,18.2-54.5s6.7-44.6,6.7-70.1c0-49.5-16.1-91.6-48.3-126.3c14.7-38.3,13.1-79.9-4.8-124.9l-12-1.4c-8.3-1-23.2,2.5-44.7,10.5c-21.5,8-45.7,21.1-72.5,39.2c-38-10.5-77.4-15.8-118.2-15.8c-41.2,0-80.4,5.3-117.7,15.8c-16.9-11.5-32.9-21-48.1-28.5s-27.3-12.6-36.4-15.3s-17.5-4.4-25.4-5s-12.8-0.8-15.1-0.5c-2.2,0.3-3.8,0.6-4.8,1c-17.9,45.3-19.5,86.9-4.8,124.9c-32.2,34.8-48.3,76.9-48.3,126.3c0,25.5,2.2,48.9,6.7,70.1c4.5,21.2,10.5,39.4,18.2,54.5c7.7,15.2,17.1,28.7,28.5,40.7c11.3,12,23.1,21.7,35.4,29.2c12.3,7.5,26.2,14,41.6,19.4c15.5,5.4,30.1,9.6,44,12.4c13.9,2.9,29.1,5.1,45.7,6.7c-26.2,17.9-39.2,46.3-39.2,85.2v74.9c-14.4-4.3-28.7-9.4-42.7-15.3c-49-20.7-93-50.4-130.8-88.2c-37.8-37.8-67.5-81.8-88.2-130.8C99.4,609.5,88.5,555.6,88.5,500c0-55.6,10.9-109.5,32.3-160.2c20.7-49,50.4-93,88.2-130.8c37.8-37.8,81.8-67.5,130.8-88.2C390.6,99.4,444.5,88.5,500,88.5c55.6,0,109.5,10.9,160.2,32.3c49,20.7,93,50.4,130.8,88.2s67.5,81.8,88.2,130.8c21.4,50.7,32.3,104.6,32.3,160.2c0,55.6-10.9,109.5-32.3,160.2C858.5,709.2,828.8,753.2,791,791L791,791z"/></g> +</svg> \ No newline at end of file diff --git a/client/src/App.vue b/client/src/App.vue index 8258dc3e..d8e395f2 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -34,7 +34,9 @@ .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 footer router-link.menuitem(to="/about") {{ st.tr["About"] }} - a.menuitem(href="https://discord.gg/a9ZFKBe") Discord + a.menuitem(href="https://github.com/yagu0/vchess") + span {{ st.tr["Code"] }} + img(src="/images/icons/github.svg") router-link.menuitem(to="/news") {{ st.tr["News"] }} p.clickable(onClick="window.doClick('modalContact')") | {{ st.tr["Contact"] }} @@ -68,8 +70,8 @@ export default { <style lang="sass"> html, * font-family: "Open Sans", Arial, sans-serif - --a-link-color: black - --a-visited-color: black + --a-link-color: darkred + --a-visited-color: darkred body padding: 0 @@ -236,13 +238,18 @@ footer color: #42b983 !important text-decoration: none & > .menuitem - display: inline-block margin: 0 12px + display: inline-flex; + align-self: center; &:link color: #2c3e50 &:visited, &:hover color: #2c3e50 text-decoration: none + & > img + height: 1.3em + display: inline-block + margin-left: 5px & > p display: inline-block margin: 0 12px diff --git a/client/src/base_rules.js b/client/src/base_rules.js index 890601d2..326da6ed 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -238,9 +238,11 @@ export const ChessRules = class ChessRules { // On which squares is color under check ? (for interface) getCheckSquares(color) { - return this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]) - ? [JSON.parse(JSON.stringify(this.kingPos[color]))] //need to duplicate! - : []; + return ( + this.underCheck(color) + ? [JSON.parse(JSON.stringify(this.kingPos[color]))] //need to duplicate! + : [] + ); } ///////////// diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue index c85d47e9..471c139c 100644 --- a/client/src/components/BaseGame.vue +++ b/client/src/components/BaseGame.vue @@ -309,8 +309,6 @@ export default { const playSubmove = (smove) => { if (!navigate) smove.notation = this.vr.getNotation(smove); this.vr.play(smove); - // Is opponent in check? - this.incheck = this.vr.getCheckSquares(this.vr.turn); if (!navigate) { if (!this.inMultimove) { if (this.cursor < this.moves.length - 1) @@ -356,6 +354,8 @@ export default { if (!smove.fen) // NOTE: only FEN of last sub-move is required (thus setting it here) smove.fen = this.vr.getFen(); + // Is opponent in check? + this.incheck = this.vr.getCheckSquares(this.vr.turn); this.lastMove = smove; this.emitFenIfAnalyze(); this.inMultimove = false; @@ -442,7 +442,6 @@ export default { if (this.cursor < minCursor) return; //no more moves move = this.moves[this.cursor]; } - // Caution; if multi-move, undo all submoves from last to first undoMove(move, this.vr); if (light) this.cursor--; else { diff --git a/client/src/components/Board.vue b/client/src/components/Board.vue index 0d531a6b..c2de1769 100644 --- a/client/src/components/Board.vue +++ b/client/src/components/Board.vue @@ -538,18 +538,18 @@ img.ghost // TODO: no predefined highlight colors, but layers. How? .light-square.lichess.highlight-light - background-color: #cdd26a !important + background-color: #cdd26a .dark-square.lichess.highlight-dark - background-color: #aaa23a !important + background-color: #aaa23a .light-square.chesscom.highlight-light - background-color: #f7f783 !important + background-color: #f7f783 .dark-square.chesscom.highlight-dark - background-color: #bacb44 !important + background-color: #bacb44 .light-square.chesstempo.highlight-light - background-color: #9f9fff !important + background-color: #9f9fff .dark-square.chesstempo.highlight-dark - background-color: #557fff !important + background-color: #557fff </style> diff --git a/client/src/components/ContactForm.vue b/client/src/components/ContactForm.vue index e5d27326..b7aea942 100644 --- a/client/src/components/ContactForm.vue +++ b/client/src/components/ContactForm.vue @@ -10,6 +10,9 @@ div ) .card label.modal-close(for="modalContact") + a#discordLink(href="https://discord.gg/a9ZFKBe") + span {{ st.tr["Discord invitation"] }} + img(src="/images/icons/discord.svg") fieldset label(for="userEmail") {{ st.tr["Email"] }} input#userEmail(type="email" :value="st.user.email") @@ -98,6 +101,16 @@ textarea#mailContent width: 100% min-height: 100px +#discordLink + display: block + margin-top: 10px + text-align: center + font-size: 1.3em + & > img + height: 1.5em + display: inline-block + margin-left: 5px + #dialog padding: 5px color: blue diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 3c0228c1..a37a75c1 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -27,6 +27,7 @@ export const translations = { "Challenge declined": "Challenge declined", "Chat here": "Chat here", "Clear history": "Clear history", + Code: "Code", "Connection token sent. Check your emails!": "Connection token sent. Check your emails!", Contact: "Contact", "Correspondance challenges": "Correspondance challenges", @@ -34,6 +35,7 @@ export const translations = { "Database error: stop private browsing, or update your browser": "Database error: stop private browsing, or update your browser", Delete: "Delete", Deterministic: "Deterministic", + "Discord invitation": "Discord invitation", Download: "Download", Draw: "Draw", "Draw offer only in your turn": "Draw offer only in your turn", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index f31fc202..e1048b7d 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -27,6 +27,7 @@ export const translations = { "Challenge declined": "DesafÃo rechazado", "Chat here": "Chat aquÃ", "Clear history": "Clara historia", + Code: "Código", "Connection token sent. Check your emails!": "Token de conexión enviado. ¡Revisa tus correos!", Contact: "Contacto", "Correspondance challenges": "DesafÃos por correspondencia", @@ -34,6 +35,7 @@ export const translations = { "Database error: stop private browsing, or update your browser": "Error de la base de datos: detener la navegación privada, o actualizar su navegador", Delete: "Borrar", Deterministic: "Determinista", + "Discord invitation": "Invitación Discord", Download: "Descargar", Draw: "Tablas", "Draw offer only in your turn": "Oferta de tablas solo en tu turno", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 0a8d5532..20b7b587 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -27,6 +27,7 @@ export const translations = { "Challenge declined": "Défi refusé", "Chat here": "Chattez ici", "Clear history": "Effacer l'historique", + Code: "Code", "Connection token sent. Check your emails!": "Token de connection envoyé. Allez voir vos emails !", Contact: "Contact", "Correspondance challenges": "Défis par correspondance", @@ -34,6 +35,7 @@ export const translations = { "Database error: stop private browsing, or update your browser": "Erreur de base de données : arrêtez la navigation privée, ou mettez à jour votre navigateur", Delete: "Supprimer", Deterministic: "Déterministe", + "Discord invitation": "Invitation Discord", Download: "Télécharger", Draw: "Nulle", "Draw offer only in your turn": "Proposition de nulle seulement sur votre temps", diff --git a/client/src/utils/playUndo.js b/client/src/utils/playUndo.js index ab11bff9..94e642cc 100644 --- a/client/src/utils/playUndo.js +++ b/client/src/utils/playUndo.js @@ -5,6 +5,7 @@ export function playMove(move, vr) { export function undoMove(move, vr) { if (!Array.isArray(move)) move = [move]; + // If multi-move, undo all submoves from last to first for (let i = move.length - 1; i >= 0; i--) vr.undo(move[i]); } diff --git a/client/src/variants/Eightpieces.js b/client/src/variants/Eightpieces.js index 88c3739a..465fbea9 100644 --- a/client/src/variants/Eightpieces.js +++ b/client/src/variants/Eightpieces.js @@ -214,6 +214,13 @@ export const VariantRules = class EightpiecesRules extends ChessRules { ); } + canTake([x1, y1], [x2, y2]) { + if (this.subTurn == 2) + // Only self captures on this subturn: + return this.getColor(x1, y1) == this.getColor(x2, y2); + return super.canTake([x1, y1], [x2, y2]); + } + // Is piece on square (x,y) immobilized? isImmobilized([x, y]) { const color = this.getColor(x, y); @@ -295,14 +302,6 @@ export const VariantRules = class EightpiecesRules extends ChessRules { return moves; } } - if (this.subTurn == 2) { - // Temporarily change pushed piece color. - // (Not using getPiece() because of lancers) - var oppCol = this.getColor(x, y); - var color = V.GetOppCol(oppCol); - var saveXYstate = this.board[x][y]; - this.board[x][y] = color + this.board[x][y].charAt(1); - } let moves = []; switch (this.getPiece(x, y)) { case V.JAILER: @@ -330,16 +329,12 @@ export const VariantRules = class EightpiecesRules extends ChessRules { } return true; }); - } - else if (this.subTurn == 2) { - // Don't forget to re-add the sentry on the board: - // Also fix color of pushed piece afterward: + } else if (this.subTurn == 2) { + // Put back the sentinel on board: + const color = this.turn; moves.forEach(m => { - m.appear.unshift({x: x, y: y, p: V.SENTRY, c: color}); - m.appear[1].c = oppCol; - m.vanish[0].c = oppCol; + m.appear.push({x: x, y: y, p: V.SENTRY, c: color}); }); - this.board[x][y] = saveXYstate; } return moves; } @@ -348,10 +343,14 @@ export const VariantRules = class EightpiecesRules extends ChessRules { const color = this.getColor(x, y); let moves = []; const [sizeX, sizeY] = [V.size.x, V.size.y]; - const shiftX = color == "w" ? -1 : 1; + let shiftX = color == "w" ? -1 : 1; + if (this.subTurn == 2) shiftX *= -1; const startRank = color == "w" ? sizeX - 2 : 1; const lastRank = color == "w" ? 0 : sizeX - 1; + // Pawns might be pushed on 1st rank and attempt to move again: + if (!V.OnBoard(x + shiftX, y)) return []; + const finalPieces = // No promotions after pushes! x + shiftX == lastRank && this.subTurn == 1 @@ -421,7 +420,12 @@ export const VariantRules = class EightpiecesRules extends ChessRules { getPotentialLancerMoves_aux([x, y], step, tr) { let moves = []; // Add all moves to vacant squares until opponent is met: - const oppCol = V.GetOppCol(this.getColor(x, y)); + const color = this.getColor(x, y); + const oppCol = + this.subTurn == 1 + ? V.GetOppCol(color) + // at subTurn == 2, consider own pieces as opponent + : color; let sq = [x + step[0], y + step[1]]; while (V.OnBoard(sq[0], sq[1]) && this.getColor(sq[0], sq[1]) != oppCol) { if (this.board[sq[0]][sq[1]] == V.EMPTY) @@ -451,7 +455,7 @@ export const VariantRules = class EightpiecesRules extends ChessRules { // do not change direction after moving, *except* if I keep the // same orientation in which I was pushed. const color = this.getColor(x, y); - const curDir = V.LANCER_DIRS[this.board[x][x].charAt(1)]; + const curDir = V.LANCER_DIRS[this.board[x][y].charAt(1)]; Object.values(V.LANCER_DIRS).forEach(step => { const dirCode = Object.keys(V.LANCER_DIRS).find(k => { return ( @@ -529,29 +533,13 @@ export const VariantRules = class EightpiecesRules extends ChessRules { m.vanish.pop(); } }); - // Can the pushed unit make any move? ...resulting in a non-self-check? const color = this.getColor(x, y); const fMoves = moves.filter(m => { - // Sentry push? + // Can the pushed unit make any move? ...resulting in a non-self-check? if (m.appear.length == 0) { let res = false; this.play(m); - let potentialMoves = this.getPotentialMovesFrom([m.end.x, m.end.y]); - // Add nudges (if any a priori possible) - for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) { - if ( - V.OnBoard(m.end.x + step[0], m.end.y + step[1]) && - this.board[m.end.x + step[0]][m.end.y + step[1]] == V.EMPTY - ) { - potentialMoves.push( - this.getBasicMove( - [m.end.x, m.end.y], - [m.end.x + step[0], m.end.y + step[1]] - ) - ); - } - } - let moves2 = this.filterValid(potentialMoves); + let moves2 = this.getPotentialMovesFrom([m.end.x, m.end.y]); for (let m2 of moves2) { this.play(m2); res = !this.underCheck(color); @@ -573,6 +561,19 @@ export const VariantRules = class EightpiecesRules extends ChessRules { }); } + getPotentialKingMoves(sq) { + const moves = this.getSlideNJumpMoves( + sq, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + return ( + this.subTurn == 1 + ? moves.concat(this.getCastleMoves(sq)) + : moves + ); + } + // Adapted: castle with jailer possible getCastleMoves([x, y]) { const c = this.getColor(x, y); @@ -655,30 +656,40 @@ export const VariantRules = class EightpiecesRules extends ChessRules { } filterValid(moves) { + if (moves.length == 0) return []; + const basicFilter = (m, c) => { + this.play(m); + const res = !this.underCheck(c); + this.undo(m); + return res; + }; // Disable check tests for sentry pushes, // because in this case the move isn't finished let movesWithoutSentryPushes = []; let movesWithSentryPushes = []; moves.forEach(m => { - if (m.appear.length > 0) movesWithoutSentryPushes.push(m); + // Second condition below for special king "pass" moves + if (m.appear.length > 0 || m.vanish.length == 0) + movesWithoutSentryPushes.push(m); else movesWithSentryPushes.push(m); }); - - // TODO: if after move a sentry can take king in 2 times?! - - const filteredMoves = super.filterValid(movesWithoutSentryPushes); - // If at least one full move made, everything is allowed: - if (this.movesCount >= 2) - return filteredMoves.concat(movesWithSentryPushes); - // Else, forbid checks and captures: - const oppCol = V.GetOppCol(this.turn); - return filteredMoves.filter(m => { - if (m.vanish.length == 2 && m.appear.length == 1) return false; - this.play(m); - const res = !this.underCheck(oppCol); - this.undo(m); - return res; - }).concat(movesWithSentryPushes); + const color = this.turn; + const oppCol = V.GetOppCol(color); + const filteredMoves = + movesWithoutSentryPushes.filter(m => basicFilter(m, color)); + // If at least one full move made, everything is allowed. + // Else: forbid checks and captures. + return ( + this.movesCount >= 2 + ? filteredMoves + : filteredMoves.filter(m => { + return ( + m.vanish.length <= 1 || + m.appear.length != 1 || + basicFilter(m, oppCol) + ); + }) + ).concat(movesWithSentryPushes); } getAllValidMoves() { @@ -689,10 +700,10 @@ export const VariantRules = class EightpiecesRules extends ChessRules { } prePlay(move) { - if (move.appear.length == 0 && move.vanish.length == 1) { + if (move.appear.length == 0 && move.vanish.length == 1) // The sentry is about to push a piece: subTurn goes from 1 to 2 this.sentryPos = { x: move.end.x, y: move.end.y }; - } else if (this.subTurn == 2 && move.vanish[0].p != V.PAWN) { + if (this.subTurn == 2 && move.vanish[0].p != V.PAWN) { // A piece is pushed: forbid array of squares between start and end // of move, included (except if it's a pawn) let squares = []; @@ -708,7 +719,7 @@ export const VariantRules = class EightpiecesRules extends ChessRules { ]; for ( let sq = {x: move.start.x, y: move.start.y}; - sq.x != move.end.x && sq.y != move.end.y; + sq.x != move.end.x || sq.y != move.end.y; sq.x += step[0], sq.y += step[1] ) { squares.push({ x: sq.x, y: sq.y }); @@ -721,9 +732,9 @@ export const VariantRules = class EightpiecesRules extends ChessRules { } play(move) { -// if (!this.states) this.states = []; -// const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen(); -// this.states.push(stateFen); + if (!this.states) this.states = []; + const stateFen = this.getFen(); + this.states.push(stateFen); this.prePlay(move); move.flags = JSON.stringify(this.aggregateFlags()); @@ -742,10 +753,35 @@ export const VariantRules = class EightpiecesRules extends ChessRules { } postPlay(move) { - if (move.vanish.length == 0) - // Special pass move of the king: nothing to update! + if (move.vanish.length == 0 || this.subTurn == 2) + // Special pass move of the king, or sentry pre-push: nothing to update return; - super.postPlay(move); + const c = move.vanish[0].c; + const piece = move.vanish[0].p; + const firstRank = c == "w" ? V.size.x - 1 : 0; + + if (piece == V.KING) { + this.kingPos[c][0] = move.appear[0].x; + this.kingPos[c][1] = move.appear[0].y; + this.castleFlags[c] = [V.size.y, V.size.y]; + return; + } + // Update castling flags if rooks are moved + const oppCol = V.GetOppCol(c); + const oppFirstRank = V.size.x - 1 - firstRank; + if ( + move.start.x == firstRank && //our rook moves? + this.castleFlags[c].includes(move.start.y) + ) { + const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1); + this.castleFlags[c][flagIdx] = V.size.y; + } else if ( + move.end.x == oppFirstRank && //we took opponent rook? + this.castleFlags[oppCol].includes(move.end.y) + ) { + const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 1); + this.castleFlags[oppCol][flagIdx] = V.size.y; + } } undo(move) { @@ -761,9 +797,9 @@ export const VariantRules = class EightpiecesRules extends ChessRules { } this.postUndo(move); -// const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen(); -// if (stateFen != this.states[this.states.length-1]) debugger; -// this.states.pop(); + const stateFen = this.getFen(); + if (stateFen != this.states[this.states.length-1]) debugger; + this.states.pop(); } postUndo(move) { @@ -779,6 +815,46 @@ export const VariantRules = class EightpiecesRules extends ChessRules { ); } + isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) { + for (let step of steps) { + let rx = x + step[0], + ry = y + step[1]; + while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) { + rx += step[0]; + ry += step[1]; + } + if ( + V.OnBoard(rx, ry) && + this.getPiece(rx, ry) === piece && + colors.includes(this.getColor(rx, ry)) && + !this.isImmobilized([rx, ry]) + ) { + return true; + } + } + return false; + } + + isAttackedByPawn([x, y], colors) { + for (let c of colors) { + const pawnShift = c == "w" ? 1 : -1; + if (x + pawnShift >= 0 && x + pawnShift < V.size.x) { + for (let i of [-1, 1]) { + if ( + y + i >= 0 && + y + i < V.size.y && + this.getPiece(x + pawnShift, y + i) == V.PAWN && + this.getColor(x + pawnShift, y + i) == c && + !this.isImmobilized([x + pawnShift, y + i]) + ) { + return true; + } + } + } + } + return false; + } + isAttackedByLancer([x, y], colors) { for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) { // If in this direction there are only enemy pieces and empty squares, @@ -793,8 +869,12 @@ export const VariantRules = class EightpiecesRules extends ChessRules { colors.includes(this.getColor(coord.x, coord.y)) ) ) { - if (this.getPiece(coord.x, coord.y) == V.LANCER) + if ( + this.getPiece(coord.x, coord.y) == V.LANCER && + !this.isImmobilized([coord.x, coord.y]) + ) { lancerPos.push({x: coord.x, y: coord.y}); + } coord.x += step[0]; coord.y += step[1]; } @@ -871,7 +951,8 @@ export const VariantRules = class EightpiecesRules extends ChessRules { for (let j=0; j<V.size.y; j++) { if ( this.getPiece(i,j) == V.SENTRY && - colors.includes(this.getColor(i,j)) + colors.includes(this.getColor(i,j)) && + !this.isImmobilized([i, j]) ) { for (let step of V.steps[V.BISHOP]) { let sq = [ i + step[0], j + step[1] ]; @@ -962,11 +1043,19 @@ export const VariantRules = class EightpiecesRules extends ChessRules { return (!choice.second ? choice : [choice, choice.second]); } - // TODO: if subTurn == 2, take some precautions, in particular pawn pushed on 1st rank. - // --> should indicate Sxb2,bxc1 getNotation(move) { // Special case "king takes jailer" is a pass move if (move.appear.length == 0 && move.vanish.length == 0) return "pass"; + if (this.subTurn == 2) { + // Do not consider appear[1] (sentry) for sentry pushes + const simpleMove = { + appear: [move.appear[0]], + vanish: move.vanish, + start: move.start, + end: move.end + }; + return super.getNotation(simpleMove); + } return super.getNotation(move); } }; diff --git a/client/src/variants/Suction.js b/client/src/variants/Suction.js index 9ac5b442..a39bec0b 100644 --- a/client/src/variants/Suction.js +++ b/client/src/variants/Suction.js @@ -6,9 +6,6 @@ export const VariantRules = class SuctionRules extends ChessRules { } setOtherVariables(fen) { - -console.log(fen); - super.setOtherVariables(fen); // Local stack of "captures" this.cmoves = [];