Experimental loss on repetition for Shogi and Pandemonium. Simplify Crazyhouse, with...
[vchess.git] / client / src / variants / Switching.js
1 import { ChessRules, Move, PiPo } from "@/base_rules";
2
3 export class SwitchingRules extends ChessRules {
4
5 // Build switch move between squares x1,y1 and x2,y2
6 getSwitchMove_s([x1, y1], [x2, y2]) {
7 const c = this.getColor(x1, y1); //same as color at square 2
8 const p1 = this.getPiece(x1, y1);
9 const p2 = this.getPiece(x2, y2);
10 let move = new Move({
11 appear: [
12 new PiPo({ x: x2, y: y2, c: c, p: p1 }),
13 new PiPo({ x: x1, y: y1, c: c, p: p2 })
14 ],
15 vanish: [
16 new PiPo({ x: x1, y: y1, c: c, p: p1 }),
17 new PiPo({ x: x2, y: y2, c: c, p: p2 })
18 ]
19 });
20 // Move completion: promote switched pawns (as in Magnetic)
21 const lastRank = (c == "w" ? 0 : V.size.x - 1);
22 let moves = [];
23 if ((p1 == V.PAWN && x2 == lastRank) || (p2 == V.PAWN && x1 == lastRank)) {
24 const idx = (p1 == V.PAWN ? 0 : 1);
25 move.appear[idx].p = V.ROOK;
26 moves.push(move);
27 for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN]) {
28 let cmove = JSON.parse(JSON.stringify(move));
29 cmove.appear[idx].p = piece;
30 moves.push(cmove);
31 }
32 if (idx == 1) {
33 // Swap moves[i].appear[0] and [1] for moves presentation [TODO...]
34 moves.forEach(m => {
35 let tmp = m.appear[0];
36 m.appear[0] = m.appear[1];
37 m.appear[1] = tmp;
38 });
39 }
40 }
41 else
42 // Other cases
43 moves.push(move);
44 return moves;
45 }
46
47 getPotentialMovesFrom([x,y]) {
48 let moves = super.getPotentialMovesFrom([x,y]);
49 const piece = this.getPiece(x,y);
50 const color = this.turn;
51 const oppCol = V.GetOppCol(color);
52 const kp = this.kingPos[color];
53 // Add switches (if not under check, from anything but the king)
54 if (piece != V.KING && !this.isAttacked(kp, oppCol)) {
55 const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
56 for (let step of steps) {
57 const [i, j] = [x+step[0], y+step[1]];
58 if (
59 V.OnBoard(i, j) &&
60 this.board[i][j] != V.EMPTY &&
61 this.getColor(i,j) == color &&
62 this.getPiece(i,j) != piece
63 ) {
64 const switchMove_s = this.getSwitchMove_s([x,y], [i,j]);
65 Array.prototype.push.apply(moves, switchMove_s);
66 }
67 }
68 }
69 return moves;
70 }
71
72 postPlay(move) {
73 // Did some king move?
74 move.appear.forEach(a => {
75 if (a.p == V.KING) {
76 this.kingPos[a.c] = [a.x, a.y];
77 this.castleFlags[a.c] = [V.size.y, V.size.y];
78 }
79 });
80 const firstRank = (move.vanish[0].c == 'w' ? 7 : 0);
81 for (let coords of [move.start, move.end]) {
82 if (
83 Object.keys(firstRank).includes(coords.x) &&
84 this.castleFlags[firstRank[coords.x]].includes(coords.y)
85 ) {
86 const c = firstRank[coords.x];
87 const flagIdx = (coords.y == this.castleFlags[c][0] ? 0 : 1);
88 this.castleFlags[c][flagIdx] = V.size.y;
89 }
90 }
91 }
92
93 postUndo(move) {
94 // Did some king move?
95 move.vanish.forEach(v => {
96 if (v.p == V.KING) this.kingPos[v.c] = [v.x, v.y];
97 });
98 }
99
100 static get SEARCH_DEPTH() {
101 // Branching factor is quite high
102 return 2;
103 }
104
105 getAllPotentialMoves() {
106 // Since this function is used only for computer play,
107 // remove duplicate switches:
108 return super.getAllPotentialMoves().filter(m => {
109 return (
110 m.appear.length == 1 ||
111 (m.appear[0].p == V.KING && m.appear[1].p == V.ROOK) ||
112 (m.appear[1].x <= m.vanish[1].x && m.appear[1].y <= m.vanish[1].y)
113 );
114 });
115 }
116
117 getNotation(move) {
118 if (move.appear.length == 1)
119 // Normal move
120 return super.getNotation(move);
121 if (move.appear[0].p == V.KING && move.appear[1].p == V.ROOK)
122 // Castle
123 return (move.end.y < move.start.y ? "0-0-0" : "0-0");
124 // Switch
125 return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end);
126 }
127
128 };