def parse_move(self, move_list, line): m = re.match(self.move_regex, line) if m is not None: pos = parse_position(self.board_size, m.group(1)) visits = int(m.group(2)) winrate = self.flip_winrate(str_to_percent(m.group(3))) mc_winrate = self.flip_winrate(str_to_percent(m.group(4))) nn_winrate = self.flip_winrate(str_to_percent(m.group(5))) nn_count = int(m.group(6)) policy_prob = str_to_percent(m.group(7)) pv = [parse_position(self.board_size, p) for p in m.group(8).split()] info = { 'pos': pos, 'visits': visits, 'winrate': winrate, 'mc_winrate': mc_winrate, 'nn_winrate': nn_winrate, 'nn_count': nn_count, 'policy_prob': policy_prob, 'pv': pv, 'color': self.whose_turn() } move_list.append(info) m = re.match(self.move_regex_no_vn, line) if m is not None: pos = parse_position(self.board_size, m.group(1)) visits = int(m.group(2)) mc_winrate = self.flip_winrate(str_to_percent(m.group(3))) r_winrate = self.flip_winrate(str_to_percent(m.group(4))) r_count = int(m.group(5)) policy_prob = str_to_percent(m.group(6)) pv = m.group(7) pv = [parse_position(self.board_size, p) for p in pv.split()] info = { 'pos': pos, 'visits': visits, 'winrate': mc_winrate, 'mc_winrate': mc_winrate, 'r_winrate': r_winrate, 'r_count': r_count, 'policy_prob': policy_prob, 'pv': pv, 'color': self.whose_turn() } move_list.append(info) return move_list
def parse_status_update(self, message): """ Parse number of visits, winrate and PV sequence :param message: :return: dictionary """ m = re.match(update_regex, message) # For non-neural-network if m is None: m = re.match(update_regex_no_vn, message) if m is not None: visits = int(m.group(1)) winrate = self.to_fraction(m.group(2)) seq = m.group(3) seq = [ utils.parse_position(self.board_size, p) for p in seq.split() ] return {'visits': visits, 'winrate': winrate, 'seq': seq} return {}
def parse_finished(self, stats, stdout): m = re.search(self.finished_regex, "".join(stdout)) if m is not None: stats['chosen'] = "resign" if m.group(1) == "resign" else parse_position(self.board_size, m.group(1)) return stats
def parse_best(self, stats, line): m = re.match(self.best_regex, line) if m is not None: stats['best'] = parse_position(self.board_size, m.group(3).split()[0]) stats['winrate'] = self.flip_winrate(str_to_percent(m.group(2))) return stats
def parse(self, stdout, stderr): """ Parse Leela stdout & stderr :param stdout: string :param stderr: string :return: stats, move_list """ if self.verbosity > 2: print("LEELA STDOUT:\n" + "".join(stdout) + "\END OF LEELA STDOUT", file=sys.stderr) print("LEELA STDERR:\n" + "".join(stderr) + "\END OF LEELA STDERR", file=sys.stderr) stats = {} move_list = [] def flip_winrate(wr): return (1.0 - wr) if self.whose_turn() == "white" else wr # function filter given list of moves by criteria or win-rate and visits def filter_redundant_moves(move_list, stats): best_move_visits = stats['visits'] best_move_winrate = stats['winrate'] return list( filter( lambda move: (best_move_winrate - move['winrate']) < 0.1 and (best_move_visits / move['visits']) < 20, move_list)) finished = False summarized = False for line in stderr: line = line.strip() if line.startswith('================'): finished = True # Find bookmove string m = re.match(bookmove_regex, line) if m is not None: stats['bookmoves'] = int(m.group(1)) stats['positions'] = int(m.group(2)) # Find status string m = re.match(status_regex, line) if m is not None: stats['mc_winrate'] = flip_winrate(float(m.group(1))) stats['nn_winrate'] = flip_winrate(float(m.group(2))) stats['margin'] = m.group(3) m = re.match(status_regex_no_vn, line) if m is not None: stats['mc_winrate'] = flip_winrate(float(m.group(1))) stats['margin'] = m.group(2) # Find move string m = re.match(move_regex, line) if m is not None: pos = utils.parse_position(self.board_size, m.group(1)) visits = int(m.group(2)) winrate = flip_winrate(self.to_fraction(m.group(3))) mc_winrate = flip_winrate(self.to_fraction(m.group(4))) nn_winrate = flip_winrate(self.to_fraction(m.group(5))) nn_count = int(m.group(6)) policy_prob = self.to_fraction(m.group(7)) pv = [ utils.parse_position(self.board_size, p) for p in m.group(8).split() ] info = { 'pos': pos, 'visits': visits, 'winrate': winrate, 'mc_winrate': mc_winrate, 'nn_winrate': nn_winrate, 'nn_count': nn_count, 'policy_prob': policy_prob, 'pv': pv, 'color': self.whose_turn() } move_list.append(info) m = re.match(move_regex_no_vn, line) if m is not None: pos = utils.parse_position(self.board_size, m.group(1)) visits = int(m.group(2)) mc_winrate = flip_winrate(self.to_fraction(m.group(3))) r_winrate = flip_winrate(self.to_fraction(m.group(4))) r_count = int(m.group(5)) policy_prob = self.to_fraction(m.group(6)) pv = m.group(7) pv = [ utils.parse_position(self.board_size, p) for p in pv.split() ] info = { 'pos': pos, 'visits': visits, 'winrate': mc_winrate, 'mc_winrate': mc_winrate, 'r_winrate': r_winrate, 'r_count': r_count, 'policy_prob': policy_prob, 'pv': pv, 'color': self.whose_turn() } move_list.append(info) if finished and not summarized: m = re.match(best_regex, line) # Parse best move and its winrate if m is not None: stats['best'] = utils.parse_position( self.board_size, m.group(3).split()[0]) stats['winrate'] = flip_winrate( self.to_fraction(m.group(2))) # Parse number of visits to stats m = re.match(stats_regex, line) if m is not None: stats['visits'] = int(m.group(1)) summarized = True # Find finished string m = re.search(finished_regex, "".join(stdout)) # Add chosen move to stats if m is not None: stats['chosen'] = "resign" if m.group( 1) == "resign" else utils.parse_position( self.board_size, m.group(1)) # Add book move to move list if 'bookmoves' in stats and len(move_list) == 0: move_list.append({'pos': stats['chosen'], 'is_book': True}) else: required_keys = [ 'mc_winrate', 'margin', 'best', 'winrate', 'visits' ] # Check for missed data for k in required_keys: if k not in stats: print("WARNING: analysis stats missing %s data" % k, file=sys.stderr) move_list = sorted( move_list, key=(lambda key: 1000000000000000 if info['pos'] == stats['best'] else info['visits']), reverse=True) move_list = [ info for (i, info) in enumerate(move_list) if i == 0 or info['visits'] > 0 ] move_list = filter_redundant_moves(move_list, stats) # In the case where Leela resigns, just replace with the move Leela did think was best if stats['chosen'] == "resign": stats['chosen'] = stats['best'] return stats, move_list