scroll, double click
This commit is contained in:
1 parent
e7b9f4be5b
commit
0de3335cd1
5 files changed
+34
-44
No files matched your search
@@ -97,30 +97,11 @@ In addition to these, there are:
|
||||
|
||||
### Configuration
|
||||
|
||||
Configuration is stored in `config.json`. Most settings are available to edit in the program, but some are not.
|
||||
Configuration is stored in `config.json`. Most settings are now available to edit in the program, but
|
||||
some cosmetic options are now.
|
||||
You can use `python katrain.py your_config_file.json` to use another config file instead.
|
||||
|
||||
#### The settings panel
|
||||
|
||||
|
||||
|
||||
The `trainer` block has the following options to tweak for engine assisted play and reviewing:
|
||||
|
||||
* `eval_off_show_last`: when the `eval` checkbox is off for a player, show coloured dots on the last this many moves regardless.
|
||||
* `undo_eval_threshold`, `undo_point_threshold`: prompt player to undo if move is worse than this in terms of points AND evaluation.
|
||||
|
||||
The following options are relevant for the `balance score` AI play mode.
|
||||
|
||||
* `balance_play_target_score`: indicates how many points the AI aims to win by when using 'balance score'.
|
||||
* `balance_play_randomize_eval`: when not needing to balance score, the AI will pick a random move which is at least this good as long as it stays ahead.
|
||||
* `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.
|
||||
|
||||
The cfg file has additional configuration for KataGo, which are documented there.
|
||||
|
||||
#### Configuring feedback
|
||||
|
||||
* `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.
|
||||
If you ever need to reset to the original settings, simply re-download the `config.json` file in this repository.
|
||||
|
||||
## FAQ
|
||||
|
||||
|
||||
@@ -33,12 +33,13 @@ Low priority
|
||||
[] more teaching / groups in danger? hard
|
||||
[] When creating a new game, the 9 buttons on the right side aren't all that useful. Maybe the 9, 13 and 19 ones make sense since these three board sizes are the traditionally used ones, but why 2, 4 and 9 stones buttons? Why 0.5, 6.5 and -40pts komi buttons?
|
||||
[] Score instead of game end
|
||||
[] Plus, explanatory bubbles would help to clarify labels like "Debug" (are there bugs in the matrix? :p), "enable_ownership" (which didn't seem to prevent or allow visualizing territory ownership on the board, as that was already covered by the "owner" checkbox anyway... so I guess I missed something), etc.
|
||||
-- possible right-click?
|
||||
|
||||
Wont do for now
|
||||
[] dual engine support -- easily possible but has weird effects on win rate etc
|
||||
[/] The UI would be directly understood if there were bubbles with a short explanation popping up when hovering above buttons and labels, e.g "Performing additional analysis to 1502 visits" which appears currently in the comments window after pushing the Extra button would also make a perfect explanatory bubble when hovering over said button too.
|
||||
|
||||
--- partially solved by config with help for some
|
||||
|
||||
bug/suggestion reports
|
||||
-- Plus, explanatory bubbles would help to clarify labels like "Debug" (are there bugs in the matrix? :p), "enable_ownership" (which didn't seem to prevent or allow visualizing territory ownership on the board, as that was already covered by the "owner" checkbox anyway... so I guess I missed something), etc.
|
||||
|
||||
@@ -43,8 +43,7 @@ class Game:
|
||||
self.komi = self.config.get("init_komi", {}).get(str(board_size), 6.5)
|
||||
self.root = GameNode(properties={**Game.DEFAULT_PROPERTIES, **{"SZ": board_size, "KM": self.komi, "DT": self.game_id}})
|
||||
|
||||
self.current_node = self.root
|
||||
self._init_chains()
|
||||
self.set_current_node(self.root)
|
||||
threading.Thread(target=lambda: self.analyze_all_nodes(-1_000_000, analyze_fast=analyze_fast), daemon=True).start() # return faster, but bypass Kivy Clock
|
||||
|
||||
def analyze_all_nodes(self, priority=0, analyze_fast=False):
|
||||
@@ -52,7 +51,7 @@ class Game:
|
||||
node.analyze(self.engines[node.next_player], priority=priority, analyze_fast=analyze_fast)
|
||||
|
||||
# -- move tree functions --
|
||||
def _init_chains(self):
|
||||
def _calculate_groups(self):
|
||||
board_size_x, board_size_y = self.board_size
|
||||
self.board = [[-1 for _x in range(board_size_x)] for _y in range(board_size_y)] # type: List[List[int]] # board pos -> chain id
|
||||
self.chains = [] # type: List[List[Move]] # chain id -> chain
|
||||
@@ -121,7 +120,7 @@ class Game:
|
||||
try:
|
||||
self._validate_move_and_update_chains(move, ignore_ko)
|
||||
except IllegalMoveException:
|
||||
self._init_chains()
|
||||
self._calculate_groups()
|
||||
raise
|
||||
played_node = self.current_node.play(move)
|
||||
self.current_node = played_node
|
||||
@@ -129,28 +128,29 @@ class Game:
|
||||
played_node.analyze(self.engines[played_node.next_player])
|
||||
return played_node
|
||||
|
||||
def set_current_node(self, node):
|
||||
self.current_node = node
|
||||
self._calculate_groups()
|
||||
|
||||
def undo(self, n_times=1):
|
||||
cn = self.current_node # avoid race conditions
|
||||
for _ in range(n_times):
|
||||
if not cn.is_root:
|
||||
cn = cn.parent
|
||||
self.current_node = cn
|
||||
self._init_chains()
|
||||
self.set_current_node(cn)
|
||||
|
||||
def redo(self, n_times=1):
|
||||
cn = self.current_node # avoid race conditions
|
||||
for _ in range(n_times):
|
||||
if cn.children:
|
||||
cn = cn.children[-1]
|
||||
self.current_node = cn
|
||||
self._init_chains()
|
||||
self.set_current_node(cn)
|
||||
|
||||
def switch_branch(self, direction):
|
||||
cn = self.current_node # avoid race conditions
|
||||
if cn.parent and len(cn.parent.children) > 1:
|
||||
ix = cn.parent.children.index(cn)
|
||||
self.current_node = cn.parent.children[(ix + direction) % len(cn.parent.children)]
|
||||
self._init_chains()
|
||||
self.set_current_node(cn.parent.children[(ix + direction) % len(cn.parent.children)])
|
||||
|
||||
def place_handicap_stones(self, n_handicaps):
|
||||
board_size_x, board_size_y = self.board_size
|
||||
|
||||
+11
-10
@@ -71,23 +71,25 @@ class BadukPanWidget(Widget):
|
||||
self.draw_hover_contents()
|
||||
|
||||
def on_touch_up(self, touch):
|
||||
if touch.button != "left":
|
||||
return
|
||||
|
||||
if not self.gridpos_x:
|
||||
if touch.button != "left" or not self.gridpos_x:
|
||||
return
|
||||
katrain = self.katrain
|
||||
if self.ghost_stone:
|
||||
if self.ghost_stone and touch.button == "left":
|
||||
katrain("play", self.ghost_stone)
|
||||
else:
|
||||
elif not self.ghost_stone:
|
||||
xd, xp = self._find_closest(touch.x, self.gridpos_x)
|
||||
yd, yp = self._find_closest(touch.y, self.gridpos_y)
|
||||
|
||||
nodes_here = [node for node in katrain.game.current_node.nodes_from_root if node.single_move and node.single_move.coords == (xp, yp)]
|
||||
if nodes_here and max(yd, xd) < self.grid_size / 2: # load old comment
|
||||
katrain.log(f"\nAnalysis:\n{nodes_here[-1].analysis}", OUTPUT_DEBUG)
|
||||
katrain.log(f"\nParent Analysis:\n{nodes_here[-1].parent.analysis}", OUTPUT_DEBUG)
|
||||
katrain.controls.info.text = nodes_here[-1].comment(sgf=True)
|
||||
if touch.is_double_tap: # navigate to move
|
||||
katrain.game.set_current_node(nodes_here[-1])
|
||||
self.draw_board_contents()
|
||||
else: # load comments
|
||||
katrain.log(f"\nAnalysis:\n{nodes_here[-1].analysis}", OUTPUT_DEBUG)
|
||||
katrain.log(f"\nParent Analysis:\n{nodes_here[-1].parent.analysis}", OUTPUT_DEBUG)
|
||||
katrain.controls.info.text = nodes_here[-1].comment(sgf=True)
|
||||
katrain.controls.info.text = nodes_here[-1].comment(sgf=True)
|
||||
|
||||
self.ghost_stone = None
|
||||
self.draw_hover_contents() # remove ghost
|
||||
@@ -191,7 +193,6 @@ class BadukPanWidget(Widget):
|
||||
realized_points_lost = None
|
||||
for i, node in enumerate(nodes[::-1]): # reverse order!
|
||||
points_lost = node.points_lost
|
||||
print("node", node.single_move, "pl", points_lost, "rpl", realized_points_lost)
|
||||
evalsize = 1
|
||||
if points_lost and realized_points_lost:
|
||||
if points_lost <= 0.5 and realized_points_lost <= 1.5:
|
||||
|
||||
@@ -228,6 +228,13 @@ class KaTrainGui(BoxLayout):
|
||||
self.log(msg, OUTPUT_INFO)
|
||||
self.controls.set_status(msg)
|
||||
|
||||
def on_touch_up(self, touch):
|
||||
if touch.button == "scrollup":
|
||||
self("redo")
|
||||
elif touch.button == "scrolldown":
|
||||
self("undo")
|
||||
return super().on_touch_up(touch)
|
||||
|
||||
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
|
||||
if isinstance(App.get_running_app().root_window.children[0], Popup):
|
||||
return # if in new game or load, don't allow keyboard shortcuts
|
||||
|
||||
Reference in new issue
Block a user