def format_delta_info(delta, this_move, board_size): comment = "" LB_values = [] if delta <= -0.2: comment += "=================================\n" comment += "Leela thinks %s is a big mistake!\n" % convert_position( board_size, this_move) comment += "Winning percentage drops by %.2f%%!\n" % (-delta * 100) comment += "=================================\n" if not is_pass(board_size, this_move): LB_values.append("%s:%s" % (this_move, "?")) elif delta <= -0.1: comment += "=================================\n" comment += "Leela thinks %s is a mistake!\n" % convert_position( board_size, this_move) comment += "Winning percentage drops by %.2f%%\n" % (-delta * 100) comment += "=================================\n" if not is_pass(board_size, this_move): LB_values.append("%s:%s" % (this_move, "?")) elif delta <= -0.05: comment += "=================================\n" comment += "Leela thinks %s is not the best choice.\n" % convert_position( board_size, this_move) comment += "Winning percentage drops by %.2f%%\n" % (-delta * 100) comment += "=================================\n" if not is_pass(board_size, this_move): LB_values.append("%s:%s" % (this_move, "?")) elif delta <= -0.025: comment += "=================================\n" comment += "Leela slightly dislikes %s.\n" % convert_position( board_size, this_move) comment += "=================================\n" comment += "\n" return comment, LB_values
def analyze(self): """Analyze current position with given seconds per search.""" stdout, stderr = self.genmove() # Drain and parse Leela stdout & stderr stats, move_list = self.parse_analysis(stdout, stderr) if stats.get('winrate') and move_list: best_move = convert_position(self.board_size, move_list[0]['pos']) winrate = (stats['winrate'] * 100) visits = stats['visits'] pv = " ".join([convert_position(self.board_size, m) for m in move_list[0]['pv']]) logger.debug(f"Suggested: %s (winrate %.2f%%, %d visits). Perfect sequence: %s", best_move, winrate, visits, pv) else: chosen_move = convert_position(self.board_size, stats['chosen']) logger.debug(f"Chosen move: %s", chosen_move) return stats, move_list
def format_winrate(stats, move_list, board_size, next_game_move): comment = "" if 'winrate' in stats: comment += "Overall black win%%: %.2f%%\n" % (stats['winrate'] * 100) else: comment += "Overall black win%: not computed (Leela still in opening book)\n" if len(move_list) > 0 and move_list[0]['pos'] != next_game_move: comment += "Leela's preferred next move: %s\n" % convert_position(board_size, move_list[0]['pos']) else: comment += "\n" return comment
def analyze_main_line(self): logger.info("Started analyzing main line.") move_num = -1 prev_stats = {} prev_move_list = [] has_prev = False previous_player = None logger.info(f"Executing analysis for %d moves", len(self.moves_to_analyze)) moves_count = 0 self.cursor.reset() self.bot = self.factory() self.bot.time_per_move = CONFIG['analyze_time'] self.bot.start() # analyze main line, without variations while not self.cursor.atEnd: self.cursor.next() move_num += 1 this_move = self.add_moves_to_bot() current_player = 'black' if 'W' in self.cursor.node else 'white' if previous_player == current_player: raise BotException('Two consecutive moves.') if move_num in self.moves_to_analyze: stats, move_list = self.do_analyze() # Here we store ALL statistics self.all_stats[move_num] = stats self.all_move_lists[move_num] = move_list if move_list and 'winrate' in move_list[0]: self.best_moves[move_num] = move_list[0] delta = 0.0 if 'winrate' in stats and (move_num - 1) in self.best_moves: if this_move != self.best_moves[move_num - 1]['pos']: delta = stats['winrate'] - self.best_moves[move_num - 1]['winrate'] delta = min(0.0, (-delta if self.bot.whose_turn() == "black" else delta)) if -delta > CONFIG['analyze_threshold']: (delta_comment, delta_lb_values) = annotations.format_delta_info(delta, this_move, self.board_size) annotations.annotate_sgf(self.cursor, delta_comment, delta_lb_values, []) if has_prev and delta <= -CONFIG['variations_threshold']: self.moves_to_variations[move_num - 1] = True if -delta > CONFIG['analyze_threshold']: logger.warning("Move %d: %s %s is a mistake (winrate dropped by %.2f%%)", move_num + 1, previous_player, convert_position(self.board_size, this_move), -delta * 100) next_game_move = self.next_move_pos() annotations.annotate_sgf(self.cursor, annotations.format_winrate(stats, move_list, self.board_size, next_game_move), [], []) if has_prev and ((move_num - 1) in self.moves_to_analyze and -delta > CONFIG['analyze_threshold'] or ( move_num - 1) in self.moves_to_variations): (analysis_comment, lb_values, tr_values) = annotations.format_analysis( prev_stats, filter_move_list(prev_move_list), this_move, self.board_size) self.cursor.previous() # adding comment to sgf with suggested alternative variations annotations.annotate_sgf(self.cursor, analysis_comment, lb_values, tr_values) self.cursor.next() prev_stats = stats prev_move_list = move_list has_prev = True self.save_to_file() self.graph_winrates() if 'winrate' in stats \ and (1 - CONFIG['stop_on_winrate'] > stats['winrate'] or stats['winrate'] > CONFIG['stop_on_winrate']): break moves_count += 1 logger.info("Analysis done for %d/%d move.", moves_count, len(self.moves_to_analyze)) else: prev_stats = {} prev_move_list = [] has_prev = False previous_player = current_player logger.info("Finished analyzing main line.")
def current_position(self, frame="/odom_combined"): return utils.convert_position(self.listener, (0, 0, 0), (0, 0, 0), "/base_link", frame)
while not game.cursor.atEnd: game.cursor.next() move_num += 1 this_move = game.add_moves_to_bot() game.bot.go_to_position() # c_node = game.cursor.node # nnode = Node() # game.cursor.append_node(nnode) for i in range(1): stdout, stderr = game.bot.genmove() # Drain and parse Leela stdout & stderr stats, move_list = game.bot.parse_analysis(stdout, stderr) if stats.get('winrate') and move_list: best_move = convert_position(19, move_list[0]['pos']) print("result={}".format(best_move)) color = game.bot.whose_turn() best_move = parse_position(19, best_move) command = f"play {color} {best_move}" print("index is {},play is {}".format(i, command)) # game.bot.send_command(command) # this_move = game.add_moves_to_bot() game.bot.add_move_to_history(color, best_move) clr = 'W' if color == 'white' else 'B' nnode = Node() nnode.add_property(Property(clr, [best_move])) game.cursor.append_node(nnode) game.cursor.next(len(game.cursor.children) - 1)
def test_convert_position_valid(): assert convert_position(19, 'aa') == 'A19' assert convert_position(19, 'as') == 'A1' assert convert_position(19, 'sa') == 'T19' assert convert_position(19, 'ss') == 'T1' assert convert_position(19, 'jj') == 'K10' assert convert_position(19, '') == 'pass' assert convert_position(13, 'aa') == 'A13' assert convert_position(13, 'am') == 'A1' assert convert_position(13, 'ma') == 'N13' assert convert_position(13, 'mm') == 'N1' assert convert_position(13, 'gg') == 'G7' assert convert_position(13, '') == 'pass' assert convert_position(9, 'aa') == 'A9' assert convert_position(9, 'ai') == 'A1' assert convert_position(9, 'ia') == 'J9' assert convert_position(9, 'ii') == 'J1' assert convert_position(9, 'ee') == 'E5' assert convert_position(9, '') == 'pass'
def current_position(self, frame="/odom_combined"): return utils.convert_position(self.listener, (0,0,0), (0,0,0), "/base_link", frame)
def add_move_to_history(self, color: str, pos: str): """ Convert given SGF coordinates to GTP console command""" move = convert_position(self.board_size, pos) command = f"play {color} {move}" self._history.append(command)