1 import { ChessRules
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { randInt
} from "@/utils/alea";
5 export class OrdaRules
extends ChessRules
{
7 static get PawnSpecs() {
11 { promotions: [V
.QUEEN
, V
.KHESHIG
] }
15 static IsGoodFlags(flags
) {
16 // Only white can castle
17 return !!flags
.match(/^[a-z]{2,2}$/);
21 if (b
[0] == 'b' || b
[1] == 'h')
22 // Horde piece or white promoted pawn in kheshig
27 static GenRandInitFen(randomness
) {
29 return "lhaykahl/8/pppppppp/8/8/8/PPPPPPPP/RNBQKBNR w 0 ah -";
31 // Mapping kingdom --> horde:
40 let pieces
= { w: new Array(8), b: new Array(8) };
42 // Shuffle pieces on first (and last rank if randomness == 2)
43 for (let c
of ["w", "b"]) {
44 if (c
== 'b' && randomness
== 1) {
45 pieces
['b'] = pieces
['w'].map(p
=> piecesMap
[p
]);
49 // TODO: same code as in base_rules. Should extract and factorize?
51 let positions
= ArrayFun
.range(8);
53 let randIndex
= 2 * randInt(4);
54 const bishop1Pos
= positions
[randIndex
];
55 let randIndex_tmp
= 2 * randInt(4) + 1;
56 const bishop2Pos
= positions
[randIndex_tmp
];
57 positions
.splice(Math
.max(randIndex
, randIndex_tmp
), 1);
58 positions
.splice(Math
.min(randIndex
, randIndex_tmp
), 1);
60 randIndex
= randInt(6);
61 const knight1Pos
= positions
[randIndex
];
62 positions
.splice(randIndex
, 1);
63 randIndex
= randInt(5);
64 const knight2Pos
= positions
[randIndex
];
65 positions
.splice(randIndex
, 1);
67 randIndex
= randInt(4);
68 const queenPos
= positions
[randIndex
];
69 positions
.splice(randIndex
, 1);
71 const rook1Pos
= positions
[0];
72 const kingPos
= positions
[1];
73 const rook2Pos
= positions
[2];
75 pieces
[c
][rook1Pos
] = "r";
76 pieces
[c
][knight1Pos
] = "n";
77 pieces
[c
][bishop1Pos
] = "b";
78 pieces
[c
][queenPos
] = "q";
79 pieces
[c
][kingPos
] = "k";
80 pieces
[c
][bishop2Pos
] = "b";
81 pieces
[c
][knight2Pos
] = "n";
82 pieces
[c
][rook2Pos
] = "r";
83 if (c
== 'b') pieces
[c
] = pieces
[c
].map(p
=> piecesMap
[p
]);
84 else flags
= V
.CoordToColumn(rook1Pos
) + V
.CoordToColumn(rook2Pos
);
86 // Add turn + flags + enpassant
88 pieces
["b"].join("") +
89 "/8/pppppppp/8/8/8/PPPPPPPP/" +
90 pieces
["w"].join("").toUpperCase() +
91 " w 0 " + flags
+ " -"
96 return this.castleFlags
['w'].map(V
.CoordToColumn
).join("");
100 this.castleFlags
= { 'w': [-1, -1] };
101 for (let i
= 0; i
< 2; i
++)
102 this.castleFlags
['w'][i
] = V
.ColumnToCoord(fenflags
.charAt(i
));
105 static get LANCER() {
108 static get ARCHER() {
111 static get KHESHIG() {
117 // Khan is technically a King, so let's keep things simple.
119 static get PIECES() {
120 return ChessRules
.PIECES
.concat([V
.LANCER
, V
.ARCHER
, V
.KHESHIG
, V
.YURT
]);
123 getPotentialMovesFrom([x
, y
]) {
124 switch (this.getPiece(x
, y
)) {
126 return this.getPotentialLancerMoves([x
, y
]);
128 return this.getPotentialArcherMoves([x
, y
]);
130 return this.getPotentialKheshigMoves([x
, y
]);
132 return this.getPotentialYurtMoves([x
, y
]);
134 return super.getPotentialMovesFrom([x
, y
]);
139 getSlideNJumpMoves([x
, y
], steps
, oneStep
, options
) {
140 options
= options
|| {};
142 outerLoop: for (let step
of steps
) {
145 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
146 if (!options
.onlyTake
) moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
147 if (!!oneStep
) continue outerLoop
;
151 if (V
.OnBoard(i
, j
) && this.canTake([x
, y
], [i
, j
]) && !options
.onlyMove
)
152 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
157 getPotentialLancerMoves(sq
) {
158 const onlyMoves
= this.getSlideNJumpMoves(
164 const onlyTakes
= this.getSlideNJumpMoves(
170 return onlyMoves
.concat(onlyTakes
);
173 getPotentialArcherMoves(sq
) {
174 const onlyMoves
= this.getSlideNJumpMoves(
180 const onlyTakes
= this.getSlideNJumpMoves(
186 return onlyMoves
.concat(onlyTakes
);
189 getPotentialLancerMoves(sq
) {
190 const onlyMoves
= this.getSlideNJumpMoves(
196 const onlyTakes
= this.getSlideNJumpMoves(
202 return onlyMoves
.concat(onlyTakes
);
205 getPotentialKheshigMoves(sq
) {
206 return this.getSlideNJumpMoves(
208 V
.steps
[V
.KNIGHT
].concat(V
.steps
[V
.ROOK
]).concat(V
.steps
[V
.BISHOP
]),
213 getPotentialYurtMoves(sq
) {
214 return this.getSlideNJumpMoves(
216 V
.steps
[V
.BISHOP
].concat([ [1, 0] ]),
221 getPotentialKingMoves([x
, y
]) {
222 if (this.getColor(x
, y
) == 'w') return super.getPotentialKingMoves([x
, y
]);
223 // Horde doesn't castle:
224 return this.getSlideNJumpMoves(
226 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
231 isAttacked(sq
, color
) {
234 super.isAttacked(sq
, color
) ||
235 this.isAttackedByKheshig(sq
, color
)
238 // Horde: only pawn, king and queen (if promotions) in common:
240 super.isAttackedByPawn(sq
, color
) ||
241 this.isAttackedByLancer(sq
, color
) ||
242 this.isAttackedByKheshig(sq
, color
) ||
243 this.isAttackedByArcher(sq
, color
) ||
244 this.isAttackedByYurt(sq
, color
) ||
245 super.isAttackedByKing(sq
, color
) ||
246 super.isAttackedByQueen(sq
, color
)
250 isAttackedByLancer(sq
, color
) {
251 return this.isAttackedBySlideNJump(sq
, color
, V
.LANCER
, V
.steps
[V
.ROOK
]);
254 isAttackedByArcher(sq
, color
) {
255 return this.isAttackedBySlideNJump(sq
, color
, V
.ARCHER
, V
.steps
[V
.BISHOP
]);
258 isAttackedByKheshig(sq
, color
) {
259 return super.isAttackedBySlideNJump(
263 V
.steps
[V
.KNIGHT
].concat(V
.steps
[V
.ROOK
]).concat(V
.steps
[V
.BISHOP
]),
268 isAttackedByYurt(sq
, color
) {
269 return super.isAttackedBySlideNJump(
273 V
.steps
[V
.BISHOP
].concat([ [1, 0] ]),
278 updateCastleFlags(move, piece
) {
279 // Only white can castle:
281 if (piece
== V
.KING
&& move.appear
[0].c
== 'w')
282 this.castleFlags
['w'] = [8, 8];
284 move.start
.x
== firstRank
&&
285 this.castleFlags
['w'].includes(move.start
.y
)
287 const flagIdx
= (move.start
.y
== this.castleFlags
['w'][0] ? 0 : 1);
288 this.castleFlags
['w'][flagIdx
] = 8;
291 move.end
.x
== firstRank
&&
292 this.castleFlags
['w'].includes(move.end
.y
)
294 const flagIdx
= (move.end
.y
== this.castleFlags
['w'][0] ? 0 : 1);
295 this.castleFlags
['w'][flagIdx
] = 8;
301 const color
= V
.GetOppCol(this.turn
);
302 const lastRank
= (color
== 'w' ? 0 : 7);
303 if (this.kingPos
[color
][0] == lastRank
)
304 // The opposing edge is reached!
305 return color
== "w" ? "1-0" : "0-1";
306 if (this.atLeastOneMove()) return "*";
308 const oppCol
= this.turn
;
309 if (!this.underCheck(oppCol
)) return "1/2";
310 return (oppCol
== "w" ? "0-1" : "1-0");
313 static get VALUES() {
314 return Object
.assign(