1 import { ChessRules
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { randInt
} from "@/utils/alea";
5 export class GrandRules
extends ChessRules
{
7 static get HasFlags() {
11 static IsGoodEnpassant(enpassant
) {
12 if (enpassant
!= "-") return !!enpassant
.match(/^([a-j][0-9]{1,2},?)+$/);
17 return ([V
.MARSHALL
, V
.CARDINAL
].includes(b
[1]) ? "Grand/" : "") + b
;
21 return { x: 10, y: 10 };
25 static get MARSHALL() {
30 static get CARDINAL() {
35 return ChessRules
.PIECES
.concat([V
.MARSHALL
, V
.CARDINAL
]);
38 getPotentialMovesFrom([x
, y
]) {
39 switch (this.getPiece(x
, y
)) {
41 return this.getPotentialMarshallMoves([x
, y
]);
43 return this.getPotentialCardinalMoves([x
, y
]);
45 return super.getPotentialMovesFrom([x
, y
]);
49 // Special pawn rules: promotions to captured friendly pieces,
50 // optional on ranks 8-9 and mandatory on rank 10.
51 getPotentialPawnMoves([x
, y
]) {
52 const color
= this.turn
;
54 const [sizeX
, sizeY
] = [V
.size
.x
, V
.size
.y
];
55 const shiftX
= color
== "w" ? -1 : 1;
56 const startRank
= (color
== "w" ? sizeX
- 3 : 2);
58 color
== "w" ? [0, 1, 2] : [sizeX
- 1, sizeX
- 2, sizeX
- 3];
59 // Always x+shiftX >= 0 && x+shiftX < sizeX, because no pawns on last rank
60 let finalPieces
= [V
.PAWN
];
61 if (lastRanks
.includes(x
+ shiftX
)) {
62 // Determine which promotion pieces are available:
63 let promotionPieces
= {
71 for (let i
=0; i
<10; i
++) {
72 for (let j
=0; j
<10; j
++) {
73 if (this.board
[i
][j
] != V
.EMPTY
&& this.getColor(i
, j
) == color
) {
74 const p
= this.getPiece(i
, j
);
75 if (![V
.PAWN
, V
.KING
].includes(p
)) promotionPieces
[p
]--;
79 const availablePieces
=
80 Object
.keys(promotionPieces
).filter(k
=> promotionPieces
[k
] > 0);
81 if (x
+ shiftX
== lastRanks
[0]) finalPieces
= availablePieces
;
82 else Array
.prototype.push
.apply(finalPieces
, availablePieces
);
84 if (this.board
[x
+ shiftX
][y
] == V
.EMPTY
) {
86 for (let piece
of finalPieces
)
88 this.getBasicMove([x
, y
], [x
+ shiftX
, y
], { c: color
, p: piece
})
90 if (x
== startRank
&& this.board
[x
+ 2 * shiftX
][y
] == V
.EMPTY
)
92 moves
.push(this.getBasicMove([x
, y
], [x
+ 2 * shiftX
, y
]));
95 for (let shiftY
of [-1, 1]) {
99 this.board
[x
+ shiftX
][y
+ shiftY
] != V
.EMPTY
&&
100 this.canTake([x
, y
], [x
+ shiftX
, y
+ shiftY
])
102 for (let piece
of finalPieces
) {
104 this.getBasicMove([x
, y
], [x
+ shiftX
, y
+ shiftY
], {
114 Array
.prototype.push
.apply(
116 this.getEnpassantCaptures([x
, y
], shiftX
)
122 getPotentialMarshallMoves(sq
) {
123 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.ROOK
]).concat(
124 this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
], 1)
128 getPotentialCardinalMoves(sq
) {
129 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
]).concat(
130 this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
], 1)
134 isAttacked(sq
, color
) {
136 super.isAttacked(sq
, color
) ||
137 this.isAttackedByMarshall(sq
, color
) ||
138 this.isAttackedByCardinal(sq
, color
)
142 isAttackedByMarshall(sq
, color
) {
144 this.isAttackedBySlideNJump(sq
, color
, V
.MARSHALL
, V
.steps
[V
.ROOK
]) ||
145 this.isAttackedBySlideNJump(sq
, color
, V
.MARSHALL
, V
.steps
[V
.KNIGHT
], 1)
149 isAttackedByCardinal(sq
, color
) {
151 this.isAttackedBySlideNJump(sq
, color
, V
.CARDINAL
, V
.steps
[V
.BISHOP
]) ||
152 this.isAttackedBySlideNJump(sq
, color
, V
.CARDINAL
, V
.steps
[V
.KNIGHT
], 1)
156 static get VALUES() {
157 return Object
.assign(
158 { c: 5, m: 7 }, //experimental
163 static get SEARCH_DEPTH() {
167 static GenRandInitFen(options
) {
168 if (options
.randomness
== 0) {
170 "r8r/1nbqkmcbn1/pppppppppp/91/91/91/91/PPPPPPPPPP/1NBQKMCBN1/R8R " +
175 let pieces
= { w: new Array(8), b: new Array(8) };
176 // Shuffle pieces on second and before-last rank
177 for (let c
of ["w", "b"]) {
178 if (c
== 'b' && options
.randomness
== 1) {
179 pieces
['b'] = pieces
['w'];
183 let positions
= ArrayFun
.range(8);
185 // Get random squares for bishops
186 let randIndex
= 2 * randInt(4);
187 const bishop1Pos
= positions
[randIndex
];
188 // The second bishop must be on a square of different color
189 let randIndex_tmp
= 2 * randInt(4) + 1;
190 const bishop2Pos
= positions
[randIndex_tmp
];
191 // Remove chosen squares
192 positions
.splice(Math
.max(randIndex
, randIndex_tmp
), 1);
193 positions
.splice(Math
.min(randIndex
, randIndex_tmp
), 1);
195 // Get random squares for knights
196 randIndex
= randInt(6);
197 const knight1Pos
= positions
[randIndex
];
198 positions
.splice(randIndex
, 1);
199 randIndex
= randInt(5);
200 const knight2Pos
= positions
[randIndex
];
201 positions
.splice(randIndex
, 1);
203 // Get random square for queen
204 randIndex
= randInt(4);
205 const queenPos
= positions
[randIndex
];
206 positions
.splice(randIndex
, 1);
208 // ...random square for marshall
209 randIndex
= randInt(3);
210 const marshallPos
= positions
[randIndex
];
211 positions
.splice(randIndex
, 1);
213 // ...random square for cardinal
214 randIndex
= randInt(2);
215 const cardinalPos
= positions
[randIndex
];
216 positions
.splice(randIndex
, 1);
218 // King position is now fixed,
219 const kingPos
= positions
[0];
221 // Finally put the shuffled pieces in the board array
222 pieces
[c
][knight1Pos
] = "n";
223 pieces
[c
][bishop1Pos
] = "b";
224 pieces
[c
][queenPos
] = "q";
225 pieces
[c
][marshallPos
] = "m";
226 pieces
[c
][cardinalPos
] = "c";
227 pieces
[c
][kingPos
] = "k";
228 pieces
[c
][bishop2Pos
] = "b";
229 pieces
[c
][knight2Pos
] = "n";
232 "r8r/1" + pieces
["b"].join("") + "1/" +
233 "pppppppppp/91/91/91/91/PPPPPPPPPP/" +
234 "1" + pieces
["w"].join("").toUpperCase() + "1/R8R" +