Add Perfect Chess, fix a bug in BaseGame when moving while choosing a promotion,...
[vchess.git] / client / src / variants / Perfect.js
1 import { ChessRules } from "@/base_rules";
2 import { ArrayFun } from "@/utils/array";
3 import { randInt } from "@/utils/alea";
4
5 export class PerfectRules extends ChessRules {
6 static get PawnSpecs() {
7 return Object.assign(
8 {},
9 ChessRules.PawnSpecs,
10 {
11 promotions:
12 ChessRules.PawnSpecs.promotions
13 .concat([V.AMAZON, V.EMPRESS, V.PRINCESS])
14 }
15 );
16 }
17
18 getPpath(b) {
19 return (
20 [V.AMAZON, V.EMPRESS, V.PRINCESS].includes(b[1])
21 ? "Perfect/"
22 : ""
23 ) + b;
24 }
25
26 // Queen + knight
27 static get AMAZON() {
28 return "a";
29 }
30
31 // Rook + knight
32 static get EMPRESS() {
33 return "e";
34 }
35
36 // Bishop + knight
37 static get PRINCESS() {
38 return "s";
39 }
40
41 static get PIECES() {
42 return ChessRules.PIECES.concat([V.AMAZON, V.EMPRESS, V.PRINCESS]);
43 }
44
45 getPotentialMovesFrom([x, y]) {
46 switch (this.getPiece(x, y)) {
47 case V.AMAZON:
48 return this.getPotentialAmazonMoves([x, y]);
49 case V.EMPRESS:
50 return this.getPotentialEmpressMoves([x, y]);
51 case V.PRINCESS:
52 return this.getPotentialPrincessMoves([x, y]);
53 default:
54 return super.getPotentialMovesFrom([x, y]);
55 }
56 }
57
58 getPotentialAmazonMoves(sq) {
59 return super.getPotentialQueenMoves(sq).concat(
60 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
61 );
62 }
63
64 getPotentialEmpressMoves(sq) {
65 return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
66 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
67 );
68 }
69
70 getPotentialPrincessMoves(sq) {
71 return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
72 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
73 );
74 }
75
76 isAttacked(sq, color) {
77 return (
78 super.isAttacked(sq, color) ||
79 this.isAttackedByAmazon(sq, color) ||
80 this.isAttackedByEmpress(sq, color) ||
81 this.isAttackedByPrincess(sq, color)
82 );
83 }
84
85 isAttackedByAmazon(sq, color) {
86 return (
87 super.isAttackedByQueen(sq, color) ||
88 this.isAttackedBySlideNJump(
89 sq,
90 color,
91 V.MARSHALL,
92 V.steps[V.KNIGHT],
93 "oneStep"
94 )
95 );
96 }
97
98 isAttackedByEmpress(sq, color) {
99 return (
100 this.isAttackedBySlideNJump(sq, color, V.MARSHALL, V.steps[V.ROOK]) ||
101 this.isAttackedBySlideNJump(
102 sq,
103 color,
104 V.MARSHALL,
105 V.steps[V.KNIGHT],
106 "oneStep"
107 )
108 );
109 }
110
111 isAttackedByPrincess(sq, color) {
112 return (
113 this.isAttackedBySlideNJump(sq, color, V.CARDINAL, V.steps[V.BISHOP]) ||
114 this.isAttackedBySlideNJump(
115 sq,
116 color,
117 V.CARDINAL,
118 V.steps[V.KNIGHT],
119 "oneStep"
120 )
121 );
122 }
123
124 static get VALUES() {
125 return Object.assign(
126 { a: 12, e: 7, s: 5 }, //experimental
127 ChessRules.VALUES
128 );
129 }
130
131 static get SEARCH_DEPTH() {
132 return 2;
133 }
134
135 static GenRandInitFen(randomness) {
136 if (randomness == 0)
137 return "esqakbnr/pppppppp/8/8/8/8/PPPPPPPP/ESQAKBNR w 0 ahah -";
138
139 let pieces = { w: new Array(8), b: new Array(8) };
140 let flags = "";
141 let whiteBishopPos = -1;
142 for (let c of ["w", "b"]) {
143 if (c == 'b' && randomness == 1) {
144 pieces['b'] = pieces['w'];
145 flags += flags;
146 break;
147 }
148
149 let positions = ArrayFun.range(8);
150
151 // Get random squares for bishop: if black, pick a different color
152 // than where the white one stands.
153 let randIndex =
154 c == 'w'
155 ? randInt(8)
156 : 2 * randInt(4) + (1 - whiteBishopPos % 2);
157 if (c == 'w') whiteBishopPos = randIndex;
158 const bishopPos = positions[randIndex];
159 positions.splice(randIndex, 1);
160
161 randIndex = randInt(7);
162 const knightPos = positions[randIndex];
163 positions.splice(randIndex, 1);
164
165 randIndex = randInt(6);
166 const queenPos = positions[randIndex];
167 positions.splice(randIndex, 1);
168
169 randIndex = randInt(5);
170 const amazonPos = positions[randIndex];
171 positions.splice(randIndex, 1);
172
173 randIndex = randInt(4);
174 const princessPos = positions[randIndex];
175 positions.splice(randIndex, 1);
176
177 // Rook, empress and king positions are now almost fixed,
178 // only the ordering rook->empress or empress->rook must be decided.
179 let rookPos = positions[0];
180 let empressPos = positions[2];
181 const kingPos = positions[1];
182 flags += V.CoordToColumn(rookPos) + V.CoordToColumn(empressPos);
183 if (Math.random() < 0.5) [rookPos, empressPos] = [empressPos, rookPos];
184
185 pieces[c][rookPos] = "r";
186 pieces[c][knightPos] = "n";
187 pieces[c][bishopPos] = "b";
188 pieces[c][queenPos] = "q";
189 pieces[c][kingPos] = "k";
190 pieces[c][amazonPos] = "a";
191 pieces[c][princessPos] = "s";
192 pieces[c][empressPos] = "e";
193 }
194 // Add turn + flags + enpassant
195 return (
196 pieces["b"].join("") +
197 "/pppppppp/8/8/8/8/PPPPPPPP/" +
198 pieces["w"].join("").toUpperCase() +
199 " w 0 " + flags + " -"
200 );
201 }
202 };