More consistent Chakart
[vchess.git] / client / src / variants / Orda.js
CommitLineData
c7550017
BA
1import { ChessRules } from "@/base_rules";
2import { ArrayFun } from "@/utils/array";
3import { randInt } from "@/utils/alea";
4
5export class OrdaRules extends ChessRules {
7e8a7ea1 6
c7550017
BA
7 static get PawnSpecs() {
8 return Object.assign(
9 {},
10 ChessRules.PawnSpecs,
11 { promotions: [V.QUEEN, V.KHESHIG] }
12 );
13 }
14
15 static IsGoodFlags(flags) {
16 // Only white can castle
17 return !!flags.match(/^[a-z]{2,2}$/);
18 }
19
20 getPpath(b) {
21 if (b[0] == 'b' || b[1] == 'h')
22 // Horde piece or white promoted pawn in kheshig
23 return "Orda/" + b;
24 return b;
25 }
26
4313762d
BA
27 static GenRandInitFen(options) {
28 if (options.randomness == 0)
c7550017
BA
29 return "lhaykahl/8/pppppppp/8/8/8/PPPPPPPP/RNBQKBNR w 0 ah -";
30
31 // Mapping kingdom --> horde:
32 const piecesMap = {
33 'r': 'l',
34 'n': 'h',
35 'b': 'a',
36 'q': 'y',
37 'k': 'k'
38 };
39
4313762d 40 const baseFen = ChessRules.GenRandInitFen(options);
c7550017 41 return (
1269441e
BA
42 baseFen.substr(0, 8).split('').map(p => piecesMap[p]).join('') +
43 // Skip 3 first rows + black castle flags
44 "/8/pppppppp" + baseFen.substr(19, 31) + " -"
c7550017
BA
45 );
46 }
47
48 getFlagsFen() {
49 return this.castleFlags['w'].map(V.CoordToColumn).join("");
50 }
51
52 setFlags(fenflags) {
53 this.castleFlags = { 'w': [-1, -1] };
54 for (let i = 0; i < 2; i++)
55 this.castleFlags['w'][i] = V.ColumnToCoord(fenflags.charAt(i));
56 }
57
58 static get LANCER() {
59 return 'l';
60 }
61 static get ARCHER() {
62 return 'a';
63 }
64 static get KHESHIG() {
65 return 'h';
66 }
67 static get YURT() {
68 return 'y';
69 }
70 // Khan is technically a King, so let's keep things simple.
71
72 static get PIECES() {
73 return ChessRules.PIECES.concat([V.LANCER, V.ARCHER, V.KHESHIG, V.YURT]);
74 }
75
76 getPotentialMovesFrom([x, y]) {
77 switch (this.getPiece(x, y)) {
78 case V.LANCER:
79 return this.getPotentialLancerMoves([x, y]);
80 case V.ARCHER:
81 return this.getPotentialArcherMoves([x, y]);
82 case V.KHESHIG:
83 return this.getPotentialKheshigMoves([x, y]);
84 case V.YURT:
85 return this.getPotentialYurtMoves([x, y]);
86 default:
87 return super.getPotentialMovesFrom([x, y]);
88 }
89 return [];
90 }
91
4313762d 92 // TODO: merge this extension into base_rules.js
c7550017
BA
93 getSlideNJumpMoves([x, y], steps, oneStep, options) {
94 options = options || {};
95 let moves = [];
96 outerLoop: for (let step of steps) {
97 let i = x + step[0];
98 let j = y + step[1];
99 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
100 if (!options.onlyTake) moves.push(this.getBasicMove([x, y], [i, j]));
4313762d 101 if (oneStep) continue outerLoop;
c7550017
BA
102 i += step[0];
103 j += step[1];
104 }
105 if (V.OnBoard(i, j) && this.canTake([x, y], [i, j]) && !options.onlyMove)
106 moves.push(this.getBasicMove([x, y], [i, j]));
107 }
108 return moves;
109 }
110
111 getPotentialLancerMoves(sq) {
112 const onlyMoves = this.getSlideNJumpMoves(
4313762d 113 sq, V.steps[V.KNIGHT], "oneStep", { onlyMove: true });
c7550017 114 const onlyTakes = this.getSlideNJumpMoves(
4313762d 115 sq, V.steps[V.ROOK], null, { onlyTake: true });
c7550017
BA
116 return onlyMoves.concat(onlyTakes);
117 }
118
119 getPotentialArcherMoves(sq) {
120 const onlyMoves = this.getSlideNJumpMoves(
4313762d 121 sq, V.steps[V.KNIGHT], "oneStep", { onlyMove: true });
c7550017 122 const onlyTakes = this.getSlideNJumpMoves(
4313762d 123 sq, V.steps[V.BISHOP], null, { onlyTake: true });
c7550017
BA
124 return onlyMoves.concat(onlyTakes);
125 }
126
c7550017 127 getPotentialKheshigMoves(sq) {
4313762d
BA
128 const steps =
129 V.steps[V.KNIGHT].concat(V.steps[V.ROOK]).concat(V.steps[V.BISHOP]);
130 return super.getSlideNJumpMoves(sq, steps, 1);
c7550017
BA
131 }
132
133 getPotentialYurtMoves(sq) {
1269441e 134 return super.getSlideNJumpMoves(
4313762d 135 sq, V.steps[V.BISHOP].concat([ [1, 0] ]), 1);
c7550017
BA
136 }
137
138 getPotentialKingMoves([x, y]) {
139 if (this.getColor(x, y) == 'w') return super.getPotentialKingMoves([x, y]);
140 // Horde doesn't castle:
1269441e 141 return super.getSlideNJumpMoves(
4313762d 142 [x, y], V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1);
c7550017
BA
143 }
144
145 isAttacked(sq, color) {
146 if (color == 'w') {
147 return (
148 super.isAttacked(sq, color) ||
149 this.isAttackedByKheshig(sq, color)
150 );
151 }
6c7cbfed 152 // Horde: only pawn, king and queen (if promotions) in common:
c7550017
BA
153 return (
154 super.isAttackedByPawn(sq, color) ||
155 this.isAttackedByLancer(sq, color) ||
156 this.isAttackedByKheshig(sq, color) ||
157 this.isAttackedByArcher(sq, color) ||
158 this.isAttackedByYurt(sq, color) ||
6c7cbfed 159 super.isAttackedByKing(sq, color) ||
c7550017
BA
160 super.isAttackedByQueen(sq, color)
161 );
162 }
163
164 isAttackedByLancer(sq, color) {
165 return this.isAttackedBySlideNJump(sq, color, V.LANCER, V.steps[V.ROOK]);
166 }
167
168 isAttackedByArcher(sq, color) {
169 return this.isAttackedBySlideNJump(sq, color, V.ARCHER, V.steps[V.BISHOP]);
170 }
171
172 isAttackedByKheshig(sq, color) {
4313762d
BA
173 const steps =
174 V.steps[V.KNIGHT].concat(V.steps[V.ROOK]).concat(V.steps[V.BISHOP]);
175 return super.isAttackedBySlideNJump(sq, color, V.KHESHIG, steps, 1);
c7550017
BA
176 }
177
178 isAttackedByYurt(sq, color) {
179 return super.isAttackedBySlideNJump(
4313762d 180 sq, color, V.YURT, V.steps[V.BISHOP].concat([ [1, 0] ]), 1);
c7550017
BA
181 }
182
183 updateCastleFlags(move, piece) {
184 // Only white can castle:
185 const firstRank = 7;
186 if (piece == V.KING && move.appear[0].c == 'w')
187 this.castleFlags['w'] = [8, 8];
188 else if (
189 move.start.x == firstRank &&
190 this.castleFlags['w'].includes(move.start.y)
191 ) {
192 const flagIdx = (move.start.y == this.castleFlags['w'][0] ? 0 : 1);
193 this.castleFlags['w'][flagIdx] = 8;
194 }
195 else if (
196 move.end.x == firstRank &&
197 this.castleFlags['w'].includes(move.end.y)
198 ) {
199 const flagIdx = (move.end.y == this.castleFlags['w'][0] ? 0 : 1);
200 this.castleFlags['w'][flagIdx] = 8;
201 }
202 }
203
204 getCurrentScore() {
205 // Turn has changed:
206 const color = V.GetOppCol(this.turn);
207 const lastRank = (color == 'w' ? 0 : 7);
208 if (this.kingPos[color][0] == lastRank)
209 // The opposing edge is reached!
210 return color == "w" ? "1-0" : "0-1";
211 if (this.atLeastOneMove()) return "*";
212 // Game over
213 const oppCol = this.turn;
214 if (!this.underCheck(oppCol)) return "1/2";
215 return (oppCol == "w" ? "0-1" : "1-0");
216 }
217
218 static get VALUES() {
219 return Object.assign(
220 {},
221 ChessRules.VALUES,
222 {
223 y: 2,
224 a: 4,
225 h: 7,
226 l: 4
227 }
228 );
229 }
7e8a7ea1 230
c7550017 231};