distinguish between analysis exists and completed, fix bugs

This commit is contained in:
Sander Land committed 2020-10-18 11:56:38 +02:00
1 parent e85b6f56ce
commit bed746087a
7 files changed
+43 -32

No files matched your search

+3 -3
View File
@@ -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
View File
@@ -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)
+5 -3
View File
@@ -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
+4 -4
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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)
)
+2 -2
View File
@@ -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