settle ai, reportevery, new katago for win
This commit is contained in:
1 parent
67e827b0ac
commit
2bf184694e
7 files changed
+47
-14
No files matched your search
Binary file not shown.
@@ -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
@@ -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(
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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)
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user