def write_sgf( self, path: str, trainer_config: Optional[Dict] = None, ): if trainer_config is None: trainer_config = self.katrain.config("trainer") save_feedback = trainer_config["save_feedback"] eval_thresholds = trainer_config["eval_thresholds"] def player_name(player_info): return f"{i18n._(player_info.player_type)} ({i18n._(player_info.player_subtype)})" if "KaTrain" in self.root.get_property("AP", ""): for bw in "BW": self.root.set_property("P" + bw, player_name(self.katrain.players_info[bw])) player_info = self.katrain.players_info[bw] if player_info.player_type == PLAYER_AI: self.root.set_property(bw + "R", rank_label(player_info.calculated_rank)) player_names = {bw: re.sub(r"['<>:\"/\\|?*]", "", self.root.get_property("P" + bw, bw)) for bw in "BW"} game_name = f"katrain_{player_names['B']} vs {player_names['W']} {self.game_id}" file_name = os.path.abspath(os.path.join(path, f"{game_name}.sgf")) os.makedirs(os.path.dirname(file_name), exist_ok=True) show_dots_for = { bw: trainer_config.get("eval_show_ai", True) or self.katrain.players_info[bw].human for bw in "BW" } sgf = self.root.sgf( save_comments_player=show_dots_for, save_comments_class=save_feedback, eval_thresholds=eval_thresholds ) with open(file_name, "w", encoding="utf-8") as f: f.write(sgf) return i18n._("sgf written").format(file_name=file_name)
def update_root_properties(self): def player_name(player_info): if player_info.name and player_info.player_type == PLAYER_HUMAN: return player_info.name else: return f"{i18n._(player_info.player_type)} ({i18n._(player_info.player_subtype)})" root_properties = self.root.properties x_properties = {} for bw in "BW": if not self.external_game: x_properties["P" + bw] = player_name(self.katrain.players_info[bw] ) + SGF_INTERNAL_COMMENTS_MARKER player_info = self.katrain.players_info[bw] if player_info.player_type == PLAYER_AI: x_properties[bw + "R"] = rank_label( player_info.calculated_rank) if "+" in str(self.end_result): x_properties["RE"] = self.end_result self.root.properties = { **root_properties, **{k: [v] for k, v in x_properties.items()} }
def update_players(self, *_args): for bw, player_info in self.katrain.players_info.items(): self.players[bw].player_type = player_info.player_type self.players[bw].player_subtype = player_info.player_subtype self.players[bw].name = player_info.name self.players[bw].rank = ( player_info.sgf_rank if player_info.player_type == PLAYER_HUMAN else rank_label(player_info.calculated_rank))
def estimate_rank_from_options(self, *_args): strategy = self.ai_select.selected[1] try: options = self.collect_properties(self) # [strategy] except InputParseError: self.estimated_rank_label.text = "??" return prefix = f"ai/{strategy}/" options = {k[len(prefix) :]: v for k, v in options.items() if k.startswith(prefix)} dan_rank = ai_rank_estimation(strategy, options) self.estimated_rank_label.text = rank_label(dan_rank)
def update_graph(self, *args): if self.rank_by_player: xscale = self.width / max(len(self.nodes) - 1, 15) available_height = self.height all_ranks = [rank for lst in self.rank_by_player.values() for seg, rank in lst if rank is not None] if not all_ranks: return min_rank = math.floor(min(all_ranks)) max_rank = math.ceil(max(all_ranks)) if max_rank == min_rank: min_rank -= 1 if (max_rank - min_rank) % 2 != 0: # make midpoint whole integer if abs(max_rank - max(all_ranks)) < abs(min(all_ranks) - min_rank) and max_rank < self.RANK_CAP: max_rank += 1 else: min_rank -= 1 rank_range = max_rank - min_rank self.ids.mid_marker.text = rank_label((max_rank + min_rank) / 2) self.ids.top_marker.text = rank_label(max_rank) + ("+" if max_rank == self.RANK_CAP else "") self.ids.bottom_marker.text = rank_label(min_rank) graph_points = {} for pl, rank_points in self.rank_by_player.items(): graph_points[pl] = [ [ self.x + i * xscale, self.y + available_height * (val - min_rank) / rank_range if val is not None else math.nan, ] for i, val in rank_points ] self.black_rank_points = sum(graph_points["B"], []) self.white_rank_points = sum(graph_points["W"], []) else: self.black_rank_points = [] self.white_rank_points = []
def generate_filename(self): def player_name(player_info): if player_info.name and player_info.player_type == PLAYER_HUMAN: return player_info.name else: return f"{i18n._(player_info.player_type)} ({i18n._(player_info.player_subtype)})" root_properties = self.root.properties x_properties = {} for bw in "BW": if not self.external_game: x_properties["P" + bw] = player_name(self.katrain.players_info[bw]) + SGF_INTERNAL_COMMENTS_MARKER player_info = self.katrain.players_info[bw] if player_info.player_type == PLAYER_AI: x_properties[bw + "R"] = rank_label(player_info.calculated_rank) if "+" in str(self.end_result): x_properties["RE"] = self.end_result x_properties["KTV"] = ANALYSIS_FORMAT_VERSION self.root.properties = {**root_properties, **{k: [v] for k, v in x_properties.items()}} player_names = { bw: re.sub(r"[\u200b\u3164'<>:\"/\\|?*]", "", self.root.get_property("P" + bw, bw)) for bw in "BW" } base_game_name = f"{PROGRAM_NAME}_{player_names['B']} vs {player_names['W']}" return f"{base_game_name} {self.game_id}.sgf"
def _refresh(self, _dt=0): game = self.katrain.game thresholds = self.katrain.config("trainer/eval_thresholds") sum_stats, histogram, player_ptloss = game_report( game, depth_filter=self.depth_filter, thresholds=thresholds) labels = [ f"≥ {pt}" if pt > 0 else f"< {thresholds[-2]}" for pt in thresholds ] table = GridLayout(cols=3, rows=6 + len(thresholds)) colors = [ [cp * 0.75 for cp in col[:3]] + [1] for col in Theme.EVAL_COLORS[self.katrain.config("trainer/theme")] ] table.add_widget( TableHeaderLabel(text="", background_color=Theme.BACKGROUND_COLOR)) table.add_widget( TableHeaderLabel(text=i18n._("header:keystats"), background_color=Theme.BACKGROUND_COLOR)) table.add_widget( TableHeaderLabel(text="", background_color=Theme.BACKGROUND_COLOR)) for i, (label, fmt, stat, scale, more_is_better) in enumerate([ ("accuracy", "{:.1f}", "accuracy", 100, True), ("meanpointloss", "{:.1f}", "mean_ptloss", 5, False), ("aitopmove", "{:.1%}", "ai_top_move", 1, True), ("aitop5", "{:.1%}", "ai_top5_move", 1, True), ]): statcell = { bw: TableStatLabel( text=fmt.format(sum_stats[bw][stat]) if stat in sum_stats[bw] else "", side=side, value=sum_stats[bw].get(stat, 0), scale=scale, bar_color=Theme.STAT_BETTER_COLOR if (sum_stats[bw].get(stat, 0) < sum_stats[Move.opponent_player(bw)].get(stat, 0)) ^ more_is_better else Theme.STAT_WORSE_COLOR, background_color=Theme.BOX_BACKGROUND_COLOR, ) for (bw, side) in zip("BW", ["left", "right"]) } table.add_widget(statcell["B"]) table.add_widget( TableCellLabel(text=i18n._(f"stat:{label}"), background_color=Theme.BOX_BACKGROUND_COLOR)) table.add_widget(statcell["W"]) table.add_widget( TableHeaderLabel(text=i18n._("header:num moves"), background_color=Theme.BACKGROUND_COLOR)) table.add_widget( TableHeaderLabel(text=i18n._("stats:pointslost"), background_color=Theme.BACKGROUND_COLOR)) table.add_widget( TableHeaderLabel(text=i18n._("header:num moves"), background_color=Theme.BACKGROUND_COLOR)) for i, (col, label, pt) in enumerate( zip(colors[::-1], labels[::-1], thresholds[::-1])): statcell = { bw: TableStatLabel( text=str(histogram[i][bw]), side=side, value=histogram[i][bw], scale=len(player_ptloss[bw]) + 1e-6, bar_color=col, background_color=Theme.BOX_BACKGROUND_COLOR, ) for (bw, side) in zip("BW", ["left", "right"]) } table.add_widget(statcell["B"]) table.add_widget(TableCellLabel(text=label, background_color=col)) table.add_widget(statcell["W"]) self.stats.clear_widgets() self.stats.add_widget(table) for bw, player_info in self.katrain.players_info.items(): self.player_infos[bw].player_type = player_info.player_type self.player_infos[bw].captures = "" # ;) self.player_infos[bw].player_subtype = player_info.player_subtype self.player_infos[bw].name = player_info.name self.player_infos[bw].rank = ( player_info.sgf_rank if player_info.player_type == PLAYER_HUMAN else rank_label(player_info.calculated_rank)) # if not done analyzing, check again in 1s if not self.katrain.engine.is_idle(): Clock.schedule_once(self._refresh, 1)