618e8330a1d277efbd6051d986874c67ce6c9728
1 class MarseilleRules
extends ChessRules
3 static IsGoodEnpassant(enpassant
)
7 const squares
= enpassant
.split(",");
8 if (squares
.length
> 2)
10 for (let sq
of squares
)
12 const ep
= V
.SquareToCoords(sq
);
13 if (isNaN(ep
.x
) || !V
.OnBoard(ep
))
22 if (this.startAtFirstMove
&& this.moves
.length
==0)
24 return this.turn
+ this.subTurn
;
27 // There may be 2 enPassant squares (if 2 pawns jump 2 squares in same turn)
30 const L
= this.epSquares
.length
;
31 if (this.epSquares
[L
-1].every(epsq
=> epsq
=== undefined))
32 return "-"; //no en-passant
34 this.epSquares
[L
-1].forEach(epsq
=> {
36 res
+= V
.CoordsToSquare(epsq
) + ",";
38 return res
.slice(0,-1); //remove last comma
41 setOtherVariables(fen
)
43 const parsedFen
= V
.ParseFen(fen
);
44 this.setFlags(parsedFen
.flags
);
45 if (parsedFen
.enpassant
== "-")
46 this.epSquares
= [ [undefined,undefined] ];
50 const squares
= parsedFen
.enpassant
.split(",");
51 for (let sq
of squares
)
52 res
.push(V
.SquareToCoords(sq
));
54 res
.push(undefined); //always 2 slots in epSquares[i]
55 this.epSquares
= [ res
];
57 this.scanKingsRooks(fen
);
58 // Extract subTurn from turn indicator: "w" (first move), or
59 // "w1" or "w2" white subturn 1 or 2, and same for black
60 const fullTurn
= V
.ParseFen(fen
).turn
;
61 this.startAtFirstMove
= (fullTurn
== "w");
62 this.turn
= fullTurn
[0];
63 this.subTurn
= (fullTurn
[1] || 1);
66 getPotentialPawnMoves([x
,y
])
68 const color
= this.turn
;
70 const [sizeX
,sizeY
] = [V
.size
.x
,V
.size
.y
];
71 const shiftX
= (color
== "w" ? -1 : 1);
72 const firstRank
= (color
== 'w' ? sizeX
-1 : 0);
73 const startRank
= (color
== "w" ? sizeX
-2 : 1);
74 const lastRank
= (color
== "w" ? 0 : sizeX
-1);
75 const pawnColor
= this.getColor(x
,y
); //can be different for checkered
76 const finalPieces
= x
+ shiftX
== lastRank
77 ? [V
.ROOK
,V
.KNIGHT
,V
.BISHOP
,V
.QUEEN
]
81 if (this.board
[x
+shiftX
][y
] == V
.EMPTY
)
83 for (let piece
of finalPieces
)
85 moves
.push(this.getBasicMove([x
,y
], [x
+shiftX
,y
],
86 {c:pawnColor
,p:piece
}));
88 // Next condition because pawns on 1st rank can generally jump
89 if ([startRank
,firstRank
].includes(x
)
90 && this.board
[x
+2*shiftX
][y
] == V
.EMPTY
)
93 moves
.push(this.getBasicMove([x
,y
], [x
+2*shiftX
,y
]));
97 for (let shiftY
of [-1,1])
99 if (y
+ shiftY
>= 0 && y
+ shiftY
< sizeY
100 && this.board
[x
+shiftX
][y
+shiftY
] != V
.EMPTY
101 && this.canTake([x
,y
], [x
+shiftX
,y
+shiftY
]))
103 for (let piece
of finalPieces
)
105 moves
.push(this.getBasicMove([x
,y
], [x
+shiftX
,y
+shiftY
],
106 {c:pawnColor
,p:piece
}));
111 // En passant: always OK if subturn 1,
112 // OK on subturn 2 only if enPassant was played at subturn 1
113 // (and if there are two e.p. squares available).
114 const Lep
= this.epSquares
.length
;
115 const epSquares
= this.epSquares
[Lep
-1]; //always at least one element
117 epSquares
.forEach(sq
=> {
121 if (epSqs
.length
== 0)
123 for (let sq
of epSqs
)
125 if (this.subTurn
== 1 || (epSqs
.length
== 2 &&
126 // Was this en-passant capture already played at subturn 1 ?
127 // (Or maybe the opponent filled the en-passant square with a piece)
128 this.board
[epSqs
[0].x
][epSqs
[0].y
] != V
.EMPTY
))
130 if (sq
.x
== x
+shiftX
&& Math
.abs(sq
.y
- y
) == 1)
132 let epMove
= this.getBasicMove([x
,y
], [sq
.x
,sq
.y
]);
137 c: this.getColor(x
,sq
.y
)
151 move.notation
= [this.getNotation(move), this.getLongNotation(move)];
152 // In this special case, we also need the "move color":
153 move.color
= this.turn
;
155 move.flags
= JSON
.stringify(this.aggregateFlags());
156 let lastEpsq
= this.epSquares
[this.epSquares
.length
-1];
157 const epSq
= this.getEpSquare(move);
158 if (lastEpsq
.length
== 1)
163 let newEpsqs
= [epSq
];
164 if (this.startAtFirstMove
&& this.moves
.length
== 0)
165 newEpsqs
.push(undefined); //at first move, to force length==2 (TODO)
166 this.epSquares
.push(newEpsqs
);
168 V
.PlayOnBoard(this.board
, move);
169 if (this.startAtFirstMove
&& this.moves
.length
== 0)
171 // Does this move give check on subturn 1? If yes, skip subturn 2
172 else if (this.subTurn
==1 && this.underCheck(this.getOppCol(this.turn
)))
174 this.epSquares
[this.epSquares
.length
-1].push(undefined);
175 this.turn
= this.getOppCol(this.turn
);
176 move.checkOnSubturn1
= true;
180 if (this.subTurn
== 2)
181 this.turn
= this.getOppCol(this.turn
);
182 this.subTurn
= 3 - this.subTurn
;
184 this.moves
.push(move);
185 this.updateVariables(move);
187 move.hash
= hex_md5(this.getFen());
192 this.disaggregateFlags(JSON
.parse(move.flags
));
193 let lastEpsq
= this.epSquares
[this.epSquares
.length
-1];
194 if (lastEpsq
.length
== 2)
196 if (!!move.checkOnSubturn1
||
197 (this.startAtFirstMove
&& this.moves
.length
== 1))
199 this.epSquares
.pop(); //remove real + artificial e.p. squares
205 this.epSquares
.pop();
206 V
.UndoOnBoard(this.board
, move);
207 if (this.startAtFirstMove
&& this.moves
.length
== 1)
209 else if (move.checkOnSubturn1
)
211 this.turn
= this.getOppCol(this.turn
);
216 if (this.subTurn
== 1)
217 this.turn
= this.getOppCol(this.turn
);
218 this.subTurn
= 3 - this.subTurn
;
221 this.unupdateVariables(move);
224 // NOTE: GenRandInitFen() is OK,
225 // since at first move turn indicator is just "w"
227 // No alpha-beta here, just adapted min-max at depth 2(+1)
230 if (this.subTurn
== 2)
231 return null; //TODO: imperfect interface setup
233 const maxeval
= V
.INFINITY
;
234 const color
= this.turn
;
235 const oppCol
= this.getOppCol(this.turn
);
237 // Search best (half) move for opponent turn
238 const getBestMoveEval
= () => {
239 let moves
= this.getAllValidMoves();
240 if (moves
.length
== 0)
242 const score
= this.checkGameEnd();
245 return maxeval
* (score
== "1-0" ? 1 : -1);
247 let res
= (oppCol
== "w" ? -maxeval : maxeval
);
251 this.turn
= color
; //very artificial...
252 if (!this.atLeastOneMove())
254 const score
= this.checkGameEnd();
256 res
= (oppCol
== "w" ? Math
.max(res
, 0) : Math
.min(res
, 0));
262 return maxeval
* (score
== "1-0" ? 1 : -1);
265 const evalPos
= this.evalPosition();
266 res
= (oppCol
== "w" ? Math
.max(res
, evalPos
) : Math
.min(res
, evalPos
));
273 let moves11
= this.getAllValidMoves();
274 let doubleMoves
= [];
275 // Rank moves using a min-max at depth 2
276 for (let i
=0; i
<moves11
.length
; i
++)
278 moves11
[i
].eval
= (color
=="w" ? -1 : 1) * maxeval
;
279 this.play(moves11
[i
]);
280 if (this.turn
!= color
)
282 // We gave check with last move: search the best opponent move
283 doubleMoves
.push({moves:[moves11
[i
]], eval:getBestMoveEval()});
287 let moves12
= this.getAllValidMoves();
288 for (let j
=0; j
<moves12
.length
; j
++)
290 this.play(moves12
[j
]);
292 moves:[moves11
[i
],moves12
[j
]],
293 eval:getBestMoveEval()});
294 this.undo(moves12
[j
]);
297 this.undo(moves11
[i
]);
300 doubleMoves
.sort( (a
,b
) => {
301 return (color
=="w" ? 1 : -1) * (b
.eval
- a
.eval
); });
302 let candidates
= [0]; //indices of candidates moves
304 i
<doubleMoves
.length
&& doubleMoves
[i
].eval
== doubleMoves
[0].eval
;
310 const selected
= doubleMoves
[_
.sample(candidates
, 1)].moves
;
311 if (selected
.length
== 1)
316 getPGN(mycolor
, score
, fenStart
, mode
)
319 pgn
+= '[Site "vchess.club"]<br>';
320 const opponent
= mode
=="human" ? "Anonymous" : "Computer";
321 pgn
+= '[Variant "' + variant
+ '"]<br>';
322 pgn
+= '[Date "' + getDate(new Date()) + '"]<br>';
323 pgn
+= '[White "' + (mycolor
=='w'?'Myself':opponent
) + '"]<br>';
324 pgn
+= '[Black "' + (mycolor
=='b'?'Myself':opponent
) + '"]<br>';
325 pgn
+= '[FenStart "' + fenStart
+ '"]<br>';
326 pgn
+= '[Fen "' + this.getFen() + '"]<br>';
327 pgn
+= '[Result "' + score
+ '"]<br><br>';
331 while (i
< this.moves
.length
)
333 pgn
+= (counter
++) + ".";
334 for (let color
of ["w","b"])
337 while (i
< this.moves
.length
&& this.moves
[i
].color
== color
)
338 move += this.moves
[i
++].notation
[0] + ",";
339 move = move.slice(0,-1); //remove last comma
340 pgn
+= move + (i
< this.moves
.length
-1 ? " " : "");
345 // "Complete moves" PGN (helping in ambiguous cases)
348 while (i
< this.moves
.length
)
350 pgn
+= (counter
++) + ".";
351 for (let color
of ["w","b"])
354 while (i
< this.moves
.length
&& this.moves
[i
].color
== color
)
355 move += this.moves
[i
++].notation
[1] + ",";
356 move = move.slice(0,-1); //remove last comma
357 pgn
+= move + (i
< this.moves
.length
-1 ? " " : "");
365 const VariantRules
= MarseilleRules
;