1 import ChessRules
from "/base_rules.js";
2 import Move
from "/utils/Move.js";
3 import PiPo
from "/utils/PiPo.js";
5 export default class AbstractSpecialCaptureRules
extends ChessRules
{
7 // Wouldn't make sense:
13 return Object
.assign({},
16 '+': {"class": "push-action"},
17 '-': {"class": "pull-action"}
22 // Modify capturing moves among listed pincer moves
23 addPincerCaptures(moves
, byChameleon
) {
24 const steps
= this.pieces()['p'].moves
[0].steps
;
25 const color
= this.turn
;
26 const oppCol
= C
.GetOppCol(color
);
28 if (byChameleon
&& m
.start
.x
!= m
.end
.x
&& m
.start
.y
!= m
.end
.y
)
29 // Chameleon not moving as pawn
31 // Try capturing in every direction
32 for (let step
of steps
) {
33 const sq2
= [m
.end
.x
+ 2 * step
[0], this.getY(m
.end
.y
+ 2 * step
[1])];
35 this.onBoard(sq2
[0], sq2
[1]) &&
36 this.board
[sq2
[0]][sq2
[1]] != "" &&
37 this.getColor(sq2
[0], sq2
[1]) == color
40 const sq1
= [m
.end
.x
+ step
[0], this.getY(m
.end
.y
+ step
[1])];
42 this.board
[sq1
[0]][sq1
[1]] != "" &&
43 this.getColor(sq1
[0], sq1
[1]) == oppCol
45 const piece1
= this.getPiece(sq1
[0], sq1
[1]);
46 if (!byChameleon
|| piece1
== 'p') {
62 addCoordinatorCaptures(moves
, byChameleon
) {
63 const color
= this.turn
;
64 const oppCol
= V
.GetOppCol(color
);
65 const kp
= this.searchKingPos(color
)[0];
67 // Check piece-king rectangle (if any) corners for enemy pieces
68 if (m
.end
.x
== kp
[0] || m
.end
.y
== kp
[1])
69 return; //"flat rectangle"
70 const corner1
= [m
.end
.x
, kp
[1]];
71 const corner2
= [kp
[0], m
.end
.y
];
72 for (let [i
, j
] of [corner1
, corner2
]) {
73 if (this.board
[i
][j
] != "" && this.getColor(i
, j
) == oppCol
) {
74 const piece
= this.getPiece(i
, j
);
75 if (!byChameleon
|| piece
== 'r') {
90 getLeaperCaptures([x
, y
], byChameleon
, onlyOne
) {
91 // Look in every direction for captures
92 const steps
= this.pieces()['r'].moves
[0].steps
;
93 const color
= this.turn
;
94 const oppCol
= C
.GetOppCol(color
);
96 outerLoop: for (let step
of steps
) {
97 let [i
, j
] = [x
+ step
[0], this.getY(y
+ step
[1])];
98 while (this.onBoard(i
, j
) && this.board
[i
][j
] == "")
99 [i
, j
] = [i
+ step
[0], this.getY(j
+ step
[1])];
101 !this.onBoard(i
, j
) ||
102 this.getColor(i
, j
) == color
||
103 (byChameleon
&& this.getPiece(i
, j
) != 'n')
105 continue; //nothing to eat
109 // Found something (more) to eat:
111 new PiPo({x: i
, y: j
, c: oppCol
, p: this.getPiece(i
, j
)}));
112 [i
, j
] = [i
+ step
[0], this.getY(j
+ step
[1])];
113 while (this.onBoard(i
, j
) && this.board
[i
][j
] == "") {
114 let mv
= this.getBasicMove([x
, y
], [i
, j
]);
115 Array
.prorotype
.push
.apply(mv
.vanish
, vanished
);
117 [i
, j
] = [i
+ step
[0], this.getY(j
+ step
[1])];
121 !this.onBoard(i
, j
) ||
122 this.getColor(i
, j
) == color
||
123 (byChameleon
&& this.getPiece(i
, j
) != 'n')
133 getChameleonCaptures(moves
, pushPullType
, onlyOneJump
) {
134 const [x
, y
] = [moves
[0].start
.x
, moves
[0].start
.y
];
135 moves
= moves
.concat(
136 this.getKnightCaptures([x
, y
], "asChameleon", onlyOneJump
));
137 // No "king capture" because king cannot remain under check
138 this.addPincerCaptures(moves
, "asChameleon");
139 this.addCoordinatorCaptures(moves
, "asChameleon");
140 this.addPushmePullyouCaptures(moves
, "asChameleon", pushPullType
);
141 // Post-processing: merge similar moves, concatenating vanish arrays
142 let mergedMoves
= {};
144 const key
= m
.end
.x
+ this.size
.x
* m
.end
.y
;
145 if (!mergedMoves
[key
])
146 mergedMoves
[key
] = m
;
148 for (let i
= 1; i
< m
.vanish
.length
; i
++)
149 mergedMoves
[key
].vanish
.push(m
.vanish
[i
]);
152 return Object
.values(mergedMoves
);
155 // type: nothing (freely, capture all), or pull or push, or "exclusive"
156 addPushmePullyouCaptures(moves
, byChameleon
, type
) {
157 if (moves
.length
== 0)
159 const [sx
, sy
] = [moves
[0].start
.x
, moves
[0].start
.y
];
160 const adjacentSteps
= this.pieces()['r'].moves
[0].steps
;
161 let capturingPullDir
= {};
162 const color
= this.turn
;
163 const oppCol
= C
.GetOppCol(color
);
164 if (type
!= "push") {
165 adjacentSteps
.forEach(step
=> {
166 const [bi
, bj
] = [sx
- step
[0], this.getY(sy
- step
[1])];
168 this.onBoard(bi
, bj
) &&
169 this.board
[bi
][bj
] != "" &&
170 this.getColor(bi
, bj
) == oppCol
&&
171 (!byChameleon
|| this.getPiece(bi
, bj
) == 'q')
173 capturingPullDir
[step
[0] + "." + step
[1]] = true;
178 const [ex
, ey
] = [m
.end
.x
, m
.end
.y
];
180 ex
!= x
? (ex
- x
) / Math
.abs(ex
- x
) : 0,
181 ey
!= y
? (ey
- y
) / Math
.abs(ey
- y
) : 0
183 let vanishPull
, vanishPush
;
184 if (type
!= "pull") {
185 const [fi
, fj
] = [ex
+ step
[0], this.getY(ey
+ step
[1])];
187 this.onBoard(fi
, fj
) &&
188 this.board
[fi
][fj
] != "" &&
189 this.getColor(bi
, bj
) == oppCol
&&
190 (!byChameleon
|| this.getPiece(fi
, fj
) == 'q')
193 new PiPo({x: fi
, y: fj
, p: this.getPiece(fi
, fj
), c: oppCol
});
196 if (capturingPullDir
[step
[0] + "." + step
[1]]) {
197 const [bi
, bj
] = [x
- step
[0], this.getY(y
- step
[1])];
199 new PiPo({x: bi
, y: bj
, p: this.getPiece(bi
, bj
), c: oppCol
});
201 if (vanishPull
&& vanishPush
&& type
== "exclusive") {
202 // Create a new move for push action (cannot play both)
203 let newMove
= JSON
.parse(JSON
.stringify(m
));
204 newMove
.vanish
.push(vanishPush
);
205 newMove
.choice
= '+';
207 m
.vanish
.push(vanishPull
);
212 m
.vanish
.push(vanishPull
);
214 m
.vanish
.push(vanishPush
);
219 underAttack([x
, y
], oppCol
) {
220 // Generate all potential opponent moves, check if king captured.
221 // TODO: do it more efficiently.
222 const color
= this.getColor(x
, y
);
223 for (let i
= 0; i
< this.size
.x
; i
++) {
224 for (let j
= 0; j
< this.size
.y
; j
++) {
226 this.board
[i
][j
] != "" && this.getColor(i
, j
) == oppCol
&&
227 this.getPotentialMovesFrom([i
, j
]).some(m
=> {
229 m
.vanish
.length
>= 2 &&
230 [1, m
.vanish
.length
- 1].some(k
=> m
.vanish
[k
].p
== 'k')