* distributed option

* i18n, pause, etc

* fixes galore

* max buffer etc

* test override config

* maxgame

* visit

* contribute menu

* restore vars

* i18n

* actually start

* retry distr and errors

* pw eye / move speed

* cfg/no log

* shutdown fix

* shutdown fix

* download fix

* wut

* stats and desc

* esc fix

* Omit terminate queries from KataGoEngine.queries. (#353)

* press key h to clear current node analysis. (#352)

* clear current node all analysis

* add reset key binding

* reset all analysis off.

* code refactor

* code change according to review.

* remove useless variable

* code review changes

* clean up reset node

* oops

* Setup position feature (#354)

* go for zebras

* setup working!

* uhh

* tweak

* img

* fix i18n

* AEEEEEEEEEE and stop

* link in readme

* new game options for edit/new/setup (#357)

* new game options for edit/new/setup

* feedback

* fix

* ai thoughts

* no tristate cb

* branch fixing (#358)

* new game options for edit/new/setup

* feedback

* fix

* ai thoughts

* no tristate cb

* fix comma etc

* normal katago

* kata

* dll

* save window pos/size

* BAD comma

* new linux binary + allow default engine

* fix

* unused icon, build mac

* exe

* player new game fix

* fix i18n

* test test

* just print

* 한국어

* –활성화

* Add new Traditional Chinese translation (#362)

Improve some translations as well.

* cn (#363)

* cn

* rm old

* rm old

* informal you

* mo

* jp (#364)

* config and readme

* mo

* kgtraining

* fix score

* lower stddev a bit

* tweak weights

* 1.8 French Po (#365)

* fr and getattr fix

* -DBUILD_DISTRIBUTED=1

* openssl

* openssl

* openssl

* openssl

* config

* jp (num undos) (#366)

* clarify undo in en, po->mo

* fix player name

* new kata

* new kata

* Load game option save config (#367)

* in the load dialog,  "load fast analysis" and "rewind to the start" option ,save to config file

* in the load dialog,  "load fast analysis" and "rewind to the start" option ,save to config file

* default option

* remove useless override

* save check state when file dialog's submit event ,

* save config in callback

* readme

* active games

* de

* log games as json always

* black

* Russian localization for 1.8.0 (#375)

* ru

Co-authored-by: pdeblanc <peter@spaceandgames.com>
Co-authored-by: xiaoyifang <105986+xiaoyifang@users.noreply.github.com>
Co-authored-by: Liou, Guan-Cheng <Tony-Liou@users.noreply.github.com>
Co-authored-by: kaorahi <38910552+kaorahi@users.noreply.github.com>
Co-authored-by: Dontbtme <66470440+Dontbtme@users.noreply.github.com>
Co-authored-by: Alexander Kiselev <satoshi.yoda.register.264@gmail.com>
This commit is contained in:
authored and GitHub committed 2021-03-05 20:40:48 +01:00
1 parent 34b3f92d20
commit c5587bf6e1
58 files changed
+5902 -752

No files matched your search

+91 -48
View File
@@ -24,8 +24,7 @@ class EngineDiedException(Exception):
pass
class KataGoEngine:
"""Starts and communicates with the KataGO analysis engine"""
class BaseEngine: # some common elements between analysis and contribute engine
# TODO: we don't support suicide in game.py, so no "tt": "tromp-taylor", "nz": "new-zealand"
RULESETS_ABBR = [
@@ -37,14 +36,62 @@ class KataGoEngine:
]
RULESETS = {fromkey: name for abbr, name in RULESETS_ABBR for fromkey in [abbr, name]}
@staticmethod
def get_rules(node):
return KataGoEngine.RULESETS.get(str(node.ruleset).lower(), "japanese")
def __init__(self, katrain, config):
self.katrain = katrain
self.queries = {} # outstanding query id -> start time and callback
self.config = config
@staticmethod
def get_rules(node):
ruleset = node.ruleset
if ruleset.strip().startswith("{"):
try:
ruleset = json.loads(ruleset)
except:
pass
if isinstance(ruleset, dict):
return ruleset
return KataGoEngine.RULESETS.get(str(ruleset).lower(), "japanese")
def advance_showing_game(self):
pass # avoid transitional error
def status(self):
return "" # avoid transitional error
def get_engine_path(self, exe):
if not exe:
if kivy_platform == "win":
exe = "katrain/KataGo/katago.exe"
elif kivy_platform == "linux":
exe = "katrain/KataGo/katago"
else:
exe = find_package_resource("katrain/KataGo/katago-osx") # github actions built
if not os.path.isfile(exe) or "arm64" in platform.version().lower():
exe = "katago" # e.g. MacOS after brewing
if exe.startswith("katrain"):
exe = find_package_resource(exe)
exepath, exename = os.path.split(exe)
if exepath and not os.path.isfile(exe):
self.katrain.log(i18n._("Kata exe not found").format(exe=exe), OUTPUT_ERROR)
return None
elif not exepath:
paths = os.getenv("PATH", ".").split(os.pathsep) + ["/opt/homebrew/bin/"]
exe_with_paths = [os.path.join(path, exe) for path in paths if os.path.isfile(os.path.join(path, exe))]
if not exe_with_paths:
self.katrain.log(i18n._("Kata exe not found in path").format(exe=exe), OUTPUT_ERROR)
return None
exe = exe_with_paths[0]
return exe
class KataGoEngine(BaseEngine):
"""Starts and communicates with the KataGO analysis engine"""
def __init__(self, katrain, config):
super().__init__(katrain, config)
self.queries = {} # outstanding query id -> start time and callback
self.query_counter = 0
self.katago_process = None
self.base_priority = 0
@@ -60,36 +107,15 @@ class KataGoEngine:
self.command = config["altcommand"]
self.shell = True
else:
if not exe:
if kivy_platform == "win":
exe = "katrain/KataGo/katago.exe"
elif kivy_platform == "linux":
exe = "katrain/KataGo/katago"
else:
exe = find_package_resource("katrain/KataGo/katago-osx") # github actions built
if not os.path.isfile(exe) or "arm64" in platform.version().lower():
exe = "katago" # e.g. MacOS after brewing
model = find_package_resource(config["model"])
cfg = find_package_resource(config["config"])
if exe.startswith("katrain"):
exe = find_package_resource(exe)
exepath, exename = os.path.split(exe)
if exepath and not os.path.isfile(exe):
self.katrain.log(i18n._("Kata exe not found").format(exe=exe), OUTPUT_ERROR)
return # don't start
elif not exepath:
paths = os.getenv("PATH", ".").split(os.pathsep) + ["/opt/homebrew/bin/"]
exe_with_paths = [os.path.join(path, exe) for path in paths if os.path.isfile(os.path.join(path, exe))]
if not exe_with_paths:
self.katrain.log(i18n._("Kata exe not found in path").format(exe=exe), OUTPUT_ERROR)
return # don't start
exe = exe_with_paths[0]
elif not os.path.isfile(model):
exe = self.get_engine_path(config["katago"])
if not exe:
return
if not os.path.isfile(model):
self.katrain.log(i18n._("Kata model not found").format(model=model), OUTPUT_ERROR)
return # don't start
elif not os.path.isfile(cfg):
if not os.path.isfile(cfg):
self.katrain.log(i18n._("Kata config not found").format(config=cfg), OUTPUT_ERROR)
return # don't start
self.command = shlex.split(
@@ -98,6 +124,7 @@ class KataGoEngine:
self.start()
def start(self):
with self.thread_lock:
self.write_queue = queue.Queue()
try:
@@ -131,10 +158,22 @@ class KataGoEngine:
self.base_priority += 1
if not self.is_idle():
with self.thread_lock:
for query_id in list(self.queries.keys()):
self.terminate_query(query_id)
self.queries = {}
self.write_queue = queue.Queue()
self.terminate_queries(only_for_node=None, lock=False)
self.queries = {}
def terminate_queries(self, only_for_node=None, lock=True):
if lock:
with self.thread_lock:
return self.terminate_queries(only_for_node=only_for_node, lock=False)
for query_id, (_, _, _, _, node) in list(self.queries.items()):
if only_for_node is None or only_for_node is node:
self.terminate_query(query_id)
def terminate_query(self, query_id):
if query_id is not None:
self.send_query({"action": "terminate", "terminateId": query_id}, None, None)
self.queries.pop(query_id, None)
def restart(self):
self.queries = {}
@@ -220,9 +259,11 @@ class KataGoEngine:
continue
query_id = analysis["id"]
if query_id not in self.queries:
self.katrain.log(f"Query result {query_id} discarded -- recent new game?", OUTPUT_DEBUG)
self.katrain.log(
f"Query result {query_id} discarded -- recent new game or node reset?", OUTPUT_DEBUG
)
continue
callback, error_callback, start_time, next_move = self.queries[query_id]
callback, error_callback, start_time, next_move, _ = self.queries[query_id]
if "error" in analysis:
del self.queries[query_id]
if error_callback:
@@ -249,6 +290,7 @@ class KataGoEngine:
callback(analysis, partial_result)
except Exception as e:
self.katrain.log(f"Error in engine callback for query {query_id}: {e}", OUTPUT_ERROR)
traceback.print_exc()
if getattr(self.katrain, "update_state", None): # easier mocking etc
self.katrain.update_state()
except Exception as e:
@@ -258,14 +300,15 @@ class KataGoEngine:
def _write_stdin_thread(self): # flush only in a thread since it returns only when the other program reads
while self.katago_process is not None:
try:
query, callback, error_callback, next_move = self.write_queue.get(block=True, timeout=0.1)
query, callback, error_callback, next_move, node = self.write_queue.get(block=True, timeout=0.1)
except queue.Empty:
continue
with self.thread_lock:
if "id" not in query:
self.query_counter += 1
query["id"] = f"QUERY:{str(self.query_counter)}"
self.queries[query["id"]] = (callback, error_callback, time.time(), next_move)
if query.get("action") != "terminate":
self.queries[query["id"]] = (callback, error_callback, time.time(), next_move, node)
self.katrain.log(f"Sending query {query['id']}: {json.dumps(query)}", OUTPUT_DEBUG)
try:
self.katago_process.stdin.write((json.dumps(query) + "\n").encode())
@@ -273,12 +316,8 @@ class KataGoEngine:
except OSError as e:
self.check_alive(os_error=str(e), exception_if_dead=False)
def send_query(self, query, callback, error_callback, next_move=None):
self.write_queue.put((query, callback, error_callback, next_move))
def terminate_query(self, query_id):
if query_id is not None:
self.send_query({"action": "terminate", "terminateId": query_id}, None, None)
def send_query(self, query, callback, error_callback, next_move=None, node=None):
self.write_queue.put((query, callback, error_callback, next_move, node))
def request_analysis(
self,
@@ -299,6 +338,11 @@ class KataGoEngine:
nodes = analysis_node.nodes_from_root
moves = [m for node in nodes for m in node.moves]
initial_stones = [m for node in nodes for m in node.placements]
clear_placements = [m for node in nodes for m in node.clear_placements]
if clear_placements: # TODO: support these
self.katrain.log(f"Not analyzing node {analysis_node} as there are AE commands in the path", OUTPUT_DEBUG)
return
if next_move:
moves.append(next_move)
if ownership is None:
@@ -341,7 +385,6 @@ class KataGoEngine:
settings["maxTime"] = self.config["max_time"]
if self.config.get("wide_root_noise", 0.0) > 0.0: # don't send if 0.0, so older versions don't error
settings["wideRootNoise"] = self.config["wide_root_noise"]
query = {
"rules": self.get_rules(analysis_node),
"priority": self.base_priority + priority,
@@ -362,5 +405,5 @@ class KataGoEngine:
query["reportDuringSearchEvery"] = report_every
if avoid:
query["avoidMoves"] = avoid
self.send_query(query, callback, error_callback, next_move)
self.send_query(query, callback, error_callback, next_move, analysis_node)
analysis_node.analysis_visits_requested = max(analysis_node.analysis_visits_requested, visits)