1 import { ChessRules
} from "@/base_rules";
2 import { randInt
} from "@/utils/alea";
4 export class TakenmakeRules
extends ChessRules
{
6 setOtherVariables(fen
) {
7 super.setOtherVariables(fen
);
8 // Stack of "last move" only for intermediate captures
9 this.lastMoveEnd
= [null];
12 getPotentialMovesFrom([x
, y
], asA
) {
13 const L
= this.lastMoveEnd
.length
;
14 if (!asA
&& !!this.lastMoveEnd
[L
-1]) {
15 asA
= this.lastMoveEnd
[L
-1].p
;
16 if (x
!= this.lastMoveEnd
[L
-1].x
|| y
!= this.lastMoveEnd
[L
-1].y
) {
17 // A capture was played: wrong square
22 const piece
= this.getPiece(x
, y
);
23 switch (asA
|| piece
) {
25 if (!asA
|| piece
== V
.PAWN
)
26 moves
= super.getPotentialPawnMoves([x
, y
]);
28 // Special case: we don't want promotion, since just moving like
29 // a pawn, but I'm in fact not a pawn :)
30 const shiftX
= (this.turn
== 'w' ? -1 : 1);
31 if (this.board
[x
+ shiftX
][y
] == V
.EMPTY
)
32 moves
= [this.getBasicMove([x
, y
], [x
+ shiftX
, y
])];
36 moves
= this.getPotentialRookMoves([x
, y
]);
39 moves
= this.getPotentialKnightMoves([x
, y
]);
42 moves
= this.getPotentialBishopMoves([x
, y
]);
45 moves
= this.getPotentialKingMoves([x
, y
]);
48 moves
= this.getPotentialQueenMoves([x
, y
]);
51 // Post-process: if capture,
52 // can a move "as-capturer" be achieved with the same piece?
54 const color
= this.turn
;
55 return moves
.filter(m
=> {
56 if (m
.vanish
.length
== 2 && m
.appear
.length
== 1) {
60 this.getPotentialMovesFrom([m
.end
.x
, m
.end
.y
], m
.vanish
[1].p
);
62 makeMoves
.every(mm
=> {
63 // Cannot castle after a capturing move
64 // (with the capturing piece):
65 if (mm
.vanish
.length
== 2) return true;
67 const res
= this.underCheck(color
);
80 // Moving "as a": filter out captures (no castles here)
81 return moves
.filter(m
=> m
.vanish
.length
== 1);
84 getPossibleMovesFrom(sq
) {
85 const L
= this.lastMoveEnd
.length
;
87 if (!!this.lastMoveEnd
[L
-1]) {
89 sq
[0] != this.lastMoveEnd
[L
-1].x
||
90 sq
[1] != this.lastMoveEnd
[L
-1].y
94 asA
= this.lastMoveEnd
[L
-1].p
;
96 return this.filterValid(this.getPotentialMovesFrom(sq
, asA
));
100 let noCaptureMoves
= [];
101 let captureMoves
= [];
103 if (m
.vanish
.length
== 1 || m
.appear
.length
== 2) noCaptureMoves
.push(m
);
104 else captureMoves
.push(m
);
106 // Capturing moves were already checked in getPotentialMovesFrom()
107 return super.filterValid(noCaptureMoves
).concat(captureMoves
);
111 move.flags
= JSON
.stringify(this.aggregateFlags());
112 this.epSquares
.push(this.getEpSquare(move));
113 V
.PlayOnBoard(this.board
, move);
114 if (move.vanish
.length
== 1 || move.appear
.length
== 2) {
115 // Not a capture: change turn
116 this.turn
= V
.GetOppCol(this.turn
);
118 this.lastMoveEnd
.push(null);
121 this.lastMoveEnd
.push(
122 Object
.assign({}, move.end
, { p: move.vanish
[1].p
})
129 const c
= move.vanish
[0].c
;
130 const piece
= move.vanish
[0].p
;
131 if (piece
== V
.KING
&& move.appear
.length
> 0) {
132 this.kingPos
[c
][0] = move.appear
[0].x
;
133 this.kingPos
[c
][1] = move.appear
[0].y
;
135 super.updateCastleFlags(move, piece
, c
);
139 this.disaggregateFlags(JSON
.parse(move.flags
));
140 this.epSquares
.pop();
141 this.lastMoveEnd
.pop();
142 V
.UndoOnBoard(this.board
, move);
143 if (move.vanish
.length
== 1 || move.appear
.length
== 2) {
144 this.turn
= V
.GetOppCol(this.turn
);
147 super.postUndo(move);
151 let moves
= this.getAllValidMoves();
152 if (moves
.length
== 0) return null;
153 // Custom "search" at depth 1 (for now. TODO?)
154 const maxeval
= V
.INFINITY
;
155 const color
= this.turn
;
158 m
.eval
= (color
== "w" ? -1 : 1) * maxeval
;
159 if (m
.vanish
.length
== 2 && m
.appear
.length
== 1) {
160 const moves2
= this.getPossibleMovesFrom([m
.end
.x
, m
.end
.y
]);
162 moves2
.forEach(m2
=> {
164 const score
= this.getCurrentScore();
166 if (score
!= "1/2") {
167 if (score
!= "*") mvEval
= (score
== "1-0" ? 1 : -1) * maxeval
;
168 else mvEval
= this.evalPosition();
171 (color
== 'w' && mvEval
> m
.eval
) ||
172 (color
== 'b' && mvEval
< m
.eval
)
181 const score
= this.getCurrentScore();
182 if (score
!= "1/2") {
183 if (score
!= "*") m
.eval
= (score
== "1-0" ? 1 : -1) * maxeval
;
184 else m
.eval
= this.evalPosition();
189 moves
.sort((a
, b
) => {
190 return (color
== "w" ? 1 : -1) * (b
.eval
- a
.eval
);
192 let candidates
= [0];
193 for (let i
= 1; i
< moves
.length
&& moves
[i
].eval
== moves
[0].eval
; i
++)
195 const mIdx
= candidates
[randInt(candidates
.length
)];
196 if (!moves
[mIdx
].next
) return moves
[mIdx
];
197 const move2
= moves
[mIdx
].next
;
198 delete moves
[mIdx
]["next"];
199 return [moves
[mIdx
], move2
];