1 import { ChessRules
} from "@/base_rules";
3 export class Doublemove1Rules
extends ChessRules
{
4 static IsGoodEnpassant(enpassant
) {
5 const squares
= enpassant
.split(",");
6 if (squares
.length
> 2) return false;
7 for (let sq
of squares
) {
9 const ep
= V
.SquareToCoords(sq
);
10 if (isNaN(ep
.x
) || !V
.OnBoard(ep
)) return false;
16 // There may be 2 enPassant squares (if 2 pawns jump 2 squares in same turn)
18 return this.epSquares
[this.epSquares
.length
- 1].map(
19 epsq
=> epsq
=== undefined
21 : V
.CoordsToSquare(epsq
)
25 setOtherVariables(fen
) {
26 const parsedFen
= V
.ParseFen(fen
);
27 this.setFlags(parsedFen
.flags
);
28 this.epSquares
= [parsedFen
.enpassant
.split(",").map(sq
=> {
29 if (sq
!= "-") return V
.SquareToCoords(sq
);
33 this.turn
= parsedFen
.turn
;
37 getEnpassantCaptures([x
, y
], shiftX
) {
39 // En passant: always OK if subturn 1,
40 // OK on subturn 2 only if enPassant was played at subturn 1
41 // (and if there are two e.p. squares available).
42 const Lep
= this.epSquares
.length
;
43 const epSquares
= this.epSquares
[Lep
- 1]; //always at least one element
45 epSquares
.forEach(sq
=> {
46 if (sq
) epSqs
.push(sq
);
48 if (epSqs
.length
== 0) return moves
;
49 const oppCol
= V
.GetOppCol(this.getColor(x
, y
));
50 for (let sq
of epSqs
) {
54 // Was this en-passant capture already played at subturn 1 ?
55 // (Or maybe the opponent filled the en-passant square with a piece)
56 this.board
[epSqs
[0].x
][epSqs
[0].y
] != V
.EMPTY
)
60 Math
.abs(sq
.y
- y
) == 1 &&
61 // Add condition "enemy pawn must be present"
62 this.getPiece(x
, sq
.y
) == V
.PAWN
&&
63 this.getColor(x
, sq
.y
) == oppCol
65 let epMove
= this.getBasicMove([x
, y
], [sq
.x
, sq
.y
]);
80 const color
= this.turn
;
81 for (let i
= 0; i
< V
.size
.x
; i
++) {
82 for (let j
= 0; j
< V
.size
.y
; j
++) {
83 if (this.board
[i
][j
] != V
.EMPTY
&& this.getColor(i
, j
) == color
) {
84 const moves
= this.getPotentialMovesFrom([i
, j
]);
85 if (moves
.length
> 0) {
86 for (let k
= 0; k
< moves
.length
; k
++) {
88 // NOTE: not using play() here (=> infinite loop)
89 V
.PlayOnBoard(this.board
, m
);
90 if (m
.vanish
[0].p
== V
.KING
)
91 this.kingPos
[color
] = [m
.appear
[0].x
, m
.appear
[0].y
];
92 const res
= !this.underCheck(color
);
93 V
.UndoOnBoard(this.board
, m
);
94 if (m
.vanish
[0].p
== V
.KING
)
95 this.kingPos
[color
] = [m
.start
.x
, m
.start
.y
];
106 move.flags
= JSON
.stringify(this.aggregateFlags());
107 move.turn
= [this.turn
, this.subTurn
];
108 V
.PlayOnBoard(this.board
, move);
109 const epSq
= this.getEpSquare(move);
110 const oppCol
= V
.GetOppCol(this.turn
);
111 if (this.movesCount
== 0) {
112 // First move in game
114 this.epSquares
.push([epSq
]);
117 // Does this move give check on subturn 1 or reach stalemate?
118 // If yes, skip subturn 2
122 this.underCheck(V
.GetOppCol(this.turn
)) ||
123 !this.atLeastOneMove()
127 this.epSquares
.push([epSq
]);
128 move.checkOnSubturn1
= true;
132 if (this.subTurn
== 2) {
134 let lastEpsq
= this.epSquares
[this.epSquares
.length
- 1];
138 this.epSquares
.push([epSq
]);
141 this.subTurn
= 3 - this.subTurn
;
147 const c
= move.turn
[0];
148 const piece
= move.vanish
[0].p
;
149 const firstRank
= c
== "w" ? V
.size
.x
- 1 : 0;
151 if (piece
== V
.KING
) {
152 this.kingPos
[c
] = [move.appear
[0].x
, move.appear
[0].y
];
153 this.castleFlags
[c
] = [V
.size
.y
, V
.size
.y
];
156 const oppCol
= V
.GetOppCol(c
);
157 const oppFirstRank
= V
.size
.x
- 1 - firstRank
;
159 move.start
.x
== firstRank
&& //our rook moves?
160 this.castleFlags
[c
].includes(move.start
.y
)
162 const flagIdx
= (move.start
.y
== this.castleFlags
[c
][0] ? 0 : 1);
163 this.castleFlags
[c
][flagIdx
] = V
.size
.y
;
166 move.end
.x
== oppFirstRank
&& //we took opponent rook?
167 this.castleFlags
[oppCol
].includes(move.end
.y
)
169 const flagIdx
= (move.end
.y
== this.castleFlags
[oppCol
][0] ? 0 : 1);
170 this.castleFlags
[oppCol
][flagIdx
] = V
.size
.y
;
175 this.disaggregateFlags(JSON
.parse(move.flags
));
176 V
.UndoOnBoard(this.board
, move);
177 if (this.movesCount
== 1 || !!move.checkOnSubturn1
|| this.subTurn
== 2) {
178 // The move may not be full, but is fully undone:
179 this.epSquares
.pop();
180 // Moves counter was just incremented:
184 // Undo the second half of a move
185 let lastEpsq
= this.epSquares
[this.epSquares
.length
- 1];
188 this.turn
= move.turn
[0];
189 this.subTurn
= move.turn
[1];
190 super.postUndo(move);
193 static get VALUES() {
199 q: 7, //slightly less than in orthodox game
204 // No alpha-beta here, just adapted min-max at depth 2(+1)
206 const maxeval
= V
.INFINITY
;
207 const color
= this.turn
;
208 const oppCol
= V
.GetOppCol(this.turn
);
210 // Search best (half) move for opponent turn
211 const getBestMoveEval
= () => {
212 let score
= this.getCurrentScore();
214 if (score
== "1/2") return 0;
215 return maxeval
* (score
== "1-0" ? 1 : -1);
217 let moves
= this.getAllValidMoves();
218 let res
= oppCol
== "w" ? -maxeval : maxeval
;
219 for (let m
of moves
) {
221 score
= this.getCurrentScore();
222 // Now turn is oppCol,2 if m doesn't give check
223 // Otherwise it's color,1. In both cases the next test makes sense
226 res
= oppCol
== "w" ? Math
.max(res
, 0) : Math
.min(res
, 0);
230 return maxeval
* (score
== "1-0" ? 1 : -1);
233 const evalPos
= this.evalPosition();
234 res
= oppCol
== "w" ? Math
.max(res
, evalPos
) : Math
.min(res
, evalPos
);
240 const moves11
= this.getAllValidMoves();
241 let doubleMove
= null;
242 let bestEval
= Number
.POSITIVE_INFINITY
* (color
== 'w' ? -1 : 1);
243 // Rank moves using a min-max at depth 2
244 for (let i
= 0; i
< moves11
.length
; i
++) {
245 this.play(moves11
[i
]);
246 if (this.turn
!= color
) {
247 // We gave check with last move: search the best opponent move
248 const evalM
= getBestMoveEval() + 0.05 - Math
.random() / 10;
250 (color
== 'w' && evalM
> bestEval
) ||
251 (color
== 'b' && evalM
< bestEval
)
253 doubleMove
= moves11
[i
];
258 let moves12
= this.getAllValidMoves();
259 for (let j
= 0; j
< moves12
.length
; j
++) {
260 this.play(moves12
[j
]);
261 const evalM
= getBestMoveEval() + 0.05 - Math
.random() / 10
263 (color
== 'w' && evalM
> bestEval
) ||
264 (color
== 'b' && evalM
< bestEval
)
266 doubleMove
= [moves11
[i
], moves12
[j
]];
269 this.undo(moves12
[j
]);
272 this.undo(moves11
[i
]);