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