From e8746dfab7a37f29384fcc567e5b24ded7c3a3d8 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Mon, 20 Jun 2022 10:18:06 +0200
Subject: [PATCH 01/16] TODO for Chakart
---
TODO | 3 +++
1 file changed, 3 insertions(+)
diff --git a/TODO b/TODO
index a17d5c3..2aeb9ae 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1,6 @@
+Chakart egg : afficher image du bonus sur la case d'arrivée, clignotante, pendant 1 seconde ?
+Invisible queen : afficher "??" sur la case de départ jusqu'au coup adverse (qui l'efface du coup) ?
+
add variants : Dark Racing Kings ?
Checkered-Teleport ?
--
2.44.0
From cc9fe4f154ec244cbec468d1a3b5845e56cb378d Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Mon, 20 Jun 2022 19:43:39 +0200
Subject: [PATCH 02/16] Chakart ready for testing
---
TODO | 3 --
pieces/chakart_mystery_black.svg | 53 +++++++++++++++++++++++
pieces/chakart_mystery_white.svg | 54 +++++++++++++++++++++++
variants/Chakart/class.js | 74 +++++++++++++++-----------------
variants/Chakart/style.css | 25 +++++++++++
5 files changed, 167 insertions(+), 42 deletions(-)
create mode 100644 pieces/chakart_mystery_black.svg
create mode 100644 pieces/chakart_mystery_white.svg
diff --git a/TODO b/TODO
index 2aeb9ae..a17d5c3 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,3 @@
-Chakart egg : afficher image du bonus sur la case d'arrivée, clignotante, pendant 1 seconde ?
-Invisible queen : afficher "??" sur la case de départ jusqu'au coup adverse (qui l'efface du coup) ?
-
add variants : Dark Racing Kings ?
Checkered-Teleport ?
diff --git a/pieces/chakart_mystery_black.svg b/pieces/chakart_mystery_black.svg
new file mode 100644
index 0000000..4950f93
--- /dev/null
+++ b/pieces/chakart_mystery_black.svg
@@ -0,0 +1,53 @@
+
+
+
+
+
+Created by potrace 1.15, written by Peter Selinger 2001-2017
+
+
+
+
+
+
diff --git a/pieces/chakart_mystery_white.svg b/pieces/chakart_mystery_white.svg
new file mode 100644
index 0000000..f2b7e06
--- /dev/null
+++ b/pieces/chakart_mystery_white.svg
@@ -0,0 +1,54 @@
+
+
+
+
+
+Created by potrace 1.15, written by Peter Selinger 2001-2017
+
+
+
+
+
+
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index a9b1908..c987649 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -64,10 +64,6 @@ export default class ChakartRules extends ChessRules {
};
}
- static get INVISIBLE_QUEEN() {
- return 'i';
- }
-
// Fictive color 'a', bomb banana mushroom egg
static get BOMB() {
return 'w'; //"Wario"
@@ -101,6 +97,7 @@ export default class ChakartRules extends ChessRules {
pieces(color, x, y) {
const specials = {
'i': {"class": "invisible"}, //queen
+ '?': {"class": "mystery"}, //...initial square
'e': {"class": "egg"},
'm': {"class": "mushroom"},
'd': {"class": "banana"},
@@ -182,11 +179,7 @@ export default class ChakartRules extends ChessRules {
for (let j = 0; j < this.size.y; j++) {
const pieceIJ = this.getPiece(i, j);
const colIJ = this.getColor(i, j);
- if (
- this.board[i][j] == "" ||
- colIJ == 'a' ||
- pieceIJ == V.INVISIBLE_QUEEN
- ) {
+ if (this.board[i][j] == "" || colIJ == 'a' || pieceIJ == 'i') {
let m = new Move({
start: {x: c, y: p},
appear: [new PiPo({x: i, y: j, c: c, p: p})],
@@ -272,7 +265,8 @@ export default class ChakartRules extends ChessRules {
canStepOver(i, j) {
return (
this.board[i][j] == "" ||
- [V.MUSHROOM, V.EGG].includes(this.getPiece(i, j)));
+ ['i', V.EGG, V.MUSHROOM].includes(this.getPiece(i, j))
+ );
}
getPawnMovesFrom([x, y]) {
@@ -281,18 +275,20 @@ export default class ChakartRules extends ChessRules {
const shiftX = (color == 'w' ? -1 : 1);
const firstRank = (color == "w" ? this.size.x - 1 : 0);
let moves = [];
+ const frontPiece = this.getPiece(x + shiftX, y);
if (
this.board[x + shiftX][y] == "" ||
this.getColor(x + shiftX, y) == 'a' ||
- this.getPiece(x + shiftX, y) == V.INVISIBLE_QUEEN
+ frontPiece == 'i'
) {
moves.push(this.getBasicMove([x, y], [x + shiftX, y]));
if (
[firstRank, firstRank + shiftX].includes(x) &&
+ ![V.BANANA, V.BOMB].includes(frontPiece) &&
(
this.board[x + 2 * shiftX][y] == "" ||
this.getColor(x + 2 * shiftX, y) == 'a' ||
- this.getPiece(x + 2 * shiftX, y) == V.INVISIBLE_QUEEN
+ this.getPiece(x + 2 * shiftX, y) == 'i'
)
) {
moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
@@ -304,7 +300,7 @@ export default class ChakartRules extends ChessRules {
y + shiftY < this.size.y &&
this.board[x + shiftX][y + shiftY] != "" &&
// Pawns cannot capture invisible queen this way!
- this.getPiece(x + shiftX, y + shiftY) != V.INVISIBLE_QUEEN &&
+ this.getPiece(x + shiftX, y + shiftY) != 'i' &&
['a', oppCol].includes(this.getColor(x + shiftX, y + shiftY))
) {
moves.push(this.getBasicMove([x, y], [x + shiftX, y + shiftY]));
@@ -351,7 +347,7 @@ export default class ChakartRules extends ChessRules {
m.vanish[0].c != 'a'
) {
let im = JSON.parse(JSON.stringify(m));
- im.appear[0].p = V.INVISIBLE_QUEEN;
+ im.appear[0].p = 'i';
im.noAnimate = true;
invisibleMoves.push(im);
}
@@ -366,17 +362,7 @@ export default class ChakartRules extends ChessRules {
if (this.powerFlags[this.turn]['k']) {
super.pieces()['k'].moves[0].steps.forEach(step => {
let [i, j] = [x + step[0], y + step[1]];
- while (
- this.onBoard(i, j) &&
- (
- this.board[i][j] == "" ||
- this.getPiece(i, j) == V.INVISIBLE_QUEEN ||
- (
- this.getColor(i, j) == 'a' &&
- [V.EGG, V.MUSHROOM].includes(this.getPiece(i, j))
- )
- )
- ) {
+ while (this.onBoard(i, j) && this.canStepOver(i, j)) {
i += step[0];
j += step[1];
}
@@ -471,10 +457,12 @@ export default class ChakartRules extends ChessRules {
}
if (move.shell)
this.powerFlags[color]['k'] = false;
- else if (move.appear.length > 0 && move.appear[0].p == V.INVISIBLE_QUEEN) {
+ else if (move.appear.length > 0 && move.appear[0].p == 'i') {
this.powerFlags[move.appear[0].c]['q'] = false;
- if (color != this.playerColor)
- alert("Invisible queen!");
+ if (color == this.playerColor) {
+ move.appear.push(
+ new PiPo({x: move.start.x, y: move.start.y, c: color, p: '?'}));
+ }
}
if (color == this.playerColor) {
// Look for an immobilized piece of my color: it can now move
@@ -501,15 +489,15 @@ export default class ChakartRules extends ChessRules {
for (let j=0; j<8; j++) {
if (
this.board[i][j] != "" &&
- this.getColor(i, j) == oppCol &&
- this.getPiece(i, j) == V.INVISIBLE_QUEEN
+ this.getColor(i, j) == oppCol
) {
- move.vanish.push(new PiPo({
- x: i, y: j, c: oppCol, p: V.INVISIBLE_QUEEN
- }));
- move.appear.push(new PiPo({
- x: i, y: j, c: oppCol, p: 'q'
- }));
+ const pieceIJ = this.getPiece(i, j);
+ if (pieceIJ == 'i') {
+ move.vanish.push(new PiPo({x: i, y: j, c: oppCol, p: 'i'}));
+ move.appear.push(new PiPo({x: i, y: j, c: oppCol, p: 'q'}));
+ }
+ else if (pieceIJ == '?')
+ move.vanish.push(new PiPo({x: i, y: j, c: oppCol, p: '?'}));
}
}
}
@@ -519,7 +507,7 @@ export default class ChakartRules extends ChessRules {
this.movesCount++;
}
if (move.egg)
- this.displayBonus(move.egg);
+ this.displayBonus(move);
this.playOnBoard(move);
this.nextMove = move.next;
}
@@ -616,6 +604,7 @@ export default class ChakartRules extends ChessRules {
p: this.getPiece(move.start.x, move.start.y)
}));
}
+ em.koopa = true; //to cancel mushroom effect
break;
case "chomp":
// Eat piece
@@ -636,6 +625,8 @@ export default class ChakartRules extends ChessRules {
}
getMushroomEffect(move) {
+ if (move.koopa)
+ return null;
let step = [move.end.x - move.start.x, move.end.y - move.start.y];
if ([0, 1].some(i => Math.abs(step[i]) >= 2 && Math.abs(step[1-i]) != 1)) {
// Slider, multi-squares: normalize step
@@ -678,8 +669,13 @@ export default class ChakartRules extends ChessRules {
return res;
}
- displayBonus(egg) {
- alert(egg); //TODO: nicer display
+ displayBonus(move) {
+ let divBonus = document.createElement("div");
+ divBonus.classList.add("bonus-text");
+ divBonus.innerHTML = move.egg;
+ let container = document.getElementById(this.containerId);
+ container.appendChild(divBonus);
+ setTimeout(() => container.removeChild(divBonus), 2000);
}
atLeastOneMove() {
diff --git a/variants/Chakart/style.css b/variants/Chakart/style.css
index 65a44fb..b3687ce 100644
--- a/variants/Chakart/style.css
+++ b/variants/Chakart/style.css
@@ -31,3 +31,28 @@ piece.immobilized {
piece.remote-capture {
background-image: url('/pieces/chakart_shell.svg');
}
+
+piece.mystery.white {
+ background-image: url('/pieces/chakart_mystery_white.svg');
+}
+piece.mystery.black {
+ background-image: url('/pieces/chakart_mystery_black.svg');
+}
+
+div.bonus-text {
+ position: relative;
+ margin-top: 5%;
+ width: 100%;
+ text-align: center;
+ background-color: transparent;
+ color: darkred;
+ font-weight: bold;
+ font-size: 2em;
+ animation: blinker 0.5s linear infinite;
+}
+
+@keyframes blinker {
+ 50% {
+ opacity: 0;
+ }
+}
--
2.44.0
From a2bb7e0621f143f912ca505fed914fe0a5e6d611 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Mon, 20 Jun 2022 21:46:20 +0200
Subject: [PATCH 03/16] Fix Chakart promotions after effects
---
base_rules.js | 2 ++
variants/Chakart/CREDITS | 1 +
variants/Chakart/class.js | 28 ++++++++++++++++++++++------
3 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/base_rules.js b/base_rules.js
index 8ad0894..8b07059 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -1007,6 +1007,8 @@ export default class ChessRules {
let chessboard = container.querySelector(".chessboard");
let choices = document.createElement("div");
choices.id = "choices";
+ if (!r)
+ r = chessboard.getBoundingClientRect();
choices.style.width = r.width + "px";
choices.style.height = r.height + "px";
choices.style.left = r.x + "px";
diff --git a/variants/Chakart/CREDITS b/variants/Chakart/CREDITS
index e15c948..53a9181 100644
--- a/variants/Chakart/CREDITS
+++ b/variants/Chakart/CREDITS
@@ -5,3 +5,4 @@ https://commons.wikimedia.org/wiki/File:Tux_Paint_banana.svg
https://www.onlinewebfonts.com/icon/425540
https://www.svgrepo.com/svg/264673/easter-egg-easter
https://www.svgrepo.com/svg/321648/turtle-shell
+https://svgsilh.com/image/40876.html
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index c987649..a0c854b 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -306,7 +306,7 @@ export default class ChakartRules extends ChessRules {
moves.push(this.getBasicMove([x, y], [x + shiftX, y + shiftY]));
}
}
- this.pawnPostProcess(moves, color, oppCol);
+ super.pawnPostProcess(moves, color, oppCol);
// Add mushroom on before-last square
moves.forEach(m => {
let revStep = [m.start.x - m.end.x, m.start.y - m.end.y];
@@ -389,6 +389,22 @@ export default class ChakartRules extends ChessRules {
}
play(move) {
+ const color = this.turn;
+ const oppCol = C.GetOppCol(color);
+ if (
+ move.appear.length > 0 &&
+ move.appear[0].p == 'p' &&
+ (
+ (color == 'w' && move.end.x == 0) ||
+ (color == 'b' && move.end.x == this.size.x - 1)
+ )
+ ) {
+ // "Forgotten" promotion, which occurred after some effect
+ let moves = [move];
+ super.pawnPostProcess(moves, color, oppCol);
+ super.showChoices(moves);
+ return false;
+ }
if (!move.nextComputed) {
// Set potential random effects, so that play() is deterministic
// from opponent viewpoint:
@@ -441,8 +457,6 @@ export default class ChakartRules extends ChessRules {
move.nextComputed = true;
}
this.egg = move.egg;
- const color = this.turn;
- const oppCol = C.GetOppCol(color);
if (move.egg == "toadette") {
this.reserve = { w: {}, b: {} };
// Randomly select a piece in pawnPromotions
@@ -510,6 +524,7 @@ export default class ChakartRules extends ChessRules {
this.displayBonus(move);
this.playOnBoard(move);
this.nextMove = move.next;
+ return true;
}
// Helper to set and apply banana/bomb effect
@@ -687,9 +702,10 @@ export default class ChakartRules extends ChessRules {
}
playPlusVisual(move, r) {
- this.moveStack.push(move);
const nextLines = () => {
- this.play(move);
+ if (!this.play(move))
+ return;
+ this.moveStack.push(move);
this.playVisual(move, r);
if (this.nextMove)
this.playPlusVisual(this.nextMove, r);
@@ -698,7 +714,7 @@ export default class ChakartRules extends ChessRules {
this.moveStack = [];
}
};
- if (this.moveStack.length == 1)
+ if (this.moveStack.length == 0)
nextLines();
else
this.animate(move, nextLines);
--
2.44.0
From bc5d61a77f1b9f2b917221ec3f5e306818c67cf8 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Mon, 20 Jun 2022 22:33:32 +0200
Subject: [PATCH 04/16] Fix mushroom effect: more consistent
---
variants/Chakart/class.js | 21 ++++-----------------
1 file changed, 4 insertions(+), 17 deletions(-)
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index a0c854b..61a8ee2 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -640,7 +640,7 @@ export default class ChakartRules extends ChessRules {
}
getMushroomEffect(move) {
- if (move.koopa)
+ if (move.koopa || typeof move.start.x == "string")
return null;
let step = [move.end.x - move.start.x, move.end.y - move.start.y];
if ([0, 1].some(i => Math.abs(step[i]) >= 2 && Math.abs(step[1-i]) != 1)) {
@@ -652,24 +652,11 @@ export default class ChakartRules extends ChessRules {
const afterSquare =
[nextSquare[0] + step[0], nextSquare[1] + step[1]];
let nextMove = null;
- this.playOnBoard(move); //HACK for getBasicMove() below
- if (
- this.onBoard(nextSquare[0], nextSquare[1]) &&
- ['k', 'p', 'n'].includes(move.vanish[0].p) &&
- !['w', 'b'].includes(this.getColor(nextSquare[0], nextSquare[1]))
- ) {
- // Speed up non-sliders
+ if (this.onBoard(nextSquare[0], nextSquare[1])) {
+ this.playOnBoard(move); //HACK for getBasicMove()
nextMove = this.getBasicMove([move.end.x, move.end.y], nextSquare);
+ this.undoOnBoard(move);
}
- else if (
- this.onBoard(afterSquare[0], afterSquare[1]) &&
- this.board[nextSquare[0]][nextSquare[1]] != "" &&
- this.getColor(nextSquare[0], nextSquare[1]) != 'a' &&
- this.getColor(afterSquare[0], afterSquare[1]) != this.turn
- ) {
- nextMove = this.getBasicMove([move.end.x, move.end.y], afterSquare);
- }
- this.undoOnBoard(move);
return nextMove;
}
--
2.44.0
From fe234391b05ffef5e3236e82ca1391adcb784b45 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Mon, 20 Jun 2022 23:19:55 +0200
Subject: [PATCH 05/16] Add Suction
---
TODO | 6 +-
variants.js | 2 +-
variants/Chakart/class.js | 2 +-
variants/Suction/class.js | 113 ++++++++++++++++++++++++++++++++++++
variants/Suction/rules.html | 5 ++
variants/Suction/style.css | 1 +
6 files changed, 125 insertions(+), 4 deletions(-)
create mode 100644 variants/Suction/class.js
create mode 100644 variants/Suction/rules.html
create mode 100644 variants/Suction/style.css
diff --git a/TODO b/TODO
index a17d5c3..d879b34 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,7 @@
-add variants : Dark Racing Kings ?
-Checkered-Teleport ?
+add variants :
+Ambiguous
+Refusal
+Dark Racing Kings ? Checkered-Teleport ?
Otage, Emergo, Pacosako : fonction "buildPiece(arg1, arg2)" returns HTML element with 2 SVG or SVG + number
réfléchir aux animations par variante (reversi, Chakart, Atomic, ...)
diff --git a/variants.js b/variants.js
index dc7540e..eb7203e 100644
--- a/variants.js
+++ b/variants.js
@@ -137,7 +137,7 @@ const variants = [
// {name: 'Spartan', desc: 'Spartan versus Persians'},
// {name: 'Squatter', desc: 'Squat last rank'},
// {name: 'Stealthbomb', desc: 'Beware the bomb'},
-// {name: 'Suction', desc: 'Attract opposite king'},
+ {name: 'Suction', desc: 'Attract opposite king'},
// {name: 'Swap', desc: 'Dangerous captures'},
// {name: 'Switching', desc: "Exchange pieces' positions"},
// {name: 'Synchrone', desc: 'Play at the same time'},
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index 61a8ee2..c527b8c 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -117,7 +117,7 @@ export default class ChakartRules extends ChessRules {
genRandInitFen(seed) {
const gr = new GiveawayRules(
- {mode: "suicide", options: {}, genFenOnly: true});
+ {mode: "suicide", options: this.options, genFenOnly: true});
// Add Peach + mario flags
return gr.genRandInitFen(seed).slice(0, -17) + '{"flags":"1111"}';
}
diff --git a/variants/Suction/class.js b/variants/Suction/class.js
new file mode 100644
index 0000000..db10e58
--- /dev/null
+++ b/variants/Suction/class.js
@@ -0,0 +1,113 @@
+import ChessRules from "/base_rules.js";
+import GiveawayRules from "/variants/Giveaway/class.js";
+import PiPo from "/utils/PiPo.js";
+import Move from "/utils/Move.js";
+
+export default class SuctionRules extends ChessRules {
+
+ static get Options() {
+ return {
+ select: C.Options.select,
+ styles: [
+ "balance",
+ "capture",
+ "cylinder",
+ "dark",
+ "doublemove",
+ "madrasi",
+ "progressive",
+ "teleport"
+ ]
+ };
+ }
+
+ get pawnPromotions() {
+ return ['p']; //no promotions
+ }
+
+ get hasFlags() {
+ return false;
+ }
+
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
+ this.cmove = null;
+ const cmove_str = fenParsed.cmove;
+ if (cmove_str != "-") {
+ this.cmove = {
+ start: C.SquareToCoords(cmove_str.substr(0, 2)),
+ end: C.SquareToCoords(cmove_str.substr(2))
+ };
+ }
+ }
+
+ genRandInitFen(seed) {
+ const gr = new GiveawayRules(
+ {mode: "suicide", options: this.options, genFenOnly: true});
+ // Add empty cmove:
+ return (
+ gr.genRandInitFen(seed).slice(0, -17) + '{"enpassant":"-","cmove":"-"}');
+ }
+
+ getFen() {
+ const cmoveFen = !this.cmove
+ ? "-"
+ : C.CoordsToSquare(this.cmove.start) + C.CoordsToSquare(this.cmove.end);
+ return super.getFen().slice(0, -1) + ',"' + cmoveFen + '"}';
+ }
+
+ getBasicMove([sx, sy], [ex, ey]) {
+ let move = super.getBasicMove([sx, sy], [ex, ey]);
+ if (move.vanish.length == 2) {
+ move.appear.push(
+ new PiPo({
+ x: sx,
+ y: sy,
+ c: move.vanish[1].c,
+ p: move.vanish[1].p
+ })
+ );
+ }
+ return move;
+ }
+
+ canIplay(x, y) {
+ return this.getPiece(x, y) != 'k' && super.canIplay(x, y);
+ }
+
+ // Does m2 un-do m1 ? (to disallow undoing captures)
+ oppositeMoves(m1, m2) {
+ return (
+ !!m1 &&
+ m2.vanish.length == 2 &&
+ m1.start.x == m2.start.x &&
+ m1.end.x == m2.end.x &&
+ m1.start.y == m2.start.y &&
+ m1.end.y == m2.end.y
+ );
+ }
+
+ filterValid(moves) {
+ return moves.filter(m => !this.oppositeMoves(this.cmove, m));
+ }
+
+ postPlay(move) {
+ super.postPlay(move);
+ this.cmove =
+ (move.vanish.length == 2 ? {start: move.start, end: move.end} : null);
+ }
+
+ atLeastOneMove() {
+ return true;
+ }
+
+ getCurrentScore() {
+ const color = this.turn;
+ const kingPos = super.searchKingPos(color);
+ if (color == "w" && kingPos[0] == 0) return "0-1";
+ if (color == "b" && kingPos[0] == this.size.x - 1) return "1-0";
+ // King is not on the opposite edge: game not over
+ return "*";
+ }
+
+};
diff --git a/variants/Suction/rules.html b/variants/Suction/rules.html
new file mode 100644
index 0000000..09a70cb
--- /dev/null
+++ b/variants/Suction/rules.html
@@ -0,0 +1,5 @@
+Pieces are swapped after captures. Kings cannot move except by being captured.
+
+Win by bringing the enemy king on your first rank.
+
+Nathaniel Virgo (2018).
diff --git a/variants/Suction/style.css b/variants/Suction/style.css
new file mode 100644
index 0000000..a3550bc
--- /dev/null
+++ b/variants/Suction/style.css
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
--
2.44.0
From f54357573d4fdf87a05b19f78506c11f16bb3a26 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Tue, 21 Jun 2022 09:45:23 +0200
Subject: [PATCH 06/16] Add Refusal
---
app.js | 6 +-
base_rules.js | 6 +-
variants.js | 2 +-
variants/Absorption/rules.html | 5 +-
variants/Atomic/rules.html | 5 +-
variants/Balanced/rules.html | 5 +-
variants/Chakart/class.js | 95 ++++++++++----------
variants/Chakart/complete_rules.html | 1 +
variants/Chakart/rules.html | 7 +-
variants/Chess960/rules.html | 4 +-
variants/Cylinder/rules.html | 5 +-
variants/Hex/rules.html | 4 +-
variants/Progressive/rules.html | 5 +-
variants/Refusal/class.js | 128 +++++++++++++++++++++++++++
variants/Refusal/rules.html | 6 ++
variants/Refusal/style.css | 1 +
variants/Suction/class.js | 2 +-
variants/Suction/rules.html | 5 +-
variants/Zen/rules.html | 5 +-
19 files changed, 235 insertions(+), 62 deletions(-)
create mode 100644 variants/Chakart/complete_rules.html
create mode 100644 variants/Refusal/class.js
create mode 100644 variants/Refusal/rules.html
create mode 100644 variants/Refusal/style.css
diff --git a/app.js b/app.js
index bb16537..7d387c5 100644
--- a/app.js
+++ b/app.js
@@ -191,7 +191,9 @@ function getGameLink() {
const vname = $.getElementById("selectVariant").value;
const color = $.getElementById("selectColor").value;
for (const select of $.querySelectorAll("#gameOptions select")) {
- const value = parseInt(select.value, 10) || select.value;
+ let value = parseInt(select.value, 10);
+ if (isNaN(value)) //not an integer
+ value = select.value;
options[ select.id.split("_")[1] ] = value;
}
for (const input of $.querySelectorAll("#gameOptions input")) {
@@ -229,7 +231,7 @@ function fillGameInfos(gameInfos, oppIndex) {
if (j == options.length)
break;
const opt = options[j];
- if (!opt[1])
+ if (!opt[1]) //includes 0 and false (lighter display)
continue;
htmlContent +=
'' +
diff --git a/base_rules.js b/base_rules.js
index 8b07059..1b22ae5 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -1908,10 +1908,12 @@ export default class ChessRules {
return [-1, -1]; //king not found
}
- filterValid(moves) {
+ // Some variants (e.g. Refusal) may need to check opponent moves too
+ filterValid(moves, color) {
if (moves.length == 0)
return [];
- const color = this.turn;
+ if (!color)
+ color = this.turn;
const oppCol = C.GetOppCol(color);
if (this.options["balance"] && [1, 3].includes(this.movesCount)) {
// Forbid moves either giving check or exploding opponent's king:
diff --git a/variants.js b/variants.js
index eb7203e..aafd02a 100644
--- a/variants.js
+++ b/variants.js
@@ -120,7 +120,7 @@ const variants = [
// {name: 'Relayup', desc: 'Upgrade pieces', disp: 'Relay-up'},
{name: 'Rifle', desc: 'Shoot pieces'},
{name: 'Recycle', desc: 'Reuse pieces'},
-// {name: 'Refusal', desc: 'Do not play that!'},
+ {name: 'Refusal', desc: 'Do not play that!'},
// {name: 'Rollerball', desc: 'As in the movie'},
// {name: 'Rococo', desc: 'Capture on the edge'},
// {name: 'Royalrace', desc: 'Kings cross the 11x11 board', disp: 'Royal Race'},
diff --git a/variants/Absorption/rules.html b/variants/Absorption/rules.html
index 53d5e45..1149295 100644
--- a/variants/Absorption/rules.html
+++ b/variants/Absorption/rules.html
@@ -1 +1,4 @@
-The capturer absorbs abilities of the captured piece (except for pawns and king).
+
+ The capturer absorbs the abilities of the captured piece
+ (except for pawns and king).
+
diff --git a/variants/Atomic/rules.html b/variants/Atomic/rules.html
index 05829a5..c46cf22 100644
--- a/variants/Atomic/rules.html
+++ b/variants/Atomic/rules.html
@@ -1,3 +1,6 @@
-The capturer explodes after each capture, as well as all pieces standing on the adjacent squares - pawns excepted.
+
+ The capturer explodes after each capture, as well as all pieces standing
+ on the adjacent squares - pawns excepted.
+
Win by checkmate or by exploding the enemy king.
diff --git a/variants/Balanced/rules.html b/variants/Balanced/rules.html
index 6d5fa49..e373943 100644
--- a/variants/Balanced/rules.html
+++ b/variants/Balanced/rules.html
@@ -1,3 +1,6 @@
-White plays first, then Black plays two moves, then White plays two moves, and after that the game proceeds normally.
+
+ White plays first, then Black plays two moves, then White plays two moves,
+ and after that the game proceeds normally.
+
See the (draft) article .
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index c527b8c..29fdee9 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -112,7 +112,22 @@ export default class ChakartRules extends ChessRules {
't': {"class": ["immobilized", "queen"]},
'l': {"class": ["immobilized", "king"]}
};
- return Object.assign({}, specials, bowsered, super.pieces(color, x, y));
+ return Object.assign(
+ {
+ 'y': {
+ // Virtual piece for "king remote shell captures"
+ moves: [],
+ attack: [
+ {
+ steps: [
+ [0, 1], [0, -1], [1, 0], [-1, 0],
+ [1, 1], [1, -1], [-1, 1], [-1, -1]
+ ]
+ }
+ ]
+ }
+ },
+ specials, bowsered, super.pieces(color, x, y));
}
genRandInitFen(seed) {
@@ -255,7 +270,7 @@ export default class ChakartRules extends ChessRules {
case 'b':
case 'r':
// Explicitely listing types to avoid moving immobilized piece
- moves = super.getPotentialMovesOf(piece, [x, y]);
+ moves = this.getPotentialMovesOf(piece, [x, y]);
break;
}
}
@@ -295,33 +310,39 @@ export default class ChakartRules extends ChessRules {
}
}
for (let shiftY of [-1, 1]) {
+ const nextY = this.getY(y + shiftY);
if (
- y + shiftY >= 0 &&
- y + shiftY < this.size.y &&
- this.board[x + shiftX][y + shiftY] != "" &&
+ nextY >= 0 &&
+ nextY < this.size.y &&
+ this.board[x + shiftX][nextY] != "" &&
// Pawns cannot capture invisible queen this way!
- this.getPiece(x + shiftX, y + shiftY) != 'i' &&
- ['a', oppCol].includes(this.getColor(x + shiftX, y + shiftY))
+ this.getPiece(x + shiftX, nextY) != 'i' &&
+ ['a', oppCol].includes(this.getColor(x + shiftX, nextY))
) {
- moves.push(this.getBasicMove([x, y], [x + shiftX, y + shiftY]));
+ moves.push(this.getBasicMove([x, y], [x + shiftX, nextY]));
}
}
- super.pawnPostProcess(moves, color, oppCol);
- // Add mushroom on before-last square
+ this.pawnPostProcess(moves, color, oppCol);
+ // Add mushroom on before-last square (+ potential segments)
moves.forEach(m => {
- let revStep = [m.start.x - m.end.x, m.start.y - m.end.y];
- for (let i of [0, 1])
- revStep[i] = revStep[i] / Math.abs(revStep[i]) || 0;
- const [blx, bly] = [m.end.x + revStep[0], m.end.y + revStep[1]];
- m.appear.push(new PiPo({x: blx, y: bly, c: 'a', p: 'm'}));
- if (blx != x && this.board[blx][bly] != "") {
+ let [mx, my] = [x, y];
+ if (Math.abs(m.end.x - m.start.x) == 2)
+ mx = (m.start.x + m.end.x) / 2;
+ m.appear.push(new PiPo({x: mx, y: my, c: 'a', p: 'm'}));
+ if (mx != x && this.board[mx][my] != "") {
m.vanish.push(new PiPo({
- x: blx,
- y: bly,
- c: this.getColor(blx, bly),
- p: this.getPiece(blx, bly)
+ x: mx,
+ y: my,
+ c: this.getColor(mx, my),
+ p: this.getPiece(mx, my)
}));
}
+ if (Math.abs(m.end.y - m.start.y) > 1) {
+ m.segments = [
+ [[x, y], [x, y]],
+ [[m.end.x, m.end.y], [m.end.x, m.end.y]]
+ ];
+ }
});
return moves;
}
@@ -360,30 +381,15 @@ export default class ChakartRules extends ChessRules {
let moves = this.getPotentialMovesOf('k', [x, y]);
// If flag allows it, add 'remote shell captures'
if (this.powerFlags[this.turn]['k']) {
- super.pieces()['k'].moves[0].steps.forEach(step => {
- let [i, j] = [x + step[0], y + step[1]];
- while (this.onBoard(i, j) && this.canStepOver(i, j)) {
- i += step[0];
- j += step[1];
- }
- if (this.onBoard(i, j)) {
- const colIJ = this.getColor(i, j);
- if (colIJ != this.turn) {
- // May just destroy a bomb or banana:
- let shellCapture = new Move({
- start: {x: x, y: y},
- end: {x: i, y: j},
- appear: [],
- vanish: [
- new PiPo({x: i, y: j, c: colIJ, p: this.getPiece(i, j)})
- ]
- });
- shellCapture.shell = true; //easier play()
- shellCapture.choice = 'z'; //to display in showChoices()
- moves.push(shellCapture);
- }
- }
+ let shellCaptures = this.getPotentialMovesOf('y', [x, y]);
+ shellCaptures.forEach(sc => {
+ sc.shell = true; //easier play()
+ sc.choice = 'z'; //to display in showChoices()
+ // Fix move (Rifle style):
+ sc.vanish.shift();
+ sc.appear.shift();
});
+ Array.prototype.push.apply(moves, shellCaptures);
}
return moves;
}
@@ -619,7 +625,6 @@ export default class ChakartRules extends ChessRules {
p: this.getPiece(move.start.x, move.start.y)
}));
}
- em.koopa = true; //to cancel mushroom effect
break;
case "chomp":
// Eat piece
@@ -640,7 +645,7 @@ export default class ChakartRules extends ChessRules {
}
getMushroomEffect(move) {
- if (move.koopa || typeof move.start.x == "string")
+ if (typeof move.start.x == "string") //drop move (toadette)
return null;
let step = [move.end.x - move.start.x, move.end.y - move.start.y];
if ([0, 1].some(i => Math.abs(step[i]) >= 2 && Math.abs(step[1-i]) != 1)) {
diff --git a/variants/Chakart/complete_rules.html b/variants/Chakart/complete_rules.html
new file mode 100644
index 0000000..c65158e
--- /dev/null
+++ b/variants/Chakart/complete_rules.html
@@ -0,0 +1 @@
+TODO
diff --git a/variants/Chakart/rules.html b/variants/Chakart/rules.html
index f2e5068..e48a4f7 100644
--- a/variants/Chakart/rules.html
+++ b/variants/Chakart/rules.html
@@ -1,4 +1,7 @@
-Pawn, Knight, Bishop and Rook add an object on the board every time they move...
+
+ Pawn, Knight, Bishop and Rook add an object on the board
+ every time they move. King and Queen have special powers.
+
Mushrooms speed-up your pieces.
@@ -7,6 +10,6 @@
Eggs hide either a bonus or malus: see full description.
-Full rules description .
+Full rules description .
Charlotte Blard & Benjamin Auder (2020).
diff --git a/variants/Chess960/rules.html b/variants/Chess960/rules.html
index 46751e1..776fa4a 100644
--- a/variants/Chess960/rules.html
+++ b/variants/Chess960/rules.html
@@ -1 +1,3 @@
-Orthodox chess rules .
+
+ Orthodox chess rules.
+
diff --git a/variants/Cylinder/rules.html b/variants/Cylinder/rules.html
index 6a9db5b..49ea15f 100644
--- a/variants/Cylinder/rules.html
+++ b/variants/Cylinder/rules.html
@@ -1 +1,4 @@
-Columns 'a' and 'h' communicate: a king on h3 can also go to a2, a3 and a4.
+
+ Columns 'a' and 'h' communicate:
+ a king on h3 can also go to a2, a3 and a4.
+
diff --git a/variants/Hex/rules.html b/variants/Hex/rules.html
index 33be5a2..04e700d 100644
--- a/variants/Hex/rules.html
+++ b/variants/Hex/rules.html
@@ -1,5 +1,7 @@
Win by connecting both edges of your color.
-Detailed rules.
+
+ Detailed rules.
+
Piet Hein (1942).
diff --git a/variants/Progressive/rules.html b/variants/Progressive/rules.html
index ee9e9b0..51ec7f2 100644
--- a/variants/Progressive/rules.html
+++ b/variants/Progressive/rules.html
@@ -1 +1,4 @@
-White play one move, then Black play two in a row, then White play 3, and so on.
+
+ White play one move, then Black play two in a row,
+ then White play 3, and so on.
+
diff --git a/variants/Refusal/class.js b/variants/Refusal/class.js
new file mode 100644
index 0000000..2cd9ca6
--- /dev/null
+++ b/variants/Refusal/class.js
@@ -0,0 +1,128 @@
+import ChessRules from "/base_rules.js";
+
+export default class RefusalRules extends ChessRules {
+
+ static get Options() {
+ return {
+ select: C.Options.select,
+ input: [
+ {
+ label: "Refuse any",
+ variable: "refuseany",
+ type: "checkbox",
+ defaut: true
+ }
+ ],
+ styles: ["cylinder"]
+ };
+ }
+
+ get hasFlags() {
+ return false;
+ }
+
+ genRandInitFen(seed) {
+ return super.genRandInitFen(seed).slice(0, -1) + ',"lastmove":"null"}';
+ }
+
+ getFen() {
+ return (
+ super.getFen().slice(0, -1) + ',"lastmove":"' +
+ JSON.stringify(this.lastMove) + '"}');
+ }
+
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
+ this.lastMove = JSON.parse(fenParsed.lastmove);
+ if (!this.lastMove) {
+ // Fill with empty values to avoid checking lastMove != null
+ this.lastMove = {
+ start: {x: -1, y: -1}, end: {x: -1, y: -1}, vanish: [{c: ''}]
+ };
+ }
+ }
+
+ canIplay(x, y) {
+ if (super.canIplay(x, y))
+ return true;
+ // Check if playing last move, reversed:
+ const lm = this.lastMove;
+ return (!lm.noRef && x == lm.end.x && y == lm.end.y);
+ }
+
+ getPotentialMovesFrom([x, y]) {
+ const moveColor = this.getColor(x, y);
+ if (moveColor != this.turn) {
+ let revLm = JSON.parse(JSON.stringify(this.lastMove));
+ [revLm.appear, revLm.vanish] = [revLm.vanish, revLm.appear];
+ [revLm.start, revLm.end] = [revLm.end, revLm.start];
+ if (!this.options["refuseany"]) {
+ // After refusing this move, can my opponent play a different move?
+ this.playOnBoard(revLm);
+ let totOppMoves = 0;
+ outerLoop: for (let i=0; i= 2)
+ break outerLoop;
+ }
+ }
+ }
+ this.undoOnBoard(revLm);
+ if (totOppMoves <= 1)
+ return [];
+ }
+ // Also reverse segments in Cylinder mode:
+ if (this.options["cylinder"])
+ revLm.segments = revLm.segments.map(seg => [seg[1], seg[0]]);
+ else
+ delete revLm["segments"];
+ revLm.refusal = true;
+ revLm.noRef = true; //cannot refuse a refusal move :)
+ return [revLm];
+ }
+ return super.getPotentialMovesFrom([x, y]);
+ }
+
+ getEpSquare(move) {
+ if (!move.refusal)
+ return super.getEpSquare(move);
+ return null;
+ }
+
+ filterValid(moves) {
+ const color = this.turn;
+ const lm = this.lastMove;
+ let rMoves = moves.filter(m => {
+ return (
+ !lm.refusal || //it's my first move attempt on this turn
+ m.start.x != lm.end.x || m.start.y != lm.end.y ||
+ m.end.x != lm.start.x || m.end.y != lm.start.y ||
+ // Doing the same move again: maybe pawn promotion?
+ (m.vanish[0].p == 'p' && m.appear[0].p != lm.appear[0].p)
+ );
+ });
+ return super.filterValid(rMoves);
+ }
+
+ prePlay(move) {
+ if (!move.noRef)
+ // My previous move was already refused?
+ move.noRef = this.lastMove.vanish[0].c == this.turn;
+ }
+
+ postPlay(move) {
+ this.lastMove = move;
+ super.postPlay(move);
+ }
+
+ atLeastOneMove() {
+ if (!this.lastMove.noRef)
+ return true;
+ return super.atLeastOneMove();
+ }
+
+};
diff --git a/variants/Refusal/rules.html b/variants/Refusal/rules.html
new file mode 100644
index 0000000..8efc3ce
--- /dev/null
+++ b/variants/Refusal/rules.html
@@ -0,0 +1,6 @@
+
+ At each turn, you can refuse one opponent move.
+ Different pawn promotions count as different moves.
+
+
+Fred Galvin (1958).
diff --git a/variants/Refusal/style.css b/variants/Refusal/style.css
new file mode 100644
index 0000000..a3550bc
--- /dev/null
+++ b/variants/Refusal/style.css
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
diff --git a/variants/Suction/class.js b/variants/Suction/class.js
index db10e58..f9cf26d 100644
--- a/variants/Suction/class.js
+++ b/variants/Suction/class.js
@@ -53,7 +53,7 @@ export default class SuctionRules extends ChessRules {
const cmoveFen = !this.cmove
? "-"
: C.CoordsToSquare(this.cmove.start) + C.CoordsToSquare(this.cmove.end);
- return super.getFen().slice(0, -1) + ',"' + cmoveFen + '"}';
+ return super.getFen().slice(0, -1) + ',"cmove":"' + cmoveFen + '"}';
}
getBasicMove([sx, sy], [ex, ey]) {
diff --git a/variants/Suction/rules.html b/variants/Suction/rules.html
index 09a70cb..4006ce0 100644
--- a/variants/Suction/rules.html
+++ b/variants/Suction/rules.html
@@ -1,4 +1,7 @@
-Pieces are swapped after captures. Kings cannot move except by being captured.
+
+ Pieces are swapped after captures.
+ Kings cannot move except by being captured.
+
Win by bringing the enemy king on your first rank.
diff --git a/variants/Zen/rules.html b/variants/Zen/rules.html
index 1508496..0386371 100644
--- a/variants/Zen/rules.html
+++ b/variants/Zen/rules.html
@@ -1,4 +1,7 @@
-Pieces capture enemy units which threaten them (normal captures are disabled).
+
+ Pieces capture enemy units which threaten them
+ (normal captures are disabled).
+
Exception: the king is attacked as usual.
--
2.44.0
From 0a36d31a3e4fd356624f70058cbafc135fbf6fba Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Tue, 21 Jun 2022 10:08:42 +0200
Subject: [PATCH 07/16] Start Ambiguous
---
variants/Ambiguous/class.js | 166 ++++++++++++++++++++++++++++++++++++
1 file changed, 166 insertions(+)
create mode 100644 variants/Ambiguous/class.js
diff --git a/variants/Ambiguous/class.js b/variants/Ambiguous/class.js
new file mode 100644
index 0000000..78d3d63
--- /dev/null
+++ b/variants/Ambiguous/class.js
@@ -0,0 +1,166 @@
+import ChessRules from "/base_rules.js";
+import { randInt, shuffle } from "@/utils/alea";
+import { ArrayFun } from "@/utils/array";
+
+export default class AmbiguousRules extends ChessRules {
+
+ // TODO: options
+
+ get hasFlags() {
+ return false;
+ }
+
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
+ if (this.movesCount == 0)
+ this.subTurn = 2;
+ else
+ this.subTurn = 1;
+ }
+
+ genRandInitFen(seed) {
+ const gr = new GiveawayRules(
+ {mode: "suicide", options: this.options, genFenOnly: true});
+ return gr.genRandInitFen(seed);
+ }
+
+ // Subturn 1: play a move for the opponent on the designated square.
+ // Subturn 2: play a move for me (which just indicate a square).
+ getPotentialMovesFrom([x, y]) {
+ const color = this.turn;
+ const oppCol = V.GetOppCol(color);
+ if (this.subTurn == 2) {
+ // Just play a normal move (which in fact only indicate a square)
+ let movesHash = {};
+ return (
+ super.getPotentialMovesFrom([x, y])
+ .filter(m => {
+ // Filter promotions: keep only one, since no choice now.
+ if (m.appear[0].p != m.vanish[0].p) {
+ const hash = V.CoordsToSquare(m.start) + V.CoordsToSquare(m.end);
+ if (!movesHash[hash]) {
+ movesHash[hash] = true;
+ return true;
+ }
+ return false;
+ }
+ return true;
+ })
+ .map(m => {
+ if (m.vanish.length == 1) m.appear[0].p = V.GOAL;
+ else m.appear[0].p = V.TARGET_CODE[m.vanish[1].p];
+ m.appear[0].c = oppCol;
+ m.vanish.shift();
+ return m;
+ })
+ );
+ }
+ // At subTurn == 1, play a targeted move for opponent
+ // Search for target (we could also have it in a stack...)
+ let target = { x: -1, y: -1 };
+ outerLoop: 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 piece = this.board[i][j][1];
+ if (
+ piece == V.GOAL ||
+ Object.keys(V.TARGET_DECODE).includes(piece)
+ ) {
+ target = { x: i, y: j};
+ break outerLoop;
+ }
+ }
+ }
+ }
+ // TODO: could be more efficient than generating all moves.
+ this.turn = oppCol;
+ const emptyTarget = (this.board[target.x][target.y][1] == V.GOAL);
+ if (emptyTarget) this.board[target.x][target.y] = V.EMPTY;
+ let moves = super.getPotentialMovesFrom([x, y]);
+ if (emptyTarget) {
+ this.board[target.x][target.y] = color + V.GOAL;
+ moves.forEach(m => {
+ m.vanish.push({
+ x: target.x,
+ y: target.y,
+ c: color,
+ p: V.GOAL
+ });
+ });
+ }
+ this.turn = color;
+ return moves.filter(m => m.end.x == target.x && m.end.y == target.y);
+ }
+
+ canIplay(x, y) {
+ const color = this.getColor(x, y);
+ return (
+ (this.subTurn == 1 && ![this.turn, this.playerColor].includes(color)) ||
+ (this.subTurn == 2 && super.canIplay(x, y))
+ );
+ }
+
+ // Code for empty square target
+ static get GOAL() {
+ return 'g';
+ }
+
+ static get TARGET_DECODE() {
+ return {
+ 's': 'p',
+ 't': 'q',
+ 'u': 'r',
+ 'o': 'n',
+ 'c': 'b',
+ 'l': 'k'
+ };
+ }
+
+ static get TARGET_CODE() {
+ return {
+ 'p': 's',
+ 'q': 't',
+ 'r': 'u',
+ 'n': 'o',
+ 'b': 'c',
+ 'k': 'l'
+ };
+ }
+
+ pieces() {
+ // .........
+ }
+
+ atLeastOneMove() {
+ // Since there are no checks this seems true (same as for Magnetic...)
+ return true;
+ }
+
+ filterValid(moves) {
+ return moves;
+ }
+
+ getCurrentScore() {
+ // This function is only called at subTurn 1
+ const color = V.GetOppCol(this.turn);
+ if (this.kingPos[color][0] < 0) return (color == 'w' ? "0-1" : "1-0");
+ return "*";
+ }
+
+ play(move) {
+ let kingCaptured = false;
+ if (this.subTurn == 1) {
+ this.prePlay(move);
+ this.epSquares.push(this.getEpSquare(move));
+ kingCaptured = this.kingPos[this.turn][0] < 0;
+ }
+ if (kingCaptured) move.kingCaptured = true;
+ V.PlayOnBoard(this.board, move);
+ if (this.subTurn == 2 || kingCaptured) {
+ this.turn = V.GetOppCol(this.turn);
+ this.movesCount++;
+ }
+ if (!kingCaptured) this.subTurn = 3 - this.subTurn;
+ }
+
+};
--
2.44.0
From 554e3ad3773a3123701bd894db1df4c1843283b8 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Tue, 21 Jun 2022 14:25:59 +0200
Subject: [PATCH 08/16] Add Ambiguous. Fix a few issues with FEN generation /
options
---
app.js | 5 +-
pieces/ambiguous_target.svg | 191 ++++++++++++++++++++++++++++++++++
server.js | 16 ++-
variants.js | 2 +-
variants/Ambiguous/class.js | 107 ++++++++++---------
variants/Ambiguous/rules.html | 11 ++
variants/Ambiguous/style.css | 43 ++++++++
variants/Chakart/class.js | 6 +-
variants/Suction/class.js | 4 +-
9 files changed, 316 insertions(+), 69 deletions(-)
create mode 100644 pieces/ambiguous_target.svg
create mode 100644 variants/Ambiguous/rules.html
create mode 100644 variants/Ambiguous/style.css
diff --git a/app.js b/app.js
index 7d387c5..4c0b5e4 100644
--- a/app.js
+++ b/app.js
@@ -545,8 +545,9 @@ function initializeGame(obj) {
break;
}
}
- fillGameInfos(obj, playerColor == "w" ? 1 : 0);
- if (obj.randvar)
+ const playerIndex = (playerColor == "w" ? 0 : 1);
+ fillGameInfos(obj, 1 - playerIndex);
+ if (obj.players[playerIndex].randvar)
toggleVisible("gameInfos");
else
toggleVisible("boardContainer");
diff --git a/pieces/ambiguous_target.svg b/pieces/ambiguous_target.svg
new file mode 100644
index 0000000..53f0ccc
--- /dev/null
+++ b/pieces/ambiguous_target.svg
@@ -0,0 +1,191 @@
+
+
+
+ Target
+
+
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+ Openclipart
+
+
+ Target
+ 2012-02-15T07:37:04
+ Target symbol
+ https://openclipart.org/detail/168253/target-by-fanda@cz
+
+
+ Fanda@CZ
+
+
+
+
+ target
+
+
+
+
+
+
+
+
+
+
+
diff --git a/server.js b/server.js
index 7e1a99b..dc8bd97 100644
--- a/server.js
+++ b/server.js
@@ -37,15 +37,12 @@ function initializeGame(vname, players, options) {
function launchGame(gid) {
moveHash[gid] = {};
const gameInfo = Object.assign(
- {seed: Math.floor(Math.random() * 1984), gid: gid},
+ {seed: Math.floor(Math.random() * 19840), gid: gid},
games[gid]
);
// players array is supposed to be full:
- for (const p of games[gid].players) {
- send(p.sid,
- "gamestart",
- Object.assign({randvar: p.randvar}, gameInfo));
- }
+ for (const p of games[gid].players)
+ send(p.sid, "gamestart", gameInfo);
}
function getRandomVariant() {
@@ -137,11 +134,10 @@ wss.on("connection", (socket, req) => {
const allrand = games[obj.gid].rematch.every(r => r == 2);
if (allrand)
vname = getRandomVariant();
- games[obj.gid].players.forEach(p =>
- p.randvar = allrand ? true : false);
+ games[obj.gid].players.forEach(p => p.randvar = allrand);
const gid = initializeGame(vname,
- games[obj.gid].players.reverse(),
- games[obj.gid].options);
+ games[obj.gid].players.reverse(),
+ games[obj.gid].options);
launchGame(gid);
}
}
diff --git a/variants.js b/variants.js
index aafd02a..c9af9e4 100644
--- a/variants.js
+++ b/variants.js
@@ -5,7 +5,7 @@ const variants = [
// {name: 'Alice', desc: 'Both sides of the mirror'},
// {name: 'Align4', desc: 'Align four pawns'},
// {name: 'Allmate', desc: 'Mate any piece'},
-// {name: 'Ambiguous', desc: "Play opponent's pieces"},
+ {name: 'Ambiguous', desc: "Play opponent's pieces"},
// {name: 'Antiking1', desc: 'Keep antiking in check', disp: 'Anti-King'},
// {name: 'Antimatter', desc: 'Dangerous collisions'},
// {name: 'Apocalypse', desc: 'The end of the world'},
diff --git a/variants/Ambiguous/class.js b/variants/Ambiguous/class.js
index 78d3d63..6a001a6 100644
--- a/variants/Ambiguous/class.js
+++ b/variants/Ambiguous/class.js
@@ -1,10 +1,14 @@
import ChessRules from "/base_rules.js";
-import { randInt, shuffle } from "@/utils/alea";
-import { ArrayFun } from "@/utils/array";
+import GiveawayRules from "/variants/Giveaway/class.js";
export default class AmbiguousRules extends ChessRules {
- // TODO: options
+ static get Options() {
+ return {
+ select: C.Options.select,
+ styles: ["cylinder"]
+ };
+ }
get hasFlags() {
return false;
@@ -19,25 +23,29 @@ export default class AmbiguousRules extends ChessRules {
}
genRandInitFen(seed) {
- const gr = new GiveawayRules(
- {mode: "suicide", options: this.options, genFenOnly: true});
+ const options = Object.assign({mode: "suicide"}, this.options);
+ const gr = new GiveawayRules({options: options, genFenOnly: true});
return gr.genRandInitFen(seed);
}
+ canStepOver(x, y) {
+ return this.board[x][y] == "" || this.getPiece(x, y) == V.GOAL;
+ }
+
// Subturn 1: play a move for the opponent on the designated square.
// Subturn 2: play a move for me (which just indicate a square).
getPotentialMovesFrom([x, y]) {
const color = this.turn;
- const oppCol = V.GetOppCol(color);
+ const oppCol = C.GetOppCol(color);
if (this.subTurn == 2) {
// Just play a normal move (which in fact only indicate a square)
let movesHash = {};
return (
super.getPotentialMovesFrom([x, y])
.filter(m => {
- // Filter promotions: keep only one, since no choice now.
+ // Filter promotions: keep only one, since no choice for now.
if (m.appear[0].p != m.vanish[0].p) {
- const hash = V.CoordsToSquare(m.start) + V.CoordsToSquare(m.end);
+ const hash = C.CoordsToSquare(m.start) + C.CoordsToSquare(m.end);
if (!movesHash[hash]) {
movesHash[hash] = true;
return true;
@@ -47,48 +55,37 @@ export default class AmbiguousRules extends ChessRules {
return true;
})
.map(m => {
- if (m.vanish.length == 1) m.appear[0].p = V.GOAL;
- else m.appear[0].p = V.TARGET_CODE[m.vanish[1].p];
- m.appear[0].c = oppCol;
+ if (m.vanish.length == 1) {
+ m.appear[0].c = 'a'; //a-color
+ m.appear[0].p = V.GOAL;
+ }
+ else {
+ m.appear[0].p = V.TARGET_CODE[m.vanish[1].p];
+ m.appear[0].c = oppCol;
+ }
m.vanish.shift();
return m;
})
);
}
- // At subTurn == 1, play a targeted move for opponent
+ // At subTurn == 1, play a targeted move for the opponent.
// Search for target (we could also have it in a stack...)
- let target = { x: -1, y: -1 };
- outerLoop: 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 piece = this.board[i][j][1];
+ let target = {x: -1, y: -1};
+ outerLoop: for (let i = 0; i < this.size.x; i++) {
+ for (let j = 0; j < this.size.y; j++) {
+ if (this.board[i][j] != "") {
+ const piece = this.getPiece(i, j);
if (
piece == V.GOAL ||
Object.keys(V.TARGET_DECODE).includes(piece)
) {
- target = { x: i, y: j};
+ target = {x: i, y:j};
break outerLoop;
}
}
}
}
- // TODO: could be more efficient than generating all moves.
- this.turn = oppCol;
- const emptyTarget = (this.board[target.x][target.y][1] == V.GOAL);
- if (emptyTarget) this.board[target.x][target.y] = V.EMPTY;
- let moves = super.getPotentialMovesFrom([x, y]);
- if (emptyTarget) {
- this.board[target.x][target.y] = color + V.GOAL;
- moves.forEach(m => {
- m.vanish.push({
- x: target.x,
- y: target.y,
- c: color,
- p: V.GOAL
- });
- });
- }
- this.turn = color;
+ const moves = super.getPotentialMovesFrom([x, y], oppCol);
return moves.filter(m => m.end.x == target.x && m.end.y == target.y);
}
@@ -127,8 +124,17 @@ export default class AmbiguousRules extends ChessRules {
};
}
- pieces() {
- // .........
+ pieces(color, x, y) {
+ const targets = {
+ 's': {"class": "target-pawn", moves: []},
+ 'u': {"class": "target-rook", moves: []},
+ 'o': {"class": "target-knight", moves: []},
+ 'c': {"class": "target-bishop", moves: []},
+ 't': {"class": "target-queen", moves: []},
+ 'l': {"class": "target-king", moves: []}
+ };
+ return Object.assign(
+ { 'g': {"class": "target"} }, targets, super.pieces(color, x, y));
}
atLeastOneMove() {
@@ -140,27 +146,26 @@ export default class AmbiguousRules extends ChessRules {
return moves;
}
+ isKing(symbol) {
+ return ['k', 'l'].includes(symbol);
+ }
+
getCurrentScore() {
// This function is only called at subTurn 1
- const color = V.GetOppCol(this.turn);
- if (this.kingPos[color][0] < 0) return (color == 'w' ? "0-1" : "1-0");
+ const color = C.GetOppCol(this.turn);
+ const kingPos = this.searchKingPos(color);
+ if (kingPos[0] < 0)
+ return (color == 'w' ? "0-1" : "1-0");
return "*";
}
- play(move) {
- let kingCaptured = false;
- if (this.subTurn == 1) {
- this.prePlay(move);
- this.epSquares.push(this.getEpSquare(move));
- kingCaptured = this.kingPos[this.turn][0] < 0;
- }
- if (kingCaptured) move.kingCaptured = true;
- V.PlayOnBoard(this.board, move);
- if (this.subTurn == 2 || kingCaptured) {
- this.turn = V.GetOppCol(this.turn);
+ postPlay(move) {
+ const color = this.turn;
+ if (this.subTurn == 2 || this.searchKingPos(color)[0] < 0) {
+ this.turn = C.GetOppCol(color);
this.movesCount++;
}
- if (!kingCaptured) this.subTurn = 3 - this.subTurn;
+ this.subTurn = 3 - this.subTurn;
}
};
diff --git a/variants/Ambiguous/rules.html b/variants/Ambiguous/rules.html
new file mode 100644
index 0000000..b51c747
--- /dev/null
+++ b/variants/Ambiguous/rules.html
@@ -0,0 +1,11 @@
+
+ Every move you play can be changed by your opponent by a move arriving
+ on the same square.
+
+
+
+ Consequently, you play twice on each turn: first to select a move for
+ your opponent, then to choose one for you - which could be altered.
+
+
+Fabrice Liardet (2005).
diff --git a/variants/Ambiguous/style.css b/variants/Ambiguous/style.css
new file mode 100644
index 0000000..e31d810
--- /dev/null
+++ b/variants/Ambiguous/style.css
@@ -0,0 +1,43 @@
+@import url("/base_pieces.css");
+
+piece.target {
+ background-image: url('/pieces/ambiguous_target.svg');
+}
+
+piece.white.target-pawn {
+ background-image: url('/pieces/yellow_pawn.svg');
+}
+piece.white.target-rook {
+ background-image: url('/pieces/yellow_rook.svg');
+}
+piece.white.target-knight {
+ background-image: url('/pieces/yellow_knight.svg');
+}
+piece.white.target-bishop {
+ background-image: url('/pieces/yellow_bishop.svg');
+}
+piece.white.target-queen {
+ background-image: url('/pieces/yellow_queen.svg');
+}
+piece.white.target-king {
+ background-image: url('/pieces/yellow_king.svg');
+}
+
+piece.black.target-pawn {
+ background-image: url('/pieces/red_pawn.svg');
+}
+piece.black.target-rook {
+ background-image: url('/pieces/red_rook.svg');
+}
+piece.black.target-knight {
+ background-image: url('/pieces/red_knight.svg');
+}
+piece.black.target-bishop {
+ background-image: url('/pieces/red_bishop.svg');
+}
+piece.black.target-queen {
+ background-image: url('/pieces/red_queen.svg');
+}
+piece.black.target-king {
+ background-image: url('/pieces/red_king.svg');
+}
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index 29fdee9..37261e4 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -131,8 +131,8 @@ export default class ChakartRules extends ChessRules {
}
genRandInitFen(seed) {
- const gr = new GiveawayRules(
- {mode: "suicide", options: this.options, genFenOnly: true});
+ const options = Object.assign({mode: "suicide"}, this.options);
+ const gr = new GiveawayRules({options: options, genFenOnly: true});
// Add Peach + mario flags
return gr.genRandInitFen(seed).slice(0, -17) + '{"flags":"1111"}';
}
@@ -180,7 +180,7 @@ export default class ChakartRules extends ChessRules {
this.moveStack = [];
// Change seed (after FEN generation!!)
// so that further calls differ between players:
- Random.setSeed(Math.floor(10000 * Math.random()));
+ Random.setSeed(Math.floor(19840 * Math.random()));
}
// For Toadette bonus
diff --git a/variants/Suction/class.js b/variants/Suction/class.js
index f9cf26d..e2e36c8 100644
--- a/variants/Suction/class.js
+++ b/variants/Suction/class.js
@@ -42,8 +42,8 @@ export default class SuctionRules extends ChessRules {
}
genRandInitFen(seed) {
- const gr = new GiveawayRules(
- {mode: "suicide", options: this.options, genFenOnly: true});
+ const options = Object.assign({mode: "suicide"}, this.options);
+ const gr = new GiveawayRules({options: options, genFenOnly: true});
// Add empty cmove:
return (
gr.genRandInitFen(seed).slice(0, -17) + '{"enpassant":"-","cmove":"-"}');
--
2.44.0
From f55a0a6753a62257c7caa62ae49c8a5673769065 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Tue, 21 Jun 2022 16:30:44 +0200
Subject: [PATCH 09/16] Add Alapo (unfinished). Reorganise pieces folder
---
TODO | 5 +-
base_rules.js | 24 +--
pieces/Alapo/black_CIRCLE.svg | 5 +
pieces/Alapo/black_SQUARE.svg | 5 +
pieces/Alapo/black_TRIANGLE.svg | 5 +
pieces/Alapo/black_TRIANGLE_inv.svg | 5 +
pieces/Alapo/black_circle.svg | 5 +
pieces/Alapo/black_square.svg | 5 +
pieces/Alapo/black_triangle.svg | 5 +
pieces/Alapo/black_triangle_inv.svg | 5 +
pieces/Alapo/white_CIRCLE.svg | 5 +
pieces/Alapo/white_SQUARE.svg | 5 +
pieces/Alapo/white_TRIANGLE.svg | 5 +
pieces/Alapo/white_TRIANGLE_inv.svg | 5 +
pieces/Alapo/white_circle.svg | 5 +
pieces/Alapo/white_square.svg | 5 +
pieces/Alapo/white_triangle.svg | 5 +
pieces/Alapo/white_triangle_inv.svg | 5 +
.../target.svg} | 0
.../banana.svg} | 0
pieces/{chakart_bomb.svg => Chakart/bomb.svg} | 0
pieces/{chakart_egg.svg => Chakart/egg.svg} | 0
.../mushroom.svg} | 0
.../mystery_black.svg} | 0
.../mystery_white.svg} | 0
.../{chakart_shell.svg => Chakart/shell.svg} | 0
variants.js | 2 +-
variants/Alapo/class.js | 156 ++++++++++++++++++
variants/Alapo/rules.html | 7 +
variants/Alapo/style.css | 49 ++++++
variants/Ambiguous/CREDITS | 3 +
variants/Ambiguous/style.css | 2 +-
variants/Chakart/style.css | 14 +-
33 files changed, 320 insertions(+), 22 deletions(-)
create mode 100644 pieces/Alapo/black_CIRCLE.svg
create mode 100644 pieces/Alapo/black_SQUARE.svg
create mode 100644 pieces/Alapo/black_TRIANGLE.svg
create mode 100644 pieces/Alapo/black_TRIANGLE_inv.svg
create mode 100644 pieces/Alapo/black_circle.svg
create mode 100644 pieces/Alapo/black_square.svg
create mode 100644 pieces/Alapo/black_triangle.svg
create mode 100644 pieces/Alapo/black_triangle_inv.svg
create mode 100644 pieces/Alapo/white_CIRCLE.svg
create mode 100644 pieces/Alapo/white_SQUARE.svg
create mode 100644 pieces/Alapo/white_TRIANGLE.svg
create mode 100644 pieces/Alapo/white_TRIANGLE_inv.svg
create mode 100644 pieces/Alapo/white_circle.svg
create mode 100644 pieces/Alapo/white_square.svg
create mode 100644 pieces/Alapo/white_triangle.svg
create mode 100644 pieces/Alapo/white_triangle_inv.svg
rename pieces/{ambiguous_target.svg => Ambiguous/target.svg} (100%)
rename pieces/{chakart_banana.svg => Chakart/banana.svg} (100%)
rename pieces/{chakart_bomb.svg => Chakart/bomb.svg} (100%)
rename pieces/{chakart_egg.svg => Chakart/egg.svg} (100%)
rename pieces/{chakart_mushroom.svg => Chakart/mushroom.svg} (100%)
rename pieces/{chakart_mystery_black.svg => Chakart/mystery_black.svg} (100%)
rename pieces/{chakart_mystery_white.svg => Chakart/mystery_white.svg} (100%)
rename pieces/{chakart_shell.svg => Chakart/shell.svg} (100%)
create mode 100644 variants/Alapo/class.js
create mode 100644 variants/Alapo/rules.html
create mode 100644 variants/Alapo/style.css
create mode 100644 variants/Ambiguous/CREDITS
diff --git a/TODO b/TODO
index d879b34..73b76b0 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,7 @@
+Debug Alapo
+Add links to complete variants rules (Ambiguous...)
+
add variants :
-Ambiguous
-Refusal
Dark Racing Kings ? Checkered-Teleport ?
Otage, Emergo, Pacosako : fonction "buildPiece(arg1, arg2)" returns HTML element with 2 SVG or SVG + number
diff --git a/base_rules.js b/base_rules.js
index 1b22ae5..c19ee64 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -569,15 +569,16 @@ export default class ChessRules {
// Compare window ratio width / height to aspectRatio:
const windowRatio = window.innerWidth / window.innerHeight;
let cbWidth, cbHeight;
- if (windowRatio <= this.size.ratio) {
+ const vRatio = this.size.ratio || 1;
+ if (windowRatio <= vRatio) {
// Limiting dimension is width:
cbWidth = Math.min(window.innerWidth, 767);
- cbHeight = cbWidth / this.size.ratio;
+ cbHeight = cbWidth / vRatio;
}
else {
// Limiting dimension is height:
cbHeight = Math.min(window.innerHeight, 767);
- cbWidth = cbHeight * this.size.ratio;
+ cbWidth = cbHeight * vRatio;
}
if (this.hasReserve) {
const sqSize = cbWidth / this.size.y;
@@ -585,7 +586,7 @@ export default class ChessRules {
// Cannot use getReserveSquareSize() here, but sqSize is an upper bound.
if ((window.innerHeight - cbHeight) / 2 < sqSize + 5) {
cbHeight = window.innerHeight - 2 * (sqSize + 5);
- cbWidth = cbHeight * this.size.ratio;
+ cbWidth = cbHeight * vRatio;
}
}
chessboard.style.width = cbWidth + "px";
@@ -610,7 +611,7 @@ export default class ChessRules {
const flipped = (this.playerColor == 'b');
let board = `
`;
for (let i=0; i < this.size.x; i++) {
for (let j=0; j < this.size.y; j++) {
@@ -798,13 +799,14 @@ export default class ChessRules {
const multFact = (mode == "up" ? 1.05 : 0.95);
let [newWidth, newHeight] = [multFact * r.width, multFact * r.height];
// Stay in window:
+ const vRatio = this.size.ratio || 1;
if (newWidth > window.innerWidth) {
newWidth = window.innerWidth;
- newHeight = newWidth / this.size.ratio;
+ newHeight = newWidth / vRatio;
}
if (newHeight > window.innerHeight) {
newHeight = window.innerHeight;
- newWidth = newHeight * this.size.ratio;
+ newWidth = newHeight * vRatio;
}
chessboard.style.width = newWidth + "px";
chessboard.style.height = newHeight + "px";
@@ -1051,7 +1053,7 @@ export default class ChessRules {
return {
x: 8,
y: 8,
- ratio: 1 //for rectangular board = y / x
+ ratio: 1 //for rectangular board = y / x (optional, 1 = default)
};
}
@@ -2210,9 +2212,9 @@ export default class ChessRules {
this.afterPlay(move); //user method
}
- getMaxDistance(rwidth) {
+ getMaxDistance(r) {
// Works for all rectangular boards:
- return Math.sqrt(rwidth ** 2 + (rwidth / this.size.ratio) ** 2);
+ return Math.sqrt(r.width ** 2 + r.height ** 2);
}
getDomPiece(x, y) {
@@ -2237,7 +2239,7 @@ export default class ChessRules {
movingPiece.style.width = pieceWidth + "px";
movingPiece.style.height = pieceWidth + "px";
}
- const maxDist = this.getMaxDistance(r.width);
+ const maxDist = this.getMaxDistance(r);
const pieces = this.pieces();
if (move.drag) {
const startCode = this.getPiece(move.start.x, move.start.y);
diff --git a/pieces/Alapo/black_CIRCLE.svg b/pieces/Alapo/black_CIRCLE.svg
new file mode 100644
index 0000000..7948061
--- /dev/null
+++ b/pieces/Alapo/black_CIRCLE.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/black_SQUARE.svg b/pieces/Alapo/black_SQUARE.svg
new file mode 100644
index 0000000..fdd9921
--- /dev/null
+++ b/pieces/Alapo/black_SQUARE.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/black_TRIANGLE.svg b/pieces/Alapo/black_TRIANGLE.svg
new file mode 100644
index 0000000..939ba3a
--- /dev/null
+++ b/pieces/Alapo/black_TRIANGLE.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/black_TRIANGLE_inv.svg b/pieces/Alapo/black_TRIANGLE_inv.svg
new file mode 100644
index 0000000..fb3c955
--- /dev/null
+++ b/pieces/Alapo/black_TRIANGLE_inv.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/black_circle.svg b/pieces/Alapo/black_circle.svg
new file mode 100644
index 0000000..8cd85d9
--- /dev/null
+++ b/pieces/Alapo/black_circle.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/black_square.svg b/pieces/Alapo/black_square.svg
new file mode 100644
index 0000000..3e86c70
--- /dev/null
+++ b/pieces/Alapo/black_square.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/black_triangle.svg b/pieces/Alapo/black_triangle.svg
new file mode 100644
index 0000000..8c531b5
--- /dev/null
+++ b/pieces/Alapo/black_triangle.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/black_triangle_inv.svg b/pieces/Alapo/black_triangle_inv.svg
new file mode 100644
index 0000000..e9afcea
--- /dev/null
+++ b/pieces/Alapo/black_triangle_inv.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/white_CIRCLE.svg b/pieces/Alapo/white_CIRCLE.svg
new file mode 100644
index 0000000..af99afa
--- /dev/null
+++ b/pieces/Alapo/white_CIRCLE.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/white_SQUARE.svg b/pieces/Alapo/white_SQUARE.svg
new file mode 100644
index 0000000..716a8e5
--- /dev/null
+++ b/pieces/Alapo/white_SQUARE.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/white_TRIANGLE.svg b/pieces/Alapo/white_TRIANGLE.svg
new file mode 100644
index 0000000..411ec2f
--- /dev/null
+++ b/pieces/Alapo/white_TRIANGLE.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/white_TRIANGLE_inv.svg b/pieces/Alapo/white_TRIANGLE_inv.svg
new file mode 100644
index 0000000..61733b1
--- /dev/null
+++ b/pieces/Alapo/white_TRIANGLE_inv.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/white_circle.svg b/pieces/Alapo/white_circle.svg
new file mode 100644
index 0000000..f5f19cf
--- /dev/null
+++ b/pieces/Alapo/white_circle.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/white_square.svg b/pieces/Alapo/white_square.svg
new file mode 100644
index 0000000..9c7e4e7
--- /dev/null
+++ b/pieces/Alapo/white_square.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/white_triangle.svg b/pieces/Alapo/white_triangle.svg
new file mode 100644
index 0000000..abad0f9
--- /dev/null
+++ b/pieces/Alapo/white_triangle.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/Alapo/white_triangle_inv.svg b/pieces/Alapo/white_triangle_inv.svg
new file mode 100644
index 0000000..4791916
--- /dev/null
+++ b/pieces/Alapo/white_triangle_inv.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pieces/ambiguous_target.svg b/pieces/Ambiguous/target.svg
similarity index 100%
rename from pieces/ambiguous_target.svg
rename to pieces/Ambiguous/target.svg
diff --git a/pieces/chakart_banana.svg b/pieces/Chakart/banana.svg
similarity index 100%
rename from pieces/chakart_banana.svg
rename to pieces/Chakart/banana.svg
diff --git a/pieces/chakart_bomb.svg b/pieces/Chakart/bomb.svg
similarity index 100%
rename from pieces/chakart_bomb.svg
rename to pieces/Chakart/bomb.svg
diff --git a/pieces/chakart_egg.svg b/pieces/Chakart/egg.svg
similarity index 100%
rename from pieces/chakart_egg.svg
rename to pieces/Chakart/egg.svg
diff --git a/pieces/chakart_mushroom.svg b/pieces/Chakart/mushroom.svg
similarity index 100%
rename from pieces/chakart_mushroom.svg
rename to pieces/Chakart/mushroom.svg
diff --git a/pieces/chakart_mystery_black.svg b/pieces/Chakart/mystery_black.svg
similarity index 100%
rename from pieces/chakart_mystery_black.svg
rename to pieces/Chakart/mystery_black.svg
diff --git a/pieces/chakart_mystery_white.svg b/pieces/Chakart/mystery_white.svg
similarity index 100%
rename from pieces/chakart_mystery_white.svg
rename to pieces/Chakart/mystery_white.svg
diff --git a/pieces/chakart_shell.svg b/pieces/Chakart/shell.svg
similarity index 100%
rename from pieces/chakart_shell.svg
rename to pieces/Chakart/shell.svg
diff --git a/variants.js b/variants.js
index c9af9e4..90e9244 100644
--- a/variants.js
+++ b/variants.js
@@ -1,7 +1,7 @@
const variants = [
// TODO: https://mancala.fandom.com/wiki/William_Daniel_Troyka Cleopatra chess
{name: 'Absorption', desc: 'Absorb powers'},
-// {name: 'Alapo', desc: 'Geometric Chess'},
+ {name: 'Alapo', desc: 'Geometric Chess'},
// {name: 'Alice', desc: 'Both sides of the mirror'},
// {name: 'Align4', desc: 'Align four pawns'},
// {name: 'Allmate', desc: 'Mate any piece'},
diff --git a/variants/Alapo/class.js b/variants/Alapo/class.js
new file mode 100644
index 0000000..eb0e73f
--- /dev/null
+++ b/variants/Alapo/class.js
@@ -0,0 +1,156 @@
+import ChessRules from "/base_rules.js";
+import { ArrayFun } from "/utils/array.js";
+import { Random } from "/utils/alea.js";
+
+export default class AlapoRules extends ChessRules {
+
+ get hasFlags() {
+ return false;
+ }
+ get hasEnpassant() {
+ return false;
+ }
+
+ getSvgChessboard() {
+ let board = super.getSvgChessboard().slice(0, -6);
+ // Add lines to delimitate goals
+ board += `
+
+
+ `;
+ return board;
+ }
+
+ genRandInitFen(seed) {
+ if (this.options["randomness"] == 0)
+ return "rbqqbr/tcssct/6/6/TCSSCT/RBQQBR w 0";
+
+ Random.setSeed(seed);
+
+ const piece2pawn = {
+ r: 't',
+ q: 's',
+ b: 'c'
+ };
+
+ let pieces = { w: new Array(6), b: new Array(6) };
+ // Shuffle pieces on first (and last rank if randomness == 2)
+ for (let c of ["w", "b"]) {
+ if (c == 'b' && this.options["randomness"] == 1) {
+ pieces['b'] = pieces['w'];
+ break;
+ }
+
+ let positions = ArrayFun.range(6);
+
+ // Get random squares for bishops
+ let randIndex = 2 * Random.randInt(3);
+ const bishop1Pos = positions[randIndex];
+ let randIndex_tmp = 2 * Random.randInt(3) + 1;
+ const bishop2Pos = positions[randIndex_tmp];
+ positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+ positions.splice(Math.min(randIndex, randIndex_tmp), 1);
+
+ // Get random square for queens
+ randIndex = Random.randInt(4);
+ const queen1Pos = positions[randIndex];
+ positions.splice(randIndex, 1);
+ randIndex = Random.randInt(3);
+ const queen2Pos = positions[randIndex];
+ positions.splice(randIndex, 1);
+
+ // Rooks positions are now fixed,
+ const rook1Pos = positions[0];
+ const rook2Pos = positions[1];
+
+ pieces[c][rook1Pos] = "r";
+ pieces[c][bishop1Pos] = "b";
+ pieces[c][queen1Pos] = "q";
+ pieces[c][queen2Pos] = "q";
+ pieces[c][bishop2Pos] = "b";
+ pieces[c][rook2Pos] = "r";
+ }
+
+ return (
+ pieces["b"].join("") + "/" +
+ pieces["b"].map(p => piece2pawn[p]).join("") +
+ "/6/6/" +
+ pieces["w"].map(p => piece2pawn[p].toUpperCase()).join("") + "/" +
+ pieces["w"].join("").toUpperCase() +
+ " w 0"
+ );
+ }
+
+ pieces(color, x, y) {
+ return {
+ 'r': super.pieces(color, x, y)['r'],
+ 'q': super.pieces(color, x, y)['q'],
+ 'b': {
+ // Triangle is rotated from opponent viewpoint
+ "class": "bishop" + (this.playerColor != color ? "_inv" : ""),
+ moves: [ { steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]] } ]
+ },
+ 's': { //"square"
+ "class": "babyrook",
+ moves: [
+ {
+ steps: [[0, 1], [0, -1], [1, 0], [-1, 0]],
+ range: 1
+ }
+ ]
+ },
+ 'c': { //"circle"
+ "class": "babyqueen",
+ moves: [
+ {
+ steps: [
+ [0, 1], [0, -1], [1, 0], [-1, 0],
+ [1, 1], [1, -1], [-1, 1], [-1, -1]
+ ],
+ range: 1
+ }
+ ]
+ },
+ 't': { //"triangle"
+ "class": "babybishop" + (this.playerColor != color ? "_inv" : ""),
+ moves: [
+ {
+ steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]],
+ range: 1
+ }
+ ]
+ }
+ };
+ }
+
+ get size() {
+ return {
+ x: 6,
+ y: 6
+ };
+ }
+
+ filterValid(moves) {
+ return moves;
+ }
+
+ getCurrentScore() {
+ // Try both colors (to detect potential suicides)
+ let won = {};
+ for (let c of ['w', 'b']) {
+ const oppCol = C.GetOppCol(c);
+ const goal = (c == 'w' ? 0 : 5);
+ won[c] = this.board[goal].some((b,j) => {
+ return (
+ this.getColor(goal, j) == c &&
+ this.findCapturesOn(
+ [goal, j], {one: true, oppCol: oppCol}).length == 0
+ );
+ });
+ }
+ if (won['w'] && won['b'])
+ return "?"; //no idea who won, not relevant anyway :)
+ return (won['w'] ? "1-0" : (won['b'] ? "0-1" : "*"));
+ }
+
+};
diff --git a/variants/Alapo/rules.html b/variants/Alapo/rules.html
new file mode 100644
index 0000000..252699c
--- /dev/null
+++ b/variants/Alapo/rules.html
@@ -0,0 +1,7 @@
+Pieces move like rook, bishop and queen. Small ones by one square only.
+
+Goal: bring a piece safely on the last rank.
+
+
+ chessvariants page.
+
diff --git a/variants/Alapo/style.css b/variants/Alapo/style.css
new file mode 100644
index 0000000..942f9f4
--- /dev/null
+++ b/variants/Alapo/style.css
@@ -0,0 +1,49 @@
+piece.black.rook {
+ background-image: url('/pieces/Alapo/black_SQUARE.svg');
+}
+piece.black.bishop {
+ background-image: url('/pieces/Alapo/black_TRIANGLE.svg');
+}
+piece.black.bishop_inv {
+ background-image: url('/pieces/Alapo/black_TRIANGLE_inv.svg');
+}
+piece.black.queen {
+ background-image: url('/pieces/Alapo/black_CIRCLE.svg');
+}
+piece.black.babyrook {
+ background-image: url('/pieces/Alapo/black_square.svg');
+}
+piece.black.babybishop {
+ background-image: url('/pieces/Alapo/black_triangle.svg');
+}
+piece.black.babybishop {
+ background-image: url('/pieces/Alapo/black_triangle_inv.svg');
+}
+piece.black.babyqueen {
+ background-image: url('/pieces/Alapo/black_circle.svg');
+}
+
+piece.white.rook {
+ background-image: url('/pieces/Alapo/white_SQUARE.svg');
+}
+piece.white.bishop {
+ background-image: url('/pieces/Alapo/white_TRIANGLE.svg');
+}
+piece.white.bishop {
+ background-image: url('/pieces/Alapo/white_TRIANGLE_inv.svg');
+}
+piece.white.queen {
+ background-image: url('/pieces/Alapo/white_CIRCLE.svg');
+}
+piece.white.babyrook {
+ background-image: url('/pieces/Alapo/white_square.svg');
+}
+piece.white.babybishop {
+ background-image: url('/pieces/Alapo/white_triangle.svg');
+}
+piece.white.babybishop {
+ background-image: url('/pieces/Alapo/white_triangle_inv.svg');
+}
+piece.white.babyqueen {
+ background-image: url('/pieces/Alapo/white_circle.svg');
+}
diff --git a/variants/Ambiguous/CREDITS b/variants/Ambiguous/CREDITS
new file mode 100644
index 0000000..68ab598
--- /dev/null
+++ b/variants/Ambiguous/CREDITS
@@ -0,0 +1,3 @@
+Images:
+
+https://freesvg.org/black-target
diff --git a/variants/Ambiguous/style.css b/variants/Ambiguous/style.css
index e31d810..db300f7 100644
--- a/variants/Ambiguous/style.css
+++ b/variants/Ambiguous/style.css
@@ -1,7 +1,7 @@
@import url("/base_pieces.css");
piece.target {
- background-image: url('/pieces/ambiguous_target.svg');
+ background-image: url('/pieces/Ambiguous/target.svg');
}
piece.white.target-pawn {
diff --git a/variants/Chakart/style.css b/variants/Chakart/style.css
index b3687ce..91ad633 100644
--- a/variants/Chakart/style.css
+++ b/variants/Chakart/style.css
@@ -1,19 +1,19 @@
@import url("/base_pieces.css");
piece.egg {
- background-image: url('/pieces/chakart_egg.svg');
+ background-image: url('/pieces/Chakart/egg.svg');
}
piece.mushroom {
- background-image: url('/pieces/chakart_mushroom.svg');
+ background-image: url('/pieces/Chakart/mushroom.svg');
}
piece.banana {
- background-image: url('/pieces/chakart_banana.svg');
+ background-image: url('/pieces/Chakart/banana.svg');
}
piece.bomb {
- background-image: url('/pieces/chakart_bomb.svg');
+ background-image: url('/pieces/Chakart/bomb.svg');
}
piece.white.invisible {
@@ -29,14 +29,14 @@ piece.immobilized {
}
piece.remote-capture {
- background-image: url('/pieces/chakart_shell.svg');
+ background-image: url('/pieces/Chakart/shell.svg');
}
piece.mystery.white {
- background-image: url('/pieces/chakart_mystery_white.svg');
+ background-image: url('/pieces/Chakart/mystery_white.svg');
}
piece.mystery.black {
- background-image: url('/pieces/chakart_mystery_black.svg');
+ background-image: url('/pieces/Chakart/mystery_black.svg');
}
div.bonus-text {
--
2.44.0
From 2159c2391f765c55ad79d5e05a62ecc6586b8522 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Tue, 21 Jun 2022 18:07:32 +0200
Subject: [PATCH 10/16] Fix Alapo
---
base_rules.js | 15 +++++++++------
variants/Alapo/style.css | 6 +++---
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/base_rules.js b/base_rules.js
index c19ee64..4b387b6 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -666,7 +666,8 @@ export default class ChessRules {
const color = this.getColor(i, j);
const piece = this.getPiece(i, j);
this.g_pieces[i][j] = document.createElement("piece");
- C.AddClass_es(this.g_pieces[i][j], this.pieces()[piece]["class"]);
+ C.AddClass_es(this.g_pieces[i][j],
+ this.pieces(color, i, j)[piece]["class"]);
this.g_pieces[i][j].classList.add(C.GetColorClass(color));
this.g_pieces[i][j].style.width = pieceWidth + "px";
this.g_pieces[i][j].style.height = pieceWidth + "px";
@@ -734,7 +735,7 @@ export default class ChessRules {
r_cell.style.height = sqResSize + "px";
rcontainer.appendChild(r_cell);
let piece = document.createElement("piece");
- C.AddClass_es(piece, this.pieces()[p]["class"]);
+ C.AddClass_es(piece, this.pieces(c, c, p)[p]["class"]);
piece.classList.add(C.GetColorClass(c));
piece.style.width = "100%";
piece.style.height = "100%";
@@ -1037,7 +1038,8 @@ export default class ChessRules {
choice.onclick = () => callback(moves[i]);
const piece = document.createElement("piece");
const cdisp = moves[i].choice || moves[i].appear[0].p;
- C.AddClass_es(piece, this.pieces()[cdisp]["class"]);
+ C.AddClass_es(piece,
+ this.pieces(color, moves[i].end.x, moves[i].end.y)[cdisp]["class"]);
piece.classList.add(C.GetColorClass(color));
piece.style.width = "100%";
piece.style.height = "100%";
@@ -2190,7 +2192,8 @@ export default class ChessRules {
const pieceWidth = this.getPieceWidth(r.width);
move.appear.forEach(a => {
this.g_pieces[a.x][a.y] = document.createElement("piece");
- C.AddClass_es(this.g_pieces[a.x][a.y], this.pieces()[a.p]["class"]);
+ C.AddClass_es(this.g_pieces[a.x][a.y],
+ this.pieces(a.c, a.x, a.y)[a.p]["class"]);
this.g_pieces[a.x][a.y].classList.add(C.GetColorClass(a.c));
this.g_pieces[a.x][a.y].style.width = pieceWidth + "px";
this.g_pieces[a.x][a.y].style.height = pieceWidth + "px";
@@ -2240,12 +2243,12 @@ export default class ChessRules {
movingPiece.style.height = pieceWidth + "px";
}
const maxDist = this.getMaxDistance(r);
- const pieces = this.pieces();
+ const apparentColor = this.getColor(move.start.x, move.start.y);
+ const pieces = this.pieces(apparentColor, move.start.x, move.start.y);
if (move.drag) {
const startCode = this.getPiece(move.start.x, move.start.y);
C.RemoveClass_es(movingPiece, pieces[startCode]["class"]);
C.AddClass_es(movingPiece, pieces[move.drag.p]["class"]);
- const apparentColor = this.getColor(move.start.x, move.start.y);
if (apparentColor != move.drag.c) {
movingPiece.classList.remove(C.GetColorClass(apparentColor));
movingPiece.classList.add(C.GetColorClass(move.drag.c));
diff --git a/variants/Alapo/style.css b/variants/Alapo/style.css
index 942f9f4..4fbefb1 100644
--- a/variants/Alapo/style.css
+++ b/variants/Alapo/style.css
@@ -16,7 +16,7 @@ piece.black.babyrook {
piece.black.babybishop {
background-image: url('/pieces/Alapo/black_triangle.svg');
}
-piece.black.babybishop {
+piece.black.babybishop_inv {
background-image: url('/pieces/Alapo/black_triangle_inv.svg');
}
piece.black.babyqueen {
@@ -29,7 +29,7 @@ piece.white.rook {
piece.white.bishop {
background-image: url('/pieces/Alapo/white_TRIANGLE.svg');
}
-piece.white.bishop {
+piece.white.bishop_inv {
background-image: url('/pieces/Alapo/white_TRIANGLE_inv.svg');
}
piece.white.queen {
@@ -41,7 +41,7 @@ piece.white.babyrook {
piece.white.babybishop {
background-image: url('/pieces/Alapo/white_triangle.svg');
}
-piece.white.babybishop {
+piece.white.babybishop_inv {
background-image: url('/pieces/Alapo/white_triangle_inv.svg');
}
piece.white.babyqueen {
--
2.44.0
From e2be4b04faeac3fca5b292499bb70b98542e45ce Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Tue, 21 Jun 2022 19:36:14 +0200
Subject: [PATCH 11/16] Add Cleopatra option to Benedict chess
---
pieces/Benedict/black_cleopatra.svg | 1 +
pieces/Benedict/white_cleopatra.svg | 1 +
variants.js | 1 -
variants/Benedict/CREDITS | 4 +++
variants/Benedict/class.js | 52 ++++++++++++++++++++++-------
variants/Benedict/rules.html | 8 +++++
variants/Benedict/style.css | 8 +++++
7 files changed, 62 insertions(+), 13 deletions(-)
create mode 100644 pieces/Benedict/black_cleopatra.svg
create mode 100644 pieces/Benedict/white_cleopatra.svg
create mode 100644 variants/Benedict/CREDITS
diff --git a/pieces/Benedict/black_cleopatra.svg b/pieces/Benedict/black_cleopatra.svg
new file mode 100644
index 0000000..4360044
--- /dev/null
+++ b/pieces/Benedict/black_cleopatra.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/pieces/Benedict/white_cleopatra.svg b/pieces/Benedict/white_cleopatra.svg
new file mode 100644
index 0000000..fd7f323
--- /dev/null
+++ b/pieces/Benedict/white_cleopatra.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/variants.js b/variants.js
index 90e9244..59a69c0 100644
--- a/variants.js
+++ b/variants.js
@@ -1,5 +1,4 @@
const variants = [
- // TODO: https://mancala.fandom.com/wiki/William_Daniel_Troyka Cleopatra chess
{name: 'Absorption', desc: 'Absorb powers'},
{name: 'Alapo', desc: 'Geometric Chess'},
// {name: 'Alice', desc: 'Both sides of the mirror'},
diff --git a/variants/Benedict/CREDITS b/variants/Benedict/CREDITS
new file mode 100644
index 0000000..e936f02
--- /dev/null
+++ b/variants/Benedict/CREDITS
@@ -0,0 +1,4 @@
+Images:
+
+https://game-icons.net/1x1/delapouite/cleopatra.html
+https://www.svgrepo.com/svg/322047/cleopatra
diff --git a/variants/Benedict/class.js b/variants/Benedict/class.js
index 81fee87..2bc60e7 100644
--- a/variants/Benedict/class.js
+++ b/variants/Benedict/class.js
@@ -6,6 +6,14 @@ export default class BenedictRules extends ChessRules {
static get Options() {
return {
select: C.Options.select,
+ input: [
+ {
+ label: "Cleopatra",
+ variable: "cleopatra",
+ type: "checkbox",
+ defaut: false
+ }
+ ],
styles: [
"balance",
"cylinder",
@@ -25,6 +33,24 @@ export default class BenedictRules extends ChessRules {
return false;
}
+ pieces(color, x, y) {
+ if (!this.options["cleopatra"])
+ return super.pieces(color, x, y);
+ return Object.assign({}, super.pieces(color, x, y), {
+ 'q': {
+ "class": "cleopatra",
+ moves: [
+ {
+ steps: [
+ [0, 1], [0, -1], [1, 0], [-1, 0],
+ [1, 1], [1, -1], [-1, 1], [-1, -1]
+ ]
+ }
+ ]
+ },
+ });
+ }
+
// Find potential captures from a square
// follow steps from x,y until something is met.
findAttacks([x, y]) {
@@ -56,20 +82,22 @@ export default class BenedictRules extends ChessRules {
postProcessPotentialMoves(moves) {
moves.forEach(m => {
- super.playOnBoard(m);
- let attacks = this.findAttacks([m.end.x, m.end.y])
- if (this.options["zen"]) {
- let endSquares = {};
- super.findCapturesOn([m.end.x, m.end.y], {zen: true}).forEach(c => {
- endSquares[C.CoordsToSquare(c.end)] = true;
+ m.flips = [];
+ if (!this.options["cleopatra"] || m.vanish[0].p == 'q') {
+ super.playOnBoard(m);
+ let attacks = this.findAttacks([m.end.x, m.end.y])
+ if (this.options["zen"]) {
+ let endSquares = {};
+ super.findCapturesOn([m.end.x, m.end.y], {zen: true}).forEach(c => {
+ endSquares[C.CoordsToSquare(c.end)] = true;
+ });
+ Array.prototype.push.apply(attacks, Object.keys(endSquares));
+ }
+ super.undoOnBoard(m);
+ attacks.map(C.SquareToCoords).forEach(a => {
+ m.flips.push({x: a.x, y: a.y});
});
- Array.prototype.push.apply(attacks, Object.keys(endSquares));
}
- super.undoOnBoard(m);
- m.flips = [];
- attacks.map(C.SquareToCoords).forEach(a => {
- m.flips.push({x: a.x, y: a.y});
- });
});
return moves;
}
diff --git a/variants/Benedict/rules.html b/variants/Benedict/rules.html
index 1a21c0c..92f633e 100644
--- a/variants/Benedict/rules.html
+++ b/variants/Benedict/rules.html
@@ -2,4 +2,12 @@
Goal: change the enemy king's color.
+
+ The "Cleopatra" option follow
+
+ these rules
+
+ where only the queen can change the color of enemy pieces.
+
+
William Daniel Troyka (2001).
diff --git a/variants/Benedict/style.css b/variants/Benedict/style.css
index a3550bc..0923e9e 100644
--- a/variants/Benedict/style.css
+++ b/variants/Benedict/style.css
@@ -1 +1,9 @@
@import url("/base_pieces.css");
+
+piece.black.cleopatra {
+ background-image: url('/pieces/Benedict/black_cleopatra.svg');
+}
+
+piece.white.cleopatra {
+ background-image: url('/pieces/Benedict/white_cleopatra.svg');
+}
--
2.44.0
From 65cf1690c6119c949e2ea8feba8835b6e90b79a2 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Wed, 22 Jun 2022 11:53:09 +0200
Subject: [PATCH 12/16] Add Alice Chess, fix a few things in base_rules.js
---
base_rules.js | 49 ++++--
pieces/Ambiguous/red_target.svg | 78 +++++++++
pieces/Ambiguous/target.svg | 191 -----------------------
pieces/Ambiguous/yellow_target.svg | 73 +++++++++
pieces/Benedict/CREDITS | 7 +
pieces/Benedict/black_cleopatra.svg | 2 +-
pieces/Benedict/black_cleopatra_TODO.svg | 1 +
pieces/Benedict/white_cleopatra.svg | 2 +-
pieces/Benedict/white_cleopatra_TODO.svg | 1 +
{variants => pieces}/Chakart/CREDITS | 2 -
variants.js | 2 +-
variants/Alapo/class.js | 13 +-
variants/Alapo/rules.html | 2 +
variants/Alice/class.js | 126 +++++++++++++++
variants/Alice/rules.html | 7 +
variants/Alice/style.css | 39 +++++
variants/Ambiguous/CREDITS | 3 -
variants/Ambiguous/class.js | 12 +-
variants/Ambiguous/style.css | 7 +-
variants/Benedict/CREDITS | 4 -
variants/Benedict/class.js | 18 +--
21 files changed, 393 insertions(+), 246 deletions(-)
create mode 100644 pieces/Ambiguous/red_target.svg
delete mode 100644 pieces/Ambiguous/target.svg
create mode 100644 pieces/Ambiguous/yellow_target.svg
create mode 100644 pieces/Benedict/CREDITS
mode change 100644 => 120000 pieces/Benedict/black_cleopatra.svg
create mode 100644 pieces/Benedict/black_cleopatra_TODO.svg
mode change 100644 => 120000 pieces/Benedict/white_cleopatra.svg
create mode 100644 pieces/Benedict/white_cleopatra_TODO.svg
rename {variants => pieces}/Chakart/CREDITS (97%)
create mode 100644 variants/Alice/class.js
create mode 100644 variants/Alice/rules.html
create mode 100644 variants/Alice/style.css
delete mode 100644 variants/Ambiguous/CREDITS
delete mode 100644 variants/Benedict/CREDITS
diff --git a/base_rules.js b/base_rules.js
index 4b387b6..a48bd3d 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -1082,8 +1082,9 @@ export default class ChessRules {
}
// Piece type on square (i,j)
- getPieceType(i, j) {
- const p = this.getPiece(i, j);
+ getPieceType(i, j, p) {
+ if (!p)
+ p = this.getPiece(i, j);
return C.CannibalKings[p] || p; //a cannibal king move as...
}
@@ -1092,7 +1093,7 @@ export default class ChessRules {
return (color == "w" ? "b" : "w");
}
- // Can thing on square1 capture (no return) thing on square2?
+ // Can thing on square1 capture (enemy) thing on square2?
canTake([x1, y1], [x2, y2]) {
return (this.getColor(x1, y1) !== this.getColor(x2, y2));
}
@@ -1214,6 +1215,8 @@ export default class ChessRules {
if (this.board[i][j] != "" && this.getColor(i, j) == color) {
const allSpecs = this.pieces(color, i, j)
let specs = allSpecs[this.getPieceType(i, j)];
+ if (specs.moveas)
+ specs = allSpecs[specs.moveas];
const attacks = specs.attack || specs.moves;
for (let a of attacks) {
outerLoop: for (let step of a.steps) {
@@ -1482,7 +1485,10 @@ export default class ChessRules {
const color = this.getColor(x, y);
const oppCol = C.GetOppCol(color);
const piece = this.getPieceType(x, y); //ok not cannibal king
- const stepSpec = this.pieces(color, x, y)[piece];
+ const allSpecs = this.pieces(color, x, y);
+ let stepSpec = allSpecs[piece];
+ if (stepSpec.moveas)
+ stepSpec = allSpecs[stepSpec.moveas];
const attacks = stepSpec.attack || stepSpec.moves;
for (let a of attacks) {
outerLoop: for (let step of a.steps) {
@@ -1506,7 +1512,7 @@ export default class ChessRules {
return false;
}
- canStepOver(i, j) {
+ canStepOver(i, j, p) {
// In some variants, objects on boards don't stop movement (Chakart)
return this.board[i][j] == "";
}
@@ -1514,7 +1520,11 @@ export default class ChessRules {
// Generic method to find possible moves of "sliding or jumping" pieces
getPotentialMovesOf(piece, [x, y]) {
const color = this.getColor(x, y);
- const stepSpec = this.pieces(color, x, y)[piece];
+ const apparentPiece = this.getPiece(x, y); //how it looks
+ const allSpecs = this.pieces(color, x, y);
+ let stepSpec = allSpecs[piece];
+ if (stepSpec.moveas)
+ stepSpec = allSpecs[stepSpec.moveas];
let moves = [];
// Next 3 for Cylinder mode:
let explored = {};
@@ -1539,7 +1549,7 @@ export default class ChessRules {
let stepCounter = 0;
while (
this.onBoard(i, j) &&
- (this.canStepOver(i, j) || (i == x && j == y))
+ ((i == x && j == y) || this.canStepOver(i, j, apparentPiece))
) {
if (
type != "attack" &&
@@ -1610,8 +1620,14 @@ export default class ChessRules {
) {
if (args.zen && this.isKing(this.getPiece(i, j)))
continue; //king not captured in this way
- const stepSpec =
- this.pieces(args.oppCol, i, j)[this.getPieceType(i, j)];
+ const apparentPiece = this.getPiece(i, j);
+ // Quick check: does this potential attacker target x,y ?
+ if (this.canStepOver(x, y, apparentPiece))
+ continue;
+ const allSpecs = this.pieces(args.oppCol, i, j);
+ let stepSpec = allSpecs[this.getPieceType(i, j)];
+ if (stepSpec.moveas)
+ stepSpec = allSpecs[stepSpec.moveas];
const attacks = stepSpec.attack || stepSpec.moves;
for (let a of attacks) {
for (let s of a.steps) {
@@ -1754,8 +1770,15 @@ export default class ChessRules {
s.y == e.y &&
Math.abs(s.x - e.x) == 2 &&
// Next conditions for variants like Atomic or Rifle, Recycle...
- (move.appear.length > 0 && move.appear[0].p == "p") &&
- (move.vanish.length > 0 && move.vanish[0].p == "p")
+ (
+ move.appear.length > 0 &&
+ this.getPieceType(0, 0, move.appear[0].p) == "p"
+ )
+ &&
+ (
+ move.vanish.length > 0 &&
+ this.getPieceType(0, 0, move.vanish[0].p) == "p"
+ )
) {
return {
x: (s.x + e.x) / 2,
@@ -1945,12 +1968,12 @@ export default class ChessRules {
let square = kingPos,
res = true; //a priori valid
if (m.vanish.some(v => {
- return C.CannibalKings[v.p] && v.c == color;
+ return this.isKing(v.p) && v.c == color;
})) {
// Search king in appear array:
const newKingIdx =
m.appear.findIndex(a => {
- return C.CannibalKings[a.p] && a.c == color;
+ return this.isKing(a.p) && a.c == color;
});
if (newKingIdx >= 0)
square = [m.appear[newKingIdx].x, m.appear[newKingIdx].y];
diff --git a/pieces/Ambiguous/red_target.svg b/pieces/Ambiguous/red_target.svg
new file mode 100644
index 0000000..2f7bb8c
--- /dev/null
+++ b/pieces/Ambiguous/red_target.svg
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Ambiguous/target.svg b/pieces/Ambiguous/target.svg
deleted file mode 100644
index 53f0ccc..0000000
--- a/pieces/Ambiguous/target.svg
+++ /dev/null
@@ -1,191 +0,0 @@
-
-
-
- Target
-
-
-
-
-
-
-
-
-
-
-
-
-
- image/svg+xml
-
-
-
-
- Openclipart
-
-
- Target
- 2012-02-15T07:37:04
- Target symbol
- https://openclipart.org/detail/168253/target-by-fanda@cz
-
-
- Fanda@CZ
-
-
-
-
- target
-
-
-
-
-
-
-
-
-
-
-
diff --git a/pieces/Ambiguous/yellow_target.svg b/pieces/Ambiguous/yellow_target.svg
new file mode 100644
index 0000000..400b872
--- /dev/null
+++ b/pieces/Ambiguous/yellow_target.svg
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Benedict/CREDITS b/pieces/Benedict/CREDITS
new file mode 100644
index 0000000..7844bd6
--- /dev/null
+++ b/pieces/Benedict/CREDITS
@@ -0,0 +1,7 @@
+Unusable?
+https://game-icons.net/1x1/delapouite/cleopatra.html
+https://www.svgrepo.com/svg/322047/cleopatra
+
+PNG? or paying...
+https://www.flaticon.com/free-icon/cleopatra_1393471
+https://www.flaticon.com/fr/icone-gratuite/cleopatre_1929899
diff --git a/pieces/Benedict/black_cleopatra.svg b/pieces/Benedict/black_cleopatra.svg
deleted file mode 100644
index 4360044..0000000
--- a/pieces/Benedict/black_cleopatra.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/pieces/Benedict/black_cleopatra.svg b/pieces/Benedict/black_cleopatra.svg
new file mode 120000
index 0000000..1669cd3
--- /dev/null
+++ b/pieces/Benedict/black_cleopatra.svg
@@ -0,0 +1 @@
+../black_queen.svg
\ No newline at end of file
diff --git a/pieces/Benedict/black_cleopatra_TODO.svg b/pieces/Benedict/black_cleopatra_TODO.svg
new file mode 100644
index 0000000..4360044
--- /dev/null
+++ b/pieces/Benedict/black_cleopatra_TODO.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/pieces/Benedict/white_cleopatra.svg b/pieces/Benedict/white_cleopatra.svg
deleted file mode 100644
index fd7f323..0000000
--- a/pieces/Benedict/white_cleopatra.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/pieces/Benedict/white_cleopatra.svg b/pieces/Benedict/white_cleopatra.svg
new file mode 120000
index 0000000..e31fd1d
--- /dev/null
+++ b/pieces/Benedict/white_cleopatra.svg
@@ -0,0 +1 @@
+../white_queen.svg
\ No newline at end of file
diff --git a/pieces/Benedict/white_cleopatra_TODO.svg b/pieces/Benedict/white_cleopatra_TODO.svg
new file mode 100644
index 0000000..fd7f323
--- /dev/null
+++ b/pieces/Benedict/white_cleopatra_TODO.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/variants/Chakart/CREDITS b/pieces/Chakart/CREDITS
similarity index 97%
rename from variants/Chakart/CREDITS
rename to pieces/Chakart/CREDITS
index 53a9181..11b0bb7 100644
--- a/variants/Chakart/CREDITS
+++ b/pieces/Chakart/CREDITS
@@ -1,5 +1,3 @@
-Images:
-
https://fr.m.wikiversity.org/wiki/Fichier:Tango_Style_Mushroom_icon.svg
https://commons.wikimedia.org/wiki/File:Tux_Paint_banana.svg
https://www.onlinewebfonts.com/icon/425540
diff --git a/variants.js b/variants.js
index 59a69c0..8918f47 100644
--- a/variants.js
+++ b/variants.js
@@ -1,7 +1,7 @@
const variants = [
{name: 'Absorption', desc: 'Absorb powers'},
{name: 'Alapo', desc: 'Geometric Chess'},
-// {name: 'Alice', desc: 'Both sides of the mirror'},
+ {name: 'Alice', desc: 'Both sides of the mirror'},
// {name: 'Align4', desc: 'Align four pawns'},
// {name: 'Allmate', desc: 'Mate any piece'},
{name: 'Ambiguous', desc: "Play opponent's pieces"},
diff --git a/variants/Alapo/class.js b/variants/Alapo/class.js
index eb0e73f..540747d 100644
--- a/variants/Alapo/class.js
+++ b/variants/Alapo/class.js
@@ -81,15 +81,14 @@ export default class AlapoRules extends ChessRules {
);
}
+ // Triangles are rotated from opponent viewpoint (=> suffix "_inv")
pieces(color, x, y) {
+ const allSpecs = super.pieces(color, x, y);
return {
- 'r': super.pieces(color, x, y)['r'],
- 'q': super.pieces(color, x, y)['q'],
- 'b': {
- // Triangle is rotated from opponent viewpoint
- "class": "bishop" + (this.playerColor != color ? "_inv" : ""),
- moves: [ { steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]] } ]
- },
+ 'r': allSpecs['r'],
+ 'q': allSpecs['q'],
+ 'b': Object.assign({}, allSpecs['b'],
+ {"class": "bishop" + (this.playerColor != color ? "_inv" : "")}),
's': { //"square"
"class": "babyrook",
moves: [
diff --git a/variants/Alapo/rules.html b/variants/Alapo/rules.html
index 252699c..9ec57e0 100644
--- a/variants/Alapo/rules.html
+++ b/variants/Alapo/rules.html
@@ -5,3 +5,5 @@
chessvariants page.
+
+Johannes Tranelis (1982).
diff --git a/variants/Alice/class.js b/variants/Alice/class.js
new file mode 100644
index 0000000..1957383
--- /dev/null
+++ b/variants/Alice/class.js
@@ -0,0 +1,126 @@
+import ChessRules from "/base_rules.js";
+import { ArrayFun } from "/utils/array.js";
+
+export default class AliceRules extends ChessRules {
+
+ static get Options() {
+ return {
+ select: C.Options.select,
+ input: C.Options.input,
+ styles: [
+ "balance",
+ "capture",
+ "cylinder",
+ "dark",
+ "doublemove",
+ "progressive",
+ "zen"
+ ]
+ };
+ }
+
+ // To the other side of the mirror and back...
+ static get ALICE_PIECES() {
+ return {
+ s: "p",
+ u: "r",
+ o: "n",
+ c: "b",
+ t: "q",
+ l: "k"
+ };
+ }
+ static get ALICE_CODES() {
+ return {
+ p: "s",
+ r: "u",
+ n: "o",
+ b: "c",
+ q: "t",
+ k: "l"
+ };
+ }
+
+ getPieceType(x, y, p) {
+ if (!p)
+ p = super.getPiece(x, y);
+ return V.ALICE_PIECES[p] || p;
+ }
+
+ pieces(color, x, y) {
+ let alices = {
+ 's': {"class": "alice-pawn", "moveas": "p"},
+ 'u': {"class": "alice-rook", "moveas": "r"},
+ 'o': {"class": "alice-knight", "moveas": "n"},
+ 'c': {"class": "alice-bishop", "moveas": "b"},
+ 't': {"class": "alice-queen", "moveas": "q"},
+ 'l': {"class": "alice-king", "moveas": "k"}
+ };
+ return Object.assign(alices, super.pieces(color, x, y));
+ }
+
+ fromSameWorld(p1, p2) {
+ return (
+ (V.ALICE_PIECES[p1] && V.ALICE_PIECES[p2]) ||
+ (V.ALICE_CODES[p1] && V.ALICE_CODES[p2])
+ );
+ }
+
+ // Step of p over i,j ?
+ canStepOver(i, j, p) {
+ return (
+ this.board[i][j] == "" || !this.fromSameWorld(this.getPiece(i, j), p));
+ }
+
+ // NOTE: castle & enPassant
+ // https://www.chessvariants.com/other.dir/alice.html
+ getPotentialMovesFrom([x, y]) {
+ return super.getPotentialMovesFrom([x, y]).filter(m => {
+ // Remove moves landing on occupied square on other board
+ return (
+ this.board[m.end.x][m.end.y] == "" ||
+ this.fromSameWorld(m.vanish[0].p, m.vanish[1].p)
+ );
+ }).map(m => {
+ // Apply Alice rule: go to the other side of the mirror
+ if (Object.keys(V.ALICE_CODES).includes(m.vanish[0].p))
+ // Board 1
+ m.appear.forEach(a => a.p = V.ALICE_CODES[a.p])
+ else
+ // Board 2
+ m.appear.forEach(a => a.p = V.ALICE_PIECES[a.p])
+ return m;
+ });
+ }
+
+ isKing(symbol) {
+ return ['k', 'l'].includes(symbol);
+ }
+
+ getCurrentScore() {
+ const color = this.turn;
+ const inCheck = this.underCheck(this.searchKingPos(color));
+ let someLegalMove = false;
+ // Search for legal moves: if any is found and
+ // does not change king world (if under check), then game not over.
+ for (let i=0; i= 1 &&
+ (
+ !inCheck ||
+ moves.some(m => m.vanish.every(v => !this.isKing(v.p)))
+ )
+ ) {
+ return "*";
+ }
+ }
+ }
+ }
+ // Couldn't find any legal move
+ return (inCheck ? "1/2" : (color == 'w' ? "0-1" : "1-0"));
+ }
+
+};
diff --git a/variants/Alice/rules.html b/variants/Alice/rules.html
new file mode 100644
index 0000000..98659ea
--- /dev/null
+++ b/variants/Alice/rules.html
@@ -0,0 +1,7 @@
+Pieces move to the next board after playing (other side of the mirror).
+
+
+ chessvariants page.
+
+
+Vernon R. Parton (1953).
diff --git a/variants/Alice/style.css b/variants/Alice/style.css
new file mode 100644
index 0000000..2683462
--- /dev/null
+++ b/variants/Alice/style.css
@@ -0,0 +1,39 @@
+@import url("/base_pieces.css");
+
+piece.white.alice-pawn {
+ background-image: url('/pieces/yellow_pawn.svg');
+}
+piece.white.alice-rook {
+ background-image: url('/pieces/yellow_rook.svg');
+}
+piece.white.alice-knight {
+ background-image: url('/pieces/yellow_knight.svg');
+}
+piece.white.alice-bishop {
+ background-image: url('/pieces/yellow_bishop.svg');
+}
+piece.white.alice-queen {
+ background-image: url('/pieces/yellow_queen.svg');
+}
+piece.white.alice-king {
+ background-image: url('/pieces/yellow_king.svg');
+}
+
+piece.black.alice-pawn {
+ background-image: url('/pieces/red_pawn.svg');
+}
+piece.black.alice-rook {
+ background-image: url('/pieces/red_rook.svg');
+}
+piece.black.alice-knight {
+ background-image: url('/pieces/red_knight.svg');
+}
+piece.black.alice-bishop {
+ background-image: url('/pieces/red_bishop.svg');
+}
+piece.black.alice-queen {
+ background-image: url('/pieces/red_queen.svg');
+}
+piece.black.alice-king {
+ background-image: url('/pieces/red_king.svg');
+}
diff --git a/variants/Ambiguous/CREDITS b/variants/Ambiguous/CREDITS
deleted file mode 100644
index 68ab598..0000000
--- a/variants/Ambiguous/CREDITS
+++ /dev/null
@@ -1,3 +0,0 @@
-Images:
-
-https://freesvg.org/black-target
diff --git a/variants/Ambiguous/class.js b/variants/Ambiguous/class.js
index 6a001a6..58458b6 100644
--- a/variants/Ambiguous/class.js
+++ b/variants/Ambiguous/class.js
@@ -55,14 +55,10 @@ export default class AmbiguousRules extends ChessRules {
return true;
})
.map(m => {
- if (m.vanish.length == 1) {
- m.appear[0].c = 'a'; //a-color
+ if (m.vanish.length == 1)
m.appear[0].p = V.GOAL;
- }
- else {
+ else
m.appear[0].p = V.TARGET_CODE[m.vanish[1].p];
- m.appear[0].c = oppCol;
- }
m.vanish.shift();
return m;
})
@@ -133,8 +129,8 @@ export default class AmbiguousRules extends ChessRules {
't': {"class": "target-queen", moves: []},
'l': {"class": "target-king", moves: []}
};
- return Object.assign(
- { 'g': {"class": "target"} }, targets, super.pieces(color, x, y));
+ return Object.assign({ 'g': {"class": "target", moves: []} },
+ targets, super.pieces(color, x, y));
}
atLeastOneMove() {
diff --git a/variants/Ambiguous/style.css b/variants/Ambiguous/style.css
index db300f7..f8dac72 100644
--- a/variants/Ambiguous/style.css
+++ b/variants/Ambiguous/style.css
@@ -1,7 +1,10 @@
@import url("/base_pieces.css");
-piece.target {
- background-image: url('/pieces/Ambiguous/target.svg');
+piece.white.target {
+ background-image: url('/pieces/Ambiguous/yellow_target.svg');
+}
+piece.black.target {
+ background-image: url('/pieces/Ambiguous/red_target.svg');
}
piece.white.target-pawn {
diff --git a/variants/Benedict/CREDITS b/variants/Benedict/CREDITS
deleted file mode 100644
index e936f02..0000000
--- a/variants/Benedict/CREDITS
+++ /dev/null
@@ -1,4 +0,0 @@
-Images:
-
-https://game-icons.net/1x1/delapouite/cleopatra.html
-https://www.svgrepo.com/svg/322047/cleopatra
diff --git a/variants/Benedict/class.js b/variants/Benedict/class.js
index 2bc60e7..e055e86 100644
--- a/variants/Benedict/class.js
+++ b/variants/Benedict/class.js
@@ -36,19 +36,11 @@ export default class BenedictRules extends ChessRules {
pieces(color, x, y) {
if (!this.options["cleopatra"])
return super.pieces(color, x, y);
- return Object.assign({}, super.pieces(color, x, y), {
- 'q': {
- "class": "cleopatra",
- moves: [
- {
- steps: [
- [0, 1], [0, -1], [1, 0], [-1, 0],
- [1, 1], [1, -1], [-1, 1], [-1, -1]
- ]
- }
- ]
- },
- });
+ const allSpecs = super.pieces(color, x, y);
+ return Object.assign({},
+ allSpecs,
+ {'q': Object.assign({}, allSpecs['q'], {"class": "cleopatra"})}
+ );
}
// Find potential captures from a square
--
2.44.0
From 475da4ac3761e5e27f06fffedb537f501688ae85 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Wed, 22 Jun 2022 12:40:43 +0200
Subject: [PATCH 13/16] Add some links
---
TODO | 3 ---
variants/Ambiguous/rules.html | 4 ++++
variants/Atomic/rules.html | 4 ++++
variants/Benedict/rules.html | 4 ++++
variants/Cannibal/rules.html | 4 ++++
variants/Crazyhouse/rules.html | 4 ++++
variants/Dark/rules.html | 4 ++++
variants/Doublemove/rules.html | 4 ++++
variants/Giveaway/rules.html | 4 ++++
variants/Hex/rules.html | 4 ++++
variants/Madrasi/rules.html | 4 ++++
variants/Progressive/rules.html | 4 ++++
variants/Recycle/rules.html | 4 ++++
variants/Refusal/rules.html | 4 ++++
variants/Rifle/rules.html | 4 ++++
variants/Suction/rules.html | 4 ++++
16 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/TODO b/TODO
index 73b76b0..0b9f7d7 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,3 @@
-Debug Alapo
-Add links to complete variants rules (Ambiguous...)
-
add variants :
Dark Racing Kings ? Checkered-Teleport ?
diff --git a/variants/Ambiguous/rules.html b/variants/Ambiguous/rules.html
index b51c747..acd3585 100644
--- a/variants/Ambiguous/rules.html
+++ b/variants/Ambiguous/rules.html
@@ -8,4 +8,8 @@
your opponent, then to choose one for you - which could be altered.
+
+ chessvariants page.
+
+
Fabrice Liardet (2005).
diff --git a/variants/Atomic/rules.html b/variants/Atomic/rules.html
index c46cf22..6067ed5 100644
--- a/variants/Atomic/rules.html
+++ b/variants/Atomic/rules.html
@@ -4,3 +4,7 @@
Win by checkmate or by exploding the enemy king.
+
+
+ lichess page.
+
diff --git a/variants/Benedict/rules.html b/variants/Benedict/rules.html
index 92f633e..db103a1 100644
--- a/variants/Benedict/rules.html
+++ b/variants/Benedict/rules.html
@@ -10,4 +10,8 @@
where only the queen can change the color of enemy pieces.
+
+ chessvariants page.
+
+
William Daniel Troyka (2001).
diff --git a/variants/Cannibal/rules.html b/variants/Cannibal/rules.html
index b9791a2..480b5cf 100644
--- a/variants/Cannibal/rules.html
+++ b/variants/Cannibal/rules.html
@@ -1 +1,5 @@
After each capture, the capturer transforms into the captured piece.
+
+
+ chessvariants page.
+
diff --git a/variants/Crazyhouse/rules.html b/variants/Crazyhouse/rules.html
index ecb0837..148015a 100644
--- a/variants/Crazyhouse/rules.html
+++ b/variants/Crazyhouse/rules.html
@@ -1,3 +1,7 @@
Captured pieces can be landed on the board in place of a normal turn.
Promoted pawns return on the board as pawns.
+
+
+ lichess page.
+
diff --git a/variants/Dark/rules.html b/variants/Dark/rules.html
index 8716272..137a626 100644
--- a/variants/Dark/rules.html
+++ b/variants/Dark/rules.html
@@ -2,4 +2,8 @@
Win by capturing the enemy king.
+
+ Wikipedia page.
+
+
Jens Baek Nielsen (1997).
diff --git a/variants/Doublemove/rules.html b/variants/Doublemove/rules.html
index 56054c8..5ac21d4 100644
--- a/variants/Doublemove/rules.html
+++ b/variants/Doublemove/rules.html
@@ -1,3 +1,7 @@
After the initial white move, each player moves twice on each turn.
+
+ chessvariants page.
+
+
Albert Fortis (1922).
diff --git a/variants/Giveaway/rules.html b/variants/Giveaway/rules.html
index 920b382..3e257bd 100644
--- a/variants/Giveaway/rules.html
+++ b/variants/Giveaway/rules.html
@@ -1 +1,5 @@
Win by losing all your material, or get stalemated.
+
+
+ lichess page.
+
diff --git a/variants/Hex/rules.html b/variants/Hex/rules.html
index 04e700d..15a91b1 100644
--- a/variants/Hex/rules.html
+++ b/variants/Hex/rules.html
@@ -4,4 +4,8 @@
Detailed rules.
+
+ A Strategy Guide.
+
+
Piet Hein (1942).
diff --git a/variants/Madrasi/rules.html b/variants/Madrasi/rules.html
index 10bfd87..741869c 100644
--- a/variants/Madrasi/rules.html
+++ b/variants/Madrasi/rules.html
@@ -1,3 +1,7 @@
Pieces of same nature attacking each other are immobilized.
+
+ Wikipedia page.
+
+
Abdul J. Karwathar (1979).
diff --git a/variants/Progressive/rules.html b/variants/Progressive/rules.html
index 51ec7f2..0521495 100644
--- a/variants/Progressive/rules.html
+++ b/variants/Progressive/rules.html
@@ -2,3 +2,7 @@
White play one move, then Black play two in a row,
then White play 3, and so on.
+
+
+ Wikipedia page.
+
diff --git a/variants/Recycle/rules.html b/variants/Recycle/rules.html
index b9b8008..ceea70c 100644
--- a/variants/Recycle/rules.html
+++ b/variants/Recycle/rules.html
@@ -3,4 +3,8 @@
can be self-captured and dropped later in the game.
+
+ chessvariants page.
+
+
Robert Huber (2000).
diff --git a/variants/Refusal/rules.html b/variants/Refusal/rules.html
index 8efc3ce..5c0b533 100644
--- a/variants/Refusal/rules.html
+++ b/variants/Refusal/rules.html
@@ -3,4 +3,8 @@
Different pawn promotions count as different moves.
+
+ chessvariants page.
+
+
Fred Galvin (1958).
diff --git a/variants/Rifle/rules.html b/variants/Rifle/rules.html
index 495bdf3..650264b 100644
--- a/variants/Rifle/rules.html
+++ b/variants/Rifle/rules.html
@@ -1,3 +1,7 @@
Pieces capture (as usual) without moving.
+
+ chessvariants page.
+
+
William Buehler Seabrook (1921).
diff --git a/variants/Suction/rules.html b/variants/Suction/rules.html
index 4006ce0..a9cfb2e 100644
--- a/variants/Suction/rules.html
+++ b/variants/Suction/rules.html
@@ -5,4 +5,8 @@
Win by bringing the enemy king on your first rank.
+
+ chessvariants page.
+
+
Nathaniel Virgo (2018).
--
2.44.0
From 4cec374b0172e0888aa2fa33283ad72210be6e56 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Wed, 22 Jun 2022 16:22:13 +0200
Subject: [PATCH 14/16] Fix memory leak of moves hash on server side. Draft
Align4
---
server.js | 11 +++---
variants.js | 2 +-
variants/Align4/class.js | 81 ++++++++++++++++++++++++++++++++++++++
variants/Align4/rules.html | 6 +++
variants/Align4/style.css | 1 +
5 files changed, 94 insertions(+), 7 deletions(-)
create mode 100644 variants/Align4/class.js
create mode 100644 variants/Align4/rules.html
create mode 100644 variants/Align4/style.css
diff --git a/server.js b/server.js
index dc8bd97..e08d13b 100644
--- a/server.js
+++ b/server.js
@@ -7,7 +7,6 @@ const wss = new WebSocket.Server({
let challenges = {}; //variantName --> socketId, name
let games = {}; //gameId --> gameInfo (vname, fen, players, options, time)
-let moveHash = {}; //gameId --> set of hashes seen so far
let sockets = {}; //socketId --> socket
const variants = require("./variants.js");
const Crypto = require("crypto");
@@ -28,14 +27,14 @@ function initializeGame(vname, players, options) {
vname: vname,
players: players,
options: options,
- time: Date.now()
+ time: Date.now(),
+ moveHash: {} //set of moves hashes seen so far
};
return gid;
}
// Provide seed in case of, so that both players initialize with same FEN
function launchGame(gid) {
- moveHash[gid] = {};
const gameInfo = Object.assign(
{seed: Math.floor(Math.random() * 19840), gid: gid},
games[gid]
@@ -195,11 +194,11 @@ wss.on("connection", (socket, req) => {
const hash = Crypto.createHash("md5")
.update(JSON.stringify(obj.fen))
.digest("hex");
- if (moveHash[hash])
+ if (games[obj.gid].moveHash[hash])
break;
- moveHash[hash] = true;
+ games[obj.gid].moveHash[hash] = true;
games[obj.gid].fen = obj.fen;
- games[obj.gid].time = Date.now(); //update timestamp in case of
+ games[obj.gid].time = Date.now(); //update useful if verrry slow game
const playingWhite = (games[obj.gid].players[0].sid == sid);
const oppSid = games[obj.gid].players[playingWhite ? 1 : 0].sid;
send(oppSid, "newmove", {moves: obj.moves});
diff --git a/variants.js b/variants.js
index 8918f47..5d04f45 100644
--- a/variants.js
+++ b/variants.js
@@ -2,7 +2,7 @@ const variants = [
{name: 'Absorption', desc: 'Absorb powers'},
{name: 'Alapo', desc: 'Geometric Chess'},
{name: 'Alice', desc: 'Both sides of the mirror'},
-// {name: 'Align4', desc: 'Align four pawns'},
+ {name: 'Align4', desc: 'Align four pawns'},
// {name: 'Allmate', desc: 'Mate any piece'},
{name: 'Ambiguous', desc: "Play opponent's pieces"},
// {name: 'Antiking1', desc: 'Keep antiking in check', disp: 'Anti-King'},
diff --git a/variants/Align4/class.js b/variants/Align4/class.js
new file mode 100644
index 0000000..b7b0c4a
--- /dev/null
+++ b/variants/Align4/class.js
@@ -0,0 +1,81 @@
+import ChessRules from "/base_rules.js";
+
+export default class Align4Rules extends ChessRules {
+
+ static get Options() {
+ return {
+ select: [{
+ label: "Randomness",
+ variable: "randomness",
+ defaut: 0,
+ options: [
+ {label: "Deterministic", value: 0},
+ {label: "Random", value: 1}
+ ]
+ }],
+ styles: ["atomic", "capture", "cylinder"]
+ };
+ }
+
+ get hasReserve() {
+ return true;
+ }
+ get hasReserveFen() {
+ return false;
+ }
+
+ genRandInitFen(seed) {
+ const baseFen = super.genRandInitFen(seed);
+ return "4k3/8" + baseFen.substring(17, 50) + " -"; //TODO: + flags 1188
+ }
+
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
+ this.reserve = { b: { p: 1 } };
+ }
+
+ // Just do not update any reserve (infinite supply)
+ updateReserve() {}
+
+ getCastleMoves([x, y]) {
+ if (this.GetColor(x, y) == 'b')
+ return [];
+ return super.getCastleMoves([x, y]);
+ }
+
+ getCurrentScore(move) {
+ const score = super.getCurrentScore(move);
+ if (score != "*")
+ return score;
+ // Check pawns connection:
+ for (let i = 0; i < this.size.x; i++) {
+ for (let j = 0; j < this.size.y; j++) {
+ if (
+ this.board[i][j] != "" &&
+ this.getColor(i, j) == 'b' &&
+ this.getPiece(i, j) == 'p'
+ ) {
+ // Exploration "rightward + downward" is enough
+ for (let step of [[1, 0], [0, 1], [1, 1], [-1, 1]]) {
+ let [ii, jj] = [i + step[0], j + step[1]];
+ let kounter = 1;
+ while (
+ this.onBoard(ii, jj) &&
+ this.board[ii][jj] != "" &&
+ this.getColor(ii, jj) == 'b' &&
+ this.getPiece(ii, jj) == 'p'
+ ) {
+ kounter++;
+ ii += step[0];
+ jj += step[1];
+ }
+ if (kounter == 4)
+ return "0-1";
+ }
+ }
+ }
+ }
+ return "*";
+ }
+
+};
diff --git a/variants/Align4/rules.html b/variants/Align4/rules.html
new file mode 100644
index 0000000..691a7a8
--- /dev/null
+++ b/variants/Align4/rules.html
@@ -0,0 +1,6 @@
+
+ Black goal is to align 4 pawns, either orthogonaly or diagonaly.
+ White goal is to checkmate the black king.
+
+
+Fynmorph [Discord] (2021).
diff --git a/variants/Align4/style.css b/variants/Align4/style.css
new file mode 100644
index 0000000..a3550bc
--- /dev/null
+++ b/variants/Align4/style.css
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
--
2.44.0
From fc12475fd434835816796ece83d93341af6c1550 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Thu, 23 Jun 2022 19:00:17 +0200
Subject: [PATCH 15/16] Fix Align4, fix mushrooms effect for Chakart
---
base_rules.js | 2 +-
variants/Align4/class.js | 15 ++++++---------
variants/Chakart/class.js | 32 +++++++++++++++++++++-----------
3 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/base_rules.js b/base_rules.js
index a48bd3d..3fac51b 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -252,7 +252,7 @@ export default class ChessRules {
parts.push(`"flags":"${flags}"`);
if (this.hasEnpassant)
parts.push('"enpassant":"-"');
- if (this.hasReserve)
+ if (this.hasReserveFen)
parts.push('"reserve":"000000000000"');
if (this.options["crazyhouse"])
parts.push('"ispawn":"-"');
diff --git a/variants/Align4/class.js b/variants/Align4/class.js
index b7b0c4a..b2683aa 100644
--- a/variants/Align4/class.js
+++ b/variants/Align4/class.js
@@ -26,23 +26,20 @@ export default class Align4Rules extends ChessRules {
genRandInitFen(seed) {
const baseFen = super.genRandInitFen(seed);
- return "4k3/8" + baseFen.substring(17, 50) + " -"; //TODO: + flags 1188
+ const fen = baseFen.replace("rnbqkbnr/pppppppp", "4k3/8");
+ const fenParts = baseFen.split(" ");
+ let others = JSON.parse(fenParts[3]);
+ others["flags"] = others["flags"].substr(0, 2) + "88";
+ return fenParts.slice(0, 3).join(" ") + " " + JSON.stringify(others);
}
- setOtherVariables(fenParsed) {
- super.setOtherVariables(fenParsed);
+ initReserves() {
this.reserve = { b: { p: 1 } };
}
// Just do not update any reserve (infinite supply)
updateReserve() {}
- getCastleMoves([x, y]) {
- if (this.GetColor(x, y) == 'b')
- return [];
- return super.getCastleMoves([x, y]);
- }
-
getCurrentScore(move) {
const score = super.getCurrentScore(move);
if (score != "*")
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index 37261e4..24bbd3e 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -133,8 +133,12 @@ export default class ChakartRules extends ChessRules {
genRandInitFen(seed) {
const options = Object.assign({mode: "suicide"}, this.options);
const gr = new GiveawayRules({options: options, genFenOnly: true});
- // Add Peach + mario flags
- return gr.genRandInitFen(seed).slice(0, -17) + '{"flags":"1111"}';
+ const baseFen = gr.genRandInitFen(seed);
+ const fenParts = baseFen.split(" ");
+ let others = JSON.parse(fenParts[3]);
+ delete others["enpassant"];
+ others["flags"] = "1111"; //Peach + Mario flags
+ return fenParts.slice(0, 3).join(" ") + " " + JSON.stringify(others);
}
fen2board(f) {
@@ -645,19 +649,25 @@ export default class ChakartRules extends ChessRules {
}
getMushroomEffect(move) {
- if (typeof move.start.x == "string") //drop move (toadette)
+ if (
+ typeof move.start.x == "string" || //drop move (toadette)
+ ['b', 'r', 'q'].includes(move.vanish[0].p) //slider
+ ) {
return null;
- let step = [move.end.x - move.start.x, move.end.y - move.start.y];
- if ([0, 1].some(i => Math.abs(step[i]) >= 2 && Math.abs(step[1-i]) != 1)) {
- // Slider, multi-squares: normalize step
- for (let j of [0, 1])
- step[j] = step[j] / Math.abs(step[j]) || 0;
}
+ let step = [move.end.x - move.start.x, move.end.y - move.start.y];
+ if (Math.abs(step[0]) == 2 && Math.abs(step[1]) == 0)
+ // Pawn initial 2-squares move: normalize step
+ step[0] /= 2;
const nextSquare = [move.end.x + step[0], move.end.y + step[1]];
- const afterSquare =
- [nextSquare[0] + step[0], nextSquare[1] + step[1]];
let nextMove = null;
- if (this.onBoard(nextSquare[0], nextSquare[1])) {
+ if (
+ this.onBoard(nextSquare[0], nextSquare[1]) &&
+ (
+ this.board[nextSquare[0]][nextSquare[1]] == "" ||
+ this.getColor(nextSquare[0], nextSquare[1]) == 'a'
+ )
+ ) {
this.playOnBoard(move); //HACK for getBasicMove()
nextMove = this.getBasicMove([move.end.x, move.end.y], nextSquare);
this.undoOnBoard(move);
--
2.44.0
From 5f08c59b29c2173cc8b2df1a3799ee971a14e691 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Sat, 25 Jun 2022 12:22:15 +0200
Subject: [PATCH 16/16] Experimental: improve animation, reduce lags in stack
moves sending. Add Allmate
---
app.js | 49 +++++----
base_rules.js | 212 ++++++++++++++++++++++++++----------
variants.js | 2 +-
variants/Allmate/class.js | 97 +++++++++++++++++
variants/Allmate/rules.html | 9 ++
variants/Allmate/style.css | 1 +
variants/Chakart/class.js | 164 +++++++++++++++-------------
variants/Suction/class.js | 9 ++
8 files changed, 386 insertions(+), 157 deletions(-)
create mode 100644 variants/Allmate/class.js
create mode 100644 variants/Allmate/rules.html
create mode 100644 variants/Allmate/style.css
diff --git a/app.js b/app.js
index 4c0b5e4..6f17c64 100644
--- a/app.js
+++ b/app.js
@@ -456,9 +456,28 @@ function notifyMe(code) {
let curMoves = [],
lastFen;
-const afterPlay = (move_s) => {
- const callbackAfterSend = () => {
- curMoves = [];
+const afterPlay = (move_s, newTurn, ops) => {
+ if (ops.send) {
+ // Pack into one moves array, then send (if turn changed)
+ if (Array.isArray(move_s))
+ // Array of simple moves (e.g. Chakart)
+ Array.prototype.push.apply(curMoves, move_s);
+ else
+ // Usual case
+ curMoves.push(move_s);
+ if (newTurn != playerColor) {
+ send("newmove",
+ {gid: gid, moves: curMoves, fen: vr.getFen()},
+ {
+ retry: true,
+ success: () => curMoves = [],
+ error: () => alert("Move not sent: reload page")
+ }
+ );
+ }
+ }
+ if (ops.res && newTurn != playerColor) {
+ toggleTurnIndicator(false); //now all moves are sent and animated
const result = vr.getCurrentScore(move_s);
if (result != "*") {
setTimeout(() => {
@@ -466,23 +485,6 @@ const afterPlay = (move_s) => {
send("gameover", {gid: gid});
}, 2000);
}
- };
- // Pack into one moves array, then send
- if (Array.isArray(move_s))
- // Array of simple moves (e.g. Chakart)
- Array.prototype.push.apply(curMoves, move_s);
- else
- // Usual case
- curMoves.push(move_s);
- if (vr.turn != playerColor) {
- toggleTurnIndicator(false);
- send("newmove",
- {gid: gid, moves: curMoves, fen: vr.getFen()},
- {
- retry: true,
- success: callbackAfterSend,
- error: () => alert("Move not sent: reload page")
- });
}
};
@@ -532,8 +534,9 @@ function initializeGame(obj) {
afterPlay: afterPlay,
options: options
});
- if (!obj.fen) {
- // Game creation: both players set FEN, in case of one is offline
+ const gameCreation = !obj.fen;
+ if (gameCreation) {
+ // Both players set FEN, in case of one is offline
send("setfen", {gid: obj.gid, fen: vr.getFen()});
localStorage.setItem("gid", obj.gid);
}
@@ -547,7 +550,7 @@ function initializeGame(obj) {
}
const playerIndex = (playerColor == "w" ? 0 : 1);
fillGameInfos(obj, 1 - playerIndex);
- if (obj.players[playerIndex].randvar)
+ if (obj.players[playerIndex].randvar && gameCreation)
toggleVisible("gameInfos");
else
toggleVisible("boardContainer");
diff --git a/base_rules.js b/base_rules.js
index 3fac51b..6562673 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -3,6 +3,21 @@ import { ArrayFun } from "/utils/array.js";
import PiPo from "/utils/PiPo.js";
import Move from "/utils/Move.js";
+// Helper class for move animation
+class TargetObj {
+
+ constructor(callOnComplete) {
+ this.value = 0;
+ this.target = 0;
+ this.callOnComplete = callOnComplete;
+ }
+ increment() {
+ if (++this.value == this.target)
+ this.callOnComplete();
+ }
+
+};
+
// NOTE: x coords: top to bottom (white perspective); y: left to right
// NOTE: ChessRules is aliased as window.C, and variants as window.V
export default class ChessRules {
@@ -96,6 +111,10 @@ export default class ChessRules {
return !!this.options["dark"];
}
+ get hasMoveStack() {
+ return false;
+ }
+
// Some variants use click infos:
doClick(coords) {
if (typeof coords.x != "number")
@@ -391,17 +410,21 @@ export default class ChessRules {
// Fen string fully describes the game state
if (!o.fen)
o.fen = this.genRandInitFen(o.seed);
- const fenParsed = this.parseFen(o.fen);
- this.board = this.getBoard(fenParsed.position);
- this.turn = fenParsed.turn;
- this.movesCount = parseInt(fenParsed.movesCount, 10);
- this.setOtherVariables(fenParsed);
+ this.re_initFromFen(o.fen);
// Graphical (can use variables defined above)
this.containerId = o.element;
this.graphicalInit();
}
+ re_initFromFen(fen, oldBoard) {
+ const fenParsed = this.parseFen(fen);
+ this.board = oldBoard || this.getBoard(fenParsed.position);
+ this.turn = fenParsed.turn;
+ this.movesCount = parseInt(fenParsed.movesCount, 10);
+ this.setOtherVariables(fenParsed);
+ }
+
// Turn position fen into double array ["wb","wp","bk",...]
getBoard(position) {
const rows = position.split("/");
@@ -433,7 +456,6 @@ export default class ChessRules {
this.initReserves(fenParsed.reserve);
if (this.options["crazyhouse"])
this.initIspawn(fenParsed.ispawn);
- this.subTurn = 1; //may be unused
if (this.options["teleport"]) {
this.subTurnTeleport = 1;
this.captured = null;
@@ -443,6 +465,9 @@ export default class ChessRules {
this.enlightened = ArrayFun.init(this.size.x, this.size.y, false);
this.updateEnlightened();
}
+ this.subTurn = 1; //may be unused
+ if (!this.moveStack) //avoid resetting (unwanted)
+ this.moveStack = [];
}
updateEnlightened() {
@@ -2128,35 +2153,38 @@ export default class ChessRules {
this.subTurnTeleport = 1;
this.captured = null;
}
- if (this.options["balance"]) {
- if (![1, 3].includes(this.movesCount))
- this.turn = oppCol;
- }
- else {
+ if (
+ (
+ this.options["doublemove"] &&
+ this.movesCount >= 1 &&
+ this.subTurn == 1
+ ) ||
+ (this.options["progressive"] && this.subTurn <= this.movesCount)
+ ) {
+ const oppKingPos = this.searchKingPos(oppCol);
if (
+ oppKingPos[0] >= 0 &&
(
- this.options["doublemove"] &&
- this.movesCount >= 1 &&
- this.subTurn == 1
- ) ||
- (this.options["progressive"] && this.subTurn <= this.movesCount)
+ this.options["taking"] ||
+ !this.underCheck(oppKingPos, color)
+ )
) {
- const oppKingPos = this.searchKingPos(oppCol);
- if (
- oppKingPos[0] >= 0 &&
- (
- this.options["taking"] ||
- !this.underCheck(oppKingPos, color)
- )
- ) {
- this.subTurn++;
- return;
- }
+ this.subTurn++;
+ return;
}
+ }
+ if (this.isLastMove(move)) {
this.turn = oppCol;
+ this.movesCount++;
+ this.subTurn = 1;
}
- this.movesCount++;
- this.subTurn = 1;
+ }
+
+ isLastMove(move) {
+ return (
+ (this.options["balance"] && ![1, 3].includes(this.movesCount)) ||
+ !move.next
+ );
}
// "Stop at the first move found"
@@ -2233,11 +2261,50 @@ export default class ChessRules {
}
playPlusVisual(move, r) {
+ if (this.hasMoveStack)
+ this.buildMoveStack(move);
+ else {
+ this.play(move);
+ this.playVisual(move, r);
+ this.afterPlay(move, this.turn, {send: true, res: true}); //user method
+ }
+ }
+
+ // TODO: send stack receive stack, or allow incremental? (good/bad points)
+ buildMoveStack(move) {
+ this.moveStack.push(move);
+ this.computeNextMove(move);
this.play(move);
- this.playVisual(move, r);
- this.afterPlay(move); //user method
+ const newTurn = this.turn;
+ if (this.moveStack.length == 1) {
+ this.playVisual(move);
+ this.gameState = {
+ fen: this.getFen(),
+ board: JSON.parse(JSON.stringify(this.board)) //easier
+ };
+ }
+ if (move.next)
+ this.buildMoveStack(move.next);
+ else {
+ // Send, animate + play until here
+ if (this.moveStack.length == 1) {
+ this.afterPlay(this.moveStack, newTurn, {send: true, res: true});
+ this.moveStack = []
+ }
+ else {
+ this.afterPlay(this.moveStack, newTurn, {send: true, res: false});
+ this.re_initFromFen(this.gameState.fen, this.gameState.board);
+ this.playReceivedMove(this.moveStack.slice(1), () => {
+ this.afterPlay(this.moveStack, newTurn, {send: false, res: true});
+ this.moveStack = []
+ });
+ }
+ }
}
+ // Implemented in variants using (automatic) moveStack
+ computeNextMove(move) {}
+
getMaxDistance(r) {
// Works for all rectangular boards:
return Math.sqrt(r.width ** 2 + r.height ** 2);
@@ -2247,41 +2314,37 @@ export default class ChessRules {
return (typeof x == "string" ? this.r_pieces : this.g_pieces)[x][y];
}
- animate(move, callback) {
- if (this.noAnimate || move.noAnimate) {
- callback();
- return;
- }
- let initPiece = this.getDomPiece(move.start.x, move.start.y);
- // NOTE: cloning generally not required, but light enough, and simpler
+ animateMoving(start, end, drag, segments, cb) {
+ let initPiece = this.getDomPiece(start.x, start.y);
+ // NOTE: cloning often not required, but light enough, and simpler
let movingPiece = initPiece.cloneNode();
initPiece.style.opacity = "0";
let container =
document.getElementById(this.containerId)
const r = container.querySelector(".chessboard").getBoundingClientRect();
- if (typeof move.start.x == "string") {
+ if (typeof start.x == "string") {
// Need to bound width/height (was 100% for reserve pieces)
const pieceWidth = this.getPieceWidth(r.width);
movingPiece.style.width = pieceWidth + "px";
movingPiece.style.height = pieceWidth + "px";
}
const maxDist = this.getMaxDistance(r);
- const apparentColor = this.getColor(move.start.x, move.start.y);
- const pieces = this.pieces(apparentColor, move.start.x, move.start.y);
- if (move.drag) {
- const startCode = this.getPiece(move.start.x, move.start.y);
+ const apparentColor = this.getColor(start.x, start.y);
+ const pieces = this.pieces(apparentColor, start.x, start.y);
+ if (drag) {
+ const startCode = this.getPiece(start.x, start.y);
C.RemoveClass_es(movingPiece, pieces[startCode]["class"]);
- C.AddClass_es(movingPiece, pieces[move.drag.p]["class"]);
- if (apparentColor != move.drag.c) {
+ C.AddClass_es(movingPiece, pieces[drag.p]["class"]);
+ if (apparentColor != drag.c) {
movingPiece.classList.remove(C.GetColorClass(apparentColor));
- movingPiece.classList.add(C.GetColorClass(move.drag.c));
+ movingPiece.classList.add(C.GetColorClass(drag.c));
}
}
container.appendChild(movingPiece);
const animateSegment = (index, cb) => {
// NOTE: move.drag could be generalized per-segment (usage?)
- const [i1, j1] = move.segments[index][0];
- const [i2, j2] = move.segments[index][1];
+ const [i1, j1] = segments[index][0];
+ const [i2, j2] = segments[index][1];
const dep = this.getPixelPosition(i1, j1, r);
const arr = this.getPixelPosition(i2, j2, r);
movingPiece.style.transitionDuration = "0s";
@@ -2297,24 +2360,63 @@ export default class ChessRules {
setTimeout(cb, duration * 1000);
}, 50);
};
- if (!move.segments) {
- move.segments = [
- [[move.start.x, move.start.y], [move.end.x, move.end.y]]
- ];
- }
let index = 0;
const animateSegmentCallback = () => {
- if (index < move.segments.length)
+ if (index < segments.length)
animateSegment(index++, animateSegmentCallback);
else {
movingPiece.remove();
initPiece.style.opacity = "1";
- callback();
+ cb();
}
};
animateSegmentCallback();
}
+ // Input array of objects with at least fields x,y (e.g. PiPo)
+ animateFading(arr, cb) {
+ const animLength = 350; //TODO: 350ms? More? Less?
+ arr.forEach(v => {
+ let fadingPiece = this.getDomPiece(v.x, v.y);
+ fadingPiece.style.transitionDuration = (animLength / 1000) + "s";
+ fadingPiece.style.opacity = "0";
+ });
+ setTimeout(cb, animLength);
+ }
+
+ animate(move, callback) {
+ if (this.noAnimate || move.noAnimate) {
+ callback();
+ return;
+ }
+ let segments = move.segments;
+ if (!segments)
+ segments = [ [[move.start.x, move.start.y], [move.end.x, move.end.y]] ];
+ let targetObj = new TargetObj(callback);
+ if (move.start.x != move.end.x || move.start.y != move.end.y) {
+ targetObj.target++;
+ this.animateMoving(move.start, move.end, move.drag, segments,
+ () => targetObj.increment());
+ }
+ if (move.vanish.length > move.appear.length) {
+ const arr = move.vanish.slice(move.appear.length)
+ .filter(v => v.x != move.end.x || v.y != move.end.y);
+ if (arr.length > 0) {
+ targetObj.target++;
+ this.animateFading(arr, () => targetObj.increment());
+ }
+ }
+ targetObj.target +=
+ this.customAnimate(move, segments, () => targetObj.increment());
+ if (targetObj.target == 0)
+ callback();
+ }
+
+ // Potential other animations (e.g. for Suction variant)
+ customAnimate(move, segments, cb) {
+ return 0; //nb of targets
+ }
+
playReceivedMove(moves, callback) {
const launchAnimation = () => {
const r = container.querySelector(".chessboard").getBoundingClientRect();
diff --git a/variants.js b/variants.js
index 5d04f45..18d5891 100644
--- a/variants.js
+++ b/variants.js
@@ -3,7 +3,7 @@ const variants = [
{name: 'Alapo', desc: 'Geometric Chess'},
{name: 'Alice', desc: 'Both sides of the mirror'},
{name: 'Align4', desc: 'Align four pawns'},
-// {name: 'Allmate', desc: 'Mate any piece'},
+ {name: 'Allmate', desc: 'Mate any piece'},
{name: 'Ambiguous', desc: "Play opponent's pieces"},
// {name: 'Antiking1', desc: 'Keep antiking in check', disp: 'Anti-King'},
// {name: 'Antimatter', desc: 'Dangerous collisions'},
diff --git a/variants/Allmate/class.js b/variants/Allmate/class.js
new file mode 100644
index 0000000..8c5a92c
--- /dev/null
+++ b/variants/Allmate/class.js
@@ -0,0 +1,97 @@
+import ChessRules from "/base_rules.js";
+import PiPo from "/utils/PiPo.js";
+import Move from "/utils/Move.js";
+
+export default class AllmateRules extends ChessRules {
+
+ static get Options() {
+ return {
+ select: C.Options.select,
+ styles: [
+ "cylinder",
+ "madrasi",
+ "zen"
+ ]
+ };
+ }
+
+ get hasEnpassant() {
+ return false;
+ }
+ get hasMoveStack() {
+ return true;
+ }
+
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
+ this.curMove = null;
+ }
+
+ getPotentialMovesFrom(sq) {
+ // Remove direct captures:
+ return super.getPotentialMovesFrom(sq)
+ .filter(m => m.vanish.length == m.appear.length);
+ }
+
+ // Called "recursively" before a move is played, until no effect
+ computeNextMove(move) {
+ if (move.appear.length > 0)
+ this.curMove = move;
+ const color = this.turn;
+ const oppCol = C.GetOppCol(this.turn);
+ let mv = new Move({
+ start: this.curMove.end,
+ end: this.curMove.end,
+ appear: [],
+ vanish: []
+ });
+ this.playOnBoard(move);
+ for (let i=0; i 0 ? mv : null);
+ }
+
+ // is piece on square x,y mated by color?
+ isMated(x, y, color) {
+ const myColor = C.GetOppCol(color);
+ if (!this.underCheck([x, y], color))
+ return false;
+ for (let i=0; iTODO
+
+Win by mate-capturing the enemy king.
+
+
+ chessvariants page.
+
+
+Dr. Chris Taylor (1979).
diff --git a/variants/Allmate/style.css b/variants/Allmate/style.css
new file mode 100644
index 0000000..a3550bc
--- /dev/null
+++ b/variants/Allmate/style.css
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index 24bbd3e..abd695b 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -41,6 +41,9 @@ export default class ChakartRules extends ChessRules {
get hasReserveFen() {
return false;
}
+ get hasMoveStack() {
+ return true;
+ }
static get IMMOBILIZE_CODE() {
return {
@@ -178,15 +181,17 @@ export default class ChakartRules extends ChessRules {
}
setOtherVariables(fenParsed) {
- this.setFlags(fenParsed.flags);
- this.reserve = {}; //to be filled later
+ super.setOtherVariables(fenParsed);
this.egg = null;
- this.moveStack = [];
// Change seed (after FEN generation!!)
// so that further calls differ between players:
Random.setSeed(Math.floor(19840 * Math.random()));
}
+ initReserves() {
+ this.reserve = {}; //to be filled later
+ }
+
// For Toadette bonus
getDropMovesFrom([c, p]) {
if (typeof c != "string" || this.reserve[c][p] == 0)
@@ -415,57 +420,11 @@ export default class ChakartRules extends ChessRules {
super.showChoices(moves);
return false;
}
- if (!move.nextComputed) {
- // Set potential random effects, so that play() is deterministic
- // from opponent viewpoint:
- const endPiece = this.getPiece(move.end.x, move.end.y);
- switch (endPiece) {
- case V.EGG:
- move.egg = Random.sample(V.EGG_SURPRISE);
- move.next = this.getEggEffect(move);
- break;
- case V.MUSHROOM:
- move.next = this.getMushroomEffect(move);
- break;
- case V.BANANA:
- case V.BOMB:
- move.next = this.getBombBananaEffect(move, endPiece);
- break;
- }
- if (!move.next && move.appear.length > 0 && !move.kingboo) {
- const movingPiece = move.appear[0].p;
- if (['b', 'r'].includes(movingPiece)) {
- // Drop a banana or bomb:
- const bs =
- this.getRandomSquare([move.end.x, move.end.y],
- movingPiece == 'r'
- ? [[1, 1], [1, -1], [-1, 1], [-1, -1]]
- : [[1, 0], [-1, 0], [0, 1], [0, -1]],
- "freeSquare");
- if (bs) {
- move.appear.push(
- new PiPo({
- x: bs[0],
- y: bs[1],
- c: 'a',
- p: movingPiece == 'r' ? 'd' : 'w'
- })
- );
- if (this.board[bs[0]][bs[1]] != "") {
- move.vanish.push(
- new PiPo({
- x: bs[0],
- y: bs[1],
- c: this.getColor(bs[0], bs[1]),
- p: this.getPiece(bs[0], bs[1])
- })
- );
- }
- }
- }
- }
- move.nextComputed = true;
- }
+ this.postPlay(move, color, oppCol);
+ return true;
+ }
+
+ postPlay(move, color, oppCol) {
this.egg = move.egg;
if (move.egg == "toadette") {
this.reserve = { w: {}, b: {} };
@@ -526,15 +485,73 @@ export default class ChakartRules extends ChessRules {
}
}
}
- if (!move.next && !["daisy", "toadette", "kingboo"].includes(move.egg)) {
- this.turn = oppCol;
- this.movesCount++;
- }
+ this.playOnBoard(move);
+ super.postPlay(move);
+ }
+
+ playVisual(move, r) {
+ super.playVisual(move, r);
if (move.egg)
this.displayBonus(move);
- this.playOnBoard(move);
- this.nextMove = move.next;
- return true;
+ }
+
+ computeNextMove(move) {
+ // Set potential random effects, so that play() is deterministic
+ // from opponent viewpoint:
+ const endPiece = this.getPiece(move.end.x, move.end.y);
+ switch (endPiece) {
+ case V.EGG:
+ move.egg = Random.sample(V.EGG_SURPRISE);
+ move.next = this.getEggEffect(move);
+ break;
+ case V.MUSHROOM:
+ move.next = this.getMushroomEffect(move);
+ break;
+ case V.BANANA:
+ case V.BOMB:
+ move.next = this.getBombBananaEffect(move, endPiece);
+ break;
+ }
+ // NOTE: Chakart has also some side-effects:
+ if (
+ !move.next && move.appear.length > 0 &&
+ !move.kingboo && !move.luigiEffect
+ ) {
+ const movingPiece = move.appear[0].p;
+ if (['b', 'r'].includes(movingPiece)) {
+ // Drop a banana or bomb:
+ const bs =
+ this.getRandomSquare([move.end.x, move.end.y],
+ movingPiece == 'r'
+ ? [[1, 1], [1, -1], [-1, 1], [-1, -1]]
+ : [[1, 0], [-1, 0], [0, 1], [0, -1]],
+ "freeSquare");
+ if (bs) {
+ move.appear.push(
+ new PiPo({
+ x: bs[0],
+ y: bs[1],
+ c: 'a',
+ p: movingPiece == 'r' ? 'd' : 'w'
+ })
+ );
+ if (this.board[bs[0]][bs[1]] != "") {
+ move.vanish.push(
+ new PiPo({
+ x: bs[0],
+ y: bs[1],
+ c: this.getColor(bs[0], bs[1]),
+ p: this.getPiece(bs[0], bs[1])
+ })
+ );
+ }
+ }
+ }
+ }
+ }
+
+ isLastMove(move) {
+ return !move.next && !["daisy", "toadette", "kingboo"].includes(move.egg);
}
// Helper to set and apply banana/bomb effect
@@ -584,6 +601,7 @@ export default class ChakartRules extends ChessRules {
new PiPo({x: coords[0], y: coords[1], c: oldColor, p: piece})
]
});
+ em.luigiEffect = true; //avoid dropping bomb/banana by mistake
}
break;
case "bowser":
@@ -703,23 +721,13 @@ export default class ChakartRules extends ChessRules {
return moves;
}
- playPlusVisual(move, r) {
- const nextLines = () => {
- if (!this.play(move))
- return;
- this.moveStack.push(move);
- this.playVisual(move, r);
- if (this.nextMove)
- this.playPlusVisual(this.nextMove, r);
- else {
- this.afterPlay(this.moveStack);
- this.moveStack = [];
- }
- };
- if (this.moveStack.length == 0)
- nextLines();
- else
- this.animate(move, nextLines);
+ // Kingboo bonus can be animated better:
+ customAnimate(move, segments, cb) {
+ if (!move.kingboo)
+ return 0;
+ super.animateMoving(move.end, move.start, null,
+ segments.reverse().map(s => s.reverse()), cb);
+ return 1;
}
};
diff --git a/variants/Suction/class.js b/variants/Suction/class.js
index e2e36c8..dbaefa0 100644
--- a/variants/Suction/class.js
+++ b/variants/Suction/class.js
@@ -110,4 +110,13 @@ export default class SuctionRules extends ChessRules {
return "*";
}
+ // Better animation for swaps
+ customAnimate(move, segments, cb) {
+ if (move.vanish.length < 2)
+ return 0;
+ super.animateMoving(move.end, move.start, null,
+ segments.reverse().map(s => s.reverse()), cb);
+ return 1;
+ }
+
};
--
2.44.0