readme update
This commit is contained in:
1 parent
0de3335cd1
commit
26a37724dd
10 files changed
+85
-62
No files matched your search
@@ -10,7 +10,19 @@ but has since grown to include a wide range of features, including:
|
||||
* Play against a wide range of weakened versions of AI with various styles.
|
||||
* Play against a stronger player and use the retry option instead of handicap stones.
|
||||
|
||||

|
||||
## Screenshots
|
||||
|
||||
|
||||
| Analyze games | Play against an AI Teacher |
|
||||
| ------------- | ------------- |
|
||||
|  |  |
|
||||
|
||||
## Quickstart
|
||||
* Right-click any button you don't understand for help.
|
||||
* To analyze a game, load it using the button in the top right, or press `ctrl-L`
|
||||
* To play against AI, pick an AI from the drop down a color and either 'human' or 'teach' for yourself and start playing.
|
||||
* For different board sizes, use the button with the little goban in the bottom right for a new game.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -35,70 +47,80 @@ but has since grown to include a wide range of features, including:
|
||||
* Executables for Mac are not available, so compiling from source code is required there.
|
||||
* Start the app by running `python katrain.py`. Note that the program can be slow to initialize the first time, due to KataGo's GPU tuning.
|
||||
|
||||
## Quickstart
|
||||
* To analyze a game, load it using the button in the top right, or press `ctrl-L`
|
||||
* To play against AI, pick an AI from the drop down a color and either 'human' or 'teach' for yourself and start playing.
|
||||
* For different board sizes, use the button with the little goban in the bottom right for a new game.
|
||||
|
||||
## Manual
|
||||
|
||||
[Screenshot]
|
||||
|
||||
### Play
|
||||
|
||||
Under the 'play' tab you can select who is playing black and white.
|
||||
* Human is simple play with potential feedback, but without auto-undo.
|
||||
* Teach will give you instant feedback, and auto-undo bad moves to give you a second chance.
|
||||
* Settings for this mode can be found under 'Configure teacher'
|
||||
* Settings for this mode can be found under 'Configure Teacher'
|
||||
* AI will activate the AI in the dropdown next to the buttons.
|
||||
* Settings for the selected AI(s) can be found under 'Configure AI'
|
||||
* Settings for the selected AI(s) can be found under 'Configure AIs'
|
||||
|
||||
If you do not want to see 'Points lost' or other feedback for your moves,
|
||||
set 'show last n dots' to 0 under 'Configure Teacher', and click on the words 'Points lost' to hide its value.
|
||||
|
||||
#### AIs
|
||||
Available AIs, with strength indicating an estimate for the default settings, are:
|
||||
|
||||
* **9p**: **Default** is full KataGo, above professional level.
|
||||
* **[9p+]** **Default** is full KataGo, above professional level.
|
||||
* **Balance** is KataGo occasionally making weaker moves, attempting to win by ~2 points.
|
||||
* **Jigo** is KataGo aggressively making weaker moves, attempting to win by 0.5 points.
|
||||
* **~4d**: **Policy** is the top move from the policy network (it's 'shape sense' without reading), should be around high dan level depending on the model used.
|
||||
* **~1d**: **P:Weighted** will pick a random move weighted by the policy, as long as it's above `lower_bound`. `weaken_fac` uses policy^(1/weaken_fac), increasing the chance for weaker moves.
|
||||
* **~5k**: **P:Pick** will pick a `pick_n + pick_frac * <number of legal moves>` moves at random, and play the best move among them.
|
||||
* **[~4d]** **Policy** uses the top move from the policy network (it's 'shape sense' without reading), should be around high dan level depending on the model used.
|
||||
* **[~3k]**: **P:Weighted** picks a random move weighted by the policy, as long as it's above `lower_bound`. `weaken_fac` uses `policy^(1/weaken_fac)`, increasing the chance for weaker moves.
|
||||
* **[~5k]**: **P:Pick** picks `pick_n + pick_frac * <number of legal moves>` moves at random, and play the best move among them.
|
||||
The setting `pick_override` determines the minimum value at which this process is bypassed to play the best move instead, preventing obvious blunders.
|
||||
This is probably the best choice for kyu players who want a chance of winning. Variants of this strategy include:
|
||||
* **~3k**: **P:Local** will pick such moves biased towards the last move with probability related to `local_stddev`.
|
||||
* **~10k**: **~P:Tenuki** is biased in the opposite way as P:Local, using the same setting.
|
||||
* **~7k**: P:Influence is biased towards 4th+ line moves, with every line below that dividing both the chance of considering the move and the policy value by `influence_weight`. Consider setting `pick_frac=1.0` to only affect the policy weight.
|
||||
* **~7k**: P:Territory is biased in the opposite way, towards 1-3rd line moves, using the same setting.
|
||||
* * **~7k**: P:Noise mixes the policy with `noise_strength` Dirichlet noise. At `noise_strength=0.9` play is near-random, while `noise_strength=0.7` is still quite strong. Regardless, mistakes are typically strange can include senseless first-line moves.
|
||||
* `<Pause>` pauses AI moves, in case you want to do analysis without triggering moves, or simply hide the evaluation dots for this player.
|
||||
This, along with 'Weighted' are probably the best choice for kyu players who want a chance of winning without playing the sillier bots below. Variants of this strategy include:
|
||||
* **[~3k]**: **P:Local** will pick such moves biased towards the last move with probability related to `local_stddev`.
|
||||
* **[~7k]**: **~P:Tenuki** is biased in the opposite way as P:Local, using the same setting.
|
||||
* **[~7k]**: **P:Influence** is biased towards 4th+ line moves, with every line below that dividing both the chance of considering the move and the policy value by `influence_weight`. Consider setting `pick_frac=1.0` to only affect the policy weight.
|
||||
* **[~7k]**: **P:Territory** is biased in the opposite way, towards 1-3rd line moves, using the same setting.
|
||||
* * **[~7k]**: **P:Noise** mixes the policy with `noise_strength` Dirichlet noise. At `noise_strength=0.9` play is near-random, while `noise_strength=0.7` is still quite strong. A threshold setting is included to avoid senseless first-line moves.
|
||||
* **<Pause>**: pauses AI moves, in case you want to do analysis without triggering moves, or simply hide the evaluation dots for this player.
|
||||
|
||||
Selecting the AI as either white or black opens up the option to configure it under 'Configure AI'.
|
||||
|
||||
### Analysis
|
||||
|
||||
* The checkboxes have the following keyboard shortscuts, and they configure:
|
||||
* The checkboxes have the following keyboard shortcuts, and they configure:
|
||||
* **[q]**: Child moves are shown. On by default, can turn it off to avoid obscuring other information or when wanting to guess the next move.
|
||||
* **[w]**: All dots: Show all evaluation dots instead of the last few. You can configure how many are shown with thsi setting off under 'Configure Teacher'.
|
||||
* **[e]**:
|
||||
* **[w]**: All dots: Show all evaluation dots instead of the last few.
|
||||
* You can configure how many are shown with this setting off, and whether they are shown for AIs under 'Play/Configure Teacher'.
|
||||
* **[e]**: Top moves: Show the next moves KataGo considered, colored by their expected point loss. Small dots indicate high uncertainty. Hover over any of them to see the principal variation.
|
||||
* **[r]**: Show owner: Show expected ownership of each square.
|
||||
* **[t]**: NN Policy: Show KataGo's policy network evaluation, i.e. where it thinks the best next move is purely from the position, and in the absence of any 'reading'.
|
||||
|
||||
* The analysis buttons have the following keyboard shortcuts, and they do:
|
||||
* **[a]**: Extra: Re-evaluate the position using more visits, usually resulting in a more accurate evaluation.
|
||||
* **[s]**: Equalize: Re-evaluate all currently shown next moves with the same visits as the current top move. Useful to increase confidence in the suggestions with high uncertainty.
|
||||
* **[d]**: Sweep: Evaluate all possible next moves. This can take a bit of time, but the result is nothing if not colourful.
|
||||
|
||||
|
||||
### Keyboard shortcuts
|
||||
|
||||
In addition to shortcuts mentioned above, there are:
|
||||
|
||||
In addition to these, there are:
|
||||
|
||||
* Tab to switch between analysis and play modes. (NB. keyboard shortcuts function regardless)
|
||||
* ~ or ` or p : Hide side panel UI and only show the board.
|
||||
* Ctrl-v : Load SGF from clipboard
|
||||
* Ctrl-c : Save SGF to clipboard
|
||||
* Ctrl-l : Load SGF from file
|
||||
* Ctrl-s : Load SGF to file
|
||||
* Ctrl-n : Load SGF from clipboard
|
||||
* **[Tab]**: to switch between analysis and play modes. (NB. keyboard shortcuts function regardless)
|
||||
* **[~]** or **[`]** or **[p]**: Hide side panel UI and only show the board.
|
||||
* **[enter]**: AI Move
|
||||
* **[arrow up]** or **[z]**: Undo move. Hold shift for 10 moves at a time, or ctrl to skip to thte start.
|
||||
* **[arrow down]** or **[x]**: Redo move. Hold shift for 10 moves at a time, or ctrl to skip to thte start.
|
||||
* **[scroll up]**: Undo move. Only works with the mouse pointer on the board.
|
||||
* **[scroll down]**: Redo move.
|
||||
* **[Ctrl-v]**: Load SGF from clipboard
|
||||
* **[Ctrl-c]**: Save SGF to clipboard
|
||||
* **[Ctrl-l]**: Load SGF from file
|
||||
* **[Ctrl-s]**: Load SGF to file
|
||||
* **[Ctrl-n]**: Load SGF from clipboard
|
||||
* **[space]**: Pass
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
Configuration is stored in `config.json`. Most settings are now available to edit in the program, but
|
||||
some cosmetic options are now.
|
||||
some cosmetic options are not.
|
||||
You can use `python katrain.py your_config_file.json` to use another config file instead.
|
||||
|
||||
If you ever need to reset to the original settings, simply re-download the `config.json` file in this repository.
|
||||
@@ -108,7 +130,7 @@ If you ever need to reset to the original settings, simply re-download the `conf
|
||||
* The program is slow to start!
|
||||
* The first startup of KataGo can be slow, after that it should be much faster.
|
||||
* The program is running too slowly!
|
||||
* Lower the visits count in the `max_visits` block of `config.json` by half or so and try again.
|
||||
* Adjust the number of visits or maximum time allowed in the settings.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -124,11 +124,6 @@
|
||||
"line_weight": 5,
|
||||
"_help_right": "Samples `pick_n + pick_frac * <number of legal moves>` and plays the best one, biased to below the `threshold` line.",
|
||||
"_help_left": "Increase `line_weight` to penalize moves closer to the center more."
|
||||
},
|
||||
"<Pause>": {
|
||||
"_help_right": "--Edward Lasker",
|
||||
"_help_left": "The rules of go are so elegant, organic, and rigorously logical that if intelligent life forms exist elsewhere in the universe, they almost certainly play go."
|
||||
|
||||
}
|
||||
},
|
||||
"board_ui": {
|
||||
|
||||
+10
-6
@@ -19,13 +19,13 @@ class GameNode(SGFNode):
|
||||
self.move_number = 0
|
||||
self.undo_threshold = random.random() # for fractional undos, store the random threshold in the move itself for consistency
|
||||
|
||||
def sgf_properties(self, save_comments_player, save_comments_class, eval_thresholds):
|
||||
def sgf_properties(self, save_comments_player=None, save_comments_class=None, eval_thresholds=None):
|
||||
properties = copy.copy(super().sgf_properties())
|
||||
if self.points_lost:
|
||||
if self.points_lost and save_comments_class is not None and eval_thresholds is not None:
|
||||
show_class = save_comments_class[evaluation_class(self.points_lost, eval_thresholds)]
|
||||
else:
|
||||
show_class = False
|
||||
if save_comments_player.get(self.player, False) and show_class and self.analysis_ready:
|
||||
if (save_comments_player or {}).get(self.player, False) and show_class and self.analysis_ready:
|
||||
candidate_moves = self.candidate_moves
|
||||
top_x = Move.from_gtp(candidate_moves[0]["move"]).sgf(self.board_size)
|
||||
best_sq = [Move.from_gtp(d["move"]).sgf(self.board_size) for d in candidate_moves[1:] if d["pointsLost"] <= 0.5]
|
||||
@@ -39,7 +39,7 @@ class GameNode(SGFNode):
|
||||
if self.is_root:
|
||||
properties["C"] = [
|
||||
"Moves marked 'X' indicate the top move according to KataGo, those with a square are all moves which lose at most 0.5 points.\n"
|
||||
+ properties.get("C", "")
|
||||
+ "\n".join(properties.get("C", ""))
|
||||
+ "\nSGF with review generated by KaTrain."
|
||||
]
|
||||
return properties
|
||||
@@ -97,12 +97,16 @@ class GameNode(SGFNode):
|
||||
previous_top_move = self.parent.candidate_moves[0]
|
||||
if sgf or hints:
|
||||
if previous_top_move["move"] != single_move.gtp():
|
||||
text += f"Predicted top move was {previous_top_move['move']} ({self.format_score(previous_top_move['scoreLead'])}).\n"
|
||||
points_lost = self.points_lost
|
||||
if sgf and points_lost > 0.5:
|
||||
text += f"Estimated point loss: {points_lost:.1f}\n"
|
||||
text += f"Predicted top move was {previous_top_move['move']} ({self.format_score(previous_top_move['scoreLead'])}).\n"
|
||||
else:
|
||||
text += f"Move was predicted best move.\n"
|
||||
if sgf:
|
||||
if previous_top_move.get("pv") and (sgf or hints):
|
||||
text += f"PV: {' '.join(previous_top_move['pv'])}"
|
||||
|
||||
if sgf or hints or teach:
|
||||
policy_ranking = self.parent.policy_ranking
|
||||
policy_ix = [ix + 1 for (p, m), ix in zip(policy_ranking, range(len(policy_ranking))) if m == single_move]
|
||||
@@ -112,7 +116,7 @@ class GameNode(SGFNode):
|
||||
text += f"Top policy move was {policy_ranking[0][1].gtp()} ({policy_ranking[0][0]:.1%}).\n"
|
||||
if self.auto_undo and sgf:
|
||||
text += "Move was automatically undone in teaching mode."
|
||||
if self.ai_thoughts:
|
||||
if self.ai_thoughts and (sgf or hints):
|
||||
text += f"\nAI thought process: {self.ai_thoughts}"
|
||||
else:
|
||||
text = "No analysis available" if sgf else "Analyzing move..."
|
||||
|
||||
@@ -199,7 +199,6 @@ class BadukPanWidget(Widget):
|
||||
evalsize = 0
|
||||
else:
|
||||
evalsize = min(1, max(0, realized_points_lost / points_lost))
|
||||
print(node.single_move, realized_points_lost, points_lost, evalsize)
|
||||
for m in node.move_with_placements:
|
||||
if has_stone.get(m.coords) and not drawn_stone.get(m.coords): # skip captures, last only for
|
||||
move_eval_on = show_dots_for.get(m.player) and (i < show_n_eval or full_eval_on)
|
||||
|
||||
@@ -65,9 +65,15 @@ class Controls(BoxLayout):
|
||||
points_lost = current_node.points_lost
|
||||
self.score_change.label = f"Points lost" if points_lost and points_lost > 0 else f"Points gained"
|
||||
self.score_change.text = f"{move.player}: {abs(points_lost):.1f}" if points_lost else "..."
|
||||
print(current_player_is_ai_playing_human, move, next_player_is_human_or_both_robots)
|
||||
elif not current_player_is_ai_playing_human:
|
||||
print(current_player_is_ai_playing_human, move, next_player_is_human_or_both_robots)
|
||||
self.score_change.label = f"Points lost"
|
||||
self.score_change.text = ""
|
||||
elif current_player_is_ai_playing_human and current_node.parent and current_node.parent.single_move:
|
||||
points_lost = current_node.parent.points_lost
|
||||
self.score_change.label = f"Points lost" if points_lost and points_lost > 0 else f"Points gained"
|
||||
self.score_change.text = f"{current_node.parent.single_move.player}: {abs(points_lost):.1f}" if points_lost else "..."
|
||||
elif both_players_are_robots and current_node.parent and current_node.parent.analysis_ready:
|
||||
self.score.text = current_node.parent.format_score()
|
||||
self.win_rate.text = current_node.parent.format_win_rate()
|
||||
|
||||
@@ -237,7 +237,6 @@ class ConfigTeacherPopup(QuickConfigGui):
|
||||
def __init__(self, katrain, popup, **kwargs):
|
||||
self.settings = katrain.config("trainer")
|
||||
self.sgf_settings = katrain.config("sgf")
|
||||
print(self.sgf_settings, katrain.config("sgf"))
|
||||
self.ui_settings = katrain.config("board_ui")
|
||||
super().__init__(katrain, popup, self.settings, **kwargs)
|
||||
Clock.schedule_once(self._build, 0)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 161 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
+6
-2
@@ -366,6 +366,7 @@
|
||||
scroll_type: ['bars']
|
||||
bar_width: 5
|
||||
bar_color: BUTTON_COLOR
|
||||
label: label
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: root.border_color
|
||||
@@ -378,6 +379,7 @@
|
||||
rectangle: [self.pos[0]+1,self.pos[1]+2,self.width-3,self.height-3]
|
||||
width:1
|
||||
Label:
|
||||
id: label
|
||||
padding: 5, 5
|
||||
font_size: dp(18)
|
||||
color: BLACK
|
||||
@@ -588,10 +590,12 @@
|
||||
border_color: 0.5,0.1,0.1,1
|
||||
opacity: 1 if self.text else 0
|
||||
text: ''
|
||||
size_hint_y: 0.00001 if not self.text else 0.66
|
||||
size_hint_y: None
|
||||
height: 0.00001 if not self.text else min(0.66*self.parent.height,self.label.texture_size[1])
|
||||
ScrollableLabel:
|
||||
id: info
|
||||
size_hint: 1, 1 - status_label.size_hint_y
|
||||
size_hint: 1, None
|
||||
height: self.parent.height - status_label.height - 1
|
||||
BoxLayout:
|
||||
orientation: 'horizontal'
|
||||
size_hint: 1, None
|
||||
|
||||
+7
-13
@@ -91,14 +91,7 @@ class KaTrainGui(BoxLayout):
|
||||
auto_undo = cn.player and "undo" in self.controls.player_mode(cn.player)
|
||||
if auto_undo and cn.analysis_ready and cn.parent and cn.parent.analysis_ready:
|
||||
self.game.analyze_undo(cn, self.config("trainer")) # not via message loop
|
||||
if (
|
||||
cn.analysis_ready
|
||||
and "ai" in self.controls.player_mode(cn.next_player).lower()
|
||||
and "pause" not in self.controls.ai_mode(cn.next_player).lower()
|
||||
and not cn.children
|
||||
and not self.game.ended
|
||||
and not (auto_undo and cn.auto_undo is None)
|
||||
):
|
||||
if cn.analysis_ready and "ai" in self.controls.player_mode(cn.next_player).lower() and not cn.children and not self.game.ended and not (auto_undo and cn.auto_undo is None):
|
||||
self._do_ai_move(cn) # cn mismatch stops this if undo fired. avoid message loop here or fires repeatedly.
|
||||
|
||||
# Handle prisoners and next player display
|
||||
@@ -195,7 +188,7 @@ class KaTrainGui(BoxLayout):
|
||||
except ParseError as e:
|
||||
self.log(f"Failed to load SGF. Parse Error: {e}", OUTPUT_ERROR)
|
||||
return
|
||||
self._do_new_game(move_tree=move_tree, analyze_fast=fileselect_popup.fast.active)
|
||||
self._do_new_game(move_tree=move_tree, analyze_fast=popup_contents.fast.active)
|
||||
|
||||
popup_contents.filesel.on_submit = readfile
|
||||
fileselect_popup.open()
|
||||
@@ -229,10 +222,11 @@ class KaTrainGui(BoxLayout):
|
||||
self.controls.set_status(msg)
|
||||
|
||||
def on_touch_up(self, touch):
|
||||
if touch.button == "scrollup":
|
||||
self("redo")
|
||||
elif touch.button == "scrolldown":
|
||||
self("undo")
|
||||
if self.board_gui.collide_point(*touch.pos) or self.board_controls.collide_point(*touch.pos):
|
||||
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):
|
||||
|
||||
Reference in new issue
Block a user