diff --git a/katrain/__main__.py b/katrain/__main__.py index 9252395..c5bbfbc 100644 --- a/katrain/__main__.py +++ b/katrain/__main__.py @@ -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), diff --git a/katrain/config.json b/katrain/config.json index ea3afd0..1a85486 100644 --- a/katrain/config.json +++ b/katrain/config.json @@ -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, diff --git a/katrain/core/engine.py b/katrain/core/engine.py index 490b97c..17ce22b 100644 --- a/katrain/core/engine.py +++ b/katrain/core/engine.py @@ -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) diff --git a/katrain/core/game.py b/katrain/core/game.py index c3a4db9..91b7ce9 100644 --- a/katrain/core/game.py +++ b/katrain/core/game.py @@ -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): diff --git a/katrain/core/game_node.py b/katrain/core/game_node.py index 48e23f4..997e4e6 100644 --- a/katrain/core/game_node.py +++ b/katrain/core/game_node.py @@ -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 diff --git a/katrain/gui.kv b/katrain/gui.kv index f4dd0cd..d88f056 100644 --- a/katrain/gui.kv +++ b/katrain/gui.kv @@ -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' diff --git a/katrain/gui/popups.py b/katrain/gui/popups.py index 0663a35..3e4c337 100644 --- a/katrain/gui/popups.py +++ b/katrain/gui/popups.py @@ -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() diff --git a/katrain/i18n/locales/cn/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/cn/LC_MESSAGES/katrain.mo index d7f987d..0143e4a 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 f16310b..19535d8 100644 --- a/katrain/i18n/locales/cn/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/cn/LC_MESSAGES/katrain.po @@ -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" diff --git a/katrain/i18n/locales/de/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/de/LC_MESSAGES/katrain.mo index 30525fe..561864a 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 78cc749..897082f 100644 --- a/katrain/i18n/locales/de/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/de/LC_MESSAGES/katrain.po @@ -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" diff --git a/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo index c55b7a7..83e6a1d 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 c92796e..aadcc9b 100644 --- a/katrain/i18n/locales/en/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/en/LC_MESSAGES/katrain.po @@ -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" diff --git a/katrain/i18n/locales/es/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/es/LC_MESSAGES/katrain.mo index 2c8731b..6bc59d4 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 08ce3dc..92ef06c 100644 --- a/katrain/i18n/locales/es/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/es/LC_MESSAGES/katrain.po @@ -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" diff --git a/katrain/i18n/locales/fr/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/fr/LC_MESSAGES/katrain.mo index a4e195f..e5c7d59 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 d535de0..1477b12 100644 --- a/katrain/i18n/locales/fr/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/fr/LC_MESSAGES/katrain.po @@ -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" diff --git a/katrain/i18n/locales/jp/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/jp/LC_MESSAGES/katrain.mo index e1bad74..160cb22 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 7901826..f30e5b3 100644 --- a/katrain/i18n/locales/jp/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/jp/LC_MESSAGES/katrain.po @@ -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" diff --git a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo index 206bb8e..5b11b24 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 ce14c9f..2464b9b 100644 --- a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po @@ -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" diff --git a/katrain/i18n/locales/ru/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/ru/LC_MESSAGES/katrain.mo index 9750b01..c2461c4 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 e0b1be1..7f444c5 100644 --- a/katrain/i18n/locales/ru/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/ru/LC_MESSAGES/katrain.po @@ -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" diff --git a/katrain/popups.kv b/katrain/popups.kv index a07c44d..12436ac 100644 --- a/katrain/popups.kv +++ b/katrain/popups.kv @@ -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) :