-
Notifications
You must be signed in to change notification settings - Fork 45
/
compressed.py
124 lines (124 loc) · 5.42 KB
/
compressed.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
class Position(namedtuple('Position', 'board score')):
def gen_moves(self):
for i, p in enumerate(self.board):
if p == 'K':
for scanpos in range(i - 16,A9,-16):
if self.board[scanpos] == 'k':
yield (i,scanpos)
elif self.board[scanpos] != '.':
break
if not p.isupper(): continue
if p == 'C':
for d in directions[p]:
cfoot = 0
for j in count(i+d, d):
q = self.board[j]
if q.isspace():break
if cfoot == 0 and q == '.':yield (i,j)
elif cfoot == 0 and q != '.':cfoot += 1
elif cfoot == 1 and q.islower(): yield (i,j);break
elif cfoot == 1 and q.isupper(): break;
continue
for d in directions[p]:
for j in count(i+d, d):
q = self.board[j]
if q.isspace() or q.isupper(): break
if p == 'P' and d in (E, W) and i > 128: break
elif p in ('Q','K') and (j < 160 or j & 15 > 8 or j & 15 < 6): break
elif p == 'B' and j < 128: break
elif p == 'N':
n_diff_x = (j - i) & 15
if n_diff_x == 14 or n_diff_x == 2:
if self.board[i + (1 if n_diff_x == 2 else -1)] != '.': break
else:
if j > i and self.board[i + 16] != '.': break
elif j < i and self.board[i - 16] != '.': break
elif p == 'B' and self.board[i + d / 2] != '.':break
yield (i, j)
if p in 'PNBQK' or q.islower(): break
def rotate(self):
return Position(
self.board[-2::-1].swapcase() + " ", -self.score)
def nullmove(self):
return self.rotate()
def move(self, move):
i, j = move
p, q = self.board[i], self.board[j]
put = lambda board, i, p: board[:i] + p + board[i+1:]
board = self.board
score = self.score + self.value(move)
board = put(board, j, board[i])
board = put(board, i, '.')
return Position(board, score).rotate()
def value(self, move):
i, j = move
p, q = self.board[i], self.board[j]
score = pst[p][j] - pst[p][i]
if q.islower():
score += pst[q.upper()][255-j-1]
return score
Entry = namedtuple('Entry', 'lower upper')
class Searcher:
def __init__(self):
self.tp_score = {}
self.tp_move = {}
self.history = set()
self.nodes = 0
def bound(self, pos, gamma, depth, root=True):
self.nodes += 1
depth = max(depth, 0)
if pos.score <= -MATE_LOWER:
return -MATE_UPPER
if DRAW_TEST:
if not root and pos in self.history:
return 0
entry = self.tp_score.get((pos, depth, root), Entry(-MATE_UPPER, MATE_UPPER))
if entry.lower >= gamma and (not root or self.tp_move.get(pos) is not None):
return entry.lower
if entry.upper < gamma:
return entry.upper
def moves():
if depth > 0 and not root and any(c in pos.board for c in 'RNC'):
yield None, -self.bound(pos.nullmove(), 1-gamma, depth-3, root=False)
if depth == 0:
yield None, pos.score
killer = self.tp_move.get(pos)
if killer and (depth > 0 or pos.value(killer) >= QS_LIMIT):
yield killer, -self.bound(pos.move(killer), 1-gamma, depth-1, root=False)
for move in sorted(pos.gen_moves(), key=pos.value, reverse=True):
if depth > 0 or pos.value(move) >= QS_LIMIT:
yield move, -self.bound(pos.move(move), 1-gamma, depth-1, root=False)
best = -MATE_UPPER
for move, score in moves():
best = max(best, score)
if best >= gamma:
if len(self.tp_move) > TABLE_SIZE: self.tp_move.clear()
self.tp_move[pos] = move
break
if best < gamma and best < 0 and depth > 0:
is_dead = lambda pos: any(pos.value(m) >= MATE_LOWER for m in pos.gen_moves())
if all(is_dead(pos.move(m)) for m in pos.gen_moves()):
in_check = is_dead(pos.nullmove())
best = -MATE_UPPER if in_check else 0
if len(self.tp_score) > TABLE_SIZE: self.tp_score.clear()
if best >= gamma:
self.tp_score[pos, depth, root] = Entry(best, entry.upper)
if best < gamma:
self.tp_score[pos, depth, root] = Entry(entry.lower, best)
return best
def search(self, pos, history=()):
self.nodes = 0
if DRAW_TEST:
self.history = set(history)
self.tp_score.clear()
for depth in range(1, 1000):
lower, upper = -MATE_UPPER, MATE_UPPER
while lower < upper - EVAL_ROUGHNESS:
gamma = (lower+upper+1)//2
score = self.bound(pos, gamma, depth)
if score >= gamma:
lower = score
if score < gamma:
upper = score
self.bound(pos, lower, depth)
yield depth, self.tp_move.get(pos), self.tp_score.get((pos, depth, True)).lower