play to end/collapse
This commit is contained in:
1 parent
a02697c783
commit
b958975628
23 files changed
+281
-28
No files matched your search
@@ -7,6 +7,7 @@ For on related licenses for these binaries and libraries see https://github.com/
|
||||
- Equalize icon and Thrash Icon: derived from work by bqlqn from www.flaticon.com
|
||||
- Local search icon, Insert icon: derived from work by Pixel Perfect from www.flaticon.com
|
||||
- Other Menu icons and Flag icons: derived from work by Freepik from www.flaticon.com
|
||||
- Collapse branch icon: derived from work by Kirill Kazachek from www.flaticon.com
|
||||
|
||||
3. The True Type Font DIGITAL-7 version 1.02 by Sizenko Alexander, which is free for non-commercial use.
|
||||
|
||||
|
||||
+10
-1
@@ -324,6 +324,9 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
def _do_analyze_extra(self, mode, **kwargs):
|
||||
self.game.analyze_extra(mode, **kwargs)
|
||||
|
||||
def _do_play_to_end(self):
|
||||
self.game.play_to_end()
|
||||
|
||||
def _do_select_box(self):
|
||||
self.controls.set_status(i18n._("analysis:region:start"), STATUS_INFO)
|
||||
self.board_gui.selecting_region_of_interest = True
|
||||
@@ -498,6 +501,7 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
"g": ("select-box",),
|
||||
"i": ("insert-mode",),
|
||||
"p": ("play", None),
|
||||
"l": ("play-to-end",),
|
||||
"n": ("next-mistake",),
|
||||
"b": ("undo", "branch"),
|
||||
"down": ("switch-branch", 1),
|
||||
@@ -517,7 +521,6 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
def _on_keyboard_down(self, _keyboard, keycode, _text, modifiers):
|
||||
if self.controls.note.focus:
|
||||
return # when making notes, don't allow keyboard shortcuts
|
||||
|
||||
popup = self.popup_open
|
||||
if popup:
|
||||
if keycode[1] in ["f5", "f6", "f7", "f8"]: # switch between popups
|
||||
@@ -552,6 +555,12 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
self("undo", 999)
|
||||
elif keycode[1] == "end":
|
||||
self("redo", 999)
|
||||
elif keycode[1] == "pageup":
|
||||
self.controls.move_tree.make_selected_node_main_branch()
|
||||
elif keycode[1] == "delete" and ctrl_pressed:
|
||||
self.controls.move_tree.delete_selected_node()
|
||||
elif keycode[1] == "c" and not ctrl_pressed:
|
||||
self.controls.move_tree.toggle_selected_node_collapse()
|
||||
elif keycode[1] == "n" and ctrl_pressed:
|
||||
self("new-game-popup")
|
||||
elif keycode[1] == "l" and ctrl_pressed:
|
||||
|
||||
+49
-2
@@ -93,6 +93,14 @@ class Game:
|
||||
|
||||
self.set_current_node(self.root)
|
||||
self.main_time_used = 0
|
||||
|
||||
# restore shortcuts
|
||||
shortcut_id_to_node = {node.get_property("KTSID", None): node for node in self.root.nodes_in_tree}
|
||||
for node in self.root.nodes_in_tree:
|
||||
shortcut_id = node.get_property("KTSF", None)
|
||||
if shortcut_id and shortcut_id in shortcut_id_to_node:
|
||||
shortcut_id_to_node[shortcut_id].add_shortcut(node)
|
||||
|
||||
threading.Thread(
|
||||
target=lambda: self.analyze_all_nodes(-1_000_000, analyze_fast=analyze_fast, even_if_present=False),
|
||||
daemon=True,
|
||||
@@ -264,7 +272,9 @@ class Game:
|
||||
n_times = 9999
|
||||
break_on_branch = True
|
||||
for _ in range(n_times):
|
||||
if not cn.is_root:
|
||||
if cn.shortcut_from:
|
||||
cn = cn.shortcut_from
|
||||
elif not cn.is_root:
|
||||
cn = cn.parent
|
||||
if break_on_branch and len(cn.children) > 1:
|
||||
break
|
||||
@@ -276,7 +286,11 @@ class Game:
|
||||
cn = self.current_node # avoid race conditions
|
||||
for move in range(n_times):
|
||||
if cn.children:
|
||||
cn = cn.ordered_children[0]
|
||||
child = cn.ordered_children[0]
|
||||
shortcut_to = [m for m, v in cn.shortcuts_to if child == v] # are we about to go to a shortcut node?
|
||||
if shortcut_to:
|
||||
child = shortcut_to[0]
|
||||
cn = child
|
||||
if (
|
||||
move > 0
|
||||
and stop_on_mistake is not None
|
||||
@@ -512,6 +526,39 @@ class Game:
|
||||
engine, priority=priority, visits=visits, refine_move=move, time_limit=False, report_every=None
|
||||
) # explicitly requested so take as long as you need
|
||||
|
||||
def play_to_end(self):
|
||||
cn = self.current_node
|
||||
count = 0
|
||||
if not cn.analysis_exists:
|
||||
return
|
||||
|
||||
def analyze_and_play_policy(node):
|
||||
nonlocal count, cn
|
||||
cand = node.candidate_moves
|
||||
if cand:
|
||||
move = Move.from_gtp(cand[0]["move"], player=node.next_player)
|
||||
else:
|
||||
polmoves = node.policy_ranking
|
||||
move = polmoves[0][1] if polmoves else Move(None)
|
||||
if move.is_pass:
|
||||
cn.add_shortcut(node)
|
||||
self.set_current_node(node)
|
||||
self.katrain.controls.set_status("", STATUS_INFO)
|
||||
return
|
||||
count += 1
|
||||
new_node = GameNode(parent=node, move=move)
|
||||
self.katrain.controls.set_status(i18n._("playtoend:status").format(num_moves=count), STATUS_INFO)
|
||||
|
||||
def set_analysis(result, _partial):
|
||||
new_node.set_analysis(result)
|
||||
analyze_and_play_policy(new_node)
|
||||
|
||||
self.engines[node.next_player].request_analysis(
|
||||
new_node, callback=set_analysis, visits=1,
|
||||
)
|
||||
|
||||
threading.Thread(target=analyze_and_play_policy, args=(cn,), daemon=True).start()
|
||||
|
||||
def analyze_undo(self, node):
|
||||
train_config = self.katrain.config("trainer")
|
||||
move = node.move
|
||||
|
||||
@@ -57,9 +57,26 @@ class GameNode(SGFNode):
|
||||
self.time_used = 0
|
||||
self.undo_threshold = random.random() # for fractional undos
|
||||
self.end_state = None
|
||||
self.shortcuts_to = []
|
||||
self.shortcut_from = None
|
||||
self.analysis_loaded = False
|
||||
self.clear_analysis()
|
||||
|
||||
def add_shortcut(self, to_node): # collapses the branch between them
|
||||
nodes = [to_node]
|
||||
while nodes[-1].parent and nodes[-1] != self: # ensure on path
|
||||
nodes.append(nodes[-1].parent)
|
||||
if nodes[-1] == self and len(nodes) > 2:
|
||||
via = nodes[-2]
|
||||
self.shortcuts_to.append((to_node, via)) # and first child
|
||||
to_node.shortcut_from = self
|
||||
|
||||
def remove_shortcut(self):
|
||||
from_node = self.shortcut_from
|
||||
if from_node:
|
||||
from_node.shortcuts_to = [(m, v) for m, v in from_node.shortcuts_to if m != self]
|
||||
self.shortcut_from = None
|
||||
|
||||
def add_list_property(self, property: str, values: List):
|
||||
if property == "KT":
|
||||
try:
|
||||
@@ -121,6 +138,14 @@ class GameNode(SGFNode):
|
||||
*comments,
|
||||
f"\nSGF with generated by {PROGRAM_NAME} {VERSION}{SGF_INTERNAL_COMMENTS_MARKER}.\n",
|
||||
]
|
||||
if self.shortcut_from:
|
||||
properties["KTSF"] = [id(self.shortcut_from)]
|
||||
elif "KTSF" in properties:
|
||||
del properties["KTSF"]
|
||||
if self.shortcuts_to:
|
||||
properties["KTSID"] = [id(self)]
|
||||
elif "KTSID" in properties:
|
||||
del properties["KTSID"]
|
||||
if note:
|
||||
comments.append(f"{self.note}")
|
||||
if comments:
|
||||
@@ -178,7 +203,11 @@ class GameNode(SGFNode):
|
||||
cur.update(move_analysis)
|
||||
|
||||
def set_analysis(
|
||||
self, analysis_json: Dict, refine_move: Optional[Move], additional_moves: bool, partial_result: bool = False
|
||||
self,
|
||||
analysis_json: Dict,
|
||||
refine_move: Optional[Move] = None,
|
||||
additional_moves: bool = False,
|
||||
partial_result: bool = False,
|
||||
):
|
||||
if refine_move:
|
||||
pvtail = analysis_json["moveInfos"][0]["pv"] if analysis_json["moveInfos"] else []
|
||||
|
||||
+9
-3
@@ -650,9 +650,10 @@
|
||||
<MenuItem>
|
||||
font_size: self.height * 0.5
|
||||
size_hint_y: None
|
||||
height: 50
|
||||
height: dp(50)
|
||||
background_color: BOX_BACKGROUND_COLOR
|
||||
content_width: icon.width + label.texture_size[0] * 1.33
|
||||
width_margin: 1.33
|
||||
content_width: icon.width + label.texture_size[0] * self.width_margin
|
||||
MDFloatLayout:
|
||||
id: icon
|
||||
size_hint: None, 1
|
||||
@@ -716,7 +717,7 @@
|
||||
<AnalysisMenuItem@MenuItem>:
|
||||
on_close: app.gui.analysis_controls.close_dropdown()
|
||||
background_color: LIGHTER_BACKGROUND_COLOR
|
||||
height: 45
|
||||
height: dp(45)
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: WHITE
|
||||
@@ -777,6 +778,11 @@
|
||||
icon: 'img/ai.png'
|
||||
shortcut: 'enter'
|
||||
on_action: root.katrain("ai-move")
|
||||
AnalysisMenuItem:
|
||||
text: i18n._("analysis:playtoend")
|
||||
icon: 'img/ai.png'
|
||||
shortcut: 'l'
|
||||
on_action: root.katrain("play-to-end")
|
||||
|
||||
|
||||
<AnalysisControls>:
|
||||
|
||||
@@ -25,6 +25,7 @@ from katrain.gui.style import (
|
||||
STONE_TEXT_COLORS,
|
||||
WHITE,
|
||||
YELLOW,
|
||||
GREY,
|
||||
)
|
||||
|
||||
|
||||
@@ -84,23 +85,43 @@ class MoveTreeCanvas(Widget):
|
||||
self.menu_selected_node = selected_node
|
||||
|
||||
def delete_selected_node(self):
|
||||
if self.menu_selected_node and self.menu_selected_node.parent:
|
||||
parent = self.menu_selected_node.parent
|
||||
parent.children.remove(self.menu_selected_node)
|
||||
selected_node = self.menu_selected_node or self.scroll_view_widget.current_node
|
||||
if selected_node and selected_node.parent:
|
||||
if selected_node.shortcut_from:
|
||||
parent = selected_node.shortcut_from
|
||||
via = [v for m, v in parent.shortcuts_to if m == selected_node]
|
||||
selected_node.remove_shortcut()
|
||||
if via: # should always be
|
||||
parent.children.remove(via[0])
|
||||
else:
|
||||
parent = selected_node.parent
|
||||
parent.children.remove(selected_node)
|
||||
self.set_game_node(parent)
|
||||
self.is_open = False
|
||||
|
||||
def make_selected_node_main_branch(self):
|
||||
|
||||
if self.menu_selected_node and self.menu_selected_node.parent:
|
||||
node = self.menu_selected_node
|
||||
selected_node = self.menu_selected_node or self.scroll_view_widget.current_node
|
||||
if selected_node and selected_node.parent:
|
||||
node = selected_node
|
||||
while node.parent is not None:
|
||||
node.parent.children.remove(node)
|
||||
node.parent.children.insert(0, node)
|
||||
node = node.parent
|
||||
self.set_game_node(self.menu_selected_node)
|
||||
self.set_game_node(selected_node)
|
||||
self.is_open = False
|
||||
|
||||
def toggle_selected_node_collapse(self):
|
||||
selected_node = self.menu_selected_node or self.scroll_view_widget.current_node
|
||||
if selected_node and selected_node.parent:
|
||||
if selected_node.shortcut_from:
|
||||
selected_node.remove_shortcut()
|
||||
else: # find branching point
|
||||
node = selected_node.parent
|
||||
while len(node.children) == 1 and not node.is_root and not node.shortcut_from:
|
||||
node = node.parent
|
||||
node.add_shortcut(selected_node)
|
||||
self.scroll_view_widget.redraw_tree_trigger()
|
||||
|
||||
def switch_branch(self, direction=1):
|
||||
pos = self.move_pos.get(self.scroll_view_widget.current_node)
|
||||
if not self.scroll_view_widget:
|
||||
@@ -120,24 +141,36 @@ class MoveTreeCanvas(Widget):
|
||||
|
||||
root = current_node.root
|
||||
|
||||
def children_with_shortcuts(move):
|
||||
shortcuts = move.shortcuts_to
|
||||
via = [v for m, v in shortcuts] # children that are shortcut
|
||||
return [m for m in move.ordered_children if m not in via] + [m for m, v in shortcuts]
|
||||
|
||||
self.move_pos = {root: (0, 0)}
|
||||
stack = root.ordered_children[::-1]
|
||||
stack = children_with_shortcuts(root)[::-1]
|
||||
next_y_pos = defaultdict(int) # x pos -> max y pos
|
||||
children = defaultdict(list) # since AI self-play etc may modify the tree between layout and draw!
|
||||
children[root] = root.ordered_children
|
||||
children[root] = [*stack]
|
||||
while stack:
|
||||
move = stack.pop()
|
||||
x = move.depth
|
||||
y = max(next_y_pos[x], self.move_pos[move.parent][1])
|
||||
if move.shortcut_from:
|
||||
parent = move.shortcut_from
|
||||
else:
|
||||
parent = move.parent
|
||||
|
||||
if parent:
|
||||
x = self.move_pos[parent][0] + 1
|
||||
else:
|
||||
x = 0
|
||||
y = max(next_y_pos[x], self.move_pos[parent][1])
|
||||
next_y_pos[x] = y + 1
|
||||
next_y_pos[x - 1] = max(next_y_pos[x], next_y_pos[x - 1])
|
||||
self.move_pos[move] = (x, y)
|
||||
children[move] = move.ordered_children
|
||||
for c in children[move][::-1]: # stack, so push top child last to process first
|
||||
stack.append(c)
|
||||
children[move] = children_with_shortcuts(move)
|
||||
stack += children[move][::-1] # stack, so push top child last to process first
|
||||
|
||||
def draw_stone(pos, player):
|
||||
draw_circle(pos, self.move_size / 2 - 0.5, STONE_COLORS[player])
|
||||
def draw_stone(pos, player, special_color=None):
|
||||
draw_circle(pos, self.move_size / 2 - 0.5, (special_color or STONE_COLORS[player]))
|
||||
Color(*STONE_TEXT_COLORS[player])
|
||||
Line(circle=(*pos, self.move_size / 2), width=1)
|
||||
|
||||
@@ -152,7 +185,7 @@ class MoveTreeCanvas(Widget):
|
||||
|
||||
self.move_xy_pos = {n: xy_pos(x, y) for n, (x, y) in self.move_pos.items()}
|
||||
|
||||
special_nodes = {self.menu_selected_node: RED, current_node: YELLOW}
|
||||
special_nodes = {current_node: YELLOW, self.menu_selected_node: RED}
|
||||
|
||||
if insert_node:
|
||||
special_nodes[insert_node.parent] = GREEN
|
||||
@@ -176,9 +209,10 @@ class MoveTreeCanvas(Widget):
|
||||
pos=[c - self.move_size / 2 - spacing / 2 for c in self.move_xy_pos[node]],
|
||||
size=(self.move_size + spacing, self.move_size + spacing),
|
||||
)
|
||||
draw_stone(pos, node.player)
|
||||
collapsed_color = LIGHTGREY if node.shortcut_from else None
|
||||
draw_stone(pos, node.player, collapsed_color)
|
||||
text = str(node.depth)
|
||||
Color(*STONE_COLORS["W" if node.player == "B" else "B"])
|
||||
Color(*STONE_COLORS["W" if node.player == "B" and not node.shortcut_from else "B"])
|
||||
draw_text(pos=pos, text=text, font_size=self.move_size * 1.75 / (1 + 1 * len(text)), font_name="Roboto")
|
||||
|
||||
if current_node in self.move_xy_pos:
|
||||
@@ -209,6 +243,9 @@ class MoveTree(ScrollView, BackgroundMixin):
|
||||
def make_selected_node_main_branch(self):
|
||||
self.move_tree_canvas.make_selected_node_main_branch()
|
||||
|
||||
def toggle_selected_node_collapse(self):
|
||||
self.move_tree_canvas.toggle_selected_node_collapse()
|
||||
|
||||
def scroll_to_pixel(self, x, y):
|
||||
if not self._viewport:
|
||||
return
|
||||
@@ -249,14 +286,26 @@ Builder.load_string(
|
||||
MoveTreeDropdownItem:
|
||||
text: i18n._("Delete Node")
|
||||
icon: 'img/delete.png'
|
||||
shortcut: ''
|
||||
shortcut: 'Ctrl+Del'
|
||||
on_action: root.katrain.controls.move_tree.delete_selected_node()
|
||||
-background_color: LIGHTER_BACKGROUND_COLOR
|
||||
-height: dp(45)
|
||||
-width_margin: 1.6
|
||||
MoveTreeDropdownItem:
|
||||
text: i18n._("Make Main Branch")
|
||||
icon: 'img/Branch.png'
|
||||
shortcut: ''
|
||||
shortcut: 'PgUp'
|
||||
on_action: root.katrain.controls.move_tree.make_selected_node_main_branch()
|
||||
-background_color: LIGHTER_BACKGROUND_COLOR
|
||||
-height: dp(45)
|
||||
-width_margin: 1.6
|
||||
MoveTreeDropdownItem:
|
||||
text: i18n._("Toggle Collapse Branch")
|
||||
icon: 'img/Collapse.png'
|
||||
shortcut: 'c'
|
||||
on_action: root.katrain.controls.move_tree.toggle_selected_node_collapse()
|
||||
-background_color: LIGHTER_BACKGROUND_COLOR
|
||||
-height: dp(45)
|
||||
-width_margin: 1.6
|
||||
"""
|
||||
)
|
||||
Binary file not shown.
@@ -753,3 +753,17 @@ msgstr ""
|
||||
#. TODO - Restrict analysis to a specific region
|
||||
msgid "analysis:region"
|
||||
msgstr "Set region of interest"
|
||||
|
||||
#. TODO - on move tree editing
|
||||
msgid "Toggle Collapse Branch"
|
||||
msgstr "Expand/Collapse"
|
||||
|
||||
#. TODO - Menu option
|
||||
msgid "analysis:playtoend"
|
||||
msgstr "Fast playout to game end"
|
||||
|
||||
#. TODO - Status message
|
||||
msgid "playtoend:status"
|
||||
msgstr ""
|
||||
"Playing game to the end with policy strategy, please wait ... {num_moves} "
|
||||
"moves played."
|
||||
Binary file not shown.
@@ -805,3 +805,17 @@ msgstr ""
|
||||
#. TODO - Restrict analysis to a specific region
|
||||
msgid "analysis:region"
|
||||
msgstr "Set region of interest"
|
||||
|
||||
#. TODO - on move tree editing
|
||||
msgid "Toggle Collapse Branch"
|
||||
msgstr "Expand/Collapse"
|
||||
|
||||
#. TODO - Menu option
|
||||
msgid "analysis:playtoend"
|
||||
msgstr "Fast playout to game end"
|
||||
|
||||
#. TODO - Status message
|
||||
msgid "playtoend:status"
|
||||
msgstr ""
|
||||
"Playing game to the end with policy strategy, please wait ... {num_moves} "
|
||||
"moves played."
|
||||
Binary file not shown.
@@ -118,6 +118,16 @@ msgid "analysis:region:start"
|
||||
msgstr ""
|
||||
"Drag to select the region of interest. Selecting a single point removes it."
|
||||
|
||||
#. Menu option
|
||||
msgid "analysis:playtoend"
|
||||
msgstr "Fast playout to game end"
|
||||
|
||||
#. Status message
|
||||
msgid "playtoend:status"
|
||||
msgstr ""
|
||||
"Playing game to the end with policy strategy, please wait ... {num_moves} "
|
||||
"moves played."
|
||||
|
||||
#. Keep improving current position analysis when there is nothing else to do
|
||||
msgid "analysis:continuous"
|
||||
msgstr "Toggle continuous analysis"
|
||||
@@ -759,6 +769,10 @@ msgstr "Number of visits per move"
|
||||
msgid "Delete Node"
|
||||
msgstr "Delete Node"
|
||||
|
||||
#. on move tree editing
|
||||
msgid "Toggle Collapse Branch"
|
||||
msgstr "Expand/Collapse"
|
||||
|
||||
#. on move tree editing
|
||||
msgid "Make Main Branch"
|
||||
msgstr "Make Main Branch"
|
||||
|
||||
Binary file not shown.
@@ -812,3 +812,17 @@ msgstr ""
|
||||
#. TODO - Restrict analysis to a specific region
|
||||
msgid "analysis:region"
|
||||
msgstr "Set region of interest"
|
||||
|
||||
#. TODO - on move tree editing
|
||||
msgid "Toggle Collapse Branch"
|
||||
msgstr "Expand/Collapse"
|
||||
|
||||
#. TODO - Menu option
|
||||
msgid "analysis:playtoend"
|
||||
msgstr "Fast playout to game end"
|
||||
|
||||
#. TODO - Status message
|
||||
msgid "playtoend:status"
|
||||
msgstr ""
|
||||
"Playing game to the end with policy strategy, please wait ... {num_moves} "
|
||||
"moves played."
|
||||
Binary file not shown.
@@ -839,3 +839,17 @@ msgstr ""
|
||||
#. TODO - Restrict analysis to a specific region
|
||||
msgid "analysis:region"
|
||||
msgstr "Set region of interest"
|
||||
|
||||
#. TODO - on move tree editing
|
||||
msgid "Toggle Collapse Branch"
|
||||
msgstr "Expand/Collapse"
|
||||
|
||||
#. TODO - Menu option
|
||||
msgid "analysis:playtoend"
|
||||
msgstr "Fast playout to game end"
|
||||
|
||||
#. TODO - Status message
|
||||
msgid "playtoend:status"
|
||||
msgstr ""
|
||||
"Playing game to the end with policy strategy, please wait ... {num_moves} "
|
||||
"moves played."
|
||||
Binary file not shown.
@@ -796,3 +796,17 @@ msgstr ""
|
||||
#. TODO - Restrict analysis to a specific region
|
||||
msgid "analysis:region"
|
||||
msgstr "Set region of interest"
|
||||
|
||||
#. TODO - on move tree editing
|
||||
msgid "Toggle Collapse Branch"
|
||||
msgstr "Expand/Collapse"
|
||||
|
||||
#. TODO - Menu option
|
||||
msgid "analysis:playtoend"
|
||||
msgstr "Fast playout to game end"
|
||||
|
||||
#. TODO - Status message
|
||||
msgid "playtoend:status"
|
||||
msgstr ""
|
||||
"Playing game to the end with policy strategy, please wait ... {num_moves} "
|
||||
"moves played."
|
||||
Binary file not shown.
@@ -766,3 +766,17 @@ msgstr "Drag to select the region of interest."
|
||||
#. TODO - Restrict analysis to a specific region
|
||||
msgid "analysis:region"
|
||||
msgstr "Set region of interest"
|
||||
|
||||
#. TODO - on move tree editing
|
||||
msgid "Toggle Collapse Branch"
|
||||
msgstr "Expand/Collapse"
|
||||
|
||||
#. TODO - Menu option
|
||||
msgid "analysis:playtoend"
|
||||
msgstr "Fast playout to game end"
|
||||
|
||||
#. TODO - Status message
|
||||
msgid "playtoend:status"
|
||||
msgstr ""
|
||||
"Playing game to the end with policy strategy, please wait ... {num_moves} "
|
||||
"moves played."
|
||||
Binary file not shown.
@@ -802,3 +802,17 @@ msgstr ""
|
||||
#. TODO - Restrict analysis to a specific region
|
||||
msgid "analysis:region"
|
||||
msgstr "Set region of interest"
|
||||
|
||||
#. TODO - on move tree editing
|
||||
msgid "Toggle Collapse Branch"
|
||||
msgstr "Expand/Collapse"
|
||||
|
||||
#. TODO - Menu option
|
||||
msgid "analysis:playtoend"
|
||||
msgstr "Fast playout to game end"
|
||||
|
||||
#. TODO - Status message
|
||||
msgid "playtoend:status"
|
||||
msgstr ""
|
||||
"Playing game to the end with policy strategy, please wait ... {num_moves} "
|
||||
"moves played."
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
Reference in new issue
Block a user