1 import { ChessRules
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { randInt
} from "@/utils/alea";
5 export const VariantRules
= class Antiking1Rules
extends ChessRules
{
6 static get HasEnpassant() {
10 static get HasCastle() {
14 static get ANTIKING() {
19 return ChessRules
.PIECES
.concat([V
.ANTIKING
]);
23 return b
[1] == "a" ? "Antiking/" + b : b
;
26 setOtherVariables(fen
) {
27 super.setOtherVariables(fen
);
28 this.antikingPos
= { w: [-1, -1], b: [-1, -1] };
29 const rows
= V
.ParseFen(fen
).position
.split("/");
30 for (let i
= 0; i
< rows
.length
; i
++) {
32 for (let j
= 0; j
< rows
[i
].length
; j
++) {
33 switch (rows
[i
].charAt(j
)) {
35 this.antikingPos
["b"] = [i
, k
];
38 this.antikingPos
["w"] = [i
, k
];
41 const num
= parseInt(rows
[i
].charAt(j
));
42 if (!isNaN(num
)) k
+= num
- 1;
50 // (Anti)King flags at 1 (true) if they can knight-jump
54 w: [...Array(2).fill(false)],
55 b: [...Array(2).fill(false)]
57 for (let c
of ["w", "b"]) {
58 for (let i
= 0; i
< 2; i
++)
59 this.kingFlags
[c
][i
] = fenflags
.charAt((c
== "w" ? 0 : 2) + i
) == "1";
64 return this.kingFlags
;
67 disaggregateFlags(flags
) {
68 this.kingFlags
= flags
;
74 for (let c
of ["w", "b"]) {
75 for (let i
= 0; i
< 2; i
++) flags
+= this.kingFlags
[c
][i
] ? "1" : "0";
80 canTake([x1
, y1
], [x2
, y2
]) {
81 const piece1
= this.getPiece(x1
, y1
);
82 const piece2
= this.getPiece(x2
, y2
);
83 const color1
= this.getColor(x1
, y1
);
84 const color2
= this.getColor(x2
, y2
);
87 ((piece1
!= "a" && color1
!= color2
) ||
88 (piece1
== "a" && color1
== color2
))
92 getPotentialMovesFrom([x
, y
]) {
94 let addKnightJumps
= false;
95 const piece
= this.getPiece(x
, y
);
96 const color
= this.getColor(x
, y
);
97 if (piece
== V
.ANTIKING
) {
98 moves
= this.getPotentialAntikingMoves([x
, y
]);
99 addKnightJumps
= this.kingFlags
[color
][1];
101 moves
= super.getPotentialMovesFrom([x
, y
]);
102 if (piece
== V
.KING
) addKnightJumps
= this.kingFlags
[color
][0];
104 if (addKnightJumps
) {
105 // Add potential knight jump to (anti)kings
106 const knightJumps
= super.getPotentialKnightMoves([x
, y
]);
107 // Remove captures (TODO: could be done more efficiently...)
108 moves
= moves
.concat(knightJumps
.filter(m
=> m
.vanish
.length
== 1));
113 getPotentialPawnMoves([x
, y
]) {
114 const color
= this.turn
;
116 const [sizeX
, sizeY
] = [V
.size
.x
, V
.size
.y
];
117 const shiftX
= color
== "w" ? -1 : 1;
118 const startRank
= color
== "w" ? sizeX
- 2 : 1;
119 const lastRank
= color
== "w" ? 0 : sizeX
- 1;
121 x
+ shiftX
== lastRank
? [V
.ROOK
, V
.KNIGHT
, V
.BISHOP
, V
.QUEEN
] : [V
.PAWN
];
123 // One square diagonally
124 for (let shiftY
of [-1, 1]) {
125 if (this.board
[x
+ shiftX
][y
+ shiftY
] == V
.EMPTY
) {
126 for (let piece
of finalPieces
) {
128 this.getBasicMove([x
, y
], [x
+ shiftX
, y
+ shiftY
], {
138 this.board
[x
+ shiftX
][y
] != V
.EMPTY
&&
139 this.canTake([x
, y
], [x
+ shiftX
, y
])
141 for (let piece
of finalPieces
)
143 this.getBasicMove([x
, y
], [x
+ shiftX
, y
], { c: color
, p: piece
})
150 getPotentialAntikingMoves(sq
) {
151 // The antiking moves like a king (only captured colors differ)
152 return this.getSlideNJumpMoves(
154 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
159 isAttacked(sq
, color
) {
161 super.isAttacked(sq
, color
) ||
162 this.isAttackedByAntiking(sq
, color
)
166 isAttackedByPawn([x
, y
], color
) {
167 let pawnShift
= (color
== "w" ? 1 : -1);
168 if (x
+ pawnShift
>= 0 && x
+ pawnShift
< V
.size
.x
) {
170 this.getPiece(x
+ pawnShift
, y
) == V
.PAWN
&&
171 this.getColor(x
+ pawnShift
, y
) == color
179 isAttackedByKing([x
, y
], color
) {
180 // Antiking is not attacked by king:
181 if (this.getPiece(x
, y
) == V
.ANTIKING
) return false;
182 return this.isAttackedBySlideNJump(
186 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
191 isAttackedByAntiking([x
, y
], color
) {
192 // (Anti)King is not attacked by antiking
193 if ([V
.KING
, V
.ANTIKING
].includes(this.getPiece(x
, y
))) return false;
194 return this.isAttackedBySlideNJump(
198 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
204 const oppCol
= V
.GetOppCol(color
);
206 this.isAttacked(this.kingPos
[color
], oppCol
) ||
207 !this.isAttacked(this.antikingPos
[color
], oppCol
);
211 getCheckSquares(color
) {
213 const oppCol
= V
.GetOppCol(color
);
214 if (this.isAttacked(this.kingPos
[color
], oppCol
))
215 res
.push(JSON
.parse(JSON
.stringify(this.kingPos
[color
])));
216 if (!this.isAttacked(this.antikingPos
[color
], oppCol
))
217 res
.push(JSON
.parse(JSON
.stringify(this.antikingPos
[color
])));
222 super.postPlay(move);
223 const piece
= move.vanish
[0].p
;
224 const c
= move.vanish
[0].c
;
225 // Update antiking position, and kings flags
226 if (piece
== V
.ANTIKING
) {
227 this.antikingPos
[c
][0] = move.appear
[0].x
;
228 this.antikingPos
[c
][1] = move.appear
[0].y
;
229 this.kingFlags
[c
][1] = false;
230 } else if (piece
== V
.KING
) this.kingFlags
[c
][0] = false;
234 super.postUndo(move);
235 const c
= move.vanish
[0].c
;
236 if (move.vanish
[0].p
== V
.ANTIKING
)
237 this.antikingPos
[c
] = [move.start
.x
, move.start
.y
];
241 if (this.atLeastOneMove())
244 const color
= this.turn
;
245 const oppCol
= V
.GetOppCol(color
);
247 !this.isAttacked(this.kingPos
[color
], oppCol
) &&
248 this.isAttacked(this.antikingPos
[color
], oppCol
)
252 return color
== "w" ? "0-1" : "1-0";
255 static get VALUES() {
256 return Object
.assign(
262 static GenRandInitFen() {
263 // Always deterministic setup
264 return "2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2 w 0 1111";
267 static get SEARCH_DEPTH() {