Fix Convert, start Chaining
[xogo.git] / variants / Convert / class.js
CommitLineData
3e72fcf5
BA
1import ChessRules from "/base_rules.js";
2import PiPo from "/utils/PiPo.js";
3import Move from "/utils/Move.js";
4
5export default class ConvertRules extends ChessRules {
6
3e72fcf5
BA
7 static get Options() {
8 return {
9 select: C.Options.select,
b98feb3f 10 styles: ["cylinder", "dark", "recycle", "teleport"]
3e72fcf5
BA
11 };
12 }
13
14 get hasEnpassant() {
15 return false;
16 }
17
18 setOtherVariables(fenParsed, pieceArray) {
19 super.setOtherVariables(fenParsed, pieceArray);
20 // Stack of "last move" only for intermediate chaining
b8950be5 21 this.lastMoveEnd = [];
3e72fcf5
BA
22 }
23
24 genRandInitBaseFen() {
25 const baseFen = super.genRandInitBaseFen();
26 return {
27 fen: baseFen.fen.replace("pppppppp/8", "8/pppppppp")
28 .replace("8/PPPPPPPP", "PPPPPPPP/8"),
29 o: baseFen.o
30 };
31 }
32
33 getBasicMove([sx, sy], [ex, ey], tr) {
34 const L = this.lastMoveEnd.length;
b98feb3f 35 const piece = (L >= 1 ? this.lastMoveEnd[L-1].p : null);
3e72fcf5
BA
36 if (this.board[ex][ey] == "") {
37 if (piece && !tr)
b98feb3f 38 tr = {c: this.turn, p: piece};
3e72fcf5
BA
39 let mv = super.getBasicMove([sx, sy], [ex, ey], tr);
40 if (piece)
b98feb3f 41 mv.vanish.pop(); //end of a chain: initial piece remains
3e72fcf5
BA
42 return mv;
43 }
44 // Capture: initial, or inside a chain
b98feb3f
BA
45 const initPiece = (piece || this.getPiece(sx, sy)),
46 destPiece = this.getPiece(ex, ey);
3e72fcf5
BA
47 let mv = new Move({
48 start: {x: sx, y: sy},
49 end: {x: ex, y: ey},
50 appear: [
51 new PiPo({
52 x: ex,
53 y: ey,
b98feb3f 54 c: this.turn,
3e72fcf5
BA
55 p: (!!tr ? tr.p : initPiece)
56 })
57 ],
58 vanish: [
59 new PiPo({
60 x: ex,
61 y: ey,
b98feb3f
BA
62 c: C.GetOppTurn(this.turn),
63 p: destPiece
3e72fcf5
BA
64 })
65 ]
66 });
67 if (!piece) {
68 // Initial capture
69 mv.vanish.unshift(
70 new PiPo({
71 x: sx,
72 y: sy,
b98feb3f 73 c: this.turn,
3e72fcf5
BA
74 p: initPiece
75 })
76 );
77 }
b98feb3f 78 mv.converted = destPiece; //easier (no need to detect it)
3e72fcf5
BA
79 return mv;
80 }
81
b8950be5
BA
82 getPiece(x, y) {
83 const L = this.lastMoveEnd.length;
84 if (L >= 1 && this.lastMoveEnd[L-1].x == x && this.lastMoveEnd[L-1].y == y)
85 return this.lastMoveEnd[L-1].p;
86 return super.getPiece(x, y);
87 }
88
89 getPotentialMovesFrom([x, y], color) {
90 const L = this.lastMoveEnd.length;
91 if (
92 L >= 1 &&
93 (x != this.lastMoveEnd[L-1].x || y != this.lastMoveEnd[L-1].y)
94 ) {
95 // A capture was played: wrong square
96 return [];
97 }
98 return super.getPotentialMovesFrom([x, y], color);
99 }
100
101 underAttack_aux([x, y], color, explored) {
102 if (explored.some(sq => sq[0] == x && sq[1] == y))
103 // Start of an infinite loop: exit
104 return false;
105 explored.push([x, y]);
106 if (super.underAttack([x, y], [color]))
107 return true;
108 // Maybe indirect "chaining" attack:
109 const myColor = this.turn;
110 let res = false;
111 let toCheck = []; //check all but king (no need)
112 // Pawns:
113 const shiftToPawn = (myColor == 'w' ? -1 : 1);
114 for (let yShift of [-1, 1]) {
115 const [i, j] = [x + shiftToPawn, y + yShift];
116 if (
117 this.onBoard(i, j) &&
118 this.board[i][j] != "" &&
119 // NOTE: no need to check color (no enemy pawn can take directly)
120 this.getPiece(i, j) == 'p'
121 ) {
122 toCheck.push([i, j]);
123 }
124 }
125 // Knights:
126 this.pieces()['n'].both[0].steps.forEach(s => {
127 const [i, j] = [x + s[0], y + s[1]];
128 if (
129 this.onBoard(i, j) &&
130 this.board[i][j] != "" &&
131 this.getPiece(i, j) == 'n'
132 ) {
133 toCheck.push([i, j]);
134 }
135 });
136 // Sliders:
137 this.pieces()['q'].both[0].steps.forEach(s => {
138 let [i, j] = [x + s[0], y + s[1]];
139 while (this.onBoard(i, j) && this.board[i][j] == "") {
140 i += s[0];
141 j += s[1];
142 }
143 if (!this.onBoard(i, j))
144 return;
145 const piece = this.getPiece(i, j);
146 if (
147 piece == 'q' ||
148 (piece == 'r' && (s[0] == 0 || s[1] == 0)) ||
149 (piece == 'b' && (s[0] != 0 && s[1] != 0))
150 ) {
151 toCheck.push([i, j]);
152 }
153 });
154 for (let ij of toCheck) {
155 if (this.underAttack_aux(ij, color, explored))
156 return true;
157 }
158 return false;
159 }
160
b98feb3f 161 underAttack([x, y], [color]) {
b8950be5
BA
162 let explored = [];
163 return this.underAttack_aux([x, y], color, explored);
164 }
165
166 filterValid(moves) {
167 // No "checks" (except to forbid castle)
168 return moves;
169 }
170
171 isLastMove(move) {
b98feb3f 172 return !move.converted;
b8950be5
BA
173 }
174
175 postPlay(move) {
176 super.postPlay(move);
b98feb3f 177 if (!!move.converted) {
b8950be5
BA
178 this.lastMoveEnd.push({
179 x: move.end.x,
180 y: move.end.y,
b98feb3f 181 p: move.converted
b8950be5
BA
182 });
183 }
b98feb3f
BA
184 else
185 this.lastMoveEnd = [];
b8950be5
BA
186 }
187
3e72fcf5 188};