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
{
11 static get Monochrome() {
15 static get Notoodark() {
21 // Draw all inter-squares lines, shifted:
22 for (let i
= 0; i
< V
.size
.x
; i
++)
23 lines
.push([[i
+0.5, 0.5], [i
+0.5, V
.size
.y
-0.5]]);
24 for (let j
= 0; j
< V
.size
.y
; j
++)
25 lines
.push([[0.5, j
+0.5], [V
.size
.x
-0.5, j
+0.5]]);
29 static get HasFlags() {
33 static get HasEnpassant() {
37 static get ReverseColors() {
41 static IsGoodPosition(position
) {
42 if (position
.length
== 0) return false;
43 const rows
= position
.split("/");
44 if (rows
.length
!= V
.size
.x
) return false;
45 for (let row
of rows
) {
47 for (let i
= 0; i
< row
.length
; i
++) {
48 if (row
[i
].toLowerCase() == V
.PAWN
) sumElts
++;
50 const num
= parseInt(row
[i
], 10);
51 if (isNaN(num
) || num
<= 0) return false;
55 if (sumElts
!= V
.size
.y
) return false;
60 static IsGoodFen(fen
) {
61 if (!ChessRules
.IsGoodFen(fen
)) return false;
62 const fenParsed
= V
.ParseFen(fen
);
63 // 3) Check capture "flag"
64 if (!fenParsed
.capture
|| !fenParsed
.capture
.match(/^[01]$/))
69 static ParseFen(fen
) {
70 const fenParts
= fen
.split(" ");
72 ChessRules
.ParseFen(fen
),
73 // Capture field allows to compute the score cleanly.
74 { capture: fenParts
[3] }
79 return { x: 12, y: 12 };
82 static GenRandInitFen() {
83 return "93/93/93/93/93/5Pp5/5pP5/93/93/93/93/93 w 0 0";
87 return super.getFen() + " " + (this.capture
? 1 : 0);
90 setOtherVariables(fen
) {
91 this.capture
= parseInt(V
.ParseFen(fen
).capture
, 10);
106 canIplay(side
, [x
, y
]) {
107 return (side
== this.turn
&& this.board
[x
][y
] == V
.EMPTY
);
110 hoverHighlight([x
, y
], side
) {
111 if (!!side
&& side
!= this.turn
) return false;
112 return (this.board
[x
][y
] == V
.EMPTY
);
115 searchForEmptySpace([x
, y
], color
, explored
) {
116 if (explored
[x
][y
]) return false; //didn't find empty space
117 explored
[x
][y
] = true;
119 for (let s
of V
.steps
[V
.ROOK
]) {
120 const [i
, j
] = [x
+ s
[0], y
+ s
[1]];
121 if (V
.OnBoard(i
, j
)) {
122 if (this.board
[i
][j
] == V
.EMPTY
) res
= true;
123 else if (this.getColor(i
, j
) == color
)
124 res
= this.searchForEmptySpace([i
, j
], color
, explored
) || res
;
131 const color
= this.turn
;
132 const oppCol
= V
.GetOppCol(color
);
133 let move = new Move({
135 new PiPo({ x: x
, y: y
, c: color
, p: V
.PAWN
})
138 start: { x: -1, y: -1 }
140 V
.PlayOnBoard(this.board
, move); //put the stone
141 let noSuicide
= false;
143 for (let s
of V
.steps
[V
.ROOK
]) {
144 const [i
, j
] = [x
+ s
[0], y
+ s
[1]];
145 if (V
.OnBoard(i
, j
)) {
146 if (this.board
[i
][j
] == V
.EMPTY
) noSuicide
= true; //clearly
147 else if (this.getColor(i
, j
) == color
) {
148 // Free space for us = not a suicide
150 let explored
= ArrayFun
.init(V
.size
.x
, V
.size
.y
, false);
151 noSuicide
= this.searchForEmptySpace([i
, j
], color
, explored
);
155 // Free space for opponent = not a capture
156 let explored
= ArrayFun
.init(V
.size
.x
, V
.size
.y
, false);
157 const captureSomething
=
158 !this.searchForEmptySpace([i
, j
], oppCol
, explored
);
159 if (captureSomething
) {
160 for (let ii
= 0; ii
< V
.size
.x
; ii
++) {
161 for (let jj
= 0; jj
< V
.size
.y
; jj
++) {
162 if (explored
[ii
][jj
]) {
164 new PiPo({ x: ii
, y: jj
, c: oppCol
, p: V
.PAWN
})
173 V
.UndoOnBoard(this.board
, move); //remove the stone
174 if (!noSuicide
&& captures
.length
== 0) return null;
175 Array
.prototype.push
.apply(move.vanish
, captures
);
179 getPotentialMovesFrom([x
, y
]) {
180 const move = this.doClick([x
, y
]);
181 return (!move ? [] : [move]);
184 getAllPotentialMoves() {
186 for (let i
= 0; i
< V
.size
.x
; i
++) {
187 for (let j
=0; j
< V
.size
.y
; j
++) {
188 if (this.board
[i
][j
] == V
.EMPTY
) {
189 const mv
= this.doClick([i
, j
]);
190 if (!!mv
) moves
.push(mv
);
206 if (move.vanish
.length
>= 1) this.capture
= true;
210 this.capture
= false;
214 if (this.capture
) return (this.turn
== 'w' ? "0-1" : "1-0");
218 // Modified version to count liberties + find locations
219 countEmptySpaces([x
, y
], color
, explored
) {
220 if (explored
[x
][y
]) return [];
221 explored
[x
][y
] = true;
223 for (let s
of V
.steps
[V
.ROOK
]) {
224 const [i
, j
] = [x
+ s
[0], y
+ s
[1]];
225 if (V
.OnBoard(i
, j
)) {
226 if (!explored
[i
][j
] && this.board
[i
][j
] == V
.EMPTY
) {
228 explored
[i
][j
] = true; //not counting liberties twice!
230 else if (this.getColor(i
, j
) == color
)
231 res
= res
.concat(this.countEmptySpaces([i
, j
], color
, explored
));
238 const moves
= super.getAllValidMoves();
239 if (moves
.length
== 0) return null;
241 const captures
= moves
.filter(m
=> m
.vanish
.length
>= 1);
242 if (captures
.length
> 0) return captures
[randInt(captures
.length
)];
243 // Any group in immediate danger?
244 const color
= this.turn
;
245 let explored
= ArrayFun
.init(V
.size
.x
, V
.size
.y
, false);
246 for (let i
= 0; i
< V
.size
.x
; i
++) {
247 for (let j
= 0; j
< V
.size
.y
; j
++) {
249 this.board
[i
][j
] != V
.EMPTY
&&
250 this.getColor(i
, j
) == color
&&
253 // Before this search, reset liberties,
254 // because two groups might share them.
255 for (let ii
= 0; ii
< V
.size
.x
; ii
++) {
256 for (let jj
= 0; jj
< V
.size
.y
; jj
++) {
257 if (explored
[ii
][jj
] && this.board
[ii
][jj
] == V
.EMPTY
)
258 explored
[ii
][jj
] = false;
261 const liberties
= this.countEmptySpaces([i
, j
], color
, explored
);
262 if (liberties
.length
== 1) {
263 const L
= liberties
[0];
264 const toPlay
= moves
.find(m
=> m
.end
.x
== L
[0] && m
.end
.y
== L
[1]);
265 if (!!toPlay
) return toPlay
;
270 // At this point, pick a random move not far from current stones (TODO)
271 const candidates
= moves
.filter(m
=> {
272 const steps
= V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]);
275 const [i
, j
] = [m
.end
.x
+ s
[0], m
.end
.y
+ s
[1]];
278 this.board
[i
][j
] != V
.EMPTY
&&
279 this.getColor(i
, j
) == color
284 if (candidates
.length
> 0) return candidates
[randInt(candidates
.length
)];
285 return moves
[randInt(moves
.length
)];
289 return V
.CoordsToSquare(move.end
);