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