Several small improvements + integrate options + first working 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], 1)
62 );
63 }
64
65 getPotentialEmpressMoves(sq) {
66 return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
67 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], 1)
68 );
69 }
70
71 getPotentialPrincessMoves(sq) {
72 return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
73 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], 1)
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(sq, color, V.AMAZON, V.steps[V.KNIGHT], 1)
91 );
92 }
93
94 isAttackedByEmpress(sq, color) {
95 return (
96 this.isAttackedBySlideNJump(sq, color, V.EMPRESS, V.steps[V.ROOK]) ||
97 this.isAttackedBySlideNJump(sq, color, V.EMPRESS, V.steps[V.KNIGHT], 1)
98 );
99 }
100
101 isAttackedByPrincess(sq, color) {
102 return (
103 this.isAttackedBySlideNJump(sq, color, V.PRINCESS, V.steps[V.BISHOP]) ||
104 this.isAttackedBySlideNJump(sq, color, V.PRINCESS, V.steps[V.KNIGHT], 1)
105 );
106 }
107
108 static get VALUES() {
109 return Object.assign(
110 { a: 12, e: 7, s: 5 }, //experimental
111 ChessRules.VALUES
112 );
113 }
114
115 static get SEARCH_DEPTH() {
116 return 2;
117 }
118
119 static GenRandInitFen(options) {
120 if (options.randomness == 0)
121 return "esqakbnr/pppppppp/8/8/8/8/PPPPPPPP/ESQAKBNR w 0 ahah -";
122
123 const baseFen = ChessRules.GenRandInitFen(options);
124 const fenParts = baseFen.split(' ');
125 const posParts = fenParts[0].split('/');
126
127 // Replace a random rook per side by an empress,
128 // a random knight by a princess, and a bishop by an amazon
129 // (Constraint: the two remaining bishops on different colors).
130
131 let newPos = { 0: "", 7: "" };
132 let amazonOddity = -1;
133 for (let rank of [0, 7]) {
134 let replaced = { 'b': -2, 'n': -2, 'r': -2 };
135 for (let i = 0; i < 8; i++) {
136 const curChar = posParts[rank].charAt(i).toLowerCase();
137 if (['b', 'n', 'r'].includes(curChar)) {
138 if (
139 replaced[curChar] == -1 ||
140 (curChar == 'b' && rank == 7 && i % 2 == amazonOddity) ||
141 (
142 (curChar != 'b' || rank == 0) &&
143 replaced[curChar] == -2 &&
144 randInt(2) == 0
145 )
146 ) {
147 replaced[curChar] = i;
148 if (curChar == 'b') {
149 if (amazonOddity < 0) amazonOddity = i % 2;
150 newPos[rank] += 'a';
151 }
152 else if (curChar == 'r') newPos[rank] += 'e';
153 else newPos[rank] += 's';
154 }
155 else {
156 if (replaced[curChar] == -2) replaced[curChar]++;
157 newPos[rank] += curChar;
158 }
159 }
160 else newPos[rank] += curChar;
161 }
162 }
163
164 return (
165 newPos[0] + "/" + posParts.slice(1, 7).join('/') + "/" +
166 newPos[7].toUpperCase() + " " + fenParts.slice(1, 5).join(' ') + " -"
167 );
168 }
169
170 };