black
This commit is contained in:
1 parent
76c982536c
commit
5f25b04bc5
7 files changed
+86
-60
No files matched your search
+4
-5
@@ -158,7 +158,6 @@ class KaTrainGui(Screen, KaTrainBase):
|
|||||||
# update move tree
|
# update move tree
|
||||||
self.controls.move_tree.current_node = self.game.current_node
|
self.controls.move_tree.current_node = self.game.current_node
|
||||||
|
|
||||||
|
|
||||||
def update_state(
|
def update_state(
|
||||||
self, redraw_board=False
|
self, redraw_board=False
|
||||||
): # is called after every message and on receiving analyses and config changes
|
): # is called after every message and on receiving analyses and config changes
|
||||||
@@ -327,10 +326,10 @@ class KaTrainGui(Screen, KaTrainBase):
|
|||||||
self.fileselect_popup.dismiss()
|
self.fileselect_popup.dismiss()
|
||||||
path, file = os.path.split(files[0])
|
path, file = os.path.split(files[0])
|
||||||
settings_path = self.config("general/sgf_load")
|
settings_path = self.config("general/sgf_load")
|
||||||
if path!=settings_path:
|
if path != settings_path:
|
||||||
self.log(f"Updating sgf load path default to {path}",OUTPUT_INFO)
|
self.log(f"Updating sgf load path default to {path}", OUTPUT_INFO)
|
||||||
self._config['general']['sgf_load'] = path
|
self._config["general"]["sgf_load"] = path
|
||||||
self.save_config('general')
|
self.save_config("general")
|
||||||
try:
|
try:
|
||||||
move_tree = KaTrainSGF.parse_file(files[0])
|
move_tree = KaTrainSGF.parse_file(files[0])
|
||||||
except ParseError as e:
|
except ParseError as e:
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from katrain.core.utils import find_package_resource
|
|||||||
|
|
||||||
from kivy.utils import platform
|
from kivy.utils import platform
|
||||||
|
|
||||||
|
|
||||||
class EngineDiedException(Exception):
|
class EngineDiedException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ class SGF:
|
|||||||
|
|
||||||
_NODE_CLASS = SGFNode # Class used for SGF Nodes, can change this to something that inherits from SGFNode
|
_NODE_CLASS = SGFNode # Class used for SGF Nodes, can change this to something that inherits from SGFNode
|
||||||
# https://xkcd.com/1171/
|
# https://xkcd.com/1171/
|
||||||
SGFPROP_PAT=re.compile(r"\s*(?:\(|\)|;|(\w+)((\s*\[([^\]\\]|\\.)*\])+))",flags=re.DOTALL)
|
SGFPROP_PAT = re.compile(r"\s*(?:\(|\)|;|(\w+)((\s*\[([^\]\\]|\\.)*\])+))", flags=re.DOTALL)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, input_str) -> SGFNode:
|
def parse(cls, input_str) -> SGFNode:
|
||||||
|
|||||||
@@ -284,9 +284,9 @@ class PlayerSetupBlock(MDBoxLayout):
|
|||||||
PlayerSetupBlock.INSTANCES.append(self)
|
PlayerSetupBlock.INSTANCES.append(self)
|
||||||
|
|
||||||
def swap_players(self):
|
def swap_players(self):
|
||||||
player_dump = {bw: p.player_type_dump for bw,p in self.players.items()}
|
player_dump = {bw: p.player_type_dump for bw, p in self.players.items()}
|
||||||
for bw in 'BW':
|
for bw in "BW":
|
||||||
self.update_players(bw,player_dump['B' if bw=='W' else 'W'])
|
self.update_players(bw, player_dump["B" if bw == "W" else "W"])
|
||||||
|
|
||||||
def update_players(self, bw, player_info): # update sub widget based on gui state change
|
def update_players(self, bw, player_info): # update sub widget based on gui state change
|
||||||
self.players[bw].update_widget(player_type=player_info.player_type, player_subtype=player_info.player_subtype)
|
self.players[bw].update_widget(player_type=player_info.player_type, player_subtype=player_info.player_subtype)
|
||||||
@@ -299,9 +299,11 @@ class PlayerInfo(MDBoxLayout, BackgroundMixin):
|
|||||||
player_subtype = StringProperty("")
|
player_subtype = StringProperty("")
|
||||||
active = BooleanProperty(True)
|
active = BooleanProperty(True)
|
||||||
|
|
||||||
|
|
||||||
class TimerOrMoveTree(BoxLayout):
|
class TimerOrMoveTree(BoxLayout):
|
||||||
mode = StringProperty(MODE_PLAY)
|
mode = StringProperty(MODE_PLAY)
|
||||||
|
|
||||||
|
|
||||||
class Timer(BGBoxLayout):
|
class Timer(BGBoxLayout):
|
||||||
state = ListProperty([30, 5, 1])
|
state = ListProperty([30, 5, 1])
|
||||||
timeout = BooleanProperty(False)
|
timeout = BooleanProperty(False)
|
||||||
|
|||||||
+22
-16
@@ -115,7 +115,7 @@ class LabelledFloatInput(LabelledTextInput):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def input_value(self):
|
def input_value(self):
|
||||||
return float('0'+self.text)
|
return float("0" + self.text)
|
||||||
|
|
||||||
|
|
||||||
class LabelledIntInput(LabelledTextInput):
|
class LabelledIntInput(LabelledTextInput):
|
||||||
@@ -126,7 +126,7 @@ class LabelledIntInput(LabelledTextInput):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def input_value(self):
|
def input_value(self):
|
||||||
return int('0'+self.text)
|
return int("0" + self.text)
|
||||||
|
|
||||||
|
|
||||||
class InputParseError(Exception):
|
class InputParseError(Exception):
|
||||||
@@ -138,7 +138,7 @@ class QuickConfigGui(MDBoxLayout):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.katrain = katrain
|
self.katrain = katrain
|
||||||
self.popup = None
|
self.popup = None
|
||||||
Clock.schedule_once(self.build_and_set_properties,0)
|
Clock.schedule_once(self.build_and_set_properties, 0)
|
||||||
|
|
||||||
def collect_properties(self, widget) -> Dict:
|
def collect_properties(self, widget) -> Dict:
|
||||||
if isinstance(widget, (LabelledTextInput, LabelledSpinner, LabelledCheckBox)) and getattr(
|
if isinstance(widget, (LabelledTextInput, LabelledSpinner, LabelledCheckBox)) and getattr(
|
||||||
@@ -147,7 +147,9 @@ class QuickConfigGui(MDBoxLayout):
|
|||||||
try:
|
try:
|
||||||
ret = {widget.input_property: widget.input_value}
|
ret = {widget.input_property: widget.input_value}
|
||||||
except Exception as e: # TODO : on widget?
|
except Exception as e: # TODO : on widget?
|
||||||
raise InputParseError(f"Could not parse value '{widget.raw_input_value}' for {widget.input_property} ({widget.__class__.__name__}): {e}")
|
raise InputParseError(
|
||||||
|
f"Could not parse value '{widget.raw_input_value}' for {widget.input_property} ({widget.__class__.__name__}): {e}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
ret = {}
|
ret = {}
|
||||||
for c in widget.children:
|
for c in widget.children:
|
||||||
@@ -177,7 +179,7 @@ class QuickConfigGui(MDBoxLayout):
|
|||||||
)
|
)
|
||||||
return config[keys[-1]], config, keys[-1]
|
return config[keys[-1]], config, keys[-1]
|
||||||
|
|
||||||
def build_and_set_properties(self,*_args):
|
def build_and_set_properties(self, *_args):
|
||||||
return self._set_properties_subtree(self)
|
return self._set_properties_subtree(self)
|
||||||
|
|
||||||
def _set_properties_subtree(self, widget):
|
def _set_properties_subtree(self, widget):
|
||||||
@@ -254,7 +256,7 @@ class ConfigTeacherPopup(QuickConfigGui):
|
|||||||
for widget in widgets:
|
for widget in widgets:
|
||||||
self.options_grid.add_widget(wrap_anchor(widget))
|
self.options_grid.add_widget(wrap_anchor(widget))
|
||||||
|
|
||||||
def build_and_set_properties(self,*_args):
|
def build_and_set_properties(self, *_args):
|
||||||
undos = self.katrain.config("trainer/num_undo_prompts")
|
undos = self.katrain.config("trainer/num_undo_prompts")
|
||||||
thresholds = self.katrain.config("trainer/eval_thresholds")
|
thresholds = self.katrain.config("trainer/eval_thresholds")
|
||||||
savesgfs = self.katrain.config("trainer/save_feedback")
|
savesgfs = self.katrain.config("trainer/save_feedback")
|
||||||
@@ -306,35 +308,39 @@ class AIPopup(QuickConfigGui):
|
|||||||
|
|
||||||
|
|
||||||
class ConfigPopup(QuickConfigGui):
|
class ConfigPopup(QuickConfigGui):
|
||||||
|
def build_and_set_properties(self, *_args):
|
||||||
def build_and_set_properties(self,*_args):
|
|
||||||
super().build_and_set_properties()
|
super().build_and_set_properties()
|
||||||
self.check_models()
|
self.check_models()
|
||||||
|
|
||||||
def check_models(self,*args): # WIP
|
def check_models(self, *args): # WIP
|
||||||
try:
|
try:
|
||||||
model = self.collect_properties(self)['engine/model']
|
model = self.collect_properties(self)["engine/model"]
|
||||||
except InputParseError:
|
except InputParseError:
|
||||||
self.model_files.values = []
|
self.model_files.values = []
|
||||||
return
|
return
|
||||||
|
|
||||||
if os.path.exists(model):
|
if os.path.exists(model):
|
||||||
if os.path.isdir(model):
|
if os.path.isdir(model):
|
||||||
path = model.rstrip('\\/')
|
path = model.rstrip("\\/")
|
||||||
file = None
|
file = None
|
||||||
else:
|
else:
|
||||||
if model.startswith('katrain'):
|
if model.startswith("katrain"):
|
||||||
model = find_package_resource(model)
|
model = find_package_resource(model)
|
||||||
path, file = os.path.split(model)
|
path, file = os.path.split(model)
|
||||||
files = sorted([os.path.split(f)[1] for ftype in ['*.bin.gz','*.txt.gz'] for f in glob.glob(path+os.path.sep+ftype)])
|
files = sorted(
|
||||||
self.model_files.values =files
|
[
|
||||||
print(file,files,file in files)
|
os.path.split(f)[1]
|
||||||
|
for ftype in ["*.bin.gz", "*.txt.gz"]
|
||||||
|
for f in glob.glob(path + os.path.sep + ftype)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.model_files.values = files
|
||||||
|
print(file, files, file in files)
|
||||||
if file in files:
|
if file in files:
|
||||||
self.model_files.text = file
|
self.model_files.text = file
|
||||||
else:
|
else:
|
||||||
self.model_files.values = []
|
self.model_files.values = []
|
||||||
|
|
||||||
|
|
||||||
def update_config(self, save_to_file=True):
|
def update_config(self, save_to_file=True):
|
||||||
updated = super().update_config(save_to_file=save_to_file)
|
updated = super().update_config(save_to_file=save_to_file)
|
||||||
self.katrain.debug_level = self.katrain.config("general/debug_level", OUTPUT_INFO)
|
self.katrain.debug_level = self.katrain.config("general/debug_level", OUTPUT_INFO)
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ BLACK = [0.05, 0.05, 0.05, 1]
|
|||||||
LIGHTGREY = [0.7, 0.7, 0.7, 1]
|
LIGHTGREY = [0.7, 0.7, 0.7, 1]
|
||||||
|
|
||||||
RED = [0.8, 0.1, 0.1, 1]
|
RED = [0.8, 0.1, 0.1, 1]
|
||||||
GREEN = [0.1,0.8,0.1,1]
|
GREEN = [0.1, 0.8, 0.1, 1]
|
||||||
YELLOW = [0.8,0.8,0.1,1]
|
YELLOW = [0.8, 0.8, 0.1, 1]
|
||||||
DARKRED = [0.3,0.1,0.1,1]
|
DARKRED = [0.3, 0.1, 0.1, 1]
|
||||||
ORANGE = [242/255,96/255,34/255,1]
|
ORANGE = [242 / 255, 96 / 255, 34 / 255, 1]
|
||||||
|
|
||||||
# gui colors
|
# gui colors
|
||||||
BACKGROUND_COLOR = [36 / 255, 48 / 255, 62 / 255, 1]
|
BACKGROUND_COLOR = [36 / 255, 48 / 255, 62 / 255, 1]
|
||||||
@@ -39,7 +39,7 @@ EVAL_DOT_MIN_SIZE = 0.25
|
|||||||
|
|
||||||
STONE_COLORS = {"B": BLACK, "W": WHITE}
|
STONE_COLORS = {"B": BLACK, "W": WHITE}
|
||||||
OUTLINE_COLORS = {"B": [0.3, 0.3, 0.3, 0.5], "W": [0.7, 0.7, 0.7, 0.5]}
|
OUTLINE_COLORS = {"B": [0.3, 0.3, 0.3, 0.5], "W": [0.7, 0.7, 0.7, 0.5]}
|
||||||
STONE_TEXT_COLORS= {"W": BLACK, "B": WHITE}
|
STONE_TEXT_COLORS = {"W": BLACK, "B": WHITE}
|
||||||
|
|
||||||
# board
|
# board
|
||||||
LINE_COLOR = [0, 0, 0]
|
LINE_COLOR = [0, 0, 0]
|
||||||
|
|||||||
@@ -10,7 +10,16 @@ from kivymd.app import MDApp
|
|||||||
|
|
||||||
from katrain.core.game_node import GameNode
|
from katrain.core.game_node import GameNode
|
||||||
from katrain.gui.kivyutils import BackgroundMixin, draw_circle, draw_text
|
from katrain.gui.kivyutils import BackgroundMixin, draw_circle, draw_text
|
||||||
from katrain.gui.style import WHITE, STONE_COLORS, LIGHTGREY, BACKGROUND_COLOR, OUTLINE_COLORS, LIGHTER_BACKGROUND_COLOR, YELLOW, STONE_TEXT_COLORS
|
from katrain.gui.style import (
|
||||||
|
WHITE,
|
||||||
|
STONE_COLORS,
|
||||||
|
LIGHTGREY,
|
||||||
|
BACKGROUND_COLOR,
|
||||||
|
OUTLINE_COLORS,
|
||||||
|
LIGHTER_BACKGROUND_COLOR,
|
||||||
|
YELLOW,
|
||||||
|
STONE_TEXT_COLORS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MoveTreeCanvas(Widget):
|
class MoveTreeCanvas(Widget):
|
||||||
@@ -19,9 +28,11 @@ class MoveTreeCanvas(Widget):
|
|||||||
move_tree = ObjectProperty(None)
|
move_tree = ObjectProperty(None)
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
def on_touch_up(self, touch):
|
||||||
if touch.button=='left':
|
if touch.button == "left":
|
||||||
node, (x,y) = min(self.move_xy_pos.items(),key=lambda n_xy: abs(n_xy[1][0]-touch.x)+abs(n_xy[1][1]-touch.y) )
|
node, (x, y) = min(
|
||||||
if max(abs(x-touch.x),abs(y-touch.y)) <= (self.move_size/2):
|
self.move_xy_pos.items(), key=lambda n_xy: abs(n_xy[1][0] - touch.x) + abs(n_xy[1][1] - touch.y)
|
||||||
|
)
|
||||||
|
if max(abs(x - touch.x), abs(y - touch.y)) <= (self.move_size / 2):
|
||||||
katrain = MDApp.get_running_app().gui
|
katrain = MDApp.get_running_app().gui
|
||||||
katrain.game.set_current_node(node)
|
katrain.game.set_current_node(node)
|
||||||
katrain.update_state()
|
katrain.update_state()
|
||||||
@@ -31,75 +42,81 @@ class MoveTreeCanvas(Widget):
|
|||||||
return
|
return
|
||||||
spacing = 5
|
spacing = 5
|
||||||
moves_vert = 3
|
moves_vert = 3
|
||||||
self.move_size = (self.move_tree.height - (moves_vert+1) * spacing) / moves_vert
|
self.move_size = (self.move_tree.height - (moves_vert + 1) * spacing) / moves_vert
|
||||||
|
|
||||||
root = current_node.root
|
root = current_node.root
|
||||||
|
|
||||||
move_pos = {root: (0,0)}
|
move_pos = {root: (0, 0)}
|
||||||
stack = GameNode.order_children( root.children )[::-1]
|
stack = GameNode.order_children(root.children)[::-1]
|
||||||
next_y_pos = defaultdict(int) # x pos -> max y pos
|
next_y_pos = defaultdict(int) # x pos -> max y pos
|
||||||
|
|
||||||
while stack:
|
while stack:
|
||||||
move = stack.pop()
|
move = stack.pop()
|
||||||
x = move.depth
|
x = move.depth
|
||||||
y = max(next_y_pos[x], move_pos[move.parent][1])
|
y = max(next_y_pos[x], move_pos[move.parent][1])
|
||||||
next_y_pos[x] = y+1
|
next_y_pos[x] = y + 1
|
||||||
next_y_pos[x-1] = max(next_y_pos[x],next_y_pos[x-1])
|
next_y_pos[x - 1] = max(next_y_pos[x], next_y_pos[x - 1])
|
||||||
move_pos[move] = (x,y)
|
move_pos[move] = (x, y)
|
||||||
for c in GameNode.order_children( move.children )[::-1]: # stack, so push top child last to process first
|
for c in GameNode.order_children(move.children)[::-1]: # stack, so push top child last to process first
|
||||||
stack.append(c)
|
stack.append(c)
|
||||||
|
|
||||||
def draw_stone(pos, player):
|
def draw_stone(pos, player):
|
||||||
draw_circle(pos,self.move_size/2-0.5,STONE_COLORS[player])
|
draw_circle(pos, self.move_size / 2 - 0.5, STONE_COLORS[player])
|
||||||
Color(*STONE_TEXT_COLORS[player])
|
Color(*STONE_TEXT_COLORS[player])
|
||||||
Line(circle=(*pos, self.move_size/2), width=1)
|
Line(circle=(*pos, self.move_size / 2), width=1)
|
||||||
|
|
||||||
def coord_pos(coord):
|
def coord_pos(coord):
|
||||||
return (coord + 0.5) * (spacing + self.move_size) + spacing / 2
|
return (coord + 0.5) * (spacing + self.move_size) + spacing / 2
|
||||||
|
|
||||||
self.width = coord_pos(max(x+0.5 for x, y in move_pos.values()))
|
self.width = coord_pos(max(x + 0.5 for x, y in move_pos.values()))
|
||||||
self.height = coord_pos(max(y+0.5 for x, y in move_pos.values()))
|
self.height = coord_pos(max(y + 0.5 for x, y in move_pos.values()))
|
||||||
|
|
||||||
def xy_pos(x,y):
|
def xy_pos(x, y):
|
||||||
return coord_pos(x), self.height - coord_pos(y)
|
return coord_pos(x), self.height - coord_pos(y)
|
||||||
|
|
||||||
self.move_xy_pos = {n: xy_pos(x,y) for n,(x,y) in move_pos.items() }
|
self.move_xy_pos = {n: xy_pos(x, y) for n, (x, y) in move_pos.items()}
|
||||||
|
|
||||||
self.canvas.clear()
|
self.canvas.clear()
|
||||||
with self.canvas:
|
with self.canvas:
|
||||||
Color(*YELLOW)
|
Color(*YELLOW)
|
||||||
Rectangle(pos=[c-self.move_size/2-spacing/2 for c in self.move_xy_pos[current_node]],size=(self.move_size+spacing,self.move_size+spacing))
|
Rectangle(
|
||||||
|
pos=[c - self.move_size / 2 - spacing / 2 for c in self.move_xy_pos[current_node]],
|
||||||
|
size=(self.move_size + spacing, self.move_size + spacing),
|
||||||
|
)
|
||||||
Color(*LIGHTGREY)
|
Color(*LIGHTGREY)
|
||||||
for node, (x,y) in self.move_xy_pos.items():
|
for node, (x, y) in self.move_xy_pos.items():
|
||||||
for ci, c in enumerate(GameNode.order_children(node.children)):
|
for ci, c in enumerate(GameNode.order_children(node.children)):
|
||||||
cx,cy = self.move_xy_pos[c]
|
cx, cy = self.move_xy_pos[c]
|
||||||
Line(points=[x,y,x,cy,cx,cy],width=1)
|
Line(points=[x, y, x, cy, cx, cy], width=1)
|
||||||
|
|
||||||
for node, pos in self.move_xy_pos.items():
|
for node, pos in self.move_xy_pos.items():
|
||||||
draw_stone(pos, node.player)
|
draw_stone(pos, node.player)
|
||||||
text = str(node.depth)
|
text = str(node.depth)
|
||||||
Color(*STONE_COLORS['W' if node.player=='B' else 'B'])
|
Color(*STONE_COLORS["W" if node.player == "B" else "B"])
|
||||||
draw_text(pos=pos, text=text, font_size=self.move_size * 1.75 / (1 + 1 * len(text)), font_name="Roboto")
|
draw_text(pos=pos, text=text, font_size=self.move_size * 1.75 / (1 + 1 * len(text)), font_name="Roboto")
|
||||||
|
|
||||||
self.move_tree.scroll_to_pixel(*self.move_xy_pos[current_node])
|
self.move_tree.scroll_to_pixel(*self.move_xy_pos[current_node])
|
||||||
|
|
||||||
|
|
||||||
class MoveTree(ScrollView,BackgroundMixin):
|
class MoveTree(ScrollView, BackgroundMixin):
|
||||||
current_node = ObjectProperty(None)
|
current_node = ObjectProperty(None)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.redraw_tree_trigger = Clock.create_trigger(lambda _dt: self.move_tree_canvas.draw_move_tree(self.current_node))
|
self.redraw_tree_trigger = Clock.create_trigger(
|
||||||
self.bind(current_node=self.redraw_tree_trigger,size=self.redraw_tree_trigger)
|
lambda _dt: self.move_tree_canvas.draw_move_tree(self.current_node)
|
||||||
|
)
|
||||||
|
self.bind(current_node=self.redraw_tree_trigger, size=self.redraw_tree_trigger)
|
||||||
|
|
||||||
def scroll_to_pixel(self, x, y):
|
def scroll_to_pixel(self, x, y):
|
||||||
if not self._viewport:
|
if not self._viewport:
|
||||||
return
|
return
|
||||||
vp = self._viewport
|
vp = self._viewport
|
||||||
if vp.width > self.width:
|
if vp.width > self.width:
|
||||||
sx = (x - self.width/2) / (vp.width - self.width)
|
sx = (x - self.width / 2) / (vp.width - self.width)
|
||||||
self.scroll_x =max(0, min(1, sx))
|
self.scroll_x = max(0, min(1, sx))
|
||||||
if vp.height > self.height:
|
if vp.height > self.height:
|
||||||
sy = (y - self.height/2) / (vp.height - self.height)
|
sy = (y - self.height / 2) / (vp.height - self.height)
|
||||||
self.scroll_y = max(0, min(1, sy))
|
self.scroll_y = max(0, min(1, sy))
|
||||||
|
|
||||||
|
|
||||||
@@ -112,4 +129,5 @@ Builder.load_string(
|
|||||||
move_tree: root
|
move_tree: root
|
||||||
id: move_tree_canvas
|
id: move_tree_canvas
|
||||||
size_hint: None, None
|
size_hint: None, None
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
Reference in new issue
Block a user