From: Benjamin Auder Date: Fri, 8 Jan 2021 17:47:47 +0000 (+0100) Subject: Synchrone2: almost OK (issues with undoing pass moves) X-Git-Url: https://git.auder.net/doc/html/%7B%7B%20path%28%27fos_user_profile_edit%27%29%20%7D%7D?a=commitdiff_plain;h=aa32568ef0c9fff28d17d19588b53ee049f0e1dc;p=vchess.git Synchrone2: almost OK (issues with undoing pass moves) --- diff --git a/client/src/translations/rules/Synchrone2/en.pug b/client/src/translations/rules/Synchrone2/en.pug index 15050ab7..057fae77 100644 --- a/client/src/translations/rules/Synchrone2/en.pug +++ b/client/src/translations/rules/Synchrone2/en.pug @@ -1,9 +1,25 @@ -p.boxed TODO +p.boxed + | After a synchronous turn, each player may capture the moving + | opponent's piece if it is on a square previously attacked. -p No en passant captures. +p + | Everything goes as in + a(href="/#/variants/Synchrone1") Synchrone1 + |  variant, but after each pair of synchronous moves (resolving as + | usual), both players may decide to capture the enemy unit just moved. + | The capturing move must be legal according to the previous board situation. + | If you would rather (or cannot) achieve such a capture, pass by playing + | any of your piece onto your king, or your king on the enemy king. -p No anticipated recaptures. +p. + Because of the added capturing turn, there are no anticipated recaptures. + In this version, there are also no en-passant captures. -p But, a deterministic "capturing turn" added. +h3 More information -p http://www.hexenspiel.de/engl/synchronous-chess/ +p + | This variant is described on + a(href="http://www.hexenspiel.de/engl/synchronous-chess/") this page + | . + +p Inventors: Ralf Hansmann, Arnold J. Krasowsky, Andrey Krasowsky. diff --git a/client/src/translations/rules/Synchrone2/es.pug b/client/src/translations/rules/Synchrone2/es.pug index 21203baa..4fa8fc2e 100644 --- a/client/src/translations/rules/Synchrone2/es.pug +++ b/client/src/translations/rules/Synchrone2/es.pug @@ -1 +1,27 @@ -p.boxed TODO +p.boxed + | Después de un turno sincrónico, cada jugador puede capturar opcionalmente + | la pieza del oponente se ha movido, si está en + | una casilla previamente atacada + +p + | Todo va como en la variante + a(href="/#/variants/Synchrone1") Synchrone1 + | , pero después de cada par de movimientos sincrónicos (resueltos como de + | costumbre), ambos jugadores pueden decidir capturar la pieza enemiga + | acaba de moverse. La jugada de captura debe ser legal dependiendo de la + | situación anterior en el tablero. Si prefiere (o no puede) realizar + | tal captura, pasa tocando cualquiera de tus piezas + | a tu rey, o del rey al rey contrario. + +p. + No hay capturas tempranas, ya que la ronda intermedia se llena + este papel. En esta versión tampoco existen capturas en passant. + +h3 Más información + +p + | Esta variante se describe en + a(href="http://www.hexenspiel.de/engl/synchronous-chess/") esta página + | . + +p Inventores: Ralf Hansmann, Arnold J. Krasowsky, Andrey Krasowsky. diff --git a/client/src/translations/rules/Synchrone2/fr.pug b/client/src/translations/rules/Synchrone2/fr.pug index 21203baa..3eaeb73a 100644 --- a/client/src/translations/rules/Synchrone2/fr.pug +++ b/client/src/translations/rules/Synchrone2/fr.pug @@ -1 +1,27 @@ -p.boxed TODO +p.boxed + | Après un tour synchrone, chaque joueur peut éventuellement capturer la + | pièce adverse s'étant déplacée, si elle se trouve sur + | une case attaquée précédemment + +p + | Tout se déroule comme dans la variante + a(href="/#/variants/Synchrone1") Synchrone1 + | , mais après chaque paire de coups synchrones (résolus comme d'habitude), + | les deux joueurs peuvent décider de capturer la pièce ennemie juste + | déplacée. Le coup capturant doit être légal selon la situation + | précédente sur l'échiquier. Si vous préférez (ou ne pouvez pas) effectuer + | une telle capture, passez en jouant n'importe laquelle de votre pièce + | vers votre roi, ou le roi vers le roi adverse. + +p. + Il n'y a pas de re-captures anticipée, puisque le tour intermédiaire remplit + ce rôle. Dans cette version, les prises en passant n'existent pas non plus. + +h3 Plus d'information + +p + | Cette variante est décrite sur + a(href="http://www.hexenspiel.de/engl/synchronous-chess/") cette page + | . + +p Inventeurs : Ralf Hansmann, Arnold J. Krasowsky, Andrey Krasowsky. diff --git a/client/src/variants/Synchrone1.js b/client/src/variants/Synchrone1.js index ba9cf9f2..2c59bc6c 100644 --- a/client/src/variants/Synchrone1.js +++ b/client/src/variants/Synchrone1.js @@ -399,7 +399,7 @@ export class Synchrone1Rules extends ChessRules { // Update king position + flags let kingAppear = { 'w': false, 'b': false }; - for (let i=0; i 0 ? this.initfenStack[L-1] : "-"; } getFen() { return ( - super.getFen() + " " + + super.getBaseFen() + " " + + super.getTurnFen() + " " + + this.movesCount + " " + + super.getFlagsFen() + " " + this.getInitfenFen() + " " + this.getWhitemoveFen() ); @@ -52,7 +50,7 @@ export class Synchrone2Rules extends Synchrone1Rules { static GenRandInitFen(randomness) { const res = ChessRules.GenRandInitFen(randomness); // Add initFen field: - return res.slice(0, -1) + " " + res.split(' ')[1] + " -"; + return res.slice(0, -1) + res.split(' ')[0] + " -"; } setOtherVariables(fen) { @@ -64,25 +62,133 @@ export class Synchrone2Rules extends Synchrone1Rules { parsedFen.whiteMove != "-" ? JSON.parse(parsedFen.whiteMove) : null; - // And initFen (not empty) - this.initFen = parsedFen.initFen; + // And initFen (could be empty) + this.initfenStack = []; + if (parsedFen.initFen != "-") this.initfenStack.push(parsedFen.initFen); } getPotentialMovesFrom([x, y]) { if (this.movesCount % 4 <= 1) return super.getPotentialMovesFrom([x, y]); - // TODO: either add a "blackMove' field in FEN (bof...), - // or write an helper function to detect from diff positions, - // which piece moved (if not disappeared!), which moves are valid. - // + do not forget pass move (king 2 king): always possible at stage 2. - return []; + // Diff current and old board to know which pieces have moved, + // and to deduce possible moves at stage 2. + const L = this.initfenStack.length; + let initBoard = V.GetBoard(this.initfenStack[L-1]); + let appeared = []; + const c = this.turn; + const oppCol = V.GetOppCol(c); + for (let i=0; i<8; i++) { + for (let j=0; j<8; j++) { + if (this.board[i][j] != initBoard[i][j]) { + if (this.board[i][j] != V.EMPTY) { + const color = this.board[i][j].charAt(0); + appeared.push({ c: color, x: i, y: j }); + // Pawns capture in diagonal => the following fix. + // (Other way would be to redefine getPotentialPawnMoves()...) + if (color == oppCol) initBoard[i][j] = this.board[i][j]; + } + } + } + } + const saveBoard = this.board; + this.board = initBoard; + const movesInit = super.getPotentialMovesFrom([x, y]); + this.board = saveBoard; + const target = appeared.find(a => a.c == oppCol); + let movesNow = super.getPotentialMovesFrom([x, y]).filter(m => { + return ( + m.end.x == target.x && + m.end.y == target.y && + movesInit.some(mi => mi.end.x == m.end.x && mi.end.y == m.end.y) + ); + }); + const passTarget = + (x != this.kingPos[c][0] || y != this.kingPos[c][1]) ? c : oppCol; + movesNow.push( + new Move({ + start: { x: x, y: y }, + end: { + x: this.kingPos[passTarget][0], + y: this.kingPos[passTarget][1] + }, + appear: [], + vanish: [] + }) + ); + return movesNow; + } + + filterValid(moves) { + if (moves.length == 0) return []; + if (moves.length == 1 && moves[0].vanish.length == 0) return moves; + // filterValid can be called when it's "not our turn": + const color = moves.find(m => m.vanish.length > 0).vanish[0].c; + return moves.filter(m => { + if (m.vanish.length == 0) return true; + const piece = m.vanish[0].p; + if (piece == V.KING) { + this.kingPos[color][0] = m.appear[0].x; + this.kingPos[color][1] = m.appear[0].y; + } + V.PlayOnBoard(this.board, m); + let res = !this.underCheck(color); + V.UndoOnBoard(this.board, m); + if (piece == V.KING) this.kingPos[color] = [m.start.x, m.start.y]; + return res; + }); + } + + getPossibleMovesFrom([x, y]) { + return this.filterValid(this.getPotentialMovesFrom([x, y])); } play(move) { + if (this.movesCount % 4 == 0) this.initfenStack.push(this.getBaseFen()); move.flags = JSON.stringify(this.aggregateFlags()); // Do not play on board (would reveal the move...) this.turn = V.GetOppCol(this.turn); this.movesCount++; - this.postPlay(move); + if ([0, 3].includes(this.movesCount % 4)) this.postPlay(move); + else super.postPlay(move); //resolve synchrone move + } + + postPlay(move) { + if (this.turn == 'b') { + // NOTE: whiteMove is used read-only, so no need to copy + this.whiteMove = move; + return; + } + + // A full "deterministic" turn just ended: no need to resolve + const smove = { + appear: this.whiteMove.appear.concat(move.appear), + vanish: this.whiteMove.vanish.concat(move.vanish) + }; + V.PlayOnBoard(this.board, smove); + move.whiteMove = this.whiteMove; //for undo + this.whiteMove = null; + + // Update king position + flags + let kingAppear = { 'w': false, 'b': false }; + for (let i=0; i < smove.appear.length; i++) { + if (smove.appear[i].p == V.KING) { + const c = smove.appear[i].c; + kingAppear[c] = true; + this.kingPos[c][0] = smove.appear[i].x; + this.kingPos[c][1] = smove.appear[i].y; + } + } + for (let i = 0; i < smove.vanish.length; i++) { + if (smove.vanish[i].p == V.KING) { + const c = smove.vanish[i].c; + if (!kingAppear[c]) { + this.kingPos[c][0] = -1; + this.kingPos[c][1] = -1; + } + break; + } + } + super.updateCastleFlags(smove); + move.smove = smove; } undo(move) { @@ -92,12 +198,13 @@ export class Synchrone2Rules extends Synchrone1Rules { V.UndoOnBoard(this.board, move.smove); this.turn = V.GetOppCol(this.turn); this.movesCount--; - this.postUndo(move); + if (this.movesCount % 4 == 0) this.initfenStack.pop(); + if (move.vanish.length > 0) super.postUndo(move); } getCurrentScore() { if (this.movesCount % 4 != 0) - // Turn (2 x white + black) not over yet + // Turn (2 x [white + black]) not over yet return "*"; // Was a king captured? if (this.kingPos['w'][0] < 0) return "0-1"; @@ -119,4 +226,9 @@ export class Synchrone2Rules extends Synchrone1Rules { return (whiteCanMove ? "1-0" : "0-1"); } + getNotation(move) { + if (move.vanish.length == 0) return "pass"; + return super.getNotation(move); + } + }; diff --git a/client/src/views/Rules.vue b/client/src/views/Rules.vue index 2edd8a88..dc96ffb4 100644 --- a/client/src/views/Rules.vue +++ b/client/src/views/Rules.vue @@ -123,9 +123,8 @@ export default { // NOTE: game might be null this.$refs["compgame"].launchGame(game); }); - } else { - this.$refs["compgame"].launchGame(); } + else this.$refs["compgame"].launchGame(); }, // The user wants to stop the game: stopGame: function() {