This commit is contained in:
Sander Land committed 2020-05-30 17:58:28 +02:00
1 parent d7915401e7
commit 48f6166d48
23 files changed
+713 -926

No files matched your search

+9 -4
View File
@@ -17,15 +17,19 @@ errors = False
po = {} po = {}
pofile = {} pofile = {}
num_todo = defaultdict(int)
for lang in locales: for lang in locales:
pofile[lang] = os.path.join(localedir, lang, "LC_MESSAGES", "katrain.po") pofile[lang] = os.path.join(localedir, lang, "LC_MESSAGES", "katrain.po")
po[lang] = polib.pofile(pofile[lang]) po[lang] = polib.pofile(pofile[lang])
for entry in po[lang].translated_entries(): for entry in po[lang].translated_entries():
if "TODO" in entry.comment: if "TODO" in entry.comment:
print(lang, "/", entry.msgid, "is TODO") num_todo[lang] += 1
else: else:
strings_to_langs[entry.msgid][lang] = entry.msgstr strings_to_langs[entry.msgid][lang] = entry.msgstr
lang_to_strings[lang].add(entry.msgid) lang_to_strings[lang].add(entry.msgid)
if num_todo[lang]:
print(f"{lang} has {num_todo[lang]} TODO entries")
for lang in locales: 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) print("Message id", msgid, "found as ", strings_to_langs[msgid], "but missing in default", DEFAULT_LANG)
errors = True errors = True
elif DEFAULT_LANG in strings_to_langs[msgid]: 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] copied_msg = strings_to_langs[msgid][DEFAULT_LANG]
if lang == "haha": if lang == "haha":
copied_msg = re.sub(r"[a-zA-Z]{1,2}", "ㅋ", copied_msg) entry = polib.POEntry(msgid=msgid, msgstr="ㅋㅋ" + copied_msg)
entry = polib.POEntry(msgid=msgid, msgstr=copied_msg, comment="TODO") 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) po[lang].append(entry)
errors = True errors = True
else: else:
+8 -10
View File
@@ -25,7 +25,7 @@ from katrain.core.utils import (
DEFAULT_LANGUAGE, DEFAULT_LANGUAGE,
find_package_resource, 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.gui.popups import ConfigTeacherPopup, ConfigTimerPopup, I18NPopup
from katrain.core.base_katrain import KaTrainBase from katrain.core.base_katrain import KaTrainBase
from katrain.core.engine import KataGoEngine 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.core.sgf_parser import Move, ParseError
from katrain.gui.kivyutils import * from katrain.gui.kivyutils import *
from katrain.gui.widgets.graph import ScoreGraph 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.badukpan import AnalysisControls, BadukPanControls, BadukPanWidget
from katrain.gui.controlspanel import ControlsPanel from katrain.gui.controlspanel import ControlsPanel
from katrain.gui.popups import ConfigPopup, LoadSGFPopup, NewGamePopup, AIPopup from katrain.gui.popups import ConfigPopup, LoadSGFPopup, NewGamePopup, AIPopup
@@ -81,10 +81,6 @@ class KaTrainGui(Screen, KaTrainBase):
def play_analyze_mode(self): def play_analyze_mode(self):
return self.play_mode.play_analyze_mode return self.play_mode.play_analyze_mode
@property
def ai_strategies(self):
return list(self.config("ai").keys())
def start(self): def start(self):
if self.engine: if self.engine:
return return
@@ -163,10 +159,10 @@ class KaTrainGui(Screen, KaTrainBase):
def __call__(self, message, *args): def __call__(self, message, *args):
if self.game: 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('-', '_')}") fn = getattr(self, f"_do_{message.replace('-', '_')}")
Clock.schedule_once(lambda _dt: fn(*args), -1) 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]) self.message_queue.put([self.game.game_id, message, *args])
def _do_new_game(self, move_tree=None, analyze_fast=False): 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: if node is None or self.game.current_node == node:
mode = self.next_player_info.strategy mode = self.next_player_info.strategy
settings = self.config(f"ai/{mode}") settings = self.config(f"ai/{mode}")
if settings: if settings is not None:
ai_move(self.game, mode, settings) ai_move(self.game, mode, settings)
else: else:
self.log(f"AI Mode {mode} not found!", OUTPUT_ERROR) 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", "."))) 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__ 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() self.fileselect_popup.dismiss()
try: try:
move_tree = KaTrainSGF.parse_file(files[0]) move_tree = KaTrainSGF.parse_file(files[0])
@@ -263,6 +260,7 @@ class KaTrainGui(Screen, KaTrainBase):
if not popup_contents.rewind.active: if not popup_contents.rewind.active:
self.game.redo(999) self.game.redo(999)
popup_contents.filesel.on_success = readfile
popup_contents.filesel.on_submit = readfile popup_contents.filesel.on_submit = readfile
self.fileselect_popup.open() self.fileselect_popup.open()
+3 -4
View File
@@ -13,7 +13,7 @@
"_enable_ownership": true "_enable_ownership": true
}, },
"general": { "general": {
"sgf_load": "~/Downloads", "sgf_load": "~/",
"sgf_save": "./sgfout", "sgf_save": "./sgfout",
"anim_pv_time": 0.5, "anim_pv_time": 0.5,
"debug_level": 1, "debug_level": 1,
@@ -68,8 +68,7 @@
"lock_ai": true "lock_ai": true
}, },
"ai": { "ai": {
"ai:default": { "ai:default": {},
},
"ai:jigo": { "ai:jigo": {
"target_score": 0.5 "target_score": 0.5
}, },
@@ -77,7 +76,7 @@
"strength": 0.5 "strength": 0.5
}, },
"ai:policy": { "ai:policy": {
"opening_moves": 20 "opening_moves": 22.0
}, },
"ai:p:weighted": { "ai:p:weighted": {
"pick_override": 1.0, "pick_override": 1.0,
+5 -6
View File
@@ -5,7 +5,7 @@ import time
from typing import Dict, List, Tuple from typing import Dict, List, Tuple
from katrain.core.utils import var_to_grid 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.engine import EngineDiedException
from katrain.core.game import Game, GameNode, Move 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] engine = game.engines[cn.next_player]
if engine.katago_process.poll() is not None: # TODO: clean up if engine.katago_process.poll() is not None: # TODO: clean up
raise EngineDiedException(f"Engine for {cn.next_player} ({engine.config}) died") raise EngineDiedException(f"Engine for {cn.next_player} ({engine.config}) died")
ai_mode = ai_mode.lower()
ai_thoughts = "" 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 policy_moves = cn.policy_ranking
pass_policy = cn.policy[-1] 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 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]] policy_grid = var_to_grid(cn.policy, size) # type: List[List[float]]
top_policy_move = policy_moves[0][1] top_policy_move = policy_moves[0][1]
ai_thoughts += f"Using policy based strategy, base top 5 moves are {fmt_moves(policy_moves[:5])}. " 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_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_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} ai_settings = {"pick_override": 0.9, "weaken_fac": 1, "lower_bound": 0.02}
if top_5_pass: if top_5_pass:
aimove = top_policy_move aimove = top_policy_move
ai_thoughts += "Playing top one because one of them is pass." 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 aimove = top_policy_move
ai_thoughts += f"Playing top policy move {aimove.gtp()}." ai_thoughts += f"Playing top policy move {aimove.gtp()}."
elif policy_moves[0][0] > ai_settings["pick_override"]: elif policy_moves[0][0] > ai_settings["pick_override"]:
aimove = top_policy_move aimove = top_policy_move
ai_thoughts += f"Top policy move has weight > {ai_settings['pick_override']:.1%}, so overriding other strategies." 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 lower_bound = max(0, ai_settings["lower_bound"]) * 2 # compensate for first halving in loop
weaken_fac = max(0.01, ai_settings["weaken_fac"]) weaken_fac = max(0.01, ai_settings["weaken_fac"])
weighted_coords = [] weighted_coords = []
+1 -1
View File
@@ -66,7 +66,7 @@ class KaTrainBase:
print(message) print(message)
def _load_config(self): 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: try:
self.log(f"Using config file {config_file}", OUTPUT_INFO) self.log(f"Using config file {config_file}", OUTPUT_INFO)
self._config_store = JsonStore(config_file, indent=4) self._config_store = JsonStore(config_file, indent=4)
+33 -2
View File
@@ -8,8 +8,39 @@ GAME_TYPES = [PLAYING_NORMAL, PLAYING_TEACHING]
MODE_PLAY, MODE_ANALYZE = "play", "analyze" MODE_PLAY, MODE_ANALYZE = "play", "analyze"
AI_DEFAULT = "default" AI_DEFAULT = "ai:default"
AI_STRATEGIES = [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_ERROR = -1
OUTPUT_KATAGO_STDERR = -0.5 OUTPUT_KATAGO_STDERR = -0.5
OUTPUT_INFO = 0 OUTPUT_INFO = 0
+16 -8
View File
@@ -1,7 +1,7 @@
import os import os
import sys import sys
from numbers import Number from numbers import Number
from typing import List, Tuple, Callable from typing import List, Tuple, Callable, TypeVar
from kivy.lang import Observable from kivy.lang import Observable
import gettext import gettext
@@ -12,8 +12,10 @@ try:
except: except:
import importlib_resources as pkg_resources 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""" """convert ownership/policy to grid format such that grid[y][x] is for move with coords x,y"""
ix = 0 ix = 0
grid = [[]] * size[1] grid = [[]] * size[1]
@@ -55,11 +57,21 @@ class Lang(Observable):
def _(self, text): def _(self, text):
return self.ugettext(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): def fbind(self, name, func, *args):
if name == "_": if name == "_":
widget, property, *_ = args[0] widget, property, *_ = args[0]
self.observers.append((widget, func, args)) 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: else:
return super(Lang, self).fbind(name, func, *args) return super(Lang, self).fbind(name, func, *args)
@@ -88,11 +100,7 @@ class Lang(Observable):
for widget, func, args in self.observers: for widget, func, args in self.observers:
try: try:
func(args[0], None, None) func(args[0], None, None)
print("setting", widget, widget.font_name, "->", self.font_name) self.set_widget_font(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
except ReferenceError: except ReferenceError:
pass # proxy no longer exists pass # proxy no longer exists
for cb in self.callbacks: for cb in self.callbacks:
+39 -80
View File
@@ -1,20 +1,15 @@
#:kivy 1.11.0 #:kivy 1.11.0
#:import i18n katrain.core.utils.i18n #: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 # TODO: refactor
#:set CP_SPACING 6 #:set CP_SPACING 6
#:set CP_SMALL_SPACING 3 #:set CP_SMALL_SPACING 3
#:set CP_PADDING 6 #:set CP_PADDING 6
#:import PLAYER_TYPES katrain.core.constants.PLAYER_TYPES #: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 RED katrain.gui.style.RED
#:import WHITE katrain.gui.style.WHITE #:import WHITE katrain.gui.style.WHITE
#:import BACKGROUND_COLOR katrain.gui.style.BACKGROUND_COLOR #:import BACKGROUND_COLOR katrain.gui.style.BACKGROUND_COLOR
@@ -147,7 +142,6 @@
width: 1 width: 1
<I18NSpinner>: <I18NSpinner>:
text: self.values[0] if self.values else ''
font_size: self.height * 0.5 font_size: self.height * 0.5
sync_height_frac: 1.0 sync_height_frac: 1.0
background_color: [*[c*255/88 for c in BACKGROUND_COLOR[:3]], 1] # compensate for texture background_color: [*[c*255/88 for c in BACKGROUND_COLOR[:3]], 1] # compensate for texture
@@ -173,59 +167,6 @@
size: image.size size: image.size
font_size: root.min_size * 1.2 / (1 + 1 * len(self.text)) font_size: root.min_size * 1.2 / (1 + 1 * len(self.text))
# -- old widgets TODO: remove
<ToolTipLabel>: # 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)
<StyledIconButton@StyledButton>:
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
<LightLabel>:
color: TEXT_COLOR
text_size: self.size # don't overflow these
halign: 'center'
valign: 'middle'
<ScaledLightLabel>: # popups
color: TEXT_COLOR
font_size: 0.45*self.height/self.num_lines
<LightHelpLabel>:
font_size: self.height/10
halign: 'center'
valign: 'middle'
multiline: True
<ClickableLabel>: <ClickableLabel>:
color: WHITE color: WHITE
halign: 'center' halign: 'center'
@@ -327,8 +268,8 @@
<PlayerInfo>: <PlayerInfo>:
captures: 0 captures: 0
player: 'B' player: 'B'
player_type: 'Player' player_type: 'player:human'
player_subtype: 'Normal game' player_subtype: 'game:normal'
background_color: BOX_BACKGROUND_COLOR if self.active else BACKGROUND_COLOR background_color: BOX_BACKGROUND_COLOR if self.active else BACKGROUND_COLOR
outline_color: BOX_BACKGROUND_COLOR outline_color: BOX_BACKGROUND_COLOR
outline_width: 2 outline_width: 2
@@ -342,11 +283,11 @@
orientation: 'vertical' orientation: 'vertical'
spacing: CP_SPACING spacing: CP_SPACING
Label: Label:
text: root.player_type text: i18n._(root.player_type)
size_hint: 1,0.55 size_hint: 1,0.55
font_size: 0.8 * self.height font_size: 0.8 * self.height
Label: Label:
text: root.player_subtype text: i18n._(root.player_subtype)
size_hint: 1,0.45 size_hint: 1,0.45
font_size: 0.7 * self.height font_size: 0.7 * self.height
@@ -585,7 +526,7 @@
do_scroll_x: False do_scroll_x: False
scroll_type: ['bars'] scroll_type: ['bars']
bar_width: 5 bar_width: 5
bar_color: BUTTON_COLOR bar_color: LIGHTGREY
label: label label: label
canvas.before: canvas.before:
Color: Color:
@@ -874,8 +815,8 @@
spacing: CP_SPACING spacing: CP_SPACING
padding: 0, CP_PADDING padding: 0, CP_PADDING
player_type: player_type player_type: player_type
player_subtype: player_subtype player_subtype_human: player_subtype_human
player_subtype_label: player_subtype_label player_subtype_ai: player_subtype_ai
MDBoxLayout: MDBoxLayout:
size_hint: 0.25, 1 size_hint: 0.25, 1
CircleWithText: CircleWithText:
@@ -893,19 +834,37 @@
I18NSpinner: I18NSpinner:
id: player_type id: player_type
value_refs: PLAYER_TYPES value_refs: PLAYER_TYPES
on_text: root.setup_options(*args) on_select: root.setup_options(*args)
MDBoxLayout: MDBoxLayout:
size_hint: 0.5, 1 size_hint: 0.5, 1
orientation: 'vertical' MDBoxLayout:
spacing: CP_SPACING orientation: 'vertical'
Label: spacing: CP_SPACING
text: 'Player Type' size_hint_x: 1 if root.mode != PLAYER_AI else 0.0001
id: player_subtype_label opacity:1 if root.mode != PLAYER_AI else 0
font_size: self.height * 0.5 Label:
I18NSpinner: text: i18n._("gametype")
id: player_subtype font_size: self.height * 0.5
value_refs: [] BoxLayout:
on_text: root.update_global_player_info() 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()
<PlayerSetupBlock>: <PlayerSetupBlock>:
orientation: 'vertical' orientation: 'vertical'
+3 -3
View File
@@ -40,8 +40,8 @@ class ControlsPanel(BoxLayout):
def update_players(self, *_args): def update_players(self, *_args):
for bw, player_info in self.katrain.players_info.items(): for bw, player_info in self.katrain.players_info.items():
self.players[bw].player_type = i18n._(player_info.player_type) self.players[bw].player_type = player_info.player_type
self.players[bw].player_subtype = i18n._(player_info.player_subtype) self.players[bw].player_subtype = player_info.player_subtype
def set_status(self, msg, at_node=None): def set_status(self, msg, at_node=None):
self.status_msg = msg self.status_msg = msg
@@ -64,7 +64,7 @@ class ControlsPanel(BoxLayout):
self.active_comment_node = current_node self.active_comment_node = current_node
if katrain.play_analyze_mode == MODE_PLAY and last_player_was_ai_playing_human: 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] self.active_comment_node = current_node.children[-1]
elif current_node.parent: elif current_node.parent:
self.active_comment_node = current_node.parent self.active_comment_node = current_node.parent
+36 -136
View File
@@ -34,7 +34,7 @@ from kivymd.uix.button import BasePressedButton, BaseFlatButton
from kivymd.uix.navigationdrawer import MDNavigationDrawer from kivymd.uix.navigationdrawer import MDNavigationDrawer
from kivy.core.text import Label as CoreLabel 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 from katrain.core.utils import i18n
# #
@@ -179,35 +179,49 @@ class BGBoxLayout(BoxLayout, BackgroundMixin):
class I18NSpinner(Spinner): class I18NSpinner(Spinner):
__events__ = ["on_select"]
sync_height_frac = NumericProperty(1.0) sync_height_frac = NumericProperty(1.0)
value_refs = ListProperty() value_refs = ListProperty()
selected_index = NumericProperty(0)
font_name = StringProperty(DEFAULT_FONT) font_name = StringProperty(DEFAULT_FONT)
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.bind(size=self.update_dropdown_props, pos=self.update_dropdown_props, value_refs=self.i18n_values) self.bind(size=self.update_dropdown_props, pos=self.update_dropdown_props, value_refs=self.i18n_values)
self.i18n_values() self.i18n_values()
MDApp.get_running_app().bind(language=self.i18n_values)
@property @property
def selected(self): def selected(self):
try: try:
selected = self.values.index(self.text) selected = self.selected_index
return selected, self.value_refs[selected], self.values[selected] return selected, self.value_refs[selected], self.values[selected]
except (ValueError, IndexError): except (ValueError, IndexError):
return 0, "", "" 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): def select_key(self, key):
try: try:
ix = self.value_refs.index(key) ix = self.value_refs.index(key)
self.text = self.values[ix] self.text = self.values[ix]
except: except (ValueError, IndexError):
pass pass
def i18n_values(self, *_args): def i18n_values(self, *_args):
if self.value_refs: if self.value_refs:
selected = self.selected[0]
self.values = [i18n._(ref) for ref in self.value_refs] 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.font_name = i18n.font_name
self.update_dropdown_props() self.update_dropdown_props()
@@ -230,29 +244,33 @@ class I18NSpinner(Spinner):
class PlayerSetup(MDBoxLayout): class PlayerSetup(MDBoxLayout):
player = OptionProperty("B", options=["B", "W"]) player = OptionProperty("B", options=["B", "W"])
mode = StringProperty("")
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**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): def setup_options(self, *_args):
self.player_subtype.clear_widgets() if self.player_type.selected[1] == self.mode:
if self.player_type.selected[0] == 0: # human return
self.player_subtype_label.text = i18n._("gametype") self.mode = self.player_type.selected[1]
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]
self.update_global_player_info() self.update_global_player_info()
@property @property
def player_type_dump(self): 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): def update_widget(self, player_type, player_subtype):
self.player_type.select_key(player_type) # should trigger setup options 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): def update_global_player_info(self):
if self.parent and self.parent.update_global: 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, 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) 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() self.build_options()
def build_options(self, *args, **kwargs): def build_options(self, *args, **kwargs):
@@ -387,7 +405,7 @@ class CollapsablePanel(MDBoxLayout):
self.header.add_widget(Label()) # spacer self.header.add_widget(Label()) # spacer
self.trigger_select(ix=None) self.trigger_select(ix=None)
else: 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) self.header.add_widget(self.open_close_button)
super().clear_widgets() super().clear_widgets()
@@ -438,60 +456,10 @@ class StatsBox(MDBoxLayout, BackgroundMixin):
player = StringProperty("") 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): class ClickableLabel(LeftButtonBehavior, Label):
pass pass
class LightHelpLabel(ScaledLightLabel):
pass
class ScrollableLabel(ScrollView, BackgroundMixin): class ScrollableLabel(ScrollView, BackgroundMixin):
__events__ = ["on_ref_press"] __events__ = ["on_ref_press"]
outline_color = ListProperty([0, 0, 0, 0]) # mixin not working for some reason outline_color = ListProperty([0, 0, 0, 0]) # mixin not working for some reason
@@ -502,74 +470,6 @@ class ScrollableLabel(ScrollView, BackgroundMixin):
pass 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): 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 = CoreLabel(text=text, bold=True, font_name=font_name or i18n.font_name, **kw) #
label.refresh() label.refresh()
+15 -9
View File
@@ -7,11 +7,12 @@ from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.boxlayout import BoxLayout from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label from kivy.uix.label import Label
from kivy.uix.popup import Popup from kivy.uix.popup import Popup
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.selectioncontrol import MDCheckbox from kivymd.uix.selectioncontrol import MDCheckbox
from kivymd.uix.textfield import MDTextField 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.engine import KataGoEngine
from katrain.core.utils import i18n, find_package_resource from katrain.core.utils import i18n, find_package_resource
from katrain.gui.kivyutils import I18NSpinner, BackgroundMixin from katrain.gui.kivyutils import I18NSpinner, BackgroundMixin
@@ -249,21 +250,22 @@ class AIPopup(QuickConfigGui):
def __init__(self, katrain): def __init__(self, katrain):
super().__init__(katrain) super().__init__(katrain)
self.ai_select.bind(text=self.build_ai_options) self.ai_select.value_refs = AI_STRATEGIES_RECOMMENDED_ORDER
self.ai_select.value_refs = katrain.ai_strategies 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.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] strategy = self.ai_select.selected[1]
print(strategy, "=strat", _args)
mode_settings = self.katrain.config(f"ai/{strategy}") mode_settings = self.katrain.config(f"ai/{strategy}")
self.options_grid.clear_widgets() self.options_grid.clear_widgets()
self.help_label.text = i18n._(strategy.replace("ai:", "aihelp:")) 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(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): 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()) self.options_grid.add_widget(Label())
@@ -294,4 +296,8 @@ class ConfigPopup(QuickConfigGui):
class LoadSGFPopup(BoxLayout): 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")
+11 -9
View File
@@ -45,7 +45,7 @@ from kivy.lang import Builder
from kivy.utils import platform from kivy.utils import platform
from kivy.clock import Clock from kivy.clock import Clock
import string 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 os import walk
from functools import partial from functools import partial
@@ -56,7 +56,7 @@ if platform == "win":
def last_modified_first(files, filesystem): 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(): 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 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) on_is_open: self.is_open and self.parent.trigger_populate(self)
<FileBrowser>: <I18NFileBrowser>:
orientation: 'vertical' orientation: 'vertical'
spacing: 5 spacing: 5
padding: [6, 6, 6, 6] padding: [6, 6, 6, 6]
@@ -224,12 +224,14 @@ Builder.load_string(
TextInput: TextInput:
id: file_text id: file_text
text: (root.selection and (root._shorten_filenames(root.selection) if root.multiselect else root.selection[0])) or '' 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 multiline: False
Button: height: '40dp'
AutoSizedRoundedRectangleButton:
id: select_button id: select_button
padding_x: 15
height: '40dp'
size_hint_x: None size_hint_x: None
width: metrics.dp(100)
text: root.select_string text: root.select_string
on_release: root.dispatch('on_success') on_release: root.dispatch('on_success')
""" """
@@ -312,8 +314,8 @@ class LinkTree(TreeView):
self.add_node(TreeLabel(text=path, path=parent + sep + path), node) self.add_node(TreeLabel(text=path, path=parent + sep + path), node)
class FileBrowser(BoxLayout): class I18NFileBrowser(BoxLayout):
"""FileBrowser class, see module documentation for more information. """I18NFileBrowser class, see module documentation for more information.
""" """
__events__ = ("on_success", "on_submit") __events__ = ("on_success", "on_submit")
@@ -436,7 +438,7 @@ class FileBrowser(BoxLayout):
pass pass
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(FileBrowser, self).__init__(**kwargs) super(I18NFileBrowser, self).__init__(**kwargs)
Clock.schedule_once(self._post_init) Clock.schedule_once(self._post_init)
def _post_init(self, *largs): def _post_init(self, *largs):
Binary file not shown.
@@ -402,7 +402,6 @@ msgid "update settings"
msgstr "Update Settings" msgstr "Update Settings"
# ai settings from here # ai settings from here
msgid "ai settings" msgid "ai settings"
msgstr "Advanced AI Settings" msgstr "Advanced AI Settings"
@@ -459,8 +458,9 @@ msgstr "KataGo"
msgid "aihelp:default" msgid "aihelp:default"
msgstr "" msgstr ""
"Full strength KataGo AI. Strength is affected by `max_visits` and " "Full strength KataGo AI. Strength is affected by `max_visits` and `model` "
"`model` in the general settings `engine` section and engine configuration file. No options are available here." "in the general settings `engine` section and engine configuration file. No "
"options are available here."
msgid "ai:jigo" msgid "ai:jigo"
msgstr "KataJigo" msgstr "KataJigo"
@@ -516,7 +516,7 @@ msgstr ""
"policy value is above `pick_override` to avoid obvious mistakes." "policy value is above `pick_override` to avoid obvious mistakes."
msgid "ai:p:local" msgid "ai:p:local"
msgstr "Local Response Style" msgstr "Local Style"
msgid "aihelp:p:local" msgid "aihelp:p:local"
msgstr "" msgstr ""
@@ -531,7 +531,8 @@ msgid "aihelp:p:tenuki"
msgstr "" msgstr ""
"Picks `pick_n + pick_frac * <number of legal moves>` weighted away from the " "Picks `pick_n + pick_frac * <number of legal moves>` weighted away from the "
"last move and plays the best one. Plays top move if policy value is above " "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" msgid "ai:p:influence"
msgstr "Influential Style" msgstr "Influential Style"
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
+11 -13
View File
@@ -138,7 +138,7 @@ msgstr ""
msgid "Engine died unexpectedly" msgid "Engine died unexpectedly"
msgstr "" msgstr ""
"Engine died unexpectedly without sending output, possibly due to out of " "Engine died unexpectedly without sending output, possibly due to out of "
"memory: {e}" "memory: {error}"
#. TODO #. TODO
msgid "analysis:territory" msgid "analysis:territory"
@@ -236,29 +236,23 @@ msgstr "Teaching Game"
msgid "aistrategy" msgid "aistrategy"
msgstr "AI Strategy" msgstr "AI Strategy"
#. TODO
msgid "game:normal" msgid "game:normal"
msgstr "Normal Game" msgstr "일반 게임"
#. TODO
msgid "player:human" msgid "player:human"
msgstr "AI" msgstr "인간"
#. TODO
msgid "player:type" msgid "player:type"
msgstr "Player" msgstr "인간/인곡지능"
#. TODO
msgid "gametype" msgid "gametype"
msgstr "Game Type" msgstr "게임 종류"
#. TODO
msgid "player:ai" msgid "player:ai"
msgstr "AI" msgstr "인곡지능"
#. TODO
msgid "game:teach" msgid "game:teach"
msgstr "Teaching Game" msgstr "교직 게임"
#. TODO #. TODO
msgid "menu:clocksettings" msgid "menu:clocksettings"
@@ -650,3 +644,7 @@ msgstr "Homepage"
#. TODO #. TODO
msgid "menu:manual" msgid "menu:manual"
msgstr "Manual" msgstr "Manual"
#. TODO
msgid "ai settings"
msgstr "Advanced AI Settings"
Binary file not shown.
@@ -274,7 +274,7 @@ msgstr "Normal Game"
#. TODO #. TODO
msgid "player:human" msgid "player:human"
msgstr "AI" msgstr "Mens"
#. TODO #. TODO
msgid "player:type" msgid "player:type"
@@ -682,3 +682,7 @@ msgstr "Homepage"
#. TODO #. TODO
msgid "menu:manual" msgid "menu:manual"
msgstr "Manual" msgstr "Manual"
#. TODO
msgid "ai settings"
msgstr "Advanced AI Settings"
+18 -25
View File
@@ -1,6 +1,7 @@
#:kivy 1.11.0 #:kivy 1.11.0
#:import i18n katrain.core.utils.i18n #:import i18n katrain.core.utils.i18n
#:import expanduser os.path.expanduser
#:import abspath os.path.abspath
#:set CP_SPACING 6 #:set CP_SPACING 6
#:set CP_SMALL_SPACING 3 #:set CP_SMALL_SPACING 3
@@ -14,7 +15,7 @@
#:import TEXT_COLOR katrain.gui.style.TEXT_COLOR #:import TEXT_COLOR katrain.gui.style.TEXT_COLOR
#MDTextField: # MDTextField:
# hint_text: "Helper text on focus" # hint_text: "Helper text on focus"
# helper_text: "This will disappear when you click off" # helper_text: "This will disappear when you click off"
# helper_text_mode: "on_focus" # helper_text_mode: "on_focus"
@@ -22,7 +23,7 @@
<LabelledTextInput>: <LabelledTextInput>:
font_size: INPUT_FONT_SIZE font_size: INPUT_FONT_SIZE
helper_text_mode: "on_focus" helper_text_mode: "none"
hint_text: "" hint_text: ""
helper_text: "" helper_text: ""
@@ -32,7 +33,6 @@
helper_text_mode: "on_error" helper_text_mode: "on_error"
helper_text: i18n._("file not found") helper_text: i18n._("file not found")
<LabelledIntInput>: <LabelledIntInput>:
font_size: INPUT_FONT_SIZE font_size: INPUT_FONT_SIZE
size_hint: 0.5, None size_hint: 0.5, None
@@ -67,7 +67,7 @@
<DescriptionLabel>: <DescriptionLabel>:
font_size: DESC_FONT_SIZE font_size: DESC_FONT_SIZE
multiline: True multiline: True
text_size: self.size text_size: self.width, None
halign: 'center' halign: 'center'
valign: 'center' valign: 'center'
@@ -101,13 +101,14 @@
AnchorLayout: AnchorLayout:
LabelledFloatInput: LabelledFloatInput:
input_property: "general/anim_pv_time" input_property: "general/anim_pv_time"
helper_text: i18n._("engine:time:hint") hint_text: i18n._("engine:time:hint")
DescriptionLabel: DescriptionLabel:
text: i18n._("general:debug_level") text: i18n._("general:debug_level")
AnchorLayout: AnchorLayout:
LabelledIntInput: LabelledIntInput:
input_property: "general/debug_level" input_property: "general/debug_level"
helper_text: i18n._("general:debug_level:hint") helper_text: i18n._("general:debug_level:hint")
helper_text_mode: "on_focus"
Label: Label:
Label: Label:
Label: Label:
@@ -157,7 +158,7 @@
AnchorLayout: AnchorLayout:
LabelledFloatInput: LabelledFloatInput:
input_property: "engine/max_time" input_property: "engine/max_time"
helper_text: i18n._("engine:time:hint") hint_text: i18n._("engine:time:hint")
DescriptionLabel: DescriptionLabel:
text: i18n._("engine:wide_root_noise") text: i18n._("engine:wide_root_noise")
AnchorLayout: AnchorLayout:
@@ -272,7 +273,7 @@
id: boardsize id: boardsize
input_property: 'game/size' input_property: 'game/size'
size_hint: 0.5, None size_hint: 0.5, None
hint_text:i18n._("non square board hint") hint_text: i18n._("non square board hint")
AnchorLayout: AnchorLayout:
MDBoxLayout: MDBoxLayout:
adaptive_size: True adaptive_size: True
@@ -360,15 +361,16 @@
BoxLayout: BoxLayout:
size_hint: 1,1 size_hint: 1,1
DescriptionLabel: DescriptionLabel:
font_size: 1.25*DESC_FONT_SIZE font_size: DESC_FONT_SIZE
text: i18n._('Select AI') text: i18n._('Select AI')
AnchorLayout: AnchorLayout:
I18NSpinner: I18NSpinner:
size_hint: 1, 0.5 size_hint: 0.8, 0.5
id: ai_select id: ai_select
SmallDescriptionLabel: SmallDescriptionLabel:
id: help_label id: help_label
size_hint: 1,1.25 size_hint: 1,1.25
text: i18n._('')
GridLayout: GridLayout:
size_hint: 1,4 size_hint: 1,4
id: options_grid id: options_grid
@@ -388,12 +390,6 @@
rewind: rewind rewind: rewind
filesel: filesel filesel: filesel
orientation: 'vertical' orientation: 'vertical'
I18NFileChooserListView:
id: filesel
multiselect: False
filters: ["*.sgf"]
path: "."
size_hint: 1,7
BoxLayout: BoxLayout:
orientation: 'horizontal' orientation: 'horizontal'
size_hint: 1,1 size_hint: 1,1
@@ -403,7 +399,6 @@
halign: 'right' halign: 'right'
LabelledCheckBox: LabelledCheckBox:
id: fast id: fast
color: (0.95, 0.95, 0.95)
size_hint: 0.05,1 size_hint: 0.05,1
DescriptionLabel: DescriptionLabel:
text: i18n._("load sgf rewind") text: i18n._("load sgf rewind")
@@ -412,12 +407,10 @@
LabelledCheckBox: LabelledCheckBox:
id: rewind id: rewind
active: True active: True
color: (0.95, 0.95, 0.95)
size_hint: 0.05,1 size_hint: 0.05,1
AnchorLayout: I18NFileBrowser:
size_hint: 1,1.5 id: filesel
AutoSizedRoundedRectangleButton: multiselect: False
padding_x: 15 filters: ["*.sgf"]
size_hint: None,0.5 path: "."
text: i18n._("Load File") size_hint: 1,7
+8
View File
@@ -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)
+12 -8
View File
@@ -1,11 +1,15 @@
import pytest import pytest
from katrain.core.game import Game, IllegalMoveException, Move from katrain.core.game import Game, IllegalMoveException, Move
from katrain.core.base_katrain import KaTrainBase, OUTPUT_INFO
class MockKaTrain: class MockKaTrain(KaTrainBase):
def log(self, msg, lvl): pass
pass
# def log(self, message, level=OUTPUT_INFO):
# pass
class MockEngine: class MockEngine:
@@ -18,7 +22,7 @@ class TestBoard:
return [c for c in b.chains if c] return [c for c in b.chains if c]
def test_merge(self): 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("B9", player="B"))
b.play(Move.from_gtp("A3", player="B")) b.play(Move.from_gtp("A3", player="B"))
b.play(Move.from_gtp("A9", player="B")) b.play(Move.from_gtp("A9", player="B"))
@@ -27,7 +31,7 @@ class TestBoard:
assert 0 == len(b.prisoners) assert 0 == len(b.prisoners)
def test_collide(self): def test_collide(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("B9", player="B"))
with pytest.raises(IllegalMoveException): with pytest.raises(IllegalMoveException):
b.play(Move.from_gtp("B9", player="W")) b.play(Move.from_gtp("B9", player="W"))
@@ -36,7 +40,7 @@ class TestBoard:
assert 0 == len(b.prisoners) assert 0 == len(b.prisoners)
def test_capture(self): 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("A2", player="B"))
b.play(Move.from_gtp("B1", player="W")) b.play(Move.from_gtp("B1", player="W"))
b.play(Move.from_gtp("A1", player="W")) b.play(Move.from_gtp("A1", player="W"))
@@ -57,7 +61,7 @@ class TestBoard:
assert 2 == len(b.prisoners) assert 2 == len(b.prisoners)
def test_snapback(self): 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"]: for move in ["C1", "D1", "E1", "C2", "D3", "E4", "F2", "F3", "F4"]:
b.play(Move.from_gtp(move, player="B")) b.play(Move.from_gtp(move, player="B"))
for move in ["D2", "E2", "C3", "D4", "C4"]: for move in ["D2", "E2", "C3", "D4", "C4"]:
@@ -75,7 +79,7 @@ class TestBoard:
assert 4 == len(b.prisoners) assert 4 == len(b.prisoners)
def test_ko(self): def test_ko(self):
b = Game(MockKaTrain(), MockEngine(), config={"init_size": 9}) b = Game(MockKaTrain(), MockEngine())
for move in ["A2", "B1"]: for move in ["A2", "B1"]:
b.play(Move.from_gtp(move, player="B")) b.play(Move.from_gtp(move, player="B"))