Fix Koopa variant: always allow castlin with a non-stunned rook
[vchess.git] / client / src / variants / Perfect.js
CommitLineData
a6836242
BA
1import { ChessRules } from "@/base_rules";
2import { ArrayFun } from "@/utils/array";
3import { randInt } from "@/utils/alea";
4
5export 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 (
61d1baa9
BA
87 this.isAttackedBySlideNJump(sq, color, V.AMAZON, V.steps[V.BISHOP]) ||
88 this.isAttackedBySlideNJump(sq, color, V.AMAZON, V.steps[V.ROOK]) ||
a6836242
BA
89 this.isAttackedBySlideNJump(
90 sq,
91 color,
61d1baa9 92 V.AMAZON,
a6836242
BA
93 V.steps[V.KNIGHT],
94 "oneStep"
95 )
96 );
97 }
98
99 isAttackedByEmpress(sq, color) {
100 return (
61d1baa9 101 this.isAttackedBySlideNJump(sq, color, V.EMPRESS, V.steps[V.ROOK]) ||
a6836242
BA
102 this.isAttackedBySlideNJump(
103 sq,
104 color,
61d1baa9 105 V.EMPRESS,
a6836242
BA
106 V.steps[V.KNIGHT],
107 "oneStep"
108 )
109 );
110 }
111
112 isAttackedByPrincess(sq, color) {
113 return (
61d1baa9 114 this.isAttackedBySlideNJump(sq, color, V.PRINCESS, V.steps[V.BISHOP]) ||
a6836242
BA
115 this.isAttackedBySlideNJump(
116 sq,
117 color,
61d1baa9 118 V.PRINCESS,
a6836242
BA
119 V.steps[V.KNIGHT],
120 "oneStep"
121 )
122 );
123 }
124
125 static get VALUES() {
126 return Object.assign(
127 { a: 12, e: 7, s: 5 }, //experimental
128 ChessRules.VALUES
129 );
130 }
131
132 static get SEARCH_DEPTH() {
133 return 2;
134 }
135
136 static GenRandInitFen(randomness) {
137 if (randomness == 0)
138 return "esqakbnr/pppppppp/8/8/8/8/PPPPPPPP/ESQAKBNR w 0 ahah -";
139
140 let pieces = { w: new Array(8), b: new Array(8) };
141 let flags = "";
142 let whiteBishopPos = -1;
143 for (let c of ["w", "b"]) {
144 if (c == 'b' && randomness == 1) {
145 pieces['b'] = pieces['w'];
146 flags += flags;
147 break;
148 }
149
150 let positions = ArrayFun.range(8);
151
152 // Get random squares for bishop: if black, pick a different color
153 // than where the white one stands.
154 let randIndex =
155 c == 'w'
156 ? randInt(8)
157 : 2 * randInt(4) + (1 - whiteBishopPos % 2);
158 if (c == 'w') whiteBishopPos = randIndex;
159 const bishopPos = positions[randIndex];
160 positions.splice(randIndex, 1);
161
162 randIndex = randInt(7);
163 const knightPos = positions[randIndex];
164 positions.splice(randIndex, 1);
165
166 randIndex = randInt(6);
167 const queenPos = positions[randIndex];
168 positions.splice(randIndex, 1);
169
170 randIndex = randInt(5);
171 const amazonPos = positions[randIndex];
172 positions.splice(randIndex, 1);
173
174 randIndex = randInt(4);
175 const princessPos = positions[randIndex];
176 positions.splice(randIndex, 1);
177
178 // Rook, empress and king positions are now almost fixed,
179 // only the ordering rook->empress or empress->rook must be decided.
180 let rookPos = positions[0];
181 let empressPos = positions[2];
182 const kingPos = positions[1];
183 flags += V.CoordToColumn(rookPos) + V.CoordToColumn(empressPos);
184 if (Math.random() < 0.5) [rookPos, empressPos] = [empressPos, rookPos];
185
186 pieces[c][rookPos] = "r";
187 pieces[c][knightPos] = "n";
188 pieces[c][bishopPos] = "b";
189 pieces[c][queenPos] = "q";
190 pieces[c][kingPos] = "k";
191 pieces[c][amazonPos] = "a";
192 pieces[c][princessPos] = "s";
193 pieces[c][empressPos] = "e";
194 }
195 // Add turn + flags + enpassant
196 return (
197 pieces["b"].join("") +
198 "/pppppppp/8/8/8/8/PPPPPPPP/" +
199 pieces["w"].join("").toUpperCase() +
200 " w 0 " + flags + " -"
201 );
202 }
203};