1 import { ChessRules
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { randInt
} from "@/utils/alea";
5 export class GrandRules
extends ChessRules
{
7 static get HasCastle() {
11 static IsGoodFen(fen
) {
12 if (!ChessRules
.IsGoodFen(fen
)) return false;
13 const fenParsed
= V
.ParseFen(fen
);
15 if (!fenParsed
.captured
|| !fenParsed
.captured
.match(/^[0-9]{12,12}$/))
20 static IsGoodEnpassant(enpassant
) {
21 if (enpassant
!= "-") return !!enpassant
.match(/^([a-j][0-9]{1,2},?)+$/);
25 static ParseFen(fen
) {
26 const fenParts
= fen
.split(" ");
28 ChessRules
.ParseFen(fen
),
29 { captured: fenParts
[4] }
34 return ([V
.MARSHALL
, V
.CARDINAL
].includes(b
[1]) ? "Grand/" : "") + b
;
38 return super.getFen() + " " + this.getCapturedFen();
42 return super.getFenForRepeat() + "_" + this.getCapturedFen();
46 let counts
= [...Array(12).fill(0)];
48 for (let j
= 0; j
< V
.PIECES
.length
; j
++) {
49 if ([V
.KING
, V
.PAWN
].includes(V
.PIECES
[j
]))
50 // No king captured, and pawns don't promote in pawns
52 counts
[i
] = this.captured
["w"][V
.PIECES
[j
]];
53 counts
[6 + i
] = this.captured
["b"][V
.PIECES
[j
]];
56 return counts
.join("");
59 setOtherVariables(fen
) {
60 super.setOtherVariables(fen
);
62 V
.ParseFen(fen
).captured
.split("").map(x
=> parseInt(x
, 10));
63 // Initialize captured pieces' counts from FEN
66 [V
.ROOK
]: captured
[0],
67 [V
.KNIGHT
]: captured
[1],
68 [V
.BISHOP
]: captured
[2],
69 [V
.QUEEN
]: captured
[3],
70 [V
.MARSHALL
]: captured
[4],
71 [V
.CARDINAL
]: captured
[5]
74 [V
.ROOK
]: captured
[6],
75 [V
.KNIGHT
]: captured
[7],
76 [V
.BISHOP
]: captured
[8],
77 [V
.QUEEN
]: captured
[9],
78 [V
.MARSHALL
]: captured
[10],
79 [V
.CARDINAL
]: captured
[11]
85 return { x: 10, y: 10 };
89 static get MARSHALL() {
94 static get CARDINAL() {
99 return ChessRules
.PIECES
.concat([V
.MARSHALL
, V
.CARDINAL
]);
102 getPotentialMovesFrom([x
, y
]) {
103 switch (this.getPiece(x
, y
)) {
105 return this.getPotentialMarshallMoves([x
, y
]);
107 return this.getPotentialCardinalMoves([x
, y
]);
109 return super.getPotentialMovesFrom([x
, y
]);
113 // Special pawn rules: promotions to captured friendly pieces,
114 // optional on ranks 8-9 and mandatory on rank 10.
115 getPotentialPawnMoves([x
, y
]) {
116 const color
= this.turn
;
118 const [sizeX
, sizeY
] = [V
.size
.x
, V
.size
.y
];
119 const shiftX
= color
== "w" ? -1 : 1;
120 const startRank
= (color
== "w" ? sizeX
- 3 : 2);
122 color
== "w" ? [0, 1, 2] : [sizeX
- 1, sizeX
- 2, sizeX
- 3];
123 const promotionPieces
= [
132 // Always x+shiftX >= 0 && x+shiftX < sizeX, because no pawns on last rank
133 let finalPieces
= undefined;
134 if (lastRanks
.includes(x
+ shiftX
)) {
135 finalPieces
= promotionPieces
.filter(p
=> this.captured
[color
][p
] > 0);
136 if (x
+ shiftX
!= lastRanks
[0]) finalPieces
.push(V
.PAWN
);
138 else finalPieces
= [V
.PAWN
];
139 if (this.board
[x
+ shiftX
][y
] == V
.EMPTY
) {
140 // One square forward
141 for (let piece
of finalPieces
)
143 this.getBasicMove([x
, y
], [x
+ shiftX
, y
], { c: color
, p: piece
})
145 if (x
== startRank
&& this.board
[x
+ 2 * shiftX
][y
] == V
.EMPTY
)
147 moves
.push(this.getBasicMove([x
, y
], [x
+ 2 * shiftX
, y
]));
150 for (let shiftY
of [-1, 1]) {
153 y
+ shiftY
< sizeY
&&
154 this.board
[x
+ shiftX
][y
+ shiftY
] != V
.EMPTY
&&
155 this.canTake([x
, y
], [x
+ shiftX
, y
+ shiftY
])
157 for (let piece
of finalPieces
) {
159 this.getBasicMove([x
, y
], [x
+ shiftX
, y
+ shiftY
], {
169 Array
.prototype.push
.apply(
171 this.getEnpassantCaptures([x
, y
], shiftX
)
177 getPotentialMarshallMoves(sq
) {
178 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.ROOK
]).concat(
179 this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
], "oneStep")
183 getPotentialCardinalMoves(sq
) {
184 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
]).concat(
185 this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
], "oneStep")
189 isAttacked(sq
, color
) {
191 super.isAttacked(sq
, color
) ||
192 this.isAttackedByMarshall(sq
, color
) ||
193 this.isAttackedByCardinal(sq
, color
)
197 isAttackedByMarshall(sq
, color
) {
199 this.isAttackedBySlideNJump(sq
, color
, V
.MARSHALL
, V
.steps
[V
.ROOK
]) ||
200 this.isAttackedBySlideNJump(
210 isAttackedByCardinal(sq
, color
) {
212 this.isAttackedBySlideNJump(sq
, color
, V
.CARDINAL
, V
.steps
[V
.BISHOP
]) ||
213 this.isAttackedBySlideNJump(
224 super.postPlay(move);
225 if (move.vanish
.length
== 2 && move.appear
.length
== 1)
226 // Capture: update this.captured
227 this.captured
[move.vanish
[1].c
][move.vanish
[1].p
]++;
231 super.postUndo(move);
232 if (move.vanish
.length
== 2 && move.appear
.length
== 1)
233 this.captured
[move.vanish
[1].c
][move.vanish
[1].p
]--;
236 static get VALUES() {
237 return Object
.assign(
238 { c: 5, m: 7 }, //experimental
243 static get SEARCH_DEPTH() {
247 static GenRandInitFen(randomness
) {
248 if (randomness
== 0) {
250 "r8r/1nbqkmcbn1/pppppppppp/91/91/91/91/PPPPPPPPPP/1NBQKMCBN1/R8R " +
251 "w 0 - 00000000000000"
255 let pieces
= { w: new Array(8), b: new Array(8) };
256 // Shuffle pieces on second and before-last rank
257 for (let c
of ["w", "b"]) {
258 if (c
== 'b' && randomness
== 1) {
259 pieces
['b'] = pieces
['w'];
263 let positions
= ArrayFun
.range(8);
265 // Get random squares for bishops
266 let randIndex
= 2 * randInt(4);
267 let bishop1Pos
= positions
[randIndex
];
268 // The second bishop must be on a square of different color
269 let randIndex_tmp
= 2 * randInt(4) + 1;
270 let bishop2Pos
= positions
[randIndex_tmp
];
271 // Remove chosen squares
272 positions
.splice(Math
.max(randIndex
, randIndex_tmp
), 1);
273 positions
.splice(Math
.min(randIndex
, randIndex_tmp
), 1);
275 // Get random squares for knights
276 randIndex
= randInt(6);
277 let knight1Pos
= positions
[randIndex
];
278 positions
.splice(randIndex
, 1);
279 randIndex
= randInt(5);
280 let knight2Pos
= positions
[randIndex
];
281 positions
.splice(randIndex
, 1);
283 // Get random square for queen
284 randIndex
= randInt(4);
285 let queenPos
= positions
[randIndex
];
286 positions
.splice(randIndex
, 1);
288 // ...random square for marshall
289 randIndex
= randInt(3);
290 let marshallPos
= positions
[randIndex
];
291 positions
.splice(randIndex
, 1);
293 // ...random square for cardinal
294 randIndex
= randInt(2);
295 let cardinalPos
= positions
[randIndex
];
296 positions
.splice(randIndex
, 1);
298 // King position is now fixed,
299 let kingPos
= positions
[0];
301 // Finally put the shuffled pieces in the board array
302 pieces
[c
][knight1Pos
] = "n";
303 pieces
[c
][bishop1Pos
] = "b";
304 pieces
[c
][queenPos
] = "q";
305 pieces
[c
][marshallPos
] = "m";
306 pieces
[c
][cardinalPos
] = "c";
307 pieces
[c
][kingPos
] = "k";
308 pieces
[c
][bishop2Pos
] = "b";
309 pieces
[c
][knight2Pos
] = "n";
312 "r8r/1" + pieces
["b"].join("") + "1/" +
313 "pppppppppp/91/91/91/91/PPPPPPPPPP/" +
314 "1" + pieces
["w"].join("").toUpperCase() + "1/R8R" +
315 " w 0 " + " - 00000000000000"