From 7ba4a5bc5b64e19a1e7f26aa232d5c50770d07ad Mon Sep 17 00:00:00 2001 From: Benjamin Auder <benjamin.auder@somewhere> Date: Wed, 4 Mar 2020 16:12:30 +0100 Subject: [PATCH] Experimental symmetric randomness + deterministic option --- TODO | 8 ---- client/src/base_rules.js | 16 ++++++-- client/src/components/BaseGame.vue | 6 ++- client/src/components/ChallengeList.vue | 15 +++++++ client/src/translations/about/en.pug | 12 +++--- client/src/translations/about/es.pug | 12 +++--- client/src/translations/about/fr.pug | 12 +++--- client/src/translations/en.js | 5 +++ client/src/translations/es.js | 5 +++ client/src/translations/fr.js | 5 +++ client/src/utils/timeControl.js | 2 + client/src/variants/Allmate1.js | 4 +- client/src/variants/Allmate2.js | 4 +- client/src/variants/Antiking.js | 11 ++++- client/src/variants/Arena.js | 6 ++- client/src/variants/Baroque.js | 12 +++++- client/src/variants/Checkered.js | 8 ++-- client/src/variants/Circular.js | 11 ++++- client/src/variants/Crazyhouse.js | 4 +- client/src/variants/Grand.js | 13 +++++- client/src/variants/Grasshopper.js | 4 +- client/src/variants/Hidden.js | 1 + client/src/variants/Hiddenqueen.js | 6 +-- client/src/variants/Knightmate.js | 53 ++----------------------- client/src/variants/Losers.js | 14 ++++++- client/src/variants/Recycle.js | 4 +- client/src/variants/Royalrace.js | 14 ++++++- client/src/variants/Shatranj.js | 4 +- client/src/variants/Suction.js | 4 +- client/src/variants/Threechecks.js | 8 ++-- client/src/variants/Upsidedown.js | 11 ++++- client/src/variants/Wildebeest.js | 13 +++++- client/src/views/Analyse.vue | 12 +++--- client/src/views/Hall.vue | 14 ++++++- server/db/create.sql | 1 + server/models/Challenge.js | 13 +++--- server/routes/challenges.js | 1 + 37 files changed, 222 insertions(+), 126 deletions(-) diff --git a/TODO b/TODO index ab06e20a..7e9c4834 100644 --- a/TODO +++ b/TODO @@ -7,14 +7,6 @@ From MyGames page: send "mconnect" to all online players (me included: potential When quit, send mdisconnect (relayed by server if no other MyGames tab). And remove current "notify through newmove" on server in sockets.js -Analyse mode when launched from a position: should keep orientation ---> $route query param, "side=w or b" - -Subcursor (intra move) for multi-move variants for better navigation (in BaseGame) - -Allow symmetric mode in all variants (vertical symmetry for racing kings) -flag in challenge, "Symmetric: true / false", option of GenRandInitFen() - # Misc: Saw once a "double challenge" bug, one anonymous and a second one logged Both were asked a challenge probably, and both challenges added as different ones. diff --git a/client/src/base_rules.js b/client/src/base_rules.js index 5b335b0f..93772b9b 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -238,11 +238,21 @@ export const ChessRules = class ChessRules { ///////////// // FEN UTILS - // Setup the initial random (assymetric) position - static GenRandInitFen() { + // Setup the initial random (asymmetric) position + static GenRandInitFen(randomness) { + if (!randomness) randomness = 2; + if (randomness == 0) + // Deterministic: + return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 1111 -"; + let pieces = { w: new Array(8), b: new Array(8) }; - // Shuffle pieces on first and last rank + // Shuffle pieces on first (and last rank if randomness == 2) for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + let positions = ArrayFun.range(8); // Get random squares for bishops diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue index 2a1a4474..4af4e153 100644 --- a/client/src/components/BaseGame.vue +++ b/client/src/components/BaseGame.vue @@ -227,11 +227,13 @@ export default { this.lastMove = null; }, analyzePosition: function() { - const newUrl = + let newUrl = "/analyse/" + this.game.vname + "/?fen=" + this.vr.getFen().replace(/ /g, "_"); + if (this.game.mycolor) + newUrl += "&side=" + this.game.mycolor; // Open in same tab in live games (against cheating) if (this.game.type == "live") this.$router.push(newUrl); else window.open("#" + newUrl); @@ -337,7 +339,7 @@ export default { } }; const playMove = () => { - const animate = V.ShowMoves == "all" && received; + const animate = V.ShowMoves == "all" && (received || navigate); if (!Array.isArray(move)) move = [move]; let moveIdx = 0; let self = this; diff --git a/client/src/components/ChallengeList.vue b/client/src/components/ChallengeList.vue index a350c705..7572ede1 100644 --- a/client/src/components/ChallengeList.vue +++ b/client/src/components/ChallengeList.vue @@ -6,6 +6,7 @@ div th {{ st.tr["Variant"] }} th {{ st.tr["With"] }} th {{ st.tr["Cadence"] }} + th {{ st.tr["Random?"] }} tbody tr( v-for="c in sortedChallenges" @@ -15,6 +16,7 @@ div td {{ c.vname }} td {{ withWho(c) }} td {{ c.cadence }} + td(:class="getRandomnessClass(c)") </template> <script> @@ -52,6 +54,11 @@ export default { if (c.from.sid == this.st.user.sid || c.from.id == this.st.user.id) return c.to || this.st.tr["Any player"]; return c.from.name || "@nonymous"; + }, + getRandomnessClass: function(c) { + return { + ["random-" + c.randomness]: true + }; } } }; @@ -63,4 +70,12 @@ tr.fromyou > td font-style: italic tr.toyou > td background-color: #fcd785 + +tr > td:last-child + &.random-0 + background-color: #FF5733 + &.random-1 + background-color: #2B63B4 + &.random-2 + background-color: #33B42B </style> diff --git a/client/src/translations/about/en.pug b/client/src/translations/about/en.pug index a47463ce..3968dac4 100644 --- a/client/src/translations/about/en.pug +++ b/client/src/translations/about/en.pug @@ -9,12 +9,14 @@ p. A few years later I had a prototype to play in live, then other variants were added and the website was made more attractive. -h3 Philosophy +h3 Notes -p Have fun and challenge your mind :-) -ul - li ELO rating is purposely absent from this website. - li Games start with a random assymetric position. +p ELO rating is purposely absent from this website. + +p. + Games start by default with a random asymmetric position. + Random symmetric or even deterministic positions are available too, + if you prefer more fairness (but less fun? :-P ). h3 Contribute diff --git a/client/src/translations/about/es.pug b/client/src/translations/about/es.pug index 4566df84..f240ef89 100644 --- a/client/src/translations/about/es.pug +++ b/client/src/translations/about/es.pug @@ -9,12 +9,14 @@ p. para jugar en vivo, y luego otras variantes se agregaron y el sitio se hizo más atractivo. -h3 FilosofÃa +h3 Notas -p Diviértete y desafÃa tu mente:-) -ul - li El ranking ELO está ausente a propósito de este sitio. - li Las partidas comienzan con una posición aleatoria no simétrica. +p El ranking ELO está ausente a propósito de este sitio. + +p. + Las partidas comienzan por defecto con una posición aleatoria no simétrica. + Es posible una posición aleatoria simétrica o incluso determinista, + si prefieres más justicia (¿pero más aburrimiento? :-P). h3 Contribuir diff --git a/client/src/translations/about/fr.pug b/client/src/translations/about/fr.pug index 2860445d..b994c792 100644 --- a/client/src/translations/about/fr.pug +++ b/client/src/translations/about/fr.pug @@ -9,12 +9,14 @@ p. tard je disposais d'un prototype pour jouer en direct, puis d'autres variantes se sont ajoutées et le site a été rendu plus attractif. -h3 Philosophie +h3 Notes -p Exercez vos neurones en vous amusant :-) -ul - li Le classement ELO est à dessein absent de ce site. - li Les parties démarrent avec une position aléatoire non symétrique. +p Le classement ELO est à dessein absent de ce site. + +p. + Les parties démarrent par défaut avec une position aléatoire non symétrique. + Une position aléatoire symétrique ou même déterministe est possible, + si vous préférez plus de justice (mais plus d'ennui ? :-P ). h3 Contribuer diff --git a/client/src/translations/en.js b/client/src/translations/en.js index e7fcb24d..856034cc 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -9,6 +9,7 @@ export const translations = { "Any player": "Any player", Apply: "Apply", "Are you sure?": "Are you sure?", + "Asymmetric random": "Asymmetric random", "Authentication successful!": "Authentication successful!", "Back to Hall in 3 seconds...": "Back to Hall in 3 seconds...", "Back to list": "Back to list", @@ -30,6 +31,7 @@ export const translations = { "Correspondance games": "Correspondance games", "Database error: stop private browsing, or update your browser": "Database error: stop private browsing, or update your browser", Delete: "Delete", + Deterministic: "Deterministic", Download: "Download", Draw: "Draw", "Draw offer only in your turn": "Draw offer only in your turn", @@ -92,6 +94,8 @@ export const translations = { "Processing... Please wait": "Processing... Please wait", Problems: "Problems", "participant(s):": "participant(s):", + "Random?": "Random?", + "Randomness": "Randomness", Refuse: "Refuse", Register: "Register", "Registration complete! Please check your emails now": "Registration complete! Please check your emails now", @@ -112,6 +116,7 @@ export const translations = { Stop: "Stop", "Stop game": "Stop game", Subject: "Subject", + "Symmetric random": "Symmetric random", "Terminate game?": "Terminate game?", "Three repetitions": "Three repetitions", Time: "Time", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 70e00e70..15926180 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -9,6 +9,7 @@ export const translations = { "Any player": "Cualquier jugador", Apply: "Aplicar", "Are you sure?": "¿Está usted seguro?", + "Asymmetric random": "Aleatorio asimétrico", "Authentication successful!": "¡Autenticación exitosa!", "Back to Hall in 3 seconds...": "Regreso al salón en 3 segundos...", "Back to list": "Volver a la lista", @@ -30,6 +31,7 @@ export const translations = { "Correspondance games": "Partidas por correspondencia", "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", Download: "Descargar", Draw: "Tablas", "Draw offer only in your turn": "Oferta de tablas solo en tu turno", @@ -92,6 +94,8 @@ export const translations = { "Processing... Please wait": "Procesando... por favor espere", Problems: "Problemas", "participant(s):": "participante(s):", + "Random?": "Aleatorio?", + "Randomness": "Grado de azar", Refuse: "Rechazar", Register: "Registrarse", "Registration complete! Please check your emails now": "¡Registro completo! Revise sus correos electrónicos ahora", @@ -112,6 +116,7 @@ export const translations = { Stop: "Interrupción", "Stop game": "Terminar la partida", Subject: "Asunto", + "Symmetric random": "Aleatorio simétrico", "Terminate game?": "¿Terminar la partida?", "Three repetitions": "Tres repeticiones", Time: "Tiempo", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 3fa73d08..848241a3 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -10,6 +10,7 @@ export const translations = { Apply: "Appliquer", "Authentication successful!": "Authentification réussie !", "Are you sure?": "Ãtes vous sûr?", + "Asymmetric random": "Aléatoire asymétrique", "Back to Hall in 3 seconds...": "Retour au Hall dans 3 secondes...", "Back to list": "Retour à la liste", "Black to move": "Trait aux noirs", @@ -30,6 +31,7 @@ export const translations = { "Correspondance games": "Parties par correspondance", "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", Download: "Télécharger", Draw: "Nulle", "Draw offer only in your turn": "Proposition de nulle seulement sur votre temps", @@ -92,6 +94,8 @@ export const translations = { "Processing... Please wait": "Traitement en cours... Attendez SVP", Problems: "Problèmes", "participant(s):": "participant(s) :", + "Random?": "Aléatoire?", + "Randomness": "Degré d'aléa", Refuse: "Refuser", Register: "S'enregistrer", "Registration complete! Please check your emails now": "Enregistrement terminé ! Allez voir vos emails maintenant", @@ -112,6 +116,7 @@ export const translations = { Stop: "Arrêt", "Stop game": "Arrêter la partie", Subject: "Sujet", + "Symmetric random": "Aléatoire symétrique", "Terminate game?": "Stopper la partie ?", "Three repetitions": "Triple répétition", Time: "Temps", diff --git a/client/src/utils/timeControl.js b/client/src/utils/timeControl.js index b4bac053..c38d08b1 100644 --- a/client/src/utils/timeControl.js +++ b/client/src/utils/timeControl.js @@ -33,6 +33,8 @@ export function extractTime(cadence) { const mainTime = timeUnitToSeconds(mainTimeValue, mainTimeUnit); let increment = 0; if (tcParts.length >= 2) { + // Correspondance games don't use an increment: + if (mainTimeUnit == 'd') return null; tcParts[1] += "s"; const incrementArray = tcParts[1].match(/^([0-9]+)([smhd]+)$/); if (!incrementArray) return null; diff --git a/client/src/variants/Allmate1.js b/client/src/variants/Allmate1.js index bf8f1be9..5511dd54 100644 --- a/client/src/variants/Allmate1.js +++ b/client/src/variants/Allmate1.js @@ -10,8 +10,8 @@ export const VariantRules = class Allmate1Rules extends ChessRules { return []; } - static GenRandInitFen() { - return ChessRules.GenRandInitFen().replace(/ -$/, ""); + static GenRandInitFen(randomness) { + return ChessRules.GenRandInitFen(randomness).replace(/ -$/, ""); } getPotentialMovesFrom([x, y]) { diff --git a/client/src/variants/Allmate2.js b/client/src/variants/Allmate2.js index 34a0f8b0..0e515b2d 100644 --- a/client/src/variants/Allmate2.js +++ b/client/src/variants/Allmate2.js @@ -10,8 +10,8 @@ export const VariantRules = class Allmate2Rules extends ChessRules { return []; } - static GenRandInitFen() { - return ChessRules.GenRandInitFen().replace(/ -$/, ""); + static GenRandInitFen(randomness) { + return ChessRules.GenRandInitFen(randomness).replace(/ -$/, ""); } getPotentialMovesFrom([x, y]) { diff --git a/client/src/variants/Antiking.js b/client/src/variants/Antiking.js index 21e37f4b..1c062bbf 100644 --- a/client/src/variants/Antiking.js +++ b/client/src/variants/Antiking.js @@ -151,10 +151,19 @@ export const VariantRules = class AntikingRules extends ChessRules { ); } - static GenRandInitFen() { + static GenRandInitFen(randomness) { + if (!randomness) randomness = 2; + if (randomness == 0) + return "rnbqkbnr/pppppppp/3A4/8/8/3a4/PPPPPPPP/RNBQKBNR w 0 1111 -"; + let pieces = { w: new Array(8), b: new Array(8) }; let antikingPos = { w: -1, b: -1 }; for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + let positions = ArrayFun.range(8); // Get random squares for bishops, but avoid corners; because, diff --git a/client/src/variants/Arena.js b/client/src/variants/Arena.js index 42b1f4af..f3713e27 100644 --- a/client/src/variants/Arena.js +++ b/client/src/variants/Arena.js @@ -5,8 +5,10 @@ export const VariantRules = class ArenaRules extends ChessRules { return false; } - static GenRandInitFen() { - return ChessRules.GenRandInitFen().replace("w 1111 -", "w -"); + static GenRandInitFen(randomness) { + return ChessRules + .GenRandInitFen(randomness) + .replace("w 0 1111 -", "w 0 -"); } static InArena(x) { diff --git a/client/src/variants/Baroque.js b/client/src/variants/Baroque.js index ae4b511f..a860fe79 100644 --- a/client/src/variants/Baroque.js +++ b/client/src/variants/Baroque.js @@ -545,10 +545,20 @@ export const VariantRules = class BaroqueRules extends ChessRules { return 2; } - static GenRandInitFen() { + static GenRandInitFen(randomness) { + if (!randomness) randomness = 2; + if (randomness == 0) + // Deterministic: + return "rnbqkbnrm/pppppppp/8/8/8/8/PPPPPPPP/MNBKQBNR w 0"; + let pieces = { w: new Array(8), b: new Array(8) }; // Shuffle pieces on first and last rank for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + let positions = ArrayFun.range(8); // Get random squares for every piece, totally freely diff --git a/client/src/variants/Checkered.js b/client/src/variants/Checkered.js index ac99602b..5f9bf8f6 100644 --- a/client/src/variants/Checkered.js +++ b/client/src/variants/Checkered.js @@ -295,10 +295,10 @@ export const VariantRules = class CheckeredRules extends ChessRules { return evaluation; } - static GenRandInitFen() { - const randFen = ChessRules.GenRandInitFen(); - // Add 16 pawns flags + empty cmove: - return randFen.replace(" w 0 1111", " w 0 11111111111111111111 -"); + static GenRandInitFen(randomness) { + return ChessRules.GenRandInitFen(randomness) + // Add 16 pawns flags + empty cmove: + .replace(" w 0 1111", " w 0 11111111111111111111 -"); } static ParseFen(fen) { diff --git a/client/src/variants/Circular.js b/client/src/variants/Circular.js index 9ec597bd..93df8efc 100644 --- a/client/src/variants/Circular.js +++ b/client/src/variants/Circular.js @@ -30,10 +30,19 @@ export const VariantRules = class CircularRules extends ChessRules { this.pawnFlags = flags; } - static GenRandInitFen() { + static GenRandInitFen(randomness) { + if (!randomness) randomness = 2; + if (randomness == 0) + return "8/8/pppppppp/rnbqkbnr/8/8/PPPPPPPP/RNBQKBNR w 0 1111111111111111"; + let pieces = { w: new Array(8), b: new Array(8) }; // Shuffle pieces on first and fifth rank for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + let positions = ArrayFun.range(8); // Get random squares for bishops diff --git a/client/src/variants/Crazyhouse.js b/client/src/variants/Crazyhouse.js index 24018d14..7dc53466 100644 --- a/client/src/variants/Crazyhouse.js +++ b/client/src/variants/Crazyhouse.js @@ -28,8 +28,8 @@ export const VariantRules = class CrazyhouseRules extends ChessRules { }); } - static GenRandInitFen() { - return ChessRules.GenRandInitFen() + " 0000000000 -"; + static GenRandInitFen(randomness) { + return ChessRules.GenRandInitFen(randomness) + " 0000000000 -"; } getFen() { diff --git a/client/src/variants/Grand.js b/client/src/variants/Grand.js index d3659af8..c910dd8e 100644 --- a/client/src/variants/Grand.js +++ b/client/src/variants/Grand.js @@ -316,10 +316,21 @@ export const VariantRules = class GrandRules extends ChessRules { return 2; } - static GenRandInitFen() { + static GenRandInitFen(randomness) { + if (!randomness) randomness = 2; + if (randomness == 0) { + return "rnbqkmcbnr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RNBQKMCBNR " + + "w 0 1111 - 00000000000000"; + } + let pieces = { w: new Array(10), b: new Array(10) }; // Shuffle pieces on first and last rank for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + let positions = ArrayFun.range(10); // Get random squares for bishops diff --git a/client/src/variants/Grasshopper.js b/client/src/variants/Grasshopper.js index 8f24340a..a991035b 100644 --- a/client/src/variants/Grasshopper.js +++ b/client/src/variants/Grasshopper.js @@ -133,8 +133,8 @@ export const VariantRules = class GrasshopperRules extends ChessRules { ); } - static GenRandInitFen() { - return ChessRules.GenRandInitFen() + static GenRandInitFen(randomness) { + return ChessRules.GenRandInitFen(randomness) .replace("w 0 1111 -", "w 0 1111") .replace( "/pppppppp/8/8/8/8/PPPPPPPP/", diff --git a/client/src/variants/Hidden.js b/client/src/variants/Hidden.js index c79267a5..5ccc3d7e 100644 --- a/client/src/variants/Hidden.js +++ b/client/src/variants/Hidden.js @@ -157,6 +157,7 @@ export const VariantRules = class HiddenRules extends ChessRules { return moves; } + // Ignore randomness here: placement is always random asymmetric static GenRandInitFen() { let pieces = { w: new Array(8), b: new Array(8) }; // Shuffle pieces + pawns on two first ranks diff --git a/client/src/variants/Hiddenqueen.js b/client/src/variants/Hiddenqueen.js index 4f66894b..7048d41c 100644 --- a/client/src/variants/Hiddenqueen.js +++ b/client/src/variants/Hiddenqueen.js @@ -175,9 +175,9 @@ export const VariantRules = class HiddenqueenRules extends ChessRules { return this.filterValid(this.getPotentialMovesFrom(sq)); } - static GenRandInitFen() { - let fen = ChessRules.GenRandInitFen(); - // Place hidden queens at random: + static GenRandInitFen(randomness) { + let fen = ChessRules.GenRandInitFen(randomness); + // Place hidden queens at random (always): let hiddenQueenPos = randInt(8); let pawnRank = "PPPPPPPP".split(""); pawnRank[hiddenQueenPos] = "T"; diff --git a/client/src/variants/Knightmate.js b/client/src/variants/Knightmate.js index c0e84c63..0ac7ebed 100644 --- a/client/src/variants/Knightmate.js +++ b/client/src/variants/Knightmate.js @@ -15,56 +15,9 @@ export const VariantRules = class KnightmateRules extends ChessRules { return ([V.KING, V.COMMONER].includes(b[1]) ? "Knightmate/" : "") + b; } - static GenRandInitFen() { - let pieces = { w: new Array(8), b: new Array(8) }; - // Shuffle pieces on first and last rank - for (let c of ["w", "b"]) { - let positions = ArrayFun.range(8); - - // Get random squares for bishops - let randIndex = 2 * randInt(4); - const bishop1Pos = positions[randIndex]; - let randIndex_tmp = 2 * randInt(4) + 1; - const bishop2Pos = positions[randIndex_tmp]; - positions.splice(Math.max(randIndex, randIndex_tmp), 1); - positions.splice(Math.min(randIndex, randIndex_tmp), 1); - - // Get random squares for commoners - randIndex = randInt(6); - const commoner1Pos = positions[randIndex]; - positions.splice(randIndex, 1); - randIndex = randInt(5); - const commoner2Pos = positions[randIndex]; - positions.splice(randIndex, 1); - - // Get random square for queen - randIndex = randInt(4); - const queenPos = positions[randIndex]; - positions.splice(randIndex, 1); - - // Rooks and king positions are now fixed, - // because of the ordering rook-king-rook - const rook1Pos = positions[0]; - const kingPos = positions[1]; - const rook2Pos = positions[2]; - - // Finally put the shuffled pieces in the board array - pieces[c][rook1Pos] = "r"; - pieces[c][commoner1Pos] = "c"; - pieces[c][bishop1Pos] = "b"; - pieces[c][queenPos] = "q"; - pieces[c][kingPos] = "k"; - pieces[c][bishop2Pos] = "b"; - pieces[c][commoner2Pos] = "c"; - pieces[c][rook2Pos] = "r"; - } - // Add turn + flags + enpassant - return ( - pieces["b"].join("") + - "/pppppppp/8/8/8/8/PPPPPPPP/" + - pieces["w"].join("").toUpperCase() + - " w 0 1111 -" - ); + static GenRandInitFen(randomness) { + return ChessRules.GenRandInitFen(randomness) + .replace(/n/g, 'c').replace(/N/g, 'C'); } getPotentialMovesFrom([x, y]) { diff --git a/client/src/variants/Losers.js b/client/src/variants/Losers.js index 81d2730a..b4385bab 100644 --- a/client/src/variants/Losers.js +++ b/client/src/variants/Losers.js @@ -142,10 +142,19 @@ export const VariantRules = class LosersRules extends ChessRules { return -super.evalPosition(); //better with less material } - static GenRandInitFen() { + static GenRandInitFen(randomness) { + if (!randomness) randomness = 2; + if (randomness == 0) + return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 -"; + let pieces = { w: new Array(8), b: new Array(8) }; // Shuffle pieces on first and last rank for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + let positions = ArrayFun.range(8); // Get random squares for bishops @@ -194,7 +203,8 @@ export const VariantRules = class LosersRules extends ChessRules { pieces["b"].join("") + "/pppppppp/8/8/8/8/PPPPPPPP/" + pieces["w"].join("").toUpperCase() + + // En-passant allowed, but no flags " w 0 -" - ); //en-passant allowed, but no flags + ); } }; diff --git a/client/src/variants/Recycle.js b/client/src/variants/Recycle.js index 50ccd74e..a234350b 100644 --- a/client/src/variants/Recycle.js +++ b/client/src/variants/Recycle.js @@ -18,8 +18,8 @@ export const VariantRules = class RecycleRules extends ChessRules { }); } - static GenRandInitFen() { - return ChessRules.GenRandInitFen() + " 0000000000"; + static GenRandInitFen(randomness) { + return ChessRules.GenRandInitFen(randomness) + " 0000000000"; } getFen() { diff --git a/client/src/variants/Royalrace.js b/client/src/variants/Royalrace.js index 6f64d24f..ff6e1e1d 100644 --- a/client/src/variants/Royalrace.js +++ b/client/src/variants/Royalrace.js @@ -19,10 +19,22 @@ export const VariantRules = class RoyalraceRules extends ChessRules { return { x: 11, y: 11 }; } - static GenRandInitFen() { + static GenRandInitFen(randomness) { + if (!randomness) randomness = 2; + if (randomness == 0) + return "11/11/11/11/11/11/11/11/11/QRBNP1pnbrq/KRBNP1pnbrk w 0"; + let pieces = { w: new Array(10), b: new Array(10) }; // Shuffle pieces on first and second rank for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = JSON.parse(JSON.stringify(pieces['w'])).reverse(); + pieces['b'] = + pieces['b'].splice(5,10).reverse().concat( + pieces['b'].splice(0,5).reverse()); + break; + } + // Reserve 4 and 5 which are pawns positions let positions = ArrayFun.range(10).filter(i => i != 4 && i != 5); diff --git a/client/src/variants/Shatranj.js b/client/src/variants/Shatranj.js index bb00101e..ef6f65b5 100644 --- a/client/src/variants/Shatranj.js +++ b/client/src/variants/Shatranj.js @@ -21,8 +21,8 @@ export const VariantRules = class ShatranjRules extends ChessRules { ]; } - static GenRandInitFen() { - return ChessRules.GenRandInitFen().replace("w 1111 -", "w"); + static GenRandInitFen(randomness) { + return ChessRules.GenRandInitFen(randomness).replace("w 1111 -", "w"); } getPotentialPawnMoves([x, y]) { diff --git a/client/src/variants/Suction.js b/client/src/variants/Suction.js index 297eef75..0fc88ce4 100644 --- a/client/src/variants/Suction.js +++ b/client/src/variants/Suction.js @@ -189,9 +189,9 @@ export const VariantRules = class SuctionRules extends ChessRules { } } - static GenRandInitFen() { + static GenRandInitFen(randomness) { // Add empty cmove: - return ChessRules.GenRandInitFen() + " -"; + return ChessRules.GenRandInitFen(randomness) + " -"; } getFen() { diff --git a/client/src/variants/Threechecks.js b/client/src/variants/Threechecks.js index 78aaf1f8..c1d5c5a9 100644 --- a/client/src/variants/Threechecks.js +++ b/client/src/variants/Threechecks.js @@ -47,10 +47,10 @@ export const VariantRules = class ThreechecksRules extends ChessRules { return super.getCurrentScore(); } - static GenRandInitFen() { - const randFen = ChessRules.GenRandInitFen(); - // Add check flags (at 0) - return randFen.replace(" w 0 1111", " w 0 111100"); + static GenRandInitFen(randomness) { + return ChessRules.GenRandInitFen(randomness) + // Add check flags (at 0) + .replace(" w 0 1111", " w 0 111100"); } getFlagsFen() { diff --git a/client/src/variants/Upsidedown.js b/client/src/variants/Upsidedown.js index 538fb4a8..44dab7f0 100644 --- a/client/src/variants/Upsidedown.js +++ b/client/src/variants/Upsidedown.js @@ -20,9 +20,18 @@ export const VariantRules = class UpsidedownRules extends ChessRules { ); } - static GenRandInitFen() { + static GenRandInitFen(randomness) { + if (!randomness) randomness = 2; + if (randomness == 0) + return "RNBQKBNR/PPPPPPPP/8/8/8/8/pppppppp/rnbqkbnr w 0"; + let pieces = { w: new Array(8), b: new Array(8) }; for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + let positions = ArrayFun.range(8); let randIndex = randInt(8); diff --git a/client/src/variants/Wildebeest.js b/client/src/variants/Wildebeest.js index fa89cb21..565f6275 100644 --- a/client/src/variants/Wildebeest.js +++ b/client/src/variants/Wildebeest.js @@ -244,9 +244,18 @@ export const VariantRules = class WildebeestRules extends ChessRules { return 2; } - static GenRandInitFen() { + static GenRandInitFen(randomness) { + if (!randomness) randomness = 2; + if (randomness == 0) + return "rnccwkqbbnr/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/RNBBQKWCCNR w 0 1111 -"; + let pieces = { w: new Array(10), b: new Array(10) }; for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + let positions = ArrayFun.range(11); // Get random squares for bishops + camels (different colors) @@ -264,7 +273,7 @@ export const VariantRules = class WildebeestRules extends ChessRules { for (let idx of randIndexes.concat(randIndexes_tmp).sort((a, b) => { return b - a; })) { - //largest indices first + // Largest indices first positions.splice(idx, 1); } diff --git a/client/src/views/Analyse.vue b/client/src/views/Analyse.vue index 0a66ffc1..baf10364 100644 --- a/client/src/views/Analyse.vue +++ b/client/src/views/Analyse.vue @@ -45,7 +45,9 @@ export default { if (!routeFen) this.alertAndQuit("Missing FEN"); else { this.gameRef.fen = routeFen.replace(/_/g, " "); - this.initialize(); + // orientation is optional: taken from FEN if missing + const orientation = this.$route.query["side"]; + this.initialize(orientation); } }, methods: { @@ -58,7 +60,7 @@ export default { this.$router.replace(newUrl); }, 500); }, - initialize: async function() { + initialize: async function(orientation) { // Obtain VariantRules object await import("@/variants/" + this.gameRef.vname + ".js") .then((vModule) => { @@ -66,17 +68,17 @@ export default { if (!V.CanAnalyze) // Late check, in case the user tried to enter URL by hand this.alertAndQuit("Analysis disabled for this variant"); - else this.loadGame(); + else this.loadGame(orientation); }) .catch((err) => { this.alertAndQuit("Mispelled variant name", true); }); }, - loadGame: function() { + loadGame: function(orientation) { // NOTE: no need to set score (~unused) this.game.vname = this.gameRef.vname; this.game.fen = this.gameRef.fen; this.curFen = this.game.fen; this.adjustFenSize(); - this.game.mycolor = V.ParseFen(this.gameRef.fen).turn; + this.game.mycolor = orientation || V.ParseFen(this.gameRef.fen).turn; this.$set(this.game, "fenStart", this.gameRef.fen); }, // Triggered by "fenchange" emitted in BaseGame: diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index 61ced0ae..469d46b2 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -54,6 +54,12 @@ main v-model="newchallenge.cadence" placeholder="5+0, 1h+30s, 5d ..." ) + fieldset + label(for="selectRandomLevel") {{ st.tr["Randomness"] }} + select#selectRandomLevel(v-model="newchallenge.randomness") + option(value="0") {{ st.tr["Deterministic"] }} + option(value="1") {{ st.tr["Symmetric random"] }} + option(value="2") {{ st.tr["Asymmetric random"] }} fieldset(v-if="st.user.id > 0") label(for="selectPlayers") {{ st.tr["Play with?"] }} input#selectPlayers( @@ -178,6 +184,7 @@ export default { vid: parseInt(localStorage.getItem("vid")) || 0, to: "", //name of challenged player (if any) cadence: localStorage.getItem("cadence") || "", + randomness: parseInt(localStorage.getItem("randomness")) || 2, // VariantRules object, stored to not interfere with // diagrams of targetted challenges: V: null, @@ -541,6 +548,7 @@ export default { id: c.id, from: this.st.user.sid, to: c.to, + randomness: c.randomness, fen: c.fen, vid: c.vid, cadence: c.cadence, @@ -561,6 +569,7 @@ export default { ) { let newChall = Object.assign({}, chall); newChall.type = this.classifyObject(chall); + newChall.randomness = chall.randomness; newChall.added = Date.now(); let fromValues = Object.assign({}, this.people[chall.from]); delete fromValues["pages"]; //irrelevant in this context @@ -719,6 +728,8 @@ export default { } // NOTE: "from" information is not required here let chall = Object.assign({}, this.newchallenge); + delete chall["V"]; + delete chall["diag"]; const finishAddChallenge = cid => { chall.id = cid || "c" + getRandString(); // Remove old challenge if any (only one at a time of a given type): @@ -753,6 +764,7 @@ export default { // Remember cadence + vid for quicker further challenges: localStorage.setItem("cadence", chall.cadence); localStorage.setItem("vid", chall.vid); + localStorage.setItem("randomness", chall.randomness); document.getElementById("modalNewgame").checked = false; // Show the challenge if not on current display if ( @@ -841,7 +853,7 @@ export default { // These game informations will be shared let gameInfo = { id: getRandString(), - fen: c.fen || V.GenRandInitFen(), + fen: c.fen || V.GenRandInitFen(c.randomness), // White player index 0, black player index 1: players: c.mycolor ? (c.mycolor == "w" ? [c.seat, c.from] : [c.from, c.seat]) diff --git a/server/db/create.sql b/server/db/create.sql index c05c87ac..e14e9a0f 100644 --- a/server/db/create.sql +++ b/server/db/create.sql @@ -43,6 +43,7 @@ create table Challenges ( uid integer, target integer, vid integer, + randomness integer, fen varchar, cadence varchar, foreign key (uid) references Users(id), diff --git a/server/models/Challenge.js b/server/models/Challenge.js index b7c20b88..243da709 100644 --- a/server/models/Challenge.js +++ b/server/models/Challenge.js @@ -8,8 +8,9 @@ const UserModel = require("./User"); * uid: user id (int) * target: recipient id (optional) * vid: variant id (int) + * randomness: integer in 0..2 * fen: varchar (optional) - * cadence: string (3m+2s, 7d+1d ...) + * cadence: string (3m+2s, 7d ...) */ const ChallengeModel = @@ -19,6 +20,7 @@ const ChallengeModel = return ( c.vid.toString().match(/^[0-9]+$/) && c.cadence.match(/^[0-9dhms +]+$/) && + c.randomness.toString().match(/^[0-2]$/) && c.fen.match(/^[a-zA-Z0-9, /-]*$/) && (!c.to || UserModel.checkNameEmail({name: c.to})) ); @@ -29,10 +31,11 @@ const ChallengeModel = db.serialize(function() { const query = "INSERT INTO Challenges " + - "(added, uid, " + (!!c.to ? "target, " : "") + "vid, fen, cadence) " + - "VALUES " + - "(" + Date.now() + "," + c.uid + "," + (!!c.to ? c.to + "," : "") + - c.vid + ",'" + c.fen + "','" + c.cadence + "')"; + "(added, uid, " + (!!c.to ? "target, " : "") + + "vid, randomness, fen, cadence) " + + "VALUES " + + "(" + Date.now() + "," + c.uid + "," + (!!c.to ? c.to + "," : "") + + c.vid + "," + c.randomness + ",'" + c.fen + "','" + c.cadence + "')"; db.run(query, function(err) { cb(err, {cid: this.lastID}); }); diff --git a/server/routes/challenges.js b/server/routes/challenges.js index efc69701..aee6fd2a 100644 --- a/server/routes/challenges.js +++ b/server/routes/challenges.js @@ -11,6 +11,7 @@ router.post("/challenges", access.logged, access.ajax, (req,res) => { { fen: req.body.chall.fen, cadence: req.body.chall.cadence, + randomness: req.body.chall.randomness, vid: req.body.chall.vid, uid: req.userId, to: req.body.chall.to, //string: user name (may be empty) -- 2.44.0