diff --git a/i18n.py b/i18n.py index 0d75a13..54c3237 100644 --- a/i18n.py +++ b/i18n.py @@ -17,15 +17,19 @@ errors = False po = {} pofile = {} +num_todo = defaultdict(int) + for lang in locales: pofile[lang] = os.path.join(localedir, lang, "LC_MESSAGES", "katrain.po") po[lang] = polib.pofile(pofile[lang]) for entry in po[lang].translated_entries(): if "TODO" in entry.comment: - print(lang, "/", entry.msgid, "is TODO") + num_todo[lang] += 1 else: strings_to_langs[entry.msgid][lang] = entry.msgstr lang_to_strings[lang].add(entry.msgid) + if num_todo[lang]: + print(f"{lang} has {num_todo[lang]} TODO entries") for lang in locales: @@ -34,11 +38,12 @@ for lang in locales: print("Message id", msgid, "found as ", strings_to_langs[msgid], "but missing in default", DEFAULT_LANG) errors = True elif DEFAULT_LANG in strings_to_langs[msgid]: - print("Message id", msgid, "missing in ", lang, "-> Adding it from", DEFAULT_LANG) copied_msg = strings_to_langs[msgid][DEFAULT_LANG] if lang == "haha": - copied_msg = re.sub(r"[a-zA-Z]{1,2}", "ㅋ", copied_msg) - entry = polib.POEntry(msgid=msgid, msgstr=copied_msg, comment="TODO") + entry = polib.POEntry(msgid=msgid, msgstr="ㅋㅋ" + copied_msg) + else: + print("Message id", msgid, "missing in ", lang, "-> Adding it from", DEFAULT_LANG) + entry = polib.POEntry(msgid=msgid, msgstr=copied_msg, comment="TODO") po[lang].append(entry) errors = True else: diff --git a/katrain/__main__.py b/katrain/__main__.py index cb493e7..f451cb2 100644 --- a/katrain/__main__.py +++ b/katrain/__main__.py @@ -25,7 +25,7 @@ from katrain.core.utils import ( DEFAULT_LANGUAGE, find_package_resource, ) -from katrain.core.constants import OUTPUT_ERROR, OUTPUT_KATAGO_STDERR, OUTPUT_INFO, OUTPUT_DEBUG, OUTPUT_EXTRA_DEBUG, MODE_PLAY +from katrain.core.constants import OUTPUT_ERROR, OUTPUT_KATAGO_STDERR, OUTPUT_INFO, OUTPUT_DEBUG, OUTPUT_EXTRA_DEBUG, MODE_PLAY, AI_STRATEGIES_RECOMMENDED_ORDER from katrain.gui.popups import ConfigTeacherPopup, ConfigTimerPopup, I18NPopup from katrain.core.base_katrain import KaTrainBase from katrain.core.engine import KataGoEngine @@ -33,7 +33,7 @@ from katrain.core.game import Game, IllegalMoveException, KaTrainSGF from katrain.core.sgf_parser import Move, ParseError from katrain.gui.kivyutils import * from katrain.gui.widgets.graph import ScoreGraph -from katrain.gui.widgets.filebrowser import I18NFileChooserListView +from katrain.gui.widgets.filebrowser import I18NFileBrowser from katrain.gui.badukpan import AnalysisControls, BadukPanControls, BadukPanWidget from katrain.gui.controlspanel import ControlsPanel from katrain.gui.popups import ConfigPopup, LoadSGFPopup, NewGamePopup, AIPopup @@ -81,10 +81,6 @@ class KaTrainGui(Screen, KaTrainBase): def play_analyze_mode(self): return self.play_mode.play_analyze_mode - @property - def ai_strategies(self): - return list(self.config("ai").keys()) - def start(self): if self.engine: return @@ -163,10 +159,10 @@ class KaTrainGui(Screen, KaTrainBase): def __call__(self, message, *args): if self.game: - if message.endswith('popup'): # gui code needs to run in main kivy thread. + if message.endswith("popup"): # gui code needs to run in main kivy thread. fn = getattr(self, f"_do_{message.replace('-', '_')}") Clock.schedule_once(lambda _dt: fn(*args), -1) - else: # game related actions + else: # game related actions self.message_queue.put([self.game.game_id, message, *args]) def _do_new_game(self, move_tree=None, analyze_fast=False): @@ -180,7 +176,7 @@ class KaTrainGui(Screen, KaTrainBase): if node is None or self.game.current_node == node: mode = self.next_player_info.strategy settings = self.config(f"ai/{mode}") - if settings: + if settings is not None: ai_move(self.game, mode, settings) else: self.log(f"AI Mode {mode} not found!", OUTPUT_ERROR) @@ -252,7 +248,8 @@ class KaTrainGui(Screen, KaTrainBase): popup_contents.filesel.path = os.path.abspath(os.path.expanduser(self.config("general/sgf_load", "."))) self.fileselect_popup = I18NPopup(title_key="load sgf title", size=[1200, 800], content=popup_contents).__self__ - def readfile(files, _mouse): + def readfile(*args): + files = popup_contents.filesel.selection self.fileselect_popup.dismiss() try: move_tree = KaTrainSGF.parse_file(files[0]) @@ -263,6 +260,7 @@ class KaTrainGui(Screen, KaTrainBase): if not popup_contents.rewind.active: self.game.redo(999) + popup_contents.filesel.on_success = readfile popup_contents.filesel.on_submit = readfile self.fileselect_popup.open() diff --git a/katrain/config.json b/katrain/config.json index 69af874..1c3a950 100644 --- a/katrain/config.json +++ b/katrain/config.json @@ -13,7 +13,7 @@ "_enable_ownership": true }, "general": { - "sgf_load": "~/Downloads", + "sgf_load": "~/", "sgf_save": "./sgfout", "anim_pv_time": 0.5, "debug_level": 1, @@ -68,8 +68,7 @@ "lock_ai": true }, "ai": { - "ai:default": { - }, + "ai:default": {}, "ai:jigo": { "target_score": 0.5 }, @@ -77,7 +76,7 @@ "strength": 0.5 }, "ai:policy": { - "opening_moves": 20 + "opening_moves": 22.0 }, "ai:p:weighted": { "pick_override": 1.0, diff --git a/katrain/core/ai.py b/katrain/core/ai.py index e3234c0..94ee8a1 100644 --- a/katrain/core/ai.py +++ b/katrain/core/ai.py @@ -5,7 +5,7 @@ import time from typing import Dict, List, Tuple from katrain.core.utils import var_to_grid -from katrain.core.constants import OUTPUT_INFO, OUTPUT_DEBUG +from katrain.core.constants import OUTPUT_INFO, OUTPUT_DEBUG, AI_STRATEGIES_POLICY, AI_POLICY, AI_WEIGHTED from katrain.core.engine import EngineDiedException from katrain.core.game import Game, GameNode, Move @@ -33,9 +33,8 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode engine = game.engines[cn.next_player] if engine.katago_process.poll() is not None: # TODO: clean up raise EngineDiedException(f"Engine for {cn.next_player} ({engine.config}) died") - ai_mode = ai_mode.lower() ai_thoughts = "" - if ("policy" in ai_mode or "p:" in ai_mode) and cn.policy: # pure policy based move + if (ai_mode in AI_STRATEGIES_POLICY) and cn.policy: # pure policy based move policy_moves = cn.policy_ranking pass_policy = cn.policy[-1] top_5_pass = any([polmove[1].is_pass for polmove in policy_moves[:5]]) # dont make it jump around for the last few sensible non pass moves @@ -44,20 +43,20 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode policy_grid = var_to_grid(cn.policy, size) # type: List[List[float]] top_policy_move = policy_moves[0][1] ai_thoughts += f"Using policy based strategy, base top 5 moves are {fmt_moves(policy_moves[:5])}. " - if "policy" in ai_mode and cn.depth <= ai_settings["opening_moves"]: + if ai_mode == AI_POLICY and cn.depth <= ai_settings["opening_moves"]: ai_mode = "p:weighted" ai_thoughts += f"Switching to weighted strategy in the opening {int(ai_settings['opening_moves'] * (game.board_size[0]*game.board_size[1]))} moves. " ai_settings = {"pick_override": 0.9, "weaken_fac": 1, "lower_bound": 0.02} if top_5_pass: aimove = top_policy_move ai_thoughts += "Playing top one because one of them is pass." - elif "policy" in ai_mode: + elif ai_mode == AI_POLICY: aimove = top_policy_move ai_thoughts += f"Playing top policy move {aimove.gtp()}." elif policy_moves[0][0] > ai_settings["pick_override"]: aimove = top_policy_move ai_thoughts += f"Top policy move has weight > {ai_settings['pick_override']:.1%}, so overriding other strategies." - elif "weighted" in ai_mode: + elif ai_mode == AI_WEIGHTED: lower_bound = max(0, ai_settings["lower_bound"]) * 2 # compensate for first halving in loop weaken_fac = max(0.01, ai_settings["weaken_fac"]) weighted_coords = [] diff --git a/katrain/core/base_katrain.py b/katrain/core/base_katrain.py index 611befb..1cdaa31 100644 --- a/katrain/core/base_katrain.py +++ b/katrain/core/base_katrain.py @@ -66,7 +66,7 @@ class KaTrainBase: print(message) def _load_config(self): - config_file = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 else find_package_resource(self.CONFIG_FILE)) + config_file = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 and sys.argv[1].endswith(".json") else find_package_resource(self.CONFIG_FILE)) try: self.log(f"Using config file {config_file}", OUTPUT_INFO) self._config_store = JsonStore(config_file, indent=4) diff --git a/katrain/core/constants.py b/katrain/core/constants.py index a2c3046..c18eaf6 100644 --- a/katrain/core/constants.py +++ b/katrain/core/constants.py @@ -8,8 +8,39 @@ GAME_TYPES = [PLAYING_NORMAL, PLAYING_TEACHING] MODE_PLAY, MODE_ANALYZE = "play", "analyze" -AI_DEFAULT = "default" -AI_STRATEGIES = [AI_DEFAULT] +AI_DEFAULT = "ai:default" +AI_SCORELOSS = "ai:scoreloss" +AI_WEIGHTED = "ai:p:weighted" +AI_JIGO = "ai:jigo" +AI_POLICY = "ai:policy" +AI_PICK = "ai:p:pick" +AI_LOCAL = "ai:p:local" +AI_TENUKI = "ai:p:tenuki" +AI_INFLUENCE = "ai:p:influence" +AI_TERRITORY = "ai:p:territory" + +AI_CONFIG_DEFAULT = AI_SCORELOSS + +AI_STRATEGIES_ENGINE = [AI_DEFAULT, AI_SCORELOSS, AI_JIGO] +AI_STRATEGIES_POLICY = [AI_WEIGHTED, AI_POLICY, AI_PICK, AI_LOCAL, AI_TENUKI, AI_INFLUENCE, AI_TERRITORY] +AI_STRATEGIES = AI_STRATEGIES_ENGINE + AI_STRATEGIES_POLICY +AI_STRATEGIES_RECOMMENDED_ORDER = [AI_DEFAULT, AI_SCORELOSS, AI_WEIGHTED, AI_PICK, AI_POLICY, AI_LOCAL, AI_TENUKI, AI_TERRITORY, AI_INFLUENCE, AI_JIGO] + + +AI_STRENGTH = { + AI_DEFAULT: "9d", + AI_POLICY: "4d", + AI_JIGO: "?d", + AI_SCORELOSS: "5k", + AI_WEIGHTED: "5k", + AI_PICK: "8k", + AI_LOCAL: "5k", + AI_TENUKI: "8k", + AI_INFLUENCE: "8k", + AI_TERRITORY: "5k", +} + + OUTPUT_ERROR = -1 OUTPUT_KATAGO_STDERR = -0.5 OUTPUT_INFO = 0 diff --git a/katrain/core/utils.py b/katrain/core/utils.py index c74c4ad..f836cd4 100644 --- a/katrain/core/utils.py +++ b/katrain/core/utils.py @@ -1,7 +1,7 @@ import os import sys from numbers import Number -from typing import List, Tuple, Callable +from typing import List, Tuple, Callable, TypeVar from kivy.lang import Observable import gettext @@ -12,8 +12,10 @@ try: except: import importlib_resources as pkg_resources +T = TypeVar("T") -def var_to_grid(array_var: List[Number], size: Tuple[int, int]) -> List[List[Number]]: + +def var_to_grid(array_var: List[T], size: Tuple[int, int]) -> List[List[T]]: """convert ownership/policy to grid format such that grid[y][x] is for move with coords x,y""" ix = 0 grid = [[]] * size[1] @@ -55,11 +57,21 @@ class Lang(Observable): def _(self, text): return self.ugettext(text) + def set_widget_font(self, widget): + widget.font_name = self.font_name + for sub_widget in [getattr(widget, "_hint_lbl", None), getattr(widget, "_msg_lbl", None)]: # MDText + if sub_widget: + sub_widget.font_name = self.font_name + def fbind(self, name, func, *args): if name == "_": widget, property, *_ = args[0] self.observers.append((widget, func, args)) - widget.font_name = self.font_name + try: + self.set_widget_font(widget) + except Exception as e: + print(e) + # pass else: return super(Lang, self).fbind(name, func, *args) @@ -88,11 +100,7 @@ class Lang(Observable): for widget, func, args in self.observers: try: func(args[0], None, None) - print("setting", widget, widget.font_name, "->", self.font_name) - widget.font_name = self.font_name - for sub_widget in [getattr(widget, "_hint_lbl", None), getattr(widget, "_msg_lbl", None)]: # MDText - if sub_widget: - sub_widget.font_name = self.font_name + self.set_widget_font(widget) except ReferenceError: pass # proxy no longer exists for cb in self.callbacks: diff --git a/katrain/gui.kv b/katrain/gui.kv index b9f7f2a..47df17e 100644 --- a/katrain/gui.kv +++ b/katrain/gui.kv @@ -1,20 +1,15 @@ #:kivy 1.11.0 #:import i18n katrain.core.utils.i18n -#:set BUTTON_COLOR [0.23, 0.30, 0.35, 1] -#:set TOOLTIP_COLOR [0.43, 0.50, 0.65, 1] -#:set ICON_BUTTON_COLOR [0.71, 0.78, 0.81, 1] -#:set ICON_BUTTON_COLOR_DOWN [0.51, 0.58, 0.51, 1] - -#-- NEW - # TODO: refactor - #:set CP_SPACING 6 #:set CP_SMALL_SPACING 3 #:set CP_PADDING 6 #:import PLAYER_TYPES katrain.core.constants.PLAYER_TYPES +#:import PLAYER_HUMAN katrain.core.constants.PLAYER_HUMAN +#:import PLAYER_AI katrain.core.constants.PLAYER_AI + #:import RED katrain.gui.style.RED #:import WHITE katrain.gui.style.WHITE #:import BACKGROUND_COLOR katrain.gui.style.BACKGROUND_COLOR @@ -147,7 +142,6 @@ width: 1 : - text: self.values[0] if self.values else '' font_size: self.height * 0.5 sync_height_frac: 1.0 background_color: [*[c*255/88 for c in BACKGROUND_COLOR[:3]], 1] # compensate for texture @@ -173,59 +167,6 @@ size: image.size font_size: root.min_size * 1.2 / (1 + 1 * len(self.text)) - -# -- old widgets TODO: remove - -: # TODO update/remove - size_hint: None, None - size: self.texture_size - tooltip_font_size: dp(14) - padding: (2,2) - halign: 'center' - canvas.before: - Color: - rgba: TOOLTIP_COLOR - Rectangle: - size: self.size - pos: self.pos - Color: - rgba: BUTTON_COLOR - Line: - width: 1.1 - rectangle: (*self.pos,*self.size) - - - -: - button_color: ICON_BUTTON_COLOR - button_color_down: ICON_BUTTON_COLOR_DOWN - icon_margin: 0.15 * self.size[0] - icon: '' - icon_size: min(self.size[0]-2*self.icon_margin,self.height-2*self.icon_margin) - Image: - size: (root.icon_size or 0,root.icon_size or 0) # square - pos: [root.pos[i] + (root.size[i] - root.icon_size)/2 for i in [0,1]] if root.icon_size else [0,0] - source: root.icon - mipmap: True - - - -: - color: TEXT_COLOR - text_size: self.size # don't overflow these - halign: 'center' - valign: 'middle' - -: # popups - color: TEXT_COLOR - font_size: 0.45*self.height/self.num_lines - -: - font_size: self.height/10 - halign: 'center' - valign: 'middle' - multiline: True - : color: WHITE halign: 'center' @@ -327,8 +268,8 @@ : captures: 0 player: 'B' - player_type: 'Player' - player_subtype: 'Normal game' + player_type: 'player:human' + player_subtype: 'game:normal' background_color: BOX_BACKGROUND_COLOR if self.active else BACKGROUND_COLOR outline_color: BOX_BACKGROUND_COLOR outline_width: 2 @@ -342,11 +283,11 @@ orientation: 'vertical' spacing: CP_SPACING Label: - text: root.player_type + text: i18n._(root.player_type) size_hint: 1,0.55 font_size: 0.8 * self.height Label: - text: root.player_subtype + text: i18n._(root.player_subtype) size_hint: 1,0.45 font_size: 0.7 * self.height @@ -585,7 +526,7 @@ do_scroll_x: False scroll_type: ['bars'] bar_width: 5 - bar_color: BUTTON_COLOR + bar_color: LIGHTGREY label: label canvas.before: Color: @@ -874,8 +815,8 @@ spacing: CP_SPACING padding: 0, CP_PADDING player_type: player_type - player_subtype: player_subtype - player_subtype_label: player_subtype_label + player_subtype_human: player_subtype_human + player_subtype_ai: player_subtype_ai MDBoxLayout: size_hint: 0.25, 1 CircleWithText: @@ -893,19 +834,37 @@ I18NSpinner: id: player_type value_refs: PLAYER_TYPES - on_text: root.setup_options(*args) + on_select: root.setup_options(*args) MDBoxLayout: size_hint: 0.5, 1 - orientation: 'vertical' - spacing: CP_SPACING - Label: - text: 'Player Type' - id: player_subtype_label - font_size: self.height * 0.5 - I18NSpinner: - id: player_subtype - value_refs: [] - on_text: root.update_global_player_info() + MDBoxLayout: + orientation: 'vertical' + spacing: CP_SPACING + size_hint_x: 1 if root.mode != PLAYER_AI else 0.0001 + opacity:1 if root.mode != PLAYER_AI else 0 + Label: + text: i18n._("gametype") + font_size: self.height * 0.5 + BoxLayout: + I18NSpinner: + id: player_subtype_human + value_refs: [] + on_select: if root.mode==PLAYER_HUMAN: root.update_global_player_info() + MDBoxLayout: + orientation: 'vertical' + spacing: CP_SPACING + size_hint_x: 1 if root.mode == PLAYER_AI else 0.0001 + opacity: 1 if root.mode == PLAYER_AI else 0 + Label: + text: i18n._("aistrategy") + font_size: self.height * 0.5 + BoxLayout: + I18NSpinner: + id: player_subtype_ai + value_refs: [] + on_select: if root.mode==PLAYER_AI: root.update_global_player_info() + + : orientation: 'vertical' diff --git a/katrain/gui/controlspanel.py b/katrain/gui/controlspanel.py index 3dad15c..684e0bb 100644 --- a/katrain/gui/controlspanel.py +++ b/katrain/gui/controlspanel.py @@ -40,8 +40,8 @@ class ControlsPanel(BoxLayout): def update_players(self, *_args): for bw, player_info in self.katrain.players_info.items(): - self.players[bw].player_type = i18n._(player_info.player_type) - self.players[bw].player_subtype = i18n._(player_info.player_subtype) + self.players[bw].player_type = player_info.player_type + self.players[bw].player_subtype = player_info.player_subtype def set_status(self, msg, at_node=None): self.status_msg = msg @@ -64,7 +64,7 @@ class ControlsPanel(BoxLayout): self.active_comment_node = current_node if katrain.play_analyze_mode == MODE_PLAY and last_player_was_ai_playing_human: - if katrain.next_player_info.being_taught and current_node.children and current_node.children.auto_undo: + if katrain.next_player_info.being_taught and current_node.children and current_node.children[-1].auto_undo: self.active_comment_node = current_node.children[-1] elif current_node.parent: self.active_comment_node = current_node.parent diff --git a/katrain/gui/kivyutils.py b/katrain/gui/kivyutils.py index c5b17c7..13024f2 100644 --- a/katrain/gui/kivyutils.py +++ b/katrain/gui/kivyutils.py @@ -34,7 +34,7 @@ from kivymd.uix.button import BasePressedButton, BaseFlatButton from kivymd.uix.navigationdrawer import MDNavigationDrawer from kivy.core.text import Label as CoreLabel -from katrain.core.constants import GAME_TYPES +from katrain.core.constants import GAME_TYPES, AI_STRATEGIES_RECOMMENDED_ORDER, PLAYER_HUMAN, PLAYER_AI from katrain.core.utils import i18n # @@ -179,35 +179,49 @@ class BGBoxLayout(BoxLayout, BackgroundMixin): class I18NSpinner(Spinner): + __events__ = ["on_select"] sync_height_frac = NumericProperty(1.0) value_refs = ListProperty() + selected_index = NumericProperty(0) font_name = StringProperty(DEFAULT_FONT) def __init__(self, **kwargs): super().__init__(**kwargs) self.bind(size=self.update_dropdown_props, pos=self.update_dropdown_props, value_refs=self.i18n_values) self.i18n_values() + MDApp.get_running_app().bind(language=self.i18n_values) @property def selected(self): try: - selected = self.values.index(self.text) + selected = self.selected_index return selected, self.value_refs[selected], self.values[selected] except (ValueError, IndexError): return 0, "", "" + def on_text(self, _widget, text): + try: + new_index = self.values.index(text) + if new_index != self.selected_index: + self.selected_index = new_index + self.dispatch("on_select") + except (ValueError, IndexError): + pass + + def on_select(self, *args): + pass + def select_key(self, key): try: ix = self.value_refs.index(key) self.text = self.values[ix] - except: + except (ValueError, IndexError): pass def i18n_values(self, *_args): if self.value_refs: - selected = self.selected[0] self.values = [i18n._(ref) for ref in self.value_refs] - self.text = self.values[selected] + self.text = self.values[self.selected_index] self.font_name = i18n.font_name self.update_dropdown_props() @@ -230,29 +244,33 @@ class I18NSpinner(Spinner): class PlayerSetup(MDBoxLayout): player = OptionProperty("B", options=["B", "W"]) + mode = StringProperty("") def __init__(self, **kwargs): super().__init__(**kwargs) - Clock.schedule_once(self.setup_options, 0) + self.player_subtype_ai.value_refs = AI_STRATEGIES_RECOMMENDED_ORDER + self.player_subtype_human.value_refs = GAME_TYPES + self.setup_options() def setup_options(self, *_args): - self.player_subtype.clear_widgets() - if self.player_type.selected[0] == 0: # human - self.player_subtype_label.text = i18n._("gametype") - self.player_subtype.value_refs = GAME_TYPES - else: - self.player_subtype_label.text = i18n._("aistrategy") - self.player_subtype.value_refs = MDApp.get_running_app().gui.ai_strategies # TODO: - self.player_subtype.text = self.player_subtype.values[0] + if self.player_type.selected[1] == self.mode: + return + self.mode = self.player_type.selected[1] self.update_global_player_info() @property def player_type_dump(self): - return {"player_type": self.player_type.selected[1], "player_subtype": self.player_subtype.selected[1]} + if self.mode == PLAYER_AI: + return {"player_type": self.player_type.selected[1], "player_subtype": self.player_subtype_ai.selected[1]} + else: + return {"player_type": self.player_type.selected[1], "player_subtype": self.player_subtype_human.selected[1]} def update_widget(self, player_type, player_subtype): self.player_type.select_key(player_type) # should trigger setup options - self.player_subtype.select_key(player_subtype) # should trigger setup options + if self.mode == PLAYER_AI: + self.player_subtype_ai.select_key(player_subtype) # should trigger setup options + else: + self.player_subtype_human.select_key(player_subtype) # should trigger setup options def update_global_player_info(self): if self.parent and self.parent.update_global: @@ -358,7 +376,7 @@ class CollapsablePanel(MDBoxLayout): options=self.build_options, option_colors=self.build_options, options_height=self.build_options, option_active=self.build_options, options_spacing=self.build_options, ) self.bind(state=self.build, size_hint_y_open=self.build, height_open=self.build) - MDApp.get_running_app().bind(language=lambda *_: Clock.schedule_once(self.build_options,0)) + MDApp.get_running_app().bind(language=lambda *_: Clock.schedule_once(self.build_options, 0)) self.build_options() def build_options(self, *args, **kwargs): @@ -387,7 +405,7 @@ class CollapsablePanel(MDBoxLayout): self.header.add_widget(Label()) # spacer self.trigger_select(ix=None) else: - self.header.add_widget(Label(text=i18n._(self.closed_label), halign="right", height=self.options_height)) + self.header.add_widget(Label(text=i18n._(self.closed_label), font_name=i18n.font_name, halign="right", height=self.options_height)) self.header.add_widget(self.open_close_button) super().clear_widgets() @@ -438,60 +456,10 @@ class StatsBox(MDBoxLayout, BackgroundMixin): player = StringProperty("") -# --- not checked - - -class ToolTipLabel(Label): - pass - - -class ToolTipBehavior(object): # TODO restyle - tooltip_text = StringProperty("") - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.tooltip = ToolTipLabel() - self.open = False - - def on_touch_up(self, touch): - inside = self.collide_point(*self.to_widget(*touch.pos)) - if inside and touch.button == "right" and self.tooltip_text: - if not self.open: - self.display_tooltip(touch.pos) - Clock.schedule_once(lambda _: self.set_position(touch.pos), 0) - elif not inside and self.open: - self.close_tooltip() - return super().on_touch_up(touch) - - def close_tooltip(self): - self.open = False - Window.remove_widget(self.tooltip) - - def on_size(self, *args): - mid = (self.pos[0] + self.width / 2, self.pos[1] + self.height / 2) - self.set_position(mid) - - def set_position(self, pos): - self.tooltip.pos = (pos[0] - self.tooltip.texture_size[0], pos[1]) - - def display_tooltip(self, pos): - self.open = True - self.tooltip.text = self.tooltip_text - Window.add_widget(self.tooltip) - - -class ScaledLightLabel(LightLabel, ToolTipBehavior): - num_lines = NumericProperty(1) - - class ClickableLabel(LeftButtonBehavior, Label): pass -class LightHelpLabel(ScaledLightLabel): - pass - - class ScrollableLabel(ScrollView, BackgroundMixin): __events__ = ["on_ref_press"] outline_color = ListProperty([0, 0, 0, 0]) # mixin not working for some reason @@ -502,74 +470,6 @@ class ScrollableLabel(ScrollView, BackgroundMixin): pass -class StyledButton(Button, LeftButtonBehavior, ToolTipBehavior): - button_color = ListProperty([]) - button_color_down = ListProperty([]) - radius = ListProperty((0,)) - - -class StyledToggleButton(StyledButton, ToggleButtonBehavior, ToolTipBehavior): - value = StringProperty("") - allow_no_selection = BooleanProperty(False) - - def _do_press(self): - if (self.last_touch and self.last_touch.button != "left") or (not self.allow_no_selection and self.state == "down"): - return - self._release_group(self) - self.state = "normal" if self.state == "down" else "down" - - -class ToggleButtonContainer(GridLayout): - __events__ = ("on_selection",) - - options = ListProperty([]) - labels = ListProperty([]) - tooltips = ListProperty(None) - selected = StringProperty("") - group = StringProperty(None) - autosize = BooleanProperty(True) - spacing = ListProperty((1, 1)) - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.rows = 1 - self.cols = len(self.options) - Clock.schedule_once(self._build, 0) - - def on_selection(self, *args): - pass - - def _build(self, _dt): - self.cols = len(self.options) - self.group = self.group or str(random.random()) - if not self.selected and self.options: - self.selected = self.options[0] - if len(self.labels) < len(self.options): - self.labels += self.options[len(self.labels) + 1 :] - - def state_handler(*args): - self.dispatch("on_selection") - - for i, opt in enumerate(self.options): - state = "down" if opt == self.selected else "normal" - tooltip = self.tooltips[i] if self.tooltips else None - self.add_widget(StyledToggleButton(group=self.group, text=self.labels[i], value=opt, state=state, on_press=state_handler, tooltip_text=tooltip,)) - Clock.schedule_once(self._size, 0) - - def _size(self, _dt): - if self.autosize: - for tb in self.children: - tb.size_hint = (tb.texture_size[0] + 10, 1) - - @property - def value(self): - for tb in self.children: - if tb.state == "down": - return tb.value - if self.options: - return self.options[0] - - def draw_text(pos, text, font_name=None, **kw): label = CoreLabel(text=text, bold=True, font_name=font_name or i18n.font_name, **kw) # label.refresh() diff --git a/katrain/gui/popups.py b/katrain/gui/popups.py index 4211f25..275fb17 100644 --- a/katrain/gui/popups.py +++ b/katrain/gui/popups.py @@ -7,11 +7,12 @@ from kivy.uix.anchorlayout import AnchorLayout from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Label from kivy.uix.popup import Popup +from kivymd.app import MDApp from kivymd.uix.boxlayout import MDBoxLayout from kivymd.uix.selectioncontrol import MDCheckbox from kivymd.uix.textfield import MDTextField -from katrain.core.constants import OUTPUT_ERROR, OUTPUT_DEBUG, OUTPUT_INFO +from katrain.core.constants import OUTPUT_ERROR, OUTPUT_DEBUG, OUTPUT_INFO, AI_DEFAULT, AI_CONFIG_DEFAULT, AI_STRATEGIES_RECOMMENDED_ORDER from katrain.core.engine import KataGoEngine from katrain.core.utils import i18n, find_package_resource from katrain.gui.kivyutils import I18NSpinner, BackgroundMixin @@ -249,21 +250,22 @@ class AIPopup(QuickConfigGui): def __init__(self, katrain): super().__init__(katrain) - self.ai_select.bind(text=self.build_ai_options) - self.ai_select.value_refs = katrain.ai_strategies + self.ai_select.value_refs = AI_STRATEGIES_RECOMMENDED_ORDER + selected_strategies = {p.strategy for p in katrain.players_info.values()} + config_strategy = list((selected_strategies - {AI_DEFAULT}) or {AI_CONFIG_DEFAULT})[0] + self.ai_select.select_key(config_strategy) self.build_ai_options() + self.ai_select.bind(text=self.build_ai_options) - def build_ai_options(self,*_args): + def build_ai_options(self, *_args): strategy = self.ai_select.selected[1] - print(strategy, "=strat", _args) mode_settings = self.katrain.config(f"ai/{strategy}") self.options_grid.clear_widgets() self.help_label.text = i18n._(strategy.replace("ai:", "aihelp:")) - for k, v in mode_settings.items(): + for k, v in sorted(mode_settings.items(), key=lambda kv: kv[0]): self.options_grid.add_widget(DescriptionLabel(text=k)) - self.options_grid.add_widget(wrap_anchor(LabelledFloatInput(text=str(v), input_property=f"{k}/{v}"))) + self.options_grid.add_widget(wrap_anchor(LabelledFloatInput(text=str(v), input_property=f"ai/{strategy}/{k}"))) for _ in range((self.max_options - len(mode_settings)) * 2): - print(_, self.options_grid.rows, self.options_grid.cols, len(self.options_grid.children)) self.options_grid.add_widget(Label()) @@ -294,4 +296,8 @@ class ConfigPopup(QuickConfigGui): class LoadSGFPopup(BoxLayout): - pass + def __init__(self, **kwargs): + super().__init__(**kwargs) + app = MDApp.get_running_app() + self.filesel.favorites = [(os.path.abspath(app.gui.config("general/sgf_load")), "SGF Load Dir"), (os.path.abspath(app.gui.config("general/sgf_save")), "SGF Save Dir")] + self.filesel.select_string = i18n._("Load File") diff --git a/katrain/gui/widgets/filebrowser.py b/katrain/gui/widgets/filebrowser.py index e31f523..92de7c3 100644 --- a/katrain/gui/widgets/filebrowser.py +++ b/katrain/gui/widgets/filebrowser.py @@ -45,7 +45,7 @@ from kivy.lang import Builder from kivy.utils import platform from kivy.clock import Clock import string -from os.path import sep, dirname, expanduser, isdir, join +from os.path import sep, dirname, expanduser, isdir, join, getmtime from os import walk from functools import partial @@ -56,7 +56,7 @@ if platform == "win": def last_modified_first(files, filesystem): - return sorted(f for f in files if filesystem.is_dir(f)) + sorted([f for f in files if not filesystem.is_dir(f)], key=lambda f: -os.path.getmtime(f)) + return sorted(f for f in files if filesystem.is_dir(f)) + sorted([f for f in files if not filesystem.is_dir(f)], key=lambda f: -getmtime(f)) def get_home_directory(): @@ -168,7 +168,7 @@ Builder.load_string( self.parent.browser.path = self.path if self.collide_point(*args[1].pos) and self.path else self.parent.browser.path on_is_open: self.is_open and self.parent.trigger_populate(self) -: +: orientation: 'vertical' spacing: 5 padding: [6, 6, 6, 6] @@ -224,12 +224,14 @@ Builder.load_string( TextInput: id: file_text text: (root.selection and (root._shorten_filenames(root.selection) if root.multiselect else root.selection[0])) or '' - hint_text: 'Filename' + hint_text: i18n._('Filename') multiline: False - Button: + height: '40dp' + AutoSizedRoundedRectangleButton: id: select_button + padding_x: 15 + height: '40dp' size_hint_x: None - width: metrics.dp(100) text: root.select_string on_release: root.dispatch('on_success') """ @@ -312,8 +314,8 @@ class LinkTree(TreeView): self.add_node(TreeLabel(text=path, path=parent + sep + path), node) -class FileBrowser(BoxLayout): - """FileBrowser class, see module documentation for more information. +class I18NFileBrowser(BoxLayout): + """I18NFileBrowser class, see module documentation for more information. """ __events__ = ("on_success", "on_submit") @@ -436,7 +438,7 @@ class FileBrowser(BoxLayout): pass def __init__(self, **kwargs): - super(FileBrowser, self).__init__(**kwargs) + super(I18NFileBrowser, self).__init__(**kwargs) Clock.schedule_once(self._post_init) def _post_init(self, *largs): diff --git a/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo index 5090fd2..1aedfa0 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 6017ec7..096b702 100644 --- a/katrain/i18n/locales/en/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/en/LC_MESSAGES/katrain.po @@ -402,7 +402,6 @@ msgid "update settings" msgstr "Update Settings" # ai settings from here - msgid "ai settings" msgstr "Advanced AI Settings" @@ -459,8 +458,9 @@ msgstr "KataGo" msgid "aihelp:default" msgstr "" -"Full strength KataGo AI. Strength is affected by `max_visits` and " -"`model` in the general settings `engine` section and engine configuration file. No options are available here." +"Full strength KataGo AI. Strength is affected by `max_visits` and `model` " +"in the general settings `engine` section and engine configuration file. No " +"options are available here." msgid "ai:jigo" msgstr "KataJigo" @@ -516,7 +516,7 @@ msgstr "" "policy value is above `pick_override` to avoid obvious mistakes." msgid "ai:p:local" -msgstr "Local Response Style" +msgstr "Local Style" msgid "aihelp:p:local" msgstr "" @@ -531,7 +531,8 @@ msgid "aihelp:p:tenuki" msgstr "" "Picks `pick_n + pick_frac * ` weighted away from the " "last move and plays the best one. Plays top move if policy value is above " -"`pick_override` to avoid obvious mistakes." +"`pick_override` to avoid obvious mistakes. A higher `stddev` makes it play " +"further away." msgid "ai:p:influence" msgstr "Influential Style" diff --git a/katrain/i18n/locales/haha/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/haha/LC_MESSAGES/katrain.mo index d543b5c..d989fb7 100644 Binary files a/katrain/i18n/locales/haha/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/haha/LC_MESSAGES/katrain.mo differ diff --git a/katrain/i18n/locales/haha/LC_MESSAGES/katrain.po b/katrain/i18n/locales/haha/LC_MESSAGES/katrain.po index e8b6868..3f6b96e 100644 --- a/katrain/i18n/locales/haha/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/haha/LC_MESSAGES/katrain.po @@ -6,637 +6,509 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -#. TODO -msgid "ruleset" -msgstr "ㅋㅋㅋ" +msgid "load sgf rewind" +msgstr "ㅋㅋRewind to start" -#. TODO -msgid "pass-button-text" -msgstr "ㅋㅋ" - -#. TODO msgid "Analyzing move..." -msgstr "ㅋㅋㅋㅋㅋ ㅋㅋ..." +msgstr "ㅋㅋAnalyzing move..." -#. TODO -msgid "chinese" -msgstr "ㅋㅋㅋㅋ" - -#. TODO -msgid "menu:aisettings" -msgstr "ㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "ai:scoreloss" -msgstr "ㅋㅋㅋ ㅋㅋ" - -#. TODO -msgid "Select AI" -msgstr "ㅋㅋㅋ ㅋ ㅋㅋㅋㅋ ㅋ ㅋㅋㅋㅋㅋ" - -#. TODO -msgid "engine:katago" -msgstr "ㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋㅋㅋ" - -#. TODO -msgid "aihelp:scoreloss" -msgstr "" -"ㅋㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋㅋㅋ. ㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋ/ㅋㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋㅋ " -"ㅋㅋㅋㅋ." - -#. TODO -msgid "general settings title" -msgstr "ㅋㅋㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "general:sgf_load" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "engine:fast_visits" -msgstr "ㅋㅋㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "ai:p:weighted" -msgstr "ㅋㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "wait-before-equalize" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ ㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋㅋ ㅋ ㅋㅋㅋㅋㅋ." - -#. TODO -msgid "save dots" -msgstr "ㅋㅋ ㅋ ㅋㅋ" - -#. TODO -msgid "menu:clocksettings" -msgstr "ㅋㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "Starting default Kata failed" -msgstr "" -"ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋㅋㅋ '{ㅋㅋㅋㅋ}' ㅋㅋㅋ ㅋㅋ ㅋㅋㅋ {ㅋㅋㅋ}. ㅋ ㅋ ㅋㅋㅋ, ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋ ㅋ ㅋㅋ ㅋㅋ " -"ㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋ, ㅋㅋ ㅋㅋ ㅋ ㅋ ㅋㅋ ㅋㅋ ㅋ 'ㅋㅋㅋ/ㅋㅋㅋ' ㅋㅋㅋㅋ." - -#. TODO -msgid "teacher settings" -msgstr "ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "gametype" -msgstr "ㅋㅋ ㅋㅋ" - -#. TODO -msgid "dot color" -msgstr "ㅋㅋㅋ (ㅋㅋㅋ)" - -#. TODO -msgid "tab:notes" -msgstr "ㅋㅋㅋ" - -#. TODO -msgid "komi" -msgstr "ㅋㅋ" - -#. TODO -msgid "menu:settings" -msgstr "ㅋㅋㅋㅋ & ㅋㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "general:debug_level" -msgstr "ㅋㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "korean" -msgstr "ㅋㅋㅋ" - -#. TODO -msgid "ai:policy" -msgstr "ㅋㅋㅋ" - -#. TODO -msgid "ai:p:pick" -msgstr "ㅋㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "handicap" -msgstr "ㅋㅋㅋㅋ" - -#. TODO -msgid "engine:wide_root_noise:hint" -msgstr "" -"ㅋㅋ 0.02-0.1 ㅋ ㅋㅋ\n" -"ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "player:type" -msgstr "ㅋㅋㅋ" - -#. TODO -msgid "general:debug_level:hint" -msgstr "ㅋㅋ ㅋ 1 ㅋㅋ ㅋㅋㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "engine:config" -msgstr "ㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋ" - -#. TODO -msgid "Info:score" -msgstr "ㅋㅋㅋ: {ㅋㅋㅋ}" - -#. TODO -msgid "Info:AI thoughts" -msgstr "ㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ: {ㅋㅋㅋㅋ}" - -#. TODO -msgid "update ai settings" -msgstr "ㅋㅋㅋ ㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "num undos" -msgstr "ㅋㅋㅋ ㅋ ㅋㅋㅋ" - -#. TODO -msgid "ai:default" -msgstr "ㅋㅋㅋ" - -#. TODO -msgid "Info:point loss" -msgstr "ㅋㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋ: {ㅋㅋㅋ_ㅋㅋ:.1ㅋ}" - -#. TODO -msgid "ai:p:tenuki" -msgstr "ㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "menu:save" -msgstr "ㅋㅋ ㅋㅋ" - -#. TODO -msgid "analysis:sweep" -msgstr "ㅋㅋ ㅋㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "update settings" -msgstr "ㅋㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "board size" -msgstr "ㅋㅋㅋ ㅋㅋ" - -#. TODO -msgid "tab:points" -msgstr "ㅋㅋㅋ ㅋㅋ" - -#. TODO -msgid "aihelp:p:weighted" -msgstr "" -"ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋㅋㅋㅋ, ㅋㅋ `ㅋㅋㅋ_ㅋㅋ` ㅋㅋㅋㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋㅋ. " -"ㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ `ㅋㅋㅋ` ㅋ.`ㅋㅋ_ㅋㅋㅋㅋ` ㅋㅋㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋㅋ, " -"ㅋㅋ `ㅋㅋㅋ_ㅋㅋㅋ` ㅋㅋㅋㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋ ㅋㅋㅋㅋ." - -#. TODO -msgid "engine:katago:hint" -msgstr "ㅋㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋㅋ" - -#. TODO -msgid "Info:best move" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋㅋㅋㅋ ㅋㅋ ㅋㅋ" - -#. TODO -msgid "menu:support" -msgstr "ㅋㅋㅋㅋ" - -#. TODO -msgid "menu:lang" -msgstr "ㅋㅋㅋㅋ" - -#. TODO -msgid "aistrategy" -msgstr "ㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "load sgf title" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋ ㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "aihelp:p:local" -msgstr "" -"ㅋㅋㅋ `ㅋㅋ_ㅋ + ㅋㅋ_ㅋㅋ * <ㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋ>` ㅋㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ. ㅋㅋㅋ" -" ㅋㅋ ㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋ `ㅋㅋ_ㅋㅋㅋㅋ` ㅋ ㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ." - -#. TODO -msgid "analysis:policy" -msgstr "" -"ㅋㅋㅋ\n" -"ㅋㅋㅋ" - -#. TODO -msgid "Info:top move" -msgstr "ㅋㅋㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ {ㅋㅋ_ㅋㅋ} ({ㅋㅋㅋ})." - -#. TODO -msgid "Info:policy rank" -msgstr "ㅋㅋ ㅋㅋ #{ㅋㅋ} ㅋㅋㅋㅋㅋ ㅋ ㅋㅋㅋ ({ㅋㅋㅋㅋㅋㅋ:.2%})." - -#. TODO -msgid "analysis:equalize" -msgstr "ㅋㅋㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "No analysis available" -msgstr "ㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋㅋ" - -#. TODO -msgid "show last n dots" -msgstr "" -"ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ\n" -"<ㅋㅋ ㅋㅋ> ㅋㅋㅋ" - -#. TODO -msgid "stats:pointsgained" -msgstr "ㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "Info:policy best" -msgstr "ㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ {ㅋㅋ} ({ㅋㅋㅋㅋㅋㅋ:.1%})." - -#. TODO -msgid "tab:score" -msgstr "ㅋㅋㅋ" - -#. TODO -msgid "Copied SGF to clipboard." -msgstr "ㅋㅋㅋ ㅋㅋ ㅋ ㅋㅋㅋㅋㅋ." - -#. TODO -msgid "ai:p:territory" -msgstr "ㅋㅋㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "btn:Play" -msgstr "ㅋㅋ" - -#. TODO -msgid "Failed to load SGF" -msgstr "ㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋ. ㅋㅋㅋ ㅋㅋㅋ: {ㅋㅋㅋ}" - -#. TODO -msgid "sgf written" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ ㅋ {ㅋㅋ_ㅋㅋ}" - -#. TODO -msgid "tab:winrate" -msgstr "ㅋㅋ ㅋㅋ" - -#. TODO -msgid "point loss threshold" -msgstr "ㅋㅋㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋㅋ" - -#. TODO -msgid "player:human" -msgstr "ㅋㅋㅋ" - -#. TODO -msgid "ai:jigo" -msgstr "ㅋㅋㅋㅋ" - -#. TODO -msgid "closedlabel:info¬es" -msgstr "ㅋㅋ ㅋㅋ & ㅋㅋㅋ" - -#. TODO -msgid "SGF start message" -msgstr "" -"ㅋㅋㅋ ㅋㅋㅋ 'ㅋ' ㅋㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋㅋㅋ ㅋ ㅋㅋㅋ, ㅋㅋㅋ ㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ 0.5 " -"ㅋㅋㅋ" - -#. TODO -msgid "engine:max_time" -msgstr "ㅋㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "aga" -msgstr "ㅋㅋ" - -#. TODO -msgid "engine:time:hint" -msgstr "ㅋㅋ ㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "update timer" -msgstr "ㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "game:teach" -msgstr "ㅋㅋㅋㅋ ㅋㅋ" - -#. TODO -msgid "byoyomi length" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋ-ㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "new game" -msgstr "ㅋㅋㅋ ㅋㅋ ㅋㅋ" - -#. TODO -msgid "japanese" -msgstr "ㅋㅋㅋㅋ" - -#. TODO -msgid "menu:teachsettings" -msgstr "ㅋㅋㅋㅋ ㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "closedlabel:scoregraph" -msgstr "ㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "engine settings" -msgstr "ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "stats:score" -msgstr "ㅋㅋㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "engine:wide_root_noise" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋㅋ (ㅋㅋㅋㅋㅋ ㅋㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋㅋㅋ)" - -#. TODO -msgid "btn:Analysis" -msgstr "ㅋㅋㅋㅋ" - -#. TODO -msgid "Info:PV" -msgstr "ㅋ: {ㅋ}" - -#. TODO -msgid "ai:p:local" -msgstr "ㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "analysis:dots" -msgstr "" -"ㅋㅋ\n" -"ㅋㅋ" - -#. TODO -msgid "show ai dots" -msgstr "" -"ㅋㅋ ㅋㅋ/ㅋㅋ ㅋㅋㅋㅋ\n" -" ㅋㅋ ㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "board-game-end" -msgstr "" -"ㅋㅋ\n" -"ㅋㅋ" - -#. TODO -msgid "SGF Notes Hint" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋㅋ." - -#. TODO -msgid "file not found" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "general:anim_pv_time" -msgstr "ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋㅋㅋㅋ ㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "menu:newgame" -msgstr "ㅋㅋ ㅋㅋ" - -#. TODO -msgid "analysis:topmoves" -msgstr "" -"ㅋㅋ\n" -"ㅋㅋㅋ" - -#. TODO -msgid "menu:playersetup" -msgstr "ㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "restarting engine" -msgstr "ㅋㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ." - -#. TODO -msgid "ai-ponder" -msgstr "" -"ㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋㅋ, ㅋㅋㅋ ㅋㅋ ㅋㅋㅋ (ㅋㅋ) ㅋ ㅋㅋㅋ (ㅋㅋㅋㅋㅋ ㅋㅋㅋㅋ). ㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ " -"ㅋㅋㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋ." - -#. TODO -msgid "stats:pointslost" -msgstr "ㅋㅋㅋ ㅋㅋ" - -#. TODO -msgid "byoyomi periods" -msgstr "ㅋㅋㅋ ㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "closedlabel:movestats" -msgstr "ㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "avoids replaying" -msgstr "" -"ㅋㅋㅋ ㅋㅋㅋㅋㅋ\n" -"ㅋㅋㅋㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "player:ai" -msgstr "ㅋ" - -#. TODO -msgid "menu:load" -msgstr "ㅋㅋ ㅋㅋ" - -#. TODO -msgid "load sgf fast analysis" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "analysis:territory" -msgstr "" -"ㅋㅋㅋㅋ\n" -"ㅋㅋㅋㅋㅋ" - -#. TODO -msgid "aihelp:p:pick" -msgstr "" -"ㅋㅋㅋ `ㅋㅋ_ㅋ + ㅋㅋ_ㅋㅋ * <ㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋ>` ㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ. ㅋㅋ `ㅋㅋ_ㅋㅋ=1` ㅋㅋㅋ" -" ㅋㅋ ㅋㅋ `ㅋㅋㅋ` ㅋ. ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋ `ㅋㅋ_ㅋㅋㅋㅋ` ㅋ ㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ." - -#. TODO -msgid "tab:info" -msgstr "ㅋㅋ" - -#. TODO -msgid "engine:model" -msgstr "ㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋ" - -#. TODO -msgid "board-pass" -msgstr "ㅋㅋ" - -#. TODO -msgid "show dots" -msgstr "ㅋㅋ ㅋㅋ" - -#. TODO -msgid "aihelp:default" -msgstr "" -"ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋ. ㅋㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋㅋ ㅋ `ㅋㅋ_ㅋㅋㅋ` ㅋㅋ `ㅋㅋㅋ` ㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ `ㅋㅋㅋ` " -"ㅋㅋㅋㅋ." - -#. TODO -msgid "timer settings" -msgstr "ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "game:normal" -msgstr "ㅋㅋㅋ ㅋㅋ" - -#. TODO -msgid "non square board hint" -msgstr "ㅋㅋ ㅋ:ㅋ (ㅋ.ㅋ. 19:9) ㅋㅋ ㅋㅋ-ㅋㅋㅋ ㅋㅋ" - -#. TODO msgid "ai:p:influence" -msgstr "ㅋㅋㅋㅋㅋㅋ ㅋㅋㅋ" +msgstr "ㅋㅋInfluential Style" -#. TODO -msgid "analysis:aimove" -msgstr "ㅋㅋㅋ ㅋ ㅋㅋ" +msgid "timer settings" +msgstr "ㅋㅋAdjust Timer Settings" -#. TODO -msgid "stats:winrate" -msgstr "ㅋㅋ ㅋㅋ" - -#. TODO msgid "analysis:nextmoves" msgstr "" -"ㅋㅋ\n" -"ㅋㅋㅋ" +"ㅋㅋNext\n" +"Moves" -#. TODO -msgid "undo-button-text" -msgstr "ㅋㅋ" +msgid "dot color" +msgstr "ㅋㅋColour (fixed)" -#. TODO -msgid "Engine died unexpectedly" -msgstr "ㅋㅋㅋ ㅋㅋ ㅋㅋㅋㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ, ㅋㅋㅋㅋ ㅋㅋ ㅋ ㅋㅋ ㅋ ㅋㅋㅋ: {ㅋㅋㅋ}" +msgid "ai:p:pick" +msgstr "ㅋㅋBlinded Policy" -#. TODO -msgid "Info:winrate" -msgstr "ㅋㅋ ㅋㅋ: {ㅋㅋㅋㅋ}" +msgid "engine:fast_visits" +msgstr "ㅋㅋMaximum number of visits in fast analysis" -#. TODO -msgid "aihelp:policy" -msgstr "" -"ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋ, ㅋㅋㅋㅋ ㅋㅋ ㅋㅋㅋㅋ. ㅋㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋㅋ ㅋ `ㅋㅋㅋ` ㅋ ㅋㅋㅋ " -"ㅋㅋㅋㅋ. ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋ." +msgid "Failed to load SGF" +msgstr "ㅋㅋFailed to load SGF. Parse Error: {error}" -#. TODO -msgid "New Game title" -msgstr "ㅋㅋㅋ ㅋㅋ ㅋㅋ" +msgid "board-pass" +msgstr "ㅋㅋpass" -#. TODO -msgid "aihelp:p:tenuki" -msgstr "" -"ㅋㅋㅋ `ㅋㅋ_ㅋ + ㅋㅋ_ㅋㅋ * <ㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋ>` ㅋㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ. " -"ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋ `ㅋㅋ_ㅋㅋㅋㅋ` ㅋ ㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ." +msgid "general settings title" +msgstr "ㅋㅋGeneral and Engine Settings" -#. TODO -msgid "Info:undo predicted PV" -msgstr "ㅋㅋㅋㅋㅋ ㅋㅋㅋ-ㅋ: {ㅋ}" - -#. TODO -msgid "engine:max_visits" -msgstr "ㅋㅋㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "general:sgf_save" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋㅋ" - -#. TODO -msgid "strength:kyu" -msgstr "ㅋ" - -#. TODO -msgid "general settings" -msgstr "ㅋㅋㅋㅋ ㅋㅋㅋㅋ" - -#. TODO -msgid "aihelp:jigo" -msgstr "ㅋㅋ ㅋㅋ ㅋ ㅋㅋ ㅋ ㅋ ㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋ (ㅋㅋㅋㅋ 0.5), ㅋㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋㅋㅋ." - -#. TODO -msgid "Info:teaching undo" -msgstr "ㅋㅋ ㅋㅋ ㅋㅋㅋㅋㅋㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋㅋ ㅋㅋ." - -#. TODO -msgid "strength:dan" -msgstr "ㅋ" - -#. TODO -msgid "lock ai when playing" -msgstr "" -"ㅋㅋㅋㅋ ㅋㅋㅋㅋ\n" -" ㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋ" - -#. TODO msgid "aihelp:p:influence" msgstr "" -"ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋ `ㅋㅋㅋㅋㅋ` ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ.ㅋㅋㅋㅋ `ㅋㅋ_ㅋㅋㅋ` ㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ" -" ㅋㅋ ㅋㅋ. ㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋ 'ㅋㅋㅋㅋ' ㅋㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋ." +"ㅋㅋPicks moves biased above the `threshold` line and plays the best " +"one.Increase `line_weight` to penalize moves near the edge more. Stops " +"strategy after the 'endgame' fraction of the board is filled." -#. TODO -msgid "Starting Kata failed" -msgstr "" -"ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋㅋㅋ '{ㅋㅋㅋㅋ}' ㅋㅋㅋ ㅋㅋ ㅋㅋㅋ {ㅋㅋㅋ}. ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ 'ㅋㅋㅋ' ㅋㅋㅋ ㅋㅋㅋ 'ㅋㅋㅋ'" -" ㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋㅋㅋ." +msgid "player:human" +msgstr "ㅋㅋHuman" -#. TODO -msgid "update teacher" -msgstr "ㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋㅋ" +msgid "komi" +msgstr "ㅋㅋKomi" + +msgid "Info:teaching undo" +msgstr "ㅋㅋMove was automatically undone in teaching mode." + +msgid "file not found" +msgstr "ㅋㅋPath does not exist" + +msgid "analysis:sweep" +msgstr "ㅋㅋFast analysis of all moves" + +msgid "sgf written" +msgstr "ㅋㅋSGF with analysis written to {file_name}" + +msgid "analysis:equalize" +msgstr "ㅋㅋEqualize visits of potential moves" + +msgid "menu:load" +msgstr "ㅋㅋLoad Game" + +msgid "ai:policy" +msgstr "ㅋㅋPolicy" + +msgid "ruleset" +msgstr "ㅋㅋRules" + +msgid "Info:point loss" +msgstr "ㅋㅋEstimated point loss: {points_lost:.1f}" + +msgid "menu:support" +msgstr "ㅋㅋSupport" + +msgid "point loss threshold" +msgstr "ㅋㅋPoint loss greater than" + +msgid "engine:max_visits" +msgstr "ㅋㅋMaximum number of visits in analysis" + +msgid "ai:default" +msgstr "ㅋㅋKataGo" + +msgid "general:anim_pv_time" +msgstr "ㅋㅋTime between moves when animating a sequence" + +msgid "tab:info" +msgstr "ㅋㅋInfo" + +msgid "player:type" +msgstr "ㅋㅋPlayer" -#. TODO msgid "clear cache" -msgstr "ㅋㅋㅋ ㅋㅋㅋ" +msgstr "ㅋㅋClear Cache" + +msgid "ai:jigo" +msgstr "ㅋㅋKataJigo" + +msgid "Info:policy best" +msgstr "ㅋㅋTop policy move was {move} ({probability:.1%})." + +msgid "Info:undo predicted PV" +msgstr "ㅋㅋPredicted follow-up: {pv}" + +msgid "menu:manual" +msgstr "ㅋㅋManual" + +msgid "aihelp:p:tenuki" +msgstr "" +"ㅋㅋPicks `pick_n + pick_frac * ` weighted away from " +"the last move and plays the best one. Plays top move if policy value is " +"above `pick_override` to avoid obvious mistakes. A higher `stddev` makes it " +"play further away." + +msgid "aihelp:p:weighted" +msgstr "" +"ㅋㅋPlays moves according to policy probability, with `weaken_fac` influencing" +" how much more likely weaker moves are played. A high negative value plays " +"like the `Policy` AI.`pick_override` determines when top move is chosen " +"without randomness, and `lower_bound` determines the lower bound policy " +"value that is allowed." + +msgid "No analysis available" +msgstr "ㅋㅋNo analysis available" + +msgid "korean" +msgstr "ㅋㅋKorean" + +msgid "menu:newgame" +msgstr "ㅋㅋNew Game" + +msgid "closedlabel:info¬es" +msgstr "ㅋㅋMove Info & Notes" + +msgid "update timer" +msgstr "ㅋㅋUpdate Timer" + +msgid "general:debug_level:hint" +msgstr "ㅋㅋSet to 1 when reporting errors" + +msgid "menu:playersetup" +msgstr "ㅋㅋPlayer Setup" + +msgid "engine:katago:hint" +msgstr "ㅋㅋLeave blank to use included executable" + +msgid "update teacher" +msgstr "ㅋㅋUpdate Feedback Settings" + +msgid "tab:score" +msgstr "ㅋㅋScore" + +msgid "avoids replaying" +msgstr "" +"ㅋㅋAvoids replaying\n" +"identical games" + +msgid "engine:katago" +msgstr "ㅋㅋPath to KataGo executable" + +msgid "gametype" +msgstr "ㅋㅋGame Type" + +msgid "analysis:extra" +msgstr "ㅋㅋDeeper analysis" + +msgid "aihelp:policy" +msgstr "" +"ㅋㅋPlays the top move from the policy network, without any reading. Strength " +"is mainly affected by `model` in engine settings. The opening moves setting " +"affects how many moves in the opening are more random." + +msgid "show last n dots" +msgstr "" +"ㅋㅋShow dots for the last\n" +" moves" + +msgid "ai:p:territory" +msgstr "ㅋㅋRespond Locally" + +msgid "lock ai when playing" +msgstr "" +"ㅋㅋDisable analysis\n" +" while in play mode" + +msgid "stats:pointslost" +msgstr "ㅋㅋPoints Lost" + +msgid "Select AI" +msgstr "ㅋㅋSelect AI strategy to configure" + +msgid "ai:scoreloss" +msgstr "ㅋㅋScore Loss" + +msgid "closedlabel:scoregraph" +msgstr "ㅋㅋScore Graph" + +msgid "menu:save" +msgstr "ㅋㅋSave Game" + +msgid "analysis:policy" +msgstr "" +"ㅋㅋPolicy\n" +"Moves" + +msgid "show ai dots" +msgstr "" +"ㅋㅋShow dots/SGF comments\n" +" for AI players" + +msgid "board-game-end" +msgstr "" +"ㅋㅋgame\n" +"end" + +msgid "new game" +msgstr "ㅋㅋStart New Game" + +msgid "teacher settings" +msgstr "ㅋㅋEdit Teacher Settings" + +msgid "tab:points" +msgstr "ㅋㅋPoint Loss" + +msgid "menu:lang" +msgstr "ㅋㅋLanguage" + +msgid "engine settings" +msgstr "ㅋㅋKataGo Engine Settings" + +msgid "show dots" +msgstr "ㅋㅋShow dots" + +msgid "aihelp:jigo" +msgstr "" +"ㅋㅋWill try to win by a set amount of points (default 0.5), without further " +"restrictions." + +msgid "btn:Analysis" +msgstr "ㅋㅋAnalysis" + +msgid "num undos" +msgstr "ㅋㅋNumber of undos" + +msgid "Engine died unexpectedly" +msgstr "" +"ㅋㅋEngine died unexpectedly without sending output, possibly due to out of " +"memory: {error}" + +msgid "analysis:topmoves" +msgstr "" +"ㅋㅋTop\n" +"Moves" + +msgid "engine:config" +msgstr "ㅋㅋPath to KataGo config file" + +msgid "menu:aisettings" +msgstr "ㅋㅋAI Settings" + +msgid "ai:p:tenuki" +msgstr "ㅋㅋTenuki Style" + +msgid "Starting default Kata failed" +msgstr "" +"ㅋㅋStarting KataGo with command '{command}' failed with error {error}. If on " +"MacOS, see the manual on how to use brew to install katago first, and add it" +" to your path or 'engine/katago' setting." + +msgid "player:ai" +msgstr "ㅋㅋAI" + +msgid "stats:winrate" +msgstr "ㅋㅋWin Rate" + +msgid "chinese" +msgstr "ㅋㅋChinese" + +msgid "handicap" +msgstr "ㅋㅋHandicap" + +msgid "btn:Analyze" +msgstr "ㅋㅋAnalyze" + +msgid "tab:notes" +msgstr "ㅋㅋNotes" + +msgid "Info:top move" +msgstr "ㅋㅋPredicted top move was {top_move} ({score})." + +msgid "board size" +msgstr "ㅋㅋBoard size" + +msgid "byoyomi periods" +msgstr "ㅋㅋNumber of Periods" + +msgid "aga" +msgstr "ㅋㅋAGA" + +msgid "engine:wide_root_noise:hint" +msgstr "" +"ㅋㅋUse 0.02-0.1 to show\n" +"more possible moves" + +msgid "ai settings" +msgstr "ㅋㅋAdvanced AI Settings" + +msgid "Info:AI thoughts" +msgstr "ㅋㅋAI thought process: {thoughts}" + +msgid "Info:score" +msgstr "ㅋㅋScore: {score}" + +msgid "SGF start message" +msgstr "" +"ㅋㅋMoves marked 'X' indicate the top move according to KataGo, those with a " +"square are moves that lose less than 0.5 points" + +msgid "New Game title" +msgstr "ㅋㅋSetup New Game" + +msgid "general:sgf_save" +msgstr "ㅋㅋPath for saving SGF files" + +msgid "update ai settings" +msgstr "ㅋㅋUpdate AI Settings" + +msgid "aihelp:p:pick" +msgstr "" +"ㅋㅋPicks `pick_n + pick_frac * ` at random and plays " +"the best one. For `pick_frac=1` plays like the `Policy` AI. Plays top move " +"if policy value is above `pick_override` to avoid obvious mistakes." -#. TODO msgid "Failed to import from clipboard" msgstr "" -"ㅋㅋㅋ ㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋㅋㅋㅋ: {ㅋㅋㅋ}\n" -"ㅋㅋㅋㅋㅋ ㅋㅋㅋㅋ: {ㅋㅋㅋㅋ}..." +"ㅋㅋFailed to import game from clipboard: {error}\n" +"Clipboard contents: {contents}..." + +msgid "ai:p:local" +msgstr "ㅋㅋLocal Style" + +msgid "strength:kyu" +msgstr "ㅋㅋk" + +msgid "analysis:aimove" +msgstr "ㅋㅋForce AI Move" + +msgid "Info:PV" +msgstr "ㅋㅋPV: {pv}" + +msgid "Starting Kata failed" +msgstr "" +"ㅋㅋStarting KataGo with command '{command}' failed with error {error}. Please" +" make sure the 'katago' value under 'engine' in settings points to the " +"correct KataGo executable." + +msgid "restarting engine" +msgstr "ㅋㅋRestarting Engine after Settings Change." + +msgid "ai:p:weighted" +msgstr "ㅋㅋPolicy Weighted" + +msgid "tab:winrate" +msgstr "ㅋㅋWin Rate" + +msgid "engine:time:hint" +msgstr "ㅋㅋTime in seconds" + +msgid "save dots" +msgstr "ㅋㅋSave in SGF" + +msgid "stats:pointsgained" +msgstr "ㅋㅋPoints Gained" + +msgid "game:teach" +msgstr "ㅋㅋTeaching Game" + +msgid "load sgf title" +msgstr "ㅋㅋLoad SGF File to Analyze" + +msgid "ai-ponder" +msgstr "" +"ㅋㅋThis simply indicates the Engine status, going from green (idle) to orange" +" (processing queries). It turns red when the engine has crashed or fails to " +"start." + +msgid "analysis:dots" +msgstr "" +"ㅋㅋShow\n" +"Dots" -#. TODO msgid "aihelp:p:territory" msgstr "" -"ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋㅋ ㅋㅋ `ㅋㅋㅋㅋㅋ` ㅋㅋ ㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ ㅋㅋ.ㅋㅋㅋㅋ `ㅋㅋ_ㅋㅋㅋ` ㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋ ㅋㅋ" -" ㅋㅋㅋ ㅋㅋ. ㅋㅋㅋ ㅋㅋㅋㅋ ㅋㅋㅋ ㅋㅋ 'ㅋㅋㅋㅋ' ㅋㅋㅋㅋ ㅋ ㅋㅋ ㅋㅋㅋ ㅋ ㅋㅋㅋ." +"ㅋㅋPicks moves biased below the `threshold` line and plays the best " +"one.Increase `line_weight` to penalize moves near the center more. Stops " +"strategy after the 'endgame' fraction of the board is filled." -#. TODO -msgid "analysis:extra" -msgstr "ㅋㅋㅋ ㅋㅋㅋㅋ" +msgid "menu:teachsettings" +msgstr "ㅋㅋTeaching Game Settings" -#. TODO -msgid "load sgf rewind" -msgstr "ㅋㅋㅋ ㅋ ㅋㅋㅋ" +msgid "closedlabel:movestats" +msgstr "ㅋㅋMove Stats" -#. TODO -msgid "btn:Analyze" -msgstr "ㅋㅋㅋㅋ" +msgid "aihelp:default" +msgstr "" +"ㅋㅋFull strength KataGo AI. Strength is affected by `max_visits` and `model`" +" in the general settings `engine` section and engine configuration file. No " +"options are available here." -#. TODO -msgid "menu:homepage" -msgstr "ㅋㅋㅋㅋ" +msgid "stats:score" +msgstr "ㅋㅋEstimated Score" -#. TODO -msgid "menu:manual" -msgstr "ㅋㅋㅋ" +msgid "byoyomi length" +msgstr "ㅋㅋTime per Byo-yomi Period" + +msgid "aihelp:p:local" +msgstr "" +"ㅋㅋPicks `pick_n + pick_frac * ` weighted towards the " +"last move and plays the best one. Plays top move if policy value is above " +"`pick_override` to avoid obvious mistakes." + +msgid "Info:best move" +msgstr "ㅋㅋMove was predicted best move" + +msgid "general:debug_level" +msgstr "ㅋㅋDebug level in the console" + +msgid "Info:policy rank" +msgstr "ㅋㅋMove was #{rank} according to policy ({probability:.2%})." + +msgid "menu:clocksettings" +msgstr "ㅋㅋTimer Settings" + +msgid "game:normal" +msgstr "ㅋㅋNormal Game" + +msgid "engine:max_time" +msgstr "ㅋㅋMaximum time for analysis" + +msgid "analysis:territory" +msgstr "" +"ㅋㅋExpected\n" +"Territory" + +msgid "japanese" +msgstr "ㅋㅋJapanese" + +msgid "aistrategy" +msgstr "ㅋㅋAI Strategy" + +msgid "wait-before-equalize" +msgstr "" +"ㅋㅋWait for initial analysis to complete before requesting a refinement." + +msgid "non square board hint" +msgstr "ㅋㅋUse x:y (e.g. 19:9) for non-square size" + +msgid "menu:settings" +msgstr "ㅋㅋGeneral & Engine Settings" + +msgid "engine:model" +msgstr "ㅋㅋPath to KataGo model file" + +msgid "general:sgf_load" +msgstr "ㅋㅋPath for loading SGF files" + +msgid "aihelp:scoreloss" +msgstr "" +"ㅋㅋPlays moves that lead to more points lost with lower probability. Likely " +"to play more varied/weaker with higher visits settings." + +msgid "general settings" +msgstr "ㅋㅋGeneral Settings" + +msgid "btn:Play" +msgstr "ㅋㅋPlay" + +msgid "engine:wide_root_noise" +msgstr "ㅋㅋWide root noise (increases variety of moves considered)" + +msgid "Info:winrate" +msgstr "ㅋㅋWin rate: {winrate}" + +msgid "Copied SGF to clipboard." +msgstr "ㅋㅋCopied SGF to clipboard." + +msgid "strength:dan" +msgstr "ㅋㅋd" + +msgid "update settings" +msgstr "ㅋㅋUpdate Settings" + +msgid "load sgf fast analysis" +msgstr "ㅋㅋUse fast analysis" + +msgid "undo-button-text" +msgstr "ㅋㅋUndo" + +msgid "SGF Notes Hint" +msgstr "ㅋㅋYour SGF notes for this position here." + +msgid "pass-button-text" +msgstr "ㅋㅋPass" diff --git a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo index 2bd4af8..879cc8e 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 892e0c7..a0f2485 100644 --- a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po @@ -138,7 +138,7 @@ msgstr "" msgid "Engine died unexpectedly" msgstr "" "Engine died unexpectedly without sending output, possibly due to out of " -"memory: {e}" +"memory: {error}" #. TODO msgid "analysis:territory" @@ -236,29 +236,23 @@ msgstr "Teaching Game" msgid "aistrategy" msgstr "AI Strategy" -#. TODO msgid "game:normal" -msgstr "Normal Game" +msgstr "일반 게임" -#. TODO msgid "player:human" -msgstr "AI" +msgstr "인간" -#. TODO msgid "player:type" -msgstr "Player" +msgstr "인간/인곡지능" -#. TODO msgid "gametype" -msgstr "Game Type" +msgstr "게임 종류" -#. TODO msgid "player:ai" -msgstr "AI" +msgstr "인곡지능" -#. TODO msgid "game:teach" -msgstr "Teaching Game" +msgstr "교직 게임" #. TODO msgid "menu:clocksettings" @@ -650,3 +644,7 @@ msgstr "Homepage" #. TODO msgid "menu:manual" msgstr "Manual" + +#. TODO +msgid "ai settings" +msgstr "Advanced AI Settings" diff --git a/katrain/i18n/locales/nl/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/nl/LC_MESSAGES/katrain.mo index aa9e0ef..d5a07e1 100644 Binary files a/katrain/i18n/locales/nl/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/nl/LC_MESSAGES/katrain.mo differ diff --git a/katrain/i18n/locales/nl/LC_MESSAGES/katrain.po b/katrain/i18n/locales/nl/LC_MESSAGES/katrain.po index 3e64d20..b4d29ea 100644 --- a/katrain/i18n/locales/nl/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/nl/LC_MESSAGES/katrain.po @@ -274,7 +274,7 @@ msgstr "Normal Game" #. TODO msgid "player:human" -msgstr "AI" +msgstr "Mens" #. TODO msgid "player:type" @@ -682,3 +682,7 @@ msgstr "Homepage" #. TODO msgid "menu:manual" msgstr "Manual" + +#. TODO +msgid "ai settings" +msgstr "Advanced AI Settings" diff --git a/katrain/popups.kv b/katrain/popups.kv index 5622541..67ec164 100644 --- a/katrain/popups.kv +++ b/katrain/popups.kv @@ -1,6 +1,7 @@ #:kivy 1.11.0 #:import i18n katrain.core.utils.i18n - +#:import expanduser os.path.expanduser +#:import abspath os.path.abspath #:set CP_SPACING 6 #:set CP_SMALL_SPACING 3 @@ -14,7 +15,7 @@ #:import TEXT_COLOR katrain.gui.style.TEXT_COLOR -#MDTextField: +# MDTextField: # hint_text: "Helper text on focus" # helper_text: "This will disappear when you click off" # helper_text_mode: "on_focus" @@ -22,7 +23,7 @@ : font_size: INPUT_FONT_SIZE - helper_text_mode: "on_focus" + helper_text_mode: "none" hint_text: "" helper_text: "" @@ -32,7 +33,6 @@ helper_text_mode: "on_error" helper_text: i18n._("file not found") - : font_size: INPUT_FONT_SIZE size_hint: 0.5, None @@ -67,7 +67,7 @@ : font_size: DESC_FONT_SIZE multiline: True - text_size: self.size + text_size: self.width, None halign: 'center' valign: 'center' @@ -101,13 +101,14 @@ AnchorLayout: LabelledFloatInput: input_property: "general/anim_pv_time" - helper_text: i18n._("engine:time:hint") + hint_text: i18n._("engine:time:hint") DescriptionLabel: text: i18n._("general:debug_level") AnchorLayout: LabelledIntInput: input_property: "general/debug_level" helper_text: i18n._("general:debug_level:hint") + helper_text_mode: "on_focus" Label: Label: Label: @@ -157,7 +158,7 @@ AnchorLayout: LabelledFloatInput: input_property: "engine/max_time" - helper_text: i18n._("engine:time:hint") + hint_text: i18n._("engine:time:hint") DescriptionLabel: text: i18n._("engine:wide_root_noise") AnchorLayout: @@ -272,7 +273,7 @@ id: boardsize input_property: 'game/size' size_hint: 0.5, None - hint_text:i18n._("non square board hint") + hint_text: i18n._("non square board hint") AnchorLayout: MDBoxLayout: adaptive_size: True @@ -360,15 +361,16 @@ BoxLayout: size_hint: 1,1 DescriptionLabel: - font_size: 1.25*DESC_FONT_SIZE + font_size: DESC_FONT_SIZE text: i18n._('Select AI') AnchorLayout: I18NSpinner: - size_hint: 1, 0.5 + size_hint: 0.8, 0.5 id: ai_select SmallDescriptionLabel: id: help_label size_hint: 1,1.25 + text: i18n._('') GridLayout: size_hint: 1,4 id: options_grid @@ -388,12 +390,6 @@ rewind: rewind filesel: filesel orientation: 'vertical' - I18NFileChooserListView: - id: filesel - multiselect: False - filters: ["*.sgf"] - path: "." - size_hint: 1,7 BoxLayout: orientation: 'horizontal' size_hint: 1,1 @@ -403,7 +399,6 @@ halign: 'right' LabelledCheckBox: id: fast - color: (0.95, 0.95, 0.95) size_hint: 0.05,1 DescriptionLabel: text: i18n._("load sgf rewind") @@ -412,12 +407,10 @@ LabelledCheckBox: id: rewind active: True - color: (0.95, 0.95, 0.95) size_hint: 0.05,1 - AnchorLayout: - size_hint: 1,1.5 - AutoSizedRoundedRectangleButton: - padding_x: 15 - size_hint: None,0.5 - text: i18n._("Load File") - + I18NFileBrowser: + id: filesel + multiselect: False + filters: ["*.sgf"] + path: "." + size_hint: 1,7 diff --git a/tests/test_ai.py b/tests/test_ai.py new file mode 100644 index 0000000..f70efa1 --- /dev/null +++ b/tests/test_ai.py @@ -0,0 +1,8 @@ +import pytest + +from katrain.core.constants import AI_STRATEGIES_RECOMMENDED_ORDER, AI_STRATEGIES + + +class TestAI: + def test_order(self): + assert set(AI_STRATEGIES_RECOMMENDED_ORDER) == set(AI_STRATEGIES) diff --git a/tests/test_board.py b/tests/test_board.py index b389e48..83839a3 100644 --- a/tests/test_board.py +++ b/tests/test_board.py @@ -1,11 +1,15 @@ import pytest from katrain.core.game import Game, IllegalMoveException, Move +from katrain.core.base_katrain import KaTrainBase, OUTPUT_INFO -class MockKaTrain: - def log(self, msg, lvl): - pass +class MockKaTrain(KaTrainBase): + pass + + +# def log(self, message, level=OUTPUT_INFO): +# pass class MockEngine: @@ -18,7 +22,7 @@ class TestBoard: return [c for c in b.chains if c] def test_merge(self): - b = Game(MockKaTrain(), MockEngine(), config={"init_size": 9}) + b = Game(MockKaTrain(), MockEngine()) b.play(Move.from_gtp("B9", player="B")) b.play(Move.from_gtp("A3", player="B")) b.play(Move.from_gtp("A9", player="B")) @@ -27,7 +31,7 @@ class TestBoard: assert 0 == len(b.prisoners) def test_collide(self): - b = Game(MockKaTrain(), MockEngine(), config={"init_size": 9}) + b = Game(MockKaTrain(), MockEngine()) b.play(Move.from_gtp("B9", player="B")) with pytest.raises(IllegalMoveException): b.play(Move.from_gtp("B9", player="W")) @@ -36,7 +40,7 @@ class TestBoard: assert 0 == len(b.prisoners) def test_capture(self): - b = Game(MockKaTrain(), MockEngine(), config={"init_size": 9}) + b = Game(MockKaTrain(), MockEngine()) b.play(Move.from_gtp("A2", player="B")) b.play(Move.from_gtp("B1", player="W")) b.play(Move.from_gtp("A1", player="W")) @@ -57,7 +61,7 @@ class TestBoard: assert 2 == len(b.prisoners) def test_snapback(self): - b = Game(MockKaTrain(), MockEngine(), config={"init_size": 9}) + b = Game(MockKaTrain(), MockEngine()) for move in ["C1", "D1", "E1", "C2", "D3", "E4", "F2", "F3", "F4"]: b.play(Move.from_gtp(move, player="B")) for move in ["D2", "E2", "C3", "D4", "C4"]: @@ -75,7 +79,7 @@ class TestBoard: assert 4 == len(b.prisoners) def test_ko(self): - b = Game(MockKaTrain(), MockEngine(), config={"init_size": 9}) + b = Game(MockKaTrain(), MockEngine()) for move in ["A2", "B1"]: b.play(Move.from_gtp(move, player="B"))