1 import { ChessRules
, PiPo
, Move
} from "@/base_rules";
3 export class ShinobiRules
extends ChessRules
{
5 static get LoseOnRepetition() {
28 static IsGoodFlags(flags
) {
29 // Only black can castle
30 return !!flags
.match(/^[a-z]{2,2}$/);
36 .concat([V
.CAPTAIN
, V
.NINJA
, V
.DRAGON
, V
.MONK
, V
.HORSE
, V
.LANCE
])
41 if (b
[0] == 'b' && b
[1] != 'c') return b
;
42 return "Shinobi/" + b
;
45 getReservePpath(index
, color
) {
46 return "Shinobi/" + color
+ V
.RESERVE_PIECES
[index
];
50 return this.castleFlags
['b'].map(V
.CoordToColumn
).join("");
54 this.castleFlags
= { 'b': [-1, -1] };
55 for (let i
= 0; i
< 2; i
++)
56 this.castleFlags
['b'][i
] = V
.ColumnToCoord(fenflags
.charAt(i
));
59 static IsGoodFen(fen
) {
60 if (!ChessRules
.IsGoodFen(fen
)) return false;
61 const fenParsed
= V
.ParseFen(fen
);
63 if (!fenParsed
.reserve
|| !fenParsed
.reserve
.match(/^[0-2]{6,6}$/))
68 static ParseFen(fen
) {
69 const fenParts
= fen
.split(" ");
71 ChessRules
.ParseFen(fen
),
72 { reserve: fenParts
[5] }
76 // In hand initially: ninja, dragon, 2 x (monk, horse), lance, pawn.
77 static GenRandInitFen(randomness
) {
78 const baseFen
= ChessRules
.GenRandInitFen(Math
.min(randomness
, 1));
80 baseFen
.substr(0, 35) + "3CK3 " +
81 "w 0 " + baseFen
.substr(48, 2) + " - 112211"
86 return super.getFen() + " " + this.getReserveFen();
90 return super.getFenForRepeat() + "_" + this.getReserveFen();
94 // TODO: can simplify other drops variants with this code:
95 return Object
.values(this.reserve
['w']).join("");
98 setOtherVariables(fen
) {
99 super.setOtherVariables(fen
);
101 V
.ParseFen(fen
).reserve
.split("").map(x
=> parseInt(x
, 10));
104 [V
.NINJA
]: reserve
[0],
105 [V
.DRAGON
]: reserve
[1],
106 [V
.MONK
]: reserve
[2],
107 [V
.HORSE
]: reserve
[3],
108 [V
.LANCE
]: reserve
[4],
115 if (i
>= V
.size
.x
) return 'w';
116 return this.board
[i
][j
].charAt(0);
120 if (i
>= V
.size
.x
) return V
.RESERVE_PIECES
[j
];
121 return this.board
[i
][j
].charAt(1);
124 static get RESERVE_PIECES() {
125 return [V
.NINJA
, V
.DRAGON
, V
.MONK
, V
.HORSE
, V
.LANCE
, V
.PAWN
];
128 getReserveMoves([x
, y
]) {
129 // color == 'w', no drops for black.
130 const p
= V
.RESERVE_PIECES
[y
];
131 if (this.reserve
['w'][p
] == 0) return [];
133 for (let i
of [4, 5, 6, 7]) {
134 for (let j
= 0; j
< V
.size
.y
; j
++) {
135 if (this.board
[i
][j
] == V
.EMPTY
) {
146 start: { x: x
, y: y
},
156 static get MapUnpromoted() {
165 getPotentialMovesFrom([x
, y
]) {
167 // Reserves, outside of board: x == sizeX(+1)
168 if (this.turn
== 'b') return [];
169 return this.getReserveMoves([x
, y
]);
172 const piece
= this.getPiece(x
, y
);
174 if ([V
.ROOK
, V
.KNIGHT
, V
.BISHOP
, V
.QUEEN
].includes(piece
))
175 return super.getPotentialMovesFrom(sq
);
177 case V
.KING: return this.getPotentialKingMoves(sq
);
178 case V
.CAPTAIN: return this.getPotentialCaptainMoves(sq
);
179 case V
.NINJA: return this.getPotentialNinjaMoves(sq
);
180 case V
.DRAGON: return this.getPotentialDragonMoves(sq
);
186 moves
= super.getPotentialPawnMoves(sq
);
189 moves
= this.getPotentialMonkMoves(sq
);
192 moves
= this.getPotentialHorseMoves(sq
);
195 moves
= this.getPotentialLanceMoves(sq
);
198 const promotionZone
= (this.turn
== 'w' ? [0, 1] : [7, 6]);
199 const promotedForm
= V
.MapUnpromoted
[piece
];
201 if (promotionZone
.includes(m
.end
.x
)) m
.appear
[0].p
= promotedForm
;
206 getPotentialKingMoves([x
, y
]) {
207 if (this.getColor(x
, y
) == 'b') return super.getPotentialKingMoves([x
, y
]);
208 // Clan doesn't castle:
209 return super.getSlideNJumpMoves(
211 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
216 getPotentialCaptainMoves(sq
) {
217 const steps
= V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]);
218 return super.getSlideNJumpMoves(sq
, steps
, "oneStep");
221 getPotentialNinjaMoves(sq
) {
223 super.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
])
224 .concat(super.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
], "oneStep"))
228 getPotentialDragonMoves(sq
) {
230 super.getSlideNJumpMoves(sq
, V
.steps
[V
.ROOK
])
231 .concat(super.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
], "oneStep"))
235 getPotentialMonkMoves(sq
) {
236 return super.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
], "oneStep");
239 getPotentialHorseMoves(sq
) {
240 return super.getSlideNJumpMoves(sq
, [ [-2, 1], [-2, -1] ], "oneStep");
243 getPotentialLanceMoves(sq
) {
244 return super.getSlideNJumpMoves(sq
, [ [-1, 0] ]);
247 isAttacked(sq
, color
) {
249 return (super.isAttacked(sq
, 'b') || this.isAttackedByCaptain(sq
, 'b'));
250 // Attacked by white:
252 super.isAttackedByKing(sq
, 'w') ||
253 super.isAttackedByPawn(sq
, 'w') ||
254 this.isAttackedByCaptain(sq
, 'w') ||
255 this.isAttackedByNinja(sq
, 'w') ||
256 this.isAttackedByDragon(sq
, 'w') ||
257 this.isAttackedByMonk(sq
, 'w') ||
258 this.isAttackedByHorse(sq
, 'w') ||
259 this.isAttackedByLance(sq
, 'w') ||
260 super.isAttackedByBishop(sq
, 'w') ||
261 super.isAttackedByKnight(sq
, 'w') ||
262 super.isAttackedByRook(sq
, 'w')
266 isAttackedByCaptain(sq
, color
) {
267 const steps
= V
.steps
[V
.BISHOP
].concat(V
.steps
[V
.ROOK
]);
269 super.isAttackedBySlideNJump(sq
, color
, V
.CAPTAIN
, steps
, "oneStep")
273 isAttackedByNinja(sq
, color
) {
275 super.isAttackedBySlideNJump(sq
, color
, V
.NINJA
, V
.steps
[V
.BISHOP
]) ||
276 super.isAttackedBySlideNJump(
277 sq
, color
, V
.NINJA
, V
.steps
[V
.KNIGHT
], "oneStep")
281 isAttackedByDragon(sq
, color
) {
283 super.isAttackedBySlideNJump(sq
, color
, V
.DRAGON
, V
.steps
[V
.ROOK
]) ||
284 super.isAttackedBySlideNJump(
285 sq
, color
, V
.DRAGON
, V
.steps
[V
.BISHOP
], "oneStep")
289 isAttackedByMonk(sq
, color
) {
291 super.isAttackedBySlideNJump(
292 sq
, color
, V
.MONK
, V
.steps
[V
.BISHOP
], "oneStep")
296 isAttackedByHorse(sq
, color
) {
298 super.isAttackedBySlideNJump(
299 sq
, color
, V
.HORSE
, [ [2, 1], [2, -1] ], "oneStep")
303 isAttackedByLance(sq
, color
) {
304 return super.isAttackedBySlideNJump(sq
, color
, V
.LANCE
, [ [1, 0] ]);
308 let moves
= super.getAllPotentialMoves();
309 if (this.turn
== 'w') {
310 for (let i
= 0; i
< V
.RESERVE_PIECES
.length
; i
++) {
311 moves
= moves
.concat(
312 this.getReserveMoves([V
.size
.x
, i
])
316 return this.filterValid(moves
);
320 if (super.atLeastOneMove()) return true;
321 if (this.turn
== 'w') {
322 // Search one reserve move
323 for (let i
= 0; i
< V
.RESERVE_PIECES
.length
; i
++) {
324 let moves
= this.filterValid(
325 this.getReserveMoves([V
.size
.x
, i
])
327 if (moves
.length
> 0) return true;
333 updateCastleFlags(move, piece
) {
334 // Only black can castle:
336 if (piece
== V
.KING
&& move.appear
[0].c
== 'b')
337 this.castleFlags
['b'] = [8, 8];
339 move.start
.x
== firstRank
&&
340 this.castleFlags
['b'].includes(move.start
.y
)
342 const flagIdx
= (move.start
.y
== this.castleFlags
['b'][0] ? 0 : 1);
343 this.castleFlags
['b'][flagIdx
] = 8;
346 move.end
.x
== firstRank
&&
347 this.castleFlags
['b'].includes(move.end
.y
)
349 const flagIdx
= (move.end
.y
== this.castleFlags
['b'][0] ? 0 : 1);
350 this.castleFlags
['b'][flagIdx
] = 8;
355 super.postPlay(move);
357 if (move.vanish
.length
== 2 && move.appear
.length
== 2) return;
358 const color
= move.appear
[0].c
;
359 if (move.vanish
.length
== 0) this.reserve
[color
][move.appear
[0].p
]--;
363 super.postUndo(move);
364 if (move.vanish
.length
== 2 && move.appear
.length
== 2) return;
365 const color
= this.turn
;
366 if (move.vanish
.length
== 0) this.reserve
[color
][move.appear
[0].p
]++;
369 static get SEARCH_DEPTH() {
374 const color
= this.turn
;
375 const nodrawResult
= (color
== "w" ? "0-1" : "1-0");
376 const oppLastRank
= (color
== 'w' ? 7 : 0);
377 if (this.kingPos
[V
.GetOppCol(color
)][0] == oppLastRank
)
379 if (this.atLeastOneMove()) return "*";
383 static get VALUES() {
400 let evaluation
= super.evalPosition();
402 for (let i
= 0; i
< V
.RESERVE_PIECES
.length
; i
++) {
403 const p
= V
.RESERVE_PIECES
[i
];
404 evaluation
+= this.reserve
["w"][p
] * V
.VALUES
[p
];
410 if (move.vanish
.length
> 0) {
411 let notation
= super.getNotation(move);
412 if (move.vanish
[0].p
!= V
.PAWN
&& move.appear
[0].p
!= move.vanish
[0].p
)
413 notation
+= "=" + move.appear
[0].p
.toUpperCase();
418 move.appear
[0].p
!= V
.PAWN
? move.appear
[0].p
.toUpperCase() : "";
419 return piece
+ "@" + V
.CoordsToSquare(move.end
);