PizzaKings https://www.chessvariants.com/unequal.dir/pizza-kings.html
https://en.m.wikipedia.org/wiki/Chess_with_different_armies#Pizza_Kings%5B11%5D_(John_Lawson)
+Coin Chess
+https://msoworld.com/product/chess-variants/
+Background: Unknown inventor
+Rules:
+Normal rules apply except for the introduction of a coin (or counter)
+Black starts by placing the coin on any unoccupied square. Play then continues with players alternating turns as in normal chess.
+On their turn, a player makes any legal move but may not move onto the square where the coin has been placed. The player may move over (but not onto), the coin square.
+The player ends their turn by leaving the coin where it stands or moving the coin to a different unoccupied square.
+The coin can never be placed on an occupied square, and therefore cannot be used to protect a piece from capture
+A player wins by checkmating the opponent. Note that the coin can be used to remove escape squares from the king.
+
https://www.chessvariants.com/other.dir/nemoroth.html :-)
version="1.1"
id="svg12"
sodipodi:docname="wl.svg"
- inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07, custom)">
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
<metadata
id="metadata18">
<rdf:RDF>
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
- inkscape:window-width="1920"
+ inkscape:window-width="960"
inkscape:window-height="1060"
id="namedview14"
showgrid="false"
inkscape:zoom="1.7246094"
inkscape:cx="256"
- inkscape:cy="255.42016"
+ inkscape:cy="254.84032"
inkscape:window-x="0"
inkscape:window-y="20"
inkscape:window-maximized="0"
- inkscape:current-layer="svg12" />
+ inkscape:current-layer="svg12"
+ inkscape:document-rotation="0" />
<g
id="010---Frog"
transform="matrix(0.8,0,0,0.8,6.0000003,6)">
d="m 337.81831,445.85023 c -10.4388,-2.80716 -18.34008,-11.95076 -19.75585,-22.86208 -0.3099,-2.38844 -0.57152,-7.14629 -0.58136,-10.573 l -0.0179,-6.23038 2.94262,-3.62692 c 3.89557,-4.80148 8.76783,-12.99552 11.31926,-19.03639 3.94316,-9.33604 6.61242,-22.77207 6.61242,-33.28449 0,-3.51978 0.11123,-3.78972 3.5586,-8.63619 5.97066,-8.39384 12.04985,-15.26538 22.90012,-25.88492 18.58016,-18.18504 33.22365,-27.66968 49.66778,-32.16997 2.66295,-0.72877 3.59096,-1.30322 4.49161,-2.78035 2.51574,-4.12599 0.70172,-9.36723 -3.75061,-10.83664 -4.93204,-1.62772 -21.05317,4.81069 -34.44901,13.75814 -9.69646,6.47653 -16.23253,12.10004 -29.43403,25.32449 l -12.81849,12.84076 -0.37277,-7.36534 c -0.20502,-4.05093 -0.51019,-7.72345 -0.67815,-8.16114 -0.67475,-1.75837 15.29792,-17.9046 26.1136,-26.39736 18.48353,-14.51375 38.21698,-23.34861 52.15124,-23.34861 6.54713,0 10.13163,1.11515 13.08827,4.07179 2.89575,2.89576 4.15016,6.83571 4.13303,12.98141 -0.0522,18.7298 -14.70341,45.11564 -38.04471,68.51594 -9.49806,9.52206 -16.97344,15.60664 -28.544,23.23334 -8.35929,5.51 -10.87871,7.93729 -12.7369,12.2711 -4.26923,9.95703 0.0894,22.23736 9.5458,26.89528 l 2.72108,1.34031 37.97962,0.28993 c 36.08008,0.27541 38.06085,0.34497 39.60381,1.39075 3.07868,2.08662 4.02373,5.39369 2.42488,8.48553 -1.82084,3.52113 -1.18172,3.46007 -36.22011,3.46007 -35.03251,0 -35.03094,-1.5e-4 -36.79159,3.40457 -2.21954,4.29214 -0.22935,9.26049 4.20261,10.49143 1.27878,0.35517 10.71904,0.59487 23.5043,0.5968 l 21.36715,0.003 1.93729,1.93728 c 2.46615,2.46616 2.95099,4.36523 1.75687,6.88163 -1.96428,4.13942 0.28881,3.94194 -44.48849,3.89944 -33.22344,-0.0315 -40.75306,-0.18432 -43.33798,-0.87945 z"
id="path26"
transform="scale(0.1171875)" />
+ <path
+ style="fill:#ffffff;stroke:#000000;stroke-width:0.579841"
+ d="m 194.60655,118.19666 c -2.41356,-1.22724 -5.07782,-4.80585 -5.93454,-7.97119 -1.03527,-3.82506 -0.3344,-9.24725 1.59667,-12.35241 2.34703,-3.774029 4.34459,-5.098424 7.68987,-5.098424 3.50397,0 6.34607,2.039401 8.26069,5.927609 1.92065,3.900445 1.91919,10.324345 -0.003,14.237645 -2.51645,5.12252 -7.46596,7.36366 -11.60946,5.25677 z"
+ id="path16"
+ transform="scale(0.1171875)" />
+ <path
+ style="fill:#ffffff;stroke:#000000;stroke-width:0.579841"
+ d="m 310.57484,118.19666 c -5.83545,-2.96721 -8.15717,-12.49955 -4.75579,-19.525881 1.86272,-3.84785 4.71914,-5.896143 8.2224,-5.896143 3.37415,0 5.34355,1.325238 7.77055,5.228919 1.58018,2.541625 1.73953,3.253555 1.73953,7.772045 0,4.38385 -0.18949,5.29646 -1.5968,7.69048 -2.88247,4.90343 -7.3707,6.76917 -11.37989,4.73058 z"
+ id="path18"
+ transform="scale(0.1171875)" />
</svg>
a.menuitem(href="https://github.com/yagu0/vchess")
span {{ st.tr["Code"] }}
img(src="/images/icons/github.svg")
- //a.menuitem(href="https://www.facebook.com/Variants-Chess-Club-112565840437886")
- img(src="/images/icons/facebook.svg")
- //a.menuitem(href="https://twitter.com/VchessC")
- img(src="/images/icons/twitter.svg")
</template>
<script>
//////////////
// MISC UTILS
+ static get Options() {
+ return {
+ select: [
+ {
+ label: "Randomness",
+ variable: "randomness",
+ defaut: 2,
+ options: [
+ { label: "Deterministic", value: 0 },
+ { label: "Symmetric random", value: 1 },
+ { label: "Asymmetric random", value: 2 }
+ ]
+ }
+ ],
+ check: []
+ };
+ }
+
+ static AbbreviateOptions(opts) {
+ return "";
+ // Randomness is a special option: (TODO?)
+ //return "R" + opts.randomness;
+ }
+
// Some variants don't have flags:
static get HasFlags() {
return true;
// FEN UTILS
// Setup the initial random (asymmetric) position
- static GenRandInitFen(randomness) {
- if (randomness == 0)
+ static GenRandInitFen(options) {
+ if (!options.randomness || options.randomness == 0)
// Deterministic:
return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 ahah -";
let flags = "";
// Shuffle pieces on first (and last rank if randomness == 2)
for (let c of ["w", "b"]) {
- if (c == 'b' && randomness == 1) {
+ if (c == 'b' && options.randomness == 1) {
pieces['b'] = pieces['w'];
flags += flags;
break;
th {{ st.tr["Variant"] }}
th {{ st.tr["With"] }}
th {{ st.tr["Cadence"] }}
- th {{ st.tr["Random?"] }}
+ th {{ st.tr["Options"] }}
tbody
tr(
v-for="c in sortedChallenges"
td {{ c.vname }}
td {{ withWho(c) }}
td {{ c.cadence }}
- td(:class="getRandomnessClass(c)")
+ td(:class="getRandomnessClass(c)") {{ c.options.abridged }}
p(v-else)
| {{ st.tr["No challenges found :( Click on 'New game'!"] }}
</template>
return c.from.name || "@nonymous";
},
getRandomnessClass: function(c) {
+ if (!c.options.randomness) return {};
return {
- ["random-" + c.randomness]: true
+ ["random-" + c.options.randomness]: true
};
}
}
tr > td:last-child
&.random-0
- background-color: #FF5733
+ background-color: #FEAF9E
&.random-1
- background-color: #2B63B4
+ background-color: #9EB2FE
&.random-2
- background-color: #33B42B
+ background-color: #A5FE9E
</style>
};
},
methods: {
- launchGame: function(game) {
+ launchGame: function(game, options) {
this.compWorker.postMessage(["scripts", this.gameInfo.vname]);
if (!game) {
game = {
vname: this.gameInfo.vname,
- fenStart: V.GenRandInitFen(this.st.settings.randomness),
+ fenStart: V.GenRandInitFen(options),
moves: []
};
game.fen = game.fenStart;
@click="$emit('show-game',g)"
:class="{'my-turn': !!g.myTurn}"
)
- td {{ g.vname }}
+ td {{ g.vname + '-' + g.options.abridged }}
td {{ player_s(g) }}
td(v-if="showCadence") {{ g.cadence }}
td(
type="checkbox"
v-model="st.settings.gotonext"
)
- fieldset
- label(for="setRandomness") {{ st.tr["Randomness"] }}
- select#setRandomness(v-model="st.settings.randomness")
- option(value="0") {{ st.tr["Deterministic"] }}
- option(value="1") {{ st.tr["Symmetric random"] }}
- option(value="2") {{ st.tr["Asymmetric random"] }}
</template>
<script>
const propName = event.target.id
.substr(3)
.replace(/^\w/, c => c.toLowerCase());
- const value = ["bcolor","randomness"].includes(propName)
+ const value = propName == "bcolor"
? event.target.value
: event.target.checked;
store.updateSetting(propName, value);
modalBoxes.forEach(m => {
if (
m.checked &&
- !["modalAccept", "modalConfirm", "modalChat", "modalPeople"]
- .includes(m.id)
+ !["Accept", "Confirm", "Chat", "People"]
+ .includes(m.id.substr(5)) //modalThing --> Thing
) {
m.checked = false;
}
hints: getItemDefaultTrue("hints"),
highlight: getItemDefaultTrue("highlight"),
gotonext: getItemDefaultTrue("gotonext"),
- randomness: parseInt(localStorage.getItem("randomness"), 10)
};
- if (isNaN(this.state.settings.randomness))
- // Default: random asymmetric
- this.state.settings.randomness = 2;
const supportedLangs = ["en", "es", "fr"];
const navLanguage = navigator.language.substr(0, 2);
this.state.lang =
a(href="https://mindsports.nl/index.php") mindsports.nl
a(href="https://www.jocly.com/#/games") jocly.com
a(href="http://www.iggamecenter.com/") iggamecenter.com
+ a(href="https://boardspace.net/english/index.shtml") boardspace.net
a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
a(href="https://schemingmind.com/") schemingmind.com
+ a(href="https://boardgamearena.com/") boardgamearena.com
a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
a(href="https://brainking.com/") brainking.com
a(href="https://mindsports.nl/index.php") mindsports.nl
a(href="https://www.jocly.com/#/games") jocly.com
a(href="http://www.iggamecenter.com/") iggamecenter.com
+ a(href="https://boardspace.net/english/index.shtml") boardspace.net
a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
a(href="https://schemingmind.com/") schemingmind.com
+ a(href="https://boardgamearena.com/") boardgamearena.com
a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
a(href="https://brainking.com/") brainking.com
a(href="https://mindsports.nl/index.php") mindsports.nl
a(href="https://www.jocly.com/#/games") jocly.com
a(href="http://www.iggamecenter.com/") iggamecenter.com
+ a(href="https://boardspace.net/english/index.shtml") boardspace.net
a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
a(href="https://schemingmind.com/") schemingmind.com
+ a(href="https://boardgamearena.com/") boardgamearena.com
a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
a(href="https://brainking.com/") brainking.com
"No more problems": "No more problems",
"No subject. Send anyway?": "No subject. Send anyway?",
"Notifications by email": "Notifications by email",
- Number: "Number",
+ "Number": "Number",
Observe: "Observe",
"Offer draw?": "Offer draw?",
"Opponent action": "Opponent action",
Previous_p: "Previous",
"Processing... Please wait": "Processing... Please wait",
Problems: "Problems",
- "Random?": "Random?",
- "Randomness": "Randomness",
Refuse: "Refuse",
Register: "Register",
"Registration complete! Please check your emails now": "Registration complete! Please check your emails now",
"Lose all pieces": "Lose all pieces",
"Malagasy Draughts": "Malagasy Draughts",
"Mandatory captures": "Mandatory captures",
- "Mate any piece (v1)": "Mate any piece (v1)",
- "Mate any piece (v2)": "Mate any piece (v2)",
+ "Mate any piece": "Mate any piece",
"Mate the knight (v1)": "Mate the knight (v1)",
"Mate the knight (v2)": "Mate the knight (v2)",
"Meet the Mammoth": "Meet the Mammoth",
"Run forward": "Run forward",
"Score a goal": "Score a goal",
"Seirawan-Harper Chess": "Seirawan-Harper Chess",
- "Shared pieces (v1)": "Shared pieces (v1)",
- "Shared pieces (v2)": "Shared pieces (v2)",
+ "Shared pieces": "Shared pieces",
"Shogi 5 x 5": "Shogi 5 x 5",
"Shoot pieces": "Shoot pieces",
"Spartan versus Persians": "Spartan versus Persians",
// Variants by categories:
"What is a chess variant?": "What is a chess variant?",
"Why play chess variants?": "Why play chess variants?",
- "chess_v": ": to play under standard rules, with a random (or not) symmetric (or not) initial position.",
+ "chess960_v": ": to play under standard rules, with a random symmetric (or not) initial position.",
"vt0": "Simplified games to learn chess",
"vg0": "Variants with very few different pieces, and a simplified goal.",
"vt1": "Forced captures",
"vg30": "Pieces can temporarily borrow powers from others.",
"vt31": "Miscelleanous",
"vg31": "These variants are not classified yet, generally because they are the only one of their kind on this website.",
+
+ // Variants' options
+ "Options": "Options",
+ "Randomness": "Randomness",
+ "With switch": "With switch",
};
"No more problems": "No mas problemas",
"No subject. Send anyway?": "Sin asunto. ¿Enviar sin embargo?",
"Notifications by email": "Notificaciones por email",
- Number: "Número",
+ "Number": "Número",
"Offer draw?": "¿Ofrecer tablas?",
Observe: "Observar",
"Opponent action": "Acción del adversario",
Previous_n: "Anterior",
"Processing... Please wait": "Procesando... por favor espere",
Problems: "Problemas",
- "Random?": "Aleatorio?",
- "Randomness": "Grado de azar",
Refuse: "Rechazar",
Register: "Inscribirse",
"Registration complete! Please check your emails now": "¡Registro completo! Revise sus correos electrónicos ahora",
"Lose all pieces": "Perder todas las piezas",
"Malagasy Draughts": "Damas malgaches",
"Mandatory captures": "Capturas obligatorias",
- "Mate any piece (v1)": "Matar cualquier pieza (v1)",
- "Mate any piece (v2)": "Matar cualquier pieza (v2)",
+ "Mate any piece": "Matar cualquier pieza",
"Mate the knight (v1)": "Matar el caballo (v1)",
"Mate the knight (v2)": "Matar el caballo (v2)",
"Meet the Mammoth": "Conoce al Mamut",
"Run forward": "Correr hacia adelante",
"Score a goal": "Marcar una meta",
"Seirawan-Harper Chess": "Ajedrez Seirawan-Harper",
- "Shared pieces (v1)": "Piezas compartidas (v1)",
- "Shared pieces (v2)": "Piezas compartidas (v2)",
+ "Shared pieces": "Piezas compartidas",
"Shogi 5 x 5": "Shogi 5 x 5",
"Shoot pieces": "Tirar de las piezas",
"Spartan versus Persians": "Espartanos contra Persas",
// Variants by categories:
"What is a chess variant?": "¿Qué es una variante?",
"Why play chess variants?": "¿Por qué jugar las variantes?",
- "chess_v": ": para jugar con reglas estándar, desde una posición inicial aleatorio (o no) simétrico (o no).",
+ "chess960_v": ": para jugar con reglas estándar, desde una posición inicial aleatorio simétrico (o no).",
"vt0": "Juegos simplificados para aprender ajedrez",
"vg0": "Variantes con muy pocas piezas diferentes y un propósito simplificado.",
"vt1": "Capturas obligatorias",
"vg30": "Las piezas pueden temporalmente tomar prestados poderes de otras",
"vt31": "Varios",
"vg31": "Estas variantes aún no están clasificadas, en general porque son el único representante de su tipo en este sitio.",
+
+ // Variants' options
+ "Options": "Optiones",
+ "Randomness": "Grado de azar",
+ "With switch": "Con switch",
};
"No more problems": "Plus de problèmes",
"No subject. Send anyway?": "Pas de sujet. Envoyer quand-même ??",
"Notifications by email": "Notifications par email",
- Number: "Numéro",
+ "Number": "Numéro",
"Offer draw?": "Proposer nulle ?",
Observe: "Observer",
"Opponent action": "Action de l'adversaire",
Previous_n: "Précédente",
"Processing... Please wait": "Traitement en cours... Attendez SVP",
Problems: "Problèmes",
- "Random?": "Aléatoire?",
- "Randomness": "Degré d'aléa",
Refuse: "Refuser",
Register: "S'inscrire",
"Registration complete! Please check your emails now": "Enregistrement terminé ! Allez voir vos emails maintenant",
"Lose all pieces": "Perdez toutes les pièces",
"Malagasy Draughts": "Dames malgaches",
"Mandatory captures": "Captures obligatoires",
- "Mate any piece (v1)": "Matez n'importe quelle pièce (v1)",
- "Mate any piece (v2)": "Matez n'importe quelle pièce (v2)",
+ "Mate any piece": "Matez n'importe quelle pièce",
"Mate the knight (v1)": "Matez le cavalier (v1)",
"Mate the knight (v2)": "Matez le cavalier (v2)",
"Meet the Mammoth": "Rencontrez le Mammouth",
"Run forward": "Courir vers l'avant",
"Score a goal": "Marquer un but",
"Seirawan-Harper Chess": "Échecs Seirawan-Harper",
- "Shared pieces (v1)": "Pièces partagées (v1)",
- "Shared pieces (v2)": "Pièces partagées (v2)",
+ "Shared pieces": "Pièces partagées",
"Shogi 5 x 5": "Shogi 5 x 5",
"Shoot pieces": "Tirez sur les pièces",
"Spartan versus Persians": "Spartiates contre Perses",
// Variants by categories:
"What is a chess variant?": "Qu'est-ce qu'une variante ?",
"Why play chess variants?": "Pourquoi jouer aux variantes ?",
- "chess_v": " : pour jouer avec les règles standard, depuis une position initiale aléatoire (ou non) symétrique (ou non).",
+ "chess960_v": " : pour jouer avec les règles standard, depuis une position initiale aléatoire symétrique (ou non).",
"vt0": "Jeux simplifiés pour apprendre les échecs",
"vg0": "Variantes avec très peu de pièces différentes, et un but simplifié.",
"vt1": "Captures obligatoires",
"vg30": "Les pièces peuvent temporairement emprunter des pouvoir aux autres",
"vt31": "Divers",
"vg31": "Ces variantes ne sont pas encore classées, en général car elles sont l'unique représentant de leur type sur ce site.",
+
+ // Variants' options
+ "Options": "Options",
+ "Randomness": "Degré d'aléa",
+ "With switch": "Avec switch",
};
p.
Note about moves notation: since captures do not occur on final squares,
an "X" mark is appended to the move to indicate a capture.
- To keep notation short the potential list of captured squares is not written.
+ To keep notation short the list of captured squares is not written.
h3 "Suicide" note
p.
Nota sobre la notación de las jugadas: ya que las capturas no se realizan
en la casilla de llegada, se agrega una marca "X" al movimiento para indicar
- la captura. Para evitar las notaciones demasiado largas, la lista potencial
+ la captura. Para evitar las notaciones demasiado largas, la lista
de casillas de captura no está escrita.
h3 Nota "suicidio"
p.
Note sur la notation des coups: puisque les captures ne s'effectuent pas
sur la case d'arrivée, une marque "X" est ajoutée au coup pour indiquer la
- capture. Afin d'éviter les notations à rallonge la liste potentielle des
+ capture. Afin d'éviter les notations à rallonge la liste des
cases de capture n'est pas écrite.
h3 Note "suicide"
+++ /dev/null
-p.boxed
- | An attacked piece is taken if the capture cannot
- | be prevented with non-capturing moves.
-
-p.
- This is the Allmate1 variant, with a weaker mating condition:
- capturing moves to escape from checkmate are not considered.
- (Mate-)Capturing is thus easier: on the next diagram, 1.Qe6 captures
- the d7, e7 and f7 pawns.
-
-figure.diagram-container
- .diagram.diag12
- | fen:4k3/pppppppp/8/8/4Q3/8/PPP2PPP/4K3:
- .diagram.diag22
- | fen:4k3/ppp3pp/4Q3/8/8/8/PPP2PPP/4K3:
- figcaption Before and after 1.Qe6X
-
-p Since captures are much easier, they don't cascade as in Allmate1.
-
-h3 Source
-
-p See Allmate1 variant.
+++ /dev/null
-p.boxed
- | Se captura una pieza atacada si no se puede evitar
- | la toma por movimientos sin captura.
-
-p.
- Esta es la variante Allmate1, con una condición de mate más
- débil: no se consideran las capturas para escapar de un jaque mate.
- Por lo tanto, las (mate-)capturas son más fáciles: en el siguiente diagrama,
- 1.Qe6 toma los peones d7, e7 y f7.
-
-figure.diagram-container
- .diagram.diag12
- | fen:4K3/pppppppp/8/8/4Q3/8/PPP2PPP/4K3:
- .diagram.diag22
- | fen:4K3/ppp3pp/4Q3/8/8/8/PPP2PPP/4K3:
- figcaption Antes y después 1.Qe6X
-
-p.
- Como las capturas son mucho más fáciles, no tienen lugar
- en cascada como en Allmate1.
-
-h3 Fuente
-
-p Ver la variante Allmate1.
+++ /dev/null
-p.boxed
- | Une pièce attaquée est capturée si la prise ne peut être
- | empêchée par des coups non capturants.
-
-p.
- C'est la variante Allmate1, avec une condition de mat plus
- faible : les coups capturants pour échapper à un mat ne sont pas considérés.
- Les (mat-)captures sont donc plus faciles : sur le diagramme suivant,
- 1.Qe6 prend les pions d7, e7 et f7.
-
-figure.diagram-container
- .diagram.diag12
- | fen:4k3/pppppppp/8/8/4Q3/8/PPP2PPP/4K3:
- .diagram.diag22
- | fen:4k3/ppp3pp/4Q3/8/8/8/PPP2PPP/4K3:
- figcaption Avant et après 1.Qe6X
-
-p.
- Puisque les captures sont beaucoup plus faciles, elles ne s'effectuent pas
- en cascade comme dans Allmate1.
-
-h3 Source
-
-p Voir la variante Allmate1.
h2.stageDelimiter Stage 2
+p.italic.
+ This stage can be disabled by unselecting "With switch" at game creation.
+
p.
During the game one of the two players can decide to take control of the
checkered pieces.
h2.stageDelimiter Fase 2
+p.italic.
+ Si anula la selección de "Con switch" al crear la partida, entonces
+ el juego permanecerá en la fase 1.
+
p.
Durante el juego, uno de los dos jugadores puede decidir tomar
las piezas a cuadros. Estos luego se vuelven autónomos
h2.stageDelimiter Phase 2
+p.italic.
+ Si vous désélectionnez "Avec switch" lors de la création de la partie,
+ alors le jeu restera en phase 1.
+
p.
Au cours de la partie l'un des deux joueurs peut décider de prendre le
contrôle des pièces échiquetées. Celles-ci deviennent alors autonomes
+++ /dev/null
-p.boxed
- | The capture of an enemy piece produces a new "checkered" piece belonging
- | to both players.
-
-p
- | This is the
- a(href="/#/variants/Checkered1") Checkered1 variant
- | , without the stage 2.
- | Probably more drawish, but also quite different.
- | I think both are interesting.
+++ /dev/null
-p.boxed
- | La captura de una pieza enemiga da lugar al nacimiento de una pieza
- | "a cuadros", que pertenece a ambos campos.
-
-p
- | Esta la
- a(href="/#/variants/Checkered1") variante Checkered1
- | , sin fase 2.
- | Probablemente más canceladoras, pero también bastante diferente.
- | Creo que ambos tienen interés.
+++ /dev/null
-p.boxed
- | La capture d'une pièce ennemie donne lieu à la naissance d'une pièce
- | "échiquetée", qui appartient aux deux camps.
-
-p
- | C'est la
- a(href="/#/variants/Checkered1") variante Checkered1
- | , sans la phase 2.
- | Sans doute plus annulant, mais aussi assez différente.
- | Je pense que les deux ont un intérêt.
p.boxed
- | Orthodox rules (with potentially shuffled starting position).
+ | Orthodox rules with shuffled starting position.
p.
Chess is played between two players, one moving the white pieces and the
p.boxed
- | Juego ortodoxo (con una posición inicial potencialmente aleatoria).
+ | Juego ortodoxo con una posición inicial aleatoria.
p.
El ajedrez es un juego entre dos jugadores, uno que mueve las piezas blancas
p.boxed
- | Jeu orthodoxe (avec une position de départ potentiellement aléatoire).
+ | Jeu orthodoxe avec une position de départ aléatoire.
p.
Les échecs sont un jeu entre deux joueurs, l'un déplaçant les pièces blanches
import { ChessRules, PiPo, Move } from "@/base_rules";
-export class Allmate1Rules extends ChessRules {
+export class AllmateRules extends ChessRules {
static get HasEnpassant() {
return false;
+++ /dev/null
-import { ChessRules, PiPo, Move } from "@/base_rules";
-
-export class Allmate2Rules extends ChessRules {
-
- static get HasEnpassant() {
- return false;
- }
-
- getCheckSquares() {
- // No notion of check
- return [];
- }
-
- static GenRandInitFen(randomness) {
- return ChessRules.GenRandInitFen(randomness).slice(0, -2);
- }
-
- getPotentialMovesFrom([x, y]) {
- let moves = super.getPotentialMovesFrom([x, y]);
- // Remove standard captures (without removing castling):
- moves = moves.filter(m => {
- return m.vanish.length == 1 || m.appear.length == 2;
- });
-
- // Augment moves with "mate-captures":
- // TODO: this is coded in a highly inefficient way...
- const color = this.turn;
- const oppCol = V.GetOppCol(this.turn);
- moves.forEach(m => {
- this.play(m);
-
- // 1) What is attacked?
- let attacked = {};
- for (let i=0; i<V.size.x; i++) {
- for (let j=0; j<V.size.y; j++) {
- if (this.getColor(i,j) == oppCol && this.isAttacked([i,j], color))
- attacked[i+"_"+j] = [i,j];
- }
- }
-
- // 2) Among attacked pieces, which cannot escape capture?
- // --> without (normal-)capturing: difference with Allmate1 variant
- // Avoid "oppMoves = this.getAllValidMoves();" => infinite recursion
- outerLoop: for (let i=0; i<V.size.x; i++) {
- for (let j=0; j<V.size.y; j++) {
- if (this.getColor(i,j) == oppCol) {
- let oppMoves = [];
- switch (this.getPiece(i, j)) {
- case V.PAWN:
- oppMoves = this.getPotentialPawnMoves([i, j]);
- break;
- case V.ROOK:
- oppMoves = this.getPotentialRookMoves([i, j]);
- break;
- case V.KNIGHT:
- oppMoves = this.getPotentialKnightMoves([i, j]);
- break;
- case V.BISHOP:
- oppMoves = this.getPotentialBishopMoves([i, j]);
- break;
- case V.QUEEN:
- oppMoves = this.getPotentialQueenMoves([i, j]);
- break;
- case V.KING:
- // Do not allow castling to escape from check
- oppMoves = super.getSlideNJumpMoves(
- [i, j],
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
- "oneStep"
- );
- break;
- }
- for (let om of oppMoves) {
- if (om.vanish.length == 2)
- // Skip captures: forbidden in this mode
- continue;
- V.PlayOnBoard(this.board, om);
- Object.values(attacked).forEach(sq => {
- const origSq = [sq[0], sq[1]];
- if (om.start.x == sq[0] && om.start.y == sq[1])
- // Piece moved:
- sq = [om.appear[0].x, om.appear[0].y];
- if (!this.isAttacked(sq, color))
- delete attacked[origSq[0]+"_"+origSq[1]];
- });
- V.UndoOnBoard(this.board, om);
- if (Object.keys(attacked).length == 0)
- // No need to explore more moves
- break outerLoop;
- }
- }
- }
- }
- this.undo(m);
-
- // 3) Add mate-captures:
- Object.values(attacked).forEach(sq => {
- m.vanish.push(new PiPo({
- x: sq[0],
- y: sq[1],
- c: oppCol,
- p: this.getPiece(sq[0], sq[1])
- }));
- });
- });
-
- return moves;
- }
-
- // No "under check" conditions in castling
- getCastleMoves(sq) {
- return super.getCastleMoves(sq, null, "castleInCheck");
- }
-
- // TODO: allow pieces to "commit suicide"? (Currently yes except king)
- filterValid(moves) {
- // Remove moves which let the king mate-captured:
- if (moves.length == 0) return [];
- const color = this.turn;
- const oppCol = V.GetOppCol(color);
- return moves.filter(m => {
- let res = true;
- this.play(m);
- if (this.underCheck(color)) {
- res = false;
- const attacked = this.kingPos[color];
- // Try to find a move to escape check
- // TODO: very inefficient method.
- outerLoop: for (let i=0; i<V.size.x; i++) {
- for (let j=0; j<V.size.y; j++) {
- if (this.getColor(i,j) == color) {
- let emoves = [];
- // Artficial turn change to "play twice":
- this.turn = color;
- switch (this.getPiece(i, j)) {
- case V.PAWN:
- emoves = this.getPotentialPawnMoves([i, j]);
- break;
- case V.ROOK:
- emoves = this.getPotentialRookMoves([i, j]);
- break;
- case V.KNIGHT:
- emoves = this.getPotentialKnightMoves([i, j]);
- break;
- case V.BISHOP:
- emoves = this.getPotentialBishopMoves([i, j]);
- break;
- case V.QUEEN:
- emoves = this.getPotentialQueenMoves([i, j]);
- break;
- case V.KING:
- emoves = this.getPotentialKingMoves([i, j]);
- break;
- }
- this.turn = oppCol;
- for (let em of emoves) {
- V.PlayOnBoard(this.board, em);
- let sq = attacked;
- if (em.start.x == attacked[0] && em.start.y == attacked[1])
- // King moved:
- sq = [em.appear[0].x, em.appear[0].y];
- if (!this.isAttacked(sq, oppCol))
- res = true;
- V.UndoOnBoard(this.board, em);
- if (res)
- // No need to explore more moves
- break outerLoop;
- }
- }
- }
- }
- }
- this.undo(m);
- return res;
- });
- }
-
- postPlay(move) {
- super.postPlay(move);
- if (move.vanish.length >= 2 && move.appear.length == 1) {
- for (let i = 1; i<move.vanish.length; i++) {
- const v = move.vanish[i];
- // Did opponent king disappeared?
- if (v.p == V.KING)
- this.kingPos[this.turn] = [-1, -1];
- // Or maybe a rook?
- else if (v.p == V.ROOK) {
- if (v.y < this.kingPos[v.c][1])
- this.castleFlags[v.c][0] = 8;
- else
- // v.y > this.kingPos[v.c][1]
- this.castleFlags[v.c][1] = 8;
- }
- }
- }
- }
-
- preUndo(move) {
- super.preUndo(move);
- const oppCol = this.turn;
- if (move.vanish.length >= 2 && move.appear.length == 1) {
- // Did opponent king disappeared?
- const psq = move.vanish.find(v => v.p == V.KING && v.c == oppCol)
- if (psq)
- this.kingPos[psq.c] = [psq.x, psq.y];
- }
- }
-
- getCurrentScore() {
- const color = this.turn;
- const kp = this.kingPos[color];
- if (kp[0] < 0)
- // King disappeared
- return color == "w" ? "0-1" : "1-0";
- if (this.atLeastOneMove()) return "*";
- // Kings still there, no moves:
- return "1/2";
- }
-
- static get SEARCH_DEPTH() {
- return 1;
- }
-
- getNotation(move) {
- let notation = super.getNotation(move);
- // Add a capture mark (not describing what is captured...):
- if (move.vanish.length > 1 && move.appear.length == 1) {
- if (!!(notation.match(/^[a-h]x/)))
- // Pawn capture: remove initial "b" in bxc4 for example
- notation = notation.substr(1);
- notation = notation.replace("x","") + "X";
- }
- return notation;
- }
-
-};
import { ChessRules, Move, PiPo } from "@/base_rules";
-export class Checkered1Rules extends ChessRules {
+export class CheckeredRules extends ChessRules {
static board2fen(b) {
const checkered_codes = {
return (b[0] == "c" ? "Checkered/" : "") + b;
}
+ static get Options() {
+ return Object.assign(
+ {},
+ ChessRules.Options,
+ {
+ check: [
+ {
+ label: "With switch",
+ defaut: true,
+ variable: "switch"
+ }
+ ]
+ }
+ );
+ }
+
+ static AbbreviateOptions(opts) {
+ return (!opts["switch"] ? "NS" : "");
+ }
+
setOtherVariables(fen) {
super.setOtherVariables(fen);
// Local stack of non-capturing checkered moves:
// Stage 1: as Checkered2. Stage 2: checkered pieces are autonomous
const stageInfo = V.ParseFen(fen).stage;
this.stage = parseInt(stageInfo[0], 10);
+ this.canSwitch = (this.stage == 1 && stageInfo[1] != '-');
this.sideCheckered = (this.stage == 2 ? stageInfo[1] : undefined);
}
if (fenParts.length != 7) return false;
if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/))
return false;
- if (!fenParts[6].match(/^[12][wb]?$/)) return false;
+ if (!fenParts[6].match(/^[12][wb-]?$/)) return false;
return true;
}
if (this.getPiece(x, y) == V.KING) {
// If at least one checkered piece, allow switching:
if (
- !noswitch &&
+ this.canSwitch && !noswitch &&
this.board.some(b => b.some(cell => cell[0] == 'c'))
) {
const oppCol = V.GetOppCol(color);
return evaluation;
}
- static GenRandInitFen(randomness) {
- // Add 16 pawns flags + empty cmove + stage == 1:
- return ChessRules.GenRandInitFen(randomness)
- .slice(0, -2) + "1111111111111111 - - 1";
+ static GenRandInitFen(options) {
+ const baseFen = ChessRules.GenRandInitFen(options.randomness);
+ return (
+ // Add 16 pawns flags + empty cmove + stage == 1:
+ baseFen.slice(0, -2) + "1111111111111111 - - 1" +
+ (!options["switch"] ? '-' : "")
+ );
}
static ParseFen(fen) {
}
getStageFen() {
- return (this.stage == 1 ? "1" : "2" + this.sideCheckered);
+ if (this.stage == 1) return "1" + (!this.canSwitch ? '-' : "");
+ // Stage == 2:
+ return "2" + this.sideCheckered;
}
getFen() {
+++ /dev/null
-import { ChessRules, Move, PiPo } from "@/base_rules";
-
-export class Checkered2Rules extends ChessRules {
-
- static board2fen(b) {
- const checkered_codes = {
- p: "s",
- q: "t",
- r: "u",
- b: "c",
- n: "o"
- };
- if (b[0] == "c") return checkered_codes[b[1]];
- return ChessRules.board2fen(b);
- }
-
- static fen2board(f) {
- // Tolerate upper-case versions of checkered pieces (why not?)
- const checkered_pieces = {
- s: "p",
- S: "p",
- t: "q",
- T: "q",
- u: "r",
- U: "r",
- c: "b",
- C: "b",
- o: "n",
- O: "n"
- };
- if (Object.keys(checkered_pieces).includes(f))
- return "c" + checkered_pieces[f];
- return ChessRules.fen2board(f);
- }
-
- static get PIECES() {
- return ChessRules.PIECES.concat(["s", "t", "u", "c", "o"]);
- }
-
- getPpath(b) {
- return (b[0] == "c" ? "Checkered/" : "") + b;
- }
-
- setOtherVariables(fen) {
- super.setOtherVariables(fen);
- // Local stack of non-capturing checkered moves:
- this.cmoves = [];
- const cmove = V.ParseFen(fen).cmove;
- if (cmove == "-") this.cmoves.push(null);
- else {
- this.cmoves.push({
- start: ChessRules.SquareToCoords(cmove.substr(0, 2)),
- end: ChessRules.SquareToCoords(cmove.substr(2))
- });
- }
- }
-
- static IsGoodFen(fen) {
- if (!ChessRules.IsGoodFen(fen)) return false;
- const fenParts = fen.split(" ");
- if (fenParts.length != 6) return false;
- if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/))
- return false;
- return true;
- }
-
- static IsGoodFlags(flags) {
- // 4 for castle + 16 for pawns
- return !!flags.match(/^[a-z]{4,4}[01]{16,16}$/);
- }
-
- setFlags(fenflags) {
- super.setFlags(fenflags); //castleFlags
- this.pawnFlags = {
- w: [...Array(8)], //pawns can move 2 squares?
- b: [...Array(8)]
- };
- const flags = fenflags.substr(4); //skip first 4 letters, for castle
- for (let c of ["w", "b"]) {
- for (let i = 0; i < 8; i++)
- this.pawnFlags[c][i] = flags.charAt((c == "w" ? 0 : 8) + i) == "1";
- }
- }
-
- aggregateFlags() {
- return [this.castleFlags, this.pawnFlags];
- }
-
- disaggregateFlags(flags) {
- this.castleFlags = flags[0];
- this.pawnFlags = flags[1];
- }
-
- getEpSquare(moveOrSquare) {
- if (typeof moveOrSquare !== "object" || moveOrSquare.appear[0].c != 'c')
- return super.getEpSquare(moveOrSquare);
- // Checkered move: no en-passant
- return undefined;
- }
-
- getCmove(move) {
- if (move.appear[0].c == "c" && move.vanish.length == 1)
- return { start: move.start, end: move.end };
- return null;
- }
-
- canTake([x1, y1], [x2, y2]) {
- const color1 = this.getColor(x1, y1);
- const color2 = this.getColor(x2, y2);
- // Checkered aren't captured
- return (
- color1 != color2 &&
- color2 != "c" &&
- (color1 != "c" || color2 != this.turn)
- );
- }
-
- // Post-processing: apply "checkerization" of standard moves
- getPotentialMovesFrom([x, y]) {
- let standardMoves = super.getPotentialMovesFrom([x, y]);
- const lastRank = this.turn == "w" ? 0 : 7;
- // King is treated differently: it never turn checkered
- if (this.getPiece(x, y) == V.KING) return standardMoves;
- let moves = [];
- standardMoves.forEach(m => {
- if (m.vanish[0].p == V.PAWN) {
- if (
- Math.abs(m.end.x - m.start.x) == 2 &&
- !this.pawnFlags[this.turn][m.start.y]
- ) {
- return; //skip forbidden 2-squares jumps
- }
- if (
- this.board[m.end.x][m.end.y] == V.EMPTY &&
- m.vanish.length == 2 &&
- this.getColor(m.start.x, m.start.y) == "c"
- ) {
- return; //checkered pawns cannot take en-passant
- }
- }
- if (m.vanish.length == 1)
- // No capture
- moves.push(m);
- else {
- // A capture occured (m.vanish.length == 2)
- m.appear[0].c = "c";
- moves.push(m);
- if (
- // Avoid promotions (already treated):
- m.appear[0].p != m.vanish[1].p &&
- (m.vanish[0].p != V.PAWN || m.end.x != lastRank)
- ) {
- // Add transformation into captured piece
- let m2 = JSON.parse(JSON.stringify(m));
- m2.appear[0].p = m.vanish[1].p;
- moves.push(m2);
- }
- }
- });
- return moves;
- }
-
- getPotentialPawnMoves([x, y]) {
- let moves = super.getPotentialPawnMoves([x, y]);
- // Post-process: set right color for checkered moves
- if (this.getColor(x, y) == 'c') {
- moves.forEach(m => {
- m.appear[0].c = 'c'; //may be done twice if capture
- m.vanish[0].c = 'c';
- });
- }
- return moves;
- }
-
- canIplay(side, [x, y]) {
- return side == this.turn && [side, "c"].includes(this.getColor(x, y));
- }
-
- // Does m2 un-do m1 ? (to disallow undoing checkered moves)
- oppositeMoves(m1, m2) {
- return (
- !!m1 &&
- m2.appear[0].c == "c" &&
- m2.appear.length == 1 &&
- m2.vanish.length == 1 &&
- m1.start.x == m2.end.x &&
- m1.end.x == m2.start.x &&
- m1.start.y == m2.end.y &&
- m1.end.y == m2.start.y
- );
- }
-
- filterValid(moves) {
- if (moves.length == 0) return [];
- const color = this.turn;
- const L = this.cmoves.length; //at least 1: init from FEN
- return moves.filter(m => {
- if (this.oppositeMoves(this.cmoves[L - 1], m)) return false;
- this.play(m);
- const res = !this.underCheck(color);
- this.undo(m);
- return res;
- });
- }
-
- getAllValidMoves() {
- const oppCol = V.GetOppCol(this.turn);
- let potentialMoves = [];
- for (let i = 0; i < V.size.x; i++) {
- for (let j = 0; j < V.size.y; j++) {
- // NOTE: just testing == color isn't enough because of checkered pieces
- if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
- Array.prototype.push.apply(
- potentialMoves,
- this.getPotentialMovesFrom([i, j])
- );
- }
- }
- }
- return this.filterValid(potentialMoves);
- }
-
- atLeastOneMove() {
- const oppCol = V.GetOppCol(this.turn);
- for (let i = 0; i < V.size.x; i++) {
- for (let j = 0; j < V.size.y; j++) {
- // NOTE: just testing == color isn't enough because of checkered pieces
- if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
- const moves = this.getPotentialMovesFrom([i, j]);
- if (moves.length > 0) {
- for (let k = 0; k < moves.length; k++) {
- if (this.filterValid([moves[k]]).length > 0) return true;
- }
- }
- }
- }
- }
- return false;
- }
-
- // colors: array, generally 'w' and 'c' or 'b' and 'c'
- isAttacked(sq, colors) {
- if (!Array.isArray(colors)) colors = [colors];
- return (
- this.isAttackedByPawn(sq, colors) ||
- this.isAttackedByRook(sq, colors) ||
- this.isAttackedByKnight(sq, colors) ||
- this.isAttackedByBishop(sq, colors) ||
- this.isAttackedByQueen(sq, colors) ||
- this.isAttackedByKing(sq, colors)
- );
- }
-
- isAttackedByPawn([x, y], colors) {
- for (let c of colors) {
- const color = (c == "c" ? this.turn : c);
- let pawnShift = color == "w" ? 1 : -1;
- if (x + pawnShift >= 0 && x + pawnShift < 8) {
- for (let i of [-1, 1]) {
- if (
- y + i >= 0 &&
- y + i < 8 &&
- this.getPiece(x + pawnShift, y + i) == V.PAWN &&
- this.getColor(x + pawnShift, y + i) == c
- ) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- 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))
- ) {
- return true;
- }
- }
- return false;
- }
-
- isAttackedByRook(sq, colors) {
- return this.isAttackedBySlideNJump(sq, colors, V.ROOK, V.steps[V.ROOK]);
- }
-
- isAttackedByKnight(sq, colors) {
- return this.isAttackedBySlideNJump(
- sq,
- colors,
- V.KNIGHT,
- V.steps[V.KNIGHT],
- "oneStep"
- );
- }
-
- isAttackedByBishop(sq, colors) {
- return this.isAttackedBySlideNJump(
- sq, colors, V.BISHOP, V.steps[V.BISHOP]);
- }
-
- isAttackedByQueen(sq, colors) {
- return this.isAttackedBySlideNJump(
- sq,
- colors,
- V.QUEEN,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP])
- );
- }
-
- isAttackedByKing(sq, colors) {
- return this.isAttackedBySlideNJump(
- sq,
- colors,
- V.KING,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
- "oneStep"
- );
- }
-
- underCheck(color) {
- return this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"]);
- }
-
- getCheckSquares() {
- const color = this.turn;
- // Artifically change turn, for checkered pawns
- this.turn = V.GetOppCol(color);
- const kingAttacked =
- this.isAttacked(
- this.kingPos[color],
- [this.turn, 'c']
- );
- let res = kingAttacked
- ? [JSON.parse(JSON.stringify(this.kingPos[color]))]
- : [];
- this.turn = color;
- return res;
- }
-
- postPlay(move) {
- super.postPlay(move);
- // Does this move turn off a 2-squares pawn flag?
- if (
- [1, 6].includes(move.start.x) &&
- move.vanish[0].p == V.PAWN &&
- Math.abs(move.end.x - move.start.x) == 2
- ) {
- this.pawnFlags[move.start.x == 6 ? "w" : "b"][move.start.y] = false;
- }
- this.cmoves.push(this.getCmove(move));
- }
-
- postUndo(move) {
- super.postUndo(move);
- this.cmoves.pop();
- }
-
- getCurrentScore() {
- if (this.atLeastOneMove()) return "*";
- const color = this.turn;
- // Artifically change turn, for checkered pawns
- this.turn = V.GetOppCol(this.turn);
- const res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"])
- ? color == "w"
- ? "0-1"
- : "1-0"
- : "1/2";
- this.turn = V.GetOppCol(this.turn);
- return res;
- }
-
- evalPosition() {
- let evaluation = 0;
- // Just count material for now, considering checkered neutral (...)
- for (let i = 0; i < V.size.x; i++) {
- for (let j = 0; j < V.size.y; j++) {
- if (this.board[i][j] != V.EMPTY) {
- const sqColor = this.getColor(i, j);
- if (["w","b"].includes(sqColor)) {
- const sign = sqColor == "w" ? 1 : -1;
- evaluation += sign * V.VALUES[this.getPiece(i, j)];
- }
- }
- }
- }
- return evaluation;
- }
-
- static GenRandInitFen(randomness) {
- // Add 16 pawns flags + empty cmove:
- return ChessRules.GenRandInitFen(randomness)
- .slice(0, -2) + "1111111111111111 - -";
- }
-
- static ParseFen(fen) {
- return Object.assign(
- ChessRules.ParseFen(fen),
- { cmove: fen.split(" ")[5] }
- );
- }
-
- getCmoveFen() {
- const L = this.cmoves.length;
- return (
- !this.cmoves[L - 1]
- ? "-"
- : ChessRules.CoordsToSquare(this.cmoves[L - 1].start) +
- ChessRules.CoordsToSquare(this.cmoves[L - 1].end)
- );
- }
-
- getFen() {
- return super.getFen() + " " + this.getCmoveFen();
- }
-
- getFenForRepeat() {
- return super.getFenForRepeat() + "_" + this.getCmoveFen();
- }
-
- getFlagsFen() {
- let fen = super.getFlagsFen();
- // Add pawns flags
- for (let c of ["w", "b"])
- for (let i = 0; i < 8; i++) fen += (this.pawnFlags[c][i] ? "1" : "0");
- return fen;
- }
-
- static get SEARCH_DEPTH() {
- return 2;
- }
-
- getNotation(move) {
- if (move.appear.length == 2) {
- // Castle
- if (move.end.y < move.start.y) return "0-0-0";
- return "0-0";
- }
-
- const finalSquare = V.CoordsToSquare(move.end);
- const piece = this.getPiece(move.start.x, move.start.y);
- let notation = "";
- if (piece == V.PAWN) {
- // Pawn move
- if (move.vanish.length > 1) {
- // Capture
- const startColumn = V.CoordToColumn(move.start.y);
- notation = startColumn + "x" + finalSquare;
- } else notation = finalSquare;
- } else {
- // Piece movement
- notation =
- piece.toUpperCase() +
- (move.vanish.length > 1 ? "x" : "") +
- finalSquare;
- }
- if (move.appear[0].p != move.vanish[0].p)
- notation += "=" + move.appear[0].p.toUpperCase();
- return notation;
- }
-
-};
+++ /dev/null
-../base_rules.js
\ No newline at end of file
--- /dev/null
+import { ChessRules } from "@/base_rules";
+
+export class Chess960Rules extends ChessRules {
+
+ // Do not allow standard chess:
+ static get Options() {
+ return {
+ select: [
+ {
+ label: "Randomness",
+ variable: "randomness",
+ defaut: 2,
+ options: [
+ { label: "Symmetric random", value: 1 },
+ { label: "Asymmetric random", value: 2 }
+ ]
+ }
+ ],
+ check: []
+ };
+ }
+
+};
rulesContent: "",
gameRef: {
vname: "",
- fen: ""
+ fen: "",
+ options: {}
},
game: {
players: [{ name: "Analyse" }, { name: "Analyse" }],
.row
#aboveBoard.col-sm-12
span.variant-cadence(v-if="game.type!='import'") {{ game.cadence }}
- span.variant-name {{ game.vname }}
+ span.variant-name
+ | {{ game.vname }}
+ | -
+ | {{ vr.constructor.AbbreviateOptions(game.options) }}
span#nextGame(
v-if="nextIds.length > 0"
@click="showNextGame()"
this.conn.onopen = () => callback();
};
this.fetchGame((game) => {
- if (!!game)
+ if (!!game) {
+ if (!game.options) {
+ // Patch for retro-compatibility (TODO: remove it)
+ game.options = { randomness: game.randomness };
+ delete game["randomness"];
+ }
+ else game.options = JSON.parse(game.options);
this.loadVariantThenGame(game, () => socketInit(this.roomInit));
+ }
else
// Live game stored remotely: need socket to retrieve it
// NOTE: the callback "roomInit" will be lost, so it's not provided.
const gameToSend = Object.keys(this.game)
.filter(k =>
[
- "id","fen","players","vid","cadence","fenStart",
+ "id","fen","players","vid","cadence","fenStart","options",
"moves","clocks","score","drawOffer","rematchOffer"
].includes(k))
.reduce(
// Start a new game!
let gameInfo = {
id: getRandString(), //ignored if corr
- fen: V.GenRandInitFen(this.game.randomness),
- randomness: this.game.randomness,
+ fen: V.GenRandInitFen(this.game.options),
+ options: this.game.options,
players: [this.game.players[1], this.game.players[0]],
vid: this.game.vid,
cadence: this.game.cadence
"/games",
"POST",
{
- data: { gameInfo: gameInfo },
+ data: Object.assign(
+ {},
+ gameInfo,
+ { options: JSON.stringify(this.game.options) }
+ ),
success: (response) => {
gameInfo.id = response.id;
notifyNewGame();
div#acceptDiv(role="dialog")
.card
p.text-center
- span.variantName {{ curChallToAccept.vname }}
+ span.variantName
+ | {{ curChallToAccept.vname }}
+ | {{ curChallToAccept.options.abridged }}
span {{ curChallToAccept.cadence }}
span {{ st.tr["with"] + " " + curChallToAccept.from.name }}
p.text-center(v-if="!!curChallToAccept.color")
:selected="newchallenge.vid==v.id"
)
| {{ v.display }}
+ // Variant-specific options (often at least randomness)
+ fieldset(v-if="!!newchallenge.V")
+ div(v-for="select of newchallenge.V.Options.select")
+ label(:for="select.variable + '_opt'") {{ st.tr[select.label] }} *
+ select(:id="select.variable + '_opt'")
+ option(
+ v-for="o of select.options"
+ :value="o.value"
+ :selected="o.value == select.defaut"
+ )
+ | {{ st.tr[o.label] }}
+ div(v-for="check of newchallenge.V.Options.check")
+ label(:for="check.variable + '_opt'") {{ st.tr[check.label] }} *
+ input(
+ :id="check.variable + '_opt'"
+ type="checkbox"
+ :checked="check.defaut")
fieldset
label(for="cadence") {{ st.tr["Cadence"] }} *
div#predefinedCadences
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
label(for="memorizeChall") {{ st.tr["Memorize"] }}
input#memorizeChall(
tr
th {{ st.tr["Variant"] }}
th {{ st.tr["Cadence"] }}
- th {{ st.tr["Random?"] }}
+ th {{ st.tr["Options"] }}
th
tbody
tr(
)
td {{ pc.vname }}
td {{ pc.cadence }}
- td(:class="getRandomnessClass(pc)")
+ td(:class="getRandomnessClass(pc)") {{ pc.options.abridged }}
td.remove-preset(@click="removePresetChall($event, pc)")
img(src="/images/icons/delete.svg")
.row
to: "", //name of challenged player (if any)
color: '',
cadence: localStorage.getItem("cadence") || "",
- randomness:
- // Warning: randomness can be 0, then !!randomness is false
- (parseInt(localStorage.getItem("challRandomness"),10)+1 || 3) - 1,
+ options: {},
// VariantRules object, stored to not interfere with
- // diagrams of targetted challenges:
+ // diagrams of targeted challenges:
V: null,
vname: "",
diag: "", //visualizing FEN
},
focus: true,
tchallDiag: "",
- curChallToAccept: { from: {} },
+ curChallToAccept: { from: {}, options: {} },
presetChalls: JSON.parse(localStorage.getItem("presetChalls") || "[]"),
conn: null,
connexionString: "",
to: this.$route.query["challenge"],
color: this.$route.query["color"] || '',
cadence: this.$route.query["cadence"],
- // Tournament: no randomness (TODO: for now at least)
- randomness: 0,
+ options: {},
memorize: false
}
);
window.doClick("modalNewgame");
- //this.issueNewChallenge(); //NOTE: doesn't work yet.
},
this.$route.query["variant"]
);
response.challenges.map(c => {
const from = { name: names[c.uid], id: c.uid }; //or just name
const type = this.classifyObject(c);
- const vname = this.getVname(c.vid);
+ this.setVname(c);
return Object.assign(
+ {},
+ c,
{
type: type,
- vname: vname,
from: from,
- to: c.target ? names[c.target] : ""
- },
- c
+ to: c.target ? names[c.target] : "",
+ options: JSON.parse(c.options)
+ }
);
})
);
this.conn = null;
},
getRandomnessClass: function(pc) {
+ if (!pc.options.randomness) return {};
return {
- ["random-" + pc.randomness]: true
+ ["random-" + pc.options.randomness]: true
};
},
anonymousCount: function() {
this.partialResetNewchallenge();
window.doClick("modalNewgame");
},
+ sameOptions: function(opt1, opt2) {
+ const keys1 = Object.keys(opt1),
+ keys2 = Object.keys(opt2);
+ if (keys1.length != keys2.length) return false;
+ for (const key1 of keys1) {
+ if (!keys2.includes(key1)) return false;
+ if (opt1[key1] != opt2[key1]) return false;
+ }
+ return true;
+ },
addPresetChall: function(chall) {
// Add only if not already existing:
if (this.presetChalls.some(c =>
c.vid == chall.vid &&
c.cadence == chall.cadence &&
- c.randomness == chall.randomness
+ this.sameOptions(c.options, chall.options)
)) {
return;
}
vid: chall.vid,
vname: chall.vname, //redundant, but easier
cadence: chall.cadence,
- randomness: chall.randomness
+ options: chall.options
});
localStorage.setItem("presetChalls", JSON.stringify(this.presetChalls));
},
id: c.id,
from: this.st.user.sid,
to: c.to,
- randomness: c.randomness,
+ options: c.options,
fen: c.fen,
vid: c.vid,
cadence: c.cadence,
) {
let newChall = Object.assign({}, chall);
newChall.type = this.classifyObject(chall);
- newChall.randomness = chall.randomness;
+ newChall.options = chall.options;
newChall.added = Date.now();
let fromValues = Object.assign({}, this.people[chall.from]);
delete fromValues["pages"]; //irrelevant in this context
this.partialResetNewchallenge();
this.newchallenge.vid = pchall.vid;
this.newchallenge.cadence = pchall.cadence;
- this.newchallenge.randomness = pchall.randomness;
+ this.newchallenge.options = pchall.options;
this.loadNewchallVariant(this.issueNewChallenge);
},
issueNewChallenge: async function() {
}
// NOTE: "from" information is not required here
let chall = Object.assign({}, this.newchallenge);
+ chall.options = {};
+ // Get/set options variables (if any) / TODO: v-model?!
+ for (const check of this.newchallenge.V.Options.check) {
+ const elt = document.getElementById(check.variable + "_opt");
+ if (elt.checked) chall.options[check.variable] = true;
+ }
+ for (const select of this.newchallenge.V.Options.select) {
+ const elt = document.getElementById(select.variable + "_opt");
+ chall.options[select.variable] = elt.value;
+ }
+ chall.options.abridged = V.AbbreviateOptions(chall.options);
// Add only if not already issued (not counting FEN):
if (this.challenges.some(c =>
(
&&
c.vid == chall.vid &&
c.cadence == chall.cadence &&
- c.randomness == chall.randomness
+ this.sameOptions(c.options, chall.options)
)) {
alert(this.st.tr["Challenge already exists"]);
return;
chall.type = ctype;
chall.vname = this.newchallenge.vname;
this.challenges.push(chall);
- // Remember cadence + vid for quicker further challenges:
+ // Remember cadence + vid for quicker further challenges:
localStorage.setItem("cadence", chall.cadence);
localStorage.setItem("vid", chall.vid);
- localStorage.setItem("challRandomness", chall.randomness);
document.getElementById("modalNewgame").checked = false;
// Show the challenge if not on current display
if (
"/challenges",
"POST",
{
- data: { chall: chall },
+ data: {
+ chall: Object.assign(
+ {},
+ chall,
+ { options: JSON.stringify(chall.options) }
+ )
+ },
success: (response) => {
finishAddChallenge(response.id);
}
// These game informations will be shared
let gameInfo = {
id: getRandString(),
- fen: c.fen || V.GenRandInitFen(c.randomness),
- randomness: c.randomness, //for rematch
+ fen: c.fen || V.GenRandInitFen(c.options),
+ options: c.options, //for rematch
players: players,
vid: c.vid,
cadence: c.cadence
{
// cid is useful to delete the challenge:
data: {
- gameInfo: gameInfo,
+ gameInfo: Object.assign(
+ {},
+ gameInfo,
+ { options: JSON.stringify(gameInfo.options) }
+ ),
cid: c.id
},
success: (response) => {
tr > td
&.random-0
- background-color: #FF5733
+ background-color: #FEAF9E
&.random-1
- background-color: #2B63B4
+ background-color: #9EB2FE
&.random-2
- background-color: #33B42B
+ background-color: #A5FE9E
@media screen and (max-width: 767px)
h4
<template lang="pug">
main
+ input#modalOptions.modal(type="checkbox")
+ div#optionsDiv(
+ role="dialog"
+ data-checkbox="modalOptions"
+ )
+ .card
+ label.modal-close(for="modalOptions")
+ h3 {{ st.tr["Options"] }}
+ fieldset(v-if="!!V")
+ div(v-for="select of V.Options.select")
+ label(:for="select.variable + '_opt'") {{ st.tr[select.label] }} *
+ select(:id="select.variable + '_opt'")
+ option(
+ v-for="o of select.options"
+ :value="o.value"
+ :selected="o.value == select.defaut"
+ )
+ | {{ st.tr[o.label] }}
+ div(v-for="check of V.Options.check")
+ label(:for="check.variable + '_opt'") {{ st.tr[check.label] }} *
+ input(
+ :id="check.variable + '_opt'"
+ type="checkbox"
+ :checked="check.defaut")
+ button(@click="setOptions()") {{ st.tr["Validate"] }}
.row
.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
.button-group
import { store } from "@/store";
import { replaceByDiag } from "@/utils/printDiagram";
import { CompgameStorage } from "@/utils/compgameStorage";
+import { processModalClick } from "@/utils/modalClick";
import afterRawLoad from "@/utils/afterRawLoad";
export default {
name: "my-rules",
// variables passed to ComputerGame:
gameInfo: {
vname: "",
- mode: "versus",
+ mode: "versus"
},
- V: null,
+ V: null
};
},
watch: {
// NOTE: variant cannot be set before store is initialized
this.re_setVariant(this.$route.params["vname"]);
},
+ mounted: function() {
+ document.getElementById("optionsDiv")
+ .addEventListener("click", processModalClick);
+ },
computed: {
showAnalyzeBtn: function() {
return !!this.V && this.V.CanAnalyze;
}, 500);
});
},
- startGame: function(mode) {
+ setOptions: function() {
+ let options = {};
+ // Get/set options variables / TODO: v-model?!
+ for (const check of this.V.Options.check) {
+ const elt = document.getElementById(check.variable + "_opt");
+ if (elt.checked) options[check.variable] = true;
+ }
+ for (const select of this.V.Options.select) {
+ const elt = document.getElementById(select.variable + "_opt");
+ options[select.variable] = elt.value;
+ }
+ document.getElementById("modalOptions").checked = false;
+ if (this.whatNext == "analyze") this.gotoAnalyze(options);
+ else this.startGame(this.whatNext, options);
+ },
+ startGame: function(mode, options) {
if (this.gameInProgress) return;
- this.gameInProgress = true;
- this.display = "computer";
- this.gameInfo.mode = mode;
- if (this.gameInfo.mode == "versus") {
+ const next = (game, options) => {
+ this.gameInProgress = true;
+ this.display = "computer";
+ this.gameInfo.mode = mode;
+ this.$refs["compgame"].launchGame(game, options);
+ };
+ if (!!options) {
+ next(null, options);
+ return;
+ }
+ const askOptions = () => {
+ this.whatNext = mode;
+ doClick("modalOptions");
+ };
+ if (mode == "versus") {
CompgameStorage.get(this.gameInfo.vname, (game) => {
// NOTE: game might be null (if none stored yet)
if (!!game && !V.IsGoodFen(game.fen)) {
CompgameStorage.remove(game.vname);
game = null;
}
- this.$refs["compgame"].launchGame(game);
+ if (!!game || Object.keys(V.Options).length == 0) next(game);
+ else askOptions();
});
}
- else this.$refs["compgame"].launchGame();
+ else {
+ if (Object.keys(V.Options).length == 0) next();
+ else askOptions();
+ }
},
// The user wants to stop the game:
stopGame: function() {
if (this.gameInfo.mode == "versus")
CompgameStorage.remove(this.gameInfo.vname);
},
- gotoAnalyze: function() {
- this.$router.push(
- "/analyse/" + this.gameInfo.vname +
- "/?fen=" + V.GenRandInitFen(this.st.settings.randomness)
- );
+ gotoAnalyze: function(options) {
+ if (!options && Object.keys(V.Options).length > 0) {
+ this.whatNext = "analyze";
+ doClick("modalOptions");
+ }
+ else {
+ this.$router.push(
+ "/analyse/" + this.gameInfo.vname +
+ "/?fen=" + V.GenRandInitFen(options)
+ );
+ }
}
}
};
a(href="https://www.chessvariants.com/why.html")
| {{ st.tr["Why play chess variants?"] }}
p
- a(href="/#/variants/Chess") Chess
- | {{ st.tr["chess_v"] }}
+ a(href="/#/variants/Chess960") Chess960
+ | {{ st.tr["chess960_v"] }}
div(v-for="g of sortedGroups")
h3 {{ st.tr["vt" + g] }}
p {{ st.tr["vg" + g] }}
uid integer,
target integer,
vid integer,
- randomness integer,
+ options varchar,
fen varchar,
cadence varchar,
foreign key (uid) references Users(id),
score varchar default '*',
scoreMsg varchar,
cadence varchar,
- randomness integer, --for rematch
+ options varchar, --for rematch
created datetime,
drawOffer character default '',
rematchOffer character default '',
('Alapo', 'Geometric Chess', 27, 'Alapo'),
('Alice', 'Both sides of the mirror', 31, 'Alice Chess'),
('Align4', 'Align four pawns', 31, 'Align4'),
- ('Allmate1', 'Mate any piece (v1)', 11, 'Allmate1'),
- ('Allmate2', 'Mate any piece (v2)', 11, 'Allmate2'),
+ ('Allmate', 'Mate any piece', 11, 'Allmate'),
('Ambiguous', 'Play opponent''s pieces', 29, 'Ambiguous'),
('Antiking1', 'Keep antiking in check (v1)', 9, 'Anti-King 1'),
('Antiking2', 'Keep antiking in check (v2)', 9, 'Anti-King 2'),
('Capablanca', 'Capablanca Chess', 7, 'Capablanca Chess'),
('Capture', 'Mandatory captures', 1, 'Capture'),
('Castle', 'Win by castling long', 27, 'Castle'),
- ('Checkered1', 'Shared pieces (v1)', 12, 'Checkered 1'),
- ('Checkered2', 'Shared pieces (v2)', 12, 'Checkered 2'),
+ ('Checkered', 'Shared pieces', 12, 'Checkered'),
('Checkless', 'No-check mode', 18, 'Checkless'),
- ('Chess', 'Standard rules', -1, 'Chess'),
+ ('Chess960', 'Standard rules', -1, 'Chess960'),
('Circular', 'Run forward', 3, 'Circular Chess'),
('Clorange', 'A Clockwork Orange', 20, 'Clockwork Orange'),
('Colorbound', 'The colorbound clobberers', 5, 'Colorbound Clobberers'),
* uid: user id (int)
* target: recipient id (optional)
* vid: variant id (int)
- * randomness: integer in 0..2
+ * options: varchar
* fen: varchar (optional)
* cadence: string (3m+2s, 7d ...)
+ * options: string (js object)
*/
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 }))
);
const query =
"INSERT INTO Challenges " +
"(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) {
+ "vid, options, fen, cadence) " +
+ "VALUES (" + Date.now() + "," + c.uid + "," + (c.to ? c.to + "," : "")
+ + c.vid + ",?,'" + c.fen + "','" + c.cadence + "')";
+ db.run(query, c.options, function(err) {
cb(err, { id: this.lastID });
});
});
* created: datetime
* drawOffer: char ('w','b' or '' for none)
* rematchOffer: char (similar to drawOffer)
- * randomness: integer
+ * options: varchar
* deletedByWhite: boolean
* deletedByBlack: boolean
* chatReadWhite: datetime
return (
g.vid.toString().match(/^[0-9]+$/) &&
g.cadence.match(/^[0-9dhms +]+$/) &&
- g.randomness.toString().match(/^[0-2]$/) &&
g.fen.match(/^[a-zA-Z0-9, /-]*$/) &&
g.players.length == 2 &&
g.players.every(p => p.id.toString().match(/^[0-9]+$/))
});
},
- create: function(vid, fen, randomness, cadence, players, cb) {
+ create: function(vid, fen, options, cadence, players, cb) {
db.serialize(function() {
let query =
"INSERT INTO Games " +
"(" +
- "vid, fenStart, fen, randomness, " +
+ "vid, fenStart, fen, options, " +
"white, black, " +
"cadence, created" +
") " +
"VALUES " +
"(" +
- vid + ",'" + fen + "','" + fen + "'," + randomness + "," +
+ vid + ",'" + fen + "','" + fen + "',?," +
players[0].id + "," + players[1].id + "," +
"'" + cadence + "'," + Date.now() +
")";
- db.run(query, function(err) {
+ db.run(query, options, function(err) {
cb(err, { id: this.lastID });
});
});
let query =
"SELECT " +
"id, vid, fen, fenStart, cadence, created, " +
- "white, black, randomness, score, scoreMsg, " +
+ "white, black, options, score, scoreMsg, " +
"chatReadWhite, chatReadBlack, drawOffer, rematchOffer " +
"FROM Games " +
"WHERE id = " + id;
let challenge = {
fen: req.body.chall.fen,
cadence: req.body.chall.cadence,
+ options: req.body.chall.options,
randomness: req.body.chall.randomness,
vid: req.body.chall.vid,
uid: req.userId,