From 2c8375bb77dda7cbeaee983a09e202436be2191c Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Thu, 28 Dec 2023 11:46:58 +0100
Subject: [PATCH 01/16] Fix complete rules of Cwda
---
base_rules.js | 10 ++++++++--
common.css | 20 ++++++++++++++++++--
utils/drawDiagrams.js | 10 +++++++---
variants/Cwda/class.js | 2 +-
variants/Cwda/complete_rules.html | 8 ++++----
5 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/base_rules.js b/base_rules.js
index 699b1d8..757707d 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -235,7 +235,7 @@ export default class ChessRules {
randomness: this.options["randomness"],
between: [{p1: 'k', p2: 'r'}],
diffCol: ['b'],
- flags: ['r', 'k']
+ flags: ['r']
}
);
return {
@@ -642,7 +642,13 @@ export default class ChessRules {
else
this[arrName] = ArrayFun.init(this.size.x, this.size.y, null);
if (arrName == "d_pieces")
- this.marks.forEach(([i, j]) => addPiece(i, j, arrName, "mark"));
+ this.marks.forEach((m) => {
+ const formattedSquare =
+ (this.size.x - parseInt(m.substring(1), 10)).toString(36) +
+ (m.charCodeAt(0) - 97).toString(36);
+ const mCoords = V.SquareToCoords(formattedSquare);
+ addPiece(mCoords.x, mCoords.y, arrName, "mark");
+ });
};
if (this.marks)
conditionalReset("d_pieces");
diff --git a/common.css b/common.css
index cdfbc7a..5fee7ef 100644
--- a/common.css
+++ b/common.css
@@ -180,6 +180,14 @@ main > div {
padding: 0 10px;
overflow: auto;
}
+.full-rules figure.show-pieces {
+ max-width: 90%;
+ text-align: center;
+ margin: 0 auto;
+}
+.full-rules figure.show-pieces > img {
+ max-width: 100px;
+}
.full-rules > div {
margin-bottom: 20px;
}
@@ -234,6 +242,16 @@ piece.mark.transparent {
.full-rules .right {
float: right;
}
+@media screen and (max-width: 550px) {
+ .full-rules .left {
+ float: none;
+ margin-bottom: 10px;
+ }
+ .full-rules .right {
+ float: none;
+ margin-top: 10px;
+ }
+}
.full-rules figcaption {
display: block;
text-align: center;
@@ -350,8 +368,6 @@ piece.mark.transparent {
.chessboard {
position: absolute;
cursor: pointer;
- min-width: 200px;
- min-height: 200px;
}
piece {
position: absolute;
diff --git a/utils/drawDiagrams.js b/utils/drawDiagrams.js
index 9438dd6..3dd01f6 100644
--- a/utils/drawDiagrams.js
+++ b/utils/drawDiagrams.js
@@ -11,8 +11,12 @@ function fenToDiag(vname) {
function getDiagSize(elt) {
const baseWidth = Math.min(window.innerWidth, 800);
let multFact = 1;
- if (elt.classList.contains("left") || elt.classList.contains("right"))
- multFact = 0.45;
+ if (elt.classList.contains("left") || elt.classList.contains("right")) {
+ if (baseWidth >= 551)
+ multFact = 0.45;
+ else
+ multFact = 0.7;
+ }
else if (baseWidth > 630)
multFact = 0.5;
else
@@ -43,7 +47,7 @@ function re_drawDiagrams() {
element: "diag_" + i,
fen: diagrams[i].dataset.fen,
marks: diagrams[i].dataset.mks
- ? JSON.parse('[' + diagrams[i].dataset.mks + ']')
+ ? diagrams[i].dataset.mks.split(',')
: undefined,
color: diagrams[i].dataset.col || 'w',
options: {},
diff --git a/variants/Cwda/class.js b/variants/Cwda/class.js
index 3a47146..db79917 100644
--- a/variants/Cwda/class.js
+++ b/variants/Cwda/class.js
@@ -73,7 +73,7 @@ export default class CwdaRules extends ChessRules {
randomness: this.options["randomness"],
between: [{p1: 'k', p2: 'r'}],
diffCol: ['b'],
- flags: ['r', 'k']
+ flags: ['r']
}
);
let pawnLines = {
diff --git a/variants/Cwda/complete_rules.html b/variants/Cwda/complete_rules.html
index 958c0b6..9114c05 100644
--- a/variants/Cwda/complete_rules.html
+++ b/variants/Cwda/complete_rules.html
@@ -30,7 +30,7 @@
The Colorbound Clobberers
-
+
Rook = bishop + dabbabah = "bede" (D),
@@ -41,11 +41,11 @@
+ data-fen='8/8/8/3D4/8/8/8/8 w 0 {"armies":"CC","flags":"8888","enpassant":"-"}',
+ data-mks="a8,b7,c6,e4,f3,g2,h1,c4,b3,a2,e6,f7,g8,f5,d3,b5,d7">
--
2.48.1
From a55fde41f82207bf401c54b158dd6d0182799fd3 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Sat, 30 Dec 2023 11:58:13 +0100
Subject: [PATCH 02/16] Fix some complete rules
---
.gitignore | 2 +
README.md | 1 +
common.css | 4 +
variants/Alapo/rules.html | 8 +-
variants/Alice/rules.html | 8 +-
variants/Allmate/rules.html | 8 +-
variants/Ambiguous/rules.html | 8 +-
variants/Apocalypse/complete_rules.html | 1 -
variants/Apocalypse/rules.html | 8 +-
variants/Arena/rules.html | 8 +-
variants/Atomic/rules.html | 8 +-
variants/Avalam/rules.html | 8 +-
variants/Avalanche/rules.html | 8 +-
variants/Balaklava/rules.html | 8 +-
variants/Bario/complete_rules.html | 107 ++++++++++++++++--------
variants/Bario/rules.html | 16 ++--
variants/Baroque/complete_rules.html | 15 ++--
variants/Baroque/rules.html | 8 +-
variants/Benedict/rules.html | 8 +-
variants/Berolina/rules.html | 6 +-
variants/Cannibal/rules.html | 8 +-
variants/Chakart/complete_rules.html | 1 -
variants/Chakart/rules.html | 8 +-
variants/Checkered/complete_rules.html | 19 +++--
variants/Checkered/rules.html | 8 +-
variants/Chess960/rules.html | 8 +-
variants/Circular/rules.html | 2 +-
variants/Clorange/rules.html | 14 ++--
variants/Copycat/rules.html | 6 +-
variants/Crazyhouse/rules.html | 8 +-
variants/Cwda/complete_rules.html | 72 ++++++++++++----
variants/Cwda/rules.html | 8 +-
variants/Dark/rules.html | 8 +-
variants/Doublemove/rules.html | 8 +-
variants/Giveaway/rules.html | 8 +-
variants/Hex/rules.html | 16 ++--
variants/Madrasi/rules.html | 8 +-
variants/Progressive/rules.html | 8 +-
variants/Recycle/rules.html | 8 +-
variants/Refusal/rules.html | 8 +-
variants/Rifle/rules.html | 8 +-
variants/Suction/rules.html | 8 +-
variants/_Antiking/rules.html | 8 +-
43 files changed, 330 insertions(+), 176 deletions(-)
diff --git a/.gitignore b/.gitignore
index 160c63a..d3206f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,5 +3,7 @@
/node_modules/
/assets/
/assets.zip
+/extras/
+/extras.zip
/.pid
/parameters.js
diff --git a/README.md b/README.md
index b0517cd..4636ac4 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ PHP + Node.js + npm.
## Usage
```wget https://xogo.live/assets.zip && unzip assets.zip```
+```wget https://xogo.live/extras.zip && unzip extras.zip```
Rename parameters.js.dist → parameters.js, and edit file.
```npm i```
diff --git a/common.css b/common.css
index 5fee7ef..eabf641 100644
--- a/common.css
+++ b/common.css
@@ -260,6 +260,10 @@ piece.mark.transparent {
color: #0D1C46;
font-weight: bold;
}
+.full-rules .img-center {
+ display: block;
+ margin: 0 auto;
+}
/* TODO: use same CSS for rules and full-rules? */
.full-rules p, .full-rules ul, .full-rules ol {
margin: 10px 0;
diff --git a/variants/Alapo/rules.html b/variants/Alapo/rules.html
index 9ec57e0..910613b 100644
--- a/variants/Alapo/rules.html
+++ b/variants/Alapo/rules.html
@@ -2,8 +2,10 @@
Goal: bring a piece safely on the last rank.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Johannes Tranelis (1982).
diff --git a/variants/Alice/rules.html b/variants/Alice/rules.html
index 98659ea..215818f 100644
--- a/variants/Alice/rules.html
+++ b/variants/Alice/rules.html
@@ -1,7 +1,9 @@
Pieces move to the next board after playing (other side of the mirror).
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Vernon R. Parton (1953).
diff --git a/variants/Allmate/rules.html b/variants/Allmate/rules.html
index 4bdc308..4738949 100644
--- a/variants/Allmate/rules.html
+++ b/variants/Allmate/rules.html
@@ -6,8 +6,10 @@
Win by mate-capturing the enemy king.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Dr. Chris Taylor (1979).
diff --git a/variants/Ambiguous/rules.html b/variants/Ambiguous/rules.html
index acd3585..137579d 100644
--- a/variants/Ambiguous/rules.html
+++ b/variants/Ambiguous/rules.html
@@ -8,8 +8,10 @@
your opponent, then to choose one for you - which could be altered.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Fabrice Liardet (2005).
diff --git a/variants/Apocalypse/complete_rules.html b/variants/Apocalypse/complete_rules.html
index ae45dad..9e2d5c0 100644
--- a/variants/Apocalypse/complete_rules.html
+++ b/variants/Apocalypse/complete_rules.html
@@ -5,7 +5,6 @@
-
Apocalypse Rules
diff --git a/variants/Apocalypse/rules.html b/variants/Apocalypse/rules.html
index 6674bb2..75f35b2 100644
--- a/variants/Apocalypse/rules.html
+++ b/variants/Apocalypse/rules.html
@@ -8,8 +8,10 @@
The goal is to eliminate all enemy pawns.
-
- Full rules description.
-
+
+
+ Full rules description.
+
+
C.S. Elliott (1976).
diff --git a/variants/Arena/rules.html b/variants/Arena/rules.html
index 5aa8716..b9098c3 100644
--- a/variants/Arena/rules.html
+++ b/variants/Arena/rules.html
@@ -14,8 +14,10 @@
A player who lose both king and queen also lose the game.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Jeff Kiska (2000).
diff --git a/variants/Atomic/rules.html b/variants/Atomic/rules.html
index 6067ed5..6675403 100644
--- a/variants/Atomic/rules.html
+++ b/variants/Atomic/rules.html
@@ -5,6 +5,8 @@
Win by checkmate or by exploding the enemy king.
-
- lichess page.
-
+
+
+ lichess page.
+
+
diff --git a/variants/Avalam/rules.html b/variants/Avalam/rules.html
index 83e301e..6883b3b 100644
--- a/variants/Avalam/rules.html
+++ b/variants/Avalam/rules.html
@@ -9,8 +9,10 @@
largest number of towers of his color (on top) wins.
-
- More informations.
-
+
+
+ More informations.
+
+
Philippe Deweys (1995).
diff --git a/variants/Avalanche/rules.html b/variants/Avalanche/rules.html
index b8bb255..9528d3a 100644
--- a/variants/Avalanche/rules.html
+++ b/variants/Avalanche/rules.html
@@ -6,8 +6,10 @@
The goal is either to checkmate or to capture the enemy king.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Ralph Betza (1977).
diff --git a/variants/Balaklava/rules.html b/variants/Balaklava/rules.html
index 97c96a6..96ce11b 100644
--- a/variants/Balaklava/rules.html
+++ b/variants/Balaklava/rules.html
@@ -10,8 +10,10 @@
No en passant captures.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Gianluca Vecchi (1994).
diff --git a/variants/Bario/complete_rules.html b/variants/Bario/complete_rules.html
index 93ecc9d..ad700ac 100644
--- a/variants/Bario/complete_rules.html
+++ b/variants/Bario/complete_rules.html
@@ -1,64 +1,97 @@
-p.boxed
- | Your pieces remain in an undefined state until they are moved.
+
+
+
+
Bario Rules
+
+
+
+
+
+
Bario Rules
-p
- | Bario is a chess variant invented by Panos Louridas in 1998.
- a(href="https://www.bario-chess-checkers-chessphotography-spaceart.de/")
- | His website
- | contains many examples and explanations.
- | See also the discussion
- a(href="https://www.chessvariants.com/index/listcomments.php?order=DESC&itemid=Bario")
- | on chessvariants.com
- | .
+
+ Bario is a chess variant invented by Panos Louridas in 1998.
+
+ His website
+
+ contains many examples and explanations.
+ See also the discussion
+
+ on chessvariants.com
+ .
+
-figure
- img.img-center(src="/variants/Bario/chessboard2.jpg" style="width:75%")
- figcaption.text-center.
- [With author's permission]
+
+
+
+ [With author's permission]
Undefined pieces on first ranks.
+
+
-p.
+
Queens, rooks, bishops and knights begin the game in a reserve below
the board, because their location isn't determined yet.
At each turn, you can either move something already defined on the board, or
-ol
- li move a piece from your reserve to any question mark, and then
- li move the now defined piece on the board.
+
+
+ move a piece from your reserve to any question mark, and then
+ move the now defined piece on the board.
+
-p An undefined piece gives check if some specialization giving check exists.
+
Your pieces remain in an undefined state until they are moved.
-figure.diagram-container
- .diagram
- | fen:uuuuu1uk/p2p1qp1/1p6/7p/7P/5N2/PP1P1PP1/UUUKU1UU:
- figcaption The white king cannot move, a rook might be on c8 or e8
+
An undefined piece gives check if some specialization giving check exists.
-h4 Details, special cases
+
+
+
+ The white king cannot move, a rook might be on c8 or e8
+
-p.
+
Details, special cases
+
+
At the first move, you must select a square for the king anywhere on
the first rank — preferably in a corner. This deviates from the
original intention of the author, but this way you will not need to castle
later in the game. Click on a square to place the king.
+
-p.
+
If after your move all your pieces are defined (and weren't before), then
all pieces on board revert to undefined state, unless:
-ul
- li.
+
+
+
all your pieces (board + reserve) are now of the same type,
in which case none is put back in reserve, or
- li.
+
+
all opponent's pieces are of the same type, in which case
they also remain defined.
+
+
-figure.diagram-container
- .diagram.diag12
- | fen:uuu1kuuu/2p5/2n5/1p1ppppp/p4PPP/3N3Q/PPPPP1BB/1RRUK3:
- .diagram.diag22
- | fen:uuu1kuuu/2p5/2u5/1p1ppppp/p4PPP/3UU2U/PPPPP1UU/1UU1K3:
- figcaption.
+
+
+
+
+
+
Before and after knight definition on d1 (moving to e3)
+
+
-p.
+
If one of your undefined pieces is captured, you first have to choose which
one was captured from the reserve: place it at the capture location.
+
+
+
+
+
+
diff --git a/variants/Bario/rules.html b/variants/Bario/rules.html
index 733246f..2dbcf24 100644
--- a/variants/Bario/rules.html
+++ b/variants/Bario/rules.html
@@ -14,12 +14,16 @@
all pieces on board return to undefined state.
-
- Author's website.
-
+
+
+ Author's website.
+
+
-
- Full rules description.
-
+
+
+ Full rules description.
+
+
Panos Louridas (1985).
diff --git a/variants/Baroque/complete_rules.html b/variants/Baroque/complete_rules.html
index 1ac46cc..87709ed 100644
--- a/variants/Baroque/complete_rules.html
+++ b/variants/Baroque/complete_rules.html
@@ -5,7 +5,6 @@
-
Baroque Rules
@@ -108,7 +107,7 @@
+ data-mks='d4,d6,d8,a5'>
All marked squares captures are playable from d2.
@@ -120,8 +119,8 @@
+ data-fen='7k/8/8/3Qr3/8/8/8/K7 w 0'
+ data-mks="a5,b5,c5">
1.Qa5, 1.Qb5 or 1.Qc5 captures the black rook.
@@ -141,8 +140,8 @@
+ data-fen='7k/8/8/r3pP2/2n5/8/B7/K7 w 0'
+ data-mks="a5,c4">
1.Bd5 captures the two marked pieces.
@@ -166,8 +165,8 @@
+ data-fen='7k/8/8/p4r/4K3/8/8/8 w 0'
+ data-mks="e5">
1.Ke5 is impossible
diff --git a/variants/Baroque/rules.html b/variants/Baroque/rules.html
index 0e30307..ec5bb30 100644
--- a/variants/Baroque/rules.html
+++ b/variants/Baroque/rules.html
@@ -6,8 +6,10 @@
The goal is still to checkmate.
-
- Full rules description.
-
+
+
+ Full rules description.
+
+
Robert Abbott (1963).
diff --git a/variants/Benedict/rules.html b/variants/Benedict/rules.html
index db103a1..c2798b3 100644
--- a/variants/Benedict/rules.html
+++ b/variants/Benedict/rules.html
@@ -10,8 +10,10 @@
where only the queen can change the color of enemy pieces.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
William Daniel Troyka (2001).
diff --git a/variants/Berolina/rules.html b/variants/Berolina/rules.html
index bfa1cca..1824b27 100644
--- a/variants/Berolina/rules.html
+++ b/variants/Berolina/rules.html
@@ -1,3 +1,7 @@
-
Pawns movements are reversed: they capture forward, and move diagonally. From the initial rank they can move two squares in diagonal. See also .
+
+ Pawns movements are reversed: they capture forward, and move diagonally.
+ From the initial rank they can move two squares in diagonal.
+ See also .
+
Edmund Nebermann (1926).
diff --git a/variants/Cannibal/rules.html b/variants/Cannibal/rules.html
index 480b5cf..81d9c58 100644
--- a/variants/Cannibal/rules.html
+++ b/variants/Cannibal/rules.html
@@ -1,5 +1,7 @@
After each capture, the capturer transforms into the captured piece.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
diff --git a/variants/Chakart/complete_rules.html b/variants/Chakart/complete_rules.html
index 7bf3080..d1c8fd6 100644
--- a/variants/Chakart/complete_rules.html
+++ b/variants/Chakart/complete_rules.html
@@ -5,7 +5,6 @@
-
Chakart Rules
diff --git a/variants/Chakart/rules.html b/variants/Chakart/rules.html
index d45cb0d..e125478 100644
--- a/variants/Chakart/rules.html
+++ b/variants/Chakart/rules.html
@@ -10,8 +10,10 @@
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/Checkered/complete_rules.html b/variants/Checkered/complete_rules.html
index 5f21663..503e78d 100644
--- a/variants/Checkered/complete_rules.html
+++ b/variants/Checkered/complete_rules.html
@@ -6,7 +6,6 @@
-
Checkered Rules
@@ -15,12 +14,12 @@
The capture of an enemy piece produces a new "checkered" piece belonging
to both players.
-
-
-
-
-
-
+
+
+
+
+
+
Checkered pieces, born after captures.
Note: the initial French name for this variant is "l'Ãchiqueté".
@@ -52,8 +51,10 @@
Checkered pieces cannot be captured.
-
- fen:2kr4/pp6/2p5/4ss1r/1P2ns1P/2Np4/P1P1P1BP/R2o1RK1:
+
+
+
Black plays Rxh4=P. (Checkered pawn to) h5 is allowed then,
because piece's nature changed.
diff --git a/variants/Checkered/rules.html b/variants/Checkered/rules.html
index 892f352..a9209dd 100644
--- a/variants/Checkered/rules.html
+++ b/variants/Checkered/rules.html
@@ -3,8 +3,10 @@
Checkered moves cannot be undone on next turn.
-
- Full rules description.
-
+
+
+ Full rules description.
+
+
Patrick Bernier (2012).
diff --git a/variants/Chess960/rules.html b/variants/Chess960/rules.html
index 776fa4a..5192abe 100644
--- a/variants/Chess960/rules.html
+++ b/variants/Chess960/rules.html
@@ -1,3 +1,5 @@
-
- Orthodox chess rules.
-
+
+
+ Orthodox chess rules.
+
+
diff --git a/variants/Circular/rules.html b/variants/Circular/rules.html
index 6db9dd8..f9067b7 100644
--- a/variants/Circular/rules.html
+++ b/variants/Circular/rules.html
@@ -1,4 +1,4 @@
-Lower and upper sides of the board communicate.
+ Lower and upper sides of the board communicate.
All pawns move forward (up).
diff --git a/variants/Clorange/rules.html b/variants/Clorange/rules.html
index f10e7f6..541862e 100644
--- a/variants/Clorange/rules.html
+++ b/variants/Clorange/rules.html
@@ -1,8 +1,12 @@
-Captured pieces can be landed later in a non-capturing form;
-they regain their ability to capture when captured again.
+
+ Captured pieces can be landed later in a non-capturing form;
+ they regain their ability to capture when captured again.
+
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Fergus Duniho (1999).
diff --git a/variants/Copycat/rules.html b/variants/Copycat/rules.html
index 43d1f25..230d353 100644
--- a/variants/Copycat/rules.html
+++ b/variants/Copycat/rules.html
@@ -3,6 +3,10 @@
it borrows powers from it and can move (and capture) like it.
-Students from the UCF Chess Club (2020).
+
+ Students from the
+ UCF Chess Club
+ (2020).
+
See also their Discord server :-)
diff --git a/variants/Crazyhouse/rules.html b/variants/Crazyhouse/rules.html
index 148015a..f4d239f 100644
--- a/variants/Crazyhouse/rules.html
+++ b/variants/Crazyhouse/rules.html
@@ -2,6 +2,8 @@
Promoted pawns return on the board as pawns.
-
- lichess page.
-
+
+
+ lichess page.
+
+
diff --git a/variants/Cwda/complete_rules.html b/variants/Cwda/complete_rules.html
index 9114c05..2276050 100644
--- a/variants/Cwda/complete_rules.html
+++ b/variants/Cwda/complete_rules.html
@@ -5,7 +5,6 @@
-
Different Armies Rules
@@ -30,7 +29,12 @@
The Colorbound Clobberers
-
+
+
+
+
+
+
Rook = bishop + dabbabah = "bede" (D),
@@ -53,8 +57,11 @@
-
- fen:8/8/8/3F4/8/8/8/8 b7,c6,b5,f5,d3,d7,b3,c4,e4,f3,e6,f7:
+
+
+
Moves of the Fad.
@@ -67,7 +74,12 @@
The Nutty Knights
-
+
+
+
+
+
+
Rook = rook forward and sideways + king backwards = "charging rook" (G),
@@ -79,17 +91,29 @@
Queen = rook forward and sideways + knight forward + king = "colonel" (L).
-
- fen:8/8/8/8/3G4/8/8/8 d5,d6,d7,d8,c4,b4,a4,e4,f4,g4,h4,c3,d3,e3:
- fen:8/8/8/8/4i3/8/8/8 d6,d2,f6,f2,d3,d5,f5,f3:
+
+
+
+
+
Moves of the Charging rook on the left, and of the Fibnif on the right.
-
- fen:8/8/8/8/3T4/8/8/8 b5,c6,e6,f5,c4,e4,c3,d3,e3:
- fen:8/8/8/8/4c3/8/8/8 e5,e6,e7,e8,d4,c4,b4,a4,f4,g4,h4,d6,c5,f6,g5,d5,f5,d3,e3,f3:
+
+
+
+
+
Moves of the Charging knight on the left, and of the Colonel on the right.
@@ -97,7 +121,12 @@
The Remarkable Rookies
-
+
+
+
+
+
+
Rook = rook limited to 4 squares = "short rook" (S),
@@ -106,9 +135,15 @@
Queen = rook + knight = "chancellor" (O).
-
- fen:8/8/8/3y4/8/8/8/8 d3,b5,d7,f5,c5,d6,e5,d4:
- fen:8/8/8/4H3/8/8/8/8 c5,e7,g5,e3,b5,e8,h5,e2,d4,d6,f6,f4:
+
+
+
+
+
Moves of the Woody rook on the left, and of the Half-duck on the right.
@@ -120,7 +155,12 @@
Source
-Chess with different armies on chessvariants.com.
+
+
+ Chess with different armies
+
+ on chessvariants.com.
+
diff --git a/variants/Cwda/rules.html b/variants/Cwda/rules.html
index cbd2d72..dac6d17 100644
--- a/variants/Cwda/rules.html
+++ b/variants/Cwda/rules.html
@@ -5,8 +5,10 @@
.
-
- Full rules description.
-
+
+
+ Full rules description.
+
+
Ralph Betza (1996).
diff --git a/variants/Dark/rules.html b/variants/Dark/rules.html
index 137a626..66d2828 100644
--- a/variants/Dark/rules.html
+++ b/variants/Dark/rules.html
@@ -2,8 +2,10 @@
Win by capturing the enemy king.
-
- Wikipedia page.
-
+
+
+ Wikipedia page.
+
+
Jens Baek Nielsen (1997).
diff --git a/variants/Doublemove/rules.html b/variants/Doublemove/rules.html
index 5ac21d4..2a74cbf 100644
--- a/variants/Doublemove/rules.html
+++ b/variants/Doublemove/rules.html
@@ -1,7 +1,9 @@
After the initial white move, each player moves twice on each turn.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Albert Fortis (1922).
diff --git a/variants/Giveaway/rules.html b/variants/Giveaway/rules.html
index 3e257bd..54e40aa 100644
--- a/variants/Giveaway/rules.html
+++ b/variants/Giveaway/rules.html
@@ -1,5 +1,7 @@
Win by losing all your material, or get stalemated.
-
- lichess page.
-
+
+
+ lichess page.
+
+
diff --git a/variants/Hex/rules.html b/variants/Hex/rules.html
index 15a91b1..b4d7628 100644
--- a/variants/Hex/rules.html
+++ b/variants/Hex/rules.html
@@ -1,11 +1,15 @@
Win by connecting both edges of your color.
-
- Detailed rules.
-
+
+
+ Detailed rules.
+
+
-
- A Strategy Guide.
-
+
+
+ A Strategy Guide.
+
+
Piet Hein (1942).
diff --git a/variants/Madrasi/rules.html b/variants/Madrasi/rules.html
index 741869c..96627ff 100644
--- a/variants/Madrasi/rules.html
+++ b/variants/Madrasi/rules.html
@@ -1,7 +1,9 @@
Pieces of same nature attacking each other are immobilized.
-
- Wikipedia page.
-
+
+
+ Wikipedia page.
+
+
Abdul J. Karwathar (1979).
diff --git a/variants/Progressive/rules.html b/variants/Progressive/rules.html
index 0521495..b957f45 100644
--- a/variants/Progressive/rules.html
+++ b/variants/Progressive/rules.html
@@ -3,6 +3,8 @@
then White play 3, and so on.
-
- Wikipedia page.
-
+
+
+ Wikipedia page.
+
+
diff --git a/variants/Recycle/rules.html b/variants/Recycle/rules.html
index ceea70c..badd18b 100644
--- a/variants/Recycle/rules.html
+++ b/variants/Recycle/rules.html
@@ -3,8 +3,10 @@
can be self-captured and dropped later in the game.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Robert Huber (2000).
diff --git a/variants/Refusal/rules.html b/variants/Refusal/rules.html
index 5c0b533..0eadaef 100644
--- a/variants/Refusal/rules.html
+++ b/variants/Refusal/rules.html
@@ -3,8 +3,10 @@
Different pawn promotions count as different moves.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Fred Galvin (1958).
diff --git a/variants/Rifle/rules.html b/variants/Rifle/rules.html
index 650264b..79084a5 100644
--- a/variants/Rifle/rules.html
+++ b/variants/Rifle/rules.html
@@ -1,7 +1,9 @@
Pieces capture (as usual) without moving.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
William Buehler Seabrook (1921).
diff --git a/variants/Suction/rules.html b/variants/Suction/rules.html
index a9cfb2e..9ca71e4 100644
--- a/variants/Suction/rules.html
+++ b/variants/Suction/rules.html
@@ -5,8 +5,10 @@
Win by bringing the enemy king on your first rank.
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Nathaniel Virgo (2018).
diff --git a/variants/_Antiking/rules.html b/variants/_Antiking/rules.html
index e850c5e..e52edbb 100644
--- a/variants/_Antiking/rules.html
+++ b/variants/_Antiking/rules.html
@@ -18,8 +18,10 @@
-
- chessvariants page.
-
+
+
+ chessvariants page.
+
+
Peter Aronson (2002).
--
2.48.1
From 5212758164eaa08e382b7bc281f87999c8af352b Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Sat, 30 Dec 2023 20:58:25 +0100
Subject: [PATCH 03/16] Draft Diamond variant
---
base_rules.js | 15 ++++--
variants.js | 2 +-
variants/Diamond/class.js | 96 +++++++++++++++++++++++++++++++++++++
variants/Diamond/rules.html | 13 +++++
variants/Diamond/style.css | 1 +
5 files changed, 122 insertions(+), 5 deletions(-)
create mode 100644 variants/Diamond/class.js
create mode 100644 variants/Diamond/rules.html
create mode 100644 variants/Diamond/style.css
diff --git a/base_rules.js b/base_rules.js
index 757707d..0f89cc4 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -579,11 +579,18 @@ export default class ChessRules {
// Get SVG board (background, no pieces)
getSvgChessboard() {
- const flipped = this.flippedBoard;
let board = `
`;
+ board += this.getBaseSvgChessboard();
+ board += " ";
+ return board;
+ }
+
+ getBaseSvgChessboard() {
+ let board = "";
+ const flipped = this.flippedBoard;
for (let i=0; i < this.size.x; i++) {
for (let j=0; j < this.size.y; j++) {
if (!this.onBoard(i, j))
@@ -605,7 +612,6 @@ export default class ChessRules {
/>`;
}
}
- board += "";
return board;
}
@@ -2341,10 +2347,11 @@ export default class ChessRules {
if (this.options["teleport"]) {
if (
this.subTurnTeleport == 1 &&
- move.vanish.length > move.appear.length &&
+ move.vanish.length == 2 &&
+ move.appear.length == 1 &&
move.vanish[1].c == this.turn
) {
- const v = move.vanish[move.vanish.length - 1];
+ const v = move.vanish[1];
this.captured = {x: v.x, y: v.y, c: v.c, p: v.p};
this.subTurnTeleport = 2;
return;
diff --git a/variants.js b/variants.js
index 524538c..c9cdc98 100644
--- a/variants.js
+++ b/variants.js
@@ -41,7 +41,7 @@ const variants = [
{name: 'Cylinder', desc: 'Neverending rows'},
{name: 'Cwda', desc: 'New teams', disp: 'Different armies'},
{name: 'Dark', desc: 'In the shadow'},
-// {name: 'Diamond', desc: 'Rotating board'},
+ {name: 'Diamond', desc: 'Rotating board'},
// {name: 'Dice', desc: 'Roll the dice'},
// {name: 'Discoduel', desc: 'Enter the disco', disp: 'Disco Duel'},
// {name: 'Dobutsu', desc: "Let's catch the Lion!"},
diff --git a/variants/Diamond/class.js b/variants/Diamond/class.js
new file mode 100644
index 0000000..5145184
--- /dev/null
+++ b/variants/Diamond/class.js
@@ -0,0 +1,96 @@
+import ChessRules from "/base_rules.js";
+import {ArrayFun} from "/utils/array.js";
+import {Random} from "/utils/alea.js";
+
+export default class DiamondRules extends ChessRules {
+
+ get hasFlags() {
+ return false;
+ }
+
+ get hasEnpassant() {
+ return false;
+ }
+
+ getSvgChessboard() {
+ const diagonal = 10 * this.size.y * Math.sqrt(2);
+ const halfDiag = 0.5 * diagonal;
+ const deltaTrans = 10 * this.size.y * (Math.sqrt(2) - 1) / 2;
+ let board = `
+ `;
+ board += ``;
+ board += this.getBaseSvgChessboard();
+ board += " ";
+ return board;
+ }
+
+ getPieceWidth(rwidth) {
+ return (0.95 * rwidth / (Math.sqrt(2) * this.size.y));
+ }
+
+ getPixelPosition(i, j, r) {
+ if (i < 0 || j < 0 || typeof i == "string")
+ return super.getPixelPosition(i, j, r);
+ const sqSize = this.getPieceWidth(r.width) / 0.95;
+ const flipped = this.flippedBoard;
+ i = (flipped ? this.size.x - 1 - i : i);
+ j = (flipped ? this.size.y - 1 - j : j);
+ const sq2 = Math.sqrt(2);
+ const shift = [- sqSize / 2, sqSize * (sq2 - 1) / 2];
+ const x = (j - i) * sqSize / sq2 + shift[0] + r.width / 2;
+ const y = (i + j) * sqSize / sq2 + shift[1];
+ return [r.x + x, r.y + y];
+ }
+
+ genRandInitBaseFen() {
+ if (this.options["randomness"] == 0) {
+ return {
+ fen: "krbp4/rqnp4/nbpp4/pppp4/4PPPP/4PPBN/4PNQR/4PBRK",
+ o: {}
+ };
+ }
+ let pieces = { w: new Array(8), b: new Array(8) };
+ for (let c of ["w", "b"]) {
+ if (c == 'b' && options.randomness == 1) {
+ pieces['b'] = pieces['w'];
+ break;
+ }
+ // Get random squares for every piece, totally freely
+ let positions = Random.shuffle(ArrayFun.range(8));
+ const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
+ const rem2 = positions[0] % 2;
+ if (rem2 == positions[1] % 2) {
+ // Fix bishops (on different colors)
+ for (let i=2; i<8; i++) {
+ if (positions[i] % 2 != rem2) {
+ [positions[1], positions[i]] = [positions[i], positions[1]];
+ break;
+ }
+ }
+ }
+ for (let i = 0; i < 8; i++)
+ pieces[c][positions[i]] = composition[i];
+ }
+ const fen = (
+ pieces["b"].slice(0, 3).join("") + "p4/" +
+ pieces["b"].slice(3, 6).join("") + "p4/" +
+ pieces["b"].slice(6, 8).join("") + "pp4/" +
+ "pppp4/4PPPP/" +
+ "4PP" + pieces["w"].slice(6, 8).reverse().join("").toUpperCase() + "/" +
+ "4P" + pieces["w"].slice(3, 6).reverse().join("").toUpperCase() + "/" +
+ "4P" + pieces["w"].slice(0, 3).reverse().join("").toUpperCase());
+ return { fen: fen, o: {} };
+ }
+
+ pieces(color, x, y) {
+ let res = super.pieces(color, x, y);
+ const pawnShift = this.getPawnShift(color || 'w');
+ res['p'].moves = [{steps: [[pawnShift, pawnShift]], range: 1}];
+ res['p'].attack = [{steps: [[0, pawnShift], [pawnShift, 0]], range: 1}];
+ return res;
+ }
+
+};
diff --git a/variants/Diamond/rules.html b/variants/Diamond/rules.html
new file mode 100644
index 0000000..9f172b1
--- /dev/null
+++ b/variants/Diamond/rules.html
@@ -0,0 +1,13 @@
+The board is rotated by 45°, and then the game follow usual rules.
+
+Pawns move forward "one diagonal", and capture forward "orthogonally".
+
+
+ See
+
+ Diamond Chess
+
+ on chessvariants.com.
+
+
+James Alexander Porterfield Rynd (1886).
diff --git a/variants/Diamond/style.css b/variants/Diamond/style.css
new file mode 100644
index 0000000..a3550bc
--- /dev/null
+++ b/variants/Diamond/style.css
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
--
2.48.1
From d01282a527e60af95f2a71deee1fbac9c0dd26be Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Mon, 1 Jan 2024 08:51:47 +0100
Subject: [PATCH 04/16] Draft Dice chess
---
variants.js | 2 +-
variants/Dice/class.js | 108 +++++++++++++++++++++++++++++++++++++++
variants/Dice/rules.html | 3 ++
variants/Dice/style.css | 12 +++++
4 files changed, 124 insertions(+), 1 deletion(-)
create mode 100644 variants/Dice/class.js
create mode 100644 variants/Dice/rules.html
create mode 100644 variants/Dice/style.css
diff --git a/variants.js b/variants.js
index c9cdc98..cad081a 100644
--- a/variants.js
+++ b/variants.js
@@ -42,7 +42,7 @@ const variants = [
{name: 'Cwda', desc: 'New teams', disp: 'Different armies'},
{name: 'Dark', desc: 'In the shadow'},
{name: 'Diamond', desc: 'Rotating board'},
-// {name: 'Dice', desc: 'Roll the dice'},
+ {name: 'Dice', desc: 'Roll the dice'},
// {name: 'Discoduel', desc: 'Enter the disco', disp: 'Disco Duel'},
// {name: 'Dobutsu', desc: "Let's catch the Lion!"},
// {name: 'Doublearmy', desc: '64 pieces on the board', disp: 'Double Army'},
diff --git a/variants/Dice/class.js b/variants/Dice/class.js
new file mode 100644
index 0000000..8c3eed4
--- /dev/null
+++ b/variants/Dice/class.js
@@ -0,0 +1,108 @@
+import ChessRules from "/base_rules.js";
+import {Random} from "/utils/alea.js";
+
+export default class DiceRules extends ChessRules {
+
+ static get Options() {
+ let res = C.Options;
+ res.select["defaut"] = 2;
+ return {
+ select: res.select,
+ input: [
+ {
+ label: "Biased alea",
+ variable: "biased",
+ type: "checkbox",
+ defaut: true
+ },
+ {
+ label: "Falling pawn",
+ variable: "pawnfall",
+ type: "checkbox",
+ defaut: false
+ }
+ ],
+ styles: [
+ "atomic",
+ "capture",
+ "crazyhouse",
+ "cylinder",
+ "madrasi",
+ "recycle",
+ "rifle",
+ "zen"
+ ]
+ };
+ }
+
+ getPartFen(o) {
+ let toplay = '';
+ if (o.init) {
+ let canMove = (this.options["biased"]
+ ? Array(8).fill('p').concat(Array(2).fill('n'))
+ : ['p', 'n']);
+ toplay = canMove[Random.randInt(canMove.length)];
+ }
+ return Object.assign(
+ { toplay: (o.init ? toplay : this.getRandomPiece(this.turn)) },
+ super.getPartFen(o)
+ );
+ }
+
+ constructor(o) {
+ super(o);
+ this.afterPlay = (move_s, newTurn, ops) => {
+ // Movestack contains only one move:
+ move_s[0].toplay = this.getRandomPiece(this.turn);
+ super.displayMessage(this.message, move_s[0].toplay);
+ o.afterPlay(move_s, newTurn, ops);
+ };
+ }
+
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
+ this.toplay = fenParsed.toplay;
+ this.message = document.createElement("div");
+ C.AddClass_es(this.message, "piece-text");
+ this.message.innerHTML = this.toplay;
+ let container = document.getElementById(this.containerId);
+ container.appendChild(this.message);
+ }
+
+ getRandomPiece(color) {
+ // Find pieces which can move and roll a (biased) dice
+ let canMove = [];
+ for (let i=0; i<8; i++) {
+ for (let j=0; j<8; j++) {
+ if (this.board[i][j] != "" && this.getColor(i, j) == color) {
+ const piece = this.getPiece(i, j);
+ if (this.findDestSquares([i, j], {one: true}))
+ canMove.push(piece);
+ }
+ }
+ }
+ if (!this.options["biased"])
+ canMove = [...new Set(canMove)];
+ return canMove[Random.randInt(canMove.length)];
+ }
+
+ postProcessPotentialMoves(moves) {
+ return super.postProcessPotentialMoves(moves).filter(m => {
+ return (
+ (m.appear.length >= 1 && m.appear[0].p == this.toplay) ||
+ (m.vanish.length >= 1 && m.vanish[0].p == this.toplay)
+ );
+ });
+ }
+
+ filterValid(moves) {
+ return moves;
+ }
+
+ playReceivedMove(moves, callback) {
+ this.toplay = moves[0].toplay; //only one move
+ super.displayMessage(this.message, this.toplay);
+ super.playReceivedMove(moves, callback);
+ }
+
+};
diff --git a/variants/Dice/rules.html b/variants/Dice/rules.html
new file mode 100644
index 0000000..04e8682
--- /dev/null
+++ b/variants/Dice/rules.html
@@ -0,0 +1,3 @@
+Play the piece type determined by a dice roll.
+
+There is no check or checkmate: the goal is to capture the king.
diff --git a/variants/Dice/style.css b/variants/Dice/style.css
new file mode 100644
index 0000000..5881bc3
--- /dev/null
+++ b/variants/Dice/style.css
@@ -0,0 +1,12 @@
+@import url("/base_pieces.css");
+
+div.piece-text {
+ position: relative;
+ margin-top: 15px;
+ width: 100%;
+ text-align: center;
+ background-color: transparent;
+ color: darkred;
+ font-weight: bold;
+ font-size: 2em;
+}
--
2.48.1
From 7fbcb53de45ba7b7aed99d6087928779713810b1 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Mon, 1 Jan 2024 16:34:18 +0100
Subject: [PATCH 05/16] update
---
variants/Dice/class.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/variants/Dice/class.js b/variants/Dice/class.js
index 8c3eed4..02e4f6d 100644
--- a/variants/Dice/class.js
+++ b/variants/Dice/class.js
@@ -54,7 +54,8 @@ export default class DiceRules extends ChessRules {
this.afterPlay = (move_s, newTurn, ops) => {
// Movestack contains only one move:
move_s[0].toplay = this.getRandomPiece(this.turn);
- super.displayMessage(this.message, move_s[0].toplay);
+ super.displayMessage(
+ this.message, "To play: " + move_s[0].toplay.toUpperCase());
o.afterPlay(move_s, newTurn, ops);
};
}
@@ -64,7 +65,7 @@ export default class DiceRules extends ChessRules {
this.toplay = fenParsed.toplay;
this.message = document.createElement("div");
C.AddClass_es(this.message, "piece-text");
- this.message.innerHTML = this.toplay;
+ this.message.innerHTML = "To play: " + this.toplay.toUpperCase();
let container = document.getElementById(this.containerId);
container.appendChild(this.message);
}
@@ -101,7 +102,8 @@ export default class DiceRules extends ChessRules {
playReceivedMove(moves, callback) {
this.toplay = moves[0].toplay; //only one move
- super.displayMessage(this.message, this.toplay);
+ super.displayMessage(
+ this.message, "To play: " + this.toplay.toUpperCase());
super.playReceivedMove(moves, callback);
}
--
2.48.1
From 04d93b7bb3b64ecdf3fb7219eee42879f0200b88 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Tue, 2 Jan 2024 13:08:07 +0100
Subject: [PATCH 06/16] update
---
variants/Dice/class.js | 36 +++++++++++++++++++++++++++++++-----
variants/Dice/style.css | 13 ++++++++++++-
2 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/variants/Dice/class.js b/variants/Dice/class.js
index 02e4f6d..1bb6f1f 100644
--- a/variants/Dice/class.js
+++ b/variants/Dice/class.js
@@ -54,20 +54,47 @@ export default class DiceRules extends ChessRules {
this.afterPlay = (move_s, newTurn, ops) => {
// Movestack contains only one move:
move_s[0].toplay = this.getRandomPiece(this.turn);
- super.displayMessage(
- this.message, "To play: " + move_s[0].toplay.toUpperCase());
+ this.toplay = move_s[0].toplay;
+ this.displayMessage(move_s[0].toplay,
+ C.GetOppTurn(move_s[0].appear[0].c));
o.afterPlay(move_s, newTurn, ops);
};
}
+ static get PieceToUnicode() {
+ return {
+ 'K': "♔",
+ 'Q': "♕",
+ 'R': "♖",
+ 'B': "♗",
+ 'N': "♘",
+ 'P': "♙",
+ 'k': "♚",
+ 'q': "♛",
+ 'r': "♜",
+ 'b': "♝",
+ 'n': "♞",
+ 'p': "♟"
+ };
+ }
+
+ displayMessage(piece, color) {
+ if (color == 'w')
+ piece = piece.toUpperCase();
+ super.displayMessage(this.message,
+ 'to play: ' +
+ '' + V.PieceToUnicode[piece] + ' '
+ );
+ }
+
setOtherVariables(fenParsed) {
super.setOtherVariables(fenParsed);
this.toplay = fenParsed.toplay;
this.message = document.createElement("div");
C.AddClass_es(this.message, "piece-text");
- this.message.innerHTML = "To play: " + this.toplay.toUpperCase();
let container = document.getElementById(this.containerId);
container.appendChild(this.message);
+ this.displayMessage(this.toplay, fenParsed.turn);
}
getRandomPiece(color) {
@@ -102,8 +129,7 @@ export default class DiceRules extends ChessRules {
playReceivedMove(moves, callback) {
this.toplay = moves[0].toplay; //only one move
- super.displayMessage(
- this.message, "To play: " + this.toplay.toUpperCase());
+ this.displayMessage(this.toplay, C.GetOppTurn(moves[0].appear[0].c));
super.playReceivedMove(moves, callback);
}
diff --git a/variants/Dice/style.css b/variants/Dice/style.css
index 5881bc3..c9ed898 100644
--- a/variants/Dice/style.css
+++ b/variants/Dice/style.css
@@ -1,5 +1,11 @@
@import url("/base_pieces.css");
+/* doesn't work:
+@font-face {
+ font-family: chess-font;
+ src: url(/assets/FreeSerifBold-rdMp.otf);
+} */
+
div.piece-text {
position: relative;
margin-top: 15px;
@@ -8,5 +14,10 @@ div.piece-text {
background-color: transparent;
color: darkred;
font-weight: bold;
- font-size: 2em;
+ font-size: 1.7em;
}
+
+/*
+div.piece-text > span.symb {
+ font-family: chess-font;
+} */
--
2.48.1
From 03883a0d1495bc6e6fadc3a11aa99286aba5c9e1 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Tue, 2 Jan 2024 16:03:28 +0100
Subject: [PATCH 07/16] Fix Dice display
---
variants/Dice/class.js | 32 ++++++++++++--------------------
variants/Dice/style.css | 11 ++++++-----
2 files changed, 18 insertions(+), 25 deletions(-)
diff --git a/variants/Dice/class.js b/variants/Dice/class.js
index 1bb6f1f..53836eb 100644
--- a/variants/Dice/class.js
+++ b/variants/Dice/class.js
@@ -61,29 +61,21 @@ export default class DiceRules extends ChessRules {
};
}
- static get PieceToUnicode() {
- return {
- 'K': "♔",
- 'Q': "♕",
- 'R': "♖",
- 'B': "♗",
- 'N': "♘",
- 'P': "♙",
- 'k': "♚",
- 'q': "♛",
- 'r': "♜",
- 'b': "♝",
- 'n': "♞",
- 'p': "♟"
- };
- }
-
displayMessage(piece, color) {
- if (color == 'w')
- piece = piece.toUpperCase();
+ if (color == 'b') {
+ const blackPieceToCode = {
+ 'k': 'l',
+ 'p': 'o',
+ 'n': 'm',
+ 'b': 'v',
+ 'q': 'w',
+ 'r': 't'
+ };
+ piece = blackPieceToCode[piece];
+ }
super.displayMessage(this.message,
'to play: ' +
- '' + V.PieceToUnicode[piece] + ' '
+ '' + piece + ' '
);
}
diff --git a/variants/Dice/style.css b/variants/Dice/style.css
index c9ed898..ad11010 100644
--- a/variants/Dice/style.css
+++ b/variants/Dice/style.css
@@ -1,10 +1,9 @@
@import url("/base_pieces.css");
-/* doesn't work:
@font-face {
font-family: chess-font;
- src: url(/assets/FreeSerifBold-rdMp.otf);
-} */
+ src: url(/assets/MERIFONT.TTF);
+}
div.piece-text {
position: relative;
@@ -17,7 +16,9 @@ div.piece-text {
font-size: 1.7em;
}
-/*
div.piece-text > span.symb {
font-family: chess-font;
-} */
+ display: inline-block;
+ position: relative;
+ top: 5px;
+}
--
2.48.1
From 130a166fd08355be5f2dfc923777c1c6d03f09ce Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Wed, 3 Jan 2024 01:40:00 +0100
Subject: [PATCH 08/16] Chakart: fixing attempt
---
variants/Chakart/class.js | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js
index 2b502b6..3454d8f 100644
--- a/variants/Chakart/class.js
+++ b/variants/Chakart/class.js
@@ -130,6 +130,12 @@ export default class ChakartRules extends ChessRules {
);
}
+ isKing(x, y, p) {
+ if (!p)
+ p = this.getPiece(x, y);
+ return ['k', 'l'].includes(p);
+ }
+
genRandInitBaseFen() {
const s = FenUtil.setupPieces(
['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
@@ -630,19 +636,25 @@ export default class ChakartRules extends ChessRules {
});
break;
case "koopa":
- // Reverse move
+ // Reverse move, if possible
em = new Move({
- appear: [
- new PiPo({
- x: move.start.x, y: move.start.y, c: color, p: move.appear[0].p
- })
- ],
+ appear: [],
vanish: [
new PiPo({
x: move.end.x, y: move.end.y, c: color, p: move.appear[0].p
})
- ]
+ ],
+ end: {x: move.start.x, y: move.start.y} //may be irrelevant
});
+ em.koopa = true; //avoid applying effect
+ if (move.vanish.length == 0)
+ // After toadette+drop, just erase piece
+ break;
+ em.appear.push(
+ new PiPo({
+ x: move.start.x, y: move.start.y, c: color, p: move.appear[0].p
+ })
+ );
if (this.board[move.start.x][move.start.y] != "") {
// Pawn or knight let something on init square
em.vanish.push(new PiPo({
@@ -652,7 +664,6 @@ export default class ChakartRules extends ChessRules {
p: this.getPiece(move.start.x, move.start.y)
}));
}
- em.koopa = true; //avoid applying effect
break;
case "chomp":
// Eat piece
--
2.48.1
From 3232aba3419f129c70d5edd9a4ded1fefc146ea0 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Wed, 3 Jan 2024 15:38:58 +0100
Subject: [PATCH 09/16] Add Discoduel, draft Dobutsu, some code cleaning
---
base_rules.js | 20 +-
pieces/Dobutsu/LICENSE.txt | 395 ++++++++++++++++++++++++++++++++
pieces/Dobutsu/README.md | 13 ++
pieces/Dobutsu/chick.svg | 139 +++++++++++
pieces/Dobutsu/elephant.svg | 176 ++++++++++++++
pieces/Dobutsu/giraffe.svg | 186 +++++++++++++++
pieces/Dobutsu/hen.svg | 156 +++++++++++++
pieces/Dobutsu/lion.svg | 201 ++++++++++++++++
pieces/Dobutsu/rev_chick.svg | 139 +++++++++++
pieces/Dobutsu/rev_elephant.svg | 176 ++++++++++++++
pieces/Dobutsu/rev_giraffe.svg | 186 +++++++++++++++
pieces/Dobutsu/rev_hen.svg | 156 +++++++++++++
pieces/Dobutsu/rev_lion.svg | 201 ++++++++++++++++
utils/array.js | 8 +-
variants.js | 4 +-
variants/Avalanche/class.js | 2 +-
variants/Chaining/class.js | 4 +-
variants/Clorange/class.js | 13 +-
variants/Convert/class.js | 4 +-
variants/Coregal/class.js | 4 +-
variants/Discoduel/class.js | 51 +++++
variants/Discoduel/rules.html | 5 +
variants/Discoduel/style.css | 1 +
variants/Dobutsu/class.js | 100 ++++++++
variants/Dobutsu/rules.html | 12 +
variants/Dobutsu/style.css | 34 +++
26 files changed, 2355 insertions(+), 31 deletions(-)
create mode 100644 pieces/Dobutsu/LICENSE.txt
create mode 100644 pieces/Dobutsu/README.md
create mode 100644 pieces/Dobutsu/chick.svg
create mode 100644 pieces/Dobutsu/elephant.svg
create mode 100644 pieces/Dobutsu/giraffe.svg
create mode 100644 pieces/Dobutsu/hen.svg
create mode 100644 pieces/Dobutsu/lion.svg
create mode 100644 pieces/Dobutsu/rev_chick.svg
create mode 100644 pieces/Dobutsu/rev_elephant.svg
create mode 100644 pieces/Dobutsu/rev_giraffe.svg
create mode 100644 pieces/Dobutsu/rev_hen.svg
create mode 100644 pieces/Dobutsu/rev_lion.svg
create mode 100644 variants/Discoduel/class.js
create mode 100644 variants/Discoduel/rules.html
create mode 100644 variants/Discoduel/style.css
create mode 100644 variants/Dobutsu/class.js
create mode 100644 variants/Dobutsu/rules.html
create mode 100644 variants/Dobutsu/style.css
diff --git a/base_rules.js b/base_rules.js
index 0f89cc4..217d055 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -338,7 +338,7 @@ export default class ChessRules {
getReserveFen(o) {
if (o.init)
- return "000000000000";
+ return Array(2 * V.ReserveArray.length).fill('0').join("");
return (
['w', 'b'].map(c => Object.values(this.reserve[c]).join("")).join("")
);
@@ -416,14 +416,14 @@ export default class ChessRules {
}
// Some additional variables from FEN (variant dependant)
- setOtherVariables(fenParsed, pieceArray) {
+ setOtherVariables(fenParsed) {
// Set flags and enpassant:
if (this.hasFlags)
this.setFlags(fenParsed.flags);
if (this.hasEnpassant)
this.epSquare = this.getEpSquare(fenParsed.enpassant);
if (this.hasReserve && !this.isDiagram)
- this.initReserves(fenParsed.reserve, pieceArray);
+ this.initReserves(fenParsed.reserve);
if (this.options["crazyhouse"])
this.initIspawn(fenParsed.ispawn);
if (this.options["teleport"]) {
@@ -441,14 +441,16 @@ export default class ChessRules {
}
// ordering as in pieces() p,r,n,b,q,k
- initReserves(reserveStr, pieceArray) {
- if (!pieceArray)
- pieceArray = ['p', 'r', 'n', 'b', 'q', 'k'];
+ static get ReserveArray() {
+ return ['p', 'r', 'n', 'b', 'q', 'k'];
+ }
+
+ initReserves(reserveStr) {
const counts = reserveStr.split("").map(c => parseInt(c, 36));
- const L = pieceArray.length;
+ const L = V.ReserveArray.length;
this.reserve = {
- w: ArrayFun.toObject(pieceArray, counts.slice(0, L)),
- b: ArrayFun.toObject(pieceArray, counts.slice(L, 2 * L))
+ w: ArrayFun.toObject(V.ReserveArray, counts.slice(0, L)),
+ b: ArrayFun.toObject(V.ReserveArray, counts.slice(L, 2 * L))
};
}
diff --git a/pieces/Dobutsu/LICENSE.txt b/pieces/Dobutsu/LICENSE.txt
new file mode 100644
index 0000000..2f244ac
--- /dev/null
+++ b/pieces/Dobutsu/LICENSE.txt
@@ -0,0 +1,395 @@
+Attribution 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+ Considerations for licensors: Our public licenses are
+ intended for use by those authorized to give the public
+ permission to use material in ways otherwise restricted by
+ copyright and certain other rights. Our licenses are
+ irrevocable. Licensors should read and understand the terms
+ and conditions of the license they choose before applying it.
+ Licensors should also secure all rights necessary before
+ applying our licenses so that the public can reuse the
+ material as expected. Licensors should clearly mark any
+ material not subject to the license. This includes other CC-
+ licensed material, or material used under an exception or
+ limitation to copyright. More considerations for licensors:
+ wiki.creativecommons.org/Considerations_for_licensors
+
+ Considerations for the public: By using one of our public
+ licenses, a licensor grants the public permission to use the
+ licensed material under specified terms and conditions. If
+ the licensor's permission is not necessary for any reason--for
+ example, because of any applicable exception or limitation to
+ copyright--then that use is not regulated by the license. Our
+ licenses grant only permissions under copyright and certain
+ other rights that a licensor has authority to grant. Use of
+ the licensed material may still be restricted for other
+ reasons, including because others have copyright or other
+ rights in the material. A licensor may make special requests,
+ such as asking that all changes be marked or described.
+ Although not required by our licenses, you are encouraged to
+ respect those requests where reasonable. More_considerations
+ for the public:
+ wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution 4.0 International Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution 4.0 International Public License ("Public License"). To the
+extent this Public License may be interpreted as a contract, You are
+granted the Licensed Rights in consideration of Your acceptance of
+these terms and conditions, and the Licensor grants You such rights in
+consideration of benefits the Licensor receives from making the
+Licensed Material available under these terms and conditions.
+
+
+Section 1 -- Definitions.
+
+ a. Adapted Material means material subject to Copyright and Similar
+ Rights that is derived from or based upon the Licensed Material
+ and in which the Licensed Material is translated, altered,
+ arranged, transformed, or otherwise modified in a manner requiring
+ permission under the Copyright and Similar Rights held by the
+ Licensor. For purposes of this Public License, where the Licensed
+ Material is a musical work, performance, or sound recording,
+ Adapted Material is always produced where the Licensed Material is
+ synched in timed relation with a moving image.
+
+ b. Adapter's License means the license You apply to Your Copyright
+ and Similar Rights in Your contributions to Adapted Material in
+ accordance with the terms and conditions of this Public License.
+
+ c. Copyright and Similar Rights means copyright and/or similar rights
+ closely related to copyright including, without limitation,
+ performance, broadcast, sound recording, and Sui Generis Database
+ Rights, without regard to how the rights are labeled or
+ categorized. For purposes of this Public License, the rights
+ specified in Section 2(b)(1)-(2) are not Copyright and Similar
+ Rights.
+
+ d. Effective Technological Measures means those measures that, in the
+ absence of proper authority, may not be circumvented under laws
+ fulfilling obligations under Article 11 of the WIPO Copyright
+ Treaty adopted on December 20, 1996, and/or similar international
+ agreements.
+
+ e. Exceptions and Limitations means fair use, fair dealing, and/or
+ any other exception or limitation to Copyright and Similar Rights
+ that applies to Your use of the Licensed Material.
+
+ f. Licensed Material means the artistic or literary work, database,
+ or other material to which the Licensor applied this Public
+ License.
+
+ g. Licensed Rights means the rights granted to You subject to the
+ terms and conditions of this Public License, which are limited to
+ all Copyright and Similar Rights that apply to Your use of the
+ Licensed Material and that the Licensor has authority to license.
+
+ h. Licensor means the individual(s) or entity(ies) granting rights
+ under this Public License.
+
+ i. Share means to provide material to the public by any means or
+ process that requires permission under the Licensed Rights, such
+ as reproduction, public display, public performance, distribution,
+ dissemination, communication, or importation, and to make material
+ available to the public including in ways that members of the
+ public may access the material from a place and at a time
+ individually chosen by them.
+
+ j. Sui Generis Database Rights means rights other than copyright
+ resulting from Directive 96/9/EC of the European Parliament and of
+ the Council of 11 March 1996 on the legal protection of databases,
+ as amended and/or succeeded, as well as other essentially
+ equivalent rights anywhere in the world.
+
+ k. You means the individual or entity exercising the Licensed Rights
+ under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+ a. License grant.
+
+ 1. Subject to the terms and conditions of this Public License,
+ the Licensor hereby grants You a worldwide, royalty-free,
+ non-sublicensable, non-exclusive, irrevocable license to
+ exercise the Licensed Rights in the Licensed Material to:
+
+ a. reproduce and Share the Licensed Material, in whole or
+ in part; and
+
+ b. produce, reproduce, and Share Adapted Material.
+
+ 2. Exceptions and Limitations. For the avoidance of doubt, where
+ Exceptions and Limitations apply to Your use, this Public
+ License does not apply, and You do not need to comply with
+ its terms and conditions.
+
+ 3. Term. The term of this Public License is specified in Section
+ 6(a).
+
+ 4. Media and formats; technical modifications allowed. The
+ Licensor authorizes You to exercise the Licensed Rights in
+ all media and formats whether now known or hereafter created,
+ and to make technical modifications necessary to do so. The
+ Licensor waives and/or agrees not to assert any right or
+ authority to forbid You from making technical modifications
+ necessary to exercise the Licensed Rights, including
+ technical modifications necessary to circumvent Effective
+ Technological Measures. For purposes of this Public License,
+ simply making modifications authorized by this Section 2(a)
+ (4) never produces Adapted Material.
+
+ 5. Downstream recipients.
+
+ a. Offer from the Licensor -- Licensed Material. Every
+ recipient of the Licensed Material automatically
+ receives an offer from the Licensor to exercise the
+ Licensed Rights under the terms and conditions of this
+ Public License.
+
+ b. No downstream restrictions. You may not offer or impose
+ any additional or different terms or conditions on, or
+ apply any Effective Technological Measures to, the
+ Licensed Material if doing so restricts exercise of the
+ Licensed Rights by any recipient of the Licensed
+ Material.
+
+ 6. No endorsement. Nothing in this Public License constitutes or
+ may be construed as permission to assert or imply that You
+ are, or that Your use of the Licensed Material is, connected
+ with, or sponsored, endorsed, or granted official status by,
+ the Licensor or others designated to receive attribution as
+ provided in Section 3(a)(1)(A)(i).
+
+ b. Other rights.
+
+ 1. Moral rights, such as the right of integrity, are not
+ licensed under this Public License, nor are publicity,
+ privacy, and/or other similar personality rights; however, to
+ the extent possible, the Licensor waives and/or agrees not to
+ assert any such rights held by the Licensor to the limited
+ extent necessary to allow You to exercise the Licensed
+ Rights, but not otherwise.
+
+ 2. Patent and trademark rights are not licensed under this
+ Public License.
+
+ 3. To the extent possible, the Licensor waives any right to
+ collect royalties from You for the exercise of the Licensed
+ Rights, whether directly or through a collecting society
+ under any voluntary or waivable statutory or compulsory
+ licensing scheme. In all other cases the Licensor expressly
+ reserves any right to collect such royalties.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+ a. Attribution.
+
+ 1. If You Share the Licensed Material (including in modified
+ form), You must:
+
+ a. retain the following if it is supplied by the Licensor
+ with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed
+ Material and any others designated to receive
+ attribution, in any reasonable manner requested by
+ the Licensor (including by pseudonym if
+ designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of
+ warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the
+ extent reasonably practicable;
+
+ b. indicate if You modified the Licensed Material and
+ retain an indication of any previous modifications; and
+
+ c. indicate the Licensed Material is licensed under this
+ Public License, and include the text of, or the URI or
+ hyperlink to, this Public License.
+
+ 2. You may satisfy the conditions in Section 3(a)(1) in any
+ reasonable manner based on the medium, means, and context in
+ which You Share the Licensed Material. For example, it may be
+ reasonable to satisfy the conditions by providing a URI or
+ hyperlink to a resource that includes the required
+ information.
+
+ 3. If requested by the Licensor, You must remove any of the
+ information required by Section 3(a)(1)(A) to the extent
+ reasonably practicable.
+
+ 4. If You Share Adapted Material You produce, the Adapter's
+ License You apply must not prevent recipients of the Adapted
+ Material from complying with this Public License.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+ a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+ to extract, reuse, reproduce, and Share all or a substantial
+ portion of the contents of the database;
+
+ b. if You include all or a substantial portion of the database
+ contents in a database in which You have Sui Generis Database
+ Rights, then the database in which You have Sui Generis Database
+ Rights (but not its individual contents) is Adapted Material; and
+
+ c. You must comply with the conditions in Section 3(a) if You Share
+ all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+ a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+ EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+ AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+ IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+ WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+ ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+ KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+ ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+ b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+ TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+ NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+ INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+ COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+ USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+ DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+ IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+ c. The disclaimer of warranties and limitation of liability provided
+ above shall be interpreted in a manner that, to the extent
+ possible, most closely approximates an absolute disclaimer and
+ waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+ a. This Public License applies for the term of the Copyright and
+ Similar Rights licensed here. However, if You fail to comply with
+ this Public License, then Your rights under this Public License
+ terminate automatically.
+
+ b. Where Your right to use the Licensed Material has terminated under
+ Section 6(a), it reinstates:
+
+ 1. automatically as of the date the violation is cured, provided
+ it is cured within 30 days of Your discovery of the
+ violation; or
+
+ 2. upon express reinstatement by the Licensor.
+
+ For the avoidance of doubt, this Section 6(b) does not affect any
+ right the Licensor may have to seek remedies for Your violations
+ of this Public License.
+
+ c. For the avoidance of doubt, the Licensor may also offer the
+ Licensed Material under separate terms or conditions or stop
+ distributing the Licensed Material at any time; however, doing so
+ will not terminate this Public License.
+
+ d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+ License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+ a. The Licensor shall not be bound by any additional or different
+ terms or conditions communicated by You unless expressly agreed.
+
+ b. Any arrangements, understandings, or agreements regarding the
+ Licensed Material not stated herein are separate from and
+ independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+ a. For the avoidance of doubt, this Public License does not, and
+ shall not be interpreted to, reduce, limit, restrict, or impose
+ conditions on any use of the Licensed Material that could lawfully
+ be made without permission under this Public License.
+
+ b. To the extent possible, if any provision of this Public License is
+ deemed unenforceable, it shall be automatically reformed to the
+ minimum extent necessary to make it enforceable. If the provision
+ cannot be reformed, it shall be severed from this Public License
+ without affecting the enforceability of the remaining terms and
+ conditions.
+
+ c. No term or condition of this Public License will be waived and no
+ failure to comply consented to unless expressly agreed to by the
+ Licensor.
+
+ d. Nothing in this Public License constitutes or may be interpreted
+ as a limitation upon, or waiver of, any privileges and immunities
+ that apply to the Licensor or You, including from the legal
+ processes of any jurisdiction or authority.
+
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the âLicensor.â The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/pieces/Dobutsu/README.md b/pieces/Dobutsu/README.md
new file mode 100644
index 0000000..73ed708
--- /dev/null
+++ b/pieces/Dobutsu/README.md
@@ -0,0 +1,13 @@
+[Couch Tomato - I presume]
+
+I recreated all of the original [Doubutsu](https://en.wikipedia.org/wiki/D%C5%8Dbutsu_sh%C5%8Dgi) pieces in Inkscape SVG format.
+
+Credits: the original Doubutsu pieces were created by [Madoka Kitato](https://en.wikipedia.org/wiki/Madoka_Kitao).
+
+(also: [screenshot of the gshogi version](https://raw.githubusercontent.com/Ka-hu/shogi-pieces/master/_screenshots/scrot_doubutsu_gshogi.png))
+
+![doubutsu screenshot](https://raw.githubusercontent.com/Ka-hu/shogi-pieces/master/_screenshots/scrot_doubutsu_xboard.png)
+
+## License
+
+Where it's not stated otherwise, my work is lincensed under [CC-BY-4.0](https://choosealicense.com/licenses/cc-by-4.0)
diff --git a/pieces/Dobutsu/chick.svg b/pieces/Dobutsu/chick.svg
new file mode 100644
index 0000000..69358c7
--- /dev/null
+++ b/pieces/Dobutsu/chick.svg
@@ -0,0 +1,139 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Dobutsu/elephant.svg b/pieces/Dobutsu/elephant.svg
new file mode 100644
index 0000000..c7eb6dc
--- /dev/null
+++ b/pieces/Dobutsu/elephant.svg
@@ -0,0 +1,176 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Dobutsu/giraffe.svg b/pieces/Dobutsu/giraffe.svg
new file mode 100644
index 0000000..14a55a9
--- /dev/null
+++ b/pieces/Dobutsu/giraffe.svg
@@ -0,0 +1,186 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Dobutsu/hen.svg b/pieces/Dobutsu/hen.svg
new file mode 100644
index 0000000..7256bc8
--- /dev/null
+++ b/pieces/Dobutsu/hen.svg
@@ -0,0 +1,156 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Dobutsu/lion.svg b/pieces/Dobutsu/lion.svg
new file mode 100644
index 0000000..8324454
--- /dev/null
+++ b/pieces/Dobutsu/lion.svg
@@ -0,0 +1,201 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Dobutsu/rev_chick.svg b/pieces/Dobutsu/rev_chick.svg
new file mode 100644
index 0000000..bbefcfc
--- /dev/null
+++ b/pieces/Dobutsu/rev_chick.svg
@@ -0,0 +1,139 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Dobutsu/rev_elephant.svg b/pieces/Dobutsu/rev_elephant.svg
new file mode 100644
index 0000000..c8b0b3c
--- /dev/null
+++ b/pieces/Dobutsu/rev_elephant.svg
@@ -0,0 +1,176 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Dobutsu/rev_giraffe.svg b/pieces/Dobutsu/rev_giraffe.svg
new file mode 100644
index 0000000..f6eb024
--- /dev/null
+++ b/pieces/Dobutsu/rev_giraffe.svg
@@ -0,0 +1,186 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Dobutsu/rev_hen.svg b/pieces/Dobutsu/rev_hen.svg
new file mode 100644
index 0000000..e172f8b
--- /dev/null
+++ b/pieces/Dobutsu/rev_hen.svg
@@ -0,0 +1,156 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/Dobutsu/rev_lion.svg b/pieces/Dobutsu/rev_lion.svg
new file mode 100644
index 0000000..33c11d3
--- /dev/null
+++ b/pieces/Dobutsu/rev_lion.svg
@@ -0,0 +1,201 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/utils/array.js b/utils/array.js
index c2cb25f..af8462e 100644
--- a/utils/array.js
+++ b/utils/array.js
@@ -5,8 +5,12 @@ export const ArrayFun = {
return [...Array(size1)].map(() => Array(size2).fill(initElem));
},
- range: function(max) {
- return [...Array(max).keys()];
+ range: function(min, max) {
+ if (!max) {
+ max = min;
+ min = 0;
+ }
+ return [...Array(max - min).keys()].map(k => k + min);
},
toObject: function(keys, values) {
diff --git a/variants.js b/variants.js
index cad081a..f42033e 100644
--- a/variants.js
+++ b/variants.js
@@ -43,8 +43,8 @@ const variants = [
{name: 'Dark', desc: 'In the shadow'},
{name: 'Diamond', desc: 'Rotating board'},
{name: 'Dice', desc: 'Roll the dice'},
-// {name: 'Discoduel', desc: 'Enter the disco', disp: 'Disco Duel'},
-// {name: 'Dobutsu', desc: "Let's catch the Lion!"},
+ {name: 'Discoduel', desc: 'Enter the disco', disp: 'Disco Duel'},
+ {name: 'Dobutsu', desc: "Let's catch the Lion!"},
// {name: 'Doublearmy', desc: '64 pieces on the board', disp: 'Double Army'},
{name: 'Doublemove', desc: 'Double moves'},
// {name: 'Dynamo', desc: 'Push and pull'},
diff --git a/variants/Avalanche/class.js b/variants/Avalanche/class.js
index ee80550..8a4981f 100644
--- a/variants/Avalanche/class.js
+++ b/variants/Avalanche/class.js
@@ -167,7 +167,7 @@ export default class AvalancheRules extends ChessRules {
}
}
- atLeastOneMove(color, lastMove) {
+ atLeastOneMove(color) {
if (this.subTurn == 0)
return true;
return super.atLeastOneMove(color);
diff --git a/variants/Chaining/class.js b/variants/Chaining/class.js
index 9d771da..4ae7208 100644
--- a/variants/Chaining/class.js
+++ b/variants/Chaining/class.js
@@ -20,8 +20,8 @@ export default class ChainingRules extends ChessRules {
return true; //self captures induce chaining
}
- setOtherVariables(fenParsed, pieceArray) {
- super.setOtherVariables(fenParsed, pieceArray);
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
// Stack of "last move" only for intermediate chaining
this.lastMoveEnd = [];
}
diff --git a/variants/Clorange/class.js b/variants/Clorange/class.js
index 485bbd6..e8506db 100644
--- a/variants/Clorange/class.js
+++ b/variants/Clorange/class.js
@@ -14,14 +14,6 @@ export default class ClorangeRules extends ChessRules {
return true;
}
- getReserveFen(o) {
- if (o.init)
- return "00000000000000000000";
- return (
- ["w","b"].map(c => Object.values(this.reserve[c]).join("")).join("")
- );
- }
-
pieces(color, x, y) {
let res = super.pieces(color, x, y);
res['s'] = {"class": "nv-pawn", moveas: "p"};
@@ -38,9 +30,8 @@ export default class ClorangeRules extends ChessRules {
static get NV_PIECES() {
return ['s', 'u', 'o', 'c', 't'];
}
-
- setOtherVariables(fen) {
- super.setOtherVariables(fen, V.V_PIECES.concat(V.NV_PIECES));
+ static get ReserveArray() {
+ return V.V_PIECES.concat(V.NV_PIECES);
}
// Forbid non-violent pieces to capture
diff --git a/variants/Convert/class.js b/variants/Convert/class.js
index bc99c63..cd2d1a9 100644
--- a/variants/Convert/class.js
+++ b/variants/Convert/class.js
@@ -15,8 +15,8 @@ export default class ConvertRules extends ChessRules {
return false;
}
- setOtherVariables(fenParsed, pieceArray) {
- super.setOtherVariables(fenParsed, pieceArray);
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
// Stack of "last move" only for intermediate chaining
this.lastMoveEnd = [];
}
diff --git a/variants/Coregal/class.js b/variants/Coregal/class.js
index 23f67c7..bbc35a4 100644
--- a/variants/Coregal/class.js
+++ b/variants/Coregal/class.js
@@ -61,8 +61,8 @@ export default class CoregalRules extends ChessRules {
);
}
- setOtherVariables(fenParsed, pieceArray) {
- super.setOtherVariables(fenParsed, pieceArray);
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
this.relPos = {
'w': {
'k': fenParsed.relpos[0],
diff --git a/variants/Discoduel/class.js b/variants/Discoduel/class.js
new file mode 100644
index 0000000..c081b31
--- /dev/null
+++ b/variants/Discoduel/class.js
@@ -0,0 +1,51 @@
+import ChessRules from "/base_rules.js";
+import {ArrayFun} from "/utils/array.js"
+
+export default class DiscoduelRules extends ChessRules {
+
+ static get Options() {
+ return {}; //nothing would make sense
+ }
+
+ get pawnPromotions() {
+ return ['p'];
+ }
+
+ get hasFlags() {
+ return false;
+ }
+
+ genRandInitBaseFen() {
+ return {
+ fen: "1n4n1/8/8/8/8/8/PPPPPPPP/8",
+ o: {}
+ };
+ }
+
+ getPotentialMovesFrom([x, y]) {
+ const moves = super.getPotentialMovesFrom([x, y]);
+ if (this.turn == 'b')
+ // Prevent pawn captures on last rank:
+ return moves.filter(m => m.vanish.length == 1 || m.vanish[1].x != 0);
+ return moves;
+ }
+
+ filterValid(moves) {
+ return moves;
+ }
+
+ getCurrentScore() {
+ // No real winning condition (promotions count...)
+ if (
+ ArrayFun.range(1, this.size.x).every(row_idx => {
+ this.board[row_idx].every(square => square.charAt(0) != 'w')
+ })
+ ||
+ !this.atLeastOneMove(this.turn)
+ ) {
+ return "1/2";
+ }
+ return "*";
+ }
+
+};
diff --git a/variants/Discoduel/rules.html b/variants/Discoduel/rules.html
new file mode 100644
index 0000000..490d279
--- /dev/null
+++ b/variants/Discoduel/rules.html
@@ -0,0 +1,5 @@
+
+ Eight pawns try to promote, while the knights attempts to prevent them.
+ A pawn reaching last rank does not transform, but become immune to captures.
+
+Goal: "promoting" as many pawns as possible.
diff --git a/variants/Discoduel/style.css b/variants/Discoduel/style.css
new file mode 100644
index 0000000..a3550bc
--- /dev/null
+++ b/variants/Discoduel/style.css
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
diff --git a/variants/Dobutsu/class.js b/variants/Dobutsu/class.js
new file mode 100644
index 0000000..7632a17
--- /dev/null
+++ b/variants/Dobutsu/class.js
@@ -0,0 +1,100 @@
+import ChessRules from "/base_rules.js";
+
+export default class DobutsuRules extends ChessRules {
+
+ static get Options() {
+ return {};
+ }
+
+ get hasFlags() {
+ return false;
+ }
+
+ get hasEnpassant() {
+ return false;
+ }
+
+ pieces(color, x, y) {
+ const pawnShift = this.getPawnShift(color || 'w');
+ // NOTE: classs change according to playerColor (orientation)
+ const mySide = (this.playerColor == color);
+ return {
+ 'c': {
+ "class": (mySide ? "" : "rev-") + "chick",
+ both: [{steps: [[pawnShift, 0]], range: 1}]
+ },
+ 'h': {
+ "class": (mySide ? "" : "rev-") + "hen",
+ both: [
+ {
+ steps: [
+ [pawnShift, 1], [pawnShift, -1],
+ [0, 1], [0, -1], [1, 0], [-1, 0]
+ ],
+ range: 1
+ }
+ ]
+ },
+ 'e': {
+ "class": (mySide ? "" : "rev-") + "elephant",
+ both: [{steps: [[-1, 1], [-1, -1], [1, 1], [1, -1]], range: 1}]
+ },
+ 'g': {
+ "class": (mySide ? "" : "rev-") + "giraffe",
+ both: [{steps: [[0, 1], [0, -1], [1, 0], [-1, 0]], range: 1}]
+ },
+ 'l': {
+ "class": (mySide ? "" : "rev-") + "lion",
+ both: [{
+ steps: [[-1, 1], [-1, -1], [1, 1], [1, -1],
+ [0, 1], [0, -1], [1, 0], [-1, 0]],
+ range: 1
+ }]
+ }
+ };
+ }
+
+ isKing(x, y, p) {
+ if (!p)
+ p = this.getPiece(x, y);
+ return (p == 'l');
+ }
+
+ static get ReserveArray() {
+ return ['p', 'h', 'e', 'g'];
+ }
+
+ constructor(o) {
+ o.options = {crazyhouse: true, taking: true};
+ super(o);
+ }
+
+ get pawnPromotions() {
+ return ['h'];
+ }
+
+ genRandInitBaseFen() {
+ return {
+ fen: "gle/1c1/1C1/ELG",
+ o: {}
+ };
+ }
+
+ get size() {
+ return {x: 4, y: 4};
+ }
+
+ getCurrentScore(move_s) {
+ const res = super.getCurrentScore(move_s);
+ if (res != '*')
+ return res;
+ const oppCol = C.GetOppTurn(this.turn);
+ const oppLastRank = (oppCol == 'b' ? 3 : 0);
+ for (let j=0; j < this.size.y; j++) {
+ if (this.board[oppLastRank][j] == oppCol + 'l')
+ return (oppCol == 'w' ? "1-0" : "0-1");
+ }
+ return "*";
+ }
+
+};
diff --git a/variants/Dobutsu/rules.html b/variants/Dobutsu/rules.html
new file mode 100644
index 0000000..45bbc5d
--- /dev/null
+++ b/variants/Dobutsu/rules.html
@@ -0,0 +1,12 @@
+
+ Simplified Shogi game. Goal: capture the Lion.
+ Pieces move as indicated on them (red arrows).
+
+
+Captured units can be landed on the board later.
+
+
+
+ Wikipedia page
+ .
+
diff --git a/variants/Dobutsu/style.css b/variants/Dobutsu/style.css
new file mode 100644
index 0000000..fb861f1
--- /dev/null
+++ b/variants/Dobutsu/style.css
@@ -0,0 +1,34 @@
+piece.chick {
+ background-image: url('/pieces/Dobutsu/chick.svg');
+}
+piece.rev-chick {
+ background-image: url('/pieces/Dobutsu/rev_chick.svg');
+}
+
+piece.hen {
+ background-image: url('/pieces/Dobutsu/hen.svg');
+}
+piece.rev-hen {
+ background-image: url('/pieces/Dobutsu/rev_hen.svg');
+}
+
+piece.elephant {
+ background-image: url('/pieces/Dobutsu/elephant.svg');
+}
+piece.rev-elephant {
+ background-image: url('/pieces/Dobutsu/rev_elephant.svg');
+}
+
+piece.giraffe {
+ background-image: url('/pieces/Dobutsu/giraffe.svg');
+}
+piece.rev-giraffe {
+ background-image: url('/pieces/Dobutsu/rev_giraffe.svg');
+}
+
+piece.lion {
+ background-image: url('/pieces/Dobutsu/lion.svg');
+}
+piece.rev-lion {
+ background-image: url('/pieces/Dobutsu/rev_lion.svg');
+}
--
2.48.1
From c4d2eb5bdf1b23d8c4a9d09322f84a9e0da9d60c Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Wed, 3 Jan 2024 17:10:33 +0100
Subject: [PATCH 10/16] Fix Dobutsu
---
base_rules.js | 17 +++++++++--------
variants/Dobutsu/class.js | 21 ++++++++++-----------
2 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/base_rules.js b/base_rules.js
index 217d055..9a364e8 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -465,7 +465,7 @@ export default class ChessRules {
// VISUAL UTILS
getPieceWidth(rwidth) {
- return (rwidth / this.size.y);
+ return (rwidth / Math.max(this.size.x, this.size.y));
}
getReserveSquareSize(rwidth, nbR) {
@@ -856,9 +856,10 @@ export default class ChessRules {
y = (this.playerColor == i ? y = r.height + 5 : - 5 - rsqSize);
}
else {
- const sqSize = r.width / this.size.y;
+ const sqSize = r.width / Math.max(this.size.x, this.size.y);
const flipped = this.flippedBoard;
- x = (flipped ? this.size.y - 1 - j : j) * sqSize;
+ x = (flipped ? this.size.y - 1 - j : j) * sqSize +
+ Math.abs(this.size.x - this.size.y) * sqSize / 2;
y = (flipped ? this.size.x - 1 - i : i) * sqSize;
}
return [r.x + x, r.y + y];
@@ -2284,11 +2285,6 @@ export default class ChessRules {
if (this.hasCastle)
this.updateCastleFlags(move);
if (this.options["crazyhouse"]) {
- move.vanish.forEach(v => {
- const square = C.CoordsToSquare({x: v.x, y: v.y});
- if (this.ispawn[square])
- delete this.ispawn[square];
- });
if (move.appear.length > 0 && move.vanish.length > 0) {
// Assumption: something is moving
const initSquare = C.CoordsToSquare(move.start);
@@ -2307,6 +2303,11 @@ export default class ChessRules {
delete this.ispawn[destSquare];
}
}
+ move.vanish.forEach(v => {
+ const square = C.CoordsToSquare({x: v.x, y: v.y});
+ if (this.ispawn[square])
+ delete this.ispawn[square];
+ });
}
const minSize = Math.min(move.appear.length, move.vanish.length);
if (
diff --git a/variants/Dobutsu/class.js b/variants/Dobutsu/class.js
index 7632a17..29b0708 100644
--- a/variants/Dobutsu/class.js
+++ b/variants/Dobutsu/class.js
@@ -19,7 +19,7 @@ export default class DobutsuRules extends ChessRules {
// NOTE: classs change according to playerColor (orientation)
const mySide = (this.playerColor == color);
return {
- 'c': {
+ 'p': {
"class": (mySide ? "" : "rev-") + "chick",
both: [{steps: [[pawnShift, 0]], range: 1}]
},
@@ -43,7 +43,7 @@ export default class DobutsuRules extends ChessRules {
"class": (mySide ? "" : "rev-") + "giraffe",
both: [{steps: [[0, 1], [0, -1], [1, 0], [-1, 0]], range: 1}]
},
- 'l': {
+ 'k': {
"class": (mySide ? "" : "rev-") + "lion",
both: [{
steps: [[-1, 1], [-1, -1], [1, 1], [1, -1],
@@ -54,16 +54,15 @@ export default class DobutsuRules extends ChessRules {
};
}
- isKing(x, y, p) {
- if (!p)
- p = this.getPiece(x, y);
- return (p == 'l');
- }
-
static get ReserveArray() {
return ['p', 'h', 'e', 'g'];
}
+ updateReserve(color, piece, count) {
+ if (piece != 'k')
+ super.updateReserve(color, piece, count);
+ }
+
constructor(o) {
o.options = {crazyhouse: true, taking: true};
super(o);
@@ -75,13 +74,13 @@ export default class DobutsuRules extends ChessRules {
genRandInitBaseFen() {
return {
- fen: "gle/1c1/1C1/ELG",
+ fen: "gke/1p1/1P1/EKG",
o: {}
};
}
get size() {
- return {x: 4, y: 4};
+ return {x: 4, y: 3};
}
getCurrentScore(move_s) {
@@ -91,7 +90,7 @@ export default class DobutsuRules extends ChessRules {
const oppCol = C.GetOppTurn(this.turn);
const oppLastRank = (oppCol == 'b' ? 3 : 0);
for (let j=0; j < this.size.y; j++) {
- if (this.board[oppLastRank][j] == oppCol + 'l')
+ if (this.board[oppLastRank][j] == oppCol + 'k')
return (oppCol == 'w' ? "1-0" : "0-1");
}
return "*";
--
2.48.1
From 4fcd7ab062f5250757804d633df01bd0d06da137 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Thu, 4 Jan 2024 10:23:47 +0100
Subject: [PATCH 11/16] Fix Discoduel
---
variants/Discoduel/class.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/variants/Discoduel/class.js b/variants/Discoduel/class.js
index c081b31..ec96a43 100644
--- a/variants/Discoduel/class.js
+++ b/variants/Discoduel/class.js
@@ -38,7 +38,9 @@ export default class DiscoduelRules extends ChessRules {
// No real winning condition (promotions count...)
if (
ArrayFun.range(1, this.size.x).every(row_idx => {
- this.board[row_idx].every(square => square.charAt(0) != 'w')
+ return this.board[row_idx].every(square => {
+ return (!square || square.charAt(0) != 'w');
+ })
})
||
!this.atLeastOneMove(this.turn)
--
2.48.1
From d66135396f3a6e140947545630004ce11f8eee7b Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Thu, 4 Jan 2024 10:37:28 +0100
Subject: [PATCH 12/16] Fix Dobutsu, extend Align4
---
variants/Align4/class.js | 19 +++++++++++++++++++
variants/Dobutsu/class.js | 15 ++++++++++-----
2 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/variants/Align4/class.js b/variants/Align4/class.js
index 09a79ed..f5b8f83 100644
--- a/variants/Align4/class.js
+++ b/variants/Align4/class.js
@@ -13,6 +13,14 @@ export default class Align4Rules extends ChessRules {
{label: "Random", value: 1}
]
}],
+ input: [
+ {
+ label: "Pawn first",
+ variable: "pawnfirst",
+ type: "checkbox",
+ defaut: false
+ }
+ ],
styles: ["atomic", "capture", "cylinder"]
};
}
@@ -39,6 +47,17 @@ export default class Align4Rules extends ChessRules {
// Just do not update any reserve (infinite supply)
updateReserve() {}
+ canDrop([c, p], [i, j]) {
+ return (
+ this.board[i][j] == "" &&
+ (
+ p != "p" || this.options["pawnfirst"] ||
+ (c == 'w' && i < this.size.x - 1) ||
+ (c == 'b' && i > 0)
+ )
+ );
+ }
+
getCurrentScore(move_s) {
const score = super.getCurrentScore(move_s);
if (score != "*")
diff --git a/variants/Dobutsu/class.js b/variants/Dobutsu/class.js
index 29b0708..d8ceafa 100644
--- a/variants/Dobutsu/class.js
+++ b/variants/Dobutsu/class.js
@@ -87,11 +87,16 @@ export default class DobutsuRules extends ChessRules {
const res = super.getCurrentScore(move_s);
if (res != '*')
return res;
- const oppCol = C.GetOppTurn(this.turn);
- const oppLastRank = (oppCol == 'b' ? 3 : 0);
- for (let j=0; j < this.size.y; j++) {
- if (this.board[oppLastRank][j] == oppCol + 'k')
- return (oppCol == 'w' ? "1-0" : "0-1");
+ for (let lastRank of [0, 3]) {
+ const color = (lastRank == 0 ? 'w' : 'b');
+ for (let j=0; j < this.size.y; j++) {
+ if (
+ this.board[lastRank][j] == color + 'k' &&
+ !this.underAttack([lastRank, j], [C.GetOppTurn(color)])
+ ) {
+ return (color == 'w' ? "1-0" : "0-1");
+ }
+ }
}
return "*";
}
--
2.48.1
From 66ab134b7ab3ba00204fb316ba7636c904331d6c Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Thu, 4 Jan 2024 11:19:10 +0100
Subject: [PATCH 13/16] Add Doublearmy. Start thinking about Dynamo
---
README.md | 10 +-
TODO | 3 -
initialize.sh | 8 +
pieces/black_commoner.svg | 105 ++++
pieces/white_commoner.svg | 94 +++
variants.js | 4 +-
variants/Doublearmy/class.js | 43 ++
variants/Doublearmy/rules.html | 6 +
variants/Doublearmy/style.css | 9 +
variants/Dynamo/class.js | 921 ++++++++++++++++++++++++++++
variants/Dynamo/complete_rules.html | 142 +++++
variants/Dynamo/rules.html | 24 +
variants/Dynamo/style.css | 1 +
13 files changed, 1359 insertions(+), 11 deletions(-)
create mode 100755 initialize.sh
create mode 100644 pieces/black_commoner.svg
create mode 100644 pieces/white_commoner.svg
create mode 100644 variants/Doublearmy/class.js
create mode 100644 variants/Doublearmy/rules.html
create mode 100644 variants/Doublearmy/style.css
create mode 100644 variants/Dynamo/class.js
create mode 100644 variants/Dynamo/complete_rules.html
create mode 100644 variants/Dynamo/rules.html
create mode 100644 variants/Dynamo/style.css
diff --git a/README.md b/README.md
index 4636ac4..0b900c5 100644
--- a/README.md
+++ b/README.md
@@ -9,12 +9,10 @@ PHP + Node.js + npm.
## Usage
-```wget https://xogo.live/assets.zip && unzip assets.zip```
-```wget https://xogo.live/extras.zip && unzip extras.zip```
-Rename parameters.js.dist → parameters.js, and edit file.
-```npm i```
+Initialisation (done once):
-Generate some pieces:
-```python generateSVG.py``` in pieces/Avalam
+```./initialize.sh```
+
+You may want to edit the parameters.js file. Then:
```./start.sh``` (and later, ```./stop.sh```)
diff --git a/TODO b/TODO
index fac46e3..8afe466 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,3 @@
-add variants :
-Dark Racing Kings ? Checkered-Teleport ?
-
Hmm... non ? -->
Otage, Emergo, Pacosako : fonction "buildPiece(arg1, arg2)" returns HTML element with 2 SVG or SVG + number
==> plus simple : deux classes, images superposées.
diff --git a/initialize.sh b/initialize.sh
new file mode 100755
index 0000000..b7a9f4f
--- /dev/null
+++ b/initialize.sh
@@ -0,0 +1,8 @@
+!#/bin/sh
+
+wget https://xogo.live/assets.zip && unzip assets.zip
+wget https://xogo.live/extras.zip && unzip extras.zip
+cp parameters.js.dist parameters.js
+npm i
+cd pieces/Avalam && python generateSVG.py
+#cd pieces/Emergo && python generateSVG.py
diff --git a/pieces/black_commoner.svg b/pieces/black_commoner.svg
new file mode 100644
index 0000000..0995449
--- /dev/null
+++ b/pieces/black_commoner.svg
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pieces/white_commoner.svg b/pieces/white_commoner.svg
new file mode 100644
index 0000000..12f2b27
--- /dev/null
+++ b/pieces/white_commoner.svg
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/variants.js b/variants.js
index f42033e..10cf42a 100644
--- a/variants.js
+++ b/variants.js
@@ -45,9 +45,9 @@ const variants = [
{name: 'Dice', desc: 'Roll the dice'},
{name: 'Discoduel', desc: 'Enter the disco', disp: 'Disco Duel'},
{name: 'Dobutsu', desc: "Let's catch the Lion!"},
-// {name: 'Doublearmy', desc: '64 pieces on the board', disp: 'Double Army'},
+ {name: 'Doublearmy', desc: '64 pieces on the board', disp: 'Double Army'},
{name: 'Doublemove', desc: 'Double moves'},
-// {name: 'Dynamo', desc: 'Push and pull'},
+ {name: 'Dynamo', desc: 'Push and pull'},
// {name: 'Eightpieces', desc: 'Each piece is unique', disp: '8 Pieces'},
// {name: 'Emergo', desc: 'Stacking Checkers variant'},
// {name: 'Empire', desc: 'Empire versus Kingdom'},
diff --git a/variants/Doublearmy/class.js b/variants/Doublearmy/class.js
new file mode 100644
index 0000000..abbb564
--- /dev/null
+++ b/variants/Doublearmy/class.js
@@ -0,0 +1,43 @@
+import ChessRules from "/base_rules.js";
+
+export default class DoublearmyRules extends ChessRules {
+
+ static get Options() {
+ return {
+ select: C.Options.select,
+ input: C.Options.input,
+ styles: C.Options.styles.filter(s => s != "madrasi")
+ };
+ }
+
+ pieces(color, x, y) {
+ let res = super.pieces(color, x, y);
+ return Object.assign(
+ {
+ 'c': {
+ "class": "commoner",
+ moveas: 'k'
+ }
+ },
+ res
+ );
+ }
+
+ genRandInitBaseFen() {
+ const s = super.genRandInitBaseFen();
+ const rows = s.fen.split('/');
+ return {
+ fen:
+ rows[0] + "/" +
+ rows[1] + "/" +
+ rows[0].replace('k', 'c') + "/" +
+ rows[1] + "/" +
+ rows[6] + "/" +
+ rows[7].replace('K', 'C') + "/" +
+ rows[6] + "/" +
+ rows[7],
+ o: s.o
+ };
+ }
+
+};
diff --git a/variants/Doublearmy/rules.html b/variants/Doublearmy/rules.html
new file mode 100644
index 0000000..b9908ab
--- /dev/null
+++ b/variants/Doublearmy/rules.html
@@ -0,0 +1,6 @@
+
+ The four middle ranks contain a replica of the initial pieces.
+ The central "king" has no royal status, and is thus named "commoner".
+
+
+Vincent Rothuis (2020).
diff --git a/variants/Doublearmy/style.css b/variants/Doublearmy/style.css
new file mode 100644
index 0000000..e50c2f4
--- /dev/null
+++ b/variants/Doublearmy/style.css
@@ -0,0 +1,9 @@
+@import url("/base_pieces.css");
+
+piece.black.commoner {
+ background-image: url('/pieces/black_commoner.svg');
+}
+
+piece.white.commoner {
+ background-image: url('/pieces/white_commoner.svg');
+}
diff --git a/variants/Dynamo/class.js b/variants/Dynamo/class.js
new file mode 100644
index 0000000..0996a70
--- /dev/null
+++ b/variants/Dynamo/class.js
@@ -0,0 +1,921 @@
+import ChessRules from "/base_rules.js";
+
+export default class DynamoRules extends ChessRules {
+
+ // TODO? later, allow to push out pawns on a and h files
+ get hasEnpassant() {
+ return false;
+ }
+
+/// TODO:::
+
+ canIplay(side, [x, y]) {
+ // Sometimes opponent's pieces can be moved directly
+ return this.turn == side;
+ }
+
+ setOtherVariables(fen) {
+ super.setOtherVariables(fen);
+ this.subTurn = 1;
+ // Local stack of "action moves"
+ this.amoves = [];
+ const amove = V.ParseFen(fen).amove;
+ if (amove != "-") {
+ const amoveParts = amove.split("/");
+ let move = {
+ // No need for start & end
+ appear: [],
+ vanish: []
+ };
+ [0, 1].map(i => {
+ if (amoveParts[i] != "-") {
+ amoveParts[i].split(".").forEach(av => {
+ // Format is "bpe3"
+ const xy = V.SquareToCoords(av.substr(2));
+ move[i == 0 ? "appear" : "vanish"].push(
+ new PiPo({
+ x: xy.x,
+ y: xy.y,
+ c: av[0],
+ p: av[1]
+ })
+ );
+ });
+ }
+ });
+ this.amoves.push(move);
+ }
+ // Stack "first moves" (on subTurn 1) to merge and check opposite moves
+ this.firstMove = [];
+ }
+
+ static ParseFen(fen) {
+ return Object.assign(
+ ChessRules.ParseFen(fen),
+ { amove: fen.split(" ")[4] }
+ );
+ }
+
+ static IsGoodFen(fen) {
+ if (!ChessRules.IsGoodFen(fen)) return false;
+ const fenParts = fen.split(" ");
+ if (fenParts.length != 5) return false;
+ if (fenParts[4] != "-") {
+ // TODO: a single regexp instead.
+ // Format is [bpa2[.wpd3]] || '-'/[bbc3[.wrd5]] || '-'
+ const amoveParts = fenParts[4].split("/");
+ if (amoveParts.length != 2) return false;
+ for (let part of amoveParts) {
+ if (part != "-") {
+ for (let psq of part.split("."))
+ if (!psq.match(/^[a-z]{3}[1-8]$/)) return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ getFen() {
+ return super.getFen() + " " + this.getAmoveFen();
+ }
+
+ getFenForRepeat() {
+ return super.getFenForRepeat() + "_" + this.getAmoveFen();
+ }
+
+ getAmoveFen() {
+ const L = this.amoves.length;
+ if (L == 0) return "-";
+ return (
+ ["appear","vanish"].map(
+ mpart => {
+ if (this.amoves[L-1][mpart].length == 0) return "-";
+ return (
+ this.amoves[L-1][mpart].map(
+ av => {
+ const square = V.CoordsToSquare({ x: av.x, y: av.y });
+ return av.c + av.p + square;
+ }
+ ).join(".")
+ );
+ }
+ ).join("/")
+ );
+ }
+
+ canTake() {
+ // Captures don't occur (only pulls & pushes)
+ return false;
+ }
+
+ // Step is right, just add (push/pull) moves in this direction
+ // Direction is assumed normalized.
+ getMovesInDirection([x, y], [dx, dy], nbSteps) {
+ nbSteps = nbSteps || 8; //max 8 steps anyway
+ let [i, j] = [x + dx, y + dy];
+ let moves = [];
+ const color = this.getColor(x, y);
+ const piece = this.getPiece(x, y);
+ const lastRank = (color == 'w' ? 0 : 7);
+ let counter = 1;
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ if (i == lastRank && piece == V.PAWN) {
+ // Promotion by push or pull
+ V.PawnSpecs.promotions.forEach(p => {
+ let move = super.getBasicMove([x, y], [i, j], { c: color, p: p });
+ moves.push(move);
+ });
+ }
+ else moves.push(super.getBasicMove([x, y], [i, j]));
+ if (++counter > nbSteps) break;
+ i += dx;
+ j += dy;
+ }
+ if (!V.OnBoard(i, j) && piece != V.KING) {
+ // Add special "exit" move, by "taking king"
+ moves.push(
+ new Move({
+ start: { x: x, y: y },
+ end: { x: this.kingPos[color][0], y: this.kingPos[color][1] },
+ appear: [],
+ vanish: [{ x: x, y: y, c: color, p: piece }]
+ })
+ );
+ }
+ return moves;
+ }
+
+ // Normalize direction to know the step
+ getNormalizedDirection([dx, dy]) {
+ const absDir = [Math.abs(dx), Math.abs(dy)];
+ let divisor = 0;
+ if (absDir[0] != 0 && absDir[1] != 0 && absDir[0] != absDir[1])
+ // Knight
+ divisor = Math.min(absDir[0], absDir[1]);
+ else
+ // Standard slider (or maybe a pawn or king: same)
+ divisor = Math.max(absDir[0], absDir[1]);
+ return [dx / divisor, dy / divisor];
+ }
+
+ // There was something on x2,y2, maybe our color, pushed or (self)pulled
+ isAprioriValidExit([x1, y1], [x2, y2], color2, piece2) {
+ const color1 = this.getColor(x1, y1);
+ const pawnShift = (color1 == 'w' ? -1 : 1);
+ const lastRank = (color1 == 'w' ? 0 : 7);
+ const deltaX = Math.abs(x1 - x2);
+ const deltaY = Math.abs(y1 - y2);
+ const checkSlider = () => {
+ const dir = this.getNormalizedDirection([x2 - x1, y2 - y1]);
+ let [i, j] = [x1 + dir[0], y1 + dir[1]];
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ i += dir[0];
+ j += dir[1];
+ }
+ return !V.OnBoard(i, j);
+ };
+ switch (piece2 || this.getPiece(x1, y1)) {
+ case V.PAWN:
+ return (
+ x1 + pawnShift == x2 &&
+ (
+ (color1 == color2 && x2 == lastRank && y1 == y2) ||
+ (
+ color1 != color2 &&
+ deltaY == 1 &&
+ !V.OnBoard(2 * x2 - x1, 2 * y2 - y1)
+ )
+ )
+ );
+ case V.ROOK:
+ if (x1 != x2 && y1 != y2) return false;
+ return checkSlider();
+ case V.KNIGHT:
+ return (
+ deltaX + deltaY == 3 &&
+ (deltaX == 1 || deltaY == 1) &&
+ !V.OnBoard(2 * x2 - x1, 2 * y2 - y1)
+ );
+ case V.BISHOP:
+ if (deltaX != deltaY) return false;
+ return checkSlider();
+ case V.QUEEN:
+ if (deltaX != 0 && deltaY != 0 && deltaX != deltaY) return false;
+ return checkSlider();
+ case V.KING:
+ return (
+ deltaX <= 1 &&
+ deltaY <= 1 &&
+ !V.OnBoard(2 * x2 - x1, 2 * y2 - y1)
+ );
+ }
+ return false;
+ }
+
+ isAprioriValidVertical([x1, y1], x2) {
+ const piece = this.getPiece(x1, y1);
+ const deltaX = Math.abs(x1 - x2);
+ const startRank = (this.getColor(x1, y1) == 'w' ? 6 : 1);
+ return (
+ [V.QUEEN, V.ROOK].includes(piece) ||
+ (
+ [V.KING, V.PAWN].includes(piece) &&
+ (
+ deltaX == 1 ||
+ (deltaX == 2 && piece == V.PAWN && x1 == startRank)
+ )
+ )
+ );
+ }
+
+ // NOTE: for pushes, play the pushed piece first.
+ // for pulls: play the piece doing the action first
+ // NOTE: to push a piece out of the board, make it slide until its king
+ getPotentialMovesFrom([x, y]) {
+ const color = this.turn;
+ const sqCol = this.getColor(x, y);
+ const pawnShift = (color == 'w' ? -1 : 1);
+ const pawnStartRank = (color == 'w' ? 6 : 1);
+ const getMoveHash = (m) => {
+ return V.CoordsToSquare(m.start) + V.CoordsToSquare(m.end);
+ };
+ if (this.subTurn == 1) {
+ const addMoves = (dir, nbSteps) => {
+ const newMoves =
+ this.getMovesInDirection([x, y], [-dir[0], -dir[1]], nbSteps)
+ .filter(m => !movesHash[getMoveHash(m)]);
+ newMoves.forEach(m => { movesHash[getMoveHash(m)] = true; });
+ Array.prototype.push.apply(moves, newMoves);
+ };
+ // Free to play any move (if piece of my color):
+ let moves =
+ sqCol == color
+ ? super.getPotentialMovesFrom([x, y])
+ : [];
+ // There may be several suicide moves: keep only one
+ let hasExit = false;
+ moves = moves.filter(m => {
+ const suicide = (m.appear.length == 0);
+ if (suicide) {
+ if (hasExit) return false;
+ hasExit = true;
+ }
+ return true;
+ });
+ // Structure to avoid adding moves twice (can be action & move)
+ let movesHash = {};
+ moves.forEach(m => { movesHash[getMoveHash(m)] = true; });
+ // [x, y] is pushed by 'color'
+ for (let step of V.steps[V.KNIGHT]) {
+ const [i, j] = [x + step[0], y + step[1]];
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == color &&
+ this.getPiece(i, j) == V.KNIGHT
+ ) {
+ addMoves(step, 1);
+ }
+ }
+ for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
+ let [i, j] = [x + step[0], y + step[1]];
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ i += step[0];
+ j += step[1];
+ }
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == color
+ ) {
+ const deltaX = Math.abs(i - x);
+ const deltaY = Math.abs(j - y);
+ switch (this.getPiece(i, j)) {
+ case V.PAWN:
+ if (
+ (x - i) / deltaX == pawnShift &&
+ deltaX <= 2 &&
+ deltaY <= 1
+ ) {
+ if (sqCol == color && deltaY == 0) {
+ // Pushed forward
+ const maxSteps = (i == pawnStartRank && deltaX == 1 ? 2 : 1);
+ addMoves(step, maxSteps);
+ }
+ else if (sqCol != color && deltaY == 1 && deltaX == 1)
+ // Pushed diagonally
+ addMoves(step, 1);
+ }
+ break;
+ case V.ROOK:
+ if (deltaX == 0 || deltaY == 0) addMoves(step);
+ break;
+ case V.BISHOP:
+ if (deltaX == deltaY) addMoves(step);
+ break;
+ case V.QUEEN:
+ // All steps are valid for a queen:
+ addMoves(step);
+ break;
+ case V.KING:
+ if (deltaX <= 1 && deltaY <= 1) addMoves(step, 1);
+ break;
+ }
+ }
+ }
+ return moves;
+ }
+ // If subTurn == 2 then we should have a first move,
+ // which restrict what we can play now: only in the first move direction
+ const L = this.firstMove.length;
+ const fm = this.firstMove[L-1];
+ if (
+ (fm.appear.length == 2 && fm.vanish.length == 2) ||
+ (fm.vanish[0].c == sqCol && sqCol != color)
+ ) {
+ // Castle or again opponent color: no move playable then.
+ return [];
+ }
+ const piece = this.getPiece(x, y);
+ const getPushExit = () => {
+ // Piece at subTurn 1 exited: can I have caused the exit?
+ if (
+ this.isAprioriValidExit(
+ [x, y],
+ [fm.start.x, fm.start.y],
+ fm.vanish[0].c
+ )
+ ) {
+ // Seems so:
+ const dir = this.getNormalizedDirection(
+ [fm.start.x - x, fm.start.y - y]);
+ const nbSteps =
+ [V.PAWN, V.KING, V.KNIGHT].includes(piece)
+ ? 1
+ : null;
+ return this.getMovesInDirection([x, y], dir, nbSteps);
+ }
+ return [];
+ }
+ const getPushMoves = () => {
+ // Piece from subTurn 1 is still on board:
+ const dirM = this.getNormalizedDirection(
+ [fm.end.x - fm.start.x, fm.end.y - fm.start.y]);
+ const dir = this.getNormalizedDirection(
+ [fm.start.x - x, fm.start.y - y]);
+ // Normalized directions should match
+ if (dir[0] == dirM[0] && dir[1] == dirM[1]) {
+ // We don't know if first move is a pushed piece or normal move,
+ // so still must check if the push is valid.
+ const deltaX = Math.abs(fm.start.x - x);
+ const deltaY = Math.abs(fm.start.y - y);
+ switch (piece) {
+ case V.PAWN:
+ if (x == pawnStartRank) {
+ if (
+ (fm.start.x - x) * pawnShift < 0 ||
+ deltaX >= 3 ||
+ deltaY >= 2 ||
+ (fm.vanish[0].c == color && deltaY > 0) ||
+ (fm.vanish[0].c != color && deltaY == 0) ||
+ Math.abs(fm.end.x - fm.start.x) > deltaX ||
+ fm.end.y - fm.start.y != fm.start.y - y
+ ) {
+ return [];
+ }
+ }
+ else {
+ if (
+ fm.start.x - x != pawnShift ||
+ deltaY >= 2 ||
+ (fm.vanish[0].c == color && deltaY == 1) ||
+ (fm.vanish[0].c != color && deltaY == 0) ||
+ fm.end.x - fm.start.x != pawnShift ||
+ fm.end.y - fm.start.y != fm.start.y - y
+ ) {
+ return [];
+ }
+ }
+ break;
+ case V.KNIGHT:
+ if (
+ (deltaX + deltaY != 3 || (deltaX == 0 && deltaY == 0)) ||
+ (fm.end.x - fm.start.x != fm.start.x - x) ||
+ (fm.end.y - fm.start.y != fm.start.y - y)
+ ) {
+ return [];
+ }
+ break;
+ case V.KING:
+ if (
+ (deltaX >= 2 || deltaY >= 2) ||
+ (fm.end.x - fm.start.x != fm.start.x - x) ||
+ (fm.end.y - fm.start.y != fm.start.y - y)
+ ) {
+ return [];
+ }
+ break;
+ case V.BISHOP:
+ if (deltaX != deltaY) return [];
+ break;
+ case V.ROOK:
+ if (deltaX != 0 && deltaY != 0) return [];
+ break;
+ case V.QUEEN:
+ if (deltaX != deltaY && deltaX != 0 && deltaY != 0) return [];
+ break;
+ }
+ // Nothing should stand between [x, y] and the square fm.start
+ let [i, j] = [x + dir[0], y + dir[1]];
+ while (
+ (i != fm.start.x || j != fm.start.y) &&
+ this.board[i][j] == V.EMPTY
+ ) {
+ i += dir[0];
+ j += dir[1];
+ }
+ if (i == fm.start.x && j == fm.start.y)
+ return this.getMovesInDirection([x, y], dir);
+ }
+ return [];
+ }
+ const getPullExit = () => {
+ // Piece at subTurn 1 exited: can I be pulled?
+ // Note: kings cannot suicide, so fm.vanish[0].p is not KING.
+ // Could be PAWN though, if a pawn was pushed out of board.
+ if (
+ fm.vanish[0].p != V.PAWN && //pawns cannot pull
+ this.isAprioriValidExit(
+ [x, y],
+ [fm.start.x, fm.start.y],
+ fm.vanish[0].c,
+ fm.vanish[0].p
+ )
+ ) {
+ // Seems so:
+ const dir = this.getNormalizedDirection(
+ [fm.start.x - x, fm.start.y - y]);
+ const nbSteps = (fm.vanish[0].p == V.KNIGHT ? 1 : null);
+ return this.getMovesInDirection([x, y], dir, nbSteps);
+ }
+ return [];
+ };
+ const getPullMoves = () => {
+ if (fm.vanish[0].p == V.PAWN)
+ // pawns cannot pull
+ return [];
+ const dirM = this.getNormalizedDirection(
+ [fm.end.x - fm.start.x, fm.end.y - fm.start.y]);
+ const dir = this.getNormalizedDirection(
+ [fm.start.x - x, fm.start.y - y]);
+ // Normalized directions should match
+ if (dir[0] == dirM[0] && dir[1] == dirM[1]) {
+ // Am I at the right distance?
+ const deltaX = Math.abs(x - fm.start.x);
+ const deltaY = Math.abs(y - fm.start.y);
+ if (
+ (fm.vanish[0].p == V.KING && (deltaX > 1 || deltaY > 1)) ||
+ (fm.vanish[0].p == V.KNIGHT &&
+ (deltaX + deltaY != 3 || deltaX == 0 || deltaY == 0))
+ ) {
+ return [];
+ }
+ // Nothing should stand between [x, y] and the square fm.start
+ let [i, j] = [x + dir[0], y + dir[1]];
+ while (
+ (i != fm.start.x || j != fm.start.y) &&
+ this.board[i][j] == V.EMPTY
+ ) {
+ i += dir[0];
+ j += dir[1];
+ }
+ if (i == fm.start.x && j == fm.start.y)
+ return this.getMovesInDirection([x, y], dir);
+ }
+ return [];
+ };
+ if (fm.vanish[0].c != color) {
+ // Only possible action is a push:
+ if (fm.appear.length == 0) return getPushExit();
+ return getPushMoves();
+ }
+ else if (sqCol != color) {
+ // Only possible action is a pull, considering moving piece abilities
+ if (fm.appear.length == 0) return getPullExit();
+ return getPullMoves();
+ }
+ else {
+ // My color + my color: both actions possible
+ // Structure to avoid adding moves twice (can be action & move)
+ let movesHash = {};
+ if (fm.appear.length == 0) {
+ const pushes = getPushExit();
+ pushes.forEach(m => { movesHash[getMoveHash(m)] = true; });
+ return (
+ pushes.concat(getPullExit().filter(m => !movesHash[getMoveHash(m)]))
+ );
+ }
+ const pushes = getPushMoves();
+ pushes.forEach(m => { movesHash[getMoveHash(m)] = true; });
+ return (
+ pushes.concat(getPullMoves().filter(m => !movesHash[getMoveHash(m)]))
+ );
+ }
+ return [];
+ }
+
+ getSlideNJumpMoves([x, y], steps, oneStep) {
+ let moves = [];
+ const c = this.getColor(x, y);
+ const piece = this.getPiece(x, y);
+ outerLoop: for (let step of steps) {
+ let i = x + step[0];
+ let j = y + step[1];
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ moves.push(this.getBasicMove([x, y], [i, j]));
+ if (oneStep) continue outerLoop;
+ i += step[0];
+ j += step[1];
+ }
+ if (V.OnBoard(i, j)) {
+ if (this.canTake([x, y], [i, j]))
+ moves.push(this.getBasicMove([x, y], [i, j]));
+ }
+ else {
+ // Add potential board exit (suicide), except for the king
+ if (piece != V.KING) {
+ moves.push({
+ start: { x: x, y: y},
+ end: { x: this.kingPos[c][0], y: this.kingPos[c][1] },
+ appear: [],
+ vanish: [
+ new PiPo({
+ x: x,
+ y: y,
+ c: c,
+ p: piece
+ })
+ ]
+ });
+ }
+ }
+ }
+ return moves;
+ }
+
+ // Does m2 un-do m1 ? (to disallow undoing actions)
+ oppositeMoves(m1, m2) {
+ const isEqual = (av1, av2) => {
+ for (let av of av1) {
+ const avInAv2 = av2.find(elt => {
+ return (
+ elt.x == av.x &&
+ elt.y == av.y &&
+ elt.c == av.c &&
+ elt.p == av.p
+ );
+ });
+ if (!avInAv2) return false;
+ }
+ return true;
+ };
+ // All appear and vanish arrays must have the same length
+ const mL = m1.appear.length;
+ return (
+ m2.appear.length == mL &&
+ m1.vanish.length == mL &&
+ m2.vanish.length == mL &&
+ isEqual(m1.appear, m2.vanish) &&
+ isEqual(m1.vanish, m2.appear)
+ );
+ }
+
+ getAmove(move1, move2) {
+ // Just merge (one is action one is move, one may be empty)
+ return {
+ appear: move1.appear.concat(move2.appear),
+ vanish: move1.vanish.concat(move2.vanish)
+ }
+ }
+
+ filterValid(moves) {
+ const color = this.turn;
+ const La = this.amoves.length;
+ if (this.subTurn == 1) {
+ return moves.filter(m => {
+ // A move is valid either if it doesn't result in a check,
+ // or if a second move is possible to counter the check
+ // (not undoing a potential move + action of the opponent)
+ this.play(m);
+ let res = this.underCheck(color);
+ if (this.subTurn == 2) {
+ let isOpposite = La > 0 && this.oppositeMoves(this.amoves[La-1], m);
+ if (res || isOpposite) {
+ const moves2 = this.getAllPotentialMoves();
+ for (let m2 of moves2) {
+ this.play(m2);
+ const res2 = this.underCheck(color);
+ const amove = this.getAmove(m, m2);
+ isOpposite =
+ La > 0 && this.oppositeMoves(this.amoves[La-1], amove);
+ this.undo(m2);
+ if (!res2 && !isOpposite) {
+ res = false;
+ break;
+ }
+ }
+ }
+ }
+ this.undo(m);
+ return !res;
+ });
+ }
+ if (La == 0) return super.filterValid(moves);
+ const Lf = this.firstMove.length;
+ return (
+ super.filterValid(
+ moves.filter(m => {
+ // Move shouldn't undo another:
+ const amove = this.getAmove(this.firstMove[Lf-1], m);
+ return !this.oppositeMoves(this.amoves[La-1], amove);
+ })
+ )
+ );
+ }
+
+ isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
+ for (let step of steps) {
+ let rx = x + step[0],
+ ry = y + step[1];
+ while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
+ rx += step[0];
+ ry += step[1];
+ }
+ if (
+ V.OnBoard(rx, ry) &&
+ this.getPiece(rx, ry) == piece &&
+ this.getColor(rx, ry) == color
+ ) {
+ // Continue some steps in the same direction (pull)
+ rx += step[0];
+ ry += step[1];
+ while (
+ V.OnBoard(rx, ry) &&
+ this.board[rx][ry] == V.EMPTY &&
+ !oneStep
+ ) {
+ rx += step[0];
+ ry += step[1];
+ }
+ if (!V.OnBoard(rx, ry)) return true;
+ // Step in the other direction (push)
+ rx = x - step[0];
+ ry = y - step[1];
+ while (
+ V.OnBoard(rx, ry) &&
+ this.board[rx][ry] == V.EMPTY &&
+ !oneStep
+ ) {
+ rx -= step[0];
+ ry -= step[1];
+ }
+ if (!V.OnBoard(rx, ry)) return true;
+ }
+ }
+ return false;
+ }
+
+ isAttackedByPawn([x, y], color) {
+ // The king can be pushed out by a pawn on last rank or near the edge
+ const pawnShift = (color == "w" ? 1 : -1);
+ for (let i of [-1, 1]) {
+ if (
+ V.OnBoard(x + pawnShift, y + i) &&
+ this.board[x + pawnShift][y + i] != V.EMPTY &&
+ this.getPiece(x + pawnShift, y + i) == V.PAWN &&
+ this.getColor(x + pawnShift, y + i) == color
+ ) {
+ if (!V.OnBoard(x - pawnShift, y - i)) return true;
+ }
+ }
+ return false;
+ }
+
+ static OnTheEdge(x, y) {
+ return (x == 0 || x == 7 || y == 0 || y == 7);
+ }
+
+ isAttackedByKing([x, y], color) {
+ // Attacked if I'm on the edge and the opponent king just next,
+ // but not on the edge.
+ if (V.OnTheEdge(x, y)) {
+ for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
+ const [i, j] = [x + step[0], y + step[1]];
+ if (
+ V.OnBoard(i, j) &&
+ !V.OnTheEdge(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getPiece(i, j) == V.KING
+ // NOTE: since only one king of each color, and (x, y) is occupied
+ // by our king, no need to check other king's color.
+ ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // No consideration of color: all pieces could be played
+ getAllPotentialMoves() {
+ let potentialMoves = [];
+ 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) {
+ Array.prototype.push.apply(
+ potentialMoves,
+ this.getPotentialMovesFrom([i, j])
+ );
+ }
+ }
+ }
+ return potentialMoves;
+ }
+
+ getEmptyMove() {
+ return new Move({
+ start: { x: -1, y: -1 },
+ end: { x: -1, y: -1 },
+ appear: [],
+ vanish: []
+ });
+ }
+
+ doClick(square) {
+ // A click to promote a piece on subTurn 2 would trigger this.
+ // For now it would then return [NaN, NaN] because surrounding squares
+ // have no IDs in the promotion modal. TODO: improve this?
+ if (isNaN(square[0])) return null;
+ // If subTurn == 2 && square is empty && !underCheck && !isOpposite,
+ // then return an empty move, allowing to "pass" subTurn2
+ const La = this.amoves.length;
+ const Lf = this.firstMove.length;
+ if (
+ this.subTurn == 2 &&
+ this.board[square[0]][square[1]] == V.EMPTY &&
+ !this.underCheck(this.turn) &&
+ (La == 0 || !this.oppositeMoves(this.amoves[La-1], this.firstMove[Lf-1]))
+ ) {
+ return this.getEmptyMove();
+ }
+ return null;
+ }
+
+ play(move) {
+ if (this.subTurn == 1 && move.vanish.length == 0) {
+ // Patch to work with old format: (TODO: remove later)
+ move.ignore = true;
+ return;
+ }
+ const color = this.turn;
+ move.subTurn = this.subTurn; //for undo
+ const gotoNext = (mv) => {
+ const L = this.firstMove.length;
+ this.amoves.push(this.getAmove(this.firstMove[L-1], mv));
+ this.turn = V.GetOppCol(color);
+ this.subTurn = 1;
+ this.movesCount++;
+ };
+ move.flags = JSON.stringify(this.aggregateFlags());
+ V.PlayOnBoard(this.board, move);
+ if (this.subTurn == 2) gotoNext(move);
+ else {
+ this.subTurn = 2;
+ this.firstMove.push(move);
+ this.toNewKingPos(move);
+ if (
+ // Condition is true on empty arrays:
+ this.getAllPotentialMoves().every(m => {
+ V.PlayOnBoard(this.board, m);
+ this.toNewKingPos(m);
+ const res = this.underCheck(color);
+ V.UndoOnBoard(this.board, m);
+ this.toOldKingPos(m);
+ return res;
+ })
+ ) {
+ // No valid move at subTurn 2
+ gotoNext(this.getEmptyMove());
+ }
+ this.toOldKingPos(move);
+ }
+ this.postPlay(move);
+ }
+
+ toNewKingPos(move) {
+ for (let a of move.appear)
+ if (a.p == V.KING) this.kingPos[a.c] = [a.x, a.y];
+ }
+
+ postPlay(move) {
+ if (move.start.x < 0) return;
+ this.toNewKingPos(move);
+ this.updateCastleFlags(move);
+ }
+
+ updateCastleFlags(move) {
+ const firstRank = { 'w': V.size.x - 1, 'b': 0 };
+ for (let v of move.vanish) {
+ if (v.p == V.KING) this.castleFlags[v.c] = [V.size.y, V.size.y];
+ else if (v.x == firstRank[v.c] && this.castleFlags[v.c].includes(v.y)) {
+ const flagIdx = (v.y == this.castleFlags[v.c][0] ? 0 : 1);
+ this.castleFlags[v.c][flagIdx] = V.size.y;
+ }
+ }
+ }
+
+ undo(move) {
+ if (!!move.ignore) return; //TODO: remove that later
+ this.disaggregateFlags(JSON.parse(move.flags));
+ V.UndoOnBoard(this.board, move);
+ if (this.subTurn == 1) {
+ this.amoves.pop();
+ this.turn = V.GetOppCol(this.turn);
+ this.movesCount--;
+ }
+ if (move.subTurn == 1) this.firstMove.pop();
+ this.subTurn = move.subTurn;
+ this.toOldKingPos(move);
+ }
+
+ toOldKingPos(move) {
+ // (Potentially) Reset king position
+ for (let v of move.vanish)
+ if (v.p == V.KING) this.kingPos[v.c] = [v.x, v.y];
+ }
+
+ getComputerMove() {
+ let moves = this.getAllValidMoves();
+ if (moves.length == 0) return null;
+ // "Search" at depth 1 for now
+ const maxeval = V.INFINITY;
+ const color = this.turn;
+ const emptyMove = {
+ start: { x: -1, y: -1 },
+ end: { x: -1, y: -1 },
+ appear: [],
+ vanish: []
+ };
+ moves.forEach(m => {
+ this.play(m);
+ if (this.turn != color) m.eval = this.evalPosition();
+ else {
+ m.eval = (color == "w" ? -1 : 1) * maxeval;
+ const moves2 = this.getAllValidMoves().concat([emptyMove]);
+ m.next = moves2[0];
+ moves2.forEach(m2 => {
+ this.play(m2);
+ const score = this.getCurrentScore();
+ let mvEval = 0;
+ if (score != "1/2") {
+ if (score != "*") mvEval = (score == "1-0" ? 1 : -1) * maxeval;
+ else mvEval = this.evalPosition();
+ }
+ if (
+ (color == 'w' && mvEval > m.eval) ||
+ (color == 'b' && mvEval < m.eval)
+ ) {
+ m.eval = mvEval;
+ m.next = m2;
+ }
+ this.undo(m2);
+ });
+ }
+ this.undo(m);
+ });
+ moves.sort((a, b) => {
+ return (color == "w" ? 1 : -1) * (b.eval - a.eval);
+ });
+ let candidates = [0];
+ for (let i = 1; i < moves.length && moves[i].eval == moves[0].eval; i++)
+ candidates.push(i);
+ const mIdx = candidates[randInt(candidates.length)];
+ if (!moves[mIdx].next) return moves[mIdx];
+ const move2 = moves[mIdx].next;
+ delete moves[mIdx]["next"];
+ return [moves[mIdx], move2];
+ }
+
+ getNotation(move) {
+ if (move.start.x < 0)
+ // A second move is always required, but may be empty
+ return "-";
+ const initialSquare = V.CoordsToSquare(move.start);
+ const finalSquare = V.CoordsToSquare(move.end);
+ if (move.appear.length == 0)
+ // Pushed or pulled out of the board
+ return initialSquare + "R";
+ return move.appear[0].p.toUpperCase() + initialSquare + finalSquare;
+ }
+
+};
diff --git a/variants/Dynamo/complete_rules.html b/variants/Dynamo/complete_rules.html
new file mode 100644
index 0000000..68e9cc0
--- /dev/null
+++ b/variants/Dynamo/complete_rules.html
@@ -0,0 +1,142 @@
+
+
+ Dynamo Rules
+
+
+
+
+
+
Dynamo Rules
+
+
+ Pieces have the same movement as in orthodox chess, but they cannot
+ take other pieces in the usual way. Instead of the normal captures, pieces
+ can pull or push other pieces, potentially off the board.
+ The goal is to send the enemy king off the board.
+
+
+
Each turn, a player has the following options:
+
+
+ Move one of his pieces normally, then optionally pull something as an
+ effect of this move.
+
+
+ Push any piece with one of his pieces, then optionally follow the pushed
+ piece.
+
+
+
+ It seems easier to understand with some examples. For a detailed
+ introduction please visit
+
+ this page
+ (in French).
+
+
+
+
+
+ Possible "pawn moves" in the initial position.
+
+
+
+ The e2 pawn can move to e3 and e4 as usual. It can also slide diagonally,
+ being pushed by the bishop or the queen (which may or may not move along
+ this line afterward). It can also go to c3, being pushed by the knight from
+ g1; then the knight can move to e2, or stay motionless.
+ Finally, the pawn can "take the king": this is a special move indicating that
+ you want it to exit the board. Indeed it could be pushed off the board by the
+ bishop or the queen.
+
+
+
+ Note: if an action is possible but you don't want to play a second part in
+ a move, click on any empty square: this will send an empty move.
+
+
+
+
+
+
+
+
+ Pulling the d5 pawn to c3 (left: before, right: after).
+
+
+
+
+ Pawns cannot pull (because they only move forward).
+
+ When they could reach the square beyond the edge,
+ pieces can exit the board by themselves, possibly dragging another piece
+ out (friendly or enemy).
+
+
+
+
+
+
+
+ Check: the queen threatens to pull the king off the board
+ along the a4-e8 diagonal.
+
+
+
+
+ It is forbidden to undo a "move + action". For example here, white could
+ push back the black bishop on g7 but not return to d4 then.
+
+
+
+
+
+
+
+ Pushing the d4 bishop to b2 (left: before, right: after).
+
+
+
+
+ Castling is possible as long as the king and rook have not moved and
+ haven't been pushed or pulled (this differs from the chessvariants
+ description).
+
+
+
End of the game
+
+
+ The game ends when a push or pull action threatens to send the king off the
+ board, and he has no way to escape it.
+
+
+
+
+
+ Dynamo checkmate ("Dynamate" :) )
+
+
+
+ The king cannot "take" on g4: this would just push the queen one step to the
+ left, and she would then push the king beyond the 'h' file.
+ There are no en-passant captures.
+
+
+
Source
+
+
+
+ Dynamo chess
+
+ on chessvariants.com. The short description given on
+ this page
+ might help too.
+
diff --git a/variants/Dynamo/rules.html b/variants/Dynamo/rules.html
new file mode 100644
index 0000000..4a78e5a
--- /dev/null
+++ b/variants/Dynamo/rules.html
@@ -0,0 +1,24 @@
+
+ Moves are potentially played in two times:
+ move a piece, and / or push or pull something with that unit.
+
+
+
Each turn, a player has the following options:
+
+
+ Move one of his pieces normally,
+ then optionally pull something as an effect of this move.
+
+
+ Push any piece with one of his pieces,
+ then optionally follow the pushed piece.
+
+
+
+
+
+ Full rules description.
+
+
+
+
Hans Kluever and Peter Kahl (1968).
diff --git a/variants/Dynamo/style.css b/variants/Dynamo/style.css
new file mode 100644
index 0000000..a3550bc
--- /dev/null
+++ b/variants/Dynamo/style.css
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
--
2.48.1
From 939e06bf9febef0a7935c7f6a58c5e28dee4dedc Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Fri, 5 Jan 2024 09:58:16 +0100
Subject: [PATCH 14/16] Start working on Dynamo
---
base_rules.js | 22 ++-
variants/Dynamo/class.js | 289 +++++----------------------------------
2 files changed, 52 insertions(+), 259 deletions(-)
diff --git a/base_rules.js b/base_rules.js
index 9a364e8..3d8e463 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -175,6 +175,22 @@ export default class ChessRules {
return Object.values(cd).map(c => c.toString(36)).join("");
}
+ // c10 --> 02 (assuming 10 rows)
+ static SquareFromUsual(sq) {
+ return (
+ (this.size.x - parseInt(sq.substring(1), 10)).toString(36) +
+ (sq.charCodeAt(0) - 97).toString(36)
+ );
+ }
+
+ // 02 --> c10
+ static UsualFromSquare(sq) {
+ return (
+ String.fromCharCode(parseInt(sq.charAt(1), 36) + 97) +
+ (this.size.x - parseInt(sq.charAt(0), 36)).toString(10)
+ );
+ }
+
coordsToId(cd) {
if (typeof cd.x == "number") {
return (
@@ -651,10 +667,8 @@ export default class ChessRules {
this[arrName] = ArrayFun.init(this.size.x, this.size.y, null);
if (arrName == "d_pieces")
this.marks.forEach((m) => {
- const formattedSquare =
- (this.size.x - parseInt(m.substring(1), 10)).toString(36) +
- (m.charCodeAt(0) - 97).toString(36);
- const mCoords = V.SquareToCoords(formattedSquare);
+ const formattedSquare = C.SquareFromUsual(m);
+ const mCoords = C.SquareToCoords(formattedSquare);
addPiece(mCoords.x, mCoords.y, arrName, "mark");
});
};
diff --git a/variants/Dynamo/class.js b/variants/Dynamo/class.js
index 0996a70..05f2aed 100644
--- a/variants/Dynamo/class.js
+++ b/variants/Dynamo/class.js
@@ -2,110 +2,51 @@ import ChessRules from "/base_rules.js";
export default class DynamoRules extends ChessRules {
- // TODO? later, allow to push out pawns on a and h files
- get hasEnpassant() {
- return false;
+ static get Options() {
+ // TODO
}
-/// TODO:::
+ get hasEnpassant() {
+ return this.options["enpassant"];
+ }
- canIplay(side, [x, y]) {
+ canIplay(x, y) {
// Sometimes opponent's pieces can be moved directly
- return this.turn == side;
+ return this.playerColor == this.turn;
+ }
+
+ canTake() {
+ // Captures don't occur (only pulls & pushes)
+ return false;
}
- setOtherVariables(fen) {
- super.setOtherVariables(fen);
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
this.subTurn = 1;
- // Local stack of "action moves"
- this.amoves = [];
- const amove = V.ParseFen(fen).amove;
- if (amove != "-") {
- const amoveParts = amove.split("/");
- let move = {
- // No need for start & end
- appear: [],
- vanish: []
- };
- [0, 1].map(i => {
- if (amoveParts[i] != "-") {
- amoveParts[i].split(".").forEach(av => {
- // Format is "bpe3"
- const xy = V.SquareToCoords(av.substr(2));
- move[i == 0 ? "appear" : "vanish"].push(
- new PiPo({
- x: xy.x,
- y: xy.y,
- c: av[0],
- p: av[1]
- })
- );
- });
- }
+ // Last action format: e2h5/d1g4 for queen on d1 pushing pawn to h5
+ // for example, and moving herself to g4. If just move: e2h5
+ this.lastAction = [];
+ if (fenParsed.amove != '-') {
+ this.lastAction = fenParsed.amove.split('/').map(a => {
+ return {
+ c1: C.SquareToCoords(C.SquareFromUsual(a.substr(0, 2))),
+ c2: C.SquareToCoords(C.SquareFromUsual(a.substr(2, 2)))
+ };
});
- this.amoves.push(move);
}
- // Stack "first moves" (on subTurn 1) to merge and check opposite moves
- this.firstMove = [];
- }
-
- static ParseFen(fen) {
- return Object.assign(
- ChessRules.ParseFen(fen),
- { amove: fen.split(" ")[4] }
- );
}
- static IsGoodFen(fen) {
- if (!ChessRules.IsGoodFen(fen)) return false;
- const fenParts = fen.split(" ");
- if (fenParts.length != 5) return false;
- if (fenParts[4] != "-") {
- // TODO: a single regexp instead.
- // Format is [bpa2[.wpd3]] || '-'/[bbc3[.wrd5]] || '-'
- const amoveParts = fenParts[4].split("/");
- if (amoveParts.length != 2) return false;
- for (let part of amoveParts) {
- if (part != "-") {
- for (let psq of part.split("."))
- if (!psq.match(/^[a-z]{3}[1-8]$/)) return false;
- }
- }
+ getPartFen(o) {
+ let res = super.getPartFen(o);
+ if (o.init)
+ res["amove"] = '-';
+ else {
+ res["amove"] = this.lastAction.map(a => {
+ C.UsualFromSquare(C.CoordsToSquare(a.c1)) +
+ C.UsualFromSquare(C.CoordsToSquare(a.c2))
+ }).join('/');
}
- return true;
- }
-
- getFen() {
- return super.getFen() + " " + this.getAmoveFen();
- }
-
- getFenForRepeat() {
- return super.getFenForRepeat() + "_" + this.getAmoveFen();
- }
-
- getAmoveFen() {
- const L = this.amoves.length;
- if (L == 0) return "-";
- return (
- ["appear","vanish"].map(
- mpart => {
- if (this.amoves[L-1][mpart].length == 0) return "-";
- return (
- this.amoves[L-1][mpart].map(
- av => {
- const square = V.CoordsToSquare({ x: av.x, y: av.y });
- return av.c + av.p + square;
- }
- ).join(".")
- );
- }
- ).join("/")
- );
- }
-
- canTake() {
- // Captures don't occur (only pulls & pushes)
- return false;
+ return res;
}
// Step is right, just add (push/pull) moves in this direction
@@ -590,6 +531,7 @@ export default class DynamoRules extends ChessRules {
);
}
+ // TODO: just stack in this.lastAction instead
getAmove(move1, move2) {
// Just merge (one is action one is move, one may be empty)
return {
@@ -643,105 +585,6 @@ export default class DynamoRules extends ChessRules {
);
}
- isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
- for (let step of steps) {
- let rx = x + step[0],
- ry = y + step[1];
- while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
- rx += step[0];
- ry += step[1];
- }
- if (
- V.OnBoard(rx, ry) &&
- this.getPiece(rx, ry) == piece &&
- this.getColor(rx, ry) == color
- ) {
- // Continue some steps in the same direction (pull)
- rx += step[0];
- ry += step[1];
- while (
- V.OnBoard(rx, ry) &&
- this.board[rx][ry] == V.EMPTY &&
- !oneStep
- ) {
- rx += step[0];
- ry += step[1];
- }
- if (!V.OnBoard(rx, ry)) return true;
- // Step in the other direction (push)
- rx = x - step[0];
- ry = y - step[1];
- while (
- V.OnBoard(rx, ry) &&
- this.board[rx][ry] == V.EMPTY &&
- !oneStep
- ) {
- rx -= step[0];
- ry -= step[1];
- }
- if (!V.OnBoard(rx, ry)) return true;
- }
- }
- return false;
- }
-
- isAttackedByPawn([x, y], color) {
- // The king can be pushed out by a pawn on last rank or near the edge
- const pawnShift = (color == "w" ? 1 : -1);
- for (let i of [-1, 1]) {
- if (
- V.OnBoard(x + pawnShift, y + i) &&
- this.board[x + pawnShift][y + i] != V.EMPTY &&
- this.getPiece(x + pawnShift, y + i) == V.PAWN &&
- this.getColor(x + pawnShift, y + i) == color
- ) {
- if (!V.OnBoard(x - pawnShift, y - i)) return true;
- }
- }
- return false;
- }
-
- static OnTheEdge(x, y) {
- return (x == 0 || x == 7 || y == 0 || y == 7);
- }
-
- isAttackedByKing([x, y], color) {
- // Attacked if I'm on the edge and the opponent king just next,
- // but not on the edge.
- if (V.OnTheEdge(x, y)) {
- for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
- const [i, j] = [x + step[0], y + step[1]];
- if (
- V.OnBoard(i, j) &&
- !V.OnTheEdge(i, j) &&
- this.board[i][j] != V.EMPTY &&
- this.getPiece(i, j) == V.KING
- // NOTE: since only one king of each color, and (x, y) is occupied
- // by our king, no need to check other king's color.
- ) {
- return true;
- }
- }
- }
- return false;
- }
-
- // No consideration of color: all pieces could be played
- getAllPotentialMoves() {
- let potentialMoves = [];
- 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) {
- Array.prototype.push.apply(
- potentialMoves,
- this.getPotentialMovesFrom([i, j])
- );
- }
- }
- }
- return potentialMoves;
- }
-
getEmptyMove() {
return new Move({
start: { x: -1, y: -1 },
@@ -854,68 +697,4 @@ export default class DynamoRules extends ChessRules {
if (v.p == V.KING) this.kingPos[v.c] = [v.x, v.y];
}
- getComputerMove() {
- let moves = this.getAllValidMoves();
- if (moves.length == 0) return null;
- // "Search" at depth 1 for now
- const maxeval = V.INFINITY;
- const color = this.turn;
- const emptyMove = {
- start: { x: -1, y: -1 },
- end: { x: -1, y: -1 },
- appear: [],
- vanish: []
- };
- moves.forEach(m => {
- this.play(m);
- if (this.turn != color) m.eval = this.evalPosition();
- else {
- m.eval = (color == "w" ? -1 : 1) * maxeval;
- const moves2 = this.getAllValidMoves().concat([emptyMove]);
- m.next = moves2[0];
- moves2.forEach(m2 => {
- this.play(m2);
- const score = this.getCurrentScore();
- let mvEval = 0;
- if (score != "1/2") {
- if (score != "*") mvEval = (score == "1-0" ? 1 : -1) * maxeval;
- else mvEval = this.evalPosition();
- }
- if (
- (color == 'w' && mvEval > m.eval) ||
- (color == 'b' && mvEval < m.eval)
- ) {
- m.eval = mvEval;
- m.next = m2;
- }
- this.undo(m2);
- });
- }
- this.undo(m);
- });
- moves.sort((a, b) => {
- return (color == "w" ? 1 : -1) * (b.eval - a.eval);
- });
- let candidates = [0];
- for (let i = 1; i < moves.length && moves[i].eval == moves[0].eval; i++)
- candidates.push(i);
- const mIdx = candidates[randInt(candidates.length)];
- if (!moves[mIdx].next) return moves[mIdx];
- const move2 = moves[mIdx].next;
- delete moves[mIdx]["next"];
- return [moves[mIdx], move2];
- }
-
- getNotation(move) {
- if (move.start.x < 0)
- // A second move is always required, but may be empty
- return "-";
- const initialSquare = V.CoordsToSquare(move.start);
- const finalSquare = V.CoordsToSquare(move.end);
- if (move.appear.length == 0)
- // Pushed or pulled out of the board
- return initialSquare + "R";
- return move.appear[0].p.toUpperCase() + initialSquare + finalSquare;
- }
-
};
--
2.48.1
From 253e65f6c4f342e5ac8230d7340ed413354f9c7f Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Fri, 19 Jan 2024 17:53:42 +0100
Subject: [PATCH 15/16] New variant idea
---
TODO | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/TODO b/TODO
index 8afe466..f49a584 100644
--- a/TODO
+++ b/TODO
@@ -4,3 +4,7 @@ Otage, Emergo, Pacosako : fonction "buildPiece(arg1, arg2)" returns HTML element
https://fr.wikipedia.org/wiki/Unlur
Yoxii ?
+
+Idée new variant: "bed" random moves and "capture" (opponent?) pieces which cannot move on next turn (sleeping). (Crazybed ?)
+one "bed" per player.
+one turn = place bed (potentially with opponent piece), then normal move, then random bed move - dropping potential piece on initial square (awaken).
--
2.48.1
From d4be1a764e43f208ef34ee5c7298249b98e0baf1 Mon Sep 17 00:00:00 2001
From: Benjamin Auder
Date: Tue, 28 Jan 2025 18:50:55 +0100
Subject: [PATCH 16/16] Work on Dynamo
---
variants/Dynamo/class.js | 227 +++++++++++++++++++--------------------
1 file changed, 113 insertions(+), 114 deletions(-)
diff --git a/variants/Dynamo/class.js b/variants/Dynamo/class.js
index 05f2aed..ed0ea27 100644
--- a/variants/Dynamo/class.js
+++ b/variants/Dynamo/class.js
@@ -3,7 +3,11 @@ import ChessRules from "/base_rules.js";
export default class DynamoRules extends ChessRules {
static get Options() {
- // TODO
+ return {
+ select: C.Options.select,
+ input: [],
+ styles: ["cylinder", "doublemove", "progressive"]
+ };
}
get hasEnpassant() {
@@ -51,7 +55,7 @@ export default class DynamoRules extends ChessRules {
// Step is right, just add (push/pull) moves in this direction
// Direction is assumed normalized.
- getMovesInDirection([x, y], [dx, dy], nbSteps) {
+ getMovesInDirection([x, y], [dx, dy], nbSteps, kp) {
nbSteps = nbSteps || 8; //max 8 steps anyway
let [i, j] = [x + dx, y + dy];
let moves = [];
@@ -59,25 +63,27 @@ export default class DynamoRules extends ChessRules {
const piece = this.getPiece(x, y);
const lastRank = (color == 'w' ? 0 : 7);
let counter = 1;
- while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
- if (i == lastRank && piece == V.PAWN) {
+ while (this.onBoard(i, j) && this.board[i][j] == "") {
+ if (i == lastRank && piece == 'p') {
// Promotion by push or pull
- V.PawnSpecs.promotions.forEach(p => {
+ this.pawnPromotions.forEach(p => {
let move = super.getBasicMove([x, y], [i, j], { c: color, p: p });
moves.push(move);
});
}
- else moves.push(super.getBasicMove([x, y], [i, j]));
- if (++counter > nbSteps) break;
+ else
+ moves.push(super.getBasicMove([x, y], [i, j]));
+ if (++counter > nbSteps)
+ break;
i += dx;
j += dy;
}
- if (!V.OnBoard(i, j) && piece != V.KING) {
+ if (!this.onBoard(i, j) && piece != 'k') {
// Add special "exit" move, by "taking king"
moves.push(
new Move({
start: { x: x, y: y },
- end: { x: this.kingPos[color][0], y: this.kingPos[color][1] },
+ end: { x: kp[0], y: kp[1] },
appear: [],
vanish: [{ x: x, y: y, c: color, p: piece }]
})
@@ -109,14 +115,14 @@ export default class DynamoRules extends ChessRules {
const checkSlider = () => {
const dir = this.getNormalizedDirection([x2 - x1, y2 - y1]);
let [i, j] = [x1 + dir[0], y1 + dir[1]];
- while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ while (this.onBoard(i, j) && this.board[i][j] == "") {
i += dir[0];
j += dir[1];
}
- return !V.OnBoard(i, j);
+ return !this.onBoard(i, j);
};
switch (piece2 || this.getPiece(x1, y1)) {
- case V.PAWN:
+ case 'p':
return (
x1 + pawnShift == x2 &&
(
@@ -124,30 +130,33 @@ export default class DynamoRules extends ChessRules {
(
color1 != color2 &&
deltaY == 1 &&
- !V.OnBoard(2 * x2 - x1, 2 * y2 - y1)
+ !this.onBoard(2 * x2 - x1, 2 * y2 - y1)
)
)
);
- case V.ROOK:
- if (x1 != x2 && y1 != y2) return false;
+ case 'r':
+ if (x1 != x2 && y1 != y2)
+ return false;
return checkSlider();
- case V.KNIGHT:
+ case 'n':
return (
deltaX + deltaY == 3 &&
(deltaX == 1 || deltaY == 1) &&
- !V.OnBoard(2 * x2 - x1, 2 * y2 - y1)
+ !this.onBoard(2 * x2 - x1, 2 * y2 - y1)
);
- case V.BISHOP:
- if (deltaX != deltaY) return false;
+ case 'b':
+ if (deltaX != deltaY)
+ return false;
return checkSlider();
- case V.QUEEN:
- if (deltaX != 0 && deltaY != 0 && deltaX != deltaY) return false;
+ case 'q':
+ if (deltaX != 0 && deltaY != 0 && deltaX != deltaY)
+ return false;
return checkSlider();
- case V.KING:
+ case 'k':
return (
deltaX <= 1 &&
deltaY <= 1 &&
- !V.OnBoard(2 * x2 - x1, 2 * y2 - y1)
+ !this.onBoard(2 * x2 - x1, 2 * y2 - y1)
);
}
return false;
@@ -158,12 +167,12 @@ export default class DynamoRules extends ChessRules {
const deltaX = Math.abs(x1 - x2);
const startRank = (this.getColor(x1, y1) == 'w' ? 6 : 1);
return (
- [V.QUEEN, V.ROOK].includes(piece) ||
+ ['q', 'r'].includes(piece) ||
(
- [V.KING, V.PAWN].includes(piece) &&
+ ['k', 'p'].includes(piece) &&
(
deltaX == 1 ||
- (deltaX == 2 && piece == V.PAWN && x1 == startRank)
+ (deltaX == 2 && piece == 'p' && x1 == startRank)
)
)
);
@@ -178,12 +187,13 @@ export default class DynamoRules extends ChessRules {
const pawnShift = (color == 'w' ? -1 : 1);
const pawnStartRank = (color == 'w' ? 6 : 1);
const getMoveHash = (m) => {
- return V.CoordsToSquare(m.start) + V.CoordsToSquare(m.end);
+ return C.CoordsToSquare(m.start) + C.CoordsToSquare(m.end);
};
if (this.subTurn == 1) {
+ const kp = this.searchKingPos(color);
const addMoves = (dir, nbSteps) => {
const newMoves =
- this.getMovesInDirection([x, y], [-dir[0], -dir[1]], nbSteps)
+ this.getMovesInDirection([x, y], [-dir[0], -dir[1]], nbSteps, kp)
.filter(m => !movesHash[getMoveHash(m)]);
newMoves.forEach(m => { movesHash[getMoveHash(m)] = true; });
Array.prototype.push.apply(moves, newMoves);
@@ -198,7 +208,8 @@ export default class DynamoRules extends ChessRules {
moves = moves.filter(m => {
const suicide = (m.appear.length == 0);
if (suicide) {
- if (hasExit) return false;
+ if (hasExit)
+ return false;
hasExit = true;
}
return true;
@@ -207,32 +218,34 @@ export default class DynamoRules extends ChessRules {
let movesHash = {};
moves.forEach(m => { movesHash[getMoveHash(m)] = true; });
// [x, y] is pushed by 'color'
- for (let step of V.steps[V.KNIGHT]) {
+ for (let step of this.pieces()['n'].both[0].steps) {
const [i, j] = [x + step[0], y + step[1]];
if (
- V.OnBoard(i, j) &&
- this.board[i][j] != V.EMPTY &&
+ this.onBoard(i, j) &&
+ this.board[i][j] != "" &&
this.getColor(i, j) == color &&
- this.getPiece(i, j) == V.KNIGHT
+ this.getPiece(i, j) == 'n'
) {
addMoves(step, 1);
}
}
- for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
+ for (let step of this.pieces()['r'].both[0].steps.concat(
+ this.pieces()['b'].both[0].steps))
+ {
let [i, j] = [x + step[0], y + step[1]];
- while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ while (this.onBoard(i, j) && this.board[i][j] == "") {
i += step[0];
j += step[1];
}
if (
- V.OnBoard(i, j) &&
- this.board[i][j] != V.EMPTY &&
+ this.onBoard(i, j) &&
+ this.board[i][j] != "" &&
this.getColor(i, j) == color
) {
const deltaX = Math.abs(i - x);
const deltaY = Math.abs(j - y);
switch (this.getPiece(i, j)) {
- case V.PAWN:
+ case 'p':
if (
(x - i) / deltaX == pawnShift &&
deltaX <= 2 &&
@@ -248,18 +261,21 @@ export default class DynamoRules extends ChessRules {
addMoves(step, 1);
}
break;
- case V.ROOK:
- if (deltaX == 0 || deltaY == 0) addMoves(step);
+ case 'r':
+ if (deltaX == 0 || deltaY == 0)
+ addMoves(step);
break;
- case V.BISHOP:
- if (deltaX == deltaY) addMoves(step);
+ case 'b':
+ if (deltaX == deltaY)
+ addMoves(step);
break;
- case V.QUEEN:
+ case 'q':
// All steps are valid for a queen:
addMoves(step);
break;
- case V.KING:
- if (deltaX <= 1 && deltaY <= 1) addMoves(step, 1);
+ case 'k':
+ if (deltaX <= 1 && deltaY <= 1)
+ addMoves(step, 1);
break;
}
}
@@ -357,13 +373,16 @@ export default class DynamoRules extends ChessRules {
}
break;
case V.BISHOP:
- if (deltaX != deltaY) return [];
+ if (deltaX != deltaY)
+ return [];
break;
case V.ROOK:
- if (deltaX != 0 && deltaY != 0) return [];
+ if (deltaX != 0 && deltaY != 0)
+ return [];
break;
case V.QUEEN:
- if (deltaX != deltaY && deltaX != 0 && deltaY != 0) return [];
+ if (deltaX != deltaY && deltaX != 0 && deltaY != 0)
+ return [];
break;
}
// Nothing should stand between [x, y] and the square fm.start
@@ -437,12 +456,14 @@ export default class DynamoRules extends ChessRules {
};
if (fm.vanish[0].c != color) {
// Only possible action is a push:
- if (fm.appear.length == 0) return getPushExit();
+ if (fm.appear.length == 0)
+ return getPushExit();
return getPushMoves();
}
else if (sqCol != color) {
// Only possible action is a pull, considering moving piece abilities
- if (fm.appear.length == 0) return getPullExit();
+ if (fm.appear.length == 0)
+ return getPullExit();
return getPullMoves();
}
else {
@@ -474,7 +495,8 @@ export default class DynamoRules extends ChessRules {
let j = y + step[1];
while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
moves.push(this.getBasicMove([x, y], [i, j]));
- if (oneStep) continue outerLoop;
+ if (oneStep)
+ continue outerLoop;
i += step[0];
j += step[1];
}
@@ -516,7 +538,8 @@ export default class DynamoRules extends ChessRules {
elt.p == av.p
);
});
- if (!avInAv2) return false;
+ if (!avInAv2)
+ return false;
}
return true;
};
@@ -540,6 +563,7 @@ export default class DynamoRules extends ChessRules {
}
}
+ // TODO ::
filterValid(moves) {
const color = this.turn;
const La = this.amoves.length;
@@ -572,7 +596,8 @@ export default class DynamoRules extends ChessRules {
return !res;
});
}
- if (La == 0) return super.filterValid(moves);
+ if (La == 0)
+ return super.filterValid(moves);
const Lf = this.firstMove.length;
return (
super.filterValid(
@@ -598,7 +623,8 @@ export default class DynamoRules extends ChessRules {
// A click to promote a piece on subTurn 2 would trigger this.
// For now it would then return [NaN, NaN] because surrounding squares
// have no IDs in the promotion modal. TODO: improve this?
- if (isNaN(square[0])) return null;
+ if (isNaN(square[0]))
+ return null;
// If subTurn == 2 && square is empty && !underCheck && !isOpposite,
// then return an empty move, allowing to "pass" subTurn2
const La = this.amoves.length;
@@ -614,87 +640,60 @@ export default class DynamoRules extends ChessRules {
return null;
}
- play(move) {
- if (this.subTurn == 1 && move.vanish.length == 0) {
- // Patch to work with old format: (TODO: remove later)
- move.ignore = true;
- return;
+ updateCastleFlags(move) {
+ if (move.start.x < 0)
+ return; //empty move (pass subTurn 2)
+ const firstRank = { 'w': V.size.x - 1, 'b': 0 };
+ for (let v of move.vanish) {
+ if (v.p == 'k')
+ this.castleFlags[v.c] = [this.size.y, this.size.y];
+ else if (v.x == firstRank[v.c] && this.castleFlags[v.c].includes(v.y)) {
+ const flagIdx = (v.y == this.castleFlags[v.c][0] ? 0 : 1);
+ this.castleFlags[v.c][flagIdx] = this.size.y;
+ }
}
+ }
+
+ play(move) {
+// if (this.subTurn == 1 && move.vanish.length == 0) {
+// // Patch to work with old format: (TODO: remove later)
+// move.ignore = true;
+// return;
+// }
+
+ // In preplay ?
+ this.updateCastleFlags(move);
+
+ const oppCol = C.GetOppTurn(color);
+
const color = this.turn;
- move.subTurn = this.subTurn; //for undo
const gotoNext = (mv) => {
const L = this.firstMove.length;
this.amoves.push(this.getAmove(this.firstMove[L-1], mv));
- this.turn = V.GetOppCol(color);
+ this.turn = oppCol;
this.subTurn = 1;
this.movesCount++;
};
- move.flags = JSON.stringify(this.aggregateFlags());
- V.PlayOnBoard(this.board, move);
- if (this.subTurn == 2) gotoNext(move);
+ this.playOnBoard(move);
+ if (this.subTurn == 2)
+ gotoNext(move);
else {
this.subTurn = 2;
this.firstMove.push(move);
- this.toNewKingPos(move);
if (
- // Condition is true on empty arrays:
+ // Condition is true on empty arrays: //TODO: getAllPotentialMoves doesn't exist
this.getAllPotentialMoves().every(m => {
- V.PlayOnBoard(this.board, m);
- this.toNewKingPos(m);
- const res = this.underCheck(color);
- V.UndoOnBoard(this.board, m);
- this.toOldKingPos(m);
+ this.playOnBoard(m);
+ const res = this.underCheck([kp], oppCol); //TODO: find kp first
+ this.undoOnBoard(m);
return res;
})
) {
// No valid move at subTurn 2
gotoNext(this.getEmptyMove());
}
- this.toOldKingPos(move);
}
this.postPlay(move);
}
- toNewKingPos(move) {
- for (let a of move.appear)
- if (a.p == V.KING) this.kingPos[a.c] = [a.x, a.y];
- }
-
- postPlay(move) {
- if (move.start.x < 0) return;
- this.toNewKingPos(move);
- this.updateCastleFlags(move);
- }
-
- updateCastleFlags(move) {
- const firstRank = { 'w': V.size.x - 1, 'b': 0 };
- for (let v of move.vanish) {
- if (v.p == V.KING) this.castleFlags[v.c] = [V.size.y, V.size.y];
- else if (v.x == firstRank[v.c] && this.castleFlags[v.c].includes(v.y)) {
- const flagIdx = (v.y == this.castleFlags[v.c][0] ? 0 : 1);
- this.castleFlags[v.c][flagIdx] = V.size.y;
- }
- }
- }
-
- undo(move) {
- if (!!move.ignore) return; //TODO: remove that later
- this.disaggregateFlags(JSON.parse(move.flags));
- V.UndoOnBoard(this.board, move);
- if (this.subTurn == 1) {
- this.amoves.pop();
- this.turn = V.GetOppCol(this.turn);
- this.movesCount--;
- }
- if (move.subTurn == 1) this.firstMove.pop();
- this.subTurn = move.subTurn;
- this.toOldKingPos(move);
- }
-
- toOldKingPos(move) {
- // (Potentially) Reset king position
- for (let v of move.vanish)
- if (v.p == V.KING) this.kingPos[v.c] = [v.x, v.y];
- }
-
};
--
2.48.1