1 import { ChessRules
} from "@/base_rules";
2 import { randInt
} from "@/utils/alea";
4 export class MonsterRules
extends ChessRules
{
5 static IsGoodFlags(flags
) {
6 // Only black can castle
7 return !!flags
.match(/^[a-z]{2,2}$/);
10 static GenRandInitFen(randomness
) {
11 if (randomness
== 2) randomness
--;
12 const fen
= ChessRules
.GenRandInitFen(randomness
);
14 // 26 first chars are 6 rows + 6 slashes
16 // En passant available, and "half-castle"
17 .concat("2PPPP2/4K3 w 0 ")
18 .concat(fen
.substr(-6, 2))
24 return this.castleFlags
['b'].map(V
.CoordToColumn
).join("");
28 this.castleFlags
= { 'b': [-1, -1] };
29 for (let i
= 0; i
< 2; i
++)
30 this.castleFlags
['b'][i
] = V
.ColumnToCoord(fenflags
.charAt(i
));
33 setOtherVariables(fen
) {
34 super.setOtherVariables(fen
);
38 getPotentialKingMoves([x
, y
]) {
39 if (this.getColor(x
, y
) == 'b') return super.getPotentialKingMoves([x
, y
]);
40 // White doesn't castle:
41 return this.getSlideNJumpMoves(
43 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
48 isAttacked(sq
, color
, castling
) {
49 const singleMoveAttack
= super.isAttacked(sq
, color
);
50 if (singleMoveAttack
) return true;
51 if (color
== 'b' || !!castling
) return singleMoveAttack
;
52 // Attacks by white: double-move allowed
53 const curTurn
= this.turn
;
55 const w1Moves
= super.getAllPotentialMoves();
57 for (let move of w1Moves
) {
59 const res
= super.isAttacked(sq
, 'w');
67 move.flags
= JSON
.stringify(this.aggregateFlags());
68 if (this.turn
== 'b' || this.subTurn
== 2)
69 this.epSquares
.push(this.getEpSquare(move));
70 else this.epSquares
.push(null);
71 V
.PlayOnBoard(this.board
, move);
72 if (this.turn
== 'w') {
73 if (this.subTurn
== 1) this.movesCount
++;
75 this.subTurn
= 3 - this.subTurn
;
83 updateCastleFlags(move, piece
) {
84 // Only black can castle:
86 if (piece
== V
.KING
&& move.appear
[0].c
== 'b')
87 this.castleFlags
['b'] = [8, 8];
89 move.start
.x
== firstRank
&&
90 this.castleFlags
['b'].includes(move.start
.y
)
92 const flagIdx
= (move.start
.y
== this.castleFlags
['b'][0] ? 0 : 1);
93 this.castleFlags
['b'][flagIdx
] = 8;
96 move.end
.x
== firstRank
&&
97 this.castleFlags
['b'].includes(move.end
.y
)
99 const flagIdx
= (move.end
.y
== this.castleFlags
['b'][0] ? 0 : 1);
100 this.castleFlags
['b'][flagIdx
] = 8;
105 // Definition of 'c' in base class doesn't work:
106 const c
= move.vanish
[0].c
;
107 const piece
= move.vanish
[0].p
;
108 if (piece
== V
.KING
) {
109 this.kingPos
[c
][0] = move.appear
[0].x
;
110 this.kingPos
[c
][1] = move.appear
[0].y
;
112 this.updateCastleFlags(move, piece
);
116 this.epSquares
.pop();
117 this.disaggregateFlags(JSON
.parse(move.flags
));
118 V
.UndoOnBoard(this.board
, move);
119 if (this.turn
== 'w') {
120 if (this.subTurn
== 2) this.subTurn
= 1;
121 else this.turn
= 'b';
131 if (this.turn
== 'w' && this.subTurn
== 1) {
132 return moves
.filter(m1
=> {
134 // NOTE: no recursion because next call will see subTurn == 2
135 const res
= super.atLeastOneMove();
140 return super.filterValid(moves
);
143 static get SEARCH_DEPTH() {
148 const color
= this.turn
;
150 // Generate all sequences of 2-moves
151 const moves1
= this.getAllValidMoves();
152 moves1
.forEach(m1
=> {
153 m1
.eval
= -V
.INFINITY
;
156 const moves2
= this.getAllValidMoves();
157 moves2
.forEach(m2
=> {
159 const eval2
= this.evalPosition();
161 if (eval2
> m1
.eval
) {
168 moves1
.sort((a
, b
) => b
.eval
- a
.eval
);
169 let candidates
= [0];
172 i
< moves1
.length
&& moves1
[i
].eval
== moves1
[0].eval
;
177 const idx
= candidates
[randInt(candidates
.length
)];
178 const move2
= moves1
[idx
].move2
;
179 delete moves1
[idx
]["move2"];
180 return [moves1
[idx
], move2
];
182 // For black at depth 1, super method is fine:
183 return super.getComputerMove();