93df8efcca29147736aee2b146f14612e250ae8b
1 import { ChessRules
, PiPo
, Move
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { randInt
, shuffle
} from "@/utils/alea";
5 export const VariantRules
= class CircularRules
extends ChessRules
{
6 static get HasEnpassant() {
10 static get CanFlip() {
16 w: [...Array(8).fill(true)], //pawns can move 2 squares?
17 b: [...Array(8).fill(true)]
19 for (let c
of ["w", "b"]) {
20 for (let i
= 0; i
< 8; i
++)
21 this.pawnFlags
[c
][i
] = fenflags
.charAt((c
== "w" ? 0 : 8) + i
) == "1";
26 return this.pawnFlags
;
29 disaggregateFlags(flags
) {
30 this.pawnFlags
= flags
;
33 static GenRandInitFen(randomness
) {
34 if (!randomness
) randomness
= 2;
36 return "8/8/pppppppp/rnbqkbnr/8/8/PPPPPPPP/RNBQKBNR w 0 1111111111111111";
38 let pieces
= { w: new Array(8), b: new Array(8) };
39 // Shuffle pieces on first and fifth rank
40 for (let c
of ["w", "b"]) {
41 if (c
== 'b' && randomness
== 1) {
42 pieces
['b'] = pieces
['w'];
46 let positions
= ArrayFun
.range(8);
48 // Get random squares for bishops
49 let randIndex
= 2 * randInt(4);
50 const bishop1Pos
= positions
[randIndex
];
51 // The second bishop must be on a square of different color
52 let randIndex_tmp
= 2 * randInt(4) + 1;
53 const bishop2Pos
= positions
[randIndex_tmp
];
54 // Remove chosen squares
55 positions
.splice(Math
.max(randIndex
, randIndex_tmp
), 1);
56 positions
.splice(Math
.min(randIndex
, randIndex_tmp
), 1);
58 // Get random squares for knights
59 randIndex
= randInt(6);
60 const knight1Pos
= positions
[randIndex
];
61 positions
.splice(randIndex
, 1);
62 randIndex
= randInt(5);
63 const knight2Pos
= positions
[randIndex
];
64 positions
.splice(randIndex
, 1);
66 // Get random square for queen
67 randIndex
= randInt(4);
68 const queenPos
= positions
[randIndex
];
69 positions
.splice(randIndex
, 1);
71 // Rooks and king positions are now fixed,
72 // because of the ordering rook-king-rook
73 const rook1Pos
= positions
[0];
74 const kingPos
= positions
[1];
75 const rook2Pos
= positions
[2];
77 // Finally put the shuffled pieces in the board array
78 pieces
[c
][rook1Pos
] = "r";
79 pieces
[c
][knight1Pos
] = "n";
80 pieces
[c
][bishop1Pos
] = "b";
81 pieces
[c
][queenPos
] = "q";
82 pieces
[c
][kingPos
] = "k";
83 pieces
[c
][bishop2Pos
] = "b";
84 pieces
[c
][knight2Pos
] = "n";
85 pieces
[c
][rook2Pos
] = "r";
89 pieces
["b"].join("") +
91 pieces
["w"].join("").toUpperCase() +
92 // 16 flags: can pawns advance 2 squares?
93 " w 0 1111111111111111"
97 // Output basically x % 8 (circular board)
99 let res
= x
% V
.size
.x
;
105 getSlideNJumpMoves([x
, y
], steps
, oneStep
) {
107 outerLoop: for (let step
of steps
) {
108 let i
= V
.ComputeX(x
+ step
[0]);
110 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
111 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
112 if (oneStep
!== undefined) continue outerLoop
;
113 i
= V
.ComputeX(i
+ step
[0]);
116 if (V
.OnBoard(i
, j
) && this.canTake([x
, y
], [i
, j
]))
117 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
122 getPotentialPawnMoves([x
, y
]) {
123 const color
= this.turn
;
125 const [sizeX
, sizeY
] = [V
.size
.x
, V
.size
.y
];
126 // All pawns go in the same direction!
128 const startRank
= color
== "w" ? sizeX
- 2 : 2;
130 // One square forward
131 const nextRow
= V
.ComputeX(x
+ shiftX
);
132 if (this.board
[nextRow
][y
] == V
.EMPTY
) {
133 moves
.push(this.getBasicMove([x
, y
], [nextRow
, y
]));
136 this.pawnFlags
[color
][y
] &&
137 this.board
[x
+ 2 * shiftX
][y
] == V
.EMPTY
140 moves
.push(this.getBasicMove([x
, y
], [x
+ 2 * shiftX
, y
]));
144 for (let shiftY
of [-1, 1]) {
147 y
+ shiftY
< sizeY
&&
148 this.board
[nextRow
][y
+ shiftY
] != V
.EMPTY
&&
149 this.canTake([x
, y
], [nextRow
, y
+ shiftY
])
151 moves
.push(this.getBasicMove([x
, y
], [nextRow
, y
+ shiftY
]));
158 getPotentialKingMoves(sq
) {
159 return this.getSlideNJumpMoves(
161 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
167 const filteredMoves
= super.filterValid(moves
);
168 // If at least one full move made, everything is allowed:
169 if (this.movesCount
>= 2)
170 return filteredMoves
;
171 // Else, forbid check:
172 const oppCol
= V
.GetOppCol(this.turn
);
173 return filteredMoves
.filter(m
=> {
175 const res
= !this.underCheck(oppCol
);
181 isAttackedByPawn([x
, y
], colors
) {
183 const attackerRow
= V
.ComputeX(x
+ pawnShift
);
184 for (let c
of colors
) {
185 for (let i
of [-1, 1]) {
189 this.getPiece(attackerRow
, y
+ i
) == V
.PAWN
&&
190 this.getColor(attackerRow
, y
+ i
) == c
199 isAttackedBySlideNJump([x
, y
], colors
, piece
, steps
, oneStep
) {
200 for (let step
of steps
) {
201 let rx
= V
.ComputeX(x
+ step
[0]),
203 while (V
.OnBoard(rx
, ry
) && this.board
[rx
][ry
] == V
.EMPTY
&& !oneStep
) {
204 rx
= V
.ComputeX(rx
+ step
[0]);
209 this.getPiece(rx
, ry
) === piece
&&
210 colors
.includes(this.getColor(rx
, ry
))
219 // Return pawns flags
221 for (let c
of ["w", "b"]) {
222 for (let i
= 0; i
< 8; i
++) flags
+= this.pawnFlags
[c
][i
] ? "1" : "0";
227 updateVariables(move) {
228 const c
= move.vanish
[0].c
;
229 const secondRank
= {"w":6, "b":2};
230 // Update king position + flags
231 if (move.vanish
[0].p
== V
.KING
&& move.appear
.length
> 0) {
232 this.kingPos
[c
][0] = move.appear
[0].x
;
233 this.kingPos
[c
][1] = move.appear
[0].y
;
235 else if (move.vanish
[0].p
== V
.PAWN
&& secondRank
[c
] == move.start
.x
)
236 // This move turns off a 2-squares pawn flag
237 this.pawnFlags
[c
][move.start
.y
] = false;
240 static get VALUES() {