distinguish between analysis exists and completed, fix bugs
This commit is contained in:
1 parent
e85b6f56ce
commit
bed746087a
7 files changed
+43
-32
No files matched your search
+3
-3
@@ -193,14 +193,14 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
teaching_undo = cn.player and last_player.being_taught and cn.parent
|
||||
if (
|
||||
teaching_undo
|
||||
and cn.analysis_ready
|
||||
and cn.parent.analysis_ready
|
||||
and cn.analysis_complete
|
||||
and cn.parent.analysis_complete
|
||||
and not cn.children
|
||||
and not self.game.end_result
|
||||
):
|
||||
self.game.analyze_undo(cn) # not via message loop
|
||||
if (
|
||||
cn.analysis_ready
|
||||
cn.analysis_complete
|
||||
and next_player.ai
|
||||
and not cn.children
|
||||
and not self.game.end_result
|
||||
|
||||
+4
-3
@@ -164,9 +164,10 @@ def request_ai_analysis(game: Game, cn: GameNode, extra_settings: Dict) -> Optio
|
||||
error = False
|
||||
analysis = None
|
||||
|
||||
def set_analysis(a):
|
||||
def set_analysis(a, partial_result):
|
||||
nonlocal analysis
|
||||
analysis = a
|
||||
if not partial_result:
|
||||
analysis = a
|
||||
|
||||
def set_error(a):
|
||||
nonlocal error
|
||||
@@ -205,7 +206,7 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
|
||||
game.katrain.log(f"Error getting handicap-based move", OUTPUT_ERROR)
|
||||
ai_mode = AI_DEFAULT
|
||||
|
||||
while not cn.analysis_ready:
|
||||
while not cn.analysis_complete:
|
||||
time.sleep(0.01)
|
||||
game.engines[cn.next_player].check_alive(exception_if_dead=True)
|
||||
|
||||
|
||||
@@ -196,15 +196,17 @@ class KataGoEngine:
|
||||
elif "warning" in analysis:
|
||||
self.katrain.log(f"{analysis} received from KataGo", OUTPUT_DEBUG)
|
||||
else:
|
||||
if not analysis.get("isDuringSearch", False):
|
||||
partial_result = analysis.get("isDuringSearch", False)
|
||||
if not partial_result:
|
||||
del self.queries[query_id]
|
||||
time_taken = time.time() - start_time
|
||||
self.katrain.log(
|
||||
f"[{time_taken:.1f}][{query_id}] KataGo Analysis Received: {analysis.keys()}", OUTPUT_DEBUG,
|
||||
f"[{time_taken:.1f}][{query_id}][{'....' if partial_result else 'done'}] KataGo Analysis Received: {analysis.keys()}",
|
||||
OUTPUT_DEBUG,
|
||||
)
|
||||
self.katrain.log(line, OUTPUT_EXTRA_DEBUG)
|
||||
try:
|
||||
callback(analysis)
|
||||
callback(analysis, partial_result=partial_result)
|
||||
except Exception as e:
|
||||
self.katrain.log(f"Error in engine callback for query {query_id}: {e}", OUTPUT_ERROR)
|
||||
if getattr(self.katrain, "update_state", None): # easier mocking etc
|
||||
|
||||
@@ -87,7 +87,7 @@ class Game:
|
||||
def analyze_all_nodes(self, priority=0, analyze_fast=False):
|
||||
for node in self.root.nodes_in_tree:
|
||||
node.analyze(
|
||||
self.engines[node.next_player], priority=priority, analyze_fast=analyze_fast, report_every=None
|
||||
self.engines[node.next_player], priority=priority, analyze_fast=analyze_fast #, report_every=None
|
||||
)
|
||||
|
||||
# -- move tree functions --
|
||||
@@ -347,7 +347,7 @@ class Game:
|
||||
|
||||
elif mode == "sweep":
|
||||
board_size_x, board_size_y = self.board_size
|
||||
if cn.analysis_ready:
|
||||
if cn.analysis_exists:
|
||||
policy_grid = (
|
||||
var_to_grid(self.current_node.policy, size=(board_size_x, board_size_y))
|
||||
if self.current_node.policy
|
||||
@@ -373,7 +373,7 @@ class Game:
|
||||
self.katrain.controls.set_status(i18n._("sweep analysis").format(visits=visits), STATUS_ANALYSIS)
|
||||
priority = -1_000_000_000
|
||||
elif mode in ["equalize", "alternative"]:
|
||||
if not cn.analysis_ready:
|
||||
if not cn.analysis_complete:
|
||||
self.katrain.controls.set_status(i18n._("wait-before-equalize"), STATUS_INFO, self.current_node)
|
||||
return
|
||||
|
||||
@@ -397,7 +397,7 @@ class Game:
|
||||
def analyze_undo(self, node):
|
||||
train_config = self.katrain.config("trainer")
|
||||
move = node.move
|
||||
if node != self.current_node or node.auto_undo is not None or not node.analysis_ready or not move:
|
||||
if node != self.current_node or node.auto_undo is not None or not node.analysis_complete or not move:
|
||||
return
|
||||
points_lost = node.points_lost
|
||||
thresholds = train_config["eval_thresholds"]
|
||||
|
||||
+23
-15
@@ -13,7 +13,7 @@ class GameNode(SGFNode):
|
||||
|
||||
def __init__(self, parent=None, properties=None, move=None):
|
||||
super().__init__(parent=parent, properties=properties, move=move)
|
||||
self.analysis = {"moves": {}, "root": None}
|
||||
self.analysis = {"moves": {}, "root": None, "completed": False}
|
||||
self.ownership = None
|
||||
self.policy = None
|
||||
self.auto_undo = None # None = not analyzed. False: not undone (good move). True: undone (bad move)
|
||||
@@ -34,8 +34,8 @@ class GameNode(SGFNode):
|
||||
show_class = False
|
||||
if (
|
||||
self.parent
|
||||
and self.parent.analysis_ready
|
||||
and self.analysis_ready
|
||||
and self.parent.analysis_exists
|
||||
and self.analysis_exists
|
||||
and (note or ((save_comments_player or {}).get(self.player, False) and show_class))
|
||||
):
|
||||
candidate_moves = self.parent.candidate_moves
|
||||
@@ -81,7 +81,7 @@ class GameNode(SGFNode):
|
||||
):
|
||||
engine.request_analysis(
|
||||
self,
|
||||
lambda result: self.set_analysis(result, refine_move, find_alternatives),
|
||||
lambda result, partial_result: self.set_analysis(result, refine_move, find_alternatives, partial_result),
|
||||
priority=priority,
|
||||
visits=visits,
|
||||
analyze_fast=analyze_fast,
|
||||
@@ -104,7 +104,9 @@ class GameNode(SGFNode):
|
||||
if cur["visits"] < move_analysis["visits"]:
|
||||
cur.update(move_analysis)
|
||||
|
||||
def set_analysis(self, analysis_json: Dict, refine_move: Optional[Move], alternatives_mode: bool):
|
||||
def set_analysis(
|
||||
self, analysis_json: Dict, refine_move: Optional[Move], alternatives_mode: bool, partial_result: bool = False
|
||||
):
|
||||
if refine_move:
|
||||
pvtail = analysis_json["moveInfos"][0]["pv"] if analysis_json["moveInfos"] else []
|
||||
self.update_move_analysis(
|
||||
@@ -130,14 +132,20 @@ class GameNode(SGFNode):
|
||||
self.parent.update_move_analysis(
|
||||
analysis_json["rootInfo"], self.move.gtp()
|
||||
) # update analysis in parent for consistency
|
||||
is_normal_query = refine_move is None and not alternatives_mode
|
||||
self.analysis["completed"] = self.analysis["completed"] or (is_normal_query and not partial_result)
|
||||
|
||||
@property
|
||||
def analysis_ready(self):
|
||||
def analysis_exists(self):
|
||||
return self.analysis["root"] is not None
|
||||
|
||||
@property
|
||||
def analysis_complete(self):
|
||||
return self.analysis["completed"] and self.analysis["root"] is not None
|
||||
|
||||
@property
|
||||
def score(self) -> Optional[float]:
|
||||
if self.analysis_ready:
|
||||
if self.analysis_exists:
|
||||
return self.analysis["root"].get("scoreLead")
|
||||
|
||||
def format_score(self, score=None):
|
||||
@@ -147,7 +155,7 @@ class GameNode(SGFNode):
|
||||
|
||||
@property
|
||||
def winrate(self) -> Optional[float]:
|
||||
if self.analysis_ready:
|
||||
if self.analysis_exists:
|
||||
return self.analysis["root"].get("winrate")
|
||||
|
||||
def format_winrate(self, win_rate=None):
|
||||
@@ -178,12 +186,12 @@ class GameNode(SGFNode):
|
||||
return ""
|
||||
|
||||
text = i18n._("move").format(number=self.depth) + f": {single_move.player} {single_move.gtp()}\n"
|
||||
if self.analysis_ready:
|
||||
if self.analysis_exists:
|
||||
score = self.score
|
||||
if sgf:
|
||||
text += i18n._("Info:score").format(score=self.format_score(score)) + "\n"
|
||||
text += i18n._("Info:winrate").format(winrate=self.format_winrate()) + "\n"
|
||||
if self.parent and self.parent.analysis_ready:
|
||||
if self.parent and self.parent.analysis_exists:
|
||||
previous_top_move = self.parent.candidate_moves[0]
|
||||
if sgf or details:
|
||||
if previous_top_move["move"] != single_move.gtp():
|
||||
@@ -210,7 +218,7 @@ class GameNode(SGFNode):
|
||||
text += policy_best_msg.format(move=pol_move, probability=pol_prob) + "\n"
|
||||
if self.auto_undo and sgf:
|
||||
text += i18n._("Info:teaching undo") + "\n"
|
||||
top_pv = self.analysis_ready and self.candidate_moves[0].get("pv")
|
||||
top_pv = self.analysis_exists and self.candidate_moves[0].get("pv")
|
||||
if top_pv:
|
||||
text += i18n._("Info:undo predicted PV").format(pv=f"{self.next_player}{' '.join(top_pv)}") + "\n"
|
||||
else:
|
||||
@@ -227,7 +235,7 @@ class GameNode(SGFNode):
|
||||
@property
|
||||
def points_lost(self) -> Optional[float]:
|
||||
single_move = self.move
|
||||
if single_move and self.parent and self.analysis_ready and self.parent.analysis_ready:
|
||||
if single_move and self.parent and self.analysis_exists and self.parent.analysis_exists:
|
||||
parent_score = self.parent.score
|
||||
score = self.score
|
||||
return self.player_sign(single_move.player) * (parent_score - score)
|
||||
@@ -239,8 +247,8 @@ class GameNode(SGFNode):
|
||||
single_move
|
||||
and self.parent
|
||||
and self.parent.parent
|
||||
and self.analysis_ready
|
||||
and self.parent.parent.analysis_ready
|
||||
and self.analysis_exists
|
||||
and self.parent.parent.analysis_exists
|
||||
):
|
||||
parent_parent_score = self.parent.parent.score
|
||||
score = self.score
|
||||
@@ -252,7 +260,7 @@ class GameNode(SGFNode):
|
||||
|
||||
@property
|
||||
def candidate_moves(self) -> List[Dict]:
|
||||
if not self.analysis_ready:
|
||||
if not self.analysis_exists:
|
||||
return []
|
||||
if not self.analysis["moves"]:
|
||||
polmoves = self.policy_ranking
|
||||
|
||||
@@ -140,7 +140,7 @@ class BadukPanWidget(Widget):
|
||||
katrain.log(f"\nRoot Stats:\n{nodes_here[-1].analysis['root']}", OUTPUT_DEBUG)
|
||||
katrain.controls.info.text = nodes_here[-1].comment(sgf=True)
|
||||
katrain.controls.active_comment_node = nodes_here[-1]
|
||||
if nodes_here[-1].parent.analysis_ready:
|
||||
if nodes_here[-1].parent.analysis_exists:
|
||||
self.set_animating_pv(nodes_here[-1].parent.candidate_moves[0]["pv"], nodes_here[-1].parent)
|
||||
|
||||
self.ghost_stone = None
|
||||
@@ -499,7 +499,7 @@ class BadukPanWidget(Widget):
|
||||
for child_node in current_node.children:
|
||||
move = child_node.move
|
||||
if move and move.coords is not None:
|
||||
if child_node.analysis_ready:
|
||||
if child_node.analysis_exists:
|
||||
self.active_pv_moves.append(
|
||||
(move.coords, [move.gtp()] + child_node.candidate_moves[0]["pv"], current_node)
|
||||
)
|
||||
|
||||
@@ -123,7 +123,7 @@ class ControlsPanel(BoxLayout):
|
||||
self.active_comment_node = current_node.children[-1]
|
||||
elif current_node.parent:
|
||||
self.active_comment_node = current_node.parent
|
||||
elif both_players_are_robots and not current_node.analysis_ready and current_node.parent:
|
||||
elif both_players_are_robots and not current_node.analysis_exists and current_node.parent:
|
||||
self.active_comment_node = current_node.parent
|
||||
|
||||
lock_ai = katrain.config("trainer/lock_ai") and katrain.play_analyze_mode == MODE_PLAY
|
||||
@@ -134,7 +134,7 @@ class ControlsPanel(BoxLayout):
|
||||
teach=katrain.players_info[self.active_comment_node.player].being_taught, details=details
|
||||
)
|
||||
|
||||
if self.active_comment_node.analysis_ready:
|
||||
if self.active_comment_node.analysis_exists:
|
||||
self.stats.score = self.active_comment_node.format_score() or ""
|
||||
self.stats.winrate = self.active_comment_node.format_winrate() or ""
|
||||
self.stats.points_lost = self.active_comment_node.points_lost
|
||||
|
||||
Reference in new issue
Block a user