1 import { ChessRules
, Move
, PiPo
} from "@/base_rules";
2 import { randInt
} from "@/utils/alea";
3 import { ArrayFun
} from "@/utils/array";
5 export class AtarigoRules
extends ChessRules
{
7 static get Monochrome() {
11 static get Notoodark() {
17 // Draw all inter-squares lines, shifted:
18 for (let i
= 0; i
< V
.size
.x
; i
++)
19 lines
.push([[i
+0.5, 0.5], [i
+0.5, V
.size
.y
-0.5]]);
20 for (let j
= 0; j
< V
.size
.y
; j
++)
21 lines
.push([[0.5, j
+0.5], [V
.size
.x
-0.5, j
+0.5]]);
25 static get HasFlags() {
29 static get HasEnpassant() {
33 static get ReverseColors() {
37 static IsGoodPosition(position
) {
38 if (position
.length
== 0) return false;
39 const rows
= position
.split("/");
40 if (rows
.length
!= V
.size
.x
) return false;
41 for (let row
of rows
) {
43 for (let i
= 0; i
< row
.length
; i
++) {
44 if (row
[i
].toLowerCase() == V
.PAWN
) sumElts
++;
46 const num
= parseInt(row
[i
], 10);
47 if (isNaN(num
) || num
<= 0) return false;
51 if (sumElts
!= V
.size
.y
) return false;
56 static IsGoodFen(fen
) {
57 if (!ChessRules
.IsGoodFen(fen
)) return false;
58 const fenParsed
= V
.ParseFen(fen
);
59 // 3) Check capture "flag"
60 if (!fenParsed
.capture
|| !fenParsed
.capture
.match(/^[01]$/))
65 static ParseFen(fen
) {
66 const fenParts
= fen
.split(" ");
68 ChessRules
.ParseFen(fen
),
69 // Capture field allows to compute the score cleanly.
70 { capture: fenParts
[3] }
75 return { x: 12, y: 12 };
78 static GenRandInitFen() {
79 return "93/93/93/93/93/5Pp5/5pP5/93/93/93/93/93 w 0 0";
83 return super.getFen() + " " + (this.capture
? 1 : 0);
86 setOtherVariables(fen
) {
87 this.capture
= parseInt(V
.ParseFen(fen
).capture
, 10);
102 canIplay(side
, [x
, y
]) {
103 return (side
== this.turn
&& this.board
[x
][y
] == V
.EMPTY
);
106 hoverHighlight([x
, y
], side
) {
107 if (!!side
&& side
!= this.turn
) return false;
108 return (this.board
[x
][y
] == V
.EMPTY
);
111 searchForEmptySpace([x
, y
], color
, explored
) {
112 if (explored
[x
][y
]) return false; //didn't find empty space
113 explored
[x
][y
] = true;
115 for (let s
of V
.steps
[V
.ROOK
]) {
116 const [i
, j
] = [x
+ s
[0], y
+ s
[1]];
117 if (V
.OnBoard(i
, j
)) {
118 if (this.board
[i
][j
] == V
.EMPTY
) res
= true;
119 else if (this.getColor(i
, j
) == color
)
120 res
= this.searchForEmptySpace([i
, j
], color
, explored
) || res
;
127 const color
= this.turn
;
128 const oppCol
= V
.GetOppCol(color
);
129 let move = new Move({
131 new PiPo({ x: x
, y: y
, c: color
, p: V
.PAWN
})
134 start: { x: -1, y: -1 }
136 V
.PlayOnBoard(this.board
, move); //put the stone
137 let noSuicide
= false;
139 for (let s
of V
.steps
[V
.ROOK
]) {
140 const [i
, j
] = [x
+ s
[0], y
+ s
[1]];
141 if (V
.OnBoard(i
, j
)) {
142 if (this.board
[i
][j
] == V
.EMPTY
) noSuicide
= true; //clearly
143 else if (this.getColor(i
, j
) == color
) {
144 // Free space for us = not a suicide
146 let explored
= ArrayFun
.init(V
.size
.x
, V
.size
.y
, false);
147 noSuicide
= this.searchForEmptySpace([i
, j
], color
, explored
);
151 // Free space for opponent = not a capture
152 let explored
= ArrayFun
.init(V
.size
.x
, V
.size
.y
, false);
153 const captureSomething
=
154 !this.searchForEmptySpace([i
, j
], oppCol
, explored
);
155 if (captureSomething
) {
156 for (let ii
= 0; ii
< V
.size
.x
; ii
++) {
157 for (let jj
= 0; jj
< V
.size
.y
; jj
++) {
158 if (explored
[ii
][jj
]) {
160 new PiPo({ x: ii
, y: jj
, c: oppCol
, p: V
.PAWN
})
169 V
.UndoOnBoard(this.board
, move); //remove the stone
170 if (!noSuicide
&& captures
.length
== 0) return null;
171 Array
.prototype.push
.apply(move.vanish
, captures
);
175 getPotentialMovesFrom([x
, y
]) {
176 const move = this.doClick([x
, y
]);
177 return (!move ? [] : [move]);
180 getAllPotentialMoves() {
182 for (let i
= 0; i
< V
.size
.x
; i
++) {
183 for (let j
=0; j
< V
.size
.y
; j
++) {
184 if (this.board
[i
][j
] == V
.EMPTY
) {
185 const mv
= this.doClick([i
, j
]);
186 if (!!mv
) moves
.push(mv
);
202 if (move.vanish
.length
>= 1) this.capture
= true;
206 this.capture
= false;
210 if (this.capture
) return (this.turn
== 'w' ? "0-1" : "1-0");
214 // Modified version to count liberties + find locations
215 countEmptySpaces([x
, y
], color
, explored
) {
216 if (explored
[x
][y
]) return [];
217 explored
[x
][y
] = true;
219 for (let s
of V
.steps
[V
.ROOK
]) {
220 const [i
, j
] = [x
+ s
[0], y
+ s
[1]];
221 if (V
.OnBoard(i
, j
)) {
222 if (!explored
[i
][j
] && this.board
[i
][j
] == V
.EMPTY
) {
224 explored
[i
][j
] = true; //not counting liberties twice!
226 else if (this.getColor(i
, j
) == color
)
227 res
= res
.concat(this.countEmptySpaces([i
, j
], color
, explored
));
234 const moves
= super.getAllValidMoves();
235 if (moves
.length
== 0) return null;
237 const captures
= moves
.filter(m
=> m
.vanish
.length
>= 1);
238 if (captures
.length
> 0) return captures
[randInt(captures
.length
)];
239 // Any group in immediate danger?
240 const color
= this.turn
;
241 let explored
= ArrayFun
.init(V
.size
.x
, V
.size
.y
, false);
242 for (let i
= 0; i
< V
.size
.x
; i
++) {
243 for (let j
= 0; j
< V
.size
.y
; j
++) {
245 this.board
[i
][j
] != V
.EMPTY
&&
246 this.getColor(i
, j
) == color
&&
249 // Before this search, reset liberties,
250 // because two groups might share them.
251 for (let ii
= 0; ii
< V
.size
.x
; ii
++) {
252 for (let jj
= 0; jj
< V
.size
.y
; jj
++) {
253 if (explored
[ii
][jj
] && this.board
[ii
][jj
] == V
.EMPTY
)
254 explored
[ii
][jj
] = false;
257 const liberties
= this.countEmptySpaces([i
, j
], color
, explored
);
258 if (liberties
.length
== 1) {
259 const L
= liberties
[0];
260 const toPlay
= moves
.find(m
=> m
.end
.x
== L
[0] && m
.end
.y
== L
[1]);
261 if (!!toPlay
) return toPlay
;
266 // At this point, pick a random move not far from current stones (TODO)
267 const candidates
= moves
.filter(m
=> {
268 const steps
= V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]);
271 const [i
, j
] = [m
.end
.x
+ s
[0], m
.end
.y
+ s
[1]];
274 this.board
[i
][j
] != V
.EMPTY
&&
275 this.getColor(i
, j
) == color
280 if (candidates
.length
> 0) return candidates
[randInt(candidates
.length
)];
281 return moves
[randInt(moves
.length
)];
285 return V
.CoordsToSquare(move.end
);