1 import { ChessRules
} from "@/base_rules";
3 export const VariantRules
= class ShatranjRules
extends ChessRules
{
4 static get HasFlags() {
8 static get HasEnpassant() {
12 static get ElephantSteps() {
21 static GenRandInitFen(randomness
) {
22 // Remove castle flags and en-passant indication
23 return ChessRules
.GenRandInitFen(randomness
).slice(0, -7);
26 getPotentialPawnMoves([x
, y
]) {
27 const color
= this.turn
;
29 const [sizeX
, sizeY
] = [V
.size
.x
, V
.size
.y
];
30 const shiftX
= color
== "w" ? -1 : 1;
31 const startRank
= color
== "w" ? sizeX
- 2 : 1;
32 const lastRank
= color
== "w" ? 0 : sizeX
- 1;
33 // Promotion in minister (queen) only:
34 const finalPiece
= x
+ shiftX
== lastRank
? V
.QUEEN : V
.PAWN
;
36 if (this.board
[x
+ shiftX
][y
] == V
.EMPTY
) {
39 this.getBasicMove([x
, y
], [x
+ shiftX
, y
], {
46 for (let shiftY
of [-1, 1]) {
50 this.board
[x
+ shiftX
][y
+ shiftY
] != V
.EMPTY
&&
51 this.canTake([x
, y
], [x
+ shiftX
, y
+ shiftY
])
54 this.getBasicMove([x
, y
], [x
+ shiftX
, y
+ shiftY
], {
65 getPotentialBishopMoves(sq
) {
66 let moves
= this.getSlideNJumpMoves(sq
, V
.ElephantSteps
, "oneStep");
67 // Complete with "repositioning moves": like a queen, without capture
68 let repositioningMoves
= this.getSlideNJumpMoves(
72 ).filter(m
=> m
.vanish
.length
== 1);
73 return moves
.concat(repositioningMoves
);
76 getPotentialQueenMoves(sq
) {
77 // Diagonal capturing moves
78 let captures
= this.getSlideNJumpMoves(
82 ).filter(m
=> m
.vanish
.length
== 2);
83 return captures
.concat(
84 // Orthogonal non-capturing moves
85 this.getSlideNJumpMoves(
89 ).filter(m
=> m
.vanish
.length
== 1)
93 getPotentialKingMoves(sq
) {
94 return this.getSlideNJumpMoves(
96 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
101 isAttackedByBishop(sq
, color
) {
102 return this.isAttackedBySlideNJump(
111 isAttackedByQueen(sq
, color
) {
112 return this.isAttackedBySlideNJump(
122 const color
= this.turn
;
123 const getScoreLost
= () => {
125 return color
== "w" ? "0-1" : "1-0";
127 if (!this.atLeastOneMove())
128 // No valid move: I lose (this includes checkmate)
129 return getScoreLost();
130 // Win if the opponent has no pieces left (except king),
131 // and cannot bare king on the next move.
133 // No need to remember all pieces' squares:
134 // variable only used if just one remaining piece.
135 "w": {count: 0, square: null},
136 "b": {count: 0, square: null}
138 outerLoop: for (let i
=0; i
<V
.size
.x
; i
++) {
139 for (let j
=0; j
<V
.size
.y
; j
++) {
140 if (this.board
[i
][j
] != V
.EMPTY
&& this.getPiece(i
,j
) != V
.KING
) {
141 const sqCol
= this.getColor(i
,j
);
142 piecesLeft
[sqCol
].count
++;
143 piecesLeft
[sqCol
].square
= [i
,j
];
147 if (Object
.values(piecesLeft
).every(v
=> v
.count
> 0))
149 // No pieces left for some side: if both kings are bare, it's a draw
150 if (Object
.values(piecesLeft
).every(v
=> v
.count
== 0))
152 if (piecesLeft
[color
].count
> 0)
153 // He could have drawn, but didn't take my last piece...
154 return color
== "w" ? "1-0" : "0-1";
155 const oppCol
= V
.GetOppCol(color
);
156 if (piecesLeft
[oppCol
].count
>= 2)
157 // 2 enemy units or more: I lose
158 return getScoreLost();
159 // I don't have any piece, my opponent have one: can I take it?
160 if (this.isAttacked(piecesLeft
[oppCol
].square
, color
))
161 // Yes! But I still need to take it
164 return getScoreLost();
167 static get VALUES() {