1 import { ChessRules
} from "@/base_rules";
3 export class XiangqiRules
extends ChessRules
{
5 static get Monochrome() {
9 static get Notoodark() {
15 // Draw all inter-squares lines, shifted:
16 for (let i
= 0; i
< V
.size
.x
; i
++)
17 lines
.push([[i
+0.5, 0.5], [i
+0.5, V
.size
.y
-0.5]]);
18 for (let j
= 0; j
< V
.size
.y
; j
++)
19 lines
.push([[0.5, j
+0.5], [V
.size
.x
-0.5, j
+0.5]]);
21 lines
.push([[0.5, 3.5], [2.5, 5.5]]);
22 lines
.push([[0.5, 5.5], [2.5, 3.5]]);
23 lines
.push([[9.5, 3.5], [7.5, 5.5]]);
24 lines
.push([[9.5, 5.5], [7.5, 3.5]]);
26 lines
.push([[4.5, 0.5], [5.5, 8.5]]);
27 lines
.push([[5.5, 0.5], [4.5, 8.5]]);
31 static get HasFlags() {
35 static get HasEnpassant() {
39 static get ELEPHANT() {
47 static get ADVISOR() {
52 return [V
.PAWN
, V
.ROOK
, V
.KNIGHT
, V
.ELEPHANT
, V
.ADVISOR
, V
.KING
, V
.CANNON
];
56 return "Xiangqi/" + b
;
60 return { x: 10, y: 9};
63 getPotentialMovesFrom(sq
) {
64 switch (this.getPiece(sq
[0], sq
[1])) {
65 case V
.PAWN: return this.getPotentialPawnMoves(sq
);
66 case V
.ROOK: return super.getPotentialRookMoves(sq
);
67 case V
.KNIGHT: return this.getPotentialKnightMoves(sq
);
68 case V
.ELEPHANT: return this.getPotentialElephantMoves(sq
);
69 case V
.ADVISOR: return this.getPotentialAdvisorMoves(sq
);
70 case V
.KING: return this.getPotentialKingMoves(sq
);
71 case V
.CANNON: return this.getPotentialCannonMoves(sq
);
73 return []; //never reached
76 getPotentialPawnMoves([x
, y
]) {
77 const c
= this.getColor(x
, y
);
78 const shiftX
= (c
== 'w' ? -1 : 1);
79 const crossedRiver
= (c
== 'w' && x
<= 4 || c
== 'b' && x
>= 5);
80 const lastRank
= (c
== 'w' && x
== 0 || c
== 'b' && x
== 9);
82 if (!lastRank
) steps
.push([shiftX
, 0]);
84 if (y
> 0) steps
.push([0, -1]);
85 if (y
< 9) steps
.push([0, 1]);
87 return super.getSlideNJumpMoves([x
, y
], steps
, "oneStep");
90 knightStepsFromRookStep(step
) {
91 if (step
[0] == 0) return [ [1, 2*step
[1]], [-1, 2*step
[1]] ];
92 return [ [2*step
[0], 1], [2*step
[0], -1] ];
95 getPotentialKnightMoves([x
, y
]) {
97 for (let rookStep
of ChessRules
.steps
[V
.ROOK
]) {
98 const [i
, j
] = [x
+ rookStep
[0], y
+ rookStep
[1]];
99 if (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
100 Array
.prototype.push
.apply(steps
,
101 // These moves might be impossible, but need to be checked:
102 this.knightStepsFromRookStep(rookStep
));
105 return super.getSlideNJumpMoves([x
, y
], steps
, "oneStep");
108 getPotentialElephantMoves([x
, y
]) {
110 const c
= this.getColor(x
, y
);
111 for (let bishopStep
of ChessRules
.steps
[V
.BISHOP
]) {
112 const [i
, j
] = [x
+ bishopStep
[0], y
+ bishopStep
[1]];
113 if (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
114 const [newX
, newY
] = [x
+ 2*bishopStep
[0], y
+ 2*bishopStep
[1]];
115 if ((c
== 'w' && newX
>= 5) || (c
== 'b' && newX
<= 4))
116 // A priori valid (elephant don't cross the river)
117 steps
.push(bishopStep
.map(s
=> 2*s
));
118 // "out of board" checks delayed to next method
121 return super.getSlideNJumpMoves([x
, y
], steps
, "oneStep");
124 insidePalace(x
, y
, c
) {
126 (y
>= 3 && y
<= 5) &&
128 (c
== 'w' && x
>= 7) ||
134 getPotentialAdvisorMoves([x
, y
]) {
135 // Diagonal steps inside palace
137 const c
= this.getColor(x
, y
);
138 for (let s
of ChessRules
.steps
[V
.BISHOP
]) {
139 if (this.insidePalace(x
+ s
[0], y
+ s
[1], c
)) steps
.push(s
);
141 return super.getSlideNJumpMoves([x
, y
], steps
, "oneStep");
144 getPotentialKingMoves([x
, y
]) {
145 // Orthogonal steps inside palace
147 const c
= this.getColor(x
, y
);
148 for (let s
of ChessRules
.steps
[V
.ROOK
]) {
149 if (this.insidePalace(x
+ s
[0], y
+ s
[1], c
)) steps
.push(s
);
151 return super.getSlideNJumpMoves([x
, y
], steps
, "oneStep");
154 // NOTE: duplicated from Shako (TODO?)
155 getPotentialCannonMoves([x
, y
]) {
156 const oppCol
= V
.GetOppCol(this.turn
);
158 // Look in every direction until an obstacle (to jump) is met
159 for (const step
of V
.steps
[V
.ROOK
]) {
162 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
163 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
167 // Then, search for an enemy
170 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
174 if (V
.OnBoard(i
, j
) && this.getColor(i
, j
) == oppCol
)
175 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
180 // (King) Never attacked by advisor, since it stays in the palace
181 // Also, never attacked by elephants since they don't cross the river.
182 isAttacked(sq
, color
) {
184 this.isAttackedByPawn(sq
, color
) ||
185 super.isAttackedByRook(sq
, color
) ||
186 this.isAttackedByKnight(sq
, color
) ||
187 this.isAttackedByCannon(sq
, color
)
191 isAttackedByPawn([x
, y
], color
) {
192 // The pawn necessarily crossed the river (attack on king)
193 const shiftX
= (color
== 'w' ? 1 : -1); //shift from king
194 for (let s
of [[shiftX
, 0], [0, 1], [0, -1]]) {
195 const [i
, j
] = [x
+ s
[0], y
+ s
[1]];
197 this.board
[i
][j
] != V
.EMPTY
&&
198 this.getColor(i
, j
) == color
&&
199 this.getPiece(i
, j
) == V
.PAWN
207 knightStepsFromBishopStep(step
) {
208 return [ [2*step
[0], step
[1]], [step
[0], 2*step
[1]] ];
211 isAttackedByKnight([x
, y
], color
) {
212 // Check bishop steps: if empty, look continuation knight step
214 for (let s
of ChessRules
.steps
[V
.BISHOP
]) {
215 const [i
, j
] = [x
+ s
[0], y
+ s
[1]];
218 this.board
[i
][j
] == V
.EMPTY
220 Array
.prototype.push
.apply(steps
, this.knightStepsFromBishopStep(s
));
224 super.isAttackedBySlideNJump([x
, y
], color
, V
.KNIGHT
, steps
, "oneStep")
228 // NOTE: duplicated from Shako (TODO?)
229 isAttackedByCannon([x
, y
], color
) {
230 // Reversed process: is there an obstacle in line,
231 // and a cannon next in the same line?
232 for (const step
of V
.steps
[V
.ROOK
]) {
233 let [i
, j
] = [x
+step
[0], y
+step
[1]];
234 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
238 if (V
.OnBoard(i
, j
)) {
239 // Keep looking in this direction
242 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
248 this.getPiece(i
, j
) == V
.CANNON
&&
249 this.getColor(i
, j
) == color
258 static get VALUES() {
272 for (let i
= 0; i
< V
.size
.x
; i
++) {
273 for (let j
= 0; j
< V
.size
.y
; j
++) {
274 if (this.board
[i
][j
] != V
.EMPTY
) {
275 const c
= this.getColor(i
, j
);
276 const sign
= (c
== 'w' ? 1 : -1);
277 const piece
= this.getPiece(i
, j
);
278 let pieceEval
= V
.VALUES
[this.getPiece(i
, j
)];
282 (c
== 'w' && i
<= 4) ||
286 // Pawn crossed the river: higher value
289 evaluation
+= sign
* pieceEval
;
296 static GenRandInitFen() {
297 // No randomization here (TODO?)
298 return "rneakaenr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNEAKAENR w 0";