1 import { ChessRules
} from "@/base_rules";
2 import { randInt
} from "@/utils/alea";
4 export class TakenmakeRules
extends ChessRules
{
5 setOtherVariables(fen
) {
6 super.setOtherVariables(fen
);
7 // Stack of "last move" only for intermediate captures
8 this.lastMoveEnd
= [null];
11 getPotentialMovesFrom([x
, y
], asA
) {
12 const L
= this.lastMoveEnd
.length
;
13 if (!asA
&& !!this.lastMoveEnd
[L
-1]) {
14 asA
= this.lastMoveEnd
[L
-1].p
;
15 if (x
!= this.lastMoveEnd
[L
-1].x
|| y
!= this.lastMoveEnd
[L
-1].y
) {
16 // A capture was played: wrong square
21 const piece
= this.getPiece(x
, y
);
22 switch (asA
|| piece
) {
24 if (!asA
|| piece
== V
.PAWN
)
25 moves
= this.getPotentialPawnMoves([x
, y
]);
27 // Special case: we don't want promotion, since just moving like
28 // a pawn, but I'm in fact not a pawn :)
29 const shiftX
= (this.turn
== 'w' ? -1 : 1);
30 if (this.board
[x
+ shiftX
][y
] == V
.EMPTY
)
31 moves
= [this.getBasicMove([x
, y
], [x
+ shiftX
, y
])];
35 moves
= this.getPotentialRookMoves([x
, y
]);
38 moves
= this.getPotentialKnightMoves([x
, y
]);
41 moves
= this.getPotentialBishopMoves([x
, y
]);
44 moves
= this.getPotentialKingMoves([x
, y
]);
47 moves
= this.getPotentialQueenMoves([x
, y
]);
50 // Post-process: if capture,
51 // can a move "as-capturer" be achieved with the same piece?
53 const color
= this.turn
;
54 return moves
.filter(m
=> {
55 if (m
.vanish
.length
== 2 && m
.appear
.length
== 1) {
59 this.getPotentialMovesFrom([m
.end
.x
, m
.end
.y
], m
.vanish
[1].p
);
61 makeMoves
.every(mm
=> {
62 // Cannot castle after a capturing move
63 // (with the capturing piece):
64 if (mm
.vanish
.length
== 2) return true;
66 const res
= this.underCheck(color
);
79 // Moving "as a": filter out captures (no castles here)
80 return moves
.filter(m
=> m
.vanish
.length
== 1);
83 getPossibleMovesFrom(sq
) {
84 const L
= this.lastMoveEnd
.length
;
86 if (!!this.lastMoveEnd
[L
-1]) {
88 sq
[0] != this.lastMoveEnd
[L
-1].x
||
89 sq
[1] != this.lastMoveEnd
[L
-1].y
93 asA
= this.lastMoveEnd
[L
-1].p
;
95 return this.filterValid(this.getPotentialMovesFrom(sq
, asA
));
99 let noCaptureMoves
= [];
100 let captureMoves
= [];
102 if (m
.vanish
.length
== 1 || m
.appear
.length
== 2) noCaptureMoves
.push(m
);
103 else captureMoves
.push(m
);
105 // Capturing moves were already checked in getPotentialMovesFrom()
106 return super.filterValid(noCaptureMoves
).concat(captureMoves
);
110 move.flags
= JSON
.stringify(this.aggregateFlags());
111 this.epSquares
.push(this.getEpSquare(move));
112 V
.PlayOnBoard(this.board
, move);
113 if (move.vanish
.length
== 1 || move.appear
.length
== 2) {
114 // Not a capture: change turn
115 this.turn
= V
.GetOppCol(this.turn
);
117 this.lastMoveEnd
.push(null);
120 this.lastMoveEnd
.push(
121 Object
.assign({}, move.end
, { p: move.vanish
[1].p
})
128 const c
= move.vanish
[0].c
;
129 const piece
= move.vanish
[0].p
;
130 if (piece
== V
.KING
&& move.appear
.length
> 0) {
131 this.kingPos
[c
][0] = move.appear
[0].x
;
132 this.kingPos
[c
][1] = move.appear
[0].y
;
134 super.updateCastleFlags(move, piece
, c
);
138 this.disaggregateFlags(JSON
.parse(move.flags
));
139 this.epSquares
.pop();
140 this.lastMoveEnd
.pop();
141 V
.UndoOnBoard(this.board
, move);
142 if (move.vanish
.length
== 1 || move.appear
.length
== 2) {
143 this.turn
= V
.GetOppCol(this.turn
);
146 super.postUndo(move);
150 let moves
= this.getAllValidMoves();
151 if (moves
.length
== 0) return null;
152 // Custom "search" at depth 1 (for now. TODO?)
153 const maxeval
= V
.INFINITY
;
154 const color
= this.turn
;
157 m
.eval
= (color
== "w" ? -1 : 1) * maxeval
;
158 if (m
.vanish
.length
== 2 && m
.appear
.length
== 1) {
159 const moves2
= this.getPossibleMovesFrom([m
.end
.x
, m
.end
.y
]);
161 moves2
.forEach(m2
=> {
163 const score
= this.getCurrentScore();
165 if (score
!= "1/2") {
166 if (score
!= "*") mvEval
= (score
== "1-0" ? 1 : -1) * maxeval
;
167 else mvEval
= this.evalPosition();
170 (color
== 'w' && mvEval
> m
.eval
) ||
171 (color
== 'b' && mvEval
< m
.eval
)
180 const score
= this.getCurrentScore();
181 if (score
!= "1/2") {
182 if (score
!= "*") m
.eval
= (score
== "1-0" ? 1 : -1) * maxeval
;
183 else m
.eval
= this.evalPosition();
188 moves
.sort((a
, b
) => {
189 return (color
== "w" ? 1 : -1) * (b
.eval
- a
.eval
);
191 let candidates
= [0];
192 for (let i
= 1; i
< moves
.length
&& moves
[i
].eval
== moves
[0].eval
; i
++)
194 const mIdx
= candidates
[randInt(candidates
.length
)];
195 if (!moves
[mIdx
].next
) return moves
[mIdx
];
196 const move2
= moves
[mIdx
].next
;
197 delete moves
[mIdx
]["next"];
198 return [moves
[mIdx
], move2
];