fewer hints, alternatives search
This commit is contained in:
1 parent
2758e936c8
commit
4e2ee03db7
26 files changed
+132
-42
No files matched your search
@@ -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.
|
||||
|
||||
## <a name="keyboard"></a> 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.
|
||||
|
||||
|
||||
## <a name="support"></a> 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).
|
||||
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
+16
-1
@@ -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,
|
||||
|
||||
@@ -369,18 +369,24 @@ 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()]
|
||||
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)
|
||||
priority = -1_000
|
||||
else:
|
||||
raise ValueError("Invalid analysis mode")
|
||||
for move in analyze_moves:
|
||||
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
|
||||
|
||||
@@ -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,17 +100,21 @@ 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")
|
||||
if not alternatives_mode:
|
||||
self.analysis["root"] = analysis_json["rootInfo"]
|
||||
if self.parent and self.move:
|
||||
analysis_json["rootInfo"]["pv"] = [self.move.gtp()] + (
|
||||
|
||||
@@ -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'
|
||||
|
||||
+17
-7
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
Binary file not shown.
@@ -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"
|
||||
Binary file not shown.
@@ -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."
|
||||
Binary file not shown.
@@ -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 ""
|
||||
|
||||
Binary file not shown.
@@ -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"
|
||||
Binary file not shown.
@@ -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"
|
||||
Binary file not shown.
@@ -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}目損です(マウスカーソルをこの手にかざせばとがめる手順が表示されます)。打ち直してください。"
|
||||
Binary file not shown.
@@ -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"
|
||||
Binary file not shown.
@@ -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"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
Reference in new issue
Block a user