optimization, bot fixes
This commit is contained in:
1 parent
de0d99e11a
commit
b9aec4b6dd
12 files changed
+69325
-37
No files matched your search
+21
-3
@@ -2,6 +2,7 @@
|
|||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
from core.ai import ai_move
|
from core.ai import ai_move
|
||||||
from core.common import OUTPUT_ERROR, OUTPUT_INFO
|
from core.common import OUTPUT_ERROR, OUTPUT_INFO
|
||||||
@@ -103,8 +104,14 @@ while True:
|
|||||||
logger.log(f"Setting komi {game.root.properties}", OUTPUT_ERROR)
|
logger.log(f"Setting komi {game.root.properties}", OUTPUT_ERROR)
|
||||||
elif "place_free_handicap" in line:
|
elif "place_free_handicap" in line:
|
||||||
_, n = line.split(" ")
|
_, n = line.split(" ")
|
||||||
game.place_handicap_stones(int(n))
|
n = int(n)
|
||||||
gtp = [Move.from_sgf(m, game.board_size, "B").gtp() for m in game.root.get_list_property("AB")]
|
game.place_handicap_stones(n)
|
||||||
|
handicaps = set(game.root.get_list_property("AB"))
|
||||||
|
bx, by = game.board_size
|
||||||
|
while len(handicaps) < min(n, bx * by): # really obscure cases
|
||||||
|
handicaps.add(Move((random.randint(0, bx - 1), random.randint(0, by - 1)), player="B").sgf(board_size=game.board_size))
|
||||||
|
game.root.set_property("AB", list(handicaps))
|
||||||
|
gtp = [Move.from_sgf(m, game.board_size, "B").gtp() for m in handicaps]
|
||||||
logger.log(f"Chose handicap placements as {gtp}", OUTPUT_ERROR)
|
logger.log(f"Chose handicap placements as {gtp}", OUTPUT_ERROR)
|
||||||
print(f"= {' '.join(gtp)}\n")
|
print(f"= {' '.join(gtp)}\n")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
@@ -115,6 +122,12 @@ while True:
|
|||||||
game.root.set_property("AB", [Move.from_gtp(move.upper()).sgf(game.board_size) for move in stones])
|
game.root.set_property("AB", [Move.from_gtp(move.upper()).sgf(game.board_size) for move in stones])
|
||||||
logger.log(f"Set handicap placements to {game.root.get_list_property('AB')}", OUTPUT_ERROR)
|
logger.log(f"Set handicap placements to {game.root.get_list_property('AB')}", OUTPUT_ERROR)
|
||||||
elif "genmove" in line:
|
elif "genmove" in line:
|
||||||
|
_, player = line.strip().split(" ")
|
||||||
|
if player[0].upper() != game.next_player:
|
||||||
|
logger.log(f"ERROR generating move: UNEXPECTED PLAYER {player} != {game.next_player}.", OUTPUT_ERROR)
|
||||||
|
print(f"= ??\n")
|
||||||
|
sys.stdout.flush()
|
||||||
|
continue
|
||||||
logger.log(f"{ai_strategy} generating move", OUTPUT_ERROR)
|
logger.log(f"{ai_strategy} generating move", OUTPUT_ERROR)
|
||||||
game.current_node.analyze(engine)
|
game.current_node.analyze(engine)
|
||||||
malkovich_analysis(game.current_node)
|
malkovich_analysis(game.current_node)
|
||||||
@@ -130,7 +143,12 @@ while True:
|
|||||||
move = game.play(Move(None, player=game.next_player)).single_move
|
move = game.play(Move(None, player=game.next_player)).single_move
|
||||||
else:
|
else:
|
||||||
move, node = ai_move(game, ai_strategy, ai_settings)
|
move, node = ai_move(game, ai_strategy, ai_settings)
|
||||||
logger.log(f"Generated move {move}", OUTPUT_ERROR)
|
if node is None:
|
||||||
|
while node is None:
|
||||||
|
logger.log(f"ERROR generating move, backing up with weighted.", OUTPUT_ERROR)
|
||||||
|
move, node = ai_move(game, "p:weighted", {"pick_override": 1.0, "lower_bound": 0.001, "weaken_fac": 1})
|
||||||
|
else:
|
||||||
|
logger.log(f"Generated move {move}", OUTPUT_ERROR)
|
||||||
print(f"= {move.gtp()}\n")
|
print(f"= {move.gtp()}\n")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
malkovich_analysis(game.current_node)
|
malkovich_analysis(game.current_node)
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+26
-24
@@ -34,8 +34,7 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
|
|||||||
raise EngineDiedException(f"Engine for {cn.next_player} ({engine.config}) died")
|
raise EngineDiedException(f"Engine for {cn.next_player} ({engine.config}) died")
|
||||||
ai_mode = ai_mode.lower()
|
ai_mode = ai_mode.lower()
|
||||||
ai_thoughts = ""
|
ai_thoughts = ""
|
||||||
candidate_ai_moves = cn.candidate_moves
|
if ("policy" in ai_mode or "p:" in ai_mode) and cn.policy: # pure policy based move
|
||||||
if ("policy" in ai_mode or "p:" in ai_mode) and cn.policy:
|
|
||||||
policy_moves = cn.policy_ranking
|
policy_moves = cn.policy_ranking
|
||||||
pass_policy = cn.policy[-1]
|
pass_policy = cn.policy[-1]
|
||||||
top_5_pass = any([polmove[1].is_pass for polmove in policy_moves[:5]]) # dont make it jump around for the last few sensible non pass moves
|
top_5_pass = any([polmove[1].is_pass for polmove in policy_moves[:5]]) # dont make it jump around for the last few sensible non pass moves
|
||||||
@@ -131,28 +130,30 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
|
|||||||
ai_thoughts += f"Pick policy strategy {ai_mode} failed to find legal moves, so is playing top policy move {aimove.gtp()}."
|
ai_thoughts += f"Pick policy strategy {ai_mode} failed to find legal moves, so is playing top policy move {aimove.gtp()}."
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown AI mode {ai_mode}")
|
raise ValueError(f"Unknown AI mode {ai_mode}")
|
||||||
elif "balance" in ai_mode and candidate_ai_moves[0]["move"] != "pass": # don't play suicidal to balance score - pass when it's best
|
else: # Engine based move
|
||||||
sign = cn.player_sign(cn.next_player)
|
candidate_ai_moves = cn.candidate_moves
|
||||||
sel_moves = [ # top move, or anything not too bad, or anything that makes you still ahead
|
if "balance" in ai_mode and candidate_ai_moves[0]["move"] != "pass": # don't play suicidal to balance score - pass when it's best
|
||||||
move
|
sign = cn.player_sign(cn.next_player)
|
||||||
for i, move in enumerate(candidate_ai_moves)
|
sel_moves = [ # top move, or anything not too bad, or anything that makes you still ahead
|
||||||
if i == 0
|
move
|
||||||
or move["visits"] >= ai_settings["min_visits"]
|
for i, move in enumerate(candidate_ai_moves)
|
||||||
and (move["pointsLost"] < ai_settings["random_loss"] or move["pointsLost"] < ai_settings["max_loss"] and sign * move["scoreLead"] > ai_settings["target_score"])
|
if i == 0
|
||||||
]
|
or move["visits"] >= ai_settings["min_visits"]
|
||||||
aimove = Move.from_gtp(random.choice(sel_moves)["move"], player=cn.next_player)
|
and (move["pointsLost"] < ai_settings["random_loss"] or move["pointsLost"] < ai_settings["max_loss"] and sign * move["scoreLead"] > ai_settings["target_score"])
|
||||||
ai_thoughts += f"Balance strategy selected moves {sel_moves} based on target score and max points lost, and randomly chose {aimove.gtp()}."
|
]
|
||||||
elif "jigo" in ai_mode and candidate_ai_moves[0]["move"] != "pass":
|
aimove = Move.from_gtp(random.choice(sel_moves)["move"], player=cn.next_player)
|
||||||
sign = cn.player_sign(cn.next_player)
|
ai_thoughts += f"Balance strategy selected moves {sel_moves} based on target score and max points lost, and randomly chose {aimove.gtp()}."
|
||||||
jigo_move = min(candidate_ai_moves, key=lambda move: abs(sign * move["scoreLead"] - ai_settings["target_score"]))
|
elif "jigo" in ai_mode and candidate_ai_moves[0]["move"] != "pass":
|
||||||
aimove = Move.from_gtp(jigo_move["move"], player=cn.next_player)
|
sign = cn.player_sign(cn.next_player)
|
||||||
ai_thoughts += f"Jigo strategy found candidate moves {candidate_ai_moves} moves and chose {aimove.gtp()} as closest to 0.5 point win"
|
jigo_move = min(candidate_ai_moves, key=lambda move: abs(sign * move["scoreLead"] - ai_settings["target_score"]))
|
||||||
else:
|
aimove = Move.from_gtp(jigo_move["move"], player=cn.next_player)
|
||||||
if "default" not in ai_mode and "katago" not in ai_mode:
|
ai_thoughts += f"Jigo strategy found candidate moves {candidate_ai_moves} moves and chose {aimove.gtp()} as closest to 0.5 point win"
|
||||||
game.katrain.log(f"Unknown AI mode {ai_mode} or policy missing, using default.", OUTPUT_INFO)
|
else:
|
||||||
ai_thoughts += f"Strategy {ai_mode} not found or unexpected fallback."
|
if "default" not in ai_mode and "katago" not in ai_mode:
|
||||||
aimove = Move.from_gtp(candidate_ai_moves[0]["move"], player=cn.next_player)
|
game.katrain.log(f"Unknown AI mode {ai_mode} or policy missing, using default.", OUTPUT_INFO)
|
||||||
ai_thoughts += f"Default strategy found {len(candidate_ai_moves)} moves returned from the engine and chose {aimove.gtp()} as top move"
|
ai_thoughts += f"Strategy {ai_mode} not found or unexpected fallback."
|
||||||
|
aimove = Move.from_gtp(candidate_ai_moves[0]["move"], player=cn.next_player)
|
||||||
|
ai_thoughts += f"Default strategy found {len(candidate_ai_moves)} moves returned from the engine and chose {aimove.gtp()} as top move"
|
||||||
game.katrain.log(f"AI thoughts: {ai_thoughts}", OUTPUT_DEBUG)
|
game.katrain.log(f"AI thoughts: {ai_thoughts}", OUTPUT_DEBUG)
|
||||||
try:
|
try:
|
||||||
played_node = game.play(aimove)
|
played_node = game.play(aimove)
|
||||||
@@ -160,3 +161,4 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
|
|||||||
return aimove, played_node
|
return aimove, played_node
|
||||||
except IllegalMoveException as e:
|
except IllegalMoveException as e:
|
||||||
game.katrain.log(f"AI Strategy {ai_mode} generated illegal move {aimove.gtp()}: {e}", OUTPUT_ERROR)
|
game.katrain.log(f"AI Strategy {ai_mode} generated illegal move {aimove.gtp()}: {e}", OUTPUT_ERROR)
|
||||||
|
return None, None
|
||||||
+3
-5
@@ -154,10 +154,8 @@ class Game:
|
|||||||
|
|
||||||
def place_handicap_stones(self, n_handicaps):
|
def place_handicap_stones(self, n_handicaps):
|
||||||
board_size_x, board_size_y = self.board_size
|
board_size_x, board_size_y = self.board_size
|
||||||
near_x = 3 if board_size_x >= 13 else 2
|
near_x = 3 if board_size_x >= 13 else min(2, board_size_x - 1)
|
||||||
near_y = 3 if board_size_y >= 13 else 2
|
near_y = 3 if board_size_y >= 13 else min(2, board_size_y - 1)
|
||||||
if board_size_x < 3 or board_size_y < 3:
|
|
||||||
return
|
|
||||||
far_x = board_size_x - 1 - near_x
|
far_x = board_size_x - 1 - near_x
|
||||||
far_y = board_size_y - 1 - near_y
|
far_y = board_size_y - 1 - near_y
|
||||||
middle_x = board_size_x // 2 # what for even sizes?
|
middle_x = board_size_x // 2 # what for even sizes?
|
||||||
@@ -176,7 +174,7 @@ class Game:
|
|||||||
if n_handicaps % 2 == 1:
|
if n_handicaps % 2 == 1:
|
||||||
stones.append((middle_x, middle_y))
|
stones.append((middle_x, middle_y))
|
||||||
stones += [(near_x, middle_y), (far_x, middle_y), (middle_x, near_y), (middle_x, far_y)]
|
stones += [(near_x, middle_y), (far_x, middle_y), (middle_x, near_y), (middle_x, far_y)]
|
||||||
self.root.set_property("AB", [Move(stone).sgf(board_size=(board_size_x, board_size_y)) for stone in stones[:n_handicaps]])
|
self.root.set_property("AB", list({Move(stone).sgf(board_size=(board_size_x, board_size_y)) for stone in stones[:n_handicaps]}))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def board_size(self):
|
def board_size(self):
|
||||||
|
|||||||
+4
-3
@@ -79,10 +79,11 @@ class SGFNode:
|
|||||||
|
|
||||||
def sgf(self, **xargs) -> str:
|
def sgf(self, **xargs) -> str:
|
||||||
"""Generates an SGF, calling sgf_properties on each node with the given xargs, so it can filter relevant properties if needed."""
|
"""Generates an SGF, calling sgf_properties on each node with the given xargs, so it can filter relevant properties if needed."""
|
||||||
import sys
|
if self.is_root:
|
||||||
|
import sys
|
||||||
|
|
||||||
bszx, bszy = self.board_size
|
bszx, bszy = self.board_size
|
||||||
sys.setrecursionlimit(max(sys.getrecursionlimit(), 3 * bszx * bszy)) # thanks to lightvector for causing stack overflows ;)
|
sys.setrecursionlimit(max(sys.getrecursionlimit(), 4 * bszx * bszy))
|
||||||
sgf_str = "".join([prop + "".join(f"[{v}]" for v in values) for prop, values in self.sgf_properties(**xargs).items() if values])
|
sgf_str = "".join([prop + "".join(f"[{v}]" for v in values) for prop, values in self.sgf_properties(**xargs).items() if values])
|
||||||
if self.children:
|
if self.children:
|
||||||
children = [c.sgf(**xargs) for c in self.order_children(self.children)]
|
children = [c.sgf(**xargs) for c in self.order_children(self.children)]
|
||||||
|
|||||||
+2
-2
@@ -133,7 +133,7 @@ class ConfigPopup(QuickConfigGui):
|
|||||||
col_container.add_widget(cols[0])
|
col_container.add_widget(cols[0])
|
||||||
col_container.add_widget(cols[1])
|
col_container.add_widget(cols[1])
|
||||||
self.add_widget(col_container)
|
self.add_widget(col_container)
|
||||||
self.info_label = Label(halign='center')
|
self.info_label = Label(halign="center")
|
||||||
self.apply_button = StyledButton(text="Apply", on_press=lambda _: self.update_config())
|
self.apply_button = StyledButton(text="Apply", on_press=lambda _: self.update_config())
|
||||||
self.save_button = StyledButton(text="Apply and Save", on_press=lambda _: self.update_config(save_to_file=True))
|
self.save_button = StyledButton(text="Apply and Save", on_press=lambda _: self.update_config(save_to_file=True))
|
||||||
btn_container = BoxLayout(orientation="horizontal", size_hint=(1, 0.1), spacing=1, padding=1)
|
btn_container = BoxLayout(orientation="horizontal", size_hint=(1, 0.1), spacing=1, padding=1)
|
||||||
@@ -170,7 +170,7 @@ class ConfigPopup(QuickConfigGui):
|
|||||||
|
|
||||||
def restart_engine(_dt):
|
def restart_engine(_dt):
|
||||||
old_engine = self.katrain.engine # type: KataGoEngine
|
old_engine = self.katrain.engine # type: KataGoEngine
|
||||||
old_proc =old_engine.katago_process
|
old_proc = old_engine.katago_process
|
||||||
if old_proc:
|
if old_proc:
|
||||||
old_engine.shutdown(finish=True)
|
old_engine.shutdown(finish=True)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user