diff --git a/README.md b/README.md
index 508fb96..d45689b 100644
--- a/README.md
+++ b/README.md
@@ -154,23 +154,21 @@ Keyboard shortcuts are shown with **[key]**.
wanting to guess the next move.
* **[w]**: Show 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 '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]**: Policy moves: 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'.
+ * **[e]**: Top moves: Show the next moves KataGo considered, colored by their expected point loss.
+ Small/faint dots indicate high uncertainty and never show text (lower than your 'fast visits' setting).
+ Hover over any of them to see the principal variation.
+ * **[r]**: Policy moves: 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'. This turns off the 'top moves' setting as the overlap is often not useful.
* **[t]**: Expected territory: Show expected ownership of each intersection.
* The analysis options available under the 'Analysis' button are used for deeper evaluation of the position:
* **[a]**: Deeper analysis: Re-evaluate the position using more visits, usually resulting in a more accurate evaluation.
* **[s]**: Equalize visits: 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]**: Analyze all moves: Evaluate all possible next moves. This can take a bit of time even though 'fast_visits' is used, but can be useful to see how many reasonable next moves are available.
+ * **[f]**: Find alternatives: Increases analysis of current candidate moves to at least the 'fast visits' level, and request a new query that excludes all current candidate moves.
* **[spacebar]**: Turn continuous analysis on/off. This will continuously improve analysis of the current position, similar to Lizzie's 'pondering', but only when there are no other queries going on.
* **[enter]** AI move. Makes the AI move for the current player regardless of current player selection.
-### Rank Estimation
-
-A new feature in v1.3 is the rank estimation panel. This adds an extra graph which uses a statistical model to estimate the playing strength
-of both players for every 80 move segment. It can be used for determining which part of your game needs extra attention,
-but keep in mind the estimation is based purely on how well moves correspond to the 'shape sense' of KataGo and can be very inaccurate at times.
-
## Keyboard and mouse shortcuts
In addition to shortcuts mentioned above and those shown in the main menu:
@@ -197,15 +195,11 @@ In addition to shortcuts mentioned above and those shown in the main menu:
* Try using a lower number for `nnMaxBatchSize` in `KataGo/analysis_config.cfg`, and avoid using versions compiled with large board sizes.
* If still encountering problems, please start KataGo by itself to check for any errors it gives.
* Note that if you don't have a GPU, or your GPU does not support OpenCL, you should use the 'eigen' binaries which run on CPU only.
-* How can I play on larger boards?
- * For windows, change the `katago` setting to `katrain\KataGo\katago-bs52.exe`. For other operating systems, you need to compile your own KataGo version with higher limits.
-
## Support / Contribute
[](https://github.com/sanderland/katrain/issues)
[](CONTRIBUTIONS.md)
-[](https://liberapay.com/sanderbaduk/)
[](https://github.com/sponsors/sanderland)
* Ideas, feedback, and contributions to code or translations are all very welcome.
@@ -213,7 +207,7 @@ In addition to shortcuts mentioned above and those shown in the main menu:
* I am looking for contributors of more translations of both this manual and the program itself. The best way to help with this is to contact me on discord.
* You can contact me on [discord](https://discord.gg/AjTPFpN) (Sander#3278), [KakaoTalk](https://open.kakao.com/o/gTsMJCac)
or [Reddit](http://reddit.com/u/sanderbaduk) to get help, discuss improvements, or simply show your appreciation.
-* You can also donate to the project through [Liberapay](https://liberapay.com/KaTrain/) or [Github Sponsors](https://github.com/sponsors/sanderland).
+* You can also donate to the project through [Github Sponsors](https://github.com/sponsors/sanderland).
diff --git a/katrain/__main__.py b/katrain/__main__.py
index a667637..687e659 100644
--- a/katrain/__main__.py
+++ b/katrain/__main__.py
@@ -440,6 +440,7 @@ class KaTrainGui(Screen, KaTrainBase):
"a": ("analyze-extra", "extra"),
"s": ("analyze-extra", "equalize"),
"d": ("analyze-extra", "sweep"),
+ "f": ("analyze-extra", "alternative"),
"p": ("play", None),
"down": ("switch-branch", 1),
"up": ("switch-branch", -1),
diff --git a/katrain/core/engine.py b/katrain/core/engine.py
index c2a3f9e..542753d 100644
--- a/katrain/core/engine.py
+++ b/katrain/core/engine.py
@@ -77,7 +77,9 @@ class KataGoEngine:
elif 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(f'"{exe}" analysis -model "{model}" -config "{cfg}" -analysis-threads {config["threads"]}')
+ self.command = shlex.split(
+ f'"{exe}" analysis -model "{model}" -config "{cfg}" -analysis-threads {config["threads"]}'
+ )
self.start()
def start(self):
@@ -224,6 +226,7 @@ class KataGoEngine:
visits: int = None,
analyze_fast: bool = False,
time_limit=True,
+ find_alternatives: bool = False,
priority: int = 0,
ownership: Optional[bool] = None,
next_move: Optional[GameNode] = None,
@@ -241,6 +244,17 @@ class KataGoEngine:
if analyze_fast and self.config.get("fast_visits"):
visits = self.config["fast_visits"]
+ if find_alternatives:
+ avoid = [
+ {
+ "moves": list(analysis_node.analysis["moves"].keys()),
+ "player": analysis_node.next_player,
+ "untilDepth": 1,
+ }
+ ]
+ else:
+ avoid = []
+
size_x, size_y = analysis_node.board_size
settings = copy.copy(self.override_settings)
if time_limit:
@@ -254,6 +268,7 @@ class KataGoEngine:
"analyzeTurns": [len(moves)],
"maxVisits": visits,
"komi": analysis_node.komi,
+ "avoidMoves": avoid,
"boardXSize": size_x,
"boardYSize": size_y,
"includeOwnership": ownership and not next_move,
diff --git a/katrain/core/game.py b/katrain/core/game.py
index 359842e..7af8ad3 100644
--- a/katrain/core/game.py
+++ b/katrain/core/game.py
@@ -369,21 +369,27 @@ class Game:
visits = engine.config["fast_visits"]
self.katrain.controls.set_status(i18n._("sweep analysis").format(visits=visits), STATUS_ANALYSIS)
priority = -1_000_000_000
- elif mode == "equalize":
+ elif mode in ["equalize", "alternative"]:
if not cn.analysis_ready:
self.katrain.controls.set_status(i18n._("wait-before-equalize"), STATUS_INFO, self.current_node)
return
analyze_moves = [Move.from_gtp(gtp, player=cn.next_player) for gtp, _ in cn.analysis["moves"].items()]
- visits = max(d["visits"] for d in cn.analysis["moves"].values())
- self.katrain.controls.set_status(i18n._("equalizing analysis").format(visits=visits), STATUS_ANALYSIS)
priority = -1_000
+ if mode == "alternative": # also do a quick update on current candidates so it doesn't look too weird
+ self.katrain.controls.set_status("Searching for alternatives", STATUS_ANALYSIS)
+ cn.analyze(engine, priority=-500, time_limit=False, find_alternatives=True)
+ visits = engine.config["fast_visits"]
+ else:
+ visits = max(d["visits"] for d in cn.analysis["moves"].values())
+ self.katrain.controls.set_status(i18n._("equalizing analysis").format(visits=visits), STATUS_ANALYSIS)
else:
raise ValueError("Invalid analysis mode")
for move in analyze_moves:
- cn.analyze(
- engine, priority, visits=visits, refine_move=move, time_limit=False
- ) # explicitly requested so take as long as you need
+ if cn.analysis["moves"].get(move.gtp(), {"visits": 0})["visits"] < visits:
+ cn.analyze(
+ engine, priority, visits=visits, refine_move=move, time_limit=False
+ ) # explicitly requested so take as long as you need
def analyze_undo(self, node):
train_config = self.katrain.config("trainer")
diff --git a/katrain/core/game_node.py b/katrain/core/game_node.py
index 502c8bb..a9920c6 100644
--- a/katrain/core/game_node.py
+++ b/katrain/core/game_node.py
@@ -68,15 +68,25 @@ class GameNode(SGFNode):
) # analyzed/not undone main, non-teach second, undone last
# various analysis functions
- def analyze(self, engine, priority=0, visits=None, time_limit=True, refine_move=None, analyze_fast=False):
+ def analyze(
+ self,
+ engine,
+ priority=0,
+ visits=None,
+ time_limit=True,
+ refine_move=None,
+ analyze_fast=False,
+ find_alternatives=False,
+ ):
engine.request_analysis(
self,
- lambda result: self.set_analysis(result, refine_move),
+ lambda result: self.set_analysis(result, refine_move, find_alternatives),
priority=priority,
visits=visits,
analyze_fast=analyze_fast,
time_limit=time_limit,
next_move=refine_move,
+ find_alternatives=find_alternatives,
)
def update_move_analysis(self, move_analysis, move_gtp):
@@ -90,18 +100,22 @@ class GameNode(SGFNode):
elif cur["visits"] < move_analysis["visits"]:
cur.update(move_analysis)
- def set_analysis(self, analysis_json, refine_move):
+ def set_analysis(self, analysis_json, refine_move, alternatives_mode):
if refine_move:
pvtail = analysis_json["moveInfos"][0]["pv"] if analysis_json["moveInfos"] else []
self.update_move_analysis(
{"pv": [refine_move.gtp()] + pvtail, **analysis_json["rootInfo"]}, refine_move.gtp()
)
else:
+ if alternatives_mode:
+ for m in analysis_json["moveInfos"]:
+ m["order"] += 10 # offset for not making this top
for move_analysis in analysis_json["moveInfos"]:
self.update_move_analysis(move_analysis, move_analysis["move"])
self.ownership = analysis_json.get("ownership")
self.policy = analysis_json.get("policy")
- self.analysis["root"] = analysis_json["rootInfo"]
+ if not alternatives_mode:
+ self.analysis["root"] = analysis_json["rootInfo"]
if self.parent and self.move:
analysis_json["rootInfo"]["pv"] = [self.move.gtp()] + (
analysis_json["moveInfos"][0]["pv"] if analysis_json["moveInfos"] else []
diff --git a/katrain/gui.kv b/katrain/gui.kv
index fef7bf0..07219f9 100644
--- a/katrain/gui.kv
+++ b/katrain/gui.kv
@@ -744,6 +744,11 @@
icon: 'img/Sweep.png'
shortcut: 'd'
on_action: root.katrain("analyze-extra", "sweep")
+ AnalysisMenuItem:
+ text: i18n._("analysis:alternatives")
+ icon: 'img/Alternative.png'
+ shortcut: 'f'
+ on_action: root.katrain("analyze-extra", "alternative")
AnalysisMenuItem:
text: i18n._("analysis:game")
icon: 'img/analysis.png'
diff --git a/katrain/gui/badukpan.py b/katrain/gui/badukpan.py
index 488d354..90f849e 100644
--- a/katrain/gui/badukpan.py
+++ b/katrain/gui/badukpan.py
@@ -361,7 +361,7 @@ class BadukPanWidget(Widget):
polsize = 1.1 * math.sqrt(policy_grid[y][x])
policy_circle_color = (
*POLICY_COLOR,
- POLICY_ALPHA + TOP_MOVE_ALPHA * (policy_grid[y][x] == best_move_policy),
+ POLICY_ALPHA + TOP_POLICY_ALPHA * (policy_grid[y][x] == best_move_policy),
)
draw_circle(
(self.gridpos_x[x], self.gridpos_y[y]), polsize * self.stone_size, policy_circle_color
@@ -409,27 +409,37 @@ class BadukPanWidget(Widget):
self.active_pv_moves = []
# hints or PV
- if katrain.analysis_controls.hints.active and not katrain.analysis_controls.policy.active and not game_ended:
+ if (
+ katrain.analysis_controls.hints.active
+ and not katrain.analysis_controls.policy.active
+ and not game_ended
+ ):
hint_moves = current_node.candidate_moves
for i, move_dict in enumerate(hint_moves):
move = Move.from_gtp(move_dict["move"])
if move.coords is not None:
- scale = 0.95
- if move_dict["visits"] < VISITS_FRAC_SMALL * hint_moves[0]["visits"]:
- scale = 0.75
+ scale = HINT_SCALE
+ text_on = True
+ alpha = HINTS_ALPHA
+ if move_dict["visits"] < katrain.config("engine/fast_visits"):
+ scale = UNCERTAIN_HINT_SCALE
+ text_on = False
+ alpha = HINTS_MIN_ALPHA + (HINTS_ALPHA - HINTS_MIN_ALPHA) * (
+ move_dict["visits"] / katrain.config("engine/fast_visits")
+ )
if "pv" in move_dict:
self.active_pv_moves.append((move.coords, move_dict["pv"], current_node))
else:
katrain.log(f"PV missing for move_dict {move_dict}", OUTPUT_DEBUG)
evalsize = self.stone_size * scale
evalcol = self.eval_color(move_dict["pointsLost"])
- Color(*evalcol[:3], HINTS_ALPHA)
+ Color(*evalcol[:3], alpha)
Rectangle(
pos=(self.gridpos_x[move.coords[0]] - evalsize, self.gridpos_y[move.coords[1]] - evalsize),
size=(2 * evalsize, 2 * evalsize),
source="katrain/img/topmove.png",
)
- if self.trainer_config["text_point_loss"]:
+ if self.trainer_config["text_point_loss"] and text_on:
if move_dict["pointsLost"] < 0.05:
ptloss_text = "0.0"
else:
diff --git a/katrain/gui/kivyutils.py b/katrain/gui/kivyutils.py
index 40a3e43..a2b953b 100644
--- a/katrain/gui/kivyutils.py
+++ b/katrain/gui/kivyutils.py
@@ -373,6 +373,7 @@ class AnalysisToggle(MDBoxLayout):
default_active = BooleanProperty(False)
font_name = StringProperty(DEFAULT_FONT)
disabled = BooleanProperty(False)
+
def trigger_action(self, *args, **kwargs):
return self.checkbox.trigger_action(*args, **kwargs)
diff --git a/katrain/gui/style.py b/katrain/gui/style.py
index de037f4..6125534 100644
--- a/katrain/gui/style.py
+++ b/katrain/gui/style.py
@@ -59,13 +59,16 @@ POLICY_COLOR = [0.9, 0.2, 0.8]
STARPOINT_SIZE = 0.1
BOARD_COLOR = [0.85, 0.68, 0.40, 1]
STONE_SIZE = 0.505 # texture edge is transparent
-VISITS_FRAC_SMALL = 0.1
-POLICY_ALPHA = 0.5
-HINTS_ALPHA = 0.75
-TOP_MOVE_ALPHA = 0.4
+HINTS_ALPHA = 0.8
+HINTS_MIN_ALPHA = 0.1 # single visit
+POLICY_ALPHA = 0.6
+TOP_POLICY_ALPHA = 0.3
TOP_MOVE_BORDER_COLOR = [10 / 255, 200 / 255, 250 / 255, 1.0]
CHILD_SCALE = 0.95
+HINT_SCALE = 0.975
+UNCERTAIN_HINT_SCALE = 0.7
+
# ponder light
ENGINE_DOWN_COL = EVAL_COLORS["theme:normal"][1]
diff --git a/katrain/i18n/locales/cn/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/cn/LC_MESSAGES/katrain.mo
index 71c09be..db2b7ee 100644
Binary files a/katrain/i18n/locales/cn/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/cn/LC_MESSAGES/katrain.mo differ
diff --git a/katrain/i18n/locales/cn/LC_MESSAGES/katrain.po b/katrain/i18n/locales/cn/LC_MESSAGES/katrain.po
index 959abdf..8a41685 100644
--- a/katrain/i18n/locales/cn/LC_MESSAGES/katrain.po
+++ b/katrain/i18n/locales/cn/LC_MESSAGES/katrain.po
@@ -521,6 +521,7 @@ msgstr "读秒声音开启"
msgid "tab:info-details"
msgstr "详细"
+#. TODO: updated
msgid "teaching undo message"
msgstr "撤销第 {move} 手棋因为损失超过 {points_lost:.1f} 目. 悬停在该手棋 上方查看信息. 请再次尝试."
@@ -631,3 +632,7 @@ msgstr "启动katago失败因缺失dll文件. 请检查运行要求, 或尝试
#. TODO - resign the game
msgid "resign"
msgstr "Resign"
+
+#. TODO - Analyze again but exclude all currently considered moves
+msgid "analysis:alternatives"
+msgstr "Find alternative moves"
diff --git a/katrain/i18n/locales/de/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/de/LC_MESSAGES/katrain.mo
index 86b01ac..d876957 100644
Binary files a/katrain/i18n/locales/de/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/de/LC_MESSAGES/katrain.mo differ
diff --git a/katrain/i18n/locales/de/LC_MESSAGES/katrain.po b/katrain/i18n/locales/de/LC_MESSAGES/katrain.po
index fb18d0b..694e89b 100644
--- a/katrain/i18n/locales/de/LC_MESSAGES/katrain.po
+++ b/katrain/i18n/locales/de/LC_MESSAGES/katrain.po
@@ -105,12 +105,6 @@ msgstr "AI Zug erzwingen"
msgid "pass-button-text"
msgstr "Passen"
-msgid "teaching undo message"
-msgstr ""
-"Zug {move} zurückgenommen, da er {points_lost:.1f} Punkte verliert. Bewege "
-"Mauszeiger über den Zug,um die erwartete Widerlegung zu sehen. Bitte "
-"versuche es erneut."
-
msgid "ai-ponder"
msgstr ""
"Zeigt den Enginestatus an von grün (Leerlauf) bis orange(Zugberechnung). Ist"
@@ -680,3 +674,14 @@ msgstr "Zeig den Punkteverlust auf Beste Züge"
#. TODO - resign the game
msgid "resign"
msgstr "Resign"
+
+#. TODO - Analyze again but exclude all currently considered moves
+msgid "analysis:alternatives"
+msgstr "Find alternative moves"
+
+#. TODO - updated
+msgid "teaching undo message"
+msgstr ""
+"Zug {move} zurückgenommen, da er {points_lost:.1f} Punkte verliert. Bewege "
+"Mauszeiger über den Zug,um die erwartete Widerlegung zu sehen. Bitte "
+"versuche es erneut."
diff --git a/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo
index 1a85770..394e520 100644
Binary files a/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo differ
diff --git a/katrain/i18n/locales/en/LC_MESSAGES/katrain.po b/katrain/i18n/locales/en/LC_MESSAGES/katrain.po
index 31ea61c..d7d696e 100644
--- a/katrain/i18n/locales/en/LC_MESSAGES/katrain.po
+++ b/katrain/i18n/locales/en/LC_MESSAGES/katrain.po
@@ -102,6 +102,10 @@ msgstr "Fast analysis of all moves"
msgid "analysis:game"
msgstr "Deeper full game analysis"
+#. Analyze again but exclude all currently considered moves
+msgid "analysis:alternatives"
+msgstr "Find alternative moves"
+
#. Keep improving current position analysis when there is nothing else to do
msgid "analysis:continuous"
msgstr "Toggle continuous analysis"
@@ -133,10 +137,12 @@ msgstr "Pass"
msgid "resign"
msgstr "Resign"
+#. Shown when a move is undone in teaching mode.
msgid "teaching undo message"
msgstr ""
-"Undid move {move} as it lost {points_lost:.1f} points. Hover over the move "
-"to see expected refutation. Please try again."
+"Undid move {move} as it lost {points_lost:.1f} points. Please try again. For"
+" a hint, hover over the move to see expected refutation, or check expected "
+"territory (t) to see the areas of the board where point loss is expected."
msgid "ai-ponder"
msgstr ""
diff --git a/katrain/i18n/locales/es/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/es/LC_MESSAGES/katrain.mo
index f560565..76a0044 100644
Binary files a/katrain/i18n/locales/es/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/es/LC_MESSAGES/katrain.mo differ
diff --git a/katrain/i18n/locales/es/LC_MESSAGES/katrain.po b/katrain/i18n/locales/es/LC_MESSAGES/katrain.po
index e33a4dc..3eeb01f 100644
--- a/katrain/i18n/locales/es/LC_MESSAGES/katrain.po
+++ b/katrain/i18n/locales/es/LC_MESSAGES/katrain.po
@@ -105,6 +105,7 @@ msgstr "Forzar jugada IA"
msgid "pass-button-text"
msgstr "Pasar"
+#. TODO: updated
msgid "teaching undo message"
msgstr ""
"Deshice jugada {move} ya que perdía {points_lost:.1f} puntos. Coloca el "
@@ -688,3 +689,7 @@ msgstr "Show point loss on top moves"
#. TODO - resign the game
msgid "resign"
msgstr "Resign"
+
+#. TODO - Analyze again but exclude all currently considered moves
+msgid "analysis:alternatives"
+msgstr "Find alternative moves"
diff --git a/katrain/i18n/locales/fr/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/fr/LC_MESSAGES/katrain.mo
index b83f07b..e5a4e40 100644
Binary files a/katrain/i18n/locales/fr/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/fr/LC_MESSAGES/katrain.mo differ
diff --git a/katrain/i18n/locales/fr/LC_MESSAGES/katrain.po b/katrain/i18n/locales/fr/LC_MESSAGES/katrain.po
index b2aaf45..4ede1f0 100644
--- a/katrain/i18n/locales/fr/LC_MESSAGES/katrain.po
+++ b/katrain/i18n/locales/fr/LC_MESSAGES/katrain.po
@@ -522,6 +522,7 @@ msgstr ""
"possiblement pour cause de \n"
"mémoire insuffisante : {error}"
+#. TODO: updated
msgid "teaching undo message"
msgstr ""
"Le coup {move} a été annulé car il perdait {points_lost:.1f} points. Vous "
@@ -715,3 +716,7 @@ msgstr "Indiquer les points perdus"
#. TODO - resign the game
msgid "resign"
msgstr "Resign"
+
+#. TODO - Analyze again but exclude all currently considered moves
+msgid "analysis:alternatives"
+msgstr "Find alternative moves"
diff --git a/katrain/i18n/locales/jp/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/jp/LC_MESSAGES/katrain.mo
index 3d44107..1617864 100644
Binary files a/katrain/i18n/locales/jp/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/jp/LC_MESSAGES/katrain.mo differ
diff --git a/katrain/i18n/locales/jp/LC_MESSAGES/katrain.po b/katrain/i18n/locales/jp/LC_MESSAGES/katrain.po
index 62bf609..8e9e46d 100644
--- a/katrain/i18n/locales/jp/LC_MESSAGES/katrain.po
+++ b/katrain/i18n/locales/jp/LC_MESSAGES/katrain.po
@@ -129,10 +129,6 @@ msgstr "一局全体を再分析中(各{visits}手)"
msgid "pass-button-text"
msgstr "パス"
-msgid "teaching undo message"
-msgstr ""
-"待った。{move}は{points_lost:.1f}目損です(マウスカーソルをこの手にかざせばとがめる手順が表示されます)。打ち直してください。"
-
msgid "ai-ponder"
msgstr "この印はエンジンの状態をシンプルに表します(緑=空き、オレンジ=処理中)。エンジンが止まったり起動できなかったりすると赤くなります。"
@@ -670,3 +666,12 @@ msgstr "損失目数を候補手上に表示"
#. - resign the game
msgid "resign"
msgstr "投了"
+
+#. TODO - Analyze again but exclude all currently considered moves
+msgid "analysis:alternatives"
+msgstr "Find alternative moves"
+
+#. TODO - updated
+msgid "teaching undo message"
+msgstr ""
+"待った。{move}は{points_lost:.1f}目損です(マウスカーソルをこの手にかざせばとがめる手順が表示されます)。打ち直してください。"
diff --git a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo
index b637b58..4bbfd00 100644
Binary files a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo differ
diff --git a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po
index a127532..fa63bf0 100644
--- a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po
+++ b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po
@@ -546,6 +546,7 @@ msgstr ""
"`threshold`선 밑의 수를 더 많이 뽑은 후 그 중 최선의 수를 둡니다. `line_weight`을 증가시키면 바둑판 중앙에 있는"
" 수를 더욱 꺼리게 됩니다. 판이 `endgame` 비율만큼 차면 평범하게 둡니다."
+#. TODO: updated
msgid "teaching undo message"
msgstr ""
"{points_lost:.1f}집 손해이므로 {move}을/를 물렀습니다. 커서를 올려 상대의 대응책을 보십시오. 다시 두어 주세요."
@@ -648,3 +649,7 @@ msgstr "돌 위에 집 손해 표시하기"
#. TODO - resign the game
msgid "resign"
msgstr "기권함"
+
+#. TODO - Analyze again but exclude all currently considered moves
+msgid "analysis:alternatives"
+msgstr "Find alternative moves"
diff --git a/katrain/i18n/locales/ru/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/ru/LC_MESSAGES/katrain.mo
index 9133fed..6c4daa3 100644
Binary files a/katrain/i18n/locales/ru/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/ru/LC_MESSAGES/katrain.mo differ
diff --git a/katrain/i18n/locales/ru/LC_MESSAGES/katrain.po b/katrain/i18n/locales/ru/LC_MESSAGES/katrain.po
index 82a6d39..112da9f 100644
--- a/katrain/i18n/locales/ru/LC_MESSAGES/katrain.po
+++ b/katrain/i18n/locales/ru/LC_MESSAGES/katrain.po
@@ -117,6 +117,7 @@ msgstr "Дополнительный анализ всех ходов-канди
msgid "pass-button-text"
msgstr "Пас"
+#. TODO - updated
msgid "teaching undo message"
msgstr ""
"Ход {move} отменён, т.к. это потеря {points_lost:.1f} очков. Наведите на "
@@ -676,3 +677,7 @@ msgstr "Показывать потерянные очки текстом"
#. TODO - resign the game
msgid "resign"
msgstr "Resign"
+
+#. TODO - Analyze again but exclude all currently considered moves
+msgid "analysis:alternatives"
+msgstr "Find alternative moves"
diff --git a/katrain/img/Alternative.png b/katrain/img/Alternative.png
new file mode 100644
index 0000000..5f23ca2
Binary files /dev/null and b/katrain/img/Alternative.png differ