1 import { ChessRules
, Move
} from "@/base_rules";
2 import { Synchrone1Rules
} from "@/variants/Synchrone1";
3 import { randInt
} from "@/utils/alea";
5 export class Synchrone2Rules
extends Synchrone1Rules
{
7 static get CanAnalyze() {
11 static get HasEnpassant() {
15 static IsGoodFen(fen
) {
16 if (!Synchrone1Rules
.IsGoodFen(fen
)) return false;
17 const fenParsed
= V
.ParseFen(fen
);
18 // 5) Check initFen (not really... TODO?)
19 if (!fenParsed
.initFen
) return false;
23 static ParseFen(fen
) {
24 const fenParts
= fen
.split(" ");
28 whiteMove: fenParts
[5]
30 ChessRules
.ParseFen(fen
)
35 const L
= this.initfenStack
.length
;
36 return L
> 0 ? this.initfenStack
[L
-1] : "-";
41 super.getBaseFen() + " " +
42 super.getTurnFen() + " " +
43 this.movesCount
+ " " +
44 super.getFlagsFen() + " " +
45 this.getInitfenFen() + " " +
46 this.getWhitemoveFen()
50 static GenRandInitFen(randomness
) {
51 const res
= ChessRules
.GenRandInitFen(randomness
);
53 return res
.slice(0, -1) + res
.split(' ')[0] + " -";
56 setOtherVariables(fen
) {
57 const parsedFen
= V
.ParseFen(fen
);
58 this.setFlags(parsedFen
.flags
);
60 // Also init whiteMove
62 parsedFen
.whiteMove
!= "-"
63 ? JSON
.parse(parsedFen
.whiteMove
)
65 // And initFen (could be empty)
66 this.initfenStack
= [];
67 if (parsedFen
.initFen
!= "-") this.initfenStack
.push(parsedFen
.initFen
);
70 getPotentialMovesFrom([x
, y
]) {
71 if (this.movesCount
% 4 <= 1) return super.getPotentialMovesFrom([x
, y
]);
72 // Diff current and old board to know which pieces have moved,
73 // and to deduce possible moves at stage 2.
74 const L
= this.initfenStack
.length
;
75 let initBoard
= V
.GetBoard(this.initfenStack
[L
-1]);
78 const oppCol
= V
.GetOppCol(c
);
79 for (let i
=0; i
<8; i
++) {
80 for (let j
=0; j
<8; j
++) {
81 if (this.board
[i
][j
] != initBoard
[i
][j
]) {
82 if (this.board
[i
][j
] != V
.EMPTY
) {
83 const color
= this.board
[i
][j
].charAt(0);
84 appeared
.push({ c: color
, x: i
, y: j
});
85 // Pawns capture in diagonal => the following fix.
86 // (Other way would be to redefine getPotentialPawnMoves()...)
87 if (color
== oppCol
) initBoard
[i
][j
] = this.board
[i
][j
];
92 const saveBoard
= this.board
;
93 this.board
= initBoard
;
94 const movesInit
= super.getPotentialMovesFrom([x
, y
]);
95 this.board
= saveBoard
;
96 const target
= appeared
.find(a
=> a
.c
== oppCol
) || { x: -1, y: -1 };
97 let movesNow
= super.getPotentialMovesFrom([x
, y
]).filter(m
=> {
99 m
.end
.x
== target
.x
&&
100 m
.end
.y
== target
.y
&&
101 movesInit
.some(mi
=> mi
.end
.x
== m
.end
.x
&& mi
.end
.y
== m
.end
.y
)
105 (x
!= this.kingPos
[c
][0] || y
!= this.kingPos
[c
][0]) ? c : oppCol
;
108 start: { x: x
, y: y
},
110 x: this.kingPos
[passTarget
][0],
111 y: this.kingPos
[passTarget
][1]
121 const nonEmptyMove
= moves
.find(m
=> m
.vanish
.length
> 0);
122 if (!nonEmptyMove
) return moves
;
123 // filterValid can be called when it's "not our turn":
124 const color
= nonEmptyMove
.vanish
[0].c
;
125 return moves
.filter(m
=> {
126 if (m
.vanish
.length
== 0) return true;
127 const piece
= m
.vanish
[0].p
;
128 if (piece
== V
.KING
) {
129 this.kingPos
[color
][0] = m
.appear
[0].x
;
130 this.kingPos
[color
][1] = m
.appear
[0].y
;
132 V
.PlayOnBoard(this.board
, m
);
133 let res
= !this.underCheck(color
);
134 V
.UndoOnBoard(this.board
, m
);
135 if (piece
== V
.KING
) this.kingPos
[color
] = [m
.start
.x
, m
.start
.y
];
140 getPossibleMovesFrom([x
, y
]) {
141 return this.filterValid(this.getPotentialMovesFrom([x
, y
]));
145 const moves
= this.filterValid(super.getAllPotentialMoves());
146 if (this.movesCount
% 4 <= 1) return moves
;
147 const emptyIdx
= moves
.findIndex(m
=> m
.vanish
.length
== 0);
149 // Keep only one pass move (for computer play)
150 return moves
.filter((m
, i
) => m
.vanish
.length
> 0 || i
== emptyIdx
);
155 if (this.movesCount
% 4 == 0) this.initfenStack
.push(this.getBaseFen());
156 if (!noFlag
) move.flags
= JSON
.stringify(this.aggregateFlags());
157 // Do not play on board (would reveal the move...)
158 this.turn
= V
.GetOppCol(this.turn
);
160 if ([0, 3].includes(this.movesCount
% 4)) this.postPlay(move);
161 else super.postPlay(move); //resolve synchrone move
165 if (this.turn
== 'b') {
166 // NOTE: whiteMove is used read-only, so no need to copy
167 this.whiteMove
= move;
171 // A full "deterministic" turn just ended: no need to resolve
173 appear: this.whiteMove
.appear
.concat(move.appear
),
174 vanish: this.whiteMove
.vanish
.concat(move.vanish
)
176 V
.PlayOnBoard(this.board
, smove
);
177 move.whiteMove
= this.whiteMove
; //for undo
178 this.whiteMove
= null;
180 // Update king position + flags
181 let kingAppear
= { 'w': false, 'b': false };
182 for (let i
=0; i
< smove
.appear
.length
; i
++) {
183 if (smove
.appear
[i
].p
== V
.KING
) {
184 const c
= smove
.appear
[i
].c
;
185 kingAppear
[c
] = true;
186 this.kingPos
[c
][0] = smove
.appear
[i
].x
;
187 this.kingPos
[c
][1] = smove
.appear
[i
].y
;
190 for (let i
= 0; i
< smove
.vanish
.length
; i
++) {
191 if (smove
.vanish
[i
].p
== V
.KING
) {
192 const c
= smove
.vanish
[i
].c
;
193 if (!kingAppear
[c
]) {
194 this.kingPos
[c
][0] = -1;
195 this.kingPos
[c
][1] = -1;
200 super.updateCastleFlags(smove
);
205 if (!noFlag
) this.disaggregateFlags(JSON
.parse(move.flags
));
206 if (this.turn
== 'w')
207 // Back to the middle of the move
208 V
.UndoOnBoard(this.board
, move.smove
);
209 this.turn
= V
.GetOppCol(this.turn
);
211 if (this.movesCount
% 4 == 0) this.initfenStack
.pop();
216 if (this.turn
== 'w') {
217 // Reset king positions: scan board (TODO: could be more efficient)
218 if (move.vanish
.length
> 0) this.scanKings();
219 // Also reset whiteMove
220 this.whiteMove
= null;
222 else this.whiteMove
= move.whiteMove
;
226 if (this.movesCount
% 4 != 0)
227 // Turn (2 x [white + black]) not over yet
229 // Was a king captured?
230 if (this.kingPos
['w'][0] < 0) return "0-1";
231 if (this.kingPos
['b'][0] < 0) return "1-0";
232 const whiteCanMove
= this.atLeastOneMove('w');
233 const blackCanMove
= this.atLeastOneMove('b');
234 if (whiteCanMove
&& blackCanMove
) return "*";
236 const whiteInCheck
= this.underCheck('w');
237 const blackInCheck
= this.underCheck('b');
239 (whiteCanMove
&& !this.underCheck('b')) ||
240 (blackCanMove
&& !this.underCheck('w'))
244 // Checkmate: could be mutual
245 if (!whiteCanMove
&& !blackCanMove
) return "1/2";
246 return (whiteCanMove
? "1-0" : "0-1");
250 if (this.movesCount
% 4 <= 1) return super.getComputerMove();
251 const moves
= this.getAllValidMoves();
252 return moves
[randInt(moves
.length
)];
256 if (move.vanish
.length
== 0) return "pass";
257 return super.getNotation(move);