1 class DarkRules
extends ChessRules
3 // Standard rules, in the shadow
6 super.setOtherVariables(fen
);
7 const [sizeX
,sizeY
] = [V
.size
.x
,V
.size
.y
];
9 "w": doubleArray(sizeX
,sizeY
),
10 "b": doubleArray(sizeX
,sizeY
)
12 // Setup enlightened: squares reachable by each side
13 // (TODO: one side would be enough ?)
14 this.updateEnlightened();
19 // Initialize with pieces positions (which are seen)
20 for (let i
=0; i
<V
.size
.x
; i
++)
22 for (let j
=0; j
<V
.size
.y
; j
++)
24 this.enlightened
["w"][i
][j
] = false;
25 this.enlightened
["b"][i
][j
] = false;
26 if (this.board
[i
][j
] != V
.EMPTY
)
27 this.enlightened
[this.getColor(i
,j
)][i
][j
] = true;
30 const currentTurn
= this.turn
;
32 const movesWhite
= this.getAllValidMoves();
34 const movesBlack
= this.getAllValidMoves();
35 this.turn
= currentTurn
;
36 for (let move of movesWhite
)
37 this.enlightened
["w"][move.end
.x
][move.end
.y
] = true;
38 for (let move of movesBlack
)
39 this.enlightened
["b"][move.end
.x
][move.end
.y
] = true;
42 // Has to be redefined to avoid an infinite loop
45 const color
= this.turn
;
46 const oppCol
= this.getOppCol(color
);
47 let potentialMoves
= [];
48 for (let i
=0; i
<V
.size
.x
; i
++)
50 for (let j
=0; j
<V
.size
.y
; j
++)
52 if (this.board
[i
][j
] != V
.EMPTY
&& this.getColor(i
,j
) == color
)
53 Array
.prototype.push
.apply(potentialMoves
, this.getPotentialMovesFrom([i
,j
]));
56 return potentialMoves
; //because there are no checks
61 if (this.kingPos
[this.turn
][0] < 0)
63 return true; //TODO: is it right?
68 return false; //there is no check
71 getCheckSquares(color
)
78 // Update kings positions
79 const piece
= move.vanish
[0].p
;
80 const c
= move.vanish
[0].c
;
81 if (piece
== V
.KING
&& move.appear
.length
> 0)
83 this.kingPos
[c
][0] = move.appear
[0].x
;
84 this.kingPos
[c
][1] = move.appear
[0].y
;
86 if (move.vanish
.length
>= 2 && move.vanish
[1].p
== V
.KING
)
88 // We took opponent king !
89 const oppCol
= this.getOppCol(c
);
90 this.kingPos
[oppCol
] = [-1,-1];
93 // Update moves for both colors:
94 this.updateEnlightened();
97 unupdateVariables(move)
99 super.unupdateVariables(move);
100 const c
= move.vanish
[0].c
;
101 const oppCol
= this.getOppCol(c
);
102 if (this.kingPos
[oppCol
][0] < 0)
104 // Last move took opponent's king
105 for (let psq
of move.vanish
)
109 this.kingPos
[oppCol
] = [psq
.x
, psq
.y
];
115 // Update moves for both colors:
116 this.updateEnlightened();
121 // No valid move: our king disappeared
122 return this.turn
== "w" ? "0-1" : "1-0";
125 static get THRESHOLD_MATE()
127 return 500; //checkmates evals may be slightly below 1000
130 // In this special situation, we just look 1 half move ahead
133 const maxeval
= V
.INFINITY
;
134 const color
= this.turn
;
135 const oppCol
= this.getOppCol(color
);
136 const pawnShift
= (color
== "w" ? -1 : 1);
137 const kp
= this.kingPos
[color
];
139 // Do not cheat: the current enlightment is all we can see
140 const myLight
= JSON
.parse(JSON
.stringify(this.enlightened
[color
]));
142 // Can a slider on (i,j) apparently take my king?
143 // NOTE: inaccurate because assume yes if some squares are shadowed
144 const sliderTake
= ([i
,j
], piece
) => {
145 let step
= undefined;
146 if (piece
== V
.BISHOP
)
148 if (Math
.abs(kp
[0] - i
) == Math
.abs(kp
[1] - j
))
152 (i
-kp
[0]) / Math
.abs(i
-kp
[0]),
153 (j
-kp
[1]) / Math
.abs(j
-kp
[1])
157 else if (piece
== V
.ROOK
)
160 step
= [0, (j
-kp
[1]) / Math
.abs(j
-kp
[1])];
162 step
= [(i
-kp
[0]) / Math
.abs(i
-kp
[0]), 0];
166 // Check for obstacles
167 let obstacle
= false;
169 let x
=kp
[0]+step
[0], y
=kp
[1]+step
[1];
171 x
+= step
[0], y
+= step
[1])
173 if (myLight
[x
][y
] && this.board
[x
][y
] != V
.EMPTY
)
184 // Do I see something which can take my king ?
185 const kingThreats
= () => {
186 for (let i
=0; i
<V
.size
.x
; i
++)
188 for (let j
=0; j
<V
.size
.y
; j
++)
190 if (myLight
[i
][j
] && this.board
[i
][j
] != V
.EMPTY
191 && this.getColor(i
,j
) != color
)
193 switch (this.getPiece(i
,j
))
196 if (kp
[0] + pawnShift
== i
&& Math
.abs(kp
[1]-j
) == 1)
200 if ((Math
.abs(kp
[0] - i
) == 2 && Math
.abs(kp
[1] - j
) == 1) ||
201 (Math
.abs(kp
[0] - i
) == 1 && Math
.abs(kp
[1] - j
) == 2))
207 if (Math
.abs(kp
[0] - i
) == 1 && Math
.abs(kp
[1] - j
) == 1)
211 if (sliderTake([i
,j
], V
.BISHOP
))
215 if (sliderTake([i
,j
], V
.ROOK
))
219 if (sliderTake([i
,j
], V
.BISHOP
) || sliderTake([i
,j
], V
.ROOK
))
229 let moves
= this.getAllValidMoves();
230 for (let move of moves
)
233 if (this.kingPos
[oppCol
][0] >= 0 && kingThreats())
235 // We didn't take opponent king, and our king will be captured: bad
236 move.eval
= -maxeval
;
242 move.eval
= 0; //a priori...
244 // Can I take something ? If yes, do it if it seems good...
245 if (move.vanish
.length
== 2 && move.vanish
[1].c
!= color
) //avoid castle
247 const myPieceVal
= V
.VALUES
[move.appear
[0].p
];
248 const hisPieceVal
= V
.VALUES
[move.vanish
[1].p
];
249 if (myPieceVal
<= hisPieceVal
)
250 move.eval
= hisPieceVal
- myPieceVal
+ 2; //favor captures
253 // Taking a pawn with minor piece,
254 // or minor piece or pawn with a rook,
255 // or anything but a queen with a queen,
256 // or anything with a king.
257 // ==> Do it at random, although
258 // this is clearly inferior to what a human can deduce...
259 move.eval
= (Math
.random() < 0.5 ? 1 : -1);
264 // TODO: also need to implement the case when an opponent piece (in light)
265 // is threatening something - maybe not the king, but e.g. pawn takes rook.
267 moves
.sort((a
,b
) => b
.eval
- a
.eval
);
268 let candidates
= [0];
269 for (let j
=1; j
<moves
.length
&& moves
[j
].eval
== moves
[0].eval
; j
++)
271 return moves
[_
.sample(candidates
, 1)];
275 const VariantRules
= DarkRules
;