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
);
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
][1]) ? 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 if (this.movesCount
% 4 == 0) this.initfenStack
.push(this.getBaseFen());
146 move.flags
= JSON
.stringify(this.aggregateFlags());
147 // Do not play on board (would reveal the move...)
148 this.turn
= V
.GetOppCol(this.turn
);
150 if ([0, 3].includes(this.movesCount
% 4)) this.postPlay(move);
151 else super.postPlay(move); //resolve synchrone move
155 if (this.turn
== 'b') {
156 // NOTE: whiteMove is used read-only, so no need to copy
157 this.whiteMove
= move;
161 // A full "deterministic" turn just ended: no need to resolve
163 appear: this.whiteMove
.appear
.concat(move.appear
),
164 vanish: this.whiteMove
.vanish
.concat(move.vanish
)
166 V
.PlayOnBoard(this.board
, smove
);
167 move.whiteMove
= this.whiteMove
; //for undo
168 this.whiteMove
= null;
170 // Update king position + flags
171 let kingAppear
= { 'w': false, 'b': false };
172 for (let i
=0; i
< smove
.appear
.length
; i
++) {
173 if (smove
.appear
[i
].p
== V
.KING
) {
174 const c
= smove
.appear
[i
].c
;
175 kingAppear
[c
] = true;
176 this.kingPos
[c
][0] = smove
.appear
[i
].x
;
177 this.kingPos
[c
][1] = smove
.appear
[i
].y
;
180 for (let i
= 0; i
< smove
.vanish
.length
; i
++) {
181 if (smove
.vanish
[i
].p
== V
.KING
) {
182 const c
= smove
.vanish
[i
].c
;
183 if (!kingAppear
[c
]) {
184 this.kingPos
[c
][0] = -1;
185 this.kingPos
[c
][1] = -1;
190 super.updateCastleFlags(smove
);
195 this.disaggregateFlags(JSON
.parse(move.flags
));
196 if (this.turn
== 'w')
197 // Back to the middle of the move
198 V
.UndoOnBoard(this.board
, move.smove
);
199 this.turn
= V
.GetOppCol(this.turn
);
201 if (this.movesCount
% 4 == 0) this.initfenStack
.pop();
206 if (this.turn
== 'w') {
207 // Reset king positions: scan board (TODO: could be more efficient)
208 if (move.vanish
.length
> 0) this.scanKings();
209 // Also reset whiteMove
210 this.whiteMove
= null;
212 else this.whiteMove
= move.whiteMove
;
216 if (this.movesCount
% 4 != 0)
217 // Turn (2 x [white + black]) not over yet
219 // Was a king captured?
220 if (this.kingPos
['w'][0] < 0) return "0-1";
221 if (this.kingPos
['b'][0] < 0) return "1-0";
222 const whiteCanMove
= this.atLeastOneMove('w');
223 const blackCanMove
= this.atLeastOneMove('b');
224 if (whiteCanMove
&& blackCanMove
) return "*";
226 const whiteInCheck
= this.underCheck('w');
227 const blackInCheck
= this.underCheck('b');
229 (whiteCanMove
&& !this.underCheck('b')) ||
230 (blackCanMove
&& !this.underCheck('w'))
234 // Checkmate: could be mutual
235 if (!whiteCanMove
&& !blackCanMove
) return "1/2";
236 return (whiteCanMove
? "1-0" : "0-1");
240 if (move.vanish
.length
== 0) return "pass";
241 return super.getNotation(move);