1 import { ChessRules
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { randInt
} from "@/utils/alea";
5 export class OrdaRules
extends ChessRules
{
6 static get PawnSpecs() {
10 { promotions: [V
.QUEEN
, V
.KHESHIG
] }
14 static IsGoodFlags(flags
) {
15 // Only white can castle
16 return !!flags
.match(/^[a-z]{2,2}$/);
20 if (b
[0] == 'b' || b
[1] == 'h')
21 // Horde piece or white promoted pawn in kheshig
26 static GenRandInitFen(randomness
) {
28 return "lhaykahl/8/pppppppp/8/8/8/PPPPPPPP/RNBQKBNR w 0 ah -";
30 // Mapping kingdom --> horde:
39 let pieces
= { w: new Array(8), b: new Array(8) };
41 // Shuffle pieces on first (and last rank if randomness == 2)
42 for (let c
of ["w", "b"]) {
43 if (c
== 'b' && randomness
== 1) {
44 pieces
['b'] = pieces
['w'].map(p
=> piecesMap
[p
]);
48 // TODO: same code as in base_rules. Should extract and factorize?
50 let positions
= ArrayFun
.range(8);
52 let randIndex
= 2 * randInt(4);
53 const bishop1Pos
= positions
[randIndex
];
54 let randIndex_tmp
= 2 * randInt(4) + 1;
55 const bishop2Pos
= positions
[randIndex_tmp
];
56 positions
.splice(Math
.max(randIndex
, randIndex_tmp
), 1);
57 positions
.splice(Math
.min(randIndex
, randIndex_tmp
), 1);
59 randIndex
= randInt(6);
60 const knight1Pos
= positions
[randIndex
];
61 positions
.splice(randIndex
, 1);
62 randIndex
= randInt(5);
63 const knight2Pos
= positions
[randIndex
];
64 positions
.splice(randIndex
, 1);
66 randIndex
= randInt(4);
67 const queenPos
= positions
[randIndex
];
68 positions
.splice(randIndex
, 1);
70 const rook1Pos
= positions
[0];
71 const kingPos
= positions
[1];
72 const rook2Pos
= positions
[2];
74 pieces
[c
][rook1Pos
] = "r";
75 pieces
[c
][knight1Pos
] = "n";
76 pieces
[c
][bishop1Pos
] = "b";
77 pieces
[c
][queenPos
] = "q";
78 pieces
[c
][kingPos
] = "k";
79 pieces
[c
][bishop2Pos
] = "b";
80 pieces
[c
][knight2Pos
] = "n";
81 pieces
[c
][rook2Pos
] = "r";
82 if (c
== 'b') pieces
[c
] = pieces
[c
].map(p
=> piecesMap
[p
]);
83 else flags
= V
.CoordToColumn(rook1Pos
) + V
.CoordToColumn(rook2Pos
);
85 // Add turn + flags + enpassant
87 pieces
["b"].join("") +
88 "/8/pppppppp/8/8/8/PPPPPPPP/" +
89 pieces
["w"].join("").toUpperCase() +
90 " w 0 " + flags
+ " -"
95 return this.castleFlags
['w'].map(V
.CoordToColumn
).join("");
99 this.castleFlags
= { 'w': [-1, -1] };
100 for (let i
= 0; i
< 2; i
++)
101 this.castleFlags
['w'][i
] = V
.ColumnToCoord(fenflags
.charAt(i
));
104 static get LANCER() {
107 static get ARCHER() {
110 static get KHESHIG() {
116 // Khan is technically a King, so let's keep things simple.
118 static get PIECES() {
119 return ChessRules
.PIECES
.concat([V
.LANCER
, V
.ARCHER
, V
.KHESHIG
, V
.YURT
]);
122 getPotentialMovesFrom([x
, y
]) {
123 switch (this.getPiece(x
, y
)) {
125 return this.getPotentialLancerMoves([x
, y
]);
127 return this.getPotentialArcherMoves([x
, y
]);
129 return this.getPotentialKheshigMoves([x
, y
]);
131 return this.getPotentialYurtMoves([x
, y
]);
133 return super.getPotentialMovesFrom([x
, y
]);
138 getSlideNJumpMoves([x
, y
], steps
, oneStep
, options
) {
139 options
= options
|| {};
141 outerLoop: for (let step
of steps
) {
144 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
145 if (!options
.onlyTake
) moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
146 if (!!oneStep
) continue outerLoop
;
150 if (V
.OnBoard(i
, j
) && this.canTake([x
, y
], [i
, j
]) && !options
.onlyMove
)
151 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
156 getPotentialLancerMoves(sq
) {
157 const onlyMoves
= this.getSlideNJumpMoves(
163 const onlyTakes
= this.getSlideNJumpMoves(
169 return onlyMoves
.concat(onlyTakes
);
172 getPotentialArcherMoves(sq
) {
173 const onlyMoves
= this.getSlideNJumpMoves(
179 const onlyTakes
= this.getSlideNJumpMoves(
185 return onlyMoves
.concat(onlyTakes
);
188 getPotentialLancerMoves(sq
) {
189 const onlyMoves
= this.getSlideNJumpMoves(
195 const onlyTakes
= this.getSlideNJumpMoves(
201 return onlyMoves
.concat(onlyTakes
);
204 getPotentialKheshigMoves(sq
) {
205 return this.getSlideNJumpMoves(
207 V
.steps
[V
.KNIGHT
].concat(V
.steps
[V
.ROOK
]).concat(V
.steps
[V
.BISHOP
]),
212 getPotentialYurtMoves(sq
) {
213 return this.getSlideNJumpMoves(
215 V
.steps
[V
.BISHOP
].concat([ [1, 0] ]),
220 getPotentialKingMoves([x
, y
]) {
221 if (this.getColor(x
, y
) == 'w') return super.getPotentialKingMoves([x
, y
]);
222 // Horde doesn't castle:
223 return this.getSlideNJumpMoves(
225 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
230 isAttacked(sq
, color
) {
233 super.isAttacked(sq
, color
) ||
234 this.isAttackedByKheshig(sq
, color
)
237 // Horde: only pawn, king and queen (if promotions) in common:
239 super.isAttackedByPawn(sq
, color
) ||
240 this.isAttackedByLancer(sq
, color
) ||
241 this.isAttackedByKheshig(sq
, color
) ||
242 this.isAttackedByArcher(sq
, color
) ||
243 this.isAttackedByYurt(sq
, color
) ||
244 super.isAttackedByKing(sq
, color
) ||
245 super.isAttackedByQueen(sq
, color
)
249 isAttackedByLancer(sq
, color
) {
250 return this.isAttackedBySlideNJump(sq
, color
, V
.LANCER
, V
.steps
[V
.ROOK
]);
253 isAttackedByArcher(sq
, color
) {
254 return this.isAttackedBySlideNJump(sq
, color
, V
.ARCHER
, V
.steps
[V
.BISHOP
]);
257 isAttackedByKheshig(sq
, color
) {
258 return super.isAttackedBySlideNJump(
262 V
.steps
[V
.KNIGHT
].concat(V
.steps
[V
.ROOK
]).concat(V
.steps
[V
.BISHOP
]),
267 isAttackedByYurt(sq
, color
) {
268 return super.isAttackedBySlideNJump(
272 V
.steps
[V
.BISHOP
].concat([ [1, 0] ]),
277 updateCastleFlags(move, piece
) {
278 // Only white can castle:
280 if (piece
== V
.KING
&& move.appear
[0].c
== 'w')
281 this.castleFlags
['w'] = [8, 8];
283 move.start
.x
== firstRank
&&
284 this.castleFlags
['w'].includes(move.start
.y
)
286 const flagIdx
= (move.start
.y
== this.castleFlags
['w'][0] ? 0 : 1);
287 this.castleFlags
['w'][flagIdx
] = 8;
290 move.end
.x
== firstRank
&&
291 this.castleFlags
['w'].includes(move.end
.y
)
293 const flagIdx
= (move.end
.y
== this.castleFlags
['w'][0] ? 0 : 1);
294 this.castleFlags
['w'][flagIdx
] = 8;
300 const color
= V
.GetOppCol(this.turn
);
301 const lastRank
= (color
== 'w' ? 0 : 7);
302 if (this.kingPos
[color
][0] == lastRank
)
303 // The opposing edge is reached!
304 return color
== "w" ? "1-0" : "0-1";
305 if (this.atLeastOneMove()) return "*";
307 const oppCol
= this.turn
;
308 if (!this.underCheck(oppCol
)) return "1/2";
309 return (oppCol
== "w" ? "0-1" : "1-0");
312 static get VALUES() {
313 return Object
.assign(