1 import { ChessRules
} from "@/base_rules";
2 import { randInt
} from "@/utils/alea";
4 export class MonsterRules
extends ChessRules
{
6 static IsGoodFlags(flags
) {
7 // Only black can castle
8 return !!flags
.match(/^[a-z]{2,2}$/);
11 static GenRandInitFen(randomness
) {
12 if (randomness
== 2) randomness
--;
13 const fen
= ChessRules
.GenRandInitFen(randomness
);
15 // 26 first chars are 6 rows + 6 slashes
17 // En passant available, and "half-castle"
18 .concat("1PPPPPP1/4K3 w 0 ")
19 .concat(fen
.substr(-6, 2))
25 return this.castleFlags
['b'].map(V
.CoordToColumn
).join("");
29 this.castleFlags
= { 'b': [-1, -1] };
30 for (let i
= 0; i
< 2; i
++)
31 this.castleFlags
['b'][i
] = V
.ColumnToCoord(fenflags
.charAt(i
));
34 setOtherVariables(fen
) {
35 super.setOtherVariables(fen
);
39 getPotentialKingMoves([x
, y
]) {
40 if (this.getColor(x
, y
) == 'b') return super.getPotentialKingMoves([x
, y
]);
41 // White doesn't castle:
42 return this.getSlideNJumpMoves(
44 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
50 // Goal is king capture => no checks
63 const color
= this.turn
;
64 if (this.kingPos
[color
][0] < 0) return (color
== 'w' ? "0-1" : "1-0");
69 move.flags
= JSON
.stringify(this.aggregateFlags());
70 if (this.turn
== 'b' || this.subTurn
== 2)
71 this.epSquares
.push(this.getEpSquare(move));
72 else this.epSquares
.push(null);
73 V
.PlayOnBoard(this.board
, move);
74 if (this.turn
== 'w') {
75 if (this.subTurn
== 1) this.movesCount
++;
79 (move.vanish
.length
== 2 && move.vanish
[1].p
== V
.KING
)
84 else this.subTurn
= 2;
93 updateCastleFlags(move, piece
) {
94 // Only black can castle:
96 if (piece
== V
.KING
&& move.appear
[0].c
== 'b')
97 this.castleFlags
['b'] = [8, 8];
99 move.start
.x
== firstRank
&&
100 this.castleFlags
['b'].includes(move.start
.y
)
102 const flagIdx
= (move.start
.y
== this.castleFlags
['b'][0] ? 0 : 1);
103 this.castleFlags
['b'][flagIdx
] = 8;
106 move.end
.x
== firstRank
&&
107 this.castleFlags
['b'].includes(move.end
.y
)
109 const flagIdx
= (move.end
.y
== this.castleFlags
['b'][0] ? 0 : 1);
110 this.castleFlags
['b'][flagIdx
] = 8;
115 // Definition of 'c' in base class doesn't work:
116 const c
= move.vanish
[0].c
;
117 const piece
= move.vanish
[0].p
;
119 this.kingPos
[c
] = [move.appear
[0].x
, move.appear
[0].y
];
120 if (move.vanish
.length
== 2 && move.vanish
[1].p
== V
.KING
) {
121 // Opponent's king is captured, game over
122 this.kingPos
[move.vanish
[1].c
] = [-1, -1];
123 move.captureKing
= true; //for undo
125 this.updateCastleFlags(move, piece
);
129 this.epSquares
.pop();
130 this.disaggregateFlags(JSON
.parse(move.flags
));
131 V
.UndoOnBoard(this.board
, move);
132 if (this.turn
== 'w') {
133 if (this.subTurn
== 2) this.subTurn
= 1;
134 else this.turn
= 'b';
139 this.subTurn
= (!move.captureKing
? 2 : 1);
145 if (move.vanish
.length
== 2 && move.vanish
[1].p
== V
.KING
)
146 // Opponent's king was captured
147 this.kingPos
[move.vanish
[1].c
] = [move.vanish
[1].x
, move.vanish
[1].y
];
148 super.postUndo(move);
151 // Custom search at depth 1(+1)
153 const getBestWhiteMove
= (terminal
) => {
154 // Generate all sequences of 2-moves
155 let moves1
= this.getAllValidMoves();
156 moves1
.forEach(m1
=> {
157 m1
.eval
= -V
.INFINITY
;
160 if (!!terminal
) m1
.eval
= this.evalPosition();
162 const moves2
= this.getAllValidMoves();
163 moves2
.forEach(m2
=> {
165 const eval2
= this.evalPosition() + 0.05 - Math
.random() / 10;
167 if (eval2
> m1
.eval
) {
175 moves1
.sort((a
, b
) => b
.eval
- a
.eval
);
177 // The move itself doesn't matter, only its eval:
179 let candidates
= [0];
182 i
< moves1
.length
&& moves1
[i
].eval
== moves1
[0].eval
;
187 const idx
= candidates
[randInt(candidates
.length
)];
188 const move2
= moves1
[idx
].move2
;
189 delete moves1
[idx
]["move2"];
190 return [moves1
[idx
], move2
];
193 const getBestBlackMove
= () => {
194 let moves
= this.getAllValidMoves();
198 const evalM
= getBestWhiteMove("terminal").eval
200 if (evalM
< m
.eval
) m
.eval
= evalM
;
202 moves
.sort((a
, b
) => a
.eval
- b
.eval
);
203 let candidates
= [0];
206 i
< moves
.length
&& moves
[i
].eval
== moves
[0].eval
;
211 const idx
= candidates
[randInt(candidates
.length
)];
215 const color
= this.turn
;
216 return (color
== 'w' ? getBestWhiteMove() : getBestBlackMove());