lots
This commit is contained in:
1 parent
d7915401e7
commit
48f6166d48
23 files changed
+713
-926
No files matched your search
@@ -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
@@ -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
@@ -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
@@ -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 = []
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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'
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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")
|
||||||
@@ -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.
@@ -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
@@ -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
|
||||||
|
|
||||||
@@ -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
@@ -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"))
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user