3be0c149f39a6bc344cd58b912d072fbb6290a3d
1 import { ChessRules
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { randInt
, shuffle
} from "@/utils/alea";
5 export class RoyalraceRules
extends ChessRules
{
7 static get HasFlags() {
11 static get HasEnpassant() {
15 static get CanFlip() {
20 return { x: 11, y: 11 };
24 return (b
[1] == V
.KNIGHT
? "Enpassant/" : "") + b
;
27 static GenRandInitFen(options
) {
28 if (options
.randomness
== 0)
29 return "92/92/92/92/92/92/92/92/92/qrbnp1PNBRQ/krbnp1PNBRK w 0";
31 let pieces
= { w: new Array(10), b: new Array(10) };
32 // Shuffle pieces on first and second rank
33 for (let c
of ["w", "b"]) {
34 if (c
== 'b' && options
.randomness
== 1) {
35 pieces
['b'] = JSON
.parse(JSON
.stringify(pieces
['w'])).reverse();
37 pieces
['b'].splice(5,10).reverse().concat(
38 pieces
['b'].splice(0,5).reverse());
42 // Reserve 4 and 5 which are pawns positions
43 let positions
= ArrayFun
.range(10).filter(i
=> i
!= 4 && i
!= 5);
45 // Get random squares for bishops
46 let randIndex
= 2 * randInt(4);
47 const bishop1Pos
= positions
[randIndex
];
48 // The second bishop must be on a square of different color
49 let randIndex_tmp
= 2 * randInt(4) + 1;
50 const bishop2Pos
= positions
[randIndex_tmp
];
51 // Remove chosen squares
52 positions
.splice(Math
.max(randIndex
, randIndex_tmp
), 1);
53 positions
.splice(Math
.min(randIndex
, randIndex_tmp
), 1);
55 // Place the king at random on (remaining squares of) first row
57 if (positions
[maxIndex
-1] >= 4)
59 if (positions
[maxIndex
-1] >= 4)
61 randIndex
= randInt(maxIndex
);
62 const kingPos
= positions
[randIndex
];
63 positions
.splice(randIndex
, 1);
65 // Get random squares for knights
66 randIndex
= randInt(5);
67 const knight1Pos
= positions
[randIndex
];
68 positions
.splice(randIndex
, 1);
69 randIndex
= randInt(4);
70 const knight2Pos
= positions
[randIndex
];
71 positions
.splice(randIndex
, 1);
73 // Get random squares for rooks
74 randIndex
= randInt(3);
75 const rook1Pos
= positions
[randIndex
];
76 positions
.splice(randIndex
, 1);
77 randIndex
= randInt(2);
78 const rook2Pos
= positions
[randIndex
];
79 positions
.splice(randIndex
, 1);
81 // Queen position is now determined,
82 // because pawns are not placed at random
83 const queenPos
= positions
[0];
85 // Finally put the shuffled pieces in the board array
86 pieces
[c
][rook1Pos
] = "r";
87 pieces
[c
][knight1Pos
] = "n";
88 pieces
[c
][bishop1Pos
] = "b";
89 pieces
[c
][queenPos
] = "q";
90 pieces
[c
][kingPos
] = "k";
91 pieces
[c
][bishop2Pos
] = "b";
92 pieces
[c
][knight2Pos
] = "n";
93 pieces
[c
][rook2Pos
] = "r";
97 const whiteFen
= pieces
["w"].join("").toUpperCase();
98 const blackFen
= pieces
["b"].join("");
100 "92/92/92/92/92/92/92/92/92/" +
101 blackFen
.substr(5).split("").reverse().join("") +
103 whiteFen
.substr(5).split("").join("") +
105 blackFen
.substr(0,5) +
107 whiteFen
.substr(0,5).split("").reverse().join("") +
112 // TODO: simplify this when base function is more general.
113 getPotentialPawnMoves([x
, y
]) {
114 // Normal moves (as a rook)
116 this.getSlideNJumpMoves([x
, y
], V
.steps
[V
.ROOK
]).filter(m
=> {
117 // Remove captures. Alt: redefine canTake
118 return m
.vanish
.length
== 1;
121 // Captures (in both directions)
122 for (let shiftX
of [-1, 1]) {
123 for (let shiftY
of [-1, 1]) {
125 V
.OnBoard(x
+ shiftX
, y
+ shiftY
) &&
126 this.board
[x
+ shiftX
][y
+ shiftY
] != V
.EMPTY
&&
127 this.canTake([x
, y
], [x
+ shiftX
, y
+ shiftY
])
129 moves
.push(this.getBasicMove([x
, y
], [x
+ shiftX
, y
+ shiftY
]));
137 getPotentialKnightMoves(sq
) {
138 // Knight becomes knightrider:
139 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
]);
143 if (moves
.length
== 0) return [];
144 const color
= this.turn
;
145 const oppCol
= V
.GetOppCol(color
);
146 return moves
.filter(m
=> {
148 // Giving check is forbidden as well:
149 const res
= !this.underCheck(color
) && !this.underCheck(oppCol
);
155 isAttackedByPawn([x
, y
], color
) {
156 // Pawns can capture forward and backward:
157 return this.isAttackedBySlideNJump(
158 sq
, color
, V
.PAWN
, V
.steps
[V
.BISHOP
], 1);
161 isAttackedByKnight(sq
, color
) {
162 return this.isAttackedBySlideNJump(
163 sq
, color
, V
.KNIGHT
, V
.steps
[V
.KNIGHT
]);
168 const color
= V
.GetOppCol(this.turn
);
169 if (this.kingPos
[color
][0] == 0)
170 // The opposing edge is reached!
171 return color
== "w" ? "1-0" : "0-1";
172 if (this.atLeastOneMove()) return "*";
173 // Stalemate (will probably never happen)
177 static get SEARCH_DEPTH() {
181 static get VALUES() {
194 let evaluation
= super.evalPosition();
195 // Ponder with king position:
196 return evaluation
/5 + this.kingPos
["b"][0] - this.kingPos
["w"][0];
200 // Since pawns are much more mobile, treat them as other pieces:
202 move.vanish
[0].p
.toUpperCase() +
203 (move.vanish
.length
> move.appear
.length
? "x" : "") +
204 V
.CoordsToSquare(move.end
)