1 import { ChessRules
, PiPo
, Move
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { randInt
} from "@/utils/alea";
5 export const VariantRules
= class HiddenqueenRules
extends ChessRules
{
6 // Analyse in Hiddenqueen mode makes no sense
7 static get CanAnalyze() {
11 static get HIDDEN_QUEEN() {
16 return ChessRules
.PIECES
.concat(Object
.values(V
.HIDDEN_CODE
));
20 const piece
= this.board
[i
][j
].charAt(1);
22 piece
!= V
.HIDDEN_QUEEN
||
23 // 'side' is used to determine what I see: a pawn or a (hidden)queen?
24 this.getColor(i
, j
) == this.side
31 getPpath(b
, color
, score
) {
32 if (b
[1] == V
.HIDDEN_QUEEN
) {
33 // Supposed to be hidden.
34 if (score
== "*" && (!color
|| color
!= b
[0]))
36 return "Hiddenqueen/" + b
[0] + "t";
41 isValidPawnMove(move) {
42 const color
= move.vanish
[0].c
;
43 const pawnShift
= color
== "w" ? -1 : 1;
44 const startRank
= color
== "w" ? V
.size
.x
- 2 : 1;
45 const lastRank
= color
== "w" ? 0 : V
.size
.x
- 1;
47 // The queen is discovered if she reaches the 8th rank,
48 // even if this would be a technically valid pawn move.
49 move.end
.x
!= lastRank
&&
52 move.end
.x
- move.start
.x
== pawnShift
&&
56 move.end
.y
== move.start
.y
&&
57 this.board
[move.end
.x
][move.end
.y
] == V
.EMPTY
62 Math
.abs(move.end
.y
- move.start
.y
) == 1 &&
63 this.board
[move.end
.x
][move.end
.y
] != V
.EMPTY
69 // Two-spaces initial jump
70 move.start
.x
== startRank
&&
71 move.end
.y
== move.start
.y
&&
72 move.end
.x
- move.start
.x
== 2 * pawnShift
&&
73 this.board
[move.end
.x
][move.end
.y
] == V
.EMPTY
79 getPotentialMovesFrom([x
, y
]) {
80 if (this.getPiece(x
, y
) == V
.HIDDEN_QUEEN
) {
81 const pawnMoves
= this.getPotentialPawnMoves([x
, y
]);
82 let queenMoves
= super.getPotentialQueenMoves([x
, y
]);
83 // Remove from queen moves those corresponding to a pawn move:
84 queenMoves
= queenMoves
85 .filter(m
=> !this.isValidPawnMove(m
))
86 // Hidden queen is revealed if moving like a queen:
88 m
.appear
[0].p
= V
.QUEEN
;
91 return pawnMoves
.concat(queenMoves
);
93 return super.getPotentialMovesFrom([x
, y
]);
96 // TODO: find a more general way to describe pawn movements to avoid
97 // re-writing almost the same function for several variants.
98 getPotentialPawnMoves([x
, y
]) {
99 const color
= this.turn
;
100 const piece
= this.getPiece(x
, y
);
102 const [sizeX
, sizeY
] = [V
.size
.x
, V
.size
.y
];
103 const shiftX
= color
== "w" ? -1 : 1;
104 const startRank
= color
== "w" ? sizeX
- 2 : 1;
105 const lastRank
= color
== "w" ? 0 : sizeX
- 1;
108 x
+ shiftX
== lastRank
110 ? [V
.ROOK
, V
.KNIGHT
, V
.BISHOP
, V
.QUEEN
]
111 : [V
.QUEEN
] //hidden queen revealed
113 if (this.board
[x
+ shiftX
][y
] == V
.EMPTY
) {
114 // One square forward
115 for (let p
of finalPieces
) {
117 this.getBasicMove([x
, y
], [x
+ shiftX
, 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
[x
+ shiftX
][y
+ shiftY
] != V
.EMPTY
&&
137 this.canTake([x
, y
], [x
+ shiftX
, y
+ shiftY
])
139 for (let p
of finalPieces
) {
141 this.getBasicMove([x
, y
], [x
+ shiftX
, y
+ shiftY
], {
150 if (V
.HasEnpassant
) {
152 const Lep
= this.epSquares
.length
;
153 const epSquare
= this.epSquares
[Lep
- 1]; //always at least one element
156 epSquare
.x
== x
+ shiftX
&&
157 Math
.abs(epSquare
.y
- y
) == 1
159 let enpassantMove
= this.getBasicMove([x
, y
], [epSquare
.x
, epSquare
.y
]);
160 enpassantMove
.vanish
.push({
164 c: this.getColor(x
, epSquare
.y
)
166 moves
.push(enpassantMove
);
173 getPossibleMovesFrom(sq
) {
174 this.side
= this.turn
;
175 return this.filterValid(this.getPotentialMovesFrom(sq
));
178 static GenRandInitFen(randomness
) {
179 let fen
= ChessRules
.GenRandInitFen(randomness
);
180 // Place hidden queens at random (always):
181 let hiddenQueenPos
= randInt(8);
182 let pawnRank
= "PPPPPPPP".split("");
183 pawnRank
[hiddenQueenPos
] = "T";
184 fen
= fen
.replace("PPPPPPPP", pawnRank
.join(""));
185 hiddenQueenPos
= randInt(8);
186 pawnRank
= "pppppppp".split("");
187 pawnRank
[hiddenQueenPos
] = "t";
188 fen
= fen
.replace("pppppppp", pawnRank
.join(""));
192 updateVariables(move) {
193 super.updateVariables(move);
194 if (move.vanish
.length
== 2 && move.vanish
[1].p
== V
.KING
)
195 // We took opponent king
196 this.kingPos
[this.turn
] = [-1, -1];
199 unupdateVariables(move) {
200 super.unupdateVariables(move);
201 const c
= move.vanish
[0].c
;
202 const oppCol
= V
.GetOppCol(c
);
203 if (this.kingPos
[oppCol
][0] < 0)
204 // Last move took opponent's king:
205 this.kingPos
[oppCol
] = [move.vanish
[1].x
, move.vanish
[1].y
];
209 const color
= this.turn
;
210 if (this.kingPos
[color
][0] < 0)
212 return color
== "w" ? "0-1" : "1-0";
213 return super.getCurrentScore();
216 // Search is biased, so not really needed to explore deeply
217 static get SEARCH_DEPTH() {
221 static get VALUES() {
222 return Object
.assign(
229 this.side
= this.turn
;
230 return super.getComputerMove();
234 const notation
= super.getNotation(move);
235 if (notation
.charAt(0) == 'T')
236 // Do not reveal hidden queens
237 return notation
.substr(1);