settle ai, reportevery, new katago for win

This commit is contained in:
Sander Land committed 2020-10-11 12:15:28 +02:00
1 parent 67e827b0ac
commit 2bf184694e
7 files changed
+45 -12

No files matched your search

Binary file not shown.
+8
View File
@@ -96,6 +96,14 @@
"attach_penalty": 1, "attach_penalty": 1,
"tenuki_penalty": 0.5 "tenuki_penalty": 0.5
}, },
"ai:settle": {
"max_points_lost": 1.75,
"settled_weight": 1.0,
"opponent_fac": 0.5,
"min_visits": 3,
"attach_penalty": 1,
"tenuki_penalty": 0.5
},
"ai:p:weighted": { "ai:p:weighted": {
"weaken_fac": 1.25, "weaken_fac": 1.25,
"pick_override": 1.0, "pick_override": 1.0,
+20 -6
View File
@@ -32,6 +32,7 @@ from katrain.core.constants import (
OUTPUT_DEBUG, OUTPUT_DEBUG,
OUTPUT_ERROR, OUTPUT_ERROR,
OUTPUT_INFO, OUTPUT_INFO,
AI_SETTLE_STONES,
) )
from katrain.core.game import Game, GameNode, Move from katrain.core.game import Game, GameNode, Move
from katrain.core.utils import var_to_grid from katrain.core.utils import var_to_grid
@@ -356,14 +357,27 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
topmove = weighted_selection_without_replacement(moves, 1)[0] topmove = weighted_selection_without_replacement(moves, 1)[0]
aimove = topmove[2] aimove = topmove[2]
ai_thoughts += f"ScoreLoss strategy found {len(candidate_ai_moves)} candidate moves (best {top_cand.gtp()}) and chose {aimove.gtp()} (weight {topmove[1]:.3f}, point loss {topmove[0]:.1f}) based on score weights." ai_thoughts += f"ScoreLoss strategy found {len(candidate_ai_moves)} candidate moves (best {top_cand.gtp()}) and chose {aimove.gtp()} (weight {topmove[1]:.3f}, point loss {topmove[0]:.1f}) based on score weights."
elif ai_mode == AI_SIMPLE_OWNERSHIP: elif ai_mode in [AI_SIMPLE_OWNERSHIP, AI_SETTLE_STONES]:
stones_with_player = {(*s.coords, s.player) for s in game.stones} stones_with_player = {(*s.coords, s.player) for s in game.stones}
stones_without_player = {s.coords for s in game.stones}
next_player_sign = cn.player_sign(cn.next_player) next_player_sign = cn.player_sign(cn.next_player)
if ai_mode == AI_SIMPLE_OWNERSHIP:
def settledness(d, player_sign): def settledness(d, player_sign, player):
return sum([abs(o) for o in d["ownership"] if player_sign * o > 0]) return sum([abs(o) for o in d["ownership"] if player_sign * o > 0])
else:
board_size_x, board_size_y = game.board_size
def settledness(d, player_sign, player):
ownership_grid = var_to_grid(d["ownership"], (board_size_x, board_size_y))
return sum(
[abs(ownership_grid[s.coords[0]][s.coords[1]]) for s in game.stones if s.player == player]
)
def is_attachment(move): def is_attachment(move):
if move.is_pass:
return False
attach_opponent_stones = sum( attach_opponent_stones = sum(
(move.coords[0] + dx, move.coords[1] + dy, cn.player) in stones_with_player (move.coords[0] + dx, move.coords[1] + dy, cn.player) in stones_with_player
for dx in [-1, 0, 1] for dx in [-1, 0, 1]
@@ -379,7 +393,7 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
return attach_opponent_stones >= 1 and nearby_own_stones == 0 return attach_opponent_stones >= 1 and nearby_own_stones == 0
def is_tenuki(d): def is_tenuki(d):
return not any( return not d.is_pass and not any(
not node not node
or not node.move or not node.move
or node.move.is_pass or node.move.is_pass
@@ -391,8 +405,8 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
[ [
( (
move, move,
settledness(d, next_player_sign), settledness(d, next_player_sign, cn.next_player),
settledness(d, -next_player_sign), settledness(d, -next_player_sign, cn.player),
is_attachment(move), is_attachment(move),
is_tenuki(move), is_tenuki(move),
d, d,
@@ -413,7 +427,7 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
f"{move.gtp()} ({d['pointsLost']:.1f} pt lost, {d['visits']} visits, {settled:.1f} settledness, {oppsettled:.1f} opponent settledness{', attachment' if isattach else ''}{', tenuki' if istenuki else ''})" f"{move.gtp()} ({d['pointsLost']:.1f} pt lost, {d['visits']} visits, {settled:.1f} settledness, {oppsettled:.1f} opponent settledness{', attachment' if isattach else ''}{', tenuki' if istenuki else ''})"
for move, settled, oppsettled, isattach, istenuki, d in moves_with_settledness[:5] for move, settled, oppsettled, isattach, istenuki, d in moves_with_settledness[:5]
] ]
ai_thoughts += f"Simple ownership strategy. Top 5 Candidates {', '.join(cands)} " ai_thoughts += f"{ai_mode} strategy. Top 5 Candidates {', '.join(cands)} "
aimove = moves_with_settledness[0][0] aimove = moves_with_settledness[0][0]
else: else:
game.katrain.log( game.katrain.log(
+5 -2
View File
@@ -1,6 +1,6 @@
VERSION = "1.5.1" VERSION = "1.5.1"
HOMEPAGE = "https://github.com/sanderland/katrain" HOMEPAGE = "https://github.com/sanderland/katrain"
CONFIG_MIN_VERSION = "1.5.1" # keep config files from this version CONFIG_MIN_VERSION = "1.5.2" # keep config files from this version
OUTPUT_ERROR = -1 OUTPUT_ERROR = -1
OUTPUT_KATAGO_STDERR = -0.5 OUTPUT_KATAGO_STDERR = -0.5
@@ -34,10 +34,11 @@ AI_INFLUENCE = "ai:p:influence"
AI_TERRITORY = "ai:p:territory" AI_TERRITORY = "ai:p:territory"
AI_RANK = "ai:p:rank" AI_RANK = "ai:p:rank"
AI_SIMPLE_OWNERSHIP = "ai:simple" AI_SIMPLE_OWNERSHIP = "ai:simple"
AI_SETTLE_STONES = "ai:settle"
AI_CONFIG_DEFAULT = AI_RANK AI_CONFIG_DEFAULT = AI_RANK
AI_STRATEGIES_ENGINE = [AI_DEFAULT, AI_HANDICAP, AI_SCORELOSS, AI_JIGO] AI_STRATEGIES_ENGINE = [AI_DEFAULT, AI_HANDICAP, AI_SCORELOSS, AI_SIMPLE_OWNERSHIP, AI_SETTLE_STONES, AI_JIGO]
AI_STRATEGIES_PICK = [AI_PICK, AI_LOCAL, AI_TENUKI, AI_INFLUENCE, AI_TERRITORY, AI_RANK] AI_STRATEGIES_PICK = [AI_PICK, AI_LOCAL, AI_TENUKI, AI_INFLUENCE, AI_TERRITORY, AI_RANK]
AI_STRATEGIES_POLICY = [AI_WEIGHTED, AI_POLICY] + AI_STRATEGIES_PICK AI_STRATEGIES_POLICY = [AI_WEIGHTED, AI_POLICY] + AI_STRATEGIES_PICK
AI_STRATEGIES = AI_STRATEGIES_ENGINE + AI_STRATEGIES_POLICY AI_STRATEGIES = AI_STRATEGIES_ENGINE + AI_STRATEGIES_POLICY
@@ -45,6 +46,7 @@ AI_STRATEGIES_RECOMMENDED_ORDER = [
AI_DEFAULT, AI_DEFAULT,
AI_RANK, AI_RANK,
AI_SIMPLE_OWNERSHIP, AI_SIMPLE_OWNERSHIP,
AI_SETTLE_STONES,
AI_HANDICAP, AI_HANDICAP,
AI_SCORELOSS, AI_SCORELOSS,
AI_POLICY, AI_POLICY,
@@ -70,6 +72,7 @@ AI_STRENGTH = { # dan ranks, backup if model is missing. TODO: remove some?
AI_TERRITORY: -7, AI_TERRITORY: -7,
AI_RANK: float("nan"), AI_RANK: float("nan"),
AI_SIMPLE_OWNERSHIP: 3, AI_SIMPLE_OWNERSHIP: 3,
AI_SETTLE_STONES: 4,
} }
AI_OPTION_VALUES = { AI_OPTION_VALUES = {
+4
View File
@@ -196,6 +196,7 @@ class KataGoEngine:
elif "warning" in analysis: elif "warning" in analysis:
self.katrain.log(f"{analysis} received from KataGo", OUTPUT_DEBUG) self.katrain.log(f"{analysis} received from KataGo", OUTPUT_DEBUG)
else: else:
if not analysis.get("isDuringSearch", False):
del self.queries[query_id] del self.queries[query_id]
time_taken = time.time() - start_time time_taken = time.time() - start_time
self.katrain.log( self.katrain.log(
@@ -240,6 +241,7 @@ class KataGoEngine:
ownership: Optional[bool] = None, ownership: Optional[bool] = None,
next_move: Optional[GameNode] = None, next_move: Optional[GameNode] = None,
extra_settings: Optional[Dict] = None, extra_settings: Optional[Dict] = None,
report_every: Optional[float] = None,
): ):
nodes = analysis_node.nodes_from_root nodes = analysis_node.nodes_from_root
moves = [m for node in nodes for m in node.moves] moves = [m for node in nodes for m in node.moves]
@@ -288,5 +290,7 @@ class KataGoEngine:
"moves": [[m.player, m.gtp()] for m in moves], "moves": [[m.player, m.gtp()] for m in moves],
"overrideSettings": {**settings, **(extra_settings or {})}, "overrideSettings": {**settings, **(extra_settings or {})},
} }
if report_every is not None:
query["reportDuringSearchEvery"] = report_every
self.send_query(query, callback, error_callback, next_move) self.send_query(query, callback, error_callback, next_move)
analysis_node.analysis_visits_requested = max(analysis_node.analysis_visits_requested, visits) analysis_node.analysis_visits_requested = max(analysis_node.analysis_visits_requested, visits)
+5 -3
View File
@@ -86,7 +86,9 @@ class Game:
def analyze_all_nodes(self, priority=0, analyze_fast=False): def analyze_all_nodes(self, priority=0, analyze_fast=False):
for node in self.root.nodes_in_tree: for node in self.root.nodes_in_tree:
node.analyze(self.engines[node.next_player], priority=priority, analyze_fast=analyze_fast) node.analyze(
self.engines[node.next_player], priority=priority, analyze_fast=analyze_fast, report_every=None
)
# -- move tree functions -- # -- move tree functions --
def _calculate_groups(self): def _calculate_groups(self):
@@ -339,7 +341,7 @@ class Game:
min_visits = min(node.analysis_visits_requested for node in nodes) min_visits = min(node.analysis_visits_requested for node in nodes)
visits = min_visits + engine.config["max_visits"] visits = min_visits + engine.config["max_visits"]
for node in nodes: for node in nodes:
node.analyze(engine, visits=visits, priority=-1_000_000, time_limit=False) node.analyze(engine, visits=visits, priority=-1_000_000, time_limit=False, report_every=None)
self.katrain.controls.set_status(i18n._("game re-analysis").format(visits=visits), STATUS_ANALYSIS) self.katrain.controls.set_status(i18n._("game re-analysis").format(visits=visits), STATUS_ANALYSIS)
return return
@@ -389,7 +391,7 @@ class Game:
for move in analyze_moves: for move in analyze_moves:
if cn.analysis["moves"].get(move.gtp(), {"visits": 0})["visits"] < visits: if cn.analysis["moves"].get(move.gtp(), {"visits": 0})["visits"] < visits:
cn.analyze( cn.analyze(
engine, priority, visits=visits, refine_move=move, time_limit=False engine, priority, visits=visits, refine_move=move, time_limit=False, report_every=None
) # explicitly requested so take as long as you need ) # explicitly requested so take as long as you need
def analyze_undo(self, node): def analyze_undo(self, node):
+3 -1
View File
@@ -77,6 +77,7 @@ class GameNode(SGFNode):
refine_move=None, refine_move=None,
analyze_fast=False, analyze_fast=False,
find_alternatives=False, find_alternatives=False,
report_every=0.25,
): ):
engine.request_analysis( engine.request_analysis(
self, self,
@@ -87,6 +88,7 @@ class GameNode(SGFNode):
time_limit=time_limit, time_limit=time_limit,
next_move=refine_move, next_move=refine_move,
find_alternatives=find_alternatives, find_alternatives=find_alternatives,
report_every=report_every,
) )
def update_move_analysis(self, move_analysis, move_gtp): def update_move_analysis(self, move_analysis, move_gtp):
@@ -111,7 +113,7 @@ class GameNode(SGFNode):
else: else:
if alternatives_mode: if alternatives_mode:
for m in analysis_json["moveInfos"]: for m in analysis_json["moveInfos"]:
m["order"] += 10 # offset for not making this top m["order"] += 100 # offset for not making this top
if refine_move is None and not alternatives_mode: if refine_move is None and not alternatives_mode:
for move_dict in self.analysis["moves"].values(): for move_dict in self.analysis["moves"].values():
move_dict["order"] = 999 # old moves to end move_dict["order"] = 999 # old moves to end