ai selfplay, black 120
This commit is contained in:
1 parent
06cc316326
commit
9c6d521a88
16 files changed
+566
-134
No files matched your search
+90
-18
@@ -21,7 +21,9 @@ class GameNode(SGFNode):
|
||||
self.move_number = 0
|
||||
self.time_used = 0
|
||||
self.analysis_visits_requested = 0
|
||||
self.undo_threshold = random.random() # for fractional undos, store the random threshold in the move itself for consistency
|
||||
self.undo_threshold = (
|
||||
random.random()
|
||||
) # for fractional undos, store the random threshold in the move itself for consistency
|
||||
self._favourite_child = None
|
||||
|
||||
def sgf_properties(self, save_comments_player=None, save_comments_class=None, eval_thresholds=None):
|
||||
@@ -30,10 +32,18 @@ class GameNode(SGFNode):
|
||||
show_class = save_comments_class[evaluation_class(self.points_lost, eval_thresholds)]
|
||||
else:
|
||||
show_class = False
|
||||
if (save_comments_player or {}).get(self.player, False) and show_class and self.analysis_ready and self.parent and self.parent.analysis_ready:
|
||||
if (
|
||||
(save_comments_player or {}).get(self.player, False)
|
||||
and show_class
|
||||
and self.analysis_ready
|
||||
and self.parent
|
||||
and self.parent.analysis_ready
|
||||
):
|
||||
candidate_moves = self.parent.candidate_moves
|
||||
top_x = Move.from_gtp(candidate_moves[0]["move"]).sgf(self.board_size)
|
||||
best_sq = [Move.from_gtp(d["move"]).sgf(self.board_size) for d in candidate_moves[1:] if d["pointsLost"] <= 0.5]
|
||||
best_sq = [
|
||||
Move.from_gtp(d["move"]).sgf(self.board_size) for d in candidate_moves[1:] if d["pointsLost"] <= 0.5
|
||||
]
|
||||
if best_sq and "SQ" not in properties:
|
||||
properties["SQ"] = best_sq
|
||||
if top_x and "MA" not in properties:
|
||||
@@ -42,14 +52,21 @@ class GameNode(SGFNode):
|
||||
if comment:
|
||||
properties["C"] = ["\n".join(properties.get("C", "")) + comment]
|
||||
if self.is_root:
|
||||
properties["C"] = [i18n._("SGF start message") + "\n" + "\n".join(properties.get("C", "")) + "\nSGF with review generated by KaTrain."]
|
||||
properties["C"] = [
|
||||
i18n._("SGF start message")
|
||||
+ "\n"
|
||||
+ "\n".join(properties.get("C", ""))
|
||||
+ "\nSGF with review generated by KaTrain."
|
||||
]
|
||||
if self.note.strip():
|
||||
properties["C"] = ["\n".join(properties.get("C", "")) + f"\nNote: {self.note}"]
|
||||
return properties
|
||||
|
||||
@staticmethod
|
||||
def order_children(children):
|
||||
return sorted(children, key=lambda c: 0.5 if c.auto_undo is None else int(c.auto_undo)) # analyzed/not undone main, non-teach second, undone last
|
||||
return sorted(
|
||||
children, key=lambda c: 0.5 if c.auto_undo is None else int(c.auto_undo)
|
||||
) # analyzed/not undone main, non-teach second, undone last
|
||||
|
||||
def set_favourite_child(self, c):
|
||||
self._favourite_child = c
|
||||
@@ -66,20 +83,32 @@ class GameNode(SGFNode):
|
||||
if visits and not refine_move:
|
||||
self.analysis_visits_requested = max(self.analysis_visits_requested, engine.config["max_visits"])
|
||||
engine.request_analysis(
|
||||
self, lambda result: self.set_analysis(result, refine_move), priority=priority, visits=visits, analyze_fast=analyze_fast, time_limit=time_limit, next_move=refine_move
|
||||
self,
|
||||
lambda result: self.set_analysis(result, refine_move),
|
||||
priority=priority,
|
||||
visits=visits,
|
||||
analyze_fast=analyze_fast,
|
||||
time_limit=time_limit,
|
||||
next_move=refine_move,
|
||||
)
|
||||
|
||||
def update_move_analysis(self, move_analysis, move_gtp):
|
||||
cur = self.analysis["moves"].get(move_gtp)
|
||||
if cur is None:
|
||||
self.analysis["moves"][move_gtp] = {"move": move_gtp, "order": 999, **move_analysis} # some default values for keys missing in rootInfo
|
||||
self.analysis["moves"][move_gtp] = {
|
||||
"move": move_gtp,
|
||||
"order": 999,
|
||||
**move_analysis,
|
||||
} # some default values for keys missing in rootInfo
|
||||
elif cur["visits"] < move_analysis["visits"]:
|
||||
cur.update(move_analysis)
|
||||
|
||||
def set_analysis(self, analysis_json, refine_move):
|
||||
if refine_move:
|
||||
pvtail = analysis_json["moveInfos"][0]["pv"] if analysis_json["moveInfos"] else []
|
||||
self.update_move_analysis({"pv": [refine_move.gtp()] + pvtail, **analysis_json["rootInfo"]}, refine_move.gtp())
|
||||
self.update_move_analysis(
|
||||
{"pv": [refine_move.gtp()] + pvtail, **analysis_json["rootInfo"]}, refine_move.gtp()
|
||||
)
|
||||
else:
|
||||
for move_analysis in analysis_json["moveInfos"]:
|
||||
self.update_move_analysis(move_analysis, move_analysis["move"])
|
||||
@@ -87,8 +116,12 @@ class GameNode(SGFNode):
|
||||
self.policy = analysis_json.get("policy")
|
||||
self.analysis["root"] = analysis_json["rootInfo"]
|
||||
if self.parent and self.move:
|
||||
analysis_json["rootInfo"]["pv"] = [self.move.gtp()] + (analysis_json["moveInfos"][0]["pv"] if analysis_json["moveInfos"] else [])
|
||||
self.parent.update_move_analysis(analysis_json["rootInfo"], self.move.gtp()) # update analysis in parent for consistency
|
||||
analysis_json["rootInfo"]["pv"] = [self.move.gtp()] + (
|
||||
analysis_json["moveInfos"][0]["pv"] if analysis_json["moveInfos"] else []
|
||||
)
|
||||
self.parent.update_move_analysis(
|
||||
analysis_json["rootInfo"], self.move.gtp()
|
||||
) # update analysis in parent for consistency
|
||||
|
||||
@property
|
||||
def analysis_ready(self):
|
||||
@@ -138,19 +171,44 @@ class GameNode(SGFNode):
|
||||
points_lost = self.points_lost
|
||||
if sgf and points_lost > 0.5:
|
||||
text += i18n._("Info:point loss").format(points_lost=points_lost) + "\n"
|
||||
text += i18n._("Info:top move").format(top_move=previous_top_move["move"], score=self.format_score(previous_top_move["scoreLead"])) + "\n"
|
||||
text += (
|
||||
i18n._("Info:top move").format(
|
||||
top_move=previous_top_move["move"],
|
||||
score=self.format_score(previous_top_move["scoreLead"]),
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
else:
|
||||
text += i18n._("Info:best move") + "\n"
|
||||
if previous_top_move.get("pv") and (sgf or hints):
|
||||
text += i18n._("Info:PV").format(pv=self.make_pv(single_move.player, previous_top_move["pv"], interactive)) + "\n"
|
||||
text += (
|
||||
i18n._("Info:PV").format(
|
||||
pv=self.make_pv(single_move.player, previous_top_move["pv"], interactive)
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
if sgf or hints or teach:
|
||||
policy_ranking = self.parent.policy_ranking
|
||||
currmove_policy_with_ix = [(ix + 1, p) for (p, m), ix in zip(policy_ranking, range(len(policy_ranking))) if m == single_move]
|
||||
currmove_policy_with_ix = [
|
||||
(ix + 1, p)
|
||||
for (p, m), ix in zip(policy_ranking, range(len(policy_ranking)))
|
||||
if m == single_move
|
||||
]
|
||||
if currmove_policy_with_ix:
|
||||
text += i18n._("Info:policy rank").format(rank=currmove_policy_with_ix[0][0], probability=currmove_policy_with_ix[0][1]) + "\n"
|
||||
text += (
|
||||
i18n._("Info:policy rank").format(
|
||||
rank=currmove_policy_with_ix[0][0], probability=currmove_policy_with_ix[0][1]
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
if not currmove_policy_with_ix or currmove_policy_with_ix[0] != 1 and (sgf or hints):
|
||||
text += i18n._("Info:policy best").format(move=policy_ranking[0][1].gtp(), probability=policy_ranking[0][0]) + "\n"
|
||||
text += (
|
||||
i18n._("Info:policy best").format(
|
||||
move=policy_ranking[0][1].gtp(), probability=policy_ranking[0][0]
|
||||
)
|
||||
+ "\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")
|
||||
@@ -173,7 +231,13 @@ class GameNode(SGFNode):
|
||||
@property
|
||||
def parent_realized_points_lost(self) -> Optional[float]:
|
||||
single_move = self.move
|
||||
if single_move and self.parent and self.parent.parent and self.analysis_ready and self.parent.parent.analysis_ready:
|
||||
if (
|
||||
single_move
|
||||
and self.parent
|
||||
and self.parent.parent
|
||||
and self.analysis_ready
|
||||
and self.parent.parent.analysis_ready
|
||||
):
|
||||
parent_parent_score = self.parent.parent.score
|
||||
score = self.score
|
||||
return self.player_sign(single_move.player) * (score - parent_parent_score)
|
||||
@@ -189,11 +253,19 @@ class GameNode(SGFNode):
|
||||
if not self.analysis["moves"]:
|
||||
polmoves = self.policy_ranking
|
||||
top_polmove = polmoves[0][1] if polmoves else Move(None) # if no info at all, pass
|
||||
return [{**self.analysis["root"], "pointsLost": 0, "order": 0, "move": top_polmove.gtp()}] # single visit -> go by policy/root
|
||||
return [
|
||||
{**self.analysis["root"], "pointsLost": 0, "order": 0, "move": top_polmove.gtp()}
|
||||
] # single visit -> go by policy/root
|
||||
|
||||
root_score = self.analysis["root"]["scoreLead"]
|
||||
move_dicts = list(self.analysis["moves"].values()) # prevent incoming analysis from causing crash
|
||||
return sorted([{"pointsLost": self.player_sign(self.next_player) * (root_score - d["scoreLead"]), **d} for d in move_dicts], key=lambda d: (d["order"], d["pointsLost"]))
|
||||
return sorted(
|
||||
[
|
||||
{"pointsLost": self.player_sign(self.next_player) * (root_score - d["scoreLead"]), **d}
|
||||
for d in move_dicts
|
||||
],
|
||||
key=lambda d: (d["order"], d["pointsLost"]),
|
||||
)
|
||||
|
||||
@property
|
||||
def policy_ranking(self) -> Optional[List[Tuple[float, Move]]]: # return moves from highest policy value to lowest
|
||||
|
||||
Reference in new issue
Block a user