This commit is contained in:
Sander Land committed 2020-05-29 16:08:31 +02:00
1 parent 671a764c6a
commit d62fc46626
11 files changed
+308 -225

No files matched your search

+64 -179
View File
@@ -1,22 +1,21 @@
from collections import defaultdict
from typing import Dict, List, DefaultDict, Tuple
import re
from typing import Dict, Tuple, Any, Union, List
from kivy.clock import Clock
from kivy.properties import StringProperty, BooleanProperty
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.selectioncontrol import MDCheckbox
from kivymd.uix.textfield import MDTextField
from katrain.core.constants import OUTPUT_ERROR, OUTPUT_DEBUG
from katrain.core.constants import OUTPUT_ERROR, OUTPUT_DEBUG, OUTPUT_INFO
from katrain.core.engine import KataGoEngine
from katrain.core.game import Game, GameNode
from katrain.gui.kivyutils import StyledSpinner
from katrain.gui.style import DEFAULT_FONT
from katrain.core.utils import i18n
from katrain.gui.kivyutils import StyledSpinner, BackgroundMixin
from katrain.gui.style import DEFAULT_FONT, EVAL_COLORS
class I18NPopup(Popup):
@@ -98,7 +97,7 @@ class QuickConfigGui(MDBoxLayout):
self.popup = None
Clock.schedule_once(lambda _dt: self.set_properties(self))
def collect_properties(self, widget):
def collect_properties(self, widget) -> Dict:
if isinstance(widget, (LabelledTextInput, LabelledSpinner, LabelledCheckBox)) and getattr(widget, "input_property", None):
try:
ret = {widget.input_property: widget.input_value}
@@ -111,17 +110,23 @@ class QuickConfigGui(MDBoxLayout):
ret[k] = v
return ret
def get_setting(self, key):
def get_setting(self, key) -> Tuple[Any,Union[Dict,List],str]:
keys = key.split("/")
config = self.katrain._config
for k in keys[:-1]:
if k not in config:
config[k] = {}
config = config[k]
if keys[-1] not in config:
config[keys[-1]] = ""
self.katrain.log(f"Configuration setting {repr(key)} was missing, created it, but this likely indicates a broken config file.", OUTPUT_ERROR)
return config[keys[-1]], config, keys[-1]
if "::" in keys[-1]:
arraykey, ix = keys[-1].split("::")
array = config[arraykey]
return array[int(ix)], array, ix
else:
if keys[-1] not in config:
config[keys[-1]] = ""
self.katrain.log(f"Configuration setting {repr(key)} was missing, created it, but this likely indicates a broken config file.", OUTPUT_ERROR)
return config[keys[-1]], config, keys[-1]
def set_properties(self, widget):
if isinstance(widget, (LabelledTextInput, LabelledSpinner, LabelledCheckBox)) and getattr(widget, "input_property", None):
@@ -146,7 +151,7 @@ class QuickConfigGui(MDBoxLayout):
old_value, conf, key = self.get_setting(multikey)
if value != old_value:
self.katrain.log(f"Updating setting {multikey} = {value}", OUTPUT_DEBUG)
conf[key] = value # reference straight back to katrain._config
conf[key] = value # reference straight back to katrain._config - may be array or dict
updated.add(multikey)
if save_to_file:
self.katrain.save_config()
@@ -158,9 +163,6 @@ class QuickConfigGui(MDBoxLayout):
class ConfigTimerPopup(QuickConfigGui):
def __init__(self, katrain):
super().__init__(katrain)
def update_config(self, save_to_file=True):
super().update_config(save_to_file=save_to_file)
for p in self.katrain.players_info.values():
@@ -176,98 +178,66 @@ class NewGamePopup(QuickConfigGui):
self.rules_spinner.value_refs = [name for abbr, name in katrain.engine.RULESETS_ABBR]
def update_config(self, save_to_file=True):
updated = super().update_config(save_to_file=save_to_file)
super().update_config(save_to_file=save_to_file)
self.katrain.log(f"New game settings: {self.katrain.config('game')}", OUTPUT_DEBUG)
if self.restart.active:
self.katrain.log("Restarting Engine", OUTPUT_DEBUG)
self.katrain.engine.restart()
for bw, player_setup in self.player_setup.players.items():
self.katrain.update_player(bw, **player_setup.player_type_dump)
self.katrain("new-game")
# for bw, player_widget in self.nav_drawer_contents.player_setup.players.items():
# self.players[bw].update(**player_widget.player_def)
def wrap_anchor(widget):
anchor = AnchorLayout()
anchor.add_widget(widget)
return anchor
class ConfigTeacherPopup(QuickConfigGui):
def __init__(self, katrain):
self.build()
super().__init__(katrain)
def add_option_widgets(self, widgets):
for widget in widgets:
self.options_grid.add_widget(wrap_anchor(widget))
def build(self):
undos = self.katrain.config("trainer/num_undo_prompts")
thresholds = self.katrain.config("trainer/eval_thresholds")
savesgfs = self.katrain.config("trainer/save_feedback")
show_dots = self.katrain.config("trainer/show_dots")
for i, (color, threshold, undo, show_dot, savesgf) in enumerate(zip(EVAL_COLORS, thresholds, undos, show_dots, savesgfs)):
self.add_option_widgets(
[
BackgroundMixin(background_color=color[:3]),
LabelledFloatInput(text=str(threshold), input_property=f"trainer/eval_thresholds::{i}"),
LabelledFloatInput(text=str(undo), input_property=f"trainer/num_undo_prompts::{i}"),
LabelledCheckBox(text=str(show_dot), input_property=f"trainer/show_dots::{i}"),
LabelledCheckBox(text=str(savesgf), input_property=f"trainer/save_feedback::{i}"),
]
)
class ConfigPopup(QuickConfigGui):
def __init__(self, katrain, popup: Popup, config: Dict, ignore_cats: Tuple = (), **kwargs):
self.config = config
self.ignore_cats = ignore_cats
self.orientation = "vertical"
super().__init__(katrain, popup, **kwargs)
Clock.schedule_once(self.build, 0)
def update_config(self, save_to_file=True):
updated = super().update_config(save_to_file=save_to_file)
self.katrain.debug_level = self.katrain.config("general/debug_level", OUTPUT_INFO)
def build(self, _):
props_in_col = [0, 0]
cols = [BoxLayout(orientation="vertical"), BoxLayout(orientation="vertical")]
for k1, all_d in sorted(self.config.items(), key=lambda tup: -len(tup[1])): # sort to make greedy bin packing work better
if k1 in self.ignore_cats:
continue
d = {k: v for k, v in all_d.items() if isinstance(v, (int, float, str, bool)) and not k.startswith("_")} # no lists . dict could be supported but hard to scale
cat = GridLayout(cols=2, rows=len(d) + 1, size_hint=(1, len(d) + 1))
cat.add_widget(Label(text=""))
cat.add_widget(ScaledLightLabel(text=f"{k1} settings", bold=True))
for k2, v in d.items():
label = ScaledLightLabel(text=f"{k2}:")
widget = self.type_to_widget_class(v)(text=str(v), input_property=f"{k1}/{k2}")
hint = all_d.get("_hint_" + k2)
if hint:
label.tooltip_text = hint
if isinstance(widget, LabelledTextInput):
widget.hint_text = hint
cat.add_widget(label)
cat.add_widget(widget)
if props_in_col[0] <= props_in_col[1]:
cols[0].add_widget(cat)
props_in_col[0] += len(d)
else:
cols[1].add_widget(cat)
props_in_col[1] += len(d)
col_container = BoxLayout(size_hint=(1, 0.9))
col_container.add_widget(cols[0])
col_container.add_widget(cols[1])
self.add_widget(col_container)
self.info_label = Label(halign="center")
self.apply_button = StyledButton(text="Apply", on_press=lambda _: self.update_config())
self.save_button = StyledButton(text="Apply and Save", on_press=lambda _: self.update_config(save_to_file=True))
btn_container = BoxLayout(orientation="horizontal", size_hint=(1, 0.1), spacing=1, padding=1)
btn_container.add_widget(self.apply_button)
btn_container.add_widget(self.info_label)
btn_container.add_widget(self.save_button)
self.add_widget(btn_container)
def update_config(self, save_to_file=False):
updated_cat = defaultdict(list) # type: DefaultDict[List[str]]
try:
for k, v in self.collect_properties(self).items():
k1, k2 = k.split("/")
if self.config[k1][k2] != v:
self.katrain.log(f"Updating setting {k} = {v}", OUTPUT_DEBUG)
updated_cat[k1].append(k2)
self.config[k1][k2] = v
self.popup.dismiss()
except InputParseError as e:
self.info_label.text = str(e)
self.katrain.log(e, OUTPUT_ERROR)
return
if save_to_file:
self.katrain.save_config()
engine_updates = updated_cat["engine"]
if "visits" in engine_updates:
self.katrain.engine.visits = engine_updates["visits"]
if {key for key in engine_updates if key not in {"max_visits", "max_time", "enable_ownership", "wide_root_noise"}}:
self.katrain.log(f"Restarting Engine after {engine_updates} settings change")
self.info_label.text = "Restarting engine\nplease wait."
self.katrain.controls.set_status(f"Restarted Engine after {engine_updates} settings change.")
ignore = {"max_visits", "max_time", "enable_ownership", "wide_root_noise"}
detected_restart = [key for key in updated if "engine" in key and not any(ig in key for ig in ignore)]
if detected_restart:
def restart_engine(_dt):
self.katrain.log(f"Restarting Engine after {detected_restart} settings change")
self.katrain.controls.set_status(i18n._("restarting engine"))
old_engine = self.katrain.engine # type: KataGoEngine
old_proc = old_engine.katago_process
if old_proc:
old_engine.shutdown(finish=True)
old_engine.shutdown(finish=False)
new_engine = KataGoEngine(self.katrain, self.config["engine"])
self.katrain.engine = new_engine
self.katrain.game.engines = {"B": new_engine, "W": new_engine}
@@ -276,91 +246,6 @@ class ConfigPopup(QuickConfigGui):
Clock.schedule_once(restart_engine, 0)
self.katrain.debug_level = self.config["debug"]["level"]
self.katrain.update_state(redraw_board=True)
class LoadSGFPopup(BoxLayout):
pass
class ConfigTeacherPopup(QuickConfigGui):
def __init__(self, katrain, popup, **kwargs):
self.settings = katrain.config("trainer")
self.sgf_settings = katrain.config("sgf")
self.ui_settings = katrain.config("board_ui")
super().__init__(katrain, popup, self.settings, **kwargs)
self.spacing = 2
Clock.schedule_once(self.build, 0)
def build(self, _dt):
thresholds = self.settings["eval_thresholds"]
undos = self.settings["num_undo_prompts"]
colors = self.ui_settings["eval_colors"]
thrbox = GridLayout(spacing=1, padding=2, cols=5, rows=len(thresholds) + 1)
thrbox.add_widget(ScaledLightLabel(text="Point loss greater than", bold=True))
thrbox.add_widget(ScaledLightLabel(text="Gives this many undos", bold=True))
thrbox.add_widget(ScaledLightLabel(text="Color (fixed)", bold=True))
thrbox.add_widget(ScaledLightLabel(text="Show dots", bold=True))
thrbox.add_widget(ScaledLightLabel(text="Save in SGF", bold=True))
for i, (thr, undos, color) in enumerate(zip(thresholds, undos, colors)):
thrbox.add_widget(LabelledFloatInput(text=str(thr), input_property=f"eval_thresholds::{i}"))
thrbox.add_widget(LabelledFloatInput(text=str(undos), input_property=f"num_undo_prompts::{i}"))
thrbox.add_widget(BackgroundMixin(background_color=color[:3]))
thrbox.add_widget(LabelledCheckBox(text=str(color[3] == 1), input_property=f"alpha::{i}"))
thrbox.add_widget(LabelledCheckBox(size_hint=(0.5, 1), text=str(self.sgf_settings["save_feedback"][i]), input_property=f"save_feedback::{i}"))
self.add_widget(thrbox)
xsettings = BoxLayout(size_hint=(1, 0.15), spacing=2)
xsettings.add_widget(ScaledLightLabel(text="Show last <n> dots"))
xsettings.add_widget(LabelledIntInput(size_hint=(0.5, 1), text=str(self.settings["eval_off_show_last"]), input_property="eval_off_show_last"))
self.add_widget(xsettings)
xsettings = BoxLayout(size_hint=(1, 0.15), spacing=2)
xsettings.add_widget(ScaledLightLabel(text="Show dots/SGF comments for AI players"))
xsettings.add_widget(LabelledCheckBox(size_hint=(0.5, 1), text=str(self.settings["eval_show_ai"]), input_property="eval_show_ai"))
self.add_widget(xsettings)
xsettings = BoxLayout(size_hint=(1, 0.15), spacing=2)
xsettings.add_widget(ScaledLightLabel(text="Disable analysis while in teach mode"))
xsettings.add_widget(LabelledCheckBox(size_hint=(0.5, 1), text=str(self.settings["lock_ai"]), input_property="lock_ai"))
self.add_widget(xsettings)
bl = BoxLayout(size_hint=(1, 0.15), spacing=2)
bl.add_widget(StyledButton(text=f"Apply", on_press=lambda _: self.update_config(False)))
self.info_label = Label()
bl.add_widget(self.info_label)
bl.add_widget(StyledButton(text=f"Apply and Save", on_press=lambda _: self.update_config(True)))
self.add_widget(bl)
def update_config(self, save_to_file=False):
try:
for k, v in self.collect_properties(self).items():
if "::" in k:
k1, i = k.split("::")
i = int(i)
if "alpha" in k1:
v = 1.0 if v else 0.0
if self.ui_settings["eval_colors"][i][3] != v:
self.katrain.log(f"Updating alpha {i} = {v}", OUTPUT_DEBUG)
self.ui_settings["eval_colors"][i][3] = v
elif "save_feedback" in k1:
if self.sgf_settings[k1][i] != v:
self.sgf_settings[k1][i] = v
self.katrain.log(f"Updating setting sgf/{k1}[{i}] = {v}", OUTPUT_DEBUG)
else:
if self.settings[k1][i] != v:
self.settings[k1][i] = v
self.katrain.log(f"Updating setting trainer/{k1}[{i}] = {v}", OUTPUT_DEBUG)
else:
if self.settings[k] != v:
self.settings[k] = v
self.katrain.log(f"Updating setting {k} = {v}", OUTPUT_DEBUG)
if save_to_file:
self.katrain.save_config()
self.popup.dismiss()
except InputParseError as e:
self.info_label.text = str(e)
self.katrain.log(e, OUTPUT_ERROR)
return
self.katrain.update_state()
self.popup.dismiss()