Commit | Line | Data |
---|---|---|
2a0672a9 BA |
1 | import { ChessRules } from "@/base_rules"; |
2 | import { randInt } from "@/utils/alea"; | |
3 | ||
2a0672a9 BA |
4 | export class RefusalRules extends ChessRules { |
5 | ||
6 | static IsGoodFen(fen) { | |
7 | if (!ChessRules.IsGoodFen(fen)) return false; | |
8 | if (!V.ParseFen(fen).lastMove) return false; | |
9 | return true; | |
10 | } | |
11 | ||
12 | static ParseFen(fen) { | |
13 | return Object.assign( | |
14 | { lastMove: fen.split(" ")[5] }, | |
15 | ChessRules.ParseFen(fen) | |
16 | ); | |
17 | } | |
18 | ||
19 | getFen() { | |
20 | const L = this.lastMove.length; | |
21 | const lm = this.lastMove[L-1]; | |
22 | return super.getFen() + " " + JSON.stringify(lm); | |
23 | } | |
24 | ||
25 | // NOTE: with this variant's special rule, | |
26 | // some extra repetitions could be detected... TODO (...) | |
27 | ||
28 | static GenRandInitFen(randomness) { | |
29 | return ChessRules.GenRandInitFen(randomness) + " null"; | |
30 | } | |
31 | ||
32 | setOtherVariables(fen) { | |
33 | super.setOtherVariables(fen); | |
34 | this.lastMove = [JSON.parse(V.ParseFen(fen).lastMove)]; //may be null | |
35 | } | |
36 | ||
37 | canIplay(side, [x, y]) { | |
38 | if (super.canIplay(side, [x, y])) return true; | |
39 | if (this.turn != side) return false; | |
40 | // Check if playing last move, reversed: | |
41 | const L = this.lastMove.length; | |
42 | const lm = this.lastMove[L-1]; | |
43 | return (!!lm && !lm.noRef && x == lm.end.x && y == lm.end.y); | |
44 | } | |
45 | ||
46 | getPotentialMovesFrom([x, y]) { | |
47 | if (this.getColor(x, y) != this.turn) { | |
48 | const L = this.lastMove.length; | |
49 | const lm = this.lastMove[L-1]; | |
2da551a3 BA |
50 | const beforeLastRank = (this.turn == 'w' ? 1 : 6); |
51 | if ( | |
52 | !!lm && !lm.noRef && x == lm.end.x && y == lm.end.y && | |
53 | (this.getPiece(x, y) != V.PAWN || x != beforeLastRank) | |
54 | ) { | |
2a0672a9 BA |
55 | let revLm = JSON.parse(JSON.stringify(lm)); |
56 | let tmp = revLm.appear; | |
57 | revLm.appear = revLm.vanish; | |
58 | revLm.vanish = tmp; | |
59 | tmp = revLm.start; | |
60 | revLm.start = revLm.end; | |
61 | revLm.end = tmp; | |
62 | return [revLm]; | |
63 | } | |
64 | return []; | |
65 | } | |
66 | return super.getPotentialMovesFrom([x, y]); | |
67 | } | |
68 | ||
69 | // NOTE: do not take refusal move into account here (two own moves) | |
70 | atLeastTwoMoves() { | |
71 | let movesCounter = 0; | |
72 | const color = this.turn; | |
73 | for (let i = 0; i < V.size.x; i++) { | |
74 | for (let j = 0; j < V.size.y; j++) { | |
75 | if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == color) { | |
76 | const moves = this.getPotentialMovesFrom([i, j]); | |
77 | for (let m of moves) { | |
78 | if (m.vanish[0].c == color && this.filterValid([m]).length > 0) { | |
79 | movesCounter++; | |
80 | if (movesCounter >= 2) return true; | |
81 | } | |
82 | } | |
83 | } | |
84 | } | |
85 | } | |
86 | return false; | |
87 | } | |
88 | ||
89 | filterValid(moves) { | |
90 | if (moves.length == 0) return []; | |
91 | const color = this.turn; | |
92 | const L = this.lastMove.length; | |
93 | const lm = this.lastMove[L-1]; | |
94 | return moves.filter(m => { | |
95 | if ( | |
96 | !!lm && !!lm.refusal && | |
97 | m.start.x == lm.end.x && m.start.y == lm.end.y && | |
2da551a3 BA |
98 | m.end.x == lm.start.x && m.end.y == lm.start.y && |
99 | (m.vanish[0].p != V.PAWN || m.appear[0].p == lm.vanish[0].p) | |
2a0672a9 BA |
100 | ) { |
101 | return false; | |
102 | } | |
103 | // NOTE: not using this.play()/undo() ==> infinite loop | |
104 | V.PlayOnBoard(this.board, m); | |
105 | if (m.appear[0].p == V.KING) | |
106 | this.kingPos[m.appear[0].c] = [m.appear[0].x, m.appear[0].y]; | |
107 | const res = !this.underCheck(color); | |
108 | V.UndoOnBoard(this.board, m); | |
109 | if (m.vanish[0].p == V.KING) | |
110 | this.kingPos[m.vanish[0].c] = [m.vanish[0].x, m.vanish[0].y]; | |
111 | return res; | |
112 | }); | |
113 | } | |
114 | ||
115 | prePlay(move) { | |
116 | const L = this.lastMove.length; | |
117 | const lm = this.lastMove[L-1]; | |
118 | if ( | |
119 | // My previous move was already refused? | |
120 | (!!lm && this.getColor(lm.end.x, lm.end.y) == this.turn) || | |
121 | // I've only one move available? | |
122 | !this.atLeastTwoMoves() | |
123 | ) { | |
124 | move.noRef = true; | |
125 | } | |
126 | // NOTE: refusal could be recomputed, but, it's easier like this | |
127 | if (move.vanish[0].c != this.turn) move.refusal = true; | |
128 | } | |
129 | ||
130 | getEpSquare(move) { | |
131 | if (!move.refusal) return super.getEpSquare(move); | |
132 | return null; //move refusal | |
133 | } | |
134 | ||
135 | postPlay(move) { | |
136 | if (!move.refusal) super.postPlay(move); | |
137 | else { | |
138 | const L = this.lastMove.length; | |
139 | const lm = this.lastMove[L-1]; | |
140 | this.disaggregateFlags(JSON.parse(lm.flags)); | |
141 | } | |
142 | // NOTE: explicitely give fields, because some are assigned in BaseGame | |
143 | let mvInLm = { | |
144 | start: move.start, | |
145 | end: move.end, | |
146 | appear: move.appear, | |
147 | vanish: move.vanish, | |
148 | flags: move.flags | |
149 | }; | |
150 | if (!!move.noRef) mvInLm.noRef = true; | |
151 | if (!!move.refusal) mvInLm.refusal = true; | |
152 | this.lastMove.push(mvInLm); | |
153 | } | |
154 | ||
155 | postUndo(move) { | |
156 | if (!move.refusal) super.postUndo(move); | |
157 | this.lastMove.pop(); | |
158 | } | |
159 | ||
160 | getAllPotentialMoves() { | |
161 | const color = this.turn; | |
162 | const L = this.lastMove.length; | |
163 | const lm = this.lastMove[L-1]; | |
164 | let potentialMoves = []; | |
165 | for (let i = 0; i < V.size.x; i++) { | |
166 | for (let j = 0; j < V.size.y; j++) { | |
167 | if ( | |
168 | this.board[i][j] != V.EMPTY && | |
169 | ( | |
170 | this.getColor(i, j) == color || | |
171 | // Add move refusal: | |
172 | (!!lm && lm.end.x == i && lm.end.y == j) | |
173 | ) | |
174 | ) { | |
175 | Array.prototype.push.apply( | |
176 | potentialMoves, | |
177 | this.getPotentialMovesFrom([i, j]) | |
178 | ); | |
179 | } | |
180 | } | |
181 | } | |
182 | return potentialMoves; | |
183 | } | |
184 | ||
185 | getComputerMove() { | |
186 | // Just play at random for now... (TODO?) | |
187 | // Refuse last move with odds 1/3. | |
188 | const moves = this.getAllValidMoves(); | |
189 | const refusal = moves.find(m => m.vanish[0].c != this.turn); | |
190 | if (!!refusal) { | |
191 | if (Math.random() <= 0.33) return refusal; | |
192 | const others = moves.filter(m => m.vanish[0].c == this.turn); | |
193 | return others[randInt(others.length)]; | |
194 | } | |
195 | else return moves[randInt(moves.length)]; | |
196 | } | |
197 | ||
198 | getNotation(move) { | |
199 | if (move.vanish[0].c != this.turn) return "Refuse"; | |
200 | return super.getNotation(move); | |
201 | } | |
202 | ||
203 | }; |