Merge branch 'v1.6.0' into v1.5.1
This commit is contained in:
4 files changed
+29
-15
No files matched your search
+4
-3
@@ -130,9 +130,10 @@ class KaTrainGui(Screen, KaTrainBase):
|
|||||||
return self.play_mode.mode
|
return self.play_mode.mode
|
||||||
|
|
||||||
def toggle_continuous_analysis(self):
|
def toggle_continuous_analysis(self):
|
||||||
self.idle_analysis = not self.idle_analysis
|
if self.idle_analysis:
|
||||||
if not self.idle_analysis:
|
self.engine.terminate_continuous_query()
|
||||||
self.controls.set_status("", STATUS_INFO)
|
self.controls.set_status("", STATUS_INFO)
|
||||||
|
self.idle_analysis = not self.idle_analysis
|
||||||
self.update_state()
|
self.update_state()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
@@ -208,7 +209,7 @@ class KaTrainGui(Screen, KaTrainBase):
|
|||||||
): # cn mismatch stops this if undo fired. avoid message loop here or fires repeatedly.
|
): # cn mismatch stops this if undo fired. avoid message loop here or fires repeatedly.
|
||||||
self._do_ai_move(cn)
|
self._do_ai_move(cn)
|
||||||
Clock.schedule_once(self.board_gui.play_stone_sound, 0.25)
|
Clock.schedule_once(self.board_gui.play_stone_sound, 0.25)
|
||||||
if len(self.engine.queries) == 0 and self.idle_analysis:
|
if len(self.engine.queries) == 0 and self.idle_analysis: # TODO on node change, toggle twice?
|
||||||
self("analyze-extra", "extra", continuous=True)
|
self("analyze-extra", "extra", continuous=True)
|
||||||
Clock.schedule_once(lambda _dt: self.update_gui(cn, redraw_board=redraw_board), -1) # trigger?
|
Clock.schedule_once(lambda _dt: self.update_gui(cn, redraw_board=redraw_board), -1) # trigger?
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
VERSION = "1.5.1"
|
VERSION = "1.6.0"
|
||||||
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.6.0" # keep config files from this version
|
||||||
|
|
||||||
OUTPUT_ERROR = -1
|
OUTPUT_ERROR = -1
|
||||||
OUTPUT_KATAGO_STDERR = -0.5
|
OUTPUT_KATAGO_STDERR = -0.5
|
||||||
|
|||||||
+20
-5
@@ -34,6 +34,7 @@ class KataGoEngine:
|
|||||||
def __init__(self, katrain, config):
|
def __init__(self, katrain, config):
|
||||||
self.katrain = katrain
|
self.katrain = katrain
|
||||||
self.queries = {} # outstanding query id -> start time and callback
|
self.queries = {} # outstanding query id -> start time and callback
|
||||||
|
self.continuous_query = None
|
||||||
self.config = config
|
self.config = config
|
||||||
self.query_counter = 0
|
self.query_counter = 0
|
||||||
self.katago_process = None
|
self.katago_process = None
|
||||||
@@ -85,10 +86,10 @@ class KataGoEngine:
|
|||||||
def start(self):
|
def start(self):
|
||||||
try:
|
try:
|
||||||
self.katrain.log(f"Starting KataGo with {self.command}", OUTPUT_DEBUG)
|
self.katrain.log(f"Starting KataGo with {self.command}", OUTPUT_DEBUG)
|
||||||
startupinfo = None # stop command box popups on windows/pyinstaller
|
startupinfo = None
|
||||||
if hasattr(subprocess, "STARTUPINFO"):
|
if hasattr(subprocess, "STARTUPINFO"):
|
||||||
startupinfo = subprocess.STARTUPINFO()
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW # stop command box popups on win/pyinstaller
|
||||||
self.katago_process = subprocess.Popen(
|
self.katago_process = subprocess.Popen(
|
||||||
self.command,
|
self.command,
|
||||||
startupinfo=startupinfo,
|
startupinfo=startupinfo,
|
||||||
@@ -107,7 +108,10 @@ class KataGoEngine:
|
|||||||
|
|
||||||
def on_new_game(self):
|
def on_new_game(self):
|
||||||
self.base_priority += 1
|
self.base_priority += 1
|
||||||
|
for query_id in self.queries:
|
||||||
|
self.terminate_query(query_id)
|
||||||
self.queries = {}
|
self.queries = {}
|
||||||
|
self.continuous_query = None
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
self.queries = {}
|
self.queries = {}
|
||||||
@@ -206,7 +210,8 @@ class KataGoEngine:
|
|||||||
)
|
)
|
||||||
self.katrain.log(line, OUTPUT_EXTRA_DEBUG)
|
self.katrain.log(line, OUTPUT_EXTRA_DEBUG)
|
||||||
try:
|
try:
|
||||||
callback(analysis, partial_result=partial_result)
|
if callback and not analysis.get("noResults", False):
|
||||||
|
callback(analysis)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.katrain.log(f"Error in engine callback for query {query_id}: {e}", OUTPUT_ERROR)
|
self.katrain.log(f"Error in engine callback for query {query_id}: {e}", OUTPUT_ERROR)
|
||||||
if getattr(self.katrain, "update_state", None): # easier mocking etc
|
if getattr(self.katrain, "update_state", None): # easier mocking etc
|
||||||
@@ -215,12 +220,16 @@ class KataGoEngine:
|
|||||||
self.katrain.log(f"Unexpected exception {e} while processing KataGo output {line}", OUTPUT_ERROR)
|
self.katrain.log(f"Unexpected exception {e} while processing KataGo output {line}", OUTPUT_ERROR)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
def send_query(self, query, callback, error_callback, next_move=None):
|
def send_query(self, query, callback, error_callback, next_move=None, report_during_search=False):
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self.query_counter += 1
|
self.query_counter += 1
|
||||||
if "id" not in query:
|
if "id" not in query:
|
||||||
query["id"] = f"QUERY:{str(self.query_counter)}"
|
query["id"] = f"QUERY:{str(self.query_counter)}"
|
||||||
self.queries[query["id"]] = (callback, error_callback, time.time(), next_move)
|
self.queries[query["id"]] = (callback, error_callback, time.time(), next_move)
|
||||||
|
if report_during_search:
|
||||||
|
query["reportDuringSearchEvery"] = 0.25
|
||||||
|
self.terminate_continuous_query()
|
||||||
|
self.continuous_query = query["id"]
|
||||||
if self.katago_process:
|
if self.katago_process:
|
||||||
self.katrain.log(f"Sending query {query['id']}: {json.dumps(query)}", OUTPUT_DEBUG)
|
self.katrain.log(f"Sending query {query['id']}: {json.dumps(query)}", OUTPUT_DEBUG)
|
||||||
try:
|
try:
|
||||||
@@ -228,7 +237,13 @@ class KataGoEngine:
|
|||||||
self.katago_process.stdin.flush()
|
self.katago_process.stdin.flush()
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
self.check_alive(os_error=str(e), exception_if_dead=True)
|
self.check_alive(os_error=str(e), exception_if_dead=True)
|
||||||
return # do not raise, since there's nothing to catch it
|
|
||||||
|
def terminate_continuous_query(self):
|
||||||
|
self.terminate_query(self.continuous_query)
|
||||||
|
|
||||||
|
def terminate_query(self, query_id):
|
||||||
|
if query_id is not None:
|
||||||
|
self.send_query({"action": "terminate", "terminateId": query_id}, None, None)
|
||||||
|
|
||||||
def request_analysis(
|
def request_analysis(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -325,13 +325,11 @@ class Game:
|
|||||||
|
|
||||||
if mode == "extra":
|
if mode == "extra":
|
||||||
if kwargs.get("continuous", False):
|
if kwargs.get("continuous", False):
|
||||||
visits = min(
|
visits = 1_000_000_000
|
||||||
1_000_000_000, max(engine.config["max_visits"], math.ceil(cn.analysis_visits_requested * 1.25))
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
visits = cn.analysis_visits_requested + engine.config["max_visits"]
|
visits = cn.analysis_visits_requested + engine.config["max_visits"]
|
||||||
self.katrain.controls.set_status(i18n._("extra analysis").format(visits=visits), STATUS_ANALYSIS)
|
self.katrain.controls.set_status(i18n._("extra analysis").format(visits=visits), STATUS_ANALYSIS)
|
||||||
cn.analyze(engine, visits=visits, priority=-1_000, time_limit=False)
|
cn.analyze(engine, visits=visits, priority=-1_000, time_limit=False, report_during_search=True)
|
||||||
return
|
return
|
||||||
if mode == "game":
|
if mode == "game":
|
||||||
nodes = self.root.nodes_in_tree
|
nodes = self.root.nodes_in_tree
|
||||||
|
|||||||
Reference in new issue
Block a user