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("1PPPPPP1/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
]),
49 // Goal is king capture => no checks
62 const color
= this.turn
;
63 if (this.kingPos
[color
][0] < 0) return (color
== 'w' ? "0-1" : "1-0");
68 move.flags
= JSON
.stringify(this.aggregateFlags());
69 if (this.turn
== 'b' || this.subTurn
== 2)
70 this.epSquares
.push(this.getEpSquare(move));
71 else this.epSquares
.push(null);
72 V
.PlayOnBoard(this.board
, move);
73 if (this.turn
== 'w') {
74 if (this.subTurn
== 1) this.movesCount
++;
78 (move.vanish
.length
== 2 && move.vanish
[1].p
== V
.KING
)
83 else this.subTurn
= 2;
92 updateCastleFlags(move, piece
) {
93 // Only black can castle:
95 if (piece
== V
.KING
&& move.appear
[0].c
== 'b')
96 this.castleFlags
['b'] = [8, 8];
98 move.start
.x
== firstRank
&&
99 this.castleFlags
['b'].includes(move.start
.y
)
101 const flagIdx
= (move.start
.y
== this.castleFlags
['b'][0] ? 0 : 1);
102 this.castleFlags
['b'][flagIdx
] = 8;
105 move.end
.x
== firstRank
&&
106 this.castleFlags
['b'].includes(move.end
.y
)
108 const flagIdx
= (move.end
.y
== this.castleFlags
['b'][0] ? 0 : 1);
109 this.castleFlags
['b'][flagIdx
] = 8;
114 // Definition of 'c' in base class doesn't work:
115 const c
= move.vanish
[0].c
;
116 const piece
= move.vanish
[0].p
;
118 this.kingPos
[c
] = [move.appear
[0].x
, move.appear
[0].y
];
119 if (move.vanish
.length
== 2 && move.vanish
[1].p
== V
.KING
) {
120 // Opponent's king is captured, game over
121 this.kingPos
[move.vanish
[1].c
] = [-1, -1];
122 move.captureKing
= true; //for undo
124 this.updateCastleFlags(move, piece
);
128 this.epSquares
.pop();
129 this.disaggregateFlags(JSON
.parse(move.flags
));
130 V
.UndoOnBoard(this.board
, move);
131 if (this.turn
== 'w') {
132 if (this.subTurn
== 2) this.subTurn
= 1;
133 else this.turn
= 'b';
138 this.subTurn
= (!move.captureKing
? 2 : 1);
144 if (move.vanish
.length
== 2 && move.vanish
[1].p
== V
.KING
)
145 // Opponent's king was captured
146 this.kingPos
[move.vanish
[1].c
] = [move.vanish
[1].x
, move.vanish
[1].y
];
147 super.postUndo(move);
150 // Custom search at depth 1(+1)
152 const getBestWhiteMove
= (terminal
) => {
153 // Generate all sequences of 2-moves
154 let moves1
= this.getAllValidMoves();
155 moves1
.forEach(m1
=> {
156 m1
.eval
= -V
.INFINITY
;
159 if (!!terminal
) m1
.eval
= this.evalPosition();
161 const moves2
= this.getAllValidMoves();
162 moves2
.forEach(m2
=> {
164 const eval2
= this.evalPosition() + 0.05 - Math
.random() / 10;
166 if (eval2
> m1
.eval
) {
174 moves1
.sort((a
, b
) => b
.eval
- a
.eval
);
176 // The move itself doesn't matter, only its eval:
178 let candidates
= [0];
181 i
< moves1
.length
&& moves1
[i
].eval
== moves1
[0].eval
;
186 const idx
= candidates
[randInt(candidates
.length
)];
187 const move2
= moves1
[idx
].move2
;
188 delete moves1
[idx
]["move2"];
189 return [moves1
[idx
], move2
];
192 const getBestBlackMove
= () => {
193 let moves
= this.getAllValidMoves();
197 const evalM
= getBestWhiteMove("terminal").eval
199 if (evalM
< m
.eval
) m
.eval
= evalM
;
201 moves
.sort((a
, b
) => a
.eval
- b
.eval
);
202 let candidates
= [0];
205 i
< moves
.length
&& moves
[i
].eval
== moves
[0].eval
;
210 const idx
= candidates
[randInt(candidates
.length
)];
214 const color
= this.turn
;
215 return (color
== 'w' ? getBestWhiteMove() : getBestBlackMove());