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
+47 -14

No files matched your search

Binary file not shown.
+8
View File
@@ -96,6 +96,14 @@
"attach_penalty": 1,
"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": {
"weaken_fac": 1.25,
"pick_override": 1.0,
+21 -7
View File
@@ -32,6 +32,7 @@ from katrain.core.constants import (
OUTPUT_DEBUG,
OUTPUT_ERROR,
OUTPUT_INFO,
AI_SETTLE_STONES,
)
from katrain.core.game import Game, GameNode, Move
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]
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."
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_without_player = {s.coords for s in game.stones}
next_player_sign = cn.player_sign(cn.next_player)
if ai_mode == AI_SIMPLE_OWNERSHIP:
def settledness(d, player_sign):
return sum([abs(o) for o in d["ownership"] if player_sign * o > 0])
def settledness(d, player_sign, player):
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):
if move.is_pass:
return False
attach_opponent_stones = sum(
(move.coords[0] + dx, move.coords[1] + dy, cn.player) in stones_with_player
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
def is_tenuki(d):
return not any(
return not d.is_pass and not any(
not node
or not node.move
or node.move.is_pass
@@ -391,8 +405,8 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
[
(
move,
settledness(d, next_player_sign),
settledness(d, -next_player_sign),
settledness(d, next_player_sign, cn.next_player),
settledness(d, -next_player_sign, cn.player),
is_attachment(move),
is_tenuki(move),
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 ''})"
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]
else:
game.katrain.log(
+5 -2
View File
@@ -1,6 +1,6 @@
VERSION = "1.5.1"
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_KATAGO_STDERR = -0.5
@@ -34,10 +34,11 @@ AI_INFLUENCE = "ai:p:influence"
AI_TERRITORY = "ai:p:territory"
AI_RANK = "ai:p:rank"
AI_SIMPLE_OWNERSHIP = "ai:simple"
AI_SETTLE_STONES = "ai:settle"
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_POLICY = [AI_WEIGHTED, AI_POLICY] + AI_STRATEGIES_PICK
AI_STRATEGIES = AI_STRATEGIES_ENGINE + AI_STRATEGIES_POLICY
@@ -45,6 +46,7 @@ AI_STRATEGIES_RECOMMENDED_ORDER = [
AI_DEFAULT,
AI_RANK,
AI_SIMPLE_OWNERSHIP,
AI_SETTLE_STONES,
AI_HANDICAP,
AI_SCORELOSS,
AI_POLICY,
@@ -70,6 +72,7 @@ AI_STRENGTH = { # dan ranks, backup if model is missing. TODO: remove some?
AI_TERRITORY: -7,
AI_RANK: float("nan"),
AI_SIMPLE_OWNERSHIP: 3,
AI_SETTLE_STONES: 4,
}
AI_OPTION_VALUES = {
+5 -1
View File
@@ -196,7 +196,8 @@ class KataGoEngine:
elif "warning" in analysis:
self.katrain.log(f"{analysis} received from KataGo", OUTPUT_DEBUG)
else:
del self.queries[query_id]
if not analysis.get("isDuringSearch", False):
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,
@@ -240,6 +241,7 @@ class KataGoEngine:
ownership: Optional[bool] = None,
next_move: Optional[GameNode] = None,
extra_settings: Optional[Dict] = None,
report_every: Optional[float] = None,
):
nodes = analysis_node.nodes_from_root
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],
"overrideSettings": {**settings, **(extra_settings or {})},
}
if report_every is not None:
query["reportDuringSearchEvery"] = report_every
self.send_query(query, callback, error_callback, next_move)
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):
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 --
def _calculate_groups(self):
@@ -339,7 +341,7 @@ class Game:
min_visits = min(node.analysis_visits_requested for node in nodes)
visits = min_visits + engine.config["max_visits"]
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)
return
@@ -389,7 +391,7 @@ class Game:
for move in analyze_moves:
if cn.analysis["moves"].get(move.gtp(), {"visits": 0})["visits"] < visits:
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
def analyze_undo(self, node):
+3 -1
View File
@@ -77,6 +77,7 @@ class GameNode(SGFNode):
refine_move=None,
analyze_fast=False,
find_alternatives=False,
report_every=0.25,
):
engine.request_analysis(
self,
@@ -87,6 +88,7 @@ class GameNode(SGFNode):
time_limit=time_limit,
next_move=refine_move,
find_alternatives=find_alternatives,
report_every=report_every,
)
def update_move_analysis(self, move_analysis, move_gtp):
@@ -111,7 +113,7 @@ class GameNode(SGFNode):
else:
if alternatives_mode:
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:
for move_dict in self.analysis["moves"].values():
move_dict["order"] = 999 # old moves to end