1 import { ChessRules
} from "@/base_rules";
2 import { randInt
} from "@/utils/alea";
4 export class MonsterRules
extends ChessRules
{
17 label: "Number of pawns",
18 variable: "pawnsCount",
21 { label: "Two", value: 2 },
22 { label: "Four", value: 4 },
23 { label: "Six", value: 6 }
30 static AbbreviateOptions(opts
) {
31 return opts
["pawnsCount"];
34 static IsGoodFlags(flags
) {
35 // Only black can castle
36 return !!flags
.match(/^[a-z]{2,2}$/);
39 static GenRandInitFen(options
) {
40 const baseFen
= ChessRules
.GenRandInitFen(
41 { randomness: (options
.random
? 1 : 0) });
43 switch (options
.pawnsCount
) {
44 case 2: pawnsLine
= "3PP3"; break;
45 case 4: pawnsLine
= "2PPPP2"; break;
46 case 6: pawnsLine
= "1PPPPPP1"; break;
49 // 26 first chars are 6 rows + 6 slashes
50 baseFen
.substr(0, 26) + pawnsLine
+ "/4K3 w 0 " +
51 // En passant available, and "half-castle"
52 baseFen
.substr(-6, 2) + " -"
57 return this.castleFlags
['b'].map(V
.CoordToColumn
).join("");
61 this.castleFlags
= { 'b': [-1, -1] };
62 for (let i
= 0; i
< 2; i
++)
63 this.castleFlags
['b'][i
] = V
.ColumnToCoord(fenflags
.charAt(i
));
66 setOtherVariables(fen
) {
67 super.setOtherVariables(fen
);
71 getPotentialKingMoves([x
, y
]) {
72 if (this.getColor(x
, y
) == 'b') return super.getPotentialKingMoves([x
, y
]);
73 // White doesn't castle:
74 return this.getSlideNJumpMoves(
75 [x
, y
], V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]), 1);
79 // Goal is king capture => no checks
92 const color
= this.turn
;
93 if (this.kingPos
[color
][0] < 0) return (color
== 'w' ? "0-1" : "1-0");
98 move.flags
= JSON
.stringify(this.aggregateFlags());
99 if (this.turn
== 'b' || this.subTurn
== 2)
100 this.epSquares
.push(this.getEpSquare(move));
101 else this.epSquares
.push(null);
102 V
.PlayOnBoard(this.board
, move);
103 if (this.turn
== 'w') {
104 if (this.subTurn
== 1) this.movesCount
++;
108 (move.vanish
.length
== 2 && move.vanish
[1].p
== V
.KING
)
113 else this.subTurn
= 2;
122 updateCastleFlags(move, piece
) {
123 // Only black can castle:
125 if (piece
== V
.KING
&& move.appear
[0].c
== 'b')
126 this.castleFlags
['b'] = [8, 8];
128 move.start
.x
== firstRank
&&
129 this.castleFlags
['b'].includes(move.start
.y
)
131 const flagIdx
= (move.start
.y
== this.castleFlags
['b'][0] ? 0 : 1);
132 this.castleFlags
['b'][flagIdx
] = 8;
135 move.end
.x
== firstRank
&&
136 this.castleFlags
['b'].includes(move.end
.y
)
138 const flagIdx
= (move.end
.y
== this.castleFlags
['b'][0] ? 0 : 1);
139 this.castleFlags
['b'][flagIdx
] = 8;
144 // Definition of 'c' in base class doesn't work:
145 const c
= move.vanish
[0].c
;
146 const piece
= move.vanish
[0].p
;
148 this.kingPos
[c
] = [move.appear
[0].x
, move.appear
[0].y
];
149 if (move.vanish
.length
== 2 && move.vanish
[1].p
== V
.KING
) {
150 // Opponent's king is captured, game over
151 this.kingPos
[move.vanish
[1].c
] = [-1, -1];
152 move.captureKing
= true; //for undo
154 this.updateCastleFlags(move, piece
);
158 this.epSquares
.pop();
159 this.disaggregateFlags(JSON
.parse(move.flags
));
160 V
.UndoOnBoard(this.board
, move);
161 if (this.turn
== 'w') {
162 if (this.subTurn
== 2) this.subTurn
= 1;
163 else this.turn
= 'b';
168 this.subTurn
= (!move.captureKing
? 2 : 1);
174 if (move.vanish
.length
== 2 && move.vanish
[1].p
== V
.KING
)
175 // Opponent's king was captured
176 this.kingPos
[move.vanish
[1].c
] = [move.vanish
[1].x
, move.vanish
[1].y
];
177 super.postUndo(move);
180 // Custom search at depth 1(+1)
182 const getBestWhiteMove
= (terminal
) => {
183 // Generate all sequences of 2-moves
184 let moves1
= this.getAllValidMoves();
185 moves1
.forEach(m1
=> {
186 m1
.eval
= -V
.INFINITY
;
189 if (!!terminal
) m1
.eval
= this.evalPosition();
191 const moves2
= this.getAllValidMoves();
192 moves2
.forEach(m2
=> {
194 const eval2
= this.evalPosition() + 0.05 - Math
.random() / 10;
196 if (eval2
> m1
.eval
) {
204 moves1
.sort((a
, b
) => b
.eval
- a
.eval
);
206 // The move itself doesn't matter, only its eval:
208 let candidates
= [0];
211 i
< moves1
.length
&& moves1
[i
].eval
== moves1
[0].eval
;
216 const idx
= candidates
[randInt(candidates
.length
)];
217 const move2
= moves1
[idx
].move2
;
218 delete moves1
[idx
]["move2"];
219 return [moves1
[idx
], move2
];
222 const getBestBlackMove
= () => {
223 let moves
= this.getAllValidMoves();
227 const evalM
= getBestWhiteMove("terminal").eval
229 if (evalM
< m
.eval
) m
.eval
= evalM
;
231 moves
.sort((a
, b
) => a
.eval
- b
.eval
);
232 let candidates
= [0];
235 i
< moves
.length
&& moves
[i
].eval
== moves
[0].eval
;
240 const idx
= candidates
[randInt(candidates
.length
)];
244 const color
= this.turn
;
245 return (color
== 'w' ? getBestWhiteMove() : getBestBlackMove());