Add Fanorona
[vchess.git] / client / src / variants / Tencubed.js
1 import { ChessRules } from "@/base_rules";
2 import { ArrayFun } from "@/utils/array";
3 import { shuffle } from "@/utils/alea";
4
5 export class TencubedRules extends ChessRules {
6
7 static get PawnSpecs() {
8 return Object.assign(
9 {},
10 ChessRules.PawnSpecs,
11 {
12 initShift: { w: 2, b: 2 },
13 promotions: [V.QUEEN, V.MARSHALL, V.ARCHBISHOP]
14 }
15 );
16 }
17
18 static get HasFlags() {
19 return false;
20 }
21
22 getPpath(b) {
23 return (
24 [V.MARSHALL, V.ARCHBISHOP, V.CHAMPION, V.WIZARD].includes(b[1])
25 ? "Tencubed/"
26 : ""
27 ) + b;
28 }
29
30 static get size() {
31 return { x: 10, y: 10 };
32 }
33
34 // Rook + knight:
35 static get MARSHALL() {
36 return "m";
37 }
38
39 // Bishop + knight
40 static get ARCHBISHOP() {
41 return "a";
42 }
43
44 // Dabbabah + alfil + wazir
45 static get CHAMPION() {
46 return "c";
47 }
48
49 // Camel + ferz
50 static get WIZARD() {
51 return "w";
52 }
53
54 static get PIECES() {
55 return (
56 ChessRules.PIECES
57 .concat([V.MARSHALL, V.ARCHBISHOP, V.CHAMPION, V.WIZARD])
58 );
59 }
60
61 static get steps() {
62 return Object.assign(
63 {},
64 ChessRules.steps,
65 {
66 w: [
67 [-3, -1],
68 [-3, 1],
69 [-1, -3],
70 [-1, 3],
71 [1, -3],
72 [1, 3],
73 [3, -1],
74 [3, 1],
75 [-1, -1],
76 [-1, 1],
77 [1, -1],
78 [1, 1]
79 ],
80 c: [
81 [1, 0],
82 [-1, 0],
83 [0, 1],
84 [0, -1],
85 [2, 2],
86 [2, -2],
87 [-2, 2],
88 [-2, -2],
89 [-2, 0],
90 [0, -2],
91 [2, 0],
92 [0, 2]
93 ]
94 }
95 );
96 }
97
98 static GenRandInitFen(randomness) {
99 if (randomness == 0) {
100 return (
101 "2cwamwc2/1rnbqkbnr1/pppppppppp/91/91/" +
102 "91/91/PPPPPPPPPP/1RNBQKBNR1/2CWAMWC2/ " +
103 "w 0 bibi -"
104 );
105 }
106
107 const baseFen = V.ParseFen(ChessRules.GenRandInitFen(randomness));
108 const positionParts = baseFen.position.split("/");
109 const bFen = (
110 "1" + positionParts[0] +
111 "1/pppppppppp/91/91/91/91/PPPPPPPPPP/1" +
112 positionParts[7] + "1"
113 );
114 // Now just obtain randomized new pieces placements:
115 let pieces = { w: new Array(6), b: new Array(6) };
116 for (let c of ["w", "b"]) {
117 if (c == 'b' && randomness == 1) {
118 pieces['b'] = pieces['w'];
119 break;
120 }
121
122 let positions = shuffle(ArrayFun.range(6));
123 const composition = ['w', 'w', 'c', 'c', 'a', 'm'];
124 let rem2 = positions[0] % 2;
125 if (rem2 == positions[1] % 2) {
126 // Fix wizards (on different colors)
127 for (let i=4; i<6; i++) {
128 if (positions[i] % 2 != rem2)
129 [positions[1], positions[i]] = [positions[i], positions[1]];
130 }
131 }
132 rem2 = positions[2] % 2;
133 if (rem2 == positions[3] % 2) {
134 // Fix champions too: [NOTE: positions[4] & [5] should do]
135 for (let i=4; i<6; i++) {
136 if (positions[i] % 2 != rem2)
137 [positions[3], positions[i]] = [positions[i], positions[3]];
138 }
139 }
140 for (let i = 0; i < 9; i++) pieces[c][positions[i]] = composition[i];
141 }
142 return (
143 "2" + pieces["b"].join("") + "2/" +
144 bFen +
145 "/2" + pieces["w"].join("").toUpperCase() + "2" +
146 " w 0 -"
147 );
148 }
149
150 getPotentialMovesFrom([x, y]) {
151 switch (this.getPiece(x, y)) {
152 case V.MARSHALL:
153 return this.getPotentialMarshallMoves([x, y]);
154 case V.ARCHBISHOP:
155 return this.getPotentialArchbishopMoves([x, y]);
156 case V.CHAMPION:
157 return this.getPotentialChampionMoves([x, y]);
158 case V.WIZARD:
159 return this.getPotentialWizardMoves([x, y]);
160 default:
161 return super.getPotentialMovesFrom([x, y]);
162 }
163 }
164
165 getPotentialMarshallMoves(sq) {
166 return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
167 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
168 );
169 }
170
171 getPotentialArchbishopMoves(sq) {
172 return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
173 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
174 );
175 }
176
177 getPotentialChampionMoves(sq) {
178 return this.getSlideNJumpMoves(sq, V.steps[V.CHAMPION], "oneStep");
179 }
180
181 getPotentialWizardMoves(sq) {
182 return this.getSlideNJumpMoves(sq, V.steps[V.WIZARD], "oneStep");
183 }
184
185 isAttacked(sq, color) {
186 return (
187 super.isAttacked(sq, color) ||
188 this.isAttackedByMarshall(sq, color) ||
189 this.isAttackedByArchbishop(sq, color) ||
190 this.isAttackedByChampion(sq, color) ||
191 this.isAttackedByWizard(sq, color)
192 );
193 }
194
195 isAttackedByMarshall(sq, color) {
196 return (
197 this.isAttackedBySlideNJump(sq, color, V.MARSHALL, V.steps[V.ROOK]) ||
198 this.isAttackedBySlideNJump(
199 sq,
200 color,
201 V.MARSHALL,
202 V.steps[V.KNIGHT],
203 "oneStep"
204 )
205 );
206 }
207
208 isAttackedByArchbishop(sq, color) {
209 return (
210 this.isAttackedBySlideNJump(sq, color, V.ARCHBISHOP, V.steps[V.BISHOP]) ||
211 this.isAttackedBySlideNJump(
212 sq,
213 color,
214 V.ARCHBISHOP,
215 V.steps[V.KNIGHT],
216 "oneStep"
217 )
218 );
219 }
220
221 isAttackedByWizard(sq, color) {
222 return (
223 this.isAttackedBySlideNJump(
224 sq, color, V.WIZARD, V.steps[V.WIZARD], "oneStep")
225 );
226 }
227
228 isAttackedByChampion(sq, color) {
229 return (
230 this.isAttackedBySlideNJump(
231 sq, color, V.CHAMPION, V.steps[V.CHAMPION], "oneStep")
232 );
233 }
234
235 static get SEARCH_DEPTH() {
236 return 2;
237 }
238
239 static get VALUES() {
240 return Object.assign(
241 {},
242 ChessRules.VALUES,
243 { c: 4, w: 3, a: 6, m: 8 }
244 );
245 }
246
247 };