1 import { ChessRules
} from "@/base_rules";
3 export const VariantRules
= class CheckeredRules
extends ChessRules
7 return b
[0]=='c' ? "Checkered/"+b : b
;
12 const checkered_codes
= {
20 return checkered_codes
[b
[1]];
21 return ChessRules
.board2fen(b
);
26 // Tolerate upper-case versions of checkered pieces (why not?)
27 const checkered_pieces
= {
39 if (Object
.keys(checkered_pieces
).includes(f
))
40 return 'c'+checkered_pieces
[f
];
41 return ChessRules
.fen2board(f
);
46 return ChessRules
.PIECES
.concat(['s','t','u','c','o']);
49 setOtherVariables(fen
)
51 super.setOtherVariables(fen
);
52 // Local stack of non-capturing checkered moves:
54 const cmove
= fen
.split(" ")[5];
56 this.cmoves
.push(null);
60 start: ChessRules
.SquareToCoords(cmove
.substr(0,2)),
61 end: ChessRules
.SquareToCoords(cmove
.substr(2)),
68 if (!ChessRules
.IsGoodFen(fen
))
70 const fenParts
= fen
.split(" ");
71 if (fenParts
.length
!= 6)
73 if (fenParts
[5] != "-" && !fenParts
[5].match(/^([a-h][1-8]){2}$/))
78 static IsGoodFlags(flags
)
80 // 4 for castle + 16 for pawns
81 return !!flags
.match(/^[01]{20,20}$/);
86 super.setFlags(fenflags
); //castleFlags
89 "w": [...Array(8).fill(true)], //pawns can move 2 squares?
90 "b": [...Array(8).fill(true)],
94 const flags
= fenflags
.substr(4); //skip first 4 digits, for castle
95 for (let c
of ['w','b'])
97 for (let i
=0; i
<8; i
++)
98 this.pawnFlags
[c
][i
] = (flags
.charAt((c
=='w'?0:8)+i
) == '1');
104 return [this.castleFlags
, this.pawnFlags
];
107 disaggregateFlags(flags
)
109 this.castleFlags
= flags
[0];
110 this.pawnFlags
= flags
[1];
115 if (move.appear
[0].c
== 'c' && move.vanish
.length
== 1)
116 return {start: move.start
, end: move.end
};
120 canTake([x1
,y1
], [x2
,y2
])
122 const color1
= this.getColor(x1
,y1
);
123 const color2
= this.getColor(x2
,y2
);
124 // Checkered aren't captured
125 return color1
!= color2
&& color2
!= 'c' && (color1
!= 'c' || color2
!= this.turn
);
128 // Post-processing: apply "checkerization" of standard moves
129 getPotentialMovesFrom([x
,y
])
131 let standardMoves
= super.getPotentialMovesFrom([x
,y
]);
132 const lastRank
= this.turn
== "w" ? 0 : 7;
133 if (this.getPiece(x
,y
) == V
.KING
)
134 return standardMoves
; //king has to be treated differently (for castles)
136 standardMoves
.forEach(m
=> {
137 if (m
.vanish
[0].p
== V
.PAWN
)
139 if (Math
.abs(m
.end
.x
-m
.start
.x
)==2 && !this.pawnFlags
[this.turn
][m
.start
.y
])
140 return; //skip forbidden 2-squares jumps
141 if (this.board
[m
.end
.x
][m
.end
.y
] == V
.EMPTY
&& m
.vanish
.length
==2
142 && this.getColor(m
.start
.x
,m
.start
.y
) == 'c')
144 return; //checkered pawns cannot take en-passant
147 if (m
.vanish
.length
== 1)
148 moves
.push(m
); //no capture
151 // A capture occured (m.vanish.length == 2)
154 if (m
.appear
[0].p
!= m
.vanish
[1].p
//avoid promotions (already treated):
155 && (m
.vanish
[0].p
!= V
.PAWN
|| m
.end
.x
!= lastRank
))
157 // Add transformation into captured piece
158 let m2
= JSON
.parse(JSON
.stringify(m
));
159 m2
.appear
[0].p
= m
.vanish
[1].p
;
167 canIplay(side
, [x
,y
])
169 return (side
== this.turn
&& [side
,'c'].includes(this.getColor(x
,y
)));
172 // Does m2 un-do m1 ? (to disallow undoing checkered moves)
173 oppositeMoves(m1
, m2
)
175 return (!!m1
&& m2
.appear
[0].c
== 'c'
176 && m2
.appear
.length
== 1 && m2
.vanish
.length
== 1
177 && m1
.start
.x
== m2
.end
.x
&& m1
.end
.x
== m2
.start
.x
178 && m1
.start
.y
== m2
.end
.y
&& m1
.end
.y
== m2
.start
.y
);
183 if (moves
.length
== 0)
185 const color
= this.turn
;
186 return moves
.filter(m
=> {
187 const L
= this.cmoves
.length
; //at least 1: init from FEN
188 if (this.oppositeMoves(this.cmoves
[L
-1], m
))
191 const res
= !this.underCheck(color
);
197 isAttackedByPawn([x
,y
], colors
)
199 for (let c
of colors
)
201 const color
= (c
=="c" ? this.turn : c
);
202 let pawnShift
= (color
=="w" ? 1 : -1);
203 if (x
+pawnShift
>=0 && x
+pawnShift
<8)
205 for (let i
of [-1,1])
207 if (y
+i
>=0 && y
+i
<8 && this.getPiece(x
+pawnShift
,y
+i
)==V
.PAWN
208 && this.getColor(x
+pawnShift
,y
+i
)==c
)
220 return this.isAttacked(this.kingPos
[color
], [V
.GetOppCol(color
),'c']);
223 getCheckSquares(color
)
225 // Artifically change turn, for checkered pawns
226 this.turn
= V
.GetOppCol(color
);
227 const kingAttacked
= this.isAttacked(
228 this.kingPos
[color
], [V
.GetOppCol(color
),'c']);
229 let res
= kingAttacked
230 ? [JSON
.parse(JSON
.stringify(this.kingPos
[color
]))] //need to duplicate!
236 updateVariables(move)
238 super.updateVariables(move);
239 // Does this move turn off a 2-squares pawn flag?
240 const secondRank
= [1,6];
241 if (secondRank
.includes(move.start
.x
) && move.vanish
[0].p
== V
.PAWN
)
242 this.pawnFlags
[move.start
.x
==6 ? "w" : "b"][move.start
.y
] = false;
247 if (this.atLeastOneMove()) // game not over
250 const color
= this.turn
;
251 // Artifically change turn, for checkered pawns
252 this.turn
= V
.GetOppCol(this.turn
);
253 const res
= this.isAttacked(this.kingPos
[color
], [V
.GetOppCol(color
),'c'])
254 ? (color
== "w" ? "0-1" : "1-0")
256 this.turn
= V
.GetOppCol(this.turn
);
263 //Just count material for now, considering checkered neutral (...)
264 for (let i
=0; i
<V
.size
.x
; i
++)
266 for (let j
=0; j
<V
.size
.y
; j
++)
268 if (this.board
[i
][j
] != V
.EMPTY
)
270 const sqColor
= this.getColor(i
,j
);
271 const sign
= sqColor
== "w" ? 1 : (sqColor
=="b" ? -1 : 0);
272 evaluation
+= sign
* V
.VALUES
[this.getPiece(i
,j
)];
279 static GenRandInitFen()
281 const randFen
= ChessRules
.GenRandInitFen();
282 // Add 16 pawns flags + empty cmove:
283 return randFen
.replace(" w 0 1111", " w 0 11111111111111111111 -");
288 const fenParsed
= ChessRules
.ParseFen(fen
);
289 return Object
.assign({},
290 ChessRules
.ParseFen(fen
),
291 {cmove: fen
.split(" ")[5]});
296 const L
= this.cmoves
.length
;
297 const cmoveFen
= (!this.cmoves
[L
-1]
299 : ChessRules
.CoordsToSquare(this.cmoves
[L
-1].start
)
300 + ChessRules
.CoordsToSquare(this.cmoves
[L
-1].end
));
301 return super.getFen() + " " + cmoveFen
;
306 let fen
= super.getFlagsFen();
308 for (let c
of ['w','b'])
310 for (let i
=0; i
<8; i
++)
311 fen
+= this.pawnFlags
[c
][i
] ? '1' : '0';
316 // TODO (design): this cmove update here or in (un)updateVariables ?
319 this.cmoves
.push( this.getCmove(move) );
331 if (move.appear
.length
== 2)
334 if (move.end
.y
< move.start
.y
)
340 // Translate final square
341 const finalSquare
= V
.CoordsToSquare(move.end
);
343 const piece
= this.getPiece(move.start
.x
, move.start
.y
);
348 if (move.vanish
.length
> 1)
351 const startColumn
= V
.CoordToColumn(move.start
.y
);
352 notation
= startColumn
+ "x" + finalSquare
+
353 "=" + move.appear
[0].p
.toUpperCase();
357 notation
= finalSquare
;
358 if (move.appear
.length
> 0 && piece
!= move.appear
[0].p
) //promotion
359 notation
+= "=" + move.appear
[0].p
.toUpperCase();
367 return piece
.toUpperCase() + (move.vanish
.length
> 1 ? "x" : "") + finalSquare
368 + (move.vanish
.length
> 1 ? "=" + move.appear
[0].p
.toUpperCase() : "");