ebc2c7c26f2d289053d6404bab805ebf23c42e33
1 import { ChessRules
, PiPo
} from "@/base_rules";
3 export class SchessRules
extends ChessRules
{
4 static get PawnSpecs() {
10 ChessRules
.PawnSpecs
.promotions
.concat([V
.HAWK
, V
.ELEPHANT
])
19 static get ELEPHANT() {
24 return ChessRules
.PIECES
.concat([V
.HAWK
, V
.ELEPHANT
]);
28 if ([V
.HAWK
, V
.ELEPHANT
].includes(b
[1])) return "Schess/" + b
;
32 // TODO: maybe changes could be done to this method to show "empty"
33 // instead of a piece to not use a pocket piece...
36 static IsGoodFen(fen
) {
37 if (!ChessRules
.IsGoodFen(fen
)) return false;
38 const fenParsed
= V
.ParseFen(fen
);
40 if (!fenParsed
.pocket
|| !fenParsed
.pocket
.match(/^[0-1]{4,4}$/))
45 static IsGoodFlags(flags
) {
46 // 4 for castle + 16 for generators
47 return !!flags
.match(/^[a-z]{4,4}[01]{16,16}$/);
51 super.setFlags(fenflags
); //castleFlags
53 w: [...Array(8)], //pawns can move 2 squares?
56 const flags
= fenflags
.substr(4); //skip first 4 letters, for castle
57 for (let c
of ["w", "b"]) {
58 for (let i
= 0; i
< 8; i
++)
59 this.pieceFlags
[c
][i
] = flags
.charAt((c
== "w" ? 0 : 8) + i
) == "1";
64 return [this.castleFlags
, this.pieceFlags
];
67 disaggregateFlags(flags
) {
68 this.castleFlags
= flags
[0];
69 this.pieceFlags
= flags
[1];
72 static ParseFen(fen
) {
73 const fenParts
= fen
.split(" ");
75 ChessRules
.ParseFen(fen
),
76 { pocket: fenParts
[5] }
80 static GenRandInitFen(randomness
) {
82 ChessRules
.GenRandInitFen(randomness
).slice(0, -2) +
83 // Add pieceFlags + pocket
84 "1111111111111111 - 1111"
90 super.getFen() + " " +
97 super.getFenForRepeat() + "_" +
103 let fen
= super.getFlagsFen();
105 for (let c
of ["w", "b"])
106 for (let i
= 0; i
< 8; i
++) fen
+= (this.pieceFlags
[c
][i
] ? "1" : "0");
112 for (let c
of ["w", "b"])
113 res
+= this.pocket
[c
][V
.HAWK
] + this.pocket
[c
][V
.ELEPHANT
];
117 setOtherVariables(fen
) {
118 super.setOtherVariables(fen
);
119 const fenParsed
= V
.ParseFen(fen
);
122 h: parseInt(fenParsed
.pocket
[0]),
123 e: parseInt(fenParsed
.pocket
[1])
126 h: parseInt(fenParsed
.pocket
[2]),
127 e: parseInt(fenParsed
.pocket
[3])
132 getPotentialMovesFrom([x
, y
]) {
133 let moves
= undefined;
134 switch (this.getPiece(x
, y
)) {
136 moves
= this.getPotentialHawkMoves([x
, y
]);
139 moves
= this.getPotentialElephantMoves([x
, y
]);
142 moves
= super.getPotentialMovesFrom([x
, y
]);
144 // Post-processing: add choices for hawk and elephant,
145 // except for moves letting the king under check.
146 const color
= this.turn
;
147 if (Object
.values(this.pocket
[color
]).some(v
=> v
> 0)) {
148 const firstRank
= (color
== "w" ? 7 : 0);
149 let pocketMoves
= [];
151 let inCheckAfter
= false;
153 if (this.underCheck(color
)) inCheckAfter
= true;
156 for (let pp
of ['h', 'e']) {
157 if (this.pocket
[color
][pp
] > 0) {
159 m
.start
.x
== firstRank
&&
160 this.pieceFlags
[color
][m
.start
.y
] &&
162 m
.appear
.length
== 1 ||
163 // Special castle case: is initial king square free?
164 ![m
.appear
[0].y
, m
.appear
[1].y
].includes(m
.vanish
[0].y
)
167 let pMove
= JSON
.parse(JSON
.stringify(m
));
168 // NOTE: unshift instead of push, for choices presentation
169 pMove
.appear
.unshift(new PiPo({
175 pocketMoves
.push(pMove
);
178 m
.appear
.length
== 2 &&
179 ![m
.appear
[0].y
, m
.appear
[1].y
].includes(m
.vanish
[1].y
)
181 // Special castle case: rook flag was necessarily on
182 let pMove
= JSON
.parse(JSON
.stringify(m
));
183 pMove
.appear
.unshift(new PiPo({
189 pocketMoves
.push(pMove
);
195 // NOTE: the order matter, for presentation on screen
196 moves
= moves
.concat(pocketMoves
);
201 getPotentialHawkMoves(sq
) {
202 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
]).concat(
203 this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
], "oneStep")
207 getPotentialElephantMoves(sq
) {
208 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.ROOK
]).concat(
209 this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
], "oneStep")
213 isAttacked(sq
, color
) {
215 super.isAttacked(sq
, color
) ||
216 this.isAttackedByHawk(sq
, color
) ||
217 this.isAttackedByElephant(sq
, color
)
221 isAttackedByHawk(sq
, color
) {
223 this.isAttackedBySlideNJump(sq
, color
, V
.HAWK
, V
.steps
[V
.BISHOP
]) ||
224 this.isAttackedBySlideNJump(
234 isAttackedByElephant(sq
, color
) {
236 this.isAttackedBySlideNJump(sq
, color
, V
.ELEPHANT
, V
.steps
[V
.ROOK
]) ||
237 this.isAttackedBySlideNJump(
249 if (move.appear
.length
>= 2) {
250 if ([V
.HAWK
, V
.ELEPHANT
].includes(move.appear
[0].p
)) {
251 // A pocket piece is used
252 const color
= this.turn
;
253 this.pocket
[color
][move.appear
[0].p
] = 0;
259 super.postPlay(move);
260 const color
= move.vanish
[0].c
;
261 const oppCol
= V
.GetOppCol(color
);
262 const firstRank
= (color
== 'w' ? 7 : 0);
263 const oppFirstRank
= 7 - firstRank
;
264 // Does this move turn off a piece init square flag?
265 if (move.start
.x
== firstRank
) {
266 if (this.pieceFlags
[color
][move.start
.y
])
267 this.pieceFlags
[color
][move.start
.y
] = false;
268 // Special castle case:
269 if (move.appear
.length
>= 2) {
270 const L
= move.appear
.length
;
271 if (move.appear
[L
-1].p
== V
.ROOK
)
272 this.pieceFlags
[color
][move.vanish
[1].y
] = false;
275 if (move.end
.x
== oppFirstRank
&& this.pieceFlags
[oppCol
][move.end
.y
])
276 this.pieceFlags
[oppCol
][move.end
.y
] = false;
280 super.postUndo(move);
281 if (move.appear
.length
>= 2) {
282 if ([V
.HAWK
, V
.ELEPHANT
].includes(move.appear
[0].p
)) {
283 // A pocket piece was used
284 const color
= this.turn
;
285 this.pocket
[color
][move.appear
[0].p
] = 1;
290 static get SEARCH_DEPTH() {
294 static get VALUES() {
295 return Object
.assign(
304 move.appear
.length
>= 2 &&
305 [V
.HAWK
, V
.ELEPHANT
].includes(move.appear
[0].p
)
307 const suffix
= "/" + move.appear
[0].p
.toUpperCase();
308 let cmove
= JSON
.parse(JSON
.stringify(move));
309 cmove
.appear
.shift();
310 return super.getNotation(cmove
) + suffix
;
312 return super.getNotation(move);