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
= moves
[0].vanish
[0].c
;
26 const oppCol
= C
.GetOppTurn(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
= moves
[0].vanish
[0].c
;
64 const oppCol
= V
.GetOppTurn(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.getColor(x
, y
);
94 const oppCol
= C
.GetOppTurn(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
.prototype.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.getLeaperCaptures([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 const [sx
, sy
] = [moves
[0].start
.x
, moves
[0].start
.y
];
158 const adjacentSteps
= this.pieces()['r'].moves
[0].steps
;
159 let capturingPullDir
= {};
160 const color
= moves
[0].vanish
[0].c
;
161 const oppCol
= C
.GetOppTurn(color
);
162 if (type
!= "push") {
163 adjacentSteps
.forEach(step
=> {
164 const [bi
, bj
] = [sx
- step
[0], this.getY(sy
- step
[1])];
166 this.onBoard(bi
, bj
) &&
167 this.board
[bi
][bj
] != "" &&
168 this.getColor(bi
, bj
) == oppCol
&&
169 (!byChameleon
|| this.getPiece(bi
, bj
) == 'q')
171 capturingPullDir
[step
[0] + "." + step
[1]] = true;
176 const [ex
, ey
] = [m
.end
.x
, m
.end
.y
];
178 ex
!= sx
? (ex
- sx
) / Math
.abs(ex
- sx
) : 0,
179 ey
!= sy
? (ey
- sy
) / Math
.abs(ey
- sy
) : 0
181 let vanishPull
, vanishPush
;
182 if (type
!= "pull") {
183 const [fi
, fj
] = [ex
+ step
[0], this.getY(ey
+ step
[1])];
185 this.onBoard(fi
, fj
) &&
186 this.board
[fi
][fj
] != "" &&
187 this.getColor(bi
, bj
) == oppCol
&&
188 (!byChameleon
|| this.getPiece(fi
, fj
) == 'q')
191 new PiPo({x: fi
, y: fj
, p: this.getPiece(fi
, fj
), c: oppCol
});
194 if (capturingPullDir
[step
[0] + "." + step
[1]]) {
195 const [bi
, bj
] = [sx
- step
[0], this.getY(sy
- step
[1])];
197 new PiPo({x: bi
, y: bj
, p: this.getPiece(bi
, bj
), c: oppCol
});
199 if (vanishPull
&& vanishPush
&& type
== "exclusive") {
200 // Create a new move for push action (cannot play both)
201 let newMove
= JSON
.parse(JSON
.stringify(m
));
202 newMove
.vanish
.push(vanishPush
);
203 newMove
.choice
= '+';
205 m
.vanish
.push(vanishPull
);
210 m
.vanish
.push(vanishPull
);
212 m
.vanish
.push(vanishPush
);
217 underAttack([x
, y
], oppCols
) {
218 // Generate all potential opponent moves, check if king captured.
219 // TODO: do it more efficiently.
220 const color
= this.getColor(x
, y
);
221 for (let i
= 0; i
< this.size
.x
; i
++) {
222 for (let j
= 0; j
< this.size
.y
; j
++) {
224 this.board
[i
][j
] != "" && oppCols
.includes(this.getColor(i
, j
)) &&
225 this.getPotentialMovesFrom([i
, j
]).some(m
=> {
227 m
.vanish
.length
>= 2 &&
228 [1, m
.vanish
.length
- 1].some(k
=> m
.vanish
[k
].p
== 'k')