From 6c7cbfedc6ecf2b49f6b1e27a174039e92a36365 Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Thu, 16 Apr 2020 04:26:00 +0200 Subject: [PATCH] Some fixes + first draft of Chakart (just thoughts for now) --- client/src/components/BaseGame.vue | 85 ++++++++++++++++-------- client/src/variants/Chakart.js | 102 +++++++++++++++++++++++++++++ client/src/variants/Orda.js | 3 +- client/src/variants/Shogi.js | 18 +++-- client/src/views/Hall.vue | 7 +- server/models/Game.js | 13 ++-- 6 files changed, 185 insertions(+), 43 deletions(-) create mode 100644 client/src/variants/Chakart.js diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue index 62fae753..e309c5df 100644 --- a/client/src/components/BaseGame.vue +++ b/client/src/components/BaseGame.vue @@ -218,7 +218,9 @@ export default { // Post-processing: decorate each move with notation and FEN this.vr = new V(game.fenStart); this.inMultimove = false; //in case of - this.$refs["board"].resetCurrentAttempt(); //also in case of + if (!!this.$refs["board"]) + // Also in case of: + this.$refs["board"].resetCurrentAttempt(); let analyseBtn = document.getElementById("analyzeBtn"); if (!!analyseBtn) analyseBtn.classList.remove("active"); const parsedFen = V.ParseFen(game.fenStart); @@ -226,27 +228,35 @@ export default { this.firstMoveNumber = Math.floor(parsedFen.movesCount / 2) + 1; let L = this.moves.length; if (L == 0) { - this.incheck = []; - this.score = "*"; + // Could be started on a random position in analysis mode: + this.incheck = this.vr.getCheckSquares(); + this.score = this.vr.getCurrentScore(); + if (this.score != '*') { + // Show score on screen + const message = getScoreMessage(this.score); + this.showEndgameMsg(this.score + " . " + this.st.tr[message]); + } } - this.moves.forEach((move,idx) => { - // Strategy working also for multi-moves: - if (!Array.isArray(move)) move = [move]; - const Lm = move.length; - move.forEach((m,idxM) => { - m.notation = this.vr.getNotation(m); - m.unambiguous = V.GetUnambiguousNotation(m); - this.vr.play(m); - const checkSquares = this.vr.getCheckSquares(); - if (checkSquares.length > 0) m.notation += "+"; - if (idxM == Lm - 1) m.fen = this.vr.getFen(); - if (idx == L - 1 && idxM == Lm - 1) { - this.incheck = checkSquares; - this.score = this.vr.getCurrentScore(); - if (["1-0", "0-1"].includes(this.score)) m.notation += "#"; - } + else { + this.moves.forEach((move,idx) => { + // Strategy working also for multi-moves: + if (!Array.isArray(move)) move = [move]; + const Lm = move.length; + move.forEach((m,idxM) => { + m.notation = this.vr.getNotation(m); + m.unambiguous = V.GetUnambiguousNotation(m); + this.vr.play(m); + const checkSquares = this.vr.getCheckSquares(); + if (checkSquares.length > 0) m.notation += "+"; + if (idxM == Lm - 1) m.fen = this.vr.getFen(); + if (idx == L - 1 && idxM == Lm - 1) { + this.incheck = checkSquares; + this.score = this.vr.getCurrentScore(); + if (["1-0", "0-1"].includes(this.score)) m.notation += "#"; + } + }); }); - }); + } if (firstMoveColor == "b") { // 'start' & 'end' is required for Board component this.moves.unshift({ @@ -420,7 +430,12 @@ export default { // "light": if gotoMove() or gotoEnd() play: function(move, received, light, autoplay) { // Freeze while choices are shown: - if (this.$refs["board"].choices.length > 0) return; + if ( + !!this.$refs["board"].selectedPiece || + this.$refs["board"].choices.length > 0 + ) { + return; + } const navigate = !move; // Forbid navigation during autoplay: if (navigate && this.autoplay && !autoplay) return; @@ -595,8 +610,13 @@ export default { }, // "light": if gotoMove() or gotoBegin() undo: function(move, light) { - // Freeze while choices are shown: - if (this.$refs["board"].choices.length > 0 || this.autoplay) return; + if ( + this.autoplay || + !!this.$refs["board"].selectedPiece || + this.$refs["board"].choices.length > 0 + ) { + return; + } this.$refs["board"].resetCurrentAttempt(); if (this.inMultimove) { this.cancelCurrentMultimove(); @@ -623,7 +643,13 @@ export default { } }, gotoMove: function(index) { - if (this.$refs["board"].choices.length > 0 || this.autoplay) return; + if ( + this.autoplay || + !!this.$refs["board"].selectedPiece || + this.$refs["board"].choices.length > 0 + ) { + return; + } this.$refs["board"].resetCurrentAttempt(); if (this.inMultimove) this.cancelCurrentMultimove(); if (index == this.cursor) return; @@ -642,7 +668,13 @@ export default { this.emitFenIfAnalyze(); }, gotoBegin: function() { - if (this.$refs["board"].choices.length > 0 || this.autoplay) return; + if ( + this.autoplay || + !!this.$refs["board"].selectedPiece || + this.$refs["board"].choices.length > 0 + ) { + return; + } this.$refs["board"].resetCurrentAttempt(); if (this.inMultimove) this.cancelCurrentMultimove(); const minCursor = @@ -655,11 +687,8 @@ export default { this.emitFenIfAnalyze(); }, gotoEnd: function() { - if (this.$refs["board"].choices.length > 0 || this.autoplay) return; - this.$refs["board"].resetCurrentAttempt(); if (this.cursor == this.moves.length - 1) return; this.gotoMove(this.moves.length - 1); - this.emitFenIfAnalyze(); }, flip: function() { if (this.$refs["board"].choices.length > 0) return; diff --git a/client/src/variants/Chakart.js b/client/src/variants/Chakart.js new file mode 100644 index 00000000..7b2e77e2 --- /dev/null +++ b/client/src/variants/Chakart.js @@ -0,0 +1,102 @@ +import { ChessRules } from "@/base_rules"; + +export class ChakartRules extends ChessRules { + // NOTE: getBasicMove, ajouter les bonus à vanish array + // + déterminer leur effet (si cavalier) ou case (si banane ou bombe) + // (L'effet doit être caché au joueur : devrait être OK) + // + // Saut possible par dessus bonus ou champis mais pas bananes ou bombes + + // FEN : immobilized (pas flag), castle flags + flags peach (power used?) + // "pièces" supplémentaires : bananes, bombes, champis, bonus --> + couleur ? + // (Semble mieux sans couleur => couleur spéciale indiquant que c'est pas jouable) + // (Attention: pas jouables cf. getPotentialMoves...) + + setOtherVariables(fen) { + super.setOtherVariables(fen); + this.subTurn = 1; + } + + getPotentialMovesFrom([x, y]) { + // TODO: bananes et bombes limitent les déplacements (agissent comme un mur "capturable") + // bananes jaunes et rouges ?! (agissant sur une seule couleur ?) --> mauvaise idée. + if (this.subTurn == 2) { + // TODO: coup compatible avec firstMove + } + } + + getPotentialPawnMoves(sq) { + //Toad: pion + // laisse sur sa case de départ un champi turbo permettant à Peach d'aller + // une case plus loin, et aux pièces arrivant sur cette case de sauter par + // dessus une pièce immédiatement adjacente (en atterissant juste derrière). + } + + // Coups en 2 temps (si pose) + getPotentialRookMoves(sq) { + //Donkey : tour + // pose une banane (optionnel) sur une case adjacente (diagonale) à celle d'arrivée + // Si une pièce arrive sur la peau de banane, alors elle effectue un déplacement + // aléatoire d'une (2?) case (vertical ou horizontal) depuis sa position finale. + } + + // Coups en 2 temps (si pose) + getPotentialBishopMoves([x, y]) { + //Wario: fou + // pose une bombe (optionnel) sur une case orthogonalement adjacente à la case d'arrivée + // Si une pièce arrive sur une bombe, alors elle effectue un déplacement diagonal + // aléatoire d'une (2?) case depuis sa position finale (juste une case si impossible). + } + + getPotentialKnightMoves([x, y]) { + //Yoshi: cavalier + // laisse sur sa case de départ un bonus aléatoire + // (NOTE: certains bonus pourraient ne pas être applicables ==> pion bloqué par exemple) + // - i) roi boo(*E*) : échange avec n'importe quelle pièce (choix du joueur, type et/ou couleur différents) + // à condition que la position résultante ne soit pas un auto-échec + // - i*) koopa(*B*) : ramène sur la case initiale + // - ii) toadette(*R*) : permet de poser une pièce capturée sur le plateau + // (n'importe où sauf 8eme rangée pour les pions) + // - ii*) chomp(*W*) : mange la pièce ; si c'est Peach, c'est perdu + // - iii) daisy(*T*) : permet de rejouer un coup avec la même pièce --> cumulable si ensuite coup sur bonus Daisy. + // - iii*) bowser(*M*) : immobilise la pièce (marquée jaune/rouge), qui ne pourra pas jouer au tour suivant + // - iv) luigi(*L*) : fait changer de camp une pièce adverse (aléatoire ?) (sauf le roi) + // - iv*) waluigi(*D*) : fait changer de camp une de nos pièces (aléatoire ?) (sauf le roi) + // --> i, ii, iii en deux temps (subTurn 1 & 2) + // iii* : indication dans FEN (immobilized) + } + + getPotentialQueenMoves(sq) { + //Mario: dame + // pouvoir "casquette ailée" (à chaque coup?) : peut sauter par dessus n'importe quelle pièce (une seule), sans la capturer. + } + + getPotentialKingMoves(sq) { + //Peach: roi + // Carapace rouge (disons ^^) jouable une seule fois dans la partie, + // au lieu de se déplacer. Capture un ennemi au choix parmi les plus proches, + // à condition qu'ils soient visible (suivant les directions de déplacement d'une dame). + // Profite des accélérateurs posés par les pions (+ 1 case : obligatoire). + } + + play(move) { + // TODO: subTurn passe à 2 si potentiellement pose (tour, fou) ou si choix (reconnaître i (ok), ii (ok) et iii (si coup normal + pas immobilisé) ?) + // voire +2 si plusieurs daisy... + } + + undo(move) { + // TODO: reconnaissance inverse si subTurn == 1 --> juste impossible ==> marquer pendant play (comme DoubleMove1 : move.turn = ...) + } + + //atLeastOneMove() should be OK + + getCurrentScore() { + //TODO: But = capturer la princesse adverse. Variante : but = la princesse arrive de l'autre côté de l'échiquier. + //==> On peut mixer ces deux conditions : arriver au bout du plateau ou capturer la princesse adverse. + return '*'; + } + + //Détails : + //Si une pièce pose quelque chose sur une case ça remplace ce qui y était déjà. + //Pas de condition de pat, puisque l'objectif est la capture de la reine :) +}; diff --git a/client/src/variants/Orda.js b/client/src/variants/Orda.js index f1bfc911..afb736a7 100644 --- a/client/src/variants/Orda.js +++ b/client/src/variants/Orda.js @@ -234,13 +234,14 @@ export class OrdaRules extends ChessRules { this.isAttackedByKheshig(sq, color) ); } - // Horde: only pawn and queen (if promotions) in common: + // Horde: only pawn, king and queen (if promotions) in common: return ( super.isAttackedByPawn(sq, color) || this.isAttackedByLancer(sq, color) || this.isAttackedByKheshig(sq, color) || this.isAttackedByArcher(sq, color) || this.isAttackedByYurt(sq, color) || + super.isAttackedByKing(sq, color) || super.isAttackedByQueen(sq, color) ); } diff --git a/client/src/variants/Shogi.js b/client/src/variants/Shogi.js index 082d9391..50d273f2 100644 --- a/client/src/variants/Shogi.js +++ b/client/src/variants/Shogi.js @@ -372,6 +372,18 @@ export class ShogiRules extends ChessRules { ); } + getPotentialLanceMoves(sq) { + const forward = (this.turn == 'w' ? -1 : 1); + return this.getSlideNJumpMoves( + sq, + [[forward, 0]], + { + promote: V.P_LANCE, + force: true + } + ); + } + getPotentialRookMoves(sq) { return this.getSlideNJumpMoves( sq, V.steps[V.ROOK], { promote: V.P_ROOK }); @@ -382,12 +394,6 @@ export class ShogiRules extends ChessRules { sq, V.steps[V.BISHOP], { promote: V.P_BISHOP }); } - getPotentialLanceMoves(sq) { - const forward = (this.turn == 'w' ? -1 : 1); - return this.getSlideNJumpMoves( - sq, [[forward, 0]], { promote: V.P_LANCE }); - } - getPotentialDragonMoves(sq) { return ( this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat( diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index 133a5f9e..01f588f2 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -822,12 +822,13 @@ export default { // Ignore games where I play (will go in MyGames page), // and also games that I already received. if ( + this.games.findIndex(g => g.id == game.id) == -1 && game.players.every(p => { return ( p.sid != this.st.user.sid && (p.id == 0 || p.id != this.st.user.id) ); - }) && this.games.findIndex(g => g.id == game.id) == -1 + }) ) { let newGame = game; newGame.type = this.classifyObject(game); @@ -837,6 +838,7 @@ export default { newGame.score = "*"; this.games.push(newGame); if ( + newGame.score == '*' && (newGame.type == "live" && this.gdisplay == "corr") || (newGame.type == "corr" && this.gdisplay == "live") ) { @@ -888,7 +890,8 @@ export default { if ( this.cursor == Number.MAX_SAFE_INTEGER && this.games.length == 0 && - this.gdisplay == "live" + this.gdisplay == "live" && + res.games.some(g => g.score == '*') ) { // First loading: show indicators document diff --git a/server/models/Game.js b/server/models/Game.js index 401e3653..1d2cd684 100644 --- a/server/models/Game.js +++ b/server/models/Game.js @@ -128,12 +128,13 @@ const GameModel = db.serialize(function() { let query = "SELECT id, vid, cadence, created, score, white, black " + - "FROM Games "; - if (uid > 0) query += - "WHERE " + - " created < " + cursor + " AND " + - " white <> " + uid + " AND " + - " black <> " + uid + " "; + "FROM Games " + + "WHERE created < " + cursor + " "; + if (uid > 0) { + query += + " AND white <> " + uid + " " + + " AND black <> " + uid + " "; + } query += "ORDER BY created DESC " + "LIMIT 20"; //TODO: 20 hard-coded... -- 2.44.0