From 48f6166d48bb9199b45f7a49a7306cf83d379b2c Mon Sep 17 00:00:00 2001 From: Sander Land Date: Sat, 30 May 2020 17:58:28 +0200 Subject: [PATCH] lots --- i18n.py | 13 +- katrain/__main__.py | 18 +- katrain/config.json | 7 +- katrain/core/ai.py | 11 +- katrain/core/base_katrain.py | 2 +- katrain/core/constants.py | 35 +- katrain/core/utils.py | 24 +- katrain/gui.kv | 119 +- katrain/gui/controlspanel.py | 6 +- katrain/gui/kivyutils.py | 172 +-- katrain/gui/popups.py | 24 +- katrain/gui/widgets/filebrowser.py | 20 +- .../i18n/locales/en/LC_MESSAGES/katrain.mo | Bin 10146 -> 10287 bytes .../i18n/locales/en/LC_MESSAGES/katrain.po | 11 +- .../i18n/locales/haha/LC_MESSAGES/katrain.mo | Bin 12528 -> 10906 bytes .../i18n/locales/haha/LC_MESSAGES/katrain.po | 1076 ++++++++--------- .../i18n/locales/ko/LC_MESSAGES/katrain.mo | Bin 10576 -> 10663 bytes .../i18n/locales/ko/LC_MESSAGES/katrain.po | 24 +- .../i18n/locales/nl/LC_MESSAGES/katrain.mo | Bin 10508 -> 10559 bytes .../i18n/locales/nl/LC_MESSAGES/katrain.po | 6 +- katrain/popups.kv | 43 +- tests/test_ai.py | 8 + tests/test_board.py | 20 +- 23 files changed, 713 insertions(+), 926 deletions(-) create mode 100644 tests/test_ai.py 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 5090fd2ba658748e26f7e800554481ef90e94b39..1aedfa0717ef2aff40eaae16e4134c942317df0b 100644 GIT binary patch delta 2636 zcmYM!e@vBC9LMoPkb+_$KN1DxK}v{8Q7c&)P3F|10EdyS7UKpFa0lMY;Bt#bKFxG4 zBVi*~%a~SFR#S%ekFYYP&1nS_lTF&3E7F|Sh?@P;kFnk#@7ZDx&+DA?oFCuwJ$| zs144d#>diG<5Mt+^^HS89g0yqT4y`dpi)+kia3NyrDvb_qWTY@CLTh~^Cha^S=;`j zZJ$N;k7aeun~t6)UP(a{+>NTQMf#dYQ9F4Oqx#g= z$5ENOgc>)4TxM<&`Bx#4Srz#*RK3X7*PsSeq9U(F1=fbz!HcK`_h2Tzk6h+BHw83- z+Rz2m2CksS&7#J|DF1qCGFT-Qi%@TQ8LHz`))v$R-PWC`4`mO|!+z9+Zy}c%=EfT4 zIBNb0+kP3f(LYfciT7AVg%ni8S*RTrAeXt1o1Sk#Wo8rVc`a(97F1x{P$}Pq>OWxp z5V_2-t)D=RA49F@eNRCVUPh((Z`7G4Qqe?dsE*4~0}E|?3DVV+p#rJ4br;n?j0$)= zYFwXn02T1Z$o!s(P|z72i#C|ks0qG9EpQQ)%HM2#4z*wsH?n9}pq}4@TBr=Qp-ODU zdSr;1KppKgZoz-BO5gux4!(ef_i#Cmp(6YR^+R$U^Kmh=-i7z$1L#LBJZRe^xQhBu zxD=B)4~CjTRKJH%M^wRWCD!Bf`u=xQ$iZ0NpieLlNAV2m7p!+lbVvR8HuZz3mu@}V zQJ{}mYfw9_MP;xJwQx5oGq0jD_NMh9dYWj2f{tJmhw&SH0=wCUJclQ-a2Y=?IE&|T zh;MENcaf%37|u3kO$<9FjbqebU{^2UMkgB3Y1BJ0iF&uD9rC||!ml(avZUN-AUUXr z3z4-<336Pf8a2>G2ZN}<`%szKhx#lBZT%zE%Y4MPe}?SMjG{6%l}rBB;TjD}*&p`7 z9BSfZqEmY&DwX-jshHKMz$&cGcn9^DQD=V$b;hS`eFkr%9-kL|UWD54Mvp=Xg)OL; zuov~xjGz`CL#1pAwez1*U&Soy7cYic_0nad0xCzn#ICLHKn1iPm7$~76BtL``<8;< zfeBPf&Z8o`ib~aA$YoM)j|NnN%EUUci;0Ei%sDR2*3ss@sl^R>GLyc=hzHAe;?nI5-hkBV0 zV3K}dA{3PBuTUwwfa~#Uq$}Z2OoG$lhC=>8Q%59k;p2&EWes6ppx$k8${ukx+b8|u zC1?68GrIc6Gb&3RU!cKp15N&b>(mDWjsB+2c3;RJ3^)i3Sxmamx$)XyWtD|KlO_#=SYmt)BG_qdlYI8H~ zglt-kXg0Mh+#$CtifjaC(-O9DZMs;E+N#-7w5?g+Up!mx<^BBc|L1>sj~^`QiUy~% zliQ7dJNYZ*@9Sjc%r;H=-z<_(lh4Oj+`U*#{&UR6AMi3fhpCv7W){LsoQj30`bzg^ zReP9k&KkC=nMqZX2s&L5Vb>dTNSyA~DT zy#EZ$ZXuxw7I}l^s1EhWV~_Dt{gbEwx1j2Gq5|yn`UA*gpSXjlg$<+nkD>aHqjn^j z&e_awIV7}_nWzTyP+L`tig-DyL%r8GquRHj29BX7>O!^q(98S1d>GY!3^ndwsBzPn zEYJYCBvep_)Y)t#NV^Fq;~l7WOHmW7LTzCas@+D^kv)$(qTQZ9h}x+^RKJtRW25fb zOwM1CU-E)1W>LNv)u9R%c?~MCWvCUbMoqXL3-D#+u|2#LP!DQBeW(Q-L-iX*_4^5R zSN;yM|IK$w>rUX9wa>rwSJsDYxWz#c|z`J<@zZSGd&v3AeDgX-Unny(iX zU~rU#w)iyaOwXbQx`=9+NToW4P~`W_E2_dqtVNny59(;Y#0T&UMsY6Z zUcvnK5{WA)=tf0&0`&tjf-~_v&c-4}pO1H;Cf$&FQM(zaBf5@P zIo9GvT#Lo}{>MnXi>chC&oGYqIkU6ayHQ_SUj?|-e zs>$7q8fQD|>*&Csl|&bb`>}}dWefJ>G5i#FTyFLqF5z42!wR-%0Dr*+31$b2_2Vx z(uti2p+3hv&ljRDam34KA#1ZL)J`q-`jx1=)=auM|1=NeWyd$2!fC?a+*>zVc-5W5O{6f?nxD~bV;7$^XYz1no8j;6d;H7}} zqjusDYQTO}V8>Adjd=MP)Diyd_2*H6{DV4@aAaa9OHt$9h#V~zaTZf IsVJWIFR2^vv;Y7A 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 d543b5cfae76cdbc72ba908ec971b21fbe52b8b9..d989fb703fe92702292324f3cc0d999dbb9f1e17 100644 GIT binary patch literal 10906 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 JUCLdG{tM%t$b|p^ literal 12528 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 I3T@o{9~nN|yZ`_I 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 2bd4af8d669e14615cae98aa19869f11e6a6ea5d..879cc8e37922417751657c2d7ee094324f772191 100644 GIT binary patch delta 2726 zcmYk+drZ}39LMnoJlyXh1c7h>0o@=st?6>BF=&JqwNRKWR}7F$K?X>u%|nF!p^VhY zh7Pneq*@qymdfUasl`gH+`3_qETgu=X)4p)C|mCj=h?~~f3M$j{eGXzk8fV+FLC`m z-_d3G*Tes0{{QVz$(Zqw|BT^vf^q`hvId75lS(-a=VKm5<5~>DYK+8MjKbG#dxy0b z`MJzVo|<3?BXAVs@j8a%9n?hsU>rt-`N|on#Fn9QEx-j>j9N$~&cjyZXWDtF-#%n2 za~Ok&Z~AyqfC1ZZ9@TLK`I&1x)INbq(2HuHMJ4Fqq4pT0$~aLA%CYrNqQ(`YcH%|b zz6E25Z(O#+e$*BoMNK$}nsC^*U$O0Ds19DEPo?^WqK+~ib#%G5oR8YU z@<{ey1FNWDtZ71J+-A#NwtO7b?+ogUKSw406DqMkPz#tv1q`Ee5~d?n<_T0n&!QH% z9u==Div8D08mQ0&J5dvKpaLDoSUiWi{iCRUQ`SFGwtNctnTtHM<=>&& zr%-Y3;sSmDejM;-D&mnOO&jX04`LY(;By$j@)zSeOu=2K1bb0`a86)0Uc#j~jgMmz z`>eAsMb+104)$WAzW*jBh)-$DI_be-ZQhnK6U%x19w<1&5a za#TXwP`CYU%*Sq2pmB8IG-{`AqmJSMY5`GMzRR78Ol?+LHy}IbGHtwQqE4LqoUA8M zXZA4$;*hPsfC}^ls{MP^iY8HC%^lQH1o16u-V|Jhxu}F*#z3scaDD%;*@|}57Vbrz z-BHx{eij385_Q(U+4>n|ktT^wN+b`}e+>_9eG{sGn=N-*d(e;i4<+%FgjY~29JAg+jSJu=>6VA0R=5!L_aGP5e+@o`6=?G6DzA>*Ibp!4tO9 z+k1X`u-oZ5-QhjbUTi#{?DO`&VT)#Mbw%YX3Q<^5TWx%OoZe%*r~A6ym5YA#&-V77 b^SIyj_P0*&=`$(}wp*6|zsX&g9pe8lDN#Q@ delta 2629 zcmZwHdrZ}39LMo5$0Nx_0qQQ)+q)Su{865W|IIBjDln)8#M9QWtwZ| zsMs7dbl(9Ke?ah^<2Nt_j#V*k2eDs z)&|ECydB2BF8<%d|0%C(W@n@RGoIo(>dE+(^9rU?kB>G>z+8;OV!RH^aSm4CTx@Xt zJDmHFCuj%xRe&Cxjql_2_z}k9H>f}pI1m4F^~4w!r=E_=H6N4EkJ?B8XJC`--;PYR zou~x&;vCkucGqzj6{rt+te+n(Jb+5<0;+!umDnZM|2y)Smqu+Q4mEEPYF-}d1nzSE zYfy>TsGs$%nSze)8Pvp=Q31O(0FSu-W2haRM+F>p{g+Vz|3J-;Vv-U`MvYs7YAa`Xg%G1nLT}qOL5S!K!DX zPSB5I8<6$Aeo=?XVXZcL0dpU6EmIps7wn{3zVYnuFTadQ32{v3pAsSbhoQ_AdkJlkBX(QdY->>#kccfbDp{24WW8%B&Q87?off>JQCs%*1Y7hG+10{1bKe z%Q0JQBa0oB9HyT&tmj2y9Ki`6<@_39K;jYFyCw;=8$yLfg@s&x`jCy|Chbu6VI{ipz|QAb{mPhu6W!!Pgy%)5baAFtp=JjpkA z5)W`3YTrOs1raR3ah;`JLH4`w7GF4iA1cA4KF(hYzDI)=97NsiIn-M`ih2jeF&!sS zi6*6mcb0+L$#T?H6d|{1Rj7GSAdj{2qxgqV?@TXFubamCE5INPdaFN1)h{BOweOMZ zx4%&dC6I*@^dYxkIjHs`2~hFkb>^6 z1hcRTXX0K|#`|4+7qS^Ug-YZn*FJ?h`h`r^_$8?N3g=3UpdN6RqfT%=&a9-ck%HdN zO{h#8Q31B2cKEdO0BYh}sJA?X+Tm%`-+^J&_$ge8(b;A%J(XA-@aQ*G!_#)v1j@LuCt(@@=7 LyDfAuGs^QXWb_De 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 aa9e0efbf967af1806560c1c9903fe2a5e2dff83..d5a07e1ed13b4799512ebcc465e12b3c9ea96a83 100644 GIT binary patch delta 2619 zcmYM!e@s%-@>eDa$+~UGy_O3wg(x*W)(l*- zl`}SVGB>ebf8cUVt>#9V#2=Amn`wF{NKm_J)c_6ji&r>7MEkR^KjBkPIaz;b|KEd3Y?D5VG>3$1Dh}t z-?aOkW;b$p?i6QD@CByh5C(7rr{OKsM1Nxrrl-Z)MX1EeQMp#(Ok9gvNCPI|UgU85 zIBDGb$W-nqCK2C#$b|y*+Kuz5frH55E_2fTF;s##QTOkn67+G>{Vb%*1yKtswf+$5 zxwWWDykPemF^l-lvjGQDB|MIr@H5ne{dWI5yFZLt!31i;J9ghs6jdY}_56HPB1=%? zp0xg2>)(u?9@xQ!0v|w4d<-?g$JRcJ#Bg7u61s%RID#5?9TngR0ZoYsJ|YJ(805=6_xSZ*6y-)4{F?5)E<9}O8hD+vENY(xQhyy#^8LMhjh8e zQ3pNX9v2F<1y!OwHsAwPB~GFyK8GsVfcXPzMZ>5Hj3bA;%}L`DD36{? zN8Qgy%~yg-a5*ZTSH;DDdx4rDV(r&Z59$R`piWexhmphea?)P+qn;l`jk}C`Zp`{8 zQ1ecq67X|UI}aJ}xrJOP(}z(HR+{TjnTAmTHlz0LRcr4;1$+lJ;XzbMPgwgja<~he zRPrmR`{SrMQ#e!Ke;gaUm5uHELH1u@(xYTzHp;r`*Y0CRYA-oVGO0MFp3cm?~>%XMxJ zhRB+jE{ys)-;R3uGH1mS4WJfSfSI@$Jq4`bLM2&eH^OEUYJ%OU((l8a_#W0^>TF&} ztj9qt=9~K&C-EyBq%7*+Pj<`Wn4hu@(e7jwdvRls`YXUlFqYvjsP+UZ;4Rc1-$T74 z$%V0ZA%OE}2T?1jM2)XTE#yTcC-(}n&8{8w4jn-b*TYHU2MVdb_V7nK^t}$F+SgET z_YLcxM5^hgP$f(!x)LftC0K^)Uxu1^mG#%4D!d8VEw>Go*nV7yJsuZS(|wN%@P@TB zief7(!Tafd1T`*f{jcLv+8tPn{it^+m%&Qt0aSuZ%@wE$RHMF@FzOf5Yvw|4c~m05TK`Q{>2r8kJXv)#JlweD=wUxe>zLvC0)3PffZS~C!(MYi3>0ov2PG2bRVuo)`w7I3v IUz`;GFI%Pj4gdfE delta 2552 zcmYM!3uu;A7{KwfIlpZ==XBcKEH`&?lUBB2R5OQ?#uU5Y+?JFX4#Gs(7)ctd&k8iV zfu=!jbV0J_5Jy8{kaKp$G%E-%QHzRMqnHJjH5T^&vjfTN_dDmj=RD^*&-=bRzUswQ z$-!x9djtPE_4#GAd({$Vo>OZP0ga1R=AzZ39qynhTm!5K8*g?N7$4R{4zpUENsV<44f`*4W<>C+%H2G{B?i2Ak26Zjb%F$Q2IoW6Ar_`zO(T&Z7q! z!WO^(Q5qx_HuAuI*6mo2d+}Zz#tOWhRkLwDn&8vuADZW}9N)qD_yyMBRdnMT>h1V) zti-LDj|VW^S91HRQqh;i zzKpz=;Uc>6D4Jk`#X23WKr#B2RHAi6+{CR<0Wf6+Vg%qlsp7Y~vIll~0Cw_X1ov+xGavZL^oa$1oNBR&@d<{QeU(_>#h8tiUpyi1(lwua4sn zBZmk((L@f%@jkTVKce%0M>ifr->ocyrr?Zd9$LYCocJJ(*))8KN*%!Yn1Q#TC#;RG yMi*{G-|`l8qi4{+1G~}rJ-7@%>AjG7D5ZDzq*GbFZ8JZ}PF=FR_qOu%)c*iOPwFE8 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"))