f24e5b2f995f74855bb2471074dcd7a1fdbfade9
1 import { ChessRules
, PiPo
} from "@/base_rules";
3 export class KoopaRules
extends ChessRules
{
4 static get HasEnpassant() {
9 return ['s', 'u', 'o', 'c', 't', 'l'];
13 return ChessRules
.PIECES
.concat(V
.STUNNED
);
16 static ParseFen(fen
) {
17 let res
= ChessRules
.ParseFen(fen
);
18 const fenParts
= fen
.split(" ");
19 res
.stunned
= fenParts
[4];
23 static IsGoodFen(fen
) {
24 if (!ChessRules
.IsGoodFen(fen
)) return false;
25 const fenParsed
= V
.ParseFen(fen
);
30 fenParsed
.stunned
!= "-" &&
31 !fenParsed
.stunned
.match(/^([a-h][1-8][1-4],?)*$/)
40 return (V
.STUNNED
.includes(b
[1]) ? "Koopa/" : "") + b
;
44 return super.getFen() + " " + this.getStunnedFen();
48 return super.getFenForRepeat() + "_" + this.getStunnedFen();
53 Object
.keys(this.stunned
)
54 .map(square
=> square
+ this.stunned
[square
])
59 // Base GenRandInitFen() is fine because en-passant indicator will
60 // stand for stunned indicator.
63 this.INIT_COL_KING
= { w: -1, b: -1 };
64 // Squares of white and black king:
65 this.kingPos
= { w: [-1, -1], b: [-1, -1] };
66 const fenRows
= V
.ParseFen(fen
).position
.split("/");
67 const startRow
= { 'w': V
.size
.x
- 1, 'b': 0 };
68 for (let i
= 0; i
< fenRows
.length
; i
++) {
69 let k
= 0; //column index on board
70 for (let j
= 0; j
< fenRows
[i
].length
; j
++) {
71 switch (fenRows
[i
].charAt(j
)) {
74 this.kingPos
["b"] = [i
, k
];
75 this.INIT_COL_KING
["b"] = k
;
79 this.kingPos
["w"] = [i
, k
];
80 this.INIT_COL_KING
["w"] = k
;
83 const num
= parseInt(fenRows
[i
].charAt(j
));
84 if (!isNaN(num
)) k
+= num
- 1;
92 setOtherVariables(fen
) {
93 super.setOtherVariables(fen
);
94 let stunnedArray
= [];
95 const stunnedFen
= V
.ParseFen(fen
).stunned
;
96 if (stunnedFen
!= "-") {
102 square: s
.substr(0, 2),
103 state: parseInt(s
[2])
108 stunnedArray
.forEach(s
=> {
109 this.stunned
[s
.square
] = s
.state
;
113 getNormalizedStep(step
) {
114 const [deltaX
, deltaY
] = [Math
.abs(step
[0]), Math
.abs(step
[1])];
115 if (deltaX
== 0 || deltaY
== 0 || deltaX
== deltaY
)
116 return [step
[0] / deltaX
|| 0, step
[1] / deltaY
|| 0];
118 const divisor
= Math
.min(deltaX
, deltaY
)
119 return [step
[0] / divisor
, step
[1] / divisor
];
122 getPotentialMovesFrom([x
, y
]) {
123 let moves
= super.getPotentialMovesFrom([x
, y
]);
124 // Complete moves: stuns & kicks
125 let promoteAfterStun
= [];
126 const color
= this.turn
;
128 if (m
.vanish
.length
== 2 && m
.appear
.length
== 1) {
130 this.getNormalizedStep([m
.end
.x
- m
.start
.x
, m
.end
.y
- m
.start
.y
]);
131 // "Capture" something: is target stunned?
132 if (V
.STUNNED
.includes(m
.vanish
[1].p
)) {
133 // Kick it: continue movement in the same direction,
134 // destroying all on its path.
135 let [i
, j
] = [m
.end
.x
+ step
[0], m
.end
.y
+ step
[1]];
136 while (V
.OnBoard(i
, j
)) {
137 if (this.board
[i
][j
] != V
.EMPTY
) {
142 c: this.getColor(i
, j
),
143 p: this.getPiece(i
, j
)
152 // The piece is now stunned
153 m
.appear
.push(JSON
.parse(JSON
.stringify(m
.vanish
[1])));
154 const pIdx
= ChessRules
.PIECES
.findIndex(p
=> p
== m
.appear
[1].p
);
155 m
.appear
[1].p
= V
.STUNNED
[pIdx
];
156 // And the capturer continue in the same direction until an empty
157 // square or the edge of the board, maybe stunning other pieces.
158 let [i
, j
] = [m
.end
.x
+ step
[0], m
.end
.y
+ step
[1]];
159 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] != V
.EMPTY
) {
160 const colIJ
= this.getColor(i
, j
);
161 const pieceIJ
= this.getPiece(i
, j
);
162 let pIdx
= ChessRules
.PIECES
.findIndex(p
=> p
== pieceIJ
);
164 // The piece isn't already stunned
185 if (V
.OnBoard(i
, j
)) {
188 // Is it a pawn on last rank?
189 if ((color
== 'w' && i
== 0) || (color
== 'b' && i
== 7)) {
190 m
.appear
[0].p
= V
.ROOK
;
191 for (let ppiece
of [V
.KNIGHT
, V
.BISHOP
, V
.QUEEN
]) {
192 let mp
= JSON
.parse(JSON
.stringify(m
));
193 mp
.appear
[0].p
= ppiece
;
194 promoteAfterStun
.push(mp
);
204 return moves
.concat(promoteAfterStun
);
208 // Forbid kicking own king out
209 const color
= this.turn
;
210 return moves
.filter(m
=> {
211 const kingAppear
= m
.appear
.some(a
=> a
.c
== color
&& a
.p
== V
.KING
);
212 return m
.vanish
.every(v
=> {
215 !["k", "l"].includes(v
.p
) ||
216 (v
.p
== "k" && kingAppear
)
227 if (this.kingPos
['w'][0] < 0) return "0-1";
228 if (this.kingPos
['b'][0] < 0) return "1-0";
229 if (!this.atLeastOneMove()) return "1/2";
234 // Base method is fine because a stunned king (which won't be detected)
235 // can still castle after going back to normal.
236 super.postPlay(move);
237 const kIdx
= move.vanish
.findIndex(v
=> v
.p
== "l");
239 // A stunned king vanish (game over)
240 this.kingPos
[move.vanish
[kIdx
].c
] = [-1, -1];
241 move.stunned
= JSON
.stringify(this.stunned
);
242 // Array of stunned stage 1 pieces (just back to normal then)
243 Object
.keys(this.stunned
).forEach(square
=> {
244 // All (formerly) stunned pieces progress by 1 level, if still on board
245 const coords
= V
.SquareToCoords(square
);
246 const [x
, y
] = [coords
.x
, coords
.y
];
247 if (V
.STUNNED
.includes(this.board
[x
][y
][1])) {
248 // Stunned piece still on board
249 this.stunned
[square
]--;
250 if (this.stunned
[square
] == 0) {
251 delete this.stunned
[square
];
252 const color
= this.getColor(x
, y
);
253 const piece
= this.getPiece(x
, y
);
254 const pIdx
= V
.STUNNED
.findIndex(p
=> p
== piece
);
255 this.board
[x
][y
] = color
+ ChessRules
.PIECES
[pIdx
];
258 else delete this.stunned
[square
];
260 // Any new stunned pieces?
261 move.appear
.forEach(a
=> {
262 if (V
.STUNNED
.includes(a
.p
))
263 // Set to maximum stun level:
264 this.stunned
[V
.CoordsToSquare({ x: a
.x
, y: a
.y
})] = 4;
269 super.postUndo(move);
270 const kIdx
= move.vanish
.findIndex(v
=> v
.p
== "l");
272 // A stunned king vanished
273 this.kingPos
[move.vanish
[kIdx
].c
] =
274 [move.vanish
[kIdx
].x
, move.vanish
[kIdx
].y
];
276 this.stunned
= JSON
.parse(move.stunned
);
277 for (let i
=0; i
<8; i
++) {
278 for (let j
=0; j
<8; j
++) {
279 const square
= V
.CoordsToSquare({ x: i
, y: j
});
280 const pieceIJ
= this.getPiece(i
, j
);
281 if (!this.stunned
[square
]) {
282 const pIdx
= V
.STUNNED
.findIndex(p
=> p
== pieceIJ
);
284 this.board
[i
][j
] = this.getColor(i
, j
) + ChessRules
.PIECES
[pIdx
];
287 const pIdx
= ChessRules
.PIECES
.findIndex(p
=> p
== pieceIJ
);
289 this.board
[i
][j
] = this.getColor(i
, j
) + V
.STUNNED
[pIdx
];
295 static get VALUES() {
296 return Object
.assign(
309 static get SEARCH_DEPTH() {
315 move.appear
.length
== 2 &&
316 move.vanish
.length
== 2 &&
317 move.appear
.concat(move.vanish
).every(
318 av
=> ChessRules
.PIECES
.includes(av
.p
)) &&
319 move.appear
[0].p
== V
.KING
321 if (move.end
.y
< move.start
.y
) return "0-0-0";
324 const finalSquare
= V
.CoordsToSquare(move.end
);
325 const piece
= this.getPiece(move.start
.x
, move.start
.y
);
326 const captureMark
= move.vanish
.length
>= 2 ? "x" : "";
328 if (piece
== 'p' && captureMark
.length
== 1)
329 pawnMark
= V
.CoordToColumn(move.start
.y
); //start column
330 // Piece or pawn movement
332 (piece
== V
.PAWN
? pawnMark : piece
.toUpperCase()) +
333 captureMark
+ finalSquare
;
336 move.appear
[0].c
== move.vanish
[0].c
&&
337 move.appear
[0].p
!= 'p'
340 notation
+= "=" + move.appear
[0].p
.toUpperCase();