Final result to the question...assuming the code logic is sound AND loopywalt's condition when a pawn move allows castling but no artificial castling (i.e. a Q or B blocking a castling end point and another B not but standing right beside it) is IFF. (Personally, I trust it.)
599
Program output coding.
- Long and short castling rights start with True.
- If a castling without pawn move is impossible due to a B or Q between R and K or on the castling end points, right switches to False and is also coded by have=need=-9 on that side.
- If the castling gets possible by moving a pawn (a second bishop stands beside the obstructing one, not on an end point), right switches back to True and is also coded by have=need=-7 on that side.
- Otherwise, between K and R only NN stand and right stays True.
- It is clear to see that if K and R move into different directions, castling can't be obstructed for that reason. Stays True, coded by have=need=-8 on that side.
- If they move in the same direction, a careful analysis reveals that castling possibility without pawn move is only dependent on two variables: the distance from K to R's destination ("need") and the number of knights between K and the next "border", i.e. bishop ("have"). If need is bigger than have, can't castle and right switches to False. Otherwise, stays True: Ns move out, Q and maybe noncastling R can be shifted to the "holes" until they don't stand in the way.
- If False, White still can castle after a pawn move and can't castle artificially exactly if a queen stands on the kings destination and a non-blocking bishop beside her. Switch back to True and mark "need" and "have" by negating them.
Please do at least a few spot checks for each case.
# generates lexicographic next permutation
# don't bother to understand code - it works :-)
def nextperm(permo):
n = len(permo)
y = n - 1
while permo[y - 1] > permo[y]:
y -= 1
yy = n - 1
while permo[y - 1] > permo[yy]:
yy -= 1
permn = permo[0:y - 1] + [permo[yy]] + sorted(permo[y - 1:yy] + permo[yy + 1:n])
return permn
# inverts permutation to get piece type from location
def perminv(perm):
num_sqsize = len(perm)
perm_inv = [0 for _ in range(num_sqsize)]
for y in range(num_sqsize):
x = perm[y]
perm_inv[x] = y
return perm_inv
ch = ['K','Q','R','R','B','B','N','N']
perm = [_ for _ in range(8)]
ct = 0
ca = 0
# destination squares
kl = 2
kr = 6
jl = 3
jr = 5
for i in range(0,40320):
# piece locations
ki = perm[0]
qu = perm[1]
lr = perm[2]
rr = perm[3]
lb = perm[4]
rb = perm[5]
ln = perm[6]
rn = perm[7]
# check if valid chess 960 position encoding
if lr < ki < rr and ln < rn and lb < rb and (lb+rb)%2 == 1:
# Can castle without pawn move?
castl = True
castr = True
# if B or Q between R and K, can't castle
if lr < qu < ki or lr < lb < ki or lr < rb < ki:
castl = False
if ki < qu < rr or ki < lb < rr or ki < rb < rr:
castr = False
# if B between K castling destination and K, can't castle
if ki <= kl:
if ki < lb <= jl or ki < rb <= jl:
castl = False
else:
if ki > lb >= kl or ki > rb >= kl:
castl = False
if ki >= kr:
if ki > lb >= jr or ki > rb >= jr:
castr = False
else:
if ki < lb <= kr or ki < rb <= kr:
castr = False
# if not, between R and K only NN can stand.
# Thus, can castle unconditionally if castling makes R and K
# move in different directions. Else, might need N holes.
# Required holes only depend on K position
# Split over movement direction and count NN to next B border
if castr:
if kr > ki and jr >= rr:
if ki < lb < rb:
bo = lb
elif lb < ki < rb:
bo = rb
else:
bo = 8
needr = jr-ki
haver = 0
if ki < ln < bo:
haver += 1
if ki < rn < bo:
haver += 1
if haver < needr:
castr = False
if bo == 7 and qu == 6:
castr = True
haver = -haver
needr = -needr
elif kr <= ki and jr < rr:
if lb < rb < ki:
bo = rb
elif lb < ki < rb:
bo = lb
else:
bo = -1
needr = ki-jr
haver = 0
if bo < ln < ki:
haver += 1
if bo < rn < ki:
haver += 1
if haver < needr:
castr = False
if bo == 4 and qu == 5:
castr = True
haver = -haver
needr = -needr
else:
haver = -8
needr = -8
else:
haver = -9
needr = -9
if rr < lb and lb == 6 and rb == 7 and not (ki < qu < rr):
castr = True
haver = -7
needr = -7
if ki > rb and lb == 4 and rb == 5 and not (ki < qu < rr):
castr = True
haver = -7
needr = -7
if castl:
if kl >= ki and jl > lr:
if ki < lb < rb:
bo = lb
elif lb < ki < rb:
bo = rb
else:
bo = 8
needl = jl-ki
havel = 0
if ki < ln < bo:
havel += 1
if ki < rn < bo:
havel += 1
if havel < needl:
castl = False
if bo == 4 and qu == 3:
castl = True
havel = -havel
needl = -needl
elif kl < ki and jl <= lr:
if lb < rb < ki:
bo = rb
elif lb < ki < rb:
bo = lb
else:
bo = -1
needl = ki-jl
havel = 0
if bo < ln < ki:
havel += 1
if bo < rn < ki:
havel += 1
if havel < needl:
castl = False
if bo == 1 and qu == 2:
castl = True
havel = -havel
needl = -needl
else:
havel = -8
needl = -8
else:
havel = -9
needl = -9
if ki < lb and lb == 3 and rb == 4 and not (lr < qu < ki):
castl = True
havel = -7
needl = -7
if lr > rb and lb == 1 and rb == 2 and not (lr < qu < ki):
castl = True
havel = -7
needl = -7
ct += 1
permi = perminv(perm)
out = ''
for j in range(8):
out += ch[permi[j]]
if castl or castr:
ca += 1
print(ct,out,castl,havel,needl,castr,haver,needr)
perm = nextperm(perm)
print(ca)