-
Notifications
You must be signed in to change notification settings - Fork 0
/
selfplay-bf
executable file
·180 lines (166 loc) · 4.33 KB
/
selfplay-bf
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/env coffee
require './rejection_handler'
fs = require 'fs'
{
Board
BLACK
WHITE
pos_to_str
pos_from_str
pos_array_to_str
pos_array_from_str
} = require './board'
{ shuffle, watch_file } = require './util'
make_player = require './player'
uct = require './uct'
pattern_eval = require('./pattern_eval')('weights.json')
Book = require './book'
{ encode_normalized } = require './encode'
ext = require './ext'
argv = require 'yargs'
.options
n:
desc: 'Number to play'
type: 'number'
reguiresArg: true
d:
alias: 'depth'
desc: 'Depth of opening to begin with'
type: 'number'
default: 1
requiresArg: true
s:
alias: 'search'
desc: 'Number of UCT search per move'
type: 'number'
default: uct.defaults.max_search
requiresArg: true
b:
alias: 'balance'
desc: 'Balance outcome or win-loss'
default: true
type: 'boolean'
w:
alias: 'wld'
desc: 'Depth of win-loss-draw endgame search'
type: 'number'
default: make_player.defaults.solve_wld
requiresArg: true
f:
alias: 'full'
desc: 'Depth of full endgame search'
type: 'number'
default: make_player.defaults.solve_full
requiresArg: true
watch:
desc: 'Watch the file and exit when it changes'
default: 'weights.json'
requiresArg: true
wlb:
desc: 'Use win-loss balance instead of outcome'
type: 'boolean'
h:
alias: 'help'
.version false
.strict()
.argv
balance = 0
player = make_player
book: null
strategy: uct
evaluate: pattern_eval
max_search: argv.search
verbose: false
verbose: false
solve_wld: argv.wld
solve_full: argv.full
endgame_eval: pattern_eval
play = (player, opening) ->
ext.reset_hash() if ext.is_enabled and ext.reset_hash?
board = new Board
moves = []
for {move, turn} in opening
flips = board.move turn, move
throw new Error 'invalid move' unless flips.length
moves.push {move, turn}
turn = -turn
console.log board.dump true
loop
unless board.any_moves turn
turn = -turn
unless board.any_moves turn
break
{move, solved} = player board, turn
flips = board.move turn, move
unless flips.length
console.error 'invalid move'
console.log board.dump true
console.log move
console.log turn
console.log pos_to_str(move, turn)
process.exit 1
moves.push {move, turn, solved}
process.stdout.write pos_to_str(move, turn)
turn = -turn
outcome = board.outcome()
process.stdout.write " #{outcome}\n"
{moves, outcome}
unique_moves = (board, turn) ->
result = []
map = {}
for move in board.list_moves(turn)
flips = board.move turn, move
code = encode_normalized board
unless map[code]
map[code] = true
result.push move
board.undo turn, move, flips
result
find_unplayed_opening = (book, depth, cb) ->
console.log 'depth', depth
board = new Board
board.move BLACK, pos_from_str('f5')
moves = [{move:pos_from_str('f5'), turn:BLACK}]
sub = (turn, d) ->
if d >= depth
outcome = book.get_game_node(board)
unless outcome?
outcome = cb moves
return if d > depth
return unless argv.balance
if ((depth&1)==0 and balance < 0) or
((depth&1)==1 and balance > 0)
return
unless board.any_moves(turn)
turn = -turn
unless board.any_moves(turn)
return
for move in shuffle(unique_moves(board, turn))
flips = board.move turn, move
throw new Error 'invalid move' unless flips.length
moves.push {move, turn}
sub -turn, d+1
moves.pop()
board.undo turn, move, flips
sub WHITE, 1
do ->
book = new Book 'book.db'
book.init()
balance = book.sum_nodes_outcome()
reload = if argv.watch then watch_file(argv.watch) else -> false
n_games = 0
for depth in [argv.depth..60]
find_unplayed_opening book, depth, (opening) ->
process.exit 0 if reload()
console.log pos_array_to_str(opening)
{moves, outcome} = play(player, opening)
moves_str = pos_array_to_str(moves)
book.add_game moves
balance =
if argv.wlb
book.win_loss_balance()
else
book.sum_nodes_outcome()
console.log 'balance', balance
process.exit 0 if ++n_games >= argv.n
outcome