From d57e6ce87dfc69ae753794ee5cf4d557cea09062 Mon Sep 17 00:00:00 2001 From: Sander Land Date: Sat, 15 Feb 2020 19:05:55 +0100 Subject: [PATCH] readme update and undo lock option --- README.md | 20 ++++++++++++++++++-- board.py | 6 +++--- config.json | 3 ++- controller.py | 13 ++++++++++--- katrain.py | 6 ++++-- 5 files changed, 37 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 06c3fc6..dfe6c90 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,19 @@ +Introduction +============ +This repository contains tool for playing go with AI feedback aimed at kyu players. +The idea is to give immediate feedback on the many large mistakes we make in terms of inefficient moves. +It is based on the KataGo AI and relies heavily on score estimation rather than win rate. + +Some uses include: + +* Analyze your games to find the moves that were most costly in terms of points lost. +* Play against AI and get immediate feedback on mistakes with option to retry. +* Play against a stronger player and use the retry option instead of handicap stones. +* Play a match with an evenly matched friend where both players get instant feedback. + + +![screenshot](https://i.imgur.com/2T2b6qL.png) + Manual ====== @@ -23,7 +39,7 @@ Options * Undo: automatically undo poor moves for this player and make them try again. * AI: let the AI control this player. Check both for self-play. * Show owner: show expected control of territory. - * Lock AI: disallow extra undos, changing hints options, changing auto move, or AI move. + * Lock AI: disallow extra undos, changing hints options, changing auto move, or AI move. Also turns off the option to click on a move to see detailed comments. * Fast: use a lower number of max visits for evaluation/AI move. * Balance score: Deliberately make sub-optimal moves as the AI in an attempt to balance the score towawrds a slight win. @@ -58,7 +74,7 @@ Play * Click `Analyze` * Save game - * Click save to get an sgf as `out.sgf` with comments (and a short version in the text box). + * Click save to get an sgf with comments saved in the sgfout/ directory (and a short version in the text box). Configuration ------------- diff --git a/board.py b/board.py index bad555a..da9c109 100644 --- a/board.py +++ b/board.py @@ -315,8 +315,8 @@ class Board: return "\n".join("".join(Move.PLAYERS[self.chains[c][0].player] if c >= 0 else "-" for c in l) for l in self.board) + f"\ncaptures: {self.prisoner_count}" def write_sgf(self, komi, train_settings, file_name=None): - def sgfify(mvs): - return f"(;GM[1]FF[4]SZ[{self.board_size}]KM[{komi}]RU[JP];" + ";".join(mvs) + ")" + def sgfify(mvs, comment=""): + return f"(;GM[1]FF[4]SZ[{self.board_size}]KM[{komi}]RU[JP];" + ";".join(mvs) + (f"C[{comment}])" if comment else "") def format_move(move, prev_move): undos = [m for m in prev_move.children if m != move] @@ -349,4 +349,4 @@ class Board: pass with open(file_name, "w") as f: f.write(sgfify(sgfmoves)) - return sgfify(sgfmoves_small) + return sgfify(sgfmoves_small, f"SGF with analysis written to {file_name}") diff --git a/config.json b/config.json index 0c94dcd..70fbc1c 100644 --- a/config.json +++ b/config.json @@ -39,7 +39,8 @@ "undo_eval_threshold": 0.875, "undo_point_threshold": 1, "num_undo_prompts": 1, - "sgf_show_best_move_threshold": 0.95 + "sgf_show_best_move_threshold": 0.95, + "dont_lock_undos": false }, "debug": { "level": 1 diff --git a/controller.py b/controller.py index 4c94124..855cb5d 100644 --- a/controller.py +++ b/controller.py @@ -36,6 +36,7 @@ class EngineControls(GridLayout): self.komi = 6.5 # loaded from config in init self.outstanding_analysis_queries = [] # allows faster interaction while kata is starting self.kata = None + self.query_time = {} def redraw(self, include_board=False): if include_board: @@ -153,7 +154,12 @@ class EngineControls(GridLayout): self.play(aimove) def _do_undo(self): - if self.ai_lock.active and self.auto_undo.active(self.board.current_move.player) and len(self.board.current_move.parent.children) > self.train_settings["num_undo_prompts"]: + if ( + self.ai_lock.active + and self.auto_undo.active(self.board.current_move.player) + and len(self.board.current_move.parent.children) > self.train_settings["num_undo_prompts"] + and not self.train_settings.get("dont_lock_undos") + ): self.info.text = f"Can't undo more than {self.train_settings['num_undo_prompts']} time(s) when locked" return self.board.undo() @@ -183,8 +189,6 @@ class EngineControls(GridLayout): while self.outstanding_analysis_queries: self._send_analysis_query(self.outstanding_analysis_queries.pop(0)) line = self.kata.stdout.readline() - if self.debug: - print("KATA ANALYSIS RECEIVED:", line[:80], "...") if not line: # occasionally happens? return try: @@ -192,10 +196,13 @@ class EngineControls(GridLayout): except json.JSONDecodeError as e: print(f"JSON decode error: '{e}' encountered after receiving input '{line}'") return + if self.debug: + print(f"[{time.time()-self.query_time.get(analysis['id'],0):.1f}] kata analysis received:", line[:80], "...") self.board.store_analysis(analysis) self.update_evaluation() def _send_analysis_query(self, query): + self.query_time[query["id"]] = time.time() if self.kata: self.kata.stdin.write((json.dumps(query) + "\n").encode()) self.kata.stdin.flush() diff --git a/katrain.py b/katrain.py index 79eed9c..f86c39c 100644 --- a/katrain.py +++ b/katrain.py @@ -53,11 +53,13 @@ class BadukPanWidget(Widget): yd, yp = self._find_closest(touch.y) stones_here = [m for m in self.engine.board.stones if m.coords == (xp, yp)] if stones_here and max(yd, xd) < self.grid_size / 2: # load old comment - self.engine.info.text = stones_here[-1].comment(sgf=True) - self.engine.show_evaluation_stats(stones_here[-1]) if self.engine.debug: print("\nAnalysis:\n", stones_here[-1].analysis) print("\nParent Analysis:\n", stones_here[-1].parent.analysis) + if not self.engine.ai_lock.active: + self.engine.info.text = stones_here[-1].comment(sgf=True) + self.engine.show_evaluation_stats(stones_here[-1]) + self.ghost_stone = None self.redraw() # remove ghost