Fix Musketeer, Joker, Shinobi. Start draft of Cwda
[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
7 static get PawnSpecs() {
8 return Object.assign(
9 {},
10 ChessRules.PawnSpecs,
11 {
12 promotions:
13 ChessRules.PawnSpecs.promotions
14 .concat([V.AMAZON, V.EMPRESS, V.PRINCESS])
15 }
16 );
17 }
18
19 getPpath(b) {
20 return (
21 [V.AMAZON, V.EMPRESS, V.PRINCESS].includes(b[1])
22 ? "Perfect/"
23 : ""
24 ) + b;
25 }
26
27 // Queen + knight
28 static get AMAZON() {
29 return "a";
30 }
31
32 // Rook + knight
33 static get EMPRESS() {
34 return "e";
35 }
36
37 // Bishop + knight
38 static get PRINCESS() {
39 return "s";
40 }
41
42 static get PIECES() {
43 return ChessRules.PIECES.concat([V.AMAZON, V.EMPRESS, V.PRINCESS]);
44 }
45
46 getPotentialMovesFrom([x, y]) {
47 switch (this.getPiece(x, y)) {
48 case V.AMAZON:
49 return this.getPotentialAmazonMoves([x, y]);
50 case V.EMPRESS:
51 return this.getPotentialEmpressMoves([x, y]);
52 case V.PRINCESS:
53 return this.getPotentialPrincessMoves([x, y]);
54 default:
55 return super.getPotentialMovesFrom([x, y]);
56 }
57 }
58
59 getPotentialAmazonMoves(sq) {
60 return super.getPotentialQueenMoves(sq).concat(
61 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
62 );
63 }
64
65 getPotentialEmpressMoves(sq) {
66 return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
67 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
68 );
69 }
70
71 getPotentialPrincessMoves(sq) {
72 return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
73 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
74 );
75 }
76
77 isAttacked(sq, color) {
78 return (
79 super.isAttacked(sq, color) ||
80 this.isAttackedByAmazon(sq, color) ||
81 this.isAttackedByEmpress(sq, color) ||
82 this.isAttackedByPrincess(sq, color)
83 );
84 }
85
86 isAttackedByAmazon(sq, color) {
87 return (
88 this.isAttackedBySlideNJump(sq, color, V.AMAZON, V.steps[V.BISHOP]) ||
89 this.isAttackedBySlideNJump(sq, color, V.AMAZON, V.steps[V.ROOK]) ||
90 this.isAttackedBySlideNJump(
91 sq,
92 color,
93 V.AMAZON,
94 V.steps[V.KNIGHT],
95 "oneStep"
96 )
97 );
98 }
99
100 isAttackedByEmpress(sq, color) {
101 return (
102 this.isAttackedBySlideNJump(sq, color, V.EMPRESS, V.steps[V.ROOK]) ||
103 this.isAttackedBySlideNJump(
104 sq,
105 color,
106 V.EMPRESS,
107 V.steps[V.KNIGHT],
108 "oneStep"
109 )
110 );
111 }
112
113 isAttackedByPrincess(sq, color) {
114 return (
115 this.isAttackedBySlideNJump(sq, color, V.PRINCESS, V.steps[V.BISHOP]) ||
116 this.isAttackedBySlideNJump(
117 sq,
118 color,
119 V.PRINCESS,
120 V.steps[V.KNIGHT],
121 "oneStep"
122 )
123 );
124 }
125
126 static get VALUES() {
127 return Object.assign(
128 { a: 12, e: 7, s: 5 }, //experimental
129 ChessRules.VALUES
130 );
131 }
132
133 static get SEARCH_DEPTH() {
134 return 2;
135 }
136
137 static GenRandInitFen(randomness) {
138 if (randomness == 0)
139 return "esqakbnr/pppppppp/8/8/8/8/PPPPPPPP/ESQAKBNR w 0 ahah -";
140
141 const baseFen = ChessRules.GenRandInitFen(randomness);
142 const fenParts = baseFen.split(' ');
143 const posParts = fenParts[0].split('/');
144
145 // Replace a random rook per side by an empress,
146 // a random knight by a princess, and a bishop by an amazon
147 // (Constraint: the two remaining bishops on different colors).
148
149 let newPos = { 0: "", 7: "" };
150 let amazonOddity = -1;
151 for (let rank of [0, 7]) {
152 let replaced = { 'b': -2, 'n': -2, 'r': -2 };
153 for (let i = 0; i < 8; i++) {
154 const curChar = posParts[rank].charAt(i).toLowerCase();
155 if (['b', 'n', 'r'].includes(curChar)) {
156 if (
157 replaced[curChar] == -1 ||
158 (curChar == 'b' && rank == 7 && i % 2 == amazonOddity) ||
159 (
160 (curChar != 'b' || rank == 0) &&
161 replaced[curChar] == -2 &&
162 randInt(2) == 0
163 )
164 ) {
165 replaced[curChar] = i;
166 if (curChar == 'b') {
167 if (amazonOddity < 0) amazonOddity = i % 2;
168 newPos[rank] += 'a';
169 }
170 else if (curChar == 'r') newPos[rank] += 'e';
171 else newPos[rank] += 's';
172 }
173 else {
174 if (replaced[curChar] == -2) replaced[curChar]++;
175 newPos[rank] += curChar;
176 }
177 }
178 else newPos[rank] += curChar;
179 }
180 }
181
182 return (
183 newPos[0] + "/" + posParts.slice(1, 7).join('/') + "/" +
184 newPos[7].toUpperCase() + " " + fenParts.slice(1, 5).join(' ') + " -"
185 );
186 }
187
188 };