optimization, bot fixes

This commit is contained in:
Sander Land committed 2020-05-03 16:22:16 +02:00
1 parent de0d99e11a
commit b9aec4b6dd
12 files changed
+69325 -37

No files matched your search

+21 -3
View File
@@ -2,6 +2,7 @@
import json
import sys
import time
import random
from core.ai import ai_move
from core.common import OUTPUT_ERROR, OUTPUT_INFO
@@ -103,8 +104,14 @@ while True:
logger.log(f"Setting komi {game.root.properties}", OUTPUT_ERROR)
elif "place_free_handicap" in line:
_, n = line.split(" ")
game.place_handicap_stones(int(n))
gtp = [Move.from_sgf(m, game.board_size, "B").gtp() for m in game.root.get_list_property("AB")]
n = int(n)
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)
print(f"= {' '.join(gtp)}\n")
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])
logger.log(f"Set handicap placements to {game.root.get_list_property('AB')}", OUTPUT_ERROR)
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)
game.current_node.analyze(engine)
malkovich_analysis(game.current_node)
@@ -130,7 +143,12 @@ while True:
move = game.play(Move(None, player=game.next_player)).single_move
else:
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")
sys.stdout.flush()
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
View File
@@ -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")
ai_mode = ai_mode.lower()
ai_thoughts = ""
candidate_ai_moves = cn.candidate_moves
if ("policy" in ai_mode or "p:" in ai_mode) and cn.policy:
if ("policy" in ai_mode or "p:" in ai_mode) and cn.policy: # pure policy based move
policy_moves = cn.policy_ranking
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
@@ -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()}."
else:
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
sign = cn.player_sign(cn.next_player)
sel_moves = [ # top move, or anything not too bad, or anything that makes you still ahead
move
for i, move in enumerate(candidate_ai_moves)
if i == 0
or move["visits"] >= ai_settings["min_visits"]
and (move["pointsLost"] < ai_settings["random_loss"] or move["pointsLost"] < ai_settings["max_loss"] and sign * move["scoreLead"] > ai_settings["target_score"])
]
aimove = Move.from_gtp(random.choice(sel_moves)["move"], player=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()}."
elif "jigo" in ai_mode and candidate_ai_moves[0]["move"] != "pass":
sign = cn.player_sign(cn.next_player)
jigo_move = min(candidate_ai_moves, key=lambda move: abs(sign * move["scoreLead"] - ai_settings["target_score"]))
aimove = Move.from_gtp(jigo_move["move"], player=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"
else:
if "default" not in ai_mode and "katago" not in ai_mode:
game.katrain.log(f"Unknown AI mode {ai_mode} or policy missing, using default.", OUTPUT_INFO)
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"
else: # Engine based move
candidate_ai_moves = cn.candidate_moves
if "balance" in ai_mode and candidate_ai_moves[0]["move"] != "pass": # don't play suicidal to balance score - pass when it's best
sign = cn.player_sign(cn.next_player)
sel_moves = [ # top move, or anything not too bad, or anything that makes you still ahead
move
for i, move in enumerate(candidate_ai_moves)
if i == 0
or move["visits"] >= ai_settings["min_visits"]
and (move["pointsLost"] < ai_settings["random_loss"] or move["pointsLost"] < ai_settings["max_loss"] and sign * move["scoreLead"] > ai_settings["target_score"])
]
aimove = Move.from_gtp(random.choice(sel_moves)["move"], player=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()}."
elif "jigo" in ai_mode and candidate_ai_moves[0]["move"] != "pass":
sign = cn.player_sign(cn.next_player)
jigo_move = min(candidate_ai_moves, key=lambda move: abs(sign * move["scoreLead"] - ai_settings["target_score"]))
aimove = Move.from_gtp(jigo_move["move"], player=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"
else:
if "default" not in ai_mode and "katago" not in ai_mode:
game.katrain.log(f"Unknown AI mode {ai_mode} or policy missing, using default.", OUTPUT_INFO)
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)
try:
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
except IllegalMoveException as e:
game.katrain.log(f"AI Strategy {ai_mode} generated illegal move {aimove.gtp()}: {e}", OUTPUT_ERROR)
return None, None
+3 -5
View File
@@ -154,10 +154,8 @@ class Game:
def place_handicap_stones(self, n_handicaps):
board_size_x, board_size_y = self.board_size
near_x = 3 if board_size_x >= 13 else 2
near_y = 3 if board_size_y >= 13 else 2
if board_size_x < 3 or board_size_y < 3:
return
near_x = 3 if board_size_x >= 13 else min(2, board_size_x - 1)
near_y = 3 if board_size_y >= 13 else min(2, board_size_y - 1)
far_x = board_size_x - 1 - near_x
far_y = board_size_y - 1 - near_y
middle_x = board_size_x // 2 # what for even sizes?
@@ -176,7 +174,7 @@ class Game:
if n_handicaps % 2 == 1:
stones.append((middle_x, middle_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
def board_size(self):
+4 -3
View File
@@ -79,10 +79,11 @@ class SGFNode:
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."""
import sys
if self.is_root:
import sys
bszx, bszy = self.board_size
sys.setrecursionlimit(max(sys.getrecursionlimit(), 3 * bszx * bszy)) # thanks to lightvector for causing stack overflows ;)
bszx, bszy = self.board_size
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])
if self.children:
children = [c.sgf(**xargs) for c in self.order_children(self.children)]
+2 -2
View File
@@ -133,7 +133,7 @@ class ConfigPopup(QuickConfigGui):
col_container.add_widget(cols[0])
col_container.add_widget(cols[1])
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.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)
@@ -170,7 +170,7 @@ class ConfigPopup(QuickConfigGui):
def restart_engine(_dt):
old_engine = self.katrain.engine # type: KataGoEngine
old_proc =old_engine.katago_process
old_proc = old_engine.katago_process
if old_proc:
old_engine.shutdown(finish=True)