diff --git a/README.md b/README.md index e3905fb..bdb641d 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ The `trainer` block has the following options to tweak: * `balance_play_min_eval`: when needing to balance score, the AI will pick a move which is at least this good. * `balance_play_min_visits`: never pick a move with fewer playouts than this. * `undo_eval_threshold`, `undo_point_threshold`: prompt player to undo if move is worse than this in terms of points AND evaluation. -* `num_undo_prompts`: automatically undo bad moves when `undo` is on at most this many times. +* `num_undo_prompts`: automatically undo bad moves when `undo` is on at most this many times. Can be a fraction like 0.5 for 50% chance of being granted an undo on a bad move. The cfg file has additional configuration for kata. In particular, it changes the default to being more exploratory and score-based (and therefore nicer as an opponent, but weaker as analysis tool). diff --git a/board.py b/board.py index 9cba08f..618c326 100644 --- a/board.py +++ b/board.py @@ -259,6 +259,8 @@ class Board: # Play a Move from the current position, raise IllegalMoveException if invalid. def play(self, move, ignore_ko=False): + if not move.is_pass and not (0 <= move.coords[0] < self.board_size and 0 <= move.coords[1] < self.board_size): + raise IllegalMoveException(f"Move {move} outside of board coordinates") played_move = self.current_move.play(move) try: self._validate_move_and_update_chains(played_move, ignore_ko) diff --git a/controller.py b/controller.py index 0ffeafe..b5360b0 100644 --- a/controller.py +++ b/controller.py @@ -201,6 +201,17 @@ class EngineControls(GridLayout): for el in [self.ai_lock.checkbox, self.hints.black, self.hints.white, self.ai_auto.black, self.ai_auto.white, self.auto_undo.black, self.auto_undo.white, self.ai_move]: el.disabled = False + def universal_read(self, file): + with open(file, "rb") as f: + bin_c = f.read() + for encoding in ["utf-8", "iso-8859-1", "cp949", "GB18030"]: + try: + return bin_c.decode(encoding=encoding) + except: + pass + self.info.text = f"could not decode file contents of {file}" + return "" + def _do_analyze_sgf(self, sgf, faster=False, rewind=False): sgfprops = {k: v for k, v in re.findall(r"\b(\w+)\[(.*?)\]", sgf)} size = int(sgfprops.get("SZ", self.board_size)) @@ -220,8 +231,7 @@ class EngineControls(GridLayout): def readfile(files, mouse): fileselect_popup.dismiss() - with open(files[0]) as f: - self.action("analyze-sgf", f.read(), cbfast.active, cbrewind.active) + self.action("analyze-sgf", self.universal_read((files[0])), cbfast.active, cbrewind.active) fc.on_submit = readfile fileselect_popup.open()