update komi/rules on the fly, etc
This commit is contained in:
1 parent
ad00d2545c
commit
3a2e908669
24 files changed
+215
-52
No files matched your search
+1
-1
@@ -131,7 +131,6 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
|
||||
def toggle_continuous_analysis(self):
|
||||
if self.idle_analysis:
|
||||
self.engine.terminate_continuous_query()
|
||||
self.controls.set_status("", STATUS_INFO)
|
||||
self.idle_analysis = not self.idle_analysis
|
||||
self.update_state()
|
||||
@@ -442,6 +441,7 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
"s": ("analyze-extra", "equalize"),
|
||||
"d": ("analyze-extra", "sweep"),
|
||||
"f": ("analyze-extra", "alternative"),
|
||||
"g": ("analyze-extra", "local"),
|
||||
"p": ("play", None),
|
||||
"down": ("switch-branch", 1),
|
||||
"up": ("switch-branch", -1),
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
"anim_pv_time": 0.5,
|
||||
"debug_level": 0,
|
||||
"lang": "en",
|
||||
"version": "1.5.1"
|
||||
"version": "1.6.0"
|
||||
},
|
||||
"timer": {
|
||||
"byo_length": 30,
|
||||
|
||||
+26
-14
@@ -13,6 +13,7 @@ from kivy.utils import platform
|
||||
from katrain.core.constants import OUTPUT_DEBUG, OUTPUT_ERROR, OUTPUT_EXTRA_DEBUG, OUTPUT_KATAGO_STDERR
|
||||
from katrain.core.game_node import GameNode
|
||||
from katrain.core.lang import i18n
|
||||
from katrain.core.sgf_parser import Move
|
||||
from katrain.core.utils import find_package_resource
|
||||
|
||||
|
||||
@@ -34,7 +35,6 @@ class KataGoEngine:
|
||||
def __init__(self, katrain, config):
|
||||
self.katrain = katrain
|
||||
self.queries = {} # outstanding query id -> start time and callback
|
||||
self.continuous_query = None
|
||||
self.config = config
|
||||
self.query_counter = 0
|
||||
self.katago_process = None
|
||||
@@ -108,10 +108,9 @@ class KataGoEngine:
|
||||
|
||||
def on_new_game(self):
|
||||
self.base_priority += 1
|
||||
for query_id in self.queries:
|
||||
for query_id in list(self.queries.keys()):
|
||||
self.terminate_query(query_id)
|
||||
self.queries = {}
|
||||
self.continuous_query = None
|
||||
|
||||
def restart(self):
|
||||
self.queries = {}
|
||||
@@ -211,7 +210,7 @@ class KataGoEngine:
|
||||
self.katrain.log(line, OUTPUT_EXTRA_DEBUG)
|
||||
try:
|
||||
if callback and not analysis.get("noResults", False):
|
||||
callback(analysis)
|
||||
callback(analysis, partial_result)
|
||||
except Exception as e:
|
||||
self.katrain.log(f"Error in engine callback for query {query_id}: {e}", OUTPUT_ERROR)
|
||||
if getattr(self.katrain, "update_state", None): # easier mocking etc
|
||||
@@ -220,16 +219,12 @@ class KataGoEngine:
|
||||
self.katrain.log(f"Unexpected exception {e} while processing KataGo output {line}", OUTPUT_ERROR)
|
||||
traceback.print_exc()
|
||||
|
||||
def send_query(self, query, callback, error_callback, next_move=None, report_during_search=False):
|
||||
def send_query(self, query, callback, error_callback, next_move=None):
|
||||
with self._lock:
|
||||
self.query_counter += 1
|
||||
if "id" not in query:
|
||||
query["id"] = f"QUERY:{str(self.query_counter)}"
|
||||
self.queries[query["id"]] = (callback, error_callback, time.time(), next_move)
|
||||
if report_during_search:
|
||||
query["reportDuringSearchEvery"] = 0.25
|
||||
self.terminate_continuous_query()
|
||||
self.continuous_query = query["id"]
|
||||
if self.katago_process:
|
||||
self.katrain.log(f"Sending query {query['id']}: {json.dumps(query)}", OUTPUT_DEBUG)
|
||||
try:
|
||||
@@ -238,9 +233,6 @@ class KataGoEngine:
|
||||
except OSError as e:
|
||||
self.check_alive(os_error=str(e), exception_if_dead=True)
|
||||
|
||||
def terminate_continuous_query(self):
|
||||
self.terminate_query(self.continuous_query)
|
||||
|
||||
def terminate_query(self, query_id):
|
||||
if query_id is not None:
|
||||
self.send_query({"action": "terminate", "terminateId": query_id}, None, None)
|
||||
@@ -254,6 +246,7 @@ class KataGoEngine:
|
||||
analyze_fast: bool = False,
|
||||
time_limit=True,
|
||||
find_alternatives: bool = False,
|
||||
find_local: bool = False,
|
||||
priority: int = 0,
|
||||
ownership: Optional[bool] = None,
|
||||
next_move: Optional[GameNode] = None,
|
||||
@@ -272,6 +265,8 @@ class KataGoEngine:
|
||||
if analyze_fast and self.config.get("fast_visits"):
|
||||
visits = self.config["fast_visits"]
|
||||
|
||||
size_x, size_y = analysis_node.board_size
|
||||
|
||||
if find_alternatives:
|
||||
avoid = [
|
||||
{
|
||||
@@ -280,10 +275,26 @@ class KataGoEngine:
|
||||
"untilDepth": 1,
|
||||
}
|
||||
]
|
||||
elif find_local:
|
||||
distance = 5
|
||||
last_move = analysis_node.move
|
||||
if last_move is None or last_move.is_pass:
|
||||
return
|
||||
avoid = [
|
||||
{
|
||||
"moves": [
|
||||
Move((x, y)).gtp()
|
||||
for x in range(0, size_x)
|
||||
for y in range(0, size_y)
|
||||
if max(abs(x - last_move.coords[0]), abs(y - last_move.coords[1])) > distance
|
||||
],
|
||||
"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:
|
||||
settings["maxTime"] = self.config["max_time"]
|
||||
@@ -296,7 +307,6 @@ 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,
|
||||
@@ -309,5 +319,7 @@ class KataGoEngine:
|
||||
}
|
||||
if report_every is not None:
|
||||
query["reportDuringSearchEvery"] = report_every
|
||||
if avoid:
|
||||
query["avoidMoves"] = avoid
|
||||
self.send_query(query, callback, error_callback, next_move)
|
||||
analysis_node.analysis_visits_requested = max(analysis_node.analysis_visits_requested, visits)
|
||||
+18
-13
@@ -86,9 +86,8 @@ class Game:
|
||||
|
||||
def analyze_all_nodes(self, priority=0, analyze_fast=False):
|
||||
for node in self.root.nodes_in_tree:
|
||||
node.analyze(
|
||||
self.engines[node.next_player], priority=priority, analyze_fast=analyze_fast #, report_every=None
|
||||
)
|
||||
node.clear_analysis()
|
||||
node.analyze(self.engines[node.next_player], priority=priority, analyze_fast=analyze_fast)
|
||||
|
||||
# -- move tree functions --
|
||||
def _calculate_groups(self):
|
||||
@@ -325,11 +324,14 @@ class Game:
|
||||
|
||||
if mode == "extra":
|
||||
if kwargs.get("continuous", False):
|
||||
visits = 1_000_000_000
|
||||
visits = min(
|
||||
1_000_000_000, max(engine.config["max_visits"], math.ceil(cn.analysis_visits_requested * 1.25))
|
||||
)
|
||||
else:
|
||||
visits = cn.analysis_visits_requested + engine.config["max_visits"]
|
||||
self.katrain.controls.set_status(i18n._("extra analysis").format(visits=visits), STATUS_ANALYSIS)
|
||||
cn.analyze(engine, visits=visits, priority=-1_000, time_limit=False, report_during_search=True)
|
||||
self.katrain.controls.set_status(i18n._("extra analysis").format(visits=visits), STATUS_ANALYSIS)
|
||||
cn.analyze(engine, visits=visits, priority=-1_000, time_limit=False)
|
||||
return
|
||||
if mode == "game":
|
||||
nodes = self.root.nodes_in_tree
|
||||
@@ -370,26 +372,29 @@ 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 in ["equalize", "alternative"]:
|
||||
if not cn.analysis_complete:
|
||||
elif mode == "local": # also do a quick update on current candidates so it doesn't look too weird
|
||||
self.katrain.controls.set_status(i18n._("local analysis"), STATUS_ANALYSIS)
|
||||
cn.analyze(engine, priority=-500, time_limit=False, find_local="local")
|
||||
return
|
||||
elif mode in ["equalize", "alternative", "local"]:
|
||||
if not cn.analysis_complete and mode != "local":
|
||||
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(i18n._("alternative analysis"), STATUS_ANALYSIS)
|
||||
cn.analyze(engine, priority=-500, time_limit=False, find_alternatives=True)
|
||||
cn.analyze(engine, priority=-500, time_limit=False, find_alternatives="alternative")
|
||||
visits = engine.config["fast_visits"]
|
||||
else:
|
||||
else: # equalize
|
||||
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
|
||||
analyze_moves = [Move.from_gtp(gtp, player=cn.next_player) for gtp, _ in cn.analysis["moves"].items()]
|
||||
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, report_every=None
|
||||
engine, priority=priority, visits=visits, refine_move=move, time_limit=False, report_every=None
|
||||
) # explicitly requested so take as long as you need
|
||||
|
||||
def analyze_undo(self, node):
|
||||
|
||||
@@ -13,7 +13,6 @@ class GameNode(SGFNode):
|
||||
|
||||
def __init__(self, parent=None, properties=None, move=None):
|
||||
super().__init__(parent=parent, properties=properties, move=move)
|
||||
self.analysis = {"moves": {}, "root": None, "completed": False}
|
||||
self.ownership = None
|
||||
self.policy = None
|
||||
self.auto_undo = None # None = not analyzed. False: not undone (good move). True: undone (bad move)
|
||||
@@ -21,9 +20,13 @@ class GameNode(SGFNode):
|
||||
self.note = ""
|
||||
self.move_number = 0
|
||||
self.time_used = 0
|
||||
self.analysis_visits_requested = 0
|
||||
self.undo_threshold = random.random() # for fractional undos
|
||||
self.end_state = None
|
||||
self.clear_analysis()
|
||||
|
||||
def clear_analysis(self):
|
||||
self.analysis_visits_requested = 0
|
||||
self.analysis = {"moves": {}, "root": None, "completed": False}
|
||||
|
||||
def sgf_properties(self, save_comments_player=None, save_comments_class=None, eval_thresholds=None):
|
||||
properties = copy.copy(super().sgf_properties())
|
||||
@@ -77,17 +80,21 @@ class GameNode(SGFNode):
|
||||
refine_move=None,
|
||||
analyze_fast=False,
|
||||
find_alternatives=False,
|
||||
find_local=False,
|
||||
report_every=0.25,
|
||||
):
|
||||
engine.request_analysis(
|
||||
self,
|
||||
callback=lambda result, partial_result: self.set_analysis(result, refine_move, find_alternatives, partial_result),
|
||||
callback=lambda result, partial_result: self.set_analysis(
|
||||
result, refine_move, find_alternatives or find_local, partial_result
|
||||
),
|
||||
priority=priority,
|
||||
visits=visits,
|
||||
analyze_fast=analyze_fast,
|
||||
time_limit=time_limit,
|
||||
next_move=refine_move,
|
||||
find_alternatives=find_alternatives,
|
||||
find_local=find_local,
|
||||
report_every=report_every,
|
||||
)
|
||||
|
||||
@@ -105,7 +112,7 @@ class GameNode(SGFNode):
|
||||
cur.update(move_analysis)
|
||||
|
||||
def set_analysis(
|
||||
self, analysis_json: Dict, refine_move: Optional[Move], alternatives_mode: bool, partial_result: bool = False
|
||||
self, analysis_json: Dict, refine_move: Optional[Move], additional_moves: bool, partial_result: bool = False
|
||||
):
|
||||
if refine_move:
|
||||
pvtail = analysis_json["moveInfos"][0]["pv"] if analysis_json["moveInfos"] else []
|
||||
@@ -113,17 +120,17 @@ class GameNode(SGFNode):
|
||||
{"pv": [refine_move.gtp()] + pvtail, **analysis_json["rootInfo"]}, refine_move.gtp()
|
||||
)
|
||||
else:
|
||||
if alternatives_mode:
|
||||
if additional_moves:
|
||||
for m in analysis_json["moveInfos"]:
|
||||
m["order"] += 100 # offset for not making this top
|
||||
if refine_move is None and not alternatives_mode:
|
||||
del m["order"] # avoid changing order
|
||||
if refine_move is None and not additional_moves:
|
||||
for move_dict in self.analysis["moves"].values():
|
||||
move_dict["order"] = 999 # old moves to end
|
||||
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:
|
||||
if not additional_moves:
|
||||
self.analysis["root"] = analysis_json["rootInfo"]
|
||||
if self.parent and self.move:
|
||||
analysis_json["rootInfo"]["pv"] = [self.move.gtp()] + (
|
||||
@@ -132,7 +139,7 @@ class GameNode(SGFNode):
|
||||
self.parent.update_move_analysis(
|
||||
analysis_json["rootInfo"], self.move.gtp()
|
||||
) # update analysis in parent for consistency
|
||||
is_normal_query = refine_move is None and not alternatives_mode
|
||||
is_normal_query = refine_move is None and not additional_moves
|
||||
self.analysis["completed"] = self.analysis["completed"] or (is_normal_query and not partial_result)
|
||||
|
||||
@property
|
||||
|
||||
@@ -749,6 +749,11 @@
|
||||
icon: 'img/Alternative.png'
|
||||
shortcut: 'f'
|
||||
on_action: root.katrain("analyze-extra", "alternative")
|
||||
AnalysisMenuItem:
|
||||
text: i18n._("analysis:local")
|
||||
icon: 'img/Alternative.png'
|
||||
shortcut: 'g'
|
||||
on_action: root.katrain("analyze-extra", "local")
|
||||
AnalysisMenuItem:
|
||||
text: i18n._("analysis:game")
|
||||
icon: 'img/analysis.png'
|
||||
|
||||
@@ -260,6 +260,19 @@ class NewGamePopup(QuickConfigGui):
|
||||
self.katrain.update_player(bw, **player_setup.player_type_dump)
|
||||
self.katrain("new-game")
|
||||
|
||||
def update_game(self, save_to_file=True):
|
||||
props = self.collect_properties(self)
|
||||
root = self.katrain.game.root
|
||||
changed = False
|
||||
for k, v in [("RU", props["game/rules"]), ("KM", props["game/komi"])]:
|
||||
current = root.get_property(k)
|
||||
if current != v:
|
||||
changed = True
|
||||
self.katrain.game.root.set_property(k, v)
|
||||
if changed:
|
||||
self.katrain.engine.on_new_game()
|
||||
self.katrain.game.analyze_all_nodes()
|
||||
self.popup.dismiss()
|
||||
|
||||
def wrap_anchor(widget):
|
||||
anchor = AnchorLayout()
|
||||
|
||||
Binary file not shown.
@@ -686,3 +686,17 @@ msgstr ""
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Style"
|
||||
|
||||
#. TODO - Analyze again but allow only nearby moves
|
||||
msgid "analysis:local"
|
||||
msgstr "Find local responses"
|
||||
|
||||
#. TODO - status message
|
||||
msgid "local analysis"
|
||||
msgstr "Searching for local responses"
|
||||
|
||||
#. TODO - change komi etc in current game
|
||||
msgid "change current game"
|
||||
msgstr ""
|
||||
"Update komi/rules\n"
|
||||
"in active game"
|
||||
Binary file not shown.
@@ -735,3 +735,17 @@ msgstr ""
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Style"
|
||||
|
||||
#. TODO - Analyze again but allow only nearby moves
|
||||
msgid "analysis:local"
|
||||
msgstr "Find local responses"
|
||||
|
||||
#. TODO - status message
|
||||
msgid "local analysis"
|
||||
msgstr "Searching for local responses"
|
||||
|
||||
#. TODO - change komi etc in current game
|
||||
msgid "change current game"
|
||||
msgstr ""
|
||||
"Update komi/rules\n"
|
||||
"in active game"
|
||||
Binary file not shown.
@@ -106,6 +106,10 @@ msgstr "Deeper full game analysis"
|
||||
msgid "analysis:alternatives"
|
||||
msgstr "Find alternative moves"
|
||||
|
||||
#. Analyze again but allow only nearby moves
|
||||
msgid "analysis:local"
|
||||
msgstr "Find local responses"
|
||||
|
||||
#. Keep improving current position analysis when there is nothing else to do
|
||||
msgid "analysis:continuous"
|
||||
msgstr "Toggle continuous analysis"
|
||||
@@ -125,6 +129,10 @@ msgstr "Analyzing entire board using {visits} visits/move"
|
||||
msgid "alternative analysis"
|
||||
msgstr "Searching for alternative moves"
|
||||
|
||||
#. status message
|
||||
msgid "local analysis"
|
||||
msgstr "Searching for local responses"
|
||||
|
||||
#. status message
|
||||
msgid "equalizing analysis"
|
||||
msgstr "Equalizing analysis of candidate moves to {visits} visits"
|
||||
@@ -362,6 +370,12 @@ msgstr "Use x:y (e.g. 19:9) for non-square size"
|
||||
msgid "new game"
|
||||
msgstr "Start New Game"
|
||||
|
||||
#. change komi etc in current game
|
||||
msgid "change current game"
|
||||
msgstr ""
|
||||
"Update komi/rules\n"
|
||||
"in active game"
|
||||
|
||||
# teacher settings from here
|
||||
msgid "teacher settings"
|
||||
msgstr "Edit Teacher Settings"
|
||||
|
||||
Binary file not shown.
@@ -734,3 +734,17 @@ msgstr ""
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Style"
|
||||
|
||||
#. TODO - Analyze again but allow only nearby moves
|
||||
msgid "analysis:local"
|
||||
msgstr "Find local responses"
|
||||
|
||||
#. TODO - status message
|
||||
msgid "local analysis"
|
||||
msgstr "Searching for local responses"
|
||||
|
||||
#. TODO - change komi etc in current game
|
||||
msgid "change current game"
|
||||
msgstr ""
|
||||
"Update komi/rules\n"
|
||||
"in active game"
|
||||
Binary file not shown.
@@ -767,3 +767,17 @@ msgstr ""
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Stones Style"
|
||||
|
||||
#. TODO - Analyze again but allow only nearby moves
|
||||
msgid "analysis:local"
|
||||
msgstr "Find local responses"
|
||||
|
||||
#. TODO - status message
|
||||
msgid "local analysis"
|
||||
msgstr "Searching for local responses"
|
||||
|
||||
#. TODO - change komi etc in current game
|
||||
msgid "change current game"
|
||||
msgstr ""
|
||||
"Update komi/rules\n"
|
||||
"in active game"
|
||||
Binary file not shown.
@@ -714,3 +714,17 @@ msgstr ""
|
||||
|
||||
msgid "ai:settle"
|
||||
msgstr "堅実派"
|
||||
|
||||
#. TODO - Analyze again but allow only nearby moves
|
||||
msgid "analysis:local"
|
||||
msgstr "Find local responses"
|
||||
|
||||
#. TODO - status message
|
||||
msgid "local analysis"
|
||||
msgstr "Searching for local responses"
|
||||
|
||||
#. TODO - change komi etc in current game
|
||||
msgid "change current game"
|
||||
msgstr ""
|
||||
"Update komi/rules\n"
|
||||
"in active game"
|
||||
Binary file not shown.
@@ -697,3 +697,17 @@ msgstr ""
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Style"
|
||||
|
||||
#. TODO - Analyze again but allow only nearby moves
|
||||
msgid "analysis:local"
|
||||
msgstr "Find local responses"
|
||||
|
||||
#. TODO - status message
|
||||
msgid "local analysis"
|
||||
msgstr "Searching for local responses"
|
||||
|
||||
#. TODO - change komi etc in current game
|
||||
msgid "change current game"
|
||||
msgstr ""
|
||||
"Update komi/rules\n"
|
||||
"in active game"
|
||||
Binary file not shown.
@@ -730,3 +730,17 @@ msgstr ""
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Style"
|
||||
|
||||
#. TODO - Analyze again but allow only nearby moves
|
||||
msgid "analysis:local"
|
||||
msgstr "Find local responses"
|
||||
|
||||
#. TODO - status message
|
||||
msgid "local analysis"
|
||||
msgstr "Searching for local responses"
|
||||
|
||||
#. TODO - change komi etc in current game
|
||||
msgid "change current game"
|
||||
msgstr ""
|
||||
"Update komi/rules\n"
|
||||
"in active game"
|
||||
+23
-14
@@ -175,17 +175,22 @@
|
||||
font_size: DESC_FONT_SIZE * 1.5
|
||||
text: i18n._('engine settings')
|
||||
BoxLayout:
|
||||
size_hint: 1,6
|
||||
size_hint: 1,5
|
||||
orientation: 'horizontal'
|
||||
GridLayout:
|
||||
cols: 2
|
||||
rows: 4
|
||||
rows: 5
|
||||
spacing: CP_SPACING
|
||||
DescriptionLabel:
|
||||
text: i18n._("general:sgf_save")
|
||||
AnchorLayout:
|
||||
LabelledPathInput:
|
||||
input_property: "general/sgf_save"
|
||||
DescriptionLabel:
|
||||
text: i18n._('count down sound')
|
||||
AnchorLayout:
|
||||
LabelledCheckBox:
|
||||
input_property: "timer/sound"
|
||||
DescriptionLabel:
|
||||
text: i18n._("general:anim_pv_time")
|
||||
AnchorLayout:
|
||||
@@ -199,11 +204,6 @@
|
||||
input_property: "general/debug_level"
|
||||
helper_text: i18n._("general:debug_level:hint")
|
||||
helper_text_mode: "on_focus"
|
||||
DescriptionLabel:
|
||||
text: i18n._('count down sound')
|
||||
AnchorLayout:
|
||||
LabelledCheckBox:
|
||||
input_property: "timer/sound"
|
||||
SmallDescriptionLabel:
|
||||
text: i18n._('config file path')
|
||||
SmallDescriptionLabel:
|
||||
@@ -455,13 +455,22 @@
|
||||
input_property: 'game/clear_cache'
|
||||
SmallDescriptionLabel:
|
||||
text: i18n._('avoids replaying')
|
||||
AnchorLayout:
|
||||
size_hint: 1,1
|
||||
AutoSizedRoundedRectangleButton:
|
||||
padding_x: 15
|
||||
size_hint: None,0.5
|
||||
text: i18n._("new game")
|
||||
on_press: root.update_config(True)
|
||||
BoxLayout:
|
||||
size_hint: 1,0.75
|
||||
AnchorLayout:
|
||||
size_hint: 0.5, 1
|
||||
AutoSizedRoundedRectangleButton:
|
||||
padding_x: 15
|
||||
size_hint: None,0.5
|
||||
_font_size: self.height*0.35
|
||||
text: i18n._("change current game")
|
||||
on_press: root.update_game()
|
||||
AnchorLayout:
|
||||
AutoSizedRoundedRectangleButton:
|
||||
padding_x: 15
|
||||
size_hint: None,0.66
|
||||
text: i18n._("new game")
|
||||
on_press: root.update_config(True)
|
||||
|
||||
|
||||
<LabelledSelectionSlider>:
|
||||
|
||||
Reference in new issue
Block a user