1 import { ChessRules
, Move
, PiPo
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { randInt
, sample
} from "@/utils/alea";
5 export class ShakoRules
extends ChessRules
{
7 static get PawnSpecs() {
12 initShift: { w: 2, b: 2 },
14 ChessRules
.PawnSpecs
.promotions
.concat([V
.ELEPHANT
, V
.CANNON
])
19 static get ELEPHANT() {
28 return ChessRules
.PIECES
.concat([V
.ELEPHANT
, V
.CANNON
]);
32 const prefix
= [V
.ELEPHANT
, V
.CANNON
].includes(b
[1]) ? "Shako/" : "";
56 return { x: 10, y: 10};
59 getPotentialMovesFrom([x
, y
]) {
60 switch (this.getPiece(x
, y
)) {
62 return this.getPotentialElephantMoves([x
, y
]);
64 return this.getPotentialCannonMoves([x
, y
]);
66 return super.getPotentialMovesFrom([x
, y
]);
70 getPotentialElephantMoves([x
, y
]) {
71 return this.getSlideNJumpMoves([x
, y
], V
.steps
[V
.ELEPHANT
], "oneStep");
74 getPotentialCannonMoves([x
, y
]) {
75 const oppCol
= V
.GetOppCol(this.turn
);
77 // Look in every direction until an obstacle (to jump) is met
78 for (const step
of V
.steps
[V
.ROOK
]) {
81 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
82 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
86 // Then, search for an enemy
89 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
93 if (V
.OnBoard(i
, j
) && this.getColor(i
, j
) == oppCol
)
94 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
99 getCastleMoves([x
, y
]) {
100 const finalSquares
= [
104 return super.getCastleMoves([x
, y
], finalSquares
);
107 isAttacked(sq
, color
) {
109 super.isAttacked(sq
, color
) ||
110 this.isAttackedByElephant(sq
, color
) ||
111 this.isAttackedByCannon(sq
, color
)
115 isAttackedByElephant(sq
, color
) {
117 this.isAttackedBySlideNJump(
118 sq
, color
, V
.ELEPHANT
, V
.steps
[V
.ELEPHANT
], "oneStep"
123 isAttackedByCannon([x
, y
], color
) {
124 // Reversed process: is there an obstacle in line,
125 // and a cannon next in the same line?
126 for (const step
of V
.steps
[V
.ROOK
]) {
127 let [i
, j
] = [x
+step
[0], y
+step
[1]];
128 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
132 if (V
.OnBoard(i
, j
)) {
133 // Keep looking in this direction
136 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
142 this.getPiece(i
, j
) == V
.CANNON
&&
143 this.getColor(i
, j
) == color
152 updateCastleFlags(move, piece
) {
153 const c
= V
.GetOppCol(this.turn
);
154 const firstRank
= (c
== "w" ? V
.size
.x
- 2 : 1);
155 // Update castling flags if rooks are moved
156 const oppCol
= this.turn
;
157 const oppFirstRank
= V
.size
.x
- 1 - firstRank
;
159 this.castleFlags
[c
] = [V
.size
.y
, V
.size
.y
];
161 move.start
.x
== firstRank
&& //our rook moves?
162 this.castleFlags
[c
].includes(move.start
.y
)
164 const flagIdx
= (move.start
.y
== this.castleFlags
[c
][0] ? 0 : 1);
165 this.castleFlags
[c
][flagIdx
] = V
.size
.y
;
167 // NOTE: not "else if" because a rook could take an opposing rook
169 move.end
.x
== oppFirstRank
&& //we took opponent rook?
170 this.castleFlags
[oppCol
].includes(move.end
.y
)
172 const flagIdx
= (move.end
.y
== this.castleFlags
[oppCol
][0] ? 0 : 1);
173 this.castleFlags
[oppCol
][flagIdx
] = V
.size
.y
;
177 static get VALUES() {
178 return Object
.assign(
184 static get SEARCH_DEPTH() {
188 static GenRandInitFen(randomness
) {
189 if (randomness
== 0) {
191 "c8c/ernbqkbnre/pppppppppp/91/91/91/91/PPPPPPPPPP/ERNBQKBNRE/C8C " +
196 let pieces
= { w: new Array(10), b: new Array(10) };
198 // Shuffle pieces on second (and before-last rank if randomness == 2)
199 for (let c
of ["w", "b"]) {
200 if (c
== 'b' && randomness
== 1) {
201 pieces
['b'] = pieces
['w'];
206 let positions
= ArrayFun
.range(10);
208 // Get random squares for bishops + elephants
209 const be1Pos
= sample([0, 2, 4, 6, 8], 2);
210 const be2Pos
= sample([1, 3, 5, 7, 9], 2);
211 const bishop1Pos
= be1Pos
[0];
212 const bishop2Pos
= be2Pos
[0];
213 const elephant1Pos
= be1Pos
[1];
214 const elephant2Pos
= be2Pos
[1];
215 // Remove chosen squares
216 (be1Pos
.concat(be2Pos
)).sort((x
, y
) => y
- x
).forEach(pos
=> {
217 positions
.splice(pos
, 1);
220 let randIndex
= randInt(6);
221 const knight1Pos
= positions
[randIndex
];
222 positions
.splice(randIndex
, 1);
223 randIndex
= randInt(5);
224 const knight2Pos
= positions
[randIndex
];
225 positions
.splice(randIndex
, 1);
227 randIndex
= randInt(4);
228 const queenPos
= positions
[randIndex
];
229 positions
.splice(randIndex
, 1);
231 const rook1Pos
= positions
[0];
232 const kingPos
= positions
[1];
233 const rook2Pos
= positions
[2];
235 pieces
[c
][elephant1Pos
] = "e";
236 pieces
[c
][rook1Pos
] = "r";
237 pieces
[c
][knight1Pos
] = "n";
238 pieces
[c
][bishop1Pos
] = "b";
239 pieces
[c
][queenPos
] = "q";
240 pieces
[c
][kingPos
] = "k";
241 pieces
[c
][bishop2Pos
] = "b";
242 pieces
[c
][knight2Pos
] = "n";
243 pieces
[c
][rook2Pos
] = "r";
244 pieces
[c
][elephant2Pos
] = "e";
245 flags
+= V
.CoordToColumn(rook1Pos
) + V
.CoordToColumn(rook2Pos
);
247 // Add turn + flags + enpassant
249 "c8c/" + pieces
["b"].join("") +
250 "/pppppppppp/91/91/91/91/PPPPPPPPPP/" +
251 pieces
["w"].join("").toUpperCase() + "/C8C" +
252 " w 0 " + flags
+ " -"