Discussion of chess software programming and technical issues.
Moderators: hgm , Rebel , chrisw
Jan Brouwer
Posts: 201 Joined: Thu Mar 22, 2007 7:12 pm
Location: Netherlands
Post
by Jan Brouwer » Fri Mar 19, 2010 10:08 pm
My program updates castling rights (4 bits) using masks which are part of the piece encoding:
Code: Select all
state &= board[move.from] & board[move.to] & MASK;
However this requires two types of rook encodings per color, which I don't like and want to remove.
An alternative is to use a lookup table indexed by square:
Code: Select all
state &= table[move.from] & table[move.to]
(I think this works, I haven't tried it.)
The disadvantage of this approach is the additional lookup table, of which only 6 entries are really used.
So can anyone think of yet another approach for simple, branchless castling rights update?
Zach Wegner
Posts: 1922 Joined: Thu Mar 09, 2006 12:51 am
Location: Earth
Post
by Zach Wegner » Fri Mar 19, 2010 11:15 pm
Sure. Here's one I came up with a while back. I call it "Spanish Castle Magic".
I originally used a lookup table with it, but you can go without one if you redefine your castling rights (using 6 bits rather than 4). Unfortunately the castling rights become asymmetric doing this:
WKR=1
WK=16
WQR=2
BKR=8
BK=4
BQR=32
with the 1 bit indicating if white's king-side rook has moved, etc.
Code: Select all
#define CASTLE_MASK (0x9100000000000091)
#define CASTLE_MAGIC (0x0410000000000021)
#define CAN_CASTLE_W_KS(c) ((c) & 0x22)
#define CAN_CASTLE_W_QS(c) ((c) & 0x12)
#define CAN_CASTLE_B_KS(c) ((c) & 0x0C)
#define CAN_CASTLE_B_QS(c) ((c) & 0x05)
...
mask = (bb_from | bb_to) & CASTLE_MASK;
castle_rights &= mask * CASTLE_MAGIC >> 58;
Michael Sherwin
Posts: 3196 Joined: Fri May 26, 2006 3:00 am
Location: WY, USA
Full name: Michael Sherwin
Post
by Michael Sherwin » Sat Mar 20, 2010 10:24 am
In my opinion, castling rights do not need to be updated in (un)make.
In the move generator:
Code: Select all
if(ply <= whiteCastleStopPly) {
whiteCastleStopPly = INFINITY;
if(ply <= whiteShortStopPly) {
whiteShortStopPly = INFINITY;
if(board[E1] == WHITE_KING) {
if(board[H1] == WHITE_KING_ROOK) {
... rest of castling short test here ...
} else {
if(board[H1] == BlackPiece(H1)) {
whiteShortStopPly = ply - 1;
if(ply > whiteLongStopPly) whiteCastleStopPly = ply - 1;
} else {
whiteShortStopPly = ply - 2;
if(ply > whiteLongStopPly) whiteCastleStopPly = ply - 2;
}
}
} else {
whiteCastleStopPly = ply - 2;
if(ply > whiteLongStopPly) whiteCastleStopPly = ply - 2;
}
}
if(ply <= whiteLongStopPly { ... }
}
I hope that I did not flub this up as I just did it from reinventing it in my head.
The nice thing about it is, once castling is no longer possible there is just one check for it and no updating in (un)make.
If you are on a sidewalk and the covid goes beep beep
Just step aside or you might have a bit of heat
Covid covid runs through the town all day
Can the people ever change their ways
Sherwin the covid's after you
Sherwin if it catches you you're through
Jan Brouwer
Posts: 201 Joined: Thu Mar 22, 2007 7:12 pm
Location: Netherlands
Post
by Jan Brouwer » Sat Mar 20, 2010 3:30 pm
Nice, I hadn't expected to see two new approaches!
Michael, your approach of effectively replacing a stack of 4 bits with 4 numbers has a certain elegance to it, I'll look into it.
Meanwhile, thinking a bit more about this, I found a bug in my present code:
Jan Brouwer wrote: Code: Select all
state &= board[move.from] & board[move.to] & MASK;
However this requires two types of rook encodings per color, ...
I actually need a 3rd rook encoding for promoted rooks, which doesn't modify the castling rights!
So, could anyone confirm that the following perft counts are now correct?
Code: Select all
position fen 4k3/8/8/8/8/8/R7/R3K2R w KQ - 0 1
perft 3
4893
position fen 4k3/8/8/8/8/8/R7/R3K2R w Q - 0 1
perft 3
4729
position fen 4k3/8/8/8/8/8/R7/R3K2R w K - 0 1
perft 3
4686
position fen 4k3/8/8/8/8/8/R7/R3K2R w - - 0 1
perft 3
4522
They are about a promoted rook move incorrectly modifying castling state:
[d]4k3/8/8/8/8/8/R7/R3K2R w KQ - 0 1
tvrzsky
Posts: 128 Joined: Sat Sep 23, 2006 7:10 pm
Location: Prague
Post
by tvrzsky » Sat Mar 20, 2010 3:41 pm
My engine is here in agreement with yours.
Code: Select all
4k3/8/8/8/8/8/R7/R3K2R w KQ - 0 1
_______________________________
| |///| |///| |///| |///|
| | | | |(k)| | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| R | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| R | | | | K | | | R |
|___|___|___|___|___|___|___|___|
Ply: 0 Bily na tahu Pocet figur: 4 1 Material: 1560 Figmat: 1560/0
Ke1,Ra1,Ra2,Rh1,
ke8,
perft3
-------------------------------------------------------------------------------
perft(3) SubtreePerft InnerNodes
-------------------------------------------------------------------------------
01/32 O-O-O 114 4 0.00s 270.69kN/s
02/32 O-O 96 4 0.01s 108.28kN/s
03/32 Ra1-b1 195 6 0.02s 336.03kN/s
04/32 Ra1-c1 195 6 0.02s 249.87kN/s
05/32 Ra1-d1 114 4 0.03s 249.87kN/s
06/32 Ra2-a8+ 114 4 0.04s 232.02kN/s
07/32 Ra2-e2+ 152 5 0.05s 261.96kN/s
08/32 Ra2-a3 165 6 0.05s 314.36kN/s
09/32 Ra2-a4 170 6 0.06s 324.83kN/s
10/32 Ra2-a5 175 6 0.07s 314.36kN/s
11/32 Ra2-a6 180 6 0.08s 304.53kN/s
12/32 Ra2-a7 74 3 0.08s 168.02kN/s
13/32 Ra2-b2 200 6 0.09s 286.62kN/s
14/32 Ra2-c2 200 6 0.10s 314.36kN/s
15/32 Ra2-d2 117 4 0.11s 224.02kN/s
16/32 Ra2-f2 117 4 0.12s 216.56kN/s
17/32 Ra2-g2 200 6 0.12s 304.53kN/s
18/32 Ra2-h2 160 6 0.13s 314.36kN/s
19/32 Rh1-h8+ 108 4 0.14s 196.87kN/s
20/32 Rh1-f1 90 4 0.15s 224.02kN/s
21/32 Rh1-g1 155 6 0.15s 314.36kN/s
22/32 Rh1-h2 170 6 0.16s 348.04kN/s
23/32 Rh1-h3 180 6 0.17s 324.83kN/s
24/32 Rh1-h4 180 6 0.18s 314.36kN/s
25/32 Rh1-h5 180 6 0.18s 314.36kN/s
26/32 Rh1-h6 180 6 0.19s 314.36kN/s
27/32 Rh1-h7 72 3 0.20s 108.28kN/s
28/32 Ke1-d1 150 6 0.21s 336.03kN/s
29/32 Ke1-d2 175 6 0.21s 348.04kN/s
30/32 Ke1-e2 180 6 0.22s 324.83kN/s
31/32 Ke1-f1 150 6 0.23s 348.04kN/s
32/32 Ke1-f2 185 6 0.24s 374.81kN/s
-------------------------------------------------------------------------------
total: 4,893 169 0.24s 0.70kN/s
-------------------------------------------------------------------------------
perft(3) = 4,893
Cil SharperPath nenalezen! (cesta: d:\work\chessprog\Engines\Sharper.exe)
a
4k3/8/8/8/8/8/R7/R3K2R w Q - 0 1
_______________________________
| |///| |///| |///| |///|
| | | | |(k)| | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| R | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| R | | | | K | | | R |
|___|___|___|___|___|___|___|___|
Ply: 0 Bily na tahu Pocet figur: 4 1 Material: 1560 Figmat: 1560/0
Ke1,Ra1,Ra2,Rh1,
ke8,
perft3
-------------------------------------------------------------------------------
perft(3) SubtreePerft InnerNodes
-------------------------------------------------------------------------------
01/31 O-O-O 114 4 0.00s 259.87kN/s
02/31 Ra1-b1 190 6 0.01s 324.83kN/s
03/31 Ra1-c1 190 6 0.02s 304.53kN/s
04/31 Ra1-d1 111 4 0.02s 240.62kN/s
05/31 Ra2-a8+ 111 4 0.03s 209.57kN/s
06/31 Ra2-e2+ 148 5 0.04s 116.01kN/s
07/31 Ra2-a3 160 6 0.05s 295.30kN/s
08/31 Ra2-a4 165 6 0.05s 237.68kN/s
09/31 Ra2-a5 170 6 0.06s 243.63kN/s
10/31 Ra2-a6 175 6 0.07s 324.83kN/s
11/31 Ra2-a7 72 3 0.07s 174.02kN/s
12/31 Ra2-b2 195 6 0.08s 314.36kN/s
13/31 Ra2-c2 195 6 0.09s 270.69kN/s
14/31 Ra2-d2 114 4 0.10s 240.62kN/s
15/31 Ra2-f2 114 4 0.10s 224.02kN/s
16/31 Ra2-g2 195 6 0.11s 314.36kN/s
17/31 Ra2-h2 155 6 0.12s 304.53kN/s
18/31 Rh1-h8+ 108 4 0.13s 180.46kN/s
19/31 Rh1-f1 90 4 0.13s 240.62kN/s
20/31 Rh1-g1 155 6 0.14s 348.04kN/s
21/31 Rh1-h2 170 6 0.15s 278.43kN/s
22/31 Rh1-h3 180 6 0.16s 226.63kN/s
23/31 Rh1-h4 180 6 0.16s 270.69kN/s
24/31 Rh1-h5 180 6 0.17s 270.69kN/s
25/31 Rh1-h6 180 6 0.17s 256.45kN/s
26/31 Rh1-h7 72 3 0.18s 128.22kN/s
27/31 Ke1-d1 150 6 0.19s 226.63kN/s
28/31 Ke1-d2 175 6 0.19s 207.34kN/s
29/31 Ke1-e2 180 6 0.20s 211.85kN/s
30/31 Ke1-f1 150 6 0.21s 256.45kN/s
31/31 Ke1-f2 185 6 0.22s 278.43kN/s
-------------------------------------------------------------------------------
total: 4,729 165 0.22s 0.74kN/s
-------------------------------------------------------------------------------
perft(3) = 4,729
Cil SharperPath nenalezen! (cesta: d:\work\chessprog\Engines\Sharper.exe)
a
4k3/8/8/8/8/8/R7/R3K2R w K - 0 1
_______________________________
| |///| |///| |///| |///|
| | | | |(k)| | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| R | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| R | | | | K | | | R |
|___|___|___|___|___|___|___|___|
Ply: 0 Bily na tahu Pocet figur: 4 1 Material: 1560 Figmat: 1560/0
Ke1,Ra1,Ra2,Rh1,
ke8,
perft3
-------------------------------------------------------------------------------
perft(3) SubtreePerft InnerNodes
-------------------------------------------------------------------------------
01/31 O-O 96 4 0.00s 166.58kN/s
02/31 Ra1-b1 195 6 0.01s 324.83kN/s
03/31 Ra1-c1 195 6 0.02s 314.36kN/s
04/31 Ra1-d1 114 4 0.02s 240.62kN/s
05/31 Ra2-a8+ 111 4 0.03s 240.62kN/s
06/31 Ra2-e2+ 148 5 0.04s 312.34kN/s
07/31 Ra2-a3 160 6 0.05s 336.03kN/s
08/31 Ra2-a4 165 6 0.05s 141.23kN/s
09/31 Ra2-a5 170 6 0.06s 348.04kN/s
10/31 Ra2-a6 175 6 0.07s 348.04kN/s
11/31 Ra2-a7 72 3 0.08s 174.02kN/s
12/31 Ra2-b2 195 6 0.08s 324.83kN/s
13/31 Ra2-c2 195 6 0.09s 314.36kN/s
14/31 Ra2-d2 114 4 0.10s 240.62kN/s
15/31 Ra2-f2 114 4 0.11s 203.02kN/s
16/31 Ra2-g2 195 6 0.11s 336.03kN/s
17/31 Ra2-h2 155 6 0.12s 336.03kN/s
18/31 Rh1-h8+ 105 4 0.13s 259.87kN/s
19/31 Rh1-f1 87 4 0.13s 259.87kN/s
20/31 Rh1-g1 150 6 0.14s 360.93kN/s
21/31 Rh1-h2 165 6 0.15s 216.56kN/s
22/31 Rh1-h3 175 6 0.16s 304.53kN/s
23/31 Rh1-h4 175 6 0.16s 348.04kN/s
24/31 Rh1-h5 175 6 0.17s 360.93kN/s
25/31 Rh1-h6 175 6 0.18s 336.03kN/s
26/31 Rh1-h7 70 3 0.19s 203.02kN/s
27/31 Ke1-d1 150 6 0.19s 374.81kN/s
28/31 Ke1-d2 175 6 0.20s 360.93kN/s
29/31 Ke1-e2 180 6 0.21s 348.04kN/s
30/31 Ke1-f1 150 6 0.21s 374.81kN/s
31/31 Ke1-f2 185 6 0.22s 360.93kN/s
-------------------------------------------------------------------------------
total: 4,686 165 0.23s 0.73kN/s
-------------------------------------------------------------------------------
perft(3) = 4,686
Cil SharperPath nenalezen! (cesta: d:\work\chessprog\Engines\Sharper.exe)
a
4k3/8/8/8/8/8/R7/R3K2R w - - 0 1
_______________________________
| |///| |///| |///| |///|
| | | | |(k)| | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| | | | | | | | |
|___|___|___|___|___|___|___|___|
| |///| |///| |///| |///|
| R | | | | | | | |
|___|___|___|___|___|___|___|___|
|///| |///| |///| |///| |
| R | | | | K | | | R |
|___|___|___|___|___|___|___|___|
Ply: 0 Bily na tahu Pocet figur: 4 1 Material: 1560 Figmat: 1560/0
Ke1,Ra1,Ra2,Rh1,
ke8,
perft3
-------------------------------------------------------------------------------
perft(3) SubtreePerft InnerNodes
-------------------------------------------------------------------------------
01/30 Ra1-b1 190 6 0.00s 389.80kN/s
02/30 Ra1-c1 190 6 0.01s 324.83kN/s
03/30 Ra1-d1 111 4 0.02s 249.87kN/s
04/30 Ra2-a8+ 108 4 0.02s 249.87kN/s
05/30 Ra2-e2+ 144 5 0.03s 290.03kN/s
06/30 Ra2-a3 155 6 0.04s 203.02kN/s
07/30 Ra2-a4 160 6 0.04s 374.81kN/s
08/30 Ra2-a5 165 6 0.05s 324.83kN/s
09/30 Ra2-a6 170 6 0.06s 304.53kN/s
10/30 Ra2-a7 70 3 0.06s 187.40kN/s
11/30 Ra2-b2 190 6 0.07s 336.03kN/s
12/30 Ra2-c2 190 6 0.08s 304.53kN/s
13/30 Ra2-d2 111 4 0.09s 240.62kN/s
14/30 Ra2-f2 111 4 0.09s 232.02kN/s
15/30 Ra2-g2 190 6 0.10s 324.83kN/s
16/30 Ra2-h2 150 6 0.11s 360.93kN/s
17/30 Rh1-h8+ 105 4 0.11s 240.62kN/s
18/30 Rh1-f1 87 4 0.12s 224.02kN/s
19/30 Rh1-g1 150 6 0.13s 360.93kN/s
20/30 Rh1-h2 165 6 0.14s 348.04kN/s
21/30 Rh1-h3 175 6 0.14s 360.93kN/s
22/30 Rh1-h4 175 6 0.15s 374.81kN/s
23/30 Rh1-h5 175 6 0.16s 336.03kN/s
24/30 Rh1-h6 175 6 0.16s 389.80kN/s
25/30 Rh1-h7 70 3 0.17s 221.48kN/s
26/30 Ke1-d1 150 6 0.18s 336.03kN/s
27/30 Ke1-d2 175 6 0.18s 314.36kN/s
28/30 Ke1-e2 180 6 0.19s 123.35kN/s
29/30 Ke1-f1 150 6 0.20s 360.93kN/s
30/30 Ke1-f2 185 6 0.21s 324.83kN/s
-------------------------------------------------------------------------------
total: 4,522 161 0.21s 0.76kN/s
-------------------------------------------------------------------------------
perft(3) = 4,522
Cil SharperPath nenalezen! (cesta: d:\work\chessprog\Engines\Sharper.exe)
wgarvin
Posts: 838 Joined: Thu Jul 05, 2007 5:03 pm
Location: British Columbia, Canada
Post
by wgarvin » Sat Mar 20, 2010 5:03 pm
Jan Brouwer wrote: I actually need a 3rd rook encoding for promoted rooks, which doesn't modify the castling rights!
I don't understand this part. There's no way to gain castling rights by moving a piece -- all you can do is lose them. Any time you move a piece FROM the square initially occupied by the rook, you can mask off the appropriate right (if this is anything other than the first move by that rook, then the right will already be gone). Similarly, any time you move a piece TO the square initially occupied by the rook, you can mask off the appropriate right too. Either its an enemy piece capturing that rook, or the rook has already moved at least once so the right is already gone.
I don't see how promotions can change this at all. If you make a capture+promotion move with an enemy pawn, you still want to remove the castling rights from the TO square just like with any other move. The type of the moving piece is irrelevant, for anything other than a castling move you can just mask off the rights for the FROM and TO squares.
[Edit: oops, I understand now. You have a bit in your piece encoding which says "I'm a rook that can castle QS" or similar. ...Why do this at all? Just to handle one special move during move generation? Seems like overkill.]
Jan Brouwer
Posts: 201 Joined: Thu Mar 22, 2007 7:12 pm
Location: Netherlands
Post
by Jan Brouwer » Sat Mar 20, 2010 5:14 pm
tvrzsky wrote: My engine is here in agreement with yours.
Thanks, another bug squashed!
Jan Brouwer
Posts: 201 Joined: Thu Mar 22, 2007 7:12 pm
Location: Netherlands
Post
by Jan Brouwer » Sat Mar 20, 2010 5:26 pm
wgarvin wrote:
[Edit: oops, I understand now. You have a bit in your piece encoding which says "I'm a rook that can castle QS" or similar. ...Why do this at all? Just to handle one special move during move generation? Seems like overkill.]
Yes, the piece encodings contain a 4 bit mask which disables castling rights, like so:
Code: Select all
* WK : b .... .... .... .... .... ...1 1=1= .1-1
* BK : b .... .... .... .... .... ..1. =1=1 .1-1
* WQ : b .... .... .... .... ...1 .1.. 1111 .1--
* BQ : b .... .... .... .... ..1. 1... 1111 .1--
* WR : b .... .... .... .... .... .1.. 1111 .-11 (white promoted R)
* WRA : b .... .... .... .... .... .1.. 111= .-11 (white queen side R)
* WRH : b .... .... .... .... .... .1.. 1=11 .-11 (white king side R)
* BR : b .... .... .... .... .... 1... 1111 .-11 (black promoted R)
* BRA : b .... .... .... .... .... 1... 11=1 .-11 (black queen side R)
* BRH : b .... .... .... .... .... 1... =111 .-11 (black king side R)
* WB : b .... .... .... .... ...1 .... 1111 .-1-
* BB : b .... .... .... .... ..1. .... 1111 .-1-
* WN : b .... .... .... .... .1.. .... 1111 .--1
* BN : b .... .... .... .... 1... .... 1111 .--1
* WP : b .... .... .... ...1 .... .... 1111 .---
* BP : b .... .... .... ..1. .... .... 1111 .---
This allows easy update of castling state when executing a move:
Code: Select all
state &= board[move.from] & board[move.to] & 0xf0;
("state" has the castling bits conveniently located in bits 4..7.)
Note that this also handles losing castling rights by having your rook captured.
wgarvin
Posts: 838 Joined: Thu Jul 05, 2007 5:03 pm
Location: British Columbia, Canada
Post
by wgarvin » Sat Mar 20, 2010 5:59 pm
You could also just use a single bit to mean "this piece still has its initial castling right". Then do the same masking trick. Its not any more complicated in move generation because you still have to check the king and both rooks to make sure they have the right.
OTOH, you could forget about storing this bit in your piece encoding, and just keep 4 bits in your board representation for the 4 castling cases that are still allowed. Then you need to have a table with a 4-bit mask per board square, then in makemove do something like:
Code: Select all
board.castlingRights &= castlingRightsPerSquare[fromSq] & castlingRightsPerSquare[toSq];
Or if you like bitboards in your engine, you could just keep a bitboard of "pieces that don't have their initial castling right" and when making a move, just set the FROM and TO bits in this bitboard. If you do that, you won't even need a table. And you'd have to calculate the bitboards (1<<FROM) and (1<<TO) for other reasons anyway. When generating moves, just AND the bitboard with the proper mask for each castling move and if the result is non-zero, don't generate that move.
Zach Wegner
Posts: 1922 Joined: Thu Mar 09, 2006 12:51 am
Location: Earth
Post
by Zach Wegner » Sat Mar 20, 2010 6:47 pm
wgarvin wrote: Or if you like bitboards in your engine, you could just keep a bitboard of "pieces that don't have their initial castling right" and when making a move, just set the FROM and TO bits in this bitboard. If you do that, you won't even need a table. And you'd have to calculate the bitboards (1<<FROM) and (1<<TO) for other reasons anyway. When generating moves, just AND the bitboard with the proper mask for each castling move and if the result is non-zero, don't generate that move.
Not a bad idea. Funny I didn't think of that when doing my magic stuff. Saves the magic multiply/shift for having a bigger castle rights state holder (8 vs 1 byte, if that matters). One other advantage of your idea is that it becomes symmetrical:
Code: Select all
#define CAN_CASTLE_KS(s,c) ((c)>>(s)*56 & 0x90)
for white=0, black=1.