1 import { ChessRules
, PiPo
, Move
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { shuffle
} from "@/utils/alea";
5 export class CircularRules
extends ChessRules
{
7 static get HasCastle() {
11 static get HasEnpassant() {
15 static get CanFlip() {
21 w: [...Array(8).fill(true)], //pawns can move 2 squares?
22 b: [...Array(8).fill(true)]
24 for (let c
of ["w", "b"]) {
25 for (let i
= 0; i
< 8; i
++)
26 this.pawnFlags
[c
][i
] = fenflags
.charAt((c
== "w" ? 0 : 8) + i
) == "1";
31 return this.pawnFlags
;
34 disaggregateFlags(flags
) {
35 this.pawnFlags
= flags
;
38 static GenRandInitFen(randomness
) {
39 if (randomness
== 0) {
40 return "8/8/pppppppp/rnbqkbnr/8/8/PPPPPPPP/RNBQKBNR " +
41 "w 0 1111111111111111";
44 let pieces
= { w: new Array(8), b: new Array(8) };
45 // Shuffle pieces on first and last rank
46 for (let c
of ["w", "b"]) {
47 if (c
== 'b' && randomness
== 1) {
48 pieces
['b'] = pieces
['w'];
52 // Get random squares for every piece, totally freely
53 let positions
= shuffle(ArrayFun
.range(8));
54 const composition
= ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
55 const rem2
= positions
[0] % 2;
56 if (rem2
== positions
[1] % 2) {
57 // Fix bishops (on different colors)
58 for (let i
=2; i
<8; i
++) {
59 if (positions
[i
] % 2 != rem2
) {
60 [positions
[1], positions
[i
]] = [positions
[i
], positions
[1]];
65 for (let i
= 0; i
< 8; i
++) pieces
[c
][positions
[i
]] = composition
[i
];
69 pieces
["b"].join("") +
71 pieces
["w"].join("").toUpperCase() +
72 // 16 flags: can pawns advance 2 squares?
73 " w 0 1111111111111111"
77 // Output basically x % 8 (circular board)
79 let res
= x
% V
.size
.x
;
85 getSlideNJumpMoves([x
, y
], steps
, oneStep
) {
87 // Don't add move twice when running on an infinite file:
88 let infiniteSteps
= {};
89 outerLoop: for (let step
of steps
) {
90 if (!!infiniteSteps
[(-step
[0]) + "." + (-step
[1])]) continue;
91 let i
= V
.ComputeX(x
+ step
[0]);
93 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
94 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
95 if (oneStep
!== undefined) continue outerLoop
;
96 i
= V
.ComputeX(i
+ step
[0]);
99 if (V
.OnBoard(i
, j
)) {
100 if (i
== x
&& j
== y
)
101 // Looped back onto initial square
102 infiniteSteps
[step
[0] + "." + step
[1]] = true;
103 else if (this.canTake([x
, y
], [i
, j
]))
104 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
110 getPotentialPawnMoves([x
, y
]) {
111 const color
= this.turn
;
113 const [sizeX
, sizeY
] = [V
.size
.x
, V
.size
.y
];
114 // All pawns go in the same direction!
116 const startRank
= color
== "w" ? sizeX
- 2 : 2;
118 // One square forward
119 const nextRow
= V
.ComputeX(x
+ shiftX
);
120 if (this.board
[nextRow
][y
] == V
.EMPTY
) {
121 moves
.push(this.getBasicMove([x
, y
], [nextRow
, y
]));
124 this.pawnFlags
[color
][y
] &&
125 this.board
[x
+ 2 * shiftX
][y
] == V
.EMPTY
128 moves
.push(this.getBasicMove([x
, y
], [x
+ 2 * shiftX
, y
]));
132 for (let shiftY
of [-1, 1]) {
135 y
+ shiftY
< sizeY
&&
136 this.board
[nextRow
][y
+ shiftY
] != V
.EMPTY
&&
137 this.canTake([x
, y
], [nextRow
, y
+ shiftY
])
139 moves
.push(this.getBasicMove([x
, y
], [nextRow
, y
+ shiftY
]));
147 const filteredMoves
= super.filterValid(moves
);
148 // If at least one full move made, everything is allowed:
149 if (this.movesCount
>= 2) return filteredMoves
;
150 // Else, forbid check:
151 const oppCol
= V
.GetOppCol(this.turn
);
152 return filteredMoves
.filter(m
=> {
154 const res
= !this.underCheck(oppCol
);
160 isAttackedByPawn([x
, y
], color
) {
161 // pawn shift is always 1 (all pawns go the same way)
162 const attackerRow
= V
.ComputeX(x
+ 1);
163 for (let i
of [-1, 1]) {
167 this.getPiece(attackerRow
, y
+ i
) == V
.PAWN
&&
168 this.getColor(attackerRow
, y
+ i
) == color
176 isAttackedBySlideNJump([x
, y
], color
, piece
, steps
, oneStep
) {
177 for (let step
of steps
) {
178 let rx
= V
.ComputeX(x
+ step
[0]),
180 while (V
.OnBoard(rx
, ry
) && this.board
[rx
][ry
] == V
.EMPTY
&& !oneStep
) {
181 rx
= V
.ComputeX(rx
+ step
[0]);
186 this.getPiece(rx
, ry
) == piece
&&
187 this.getColor(rx
, ry
) == color
196 // Return pawns flags
198 for (let c
of ["w", "b"]) {
199 for (let i
= 0; i
< 8; i
++) flags
+= this.pawnFlags
[c
][i
] ? "1" : "0";
205 super.postPlay(move);
206 const c
= move.vanish
[0].c
;
207 const secondRank
= { "w": 6, "b": 2 };
208 if (move.vanish
[0].p
== V
.PAWN
&& secondRank
[c
] == move.start
.x
)
209 // This move turns off a 2-squares pawn flag
210 this.pawnFlags
[c
][move.start
.y
] = false;
213 static get VALUES() {
224 static get SEARCH_DEPTH() {