ui fiddling

This commit is contained in:
Sander Land committed 2020-04-21 16:54:55 +02:00
1 parent 3ea068ccd0
commit d14f077e71
16 files changed
+434 -294

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
[x] Player button to black/white with AI style spinner
[] Multi engine (detect all req. analysis in analysis[engine] fn)
[] Non square boards
[] Polish graph
[x] remove fast -> in settings?
[] List edit settings/object edit settings?
[] README
[] Release notes
[] remove button disable code?
+6 -8
View File
@@ -4,8 +4,7 @@
"model": " models/b15-1.3.2.txt.gz",
"config": "KataGo/analysis_config.cfg",
"threads": 8,
"visits": 2000,
"visits_fast": 500,
"visits": 500,
"enable_ownership": true
},
"files": {
@@ -19,13 +18,12 @@
"komi_9": 6.5
},
"trainer": {
"lock_undos": true,
"num_undo_prompts": [1,1,1,0.5,0],
"eval_thresholds": [12,6,3,1.5,0.5],
"balance_play_target_score": 2,
"balance_play_randomize_score": 1,
"balance_play_max_lost": 5,
"balance_play_min_visits": 20,
"balance_target_score": 2,
"balance_randomize_score": 1,
"balance_max_lost": 5,
"balance_min_visits": 20,
"eval_off_show_last": 3
},
"board_ui": {
@@ -41,7 +39,7 @@
"eval_colors": [[0.537, 0.129, 0.42], [1, 0, 0], [1,0.5,0], [0.88, 0.70, 0.30], [0.67, 0.95, 0.18], [0.117, 0.588, 0]],
"board_color": [0.85, 0.68, 0.40],
"line_color": [0,0,0],
"policy_color": [0,0.2,0.8],
"policy_color": [0,0.2,0.8]
},
"debug": {
"level": 2
+6 -14
View File
@@ -28,7 +28,6 @@ class KataGoEngine:
self.command = shlex.split(self.command)
self.queries = {} # outstanding query id -> start time and callback
self.config = config
self.visits = [config["visits"], config["visits_fast"]]
self.query_counter = 0
self.katago_process = None
self.base_priority = 0
@@ -79,34 +78,27 @@ class KataGoEngine:
else:
self.katrain.log(f"Query result {analysis['id']} discarded -- recent new game?", OUTPUT_DEBUG)
def request_analysis(
self, analysis_node: GameNode, callback: Callable, faster: bool = False, visits: int = None, priority: int = 0, ownership: Optional[bool] = None, refine_move=None
):
fast = self.katrain.controls.ai_fast.active
def request_analysis(self, analysis_node: GameNode, callback: Callable, visits: int = None, priority: int = 0, ownership: Optional[bool] = None, next_move=None):
query_id = f"QUERY:{str(self.query_counter)}"
self.query_counter += 1
if not visits:
visits = self.config["visits_fast" if fast else "visits"]
if faster:
visits /= 5
moves = [m for node in analysis_node.nodes_from_root for m in node.move_with_placements]
if refine_move:
moves.append(refine_move)
if next_move:
moves.append(next_move)
if ownership is None:
ownership = self.config["enable_ownership"] and not refine_move
ownership = self.config["enable_ownership"] and not next_move
query = {
"id": query_id,
"rules": self.get_rules(analysis_node),
"priority": self.base_priority + priority,
"analyzeTurns": [len(moves)],
"maxVisits": visits,
"maxVisits": visits or self.config["visits"],
"komi": analysis_node.komi,
"boardXSize": analysis_node.board_size,
"boardYSize": analysis_node.board_size,
"includeOwnership": ownership,
"includePolicy": not refine_move,
"includePolicy": not next_move,
"moves": [[m.player, m.gtp()] for m in moves],
}
self.queries[query_id] = (callback, time.time())
+12 -10
View File
@@ -7,7 +7,7 @@ from typing import List
from kivy.clock import Clock
from constants import OUTPUT_DEBUG
from constants import OUTPUT_DEBUG, OUTPUT_INFO
from game_node import GameNode
from sgf_parser import SGF, Move
@@ -202,27 +202,29 @@ class Game:
time.sleep(0.05)
# select move
candidate_ai_moves = cn.candidate_moves
ai_mode = self.katrain.controls.player_mode(cn.next_player)
ai_mode = self.katrain.controls.ai_mode(cn.next_player)
if "policy" in ai_mode and cn.policy:
policy_moves = cn.policy_ranking
self.katrain.log(f"Top 5 policy moves are: {policy_moves[:5]}", OUTPUT_DEBUG)
aimove = policy_moves[0][0]
elif "balance" in ai_mode and candidate_ai_moves[0]['move'] != "pass": # don't play suicidal to balance score - pass when it's best
sign = cn.player_sign(cn.next_player) # TODO check
sel_moves = [ # top move, or anything not too bad, or anything that makes you still ahead
elif "balance" in ai_mode and candidate_ai_moves[0]["move"] != "pass": # don't play suicidal to balance score - pass when it's best
sign = cn.player_sign(cn.next_player) # TODO check
sel_moves = [ # top move, or anything not too bad, or anything that makes you still ahead
move
for i, move in enumerate(candidate_ai_moves)
if i == 0
or move["visits"] >= train_settings["balance_play_min_visits"]
or move["visits"] >= train_settings["balance_min_visits"]
and (
move["pointsLost"] < train_settings["balance_play_randomize_score"]
or move["pointsLost"] < train_settings["balance_play_max_lost"]
and sign * move["scoreLead"] > train_settings["balance_play_target_score"]
move["pointsLost"] < train_settings["balance_randomize_score"]
or move["pointsLost"] < train_settings["balance_max_lost"]
and sign * move["scoreLead"] > train_settings["balance_target_score"]
)
]
aimove = Move.from_gtp(random.choice(sel_moves)["move"], player=cn.next_player) # TODO: could be weighted towards worse
aimove = Move.from_gtp(random.choice(sel_moves)["move"], player=cn.next_player) # TODO: could be weighted towards worse
else:
if "default" not in ai_mode:
self.katrain.log(f"Unknown AI mode {ai_mode}, using default.", OUTPUT_INFO)
aimove = Move.from_gtp(candidate_ai_moves[0]["move"], player=cn.next_player)
self.play(aimove)
+3 -3
View File
@@ -30,7 +30,7 @@ class GameNode(SGFNode):
# various analysis functions
def analyze(self, engine, priority=0, visits=None, refine_move=None):
engine.request_analysis(self, lambda result: self.set_analysis(result, refine_move), priority=priority, visits=visits, refine_move=refine_move)
engine.request_analysis(self, lambda result: self.set_analysis(result, refine_move), priority=priority, visits=visits, next_move=refine_move)
def update_move_analysis(self, move_analysis, move_gtp):
cur = self.analysis["moves"].get(move_gtp)
@@ -120,7 +120,7 @@ class GameNode(SGFNode):
moves = []
for y in range(self.board_size - 1, -1, -1):
for x in range(self.board_size):
moves.append((Move((x, y),player=self.next_player), self.policy[ix]))
moves.append((Move((x, y), player=self.next_player), self.policy[ix]))
ix += 1
moves.append((Move(None,player=self.next_player), self.policy[ix]))
moves.append((Move(None, player=self.next_player), self.policy[ix]))
return sorted(moves, key=lambda mp: -mp[1])
+1 -13
View File
@@ -1,16 +1,4 @@
from gui.badukpan import BadukPanWidget, BadukPanControls
from gui.controls import Controls
from gui.kivyutils import (
BWCheckBoxHint,
CensorableLabel,
CheckBoxHint,
LabelledFloatInput,
LabelledIntInput,
StyledButton,
DarkLabel,
BaseCircleWithText,
LabelledSpinner,
LabelledCheckBox,
LabelledTextInput,
)
from gui.kivyutils import *
from gui.popups import LoadSGFPopup
+16 -13
View File
@@ -59,9 +59,8 @@ class BadukPanWidget(Widget):
if nodes_here and max(yd, xd) < self.grid_size / 2: # load old comment
katrain.log(f"\nAnalysis:\n{nodes_here[-1].analysis}", OUTPUT_DEBUG)
katrain.log(f"\nParent Analysis:\n{nodes_here[-1].parent.analysis}", OUTPUT_DEBUG)
if not katrain.controls.ai_lock.active:
katrain.controls.info.text = nodes_here[-1].comment(sgf=True)
katrain.controls.show_evaluation_stats(nodes_here[-1])
katrain.controls.info.text = nodes_here[-1].comment(sgf=True)
katrain.controls.show_evaluation_stats(nodes_here[-1])
self.ghost_stone = None
self.draw_board_contents() # remove ghost
@@ -148,7 +147,8 @@ class BadukPanWidget(Widget):
# stones
current_node = katrain.game.current_node
next_player = katrain.game.next_player
full_eval_on = katrain.controls.eval.active_map
game_ended = katrain.game.game_ended
full_eval_on = katrain.controls.eval.active
has_stone = {}
drawn_stone = {}
for m in katrain.game.stones:
@@ -161,7 +161,7 @@ class BadukPanWidget(Widget):
evalsize = 1
for m in node.move_with_placements:
if has_stone.get(m.coords) and not drawn_stone.get(m.coords): # skip captures, last only for
move_eval_on = full_eval_on[m.player] or i < show_n_eval
move_eval_on = full_eval_on or i < show_n_eval
evalcol = self.eval_color(points_lost) if move_eval_on and points_lost is not None else None
inner = stone_color[m.opponent] if i == 0 else None
drawn_stone[m.coords] = m.player
@@ -181,7 +181,7 @@ class BadukPanWidget(Widget):
ix = ix + 1
policy = current_node.policy
if not policy and current_node.parent and current_node.parent.policy and 'ai' in katrain.controls.player_mode('B') and 'ai' in katrain.controls.player_mode('W'):
if not policy and current_node.parent and current_node.parent.policy and "ai" in katrain.controls.player_mode("B") and "ai" in katrain.controls.player_mode("W"):
policy = current_node.parent.policy # in the case of AI self-play we allow the policy to be one step out of date
pass_btn = katrain.board_controls.pass_btn
pass_btn.canvas.after.clear()
@@ -192,7 +192,10 @@ class BadukPanWidget(Widget):
for x in range(board_size):
if policy[ix] > 0:
polsize = math.sqrt(policy[ix])
policy_circle_color = (*self.ui_config["policy_color"], self.ui_config["ghost_alpha"] + self.ui_config["top_move_x_alpha"] * (policy[ix] == best_move_policy))
policy_circle_color = (
*self.ui_config["policy_color"],
self.ui_config["ghost_alpha"] + self.ui_config["top_move_x_alpha"] * (policy[ix] == best_move_policy),
)
self.draw_stone(x, y, policy_circle_color, scale=polsize)
ix = ix + 1
polsize = math.sqrt(policy[ix])
@@ -212,17 +215,17 @@ class BadukPanWidget(Widget):
self.draw_stone(m.coords[0], m.coords[1], (*stone_color[m.player][:3], alpha), None, None, evalcol, evalscale=scale, scale=scale)
# hints
if katrain.controls.hints.active(next_player):
if katrain.controls.hints.active and not game_ended:
hint_moves = current_node.candidate_moves
for i, d in enumerate(hint_moves):
move = Move.from_gtp(d["move"])
if move.coords is not None and move.coords not in undo_coords:
alpha, scale = self.ui_config["ghost_alpha"], 1.0
alpha, scale = self.ui_config["ghost_alpha"], 1.0
if i == 0:
c[3] += self.ui_config["top_move_x_alpha"]
elif d["visits"] < 0.1 * hint_moves[0]["visits"]: # TODO: config?
alpha += self.ui_config["top_move_x_alpha"]
elif d["visits"] < 0.1 * hint_moves[0]["visits"]: # TODO: config?
scale = 0.8
self.draw_stone(move.coords[0], move.coords[1], [*self.eval_color(d["pointsLost"]),alpha], scale=scale)
self.draw_stone(move.coords[0], move.coords[1], [*self.eval_color(d["pointsLost"]), alpha], scale=scale)
# hover next move ghost stone
if self.ghost_stone:
@@ -231,7 +234,7 @@ class BadukPanWidget(Widget):
# pass circle
passed = len(nodes) > 1 and current_node.is_pass
if passed:
if katrain.game.game_ended:
if game_ended:
text = "game\nend"
else:
text = "pass"
+9 -24
View File
@@ -1,6 +1,6 @@
from kivy.graphics.vertex_instructions import SmoothLine, Line
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Line, SmoothLine
from kivy.uix.boxlayout import BoxLayout
class Controls(BoxLayout):
@@ -41,11 +41,8 @@ class Controls(BoxLayout):
def player_mode(self, player):
return self.player_mode_groups[player].value
def unlock(self):
if self.ai_lock.active:
self.ai_lock.checkbox.trigger_action(duration=0)
for el in [self.ai_lock.checkbox, self.analyze_tab_button, self.ai_auto.white, self.ai_auto.black, self.ai_move]:
el.disabled = False
def ai_mode(self, player):
return self.ai_mode_groups[player].text.lower()
def on_size(self, *args):
self.update_evaluation()
@@ -63,26 +60,14 @@ class Controls(BoxLayout):
if current_node:
move = current_node.single_move
current_player_is_human_or_both_robots = not current_node.player or not self.ai_auto.active(current_node.player) or self.ai_auto.active(current_node.next_player)
current_player_is_human_or_both_robots = (
not current_node.player or "ai" not in self.player_mode(current_node.player) or "ai" in self.player_mode(current_node.next_player)
)
if current_player_is_human_or_both_robots and not current_node.is_root and move:
info += current_node.comment(eval=True, hints=self.hints.active(move.player))
info += current_node.comment(eval=True, hints=self.hints.active)
if current_player_is_human_or_both_robots:
self.show_evaluation_stats(current_node)
game_node = katrain.game.current_node
scores = [n.score for n in game_node.nodes_from_root]
# TODO: like redo, what is the node to redo / should we append? cache?
self.graph.canvas.clear()
with self.graph.canvas:
pt = []
nnscores = [s for s in scores if s is not None] + [-5, 5]
scale = max(max(*nnscores), -min(*nnscores)) * 1.05
xscale = self.graph.width * 0.9 / max(len(scores), 20)
ls = 0
for i, s in enumerate(scores):
ls = s or ls
pt.extend([self.graph.pos[0] + 0.05 * self.graph.width + i * xscale, self.graph.pos[1] + self.graph.height / 2 * (1 + ls / scale)])
Color(0, 0, 0)
Line(points=pt, width=1.0) # just set points?
self.graph.update_value(current_node.depth, current_node.score)
self.info.text = info
+55 -24
View File
@@ -10,6 +10,7 @@ import re
from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
from kivy.uix.dropdown import DropDown
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.spinner import Spinner
@@ -32,13 +33,21 @@ class DarkLabel(Label):
class StyledButton(Button):
pass
text_color = ListProperty([0.95, 0.95, 0.95, 1]) # TODO defaults as in ..
button_color = ListProperty([0.21, 0.28, 0.31, 1])
button_color_down = ListProperty([0.105, 0.14, 0.155, 1])
margin = ListProperty([1, 1, 1, 1]) # margin left bottom right top
radius = ListProperty((0,))
class StyledToggleButton(StyledButton, ToggleButtonBehavior):
value = StringProperty("")
class StyledSpinner(Spinner):
pass
class ToggleButtonContainer(GridLayout):
__events__ = ("on_selection",)
@@ -47,8 +56,7 @@ class ToggleButtonContainer(GridLayout):
selected = StringProperty("")
group = StringProperty(None)
autosize = BooleanProperty(True)
button_class = ObjectProperty(StyledToggleButton)
margin = ListProperty([1, 1, 0, 0])
margin = ListProperty([0, 0, 1, 1]) # TODO outer vs inner margin?
def __init__(self, **kwargs):
super().__init__(**kwargs)
@@ -66,20 +74,19 @@ class ToggleButtonContainer(GridLayout):
if len(self.labels) < len(self.options):
self.labels += self.options[len(self.labels) + 1 :]
def state_handler(btn,*args):
def state_handler(btn, *args):
self.dispatch("on_selection")
btn.state = 'down' # no toggle
btn.state = "down" # no toggle
for i, opt in enumerate(self.options):
state = "down" if opt == self.selected else "normal"
self.add_widget(self.button_class(group=self.group, text=self.labels[i], value=opt, state=state,
margin=self.margin,on_press=state_handler))
self.add_widget(StyledToggleButton(group=self.group, text=self.labels[i], value=opt, state=state, margin=self.margin, on_press=state_handler,))
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] + 3, 1)
tb.size_hint = (tb.texture_size[0] + 25, 1)
@property
def value(self):
@@ -96,6 +103,7 @@ class BaseCircleWithText(DarkLabel):
class LabelledTextInput(TextInput):
input_property = StringProperty("")
multiline = BooleanProperty(False)
@property
def input_value(self):
@@ -115,7 +123,7 @@ class LabelledCheckBox(CheckBox):
return bool(self.active)
class LabelledSpinner(Spinner):
class LabelledSpinner(StyledSpinner):
input_property = StringProperty("")
@property
@@ -156,30 +164,53 @@ class LabelledIntInput(LabelledTextInput):
return int(self.text)
class BWCheckBoxHint(BoxLayout):
__events__ = ("on_active",)
@property
def active_map(self):
return {"B": self.black.active, "W": self.white.active}
def active(self, player):
return self.active_map[player]
def on_active(self, *args):
pass
class CensorableLabel(BoxLayout):
@property
def text(self):
return self.value.text
class ScoreGraph(Label):
values = ListProperty([])
line_points = ListProperty([])
line = ObjectProperty(None)
dot = ObjectProperty(None)
highlighted_index = NumericProperty(0)
def __init__(self, **kwargs):
super().__init__(**kwargs)
with self.canvas:
Color(0, 0, 0)
self.line = Line(points=self.line_points, width=1.0)
Color(0.21, 0.28, 0.31)
self.dot = Ellipse(pos=self.pos, size=(2, 2))
def clear_values(self):
self.values = []
def on_size(self, *args):
if self.values:
val_range = min(self.values), max(self.values)
scale = max(5, max(-val_range[0], val_range[1]) * 1.05)
xscale = self.width * 0.9 / max(len(self.values), 20)
self.line_points = [[self.pos[0] + 0.05 * self.width + i * xscale, self.pos[1] + self.height / 2 * (1 + val / scale)] for i, val in enumerate(self.values)]
self.line.points = sum(self.line_points, [])
if self.highlighted_index:
self.dot.pos = [c - 1 for c in self.line_points[self.highlighted_index]]
def update_value(self, index, value):
self.values.extend([0] * max(0, index - (len(self.values) - 1)))
self.values[index] = value or 0
self.highlighted_index = index
self.on_size()
def draw_text(pos, text, **kw):
label = CoreLabel(text=text, bold=True, **kw)
label.refresh()
Rectangle(texture=label.texture, pos=(pos[0] - label.texture.size[0] / 2, pos[1] - label.texture.size[1] / 2), size=label.texture.size)
Rectangle(
texture=label.texture, pos=(pos[0] - label.texture.size[0] / 2, pos[1] - label.texture.size[1] / 2), size=label.texture.size,
)
def draw_circle(pos, r, col):
+31 -14
View File
@@ -1,3 +1,5 @@
from collections import defaultdict
from kivy.uix.boxlayout import BoxLayout
import os
from constants import OUTPUT_DEBUG, OUTPUT_ERROR
@@ -76,13 +78,15 @@ class ConfigPopup(QuickConfigGui):
else:
return LabelledTextInput
def __init__(self, katrain, popup, config):
def __init__(self, katrain, popup, config, ignore_cats):
self.config = config
self.orientation = "vertical"
super().__init__(katrain, popup)
cols = [BoxLayout(orientation="vertical"), BoxLayout(orientation="vertical")]
props_in_col = [0, 0]
for k1, all_d in config.items():
if k1 in ignore_cats:
continue
d = {k: v for k, v in all_d.items() if isinstance(v, (int, float, str, bool))} # no complex objects
cat = GridLayout(cols=2, rows=len(d) + 1, size_hint=(1, len(d) + 1))
cat.add_widget(Label(text=""))
@@ -101,22 +105,27 @@ class ConfigPopup(QuickConfigGui):
col_container.add_widget(cols[0])
col_container.add_widget(cols[1])
self.add_widget(col_container)
self.save_button = StyledButton(text="Apply Settings", on_press=lambda _: self.update_config(), size_hint=(1, 0.05))
self.save_button = StyledButton(text="Apply and Save Settings", on_press=lambda _: self.update_config(save_to_file=True), size_hint=(1, 0.05))
self.add_widget(self.save_button)
self.info_label = Label()
self.apply_button = StyledButton(text="Apply Settings", on_press=lambda _: self.update_config())
self.save_button = StyledButton(text="Apply and Save Settings", on_press=lambda _: self.update_config(save_to_file=True))
btn_container = BoxLayout(orientation="horizontal", size_hint=(1, 0.05))
btn_container.add_widget(self.info_label)
btn_container.add_widget(self.apply_button)
btn_container.add_widget(self.save_button)
self.add_widget(btn_container)
def update_config(self, save_to_file=False):
updated_cat = []
updated_cat = defaultdict(list)
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.append(k1)
updated_cat[k1].append(k2)
self.config[k1][k2] = v
self.popup.dismiss()
except InputParseError as e:
self.save_button.text = str(e) # TODO: nicer error
self.info_label.text = str(e)
self.katrain.log(e, OUTPUT_ERROR)
return
@@ -124,10 +133,18 @@ class ConfigPopup(QuickConfigGui):
for cat in updated_cat:
self.katrain.save_config(cat, **self.config[cat])
if "engine" in updated_cat:
self.katrain.log("Restarting Engine after settings change")
old_engine = self.katrain.engine
self.katrain.engine = KataGoEngine(self.katrain, self.config["engine"])
self.katrain.game.engine = self.katrain.engine
old_engine.shutdown(finish=True)
self.katrain.update_state(redraw_board=True)
engine_restart = False
for cat, updates in updated_cat.items():
if "engine" in cat: # TODO: multi engine support
if "visits" in updates:
self.katrain.engine.visits = self.config[cat]["visits"]
if set(updates) != {"visits"}:
self.katrain.log(f"Restarting Engine {cat} after {updates} settings change")
old_engine = self.katrain.engine
self.katrain.engine = KataGoEngine(self.katrain, self.config[cat])
self.katrain.game.engine = self.katrain.engine
old_engine.shutdown(finish=True)
engine_restart = True
if engine_restart:
self.katrain.update_state(redraw_board=True)
+1
View File
@@ -0,0 +1 @@
<svg id="_x31_px" enable-background="new 0 0 24 24" height="512" viewBox="0 0 24 24" width="512" xmlns="http://www.w3.org/2000/svg"><path d="m21 24h-18c-1.654 0-3-1.346-3-3s1.346-3 3-3h18c1.654 0 3 1.346 3 3s-1.346 3-3 3zm-18-5c-1.103 0-2 .897-2 2s.897 2 2 2h18c1.103 0 2-.897 2-2s-.897-2-2-2z"/><path d="m12 16c-.128 0-.256-.049-.354-.146l-5.5-5.5c-.142-.144-.185-.358-.108-.545s.26-.309.462-.309h3v-1.5c0-4.41 3.587-7.998 7.997-8h.003c.276 0 .5.224.5.5s-.224.5-.5.5c-1.654 0-3 1.346-3 3v5.5h3c.202 0 .385.122.462.309s.034.402-.108.545l-5.5 5.5c-.098.097-.226.146-.354.146zm-4.293-5.5 4.293 4.293 4.293-4.293h-2.293c-.276 0-.5-.224-.5-.5v-6c0-.78.225-1.508.611-2.124-2.151 1.195-3.611 3.493-3.611 6.124v2c0 .276-.224.5-.5.5z"/></svg>

After

Width:  |  Height:  |  Size: 734 B

+114
View File
@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<g>
<g>
<path d="M262.785,317.207c-1.913-4.032-6.736-5.752-10.769-3.837c-6.562,3.114-13.442,5.688-20.448,7.648
c-2.931,0.82-5.152,3.215-5.748,6.2l-8.552,42.758c-0.844,4.222-4.582,7.287-8.888,7.287h-23.329
c-4.306,0-8.043-3.064-8.888-7.287l-8.552-42.758c-0.597-2.985-2.819-5.38-5.748-6.2c-9.898-2.77-19.45-6.728-28.391-11.764
c-2.653-1.494-5.92-1.371-8.452,0.317l-36.286,24.19c-3.582,2.388-8.392,1.911-11.438-1.133L60.8,316.131
c-3.045-3.045-3.52-7.855-1.133-11.438l24.19-36.286c1.689-2.533,1.812-5.799,0.317-8.452
c-5.037-8.942-8.995-18.495-11.764-28.391c-0.82-2.931-3.215-5.152-6.2-5.748l-42.758-8.552
c-4.222-0.844-7.287-4.582-7.287-8.888v-23.329c0-4.306,3.064-8.043,7.287-8.888l42.758-8.552c2.985-0.597,5.38-2.819,6.2-5.748
c2.77-9.898,6.728-19.45,11.764-28.391c1.494-2.652,1.371-5.919-0.317-8.452L59.669,88.734
c-2.388-3.583-1.912-8.392,1.133-11.438l16.496-16.496c3.045-3.045,7.856-3.52,11.438-1.133l36.286,24.19
c2.532,1.689,5.8,1.811,8.452,0.317c8.941-5.036,18.493-8.994,28.391-11.764c2.931-0.82,5.152-3.215,5.748-6.2l8.552-42.758
c0.844-4.222,4.582-7.287,8.888-7.287h23.329c4.306,0,8.043,3.064,8.888,7.287l8.552,42.758c0.597,2.985,2.819,5.38,5.748,6.2
c9.898,2.77,19.45,6.728,28.391,11.764c2.653,1.494,5.92,1.371,8.452-0.317l36.286-24.19c3.582-2.388,8.391-1.911,11.438,1.133
l16.496,16.496c3.045,3.045,3.52,7.855,1.133,11.438l-24.19,36.286c-1.689,2.533-1.812,5.799-0.317,8.452
c5.036,8.941,8.994,18.493,11.764,28.391c0.82,2.931,3.215,5.152,6.2,5.748l42.758,8.552c4.222,0.844,7.287,4.582,7.287,8.888
v23.329c0,4.306-3.064,8.043-7.287,8.888l-42.758,8.552c-2.985,0.597-5.38,2.819-6.2,5.75c-2.009,7.18-4.66,14.223-7.88,20.934
c-1.932,4.025-0.234,8.854,3.791,10.785c4.025,1.933,8.855,0.234,10.785-3.791c2.906-6.057,5.401-12.354,7.442-18.777
l37.991-7.598c11.749-2.352,20.279-12.758,20.279-24.744v-23.329c0-11.986-8.53-22.391-20.284-24.743l-37.99-7.598
c-2.492-7.844-5.65-15.47-9.434-22.769l21.492-32.239c6.648-9.973,5.323-23.363-3.153-31.839l-16.496-16.496
c-8.477-8.476-21.867-9.8-31.839-3.153l-32.239,21.492c-7.299-3.783-14.925-6.942-22.769-9.434l-7.598-37.99
C230.772,8.53,220.366,0,208.38,0h-23.329c-11.986,0-22.391,8.53-24.743,20.284l-7.598,37.99
c-7.844,2.492-15.47,5.65-22.769,9.434L97.704,46.216c-9.972-6.648-23.363-5.324-31.839,3.153L49.369,65.865
c-8.475,8.476-9.801,21.865-3.153,31.839l21.492,32.239c-3.783,7.299-6.942,14.924-9.434,22.769l-37.991,7.598
C8.53,162.66,0,173.066,0,185.052v23.329c0,11.986,8.53,22.391,20.284,24.743l37.99,7.598c2.491,7.843,5.65,15.469,9.434,22.769
L46.216,295.73c-6.648,9.973-5.323,23.363,3.153,31.839l16.496,16.496c8.476,8.476,21.866,9.8,31.839,3.153l32.239-21.492
c7.299,3.783,14.924,6.942,22.769,9.434l7.598,37.992c2.35,11.75,12.756,20.281,24.742,20.281h23.329
c11.986,0,22.391-8.53,24.743-20.284l7.598-37.99c6.229-1.979,12.343-4.386,18.228-7.18
C262.983,326.063,264.701,321.241,262.785,317.207z"/>
<path d="M266.739,241.636c1.18,0.606,2.44,0.893,3.682,0.893c2.936,0,5.769-1.605,7.2-4.397
c6.52-12.715,9.967-27.036,9.967-41.415c0-50.107-40.765-90.873-90.873-90.873c-23.838,0-46.364,9.162-63.43,25.801
c-3.197,3.116-3.262,8.235-0.146,11.431c3.116,3.196,8.234,3.263,11.431,0.146c14.029-13.676,32.547-21.209,52.143-21.209
c41.192,0,74.705,33.512,74.705,74.705c0,11.994-2.754,23.446-8.185,34.038C261.197,234.726,262.766,239.598,266.739,241.636z"/>
<path d="M121.043,162.125c-4.223-1.453-8.821,0.792-10.274,5.014c-3.267,9.498-4.924,19.45-4.924,29.577
c0,50.108,40.766,90.873,90.873,90.873c19.271,0,37.683-5.956,53.247-17.227c3.616-2.619,4.425-7.674,1.807-11.29
c-2.618-3.616-7.674-4.425-11.29-1.807c-12.788,9.259-27.921,14.154-43.765,14.154c-41.193,0-74.705-33.513-74.705-74.705
c0-8.334,1.36-16.517,4.044-24.318C127.508,168.175,125.264,163.576,121.043,162.125z"/>
<path d="M496.442,353.316l-23.377-4.675c-1.424-4.237-3.139-8.374-5.127-12.373l13.226-19.838
c5.099-7.65,4.083-17.92-2.418-24.42l-10.84-10.84c-6.501-6.5-16.771-7.517-24.42-2.418l-19.838,13.226
c-3.999-1.988-8.136-3.703-12.373-5.126l-4.675-23.377c-1.803-9.015-9.784-15.558-18.977-15.558h-15.331
c-9.192,0-17.174,6.543-18.977,15.558l-4.675,23.377c-4.237,1.424-8.374,3.139-12.373,5.126l-19.838-13.226
c-7.65-5.098-17.92-4.083-24.42,2.418l-10.84,10.84c-6.501,6.501-7.517,16.771-2.418,24.42l13.226,19.838
c-2.734,5.498-4.949,11.256-6.605,17.177c-1.203,4.3,1.308,8.76,5.607,9.964c4.3,1.203,8.76-1.307,9.964-5.607
c1.76-6.292,4.276-12.362,7.476-18.043c1.494-2.653,1.371-5.92-0.317-8.452l-15.897-23.845c-0.839-1.259-0.672-2.949,0.398-4.019
l10.84-10.84c1.07-1.069,2.76-1.235,4.019-0.398l23.845,15.897c2.532,1.689,5.799,1.811,8.452,0.317
c5.682-3.2,11.751-5.716,18.043-7.476c2.931-0.82,5.152-3.215,5.748-6.2l5.619-28.097c0.296-1.483,1.61-2.56,3.123-2.56h15.331
c1.512,0,2.826,1.077,3.123,2.56l5.619,28.099c0.597,2.985,2.819,5.38,5.748,6.2c6.292,1.76,12.362,4.276,18.043,7.476
c2.652,1.494,5.92,1.371,8.452-0.317l23.845-15.897c1.259-0.839,2.949-0.673,4.019,0.398l10.84,10.84
c1.069,1.07,1.236,2.76,0.398,4.019l-15.897,23.845c-1.689,2.533-1.812,5.799-0.317,8.452c3.2,5.682,5.716,11.751,7.476,18.043
c0.82,2.931,3.215,5.152,6.2,5.748l28.097,5.619c1.483,0.296,2.56,1.61,2.56,3.123v15.331c0,1.512-1.077,2.826-2.56,3.123
l-28.099,5.619c-2.985,0.597-5.38,2.819-6.2,5.748c-1.76,6.292-4.276,12.362-7.476,18.043c-1.494,2.653-1.371,5.92,0.317,8.452
l15.897,23.845c0.839,1.259,0.672,2.949-0.398,4.019l-10.84,10.84c-1.069,1.07-2.759,1.236-4.019,0.398l-23.845-15.897
c-2.532-1.688-5.8-1.811-8.452-0.317c-5.682,3.2-11.751,5.716-18.043,7.476c-2.931,0.82-5.152,3.215-5.748,6.2l-5.619,28.097
c-0.296,1.483-1.61,2.56-3.123,2.56h-15.331c-1.512,0-2.826-1.077-3.123-2.56l-5.619-28.099c-0.597-2.985-2.819-5.38-5.748-6.2
c-6.292-1.76-12.362-4.276-18.043-7.476c-2.653-1.494-5.921-1.371-8.452,0.317l-23.845,15.897
c-1.259,0.839-2.949,0.673-4.019-0.398l-10.84-10.84c-1.069-1.07-1.236-2.76-0.398-4.019l15.897-23.845
c1.689-2.533,1.812-5.799,0.317-8.452c-3.2-5.682-5.716-11.751-7.476-18.043c-0.82-2.931-3.215-5.152-6.2-5.748l-28.097-5.619
c-1.483-0.296-2.56-1.61-2.56-3.123v-15.331c0-1.512,1.077-2.826,2.56-3.123l1.72-0.344c4.378-0.876,7.218-5.135,6.341-9.513
c-0.876-4.378-5.13-7.213-9.514-6.341l-1.72,0.344c-9.013,1.804-15.556,9.785-15.556,18.978v15.331
c0,9.192,6.543,17.174,15.558,18.977l23.377,4.675c1.424,4.237,3.139,8.374,5.126,12.373l-13.226,19.838
c-5.1,7.65-4.083,17.92,2.418,24.42l10.84,10.84c6.501,6.501,16.771,7.517,24.42,2.418l19.838-13.226
c3.999,1.988,8.136,3.703,12.373,5.126l4.675,23.377C355.119,505.457,363.1,512,372.293,512h15.331
c9.192,0,17.174-6.543,18.977-15.558l4.675-23.377c4.237-1.424,8.374-3.139,12.373-5.127l19.838,13.226
c7.65,5.099,17.92,4.083,24.42-2.418l10.84-10.84c6.501-6.501,7.517-16.771,2.418-24.42l-13.226-19.838
c1.988-3.999,3.703-8.136,5.126-12.373l23.377-4.675c9.014-1.803,15.557-9.784,15.557-18.977v-15.331
C512,363.1,505.457,355.119,496.442,353.316z"/>
<path d="M379.958,350.855c16.048,0,29.103,13.055,29.103,29.103c0,4.465,3.62,8.084,8.084,8.084s8.084-3.62,8.084-8.084
c0-24.963-20.309-45.272-45.272-45.272s-45.272,20.309-45.272,45.272s20.309,45.272,45.272,45.272
c10.111,0,19.675-3.261,27.659-9.429c3.533-2.73,4.184-7.807,1.455-11.341c-2.73-3.533-7.808-4.185-11.341-1.455
c-5.128,3.961-11.274,6.056-17.773,6.056c-16.048,0-29.103-13.055-29.103-29.103C350.855,363.909,363.91,350.855,379.958,350.855
z"/>
<path d="M245.969,196.716c0-27.158-22.096-49.253-49.253-49.253c-27.158,0-49.253,22.096-49.253,49.253
c0,27.158,22.095,49.253,49.253,49.253S245.969,223.873,245.969,196.716z M163.631,196.716c0-18.242,14.843-33.085,33.085-33.085
c18.242,0,33.085,14.843,33.085,33.085c0,18.242-14.842,33.085-33.085,33.085C178.472,229.801,163.631,214.958,163.631,196.716z"
/>
</g>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

+60
View File
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 434.393 434.393" style="enable-background:new 0 0 434.393 434.393;" xml:space="preserve">
<path id="XMLID_290_" d="M40.452,400.164c-11.329-7.896-18.093-20.774-18.093-34.45v-54.976c0-5.973,4.222-10.977,9.838-12.185
c0.159-0.027,0.316-0.058,0.474-0.093c0.699-0.122,1.417-0.186,2.15-0.186h135.434c1.081,0,2.13,0.139,3.132,0.398
c0.19,0.062,0.383,0.116,0.577,0.165c5.066,1.582,8.754,6.318,8.754,11.899v46.084c0,4.971,4.029,9,9,9s9-4.029,9-9v-46.084
c0-10.015-4.857-18.916-12.34-24.472c0.77-2.426,1.156-4.912,1.156-7.448c0-22.916-32.49-40.548-76.934-42.654v-12.366
c0-4.971-4.029-9-9-9s-9,4.029-9,9v12.366c-44.442,2.106-76.933,19.738-76.933,42.654c0,2.112,0.269,4.189,0.802,6.228
c-8.478,5.414-14.11,14.908-14.11,25.692v54.976c0,19.56,9.646,37.959,25.802,49.218c1.567,1.093,3.361,1.617,5.138,1.617
c2.844,0,5.643-1.345,7.392-3.855C45.532,408.615,44.53,403.006,40.452,400.164z M103.601,253.953
c41.47,0,67.934,14.727,67.934,24.865c0,0.482-0.061,0.975-0.181,1.477c-0.365-0.013-0.73-0.02-1.099-0.02H35.843
c-0.116-0.494-0.175-0.98-0.175-1.457C35.668,268.68,62.132,253.953,103.601,253.953z M421.033,0H73.212c-4.971,0-9,4.029-9,9
v86.822c-0.001,0.045-0.001,0.09-0.001,0.135s0,0.09,0.001,0.135v86.685c-0.001,0.045-0.001,0.09-0.001,0.135s0,0.09,0.001,0.135
v24.798c0,4.971,4.029,9,9,9s9-4.029,9-9v-15.933h68.956v17.829c0,4.971,4.029,9,9,9s9-4.029,9-9v-17.829h68.955v68.954h-19.844
c-4.971,0-9,4.029-9,9s4.029,9,9,9h19.844v68.957h-9.817c-4.971,0-9,4.029-9,9s4.029,9,9,9h192.728c4.971,0,9-4.029,9-9V9
C430.033,4.029,426.004,0,421.033,0z M151.168,173.911H82.212v-68.954h68.956V173.911z M151.168,86.957H82.212V18h68.956V86.957z
M238.123,173.911h-68.955v-68.954h68.955V173.911z M238.123,86.957h-68.955V18h68.955V86.957z M325.075,347.822h-68.952v-68.957
h68.952V347.822z M325.075,260.865h-68.952v-68.954h68.952V260.865z M325.075,173.911h-68.952v-68.954h68.952V173.911z
M325.075,86.957h-68.952V18h68.952V86.957z M412.033,347.822h-68.958v-68.957h68.958V347.822z M412.033,260.865h-68.958v-68.954
h68.958V260.865z M412.033,173.911h-68.958v-68.954h68.958V173.911z M412.033,86.957h-68.958V18h68.958V86.957z M187.046,378.297
c-16.59,0-30.726,4.368-39.351,11.333c-1.008-0.814-2.092-1.593-3.245-2.333c7.684-4.929,12.245-11.557,12.245-19.047
c0-15.99-20.787-28.048-48.352-28.048c-27.563,0-48.35,12.058-48.35,28.048c0,7.49,4.562,14.118,12.244,19.047
c-7.683,4.93-12.244,11.558-12.244,19.048c0,15.99,20.786,28.048,48.35,28.048c16.591,0,30.726-4.368,39.352-11.333
c8.625,6.965,22.761,11.333,39.351,11.333c27.564,0,48.351-12.058,48.351-28.048S214.61,378.297,187.046,378.297z M108.344,416.393
c-19.586,0-30.35-7.547-30.35-10.048s10.764-10.048,30.35-10.048c19.587,0,30.352,7.547,30.352,10.048
S127.931,416.393,108.344,416.393z M108.344,378.297c-19.586,0-30.35-7.546-30.35-10.047s10.764-10.048,30.35-10.048
c19.587,0,30.352,7.547,30.352,10.048S127.931,378.297,108.344,378.297z M187.046,416.393c-19.587,0-30.351-7.547-30.351-10.048
s10.764-10.048,30.351-10.048s30.351,7.547,30.351,10.048S206.633,416.393,187.046,416.393z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

+70 -120
View File
@@ -1,16 +1,14 @@
#:kivy 1.11.0
#:import ew kivy.uix.effectwidget
# import utils?
# margin left bottom right top
#:set AI_MODES ['Default','Balance','Policy']
#:set PLAYER_MODES ['Human', 'Teach','AI:']
#:set BUTTON_COLOR [0.21, 0.28, 0.31, 1]
#:set ICON_BUTTON_COLOR [0.71, 0.78, 0.81, 1]
<StyledButton>:
text_color: 0.95,0.95,0.95,1
button_color: 0.21, 0.28, 0.31, 1
button_color_down: (*[c/2 for c in self.button_color[:3]],1)
margin: (1,1,1,1)
bold: False
font_size: self.size[1] * 0.35
font_size: self.size[1] * 0.45
radius: (self.size[1]/7,)
# dont set these
disabled_mask: (0.5,0.5,0.5,1) if self.disabled else (1,1,1,1)
@@ -26,26 +24,34 @@
pos: (self.pos[0]+self.margin[0],self.pos[1]+self.margin[1])
radius: root.radius or (0.0,)
<StyledSpinnerOption@SpinnerOption>:
font_size: self.size[1] * 0.45
background_color: BUTTON_COLOR
background_normal: ''
color: (1,1,1,1)
<StyledSpinner>:
text: self.values[0] if self.values else ''
font_size: self.size[1] * 0.45
sync_height: True
background_color: [*[c*255/88 for c in BUTTON_COLOR[:3]], 1] # compensate for texture
option_cls: 'StyledSpinnerOption'
<StyledToggleButton>:
<ToggleButtonContainer>:
<StyledTabButton@StyledToggleButton>:
bold: True
font_size: self.size[1] * 0.45
radius: (self.size[1]/3,self.size[1]/3,0,0)
<ToggleButtonContainer>:
<StyledIconButton@StyledButton>:
button_color: 0.71, 0.78, 0.81, 1
button_color: ICON_BUTTON_COLOR
icon_margin: 0.15 * self.size[0]
icon: 'missing.png'
icon: ''
icon_size: min(self.size[0]-self.margin[0]-self.margin[2]-2*self.icon_margin,self.size[1]-self.margin[1]-self.margin[3]-2*self.icon_margin)
#EffectWidget: effects: [ew.FXAAEffect()]
#EffectWidget: effects: [ew.FXAAEffect()]
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]
@@ -56,7 +62,7 @@
background_color: (0,0,0,0)
margin: (1,1,1,1)
icon_size: min(self.size[0]-self.margin[0]-self.margin[2],self.size[1]-self.margin[1]-self.margin[3])
icon: 'missing.png'
icon: ''
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]
@@ -98,30 +104,6 @@
on_active: root.dispatch('on_active')
active: root.default_active
<BWCheckBoxHint>:
black: black
white: white
label: label
orientation: 'vertical'
text: ''
default_active: False
CheckBoxHintLabel:
size_hint: 1,0.2
id: label
text: root.text
font_size: self.height
on_press: if not white.disabled: white._do_press(); black._do_press()
CheckBox:
size_hint: 1,0.4
id: black
active: root.default_active
on_active: root.dispatch('on_active')
CheckBox:
size_hint: 1,0.4
id: white
active: root.default_active
on_active: root.dispatch('on_active')
<CensorableLabel>:
orientation: 'horizontal'
text: ''
@@ -185,6 +167,7 @@
size_hint: 0.05, 1
StyledButton:
text: 'Pass'
font_size: 0.35*self.size[1]
id: pass_btn
on_press: root.katrain("play",None)
size_hint: 0.1, 1
@@ -233,7 +216,16 @@
size_hint: 0.1, 1
on_press: root.katrain('redo',999)
Label:
size_hint: 0.25, 1
size_hint: 0.07, 1
StyledButton:
text: 'AI Move'
font_size: 0.35*self.size[1]
id: pass_btn
on_press: root.katrain("ai-move",None)
size_hint: 0.14, 1
margin: (0, self.size[1]/5, 0, self.size[1]/5)
Label:
size_hint: 0.04, 1
<Controls>:
@@ -248,13 +240,8 @@
ownership: ownership
policy: policy
eval: eval
ai_balance: ai_balance
ai_auto: ai_auto
ai_fast: ai_fast
ai_lock: ai_lock
ai_move: ai_move
auto_undo: auto_undo
player_mode_groups: {'B':B_player_mode,'W':W_player_mode}
ai_mode_groups: {'B':B_AI_mode,'W':W_AI_mode}
graph: graph
katrain: self.parent
canvas.before:
@@ -299,31 +286,24 @@
id: analyze_tab
opacity: 0
orientation: 'vertical'
Label:
size_hint: 1,0.02
GridLayout:
cols: 6
rows: 1
size_hint: 1, 0.35
size_hint: 1, 0.5
Label:
size_hint: 0.01, 1
BoxLayout:
orientation: 'vertical'
size_hint: 0.1, 0.5
Label:
size_hint: 1, 0.2
BlackCircleWithText:
text: 'B'
WhiteCircleWithText:
text: 'W'
BWCheckBoxHint:
size_hint: 0.05, 1
CheckBoxHint:
size_hint: 0.2, 0.5
id: eval
text: 'all eval'
text: 'show\nall dots'
default_active: False
on_active: root.katrain.update_state()
BWCheckBoxHint:
CheckBoxHint:
size_hint: 0.2, 0.5
id: hints
text: 'top move'
text: 'show\ntop move'
on_active: root.katrain.update_state()
CheckBoxHint:
size_hint: 0.2, 0.5
@@ -335,10 +315,11 @@
id: policy
text: 'show\npolicy'
on_active: root.katrain.update_state()
LargeLabel:
Label:
size_hint: 0.05, 1
ScoreGraph:
id: graph
text: 'graph?'
size_hint: 1,0.75
size_hint: 1,0.5
CensorableLabel:
id: win_rate
size_hint: 1, 0.15
@@ -354,45 +335,47 @@
size_hint: 1,1
opacity: 1
id: play_tab
Label:
size_hint: 1,0.02
GridLayout:
cols: 4
rows: 1
size_hint: 1, 0.1
StyledButton:
id: ai_move
size_hint: 0.5, 0.5
text: ' AI\nMove'
on_press: root.katrain("ai_move")
CheckBoxHint:
size_hint: 0.25, 1
text: 'lock\nAI'
id: ai_lock
on_active: self.checkbox.disabled = analyze_tab_button.disabled = ai_auto.white = ai_auto.black = ai_move.disabled = True
GridLayout:
cols: 2
rows: 2
size_hint: 1,0.125
BlackCircleWithText:
text: 'B'
size_hint: 0.1, 1
ToggleButtonContainer:
size_hint: 0.9, 1
size_hint: 0.6, 1
id: B_player_mode
options: ['human','human+undo','ai','ai+balance','ai+policy']
labels: ['Human', ' AI\nAssist','AI',' AI\nBalance',' AI\nPolicy']
options: ['human','human+undo','ai']
labels: PLAYER_MODES
on_selection: root.katrain.update_state()
StyledSpinner:
id: B_AI_mode
size_hint: 0.3, 1
values: AI_MODES
Label:
size_hint: None,1
width: 3
WhiteCircleWithText:
text: 'W'
size_hint: 0.1, 1
ToggleButtonContainer:
size_hint: 0.9, 1
size_hint: 0.6, 1
id: W_player_mode
options: ['human','human+undo','ai','ai+balance','ai+policy']
labels: ['Human', ' AI\nAssist','AI',' AI\nBalance',' AI\nPolicy']
options: ['human','human+undo','ai']
labels: PLAYER_MODES
on_selection: root.katrain.update_state()
StyledSpinner:
id: W_AI_mode
size_hint: 0.3, 1
values: AI_MODES
Label:
size_hint: None,1
width: 3
LargeLabel:
text: 'free real estate'
size_hint: 1,0.2
size_hint: 1,0.25
CensorableLabel:
id: points_lost
size_hint: 1, 0.03
@@ -402,39 +385,6 @@
id: info
size_hint: 1, 0.25
valign: 'middle'
GridLayout:
cols: 5
rows: 1
size_hint: 1, 0.1
BoxLayout:
orientation: 'vertical'
size_hint: 0.1, 0.5
Label:
size_hint: 1, 0.2
BlackCircleWithText:
text: 'B'
WhiteCircleWithText:
text: 'W'
BWCheckBoxHint:
text: 'ai'
id: ai_auto
default_active: False
size_hint: 0.25, 1
BWCheckBoxHint:
size_hint: 0.2, 0.5
id: auto_undo
text: 'undo'
on_active: root.katrain.update_state()
CheckBoxHint:
size_hint: 0.25, 1
text: 'balance\nscore'
id: ai_balance
default_active: False
CheckBoxHint:
size_hint: 0.25, 1
text: 'fast'
id: ai_fast
default_active: True
BoxLayout:
orientation: 'horizontal'
size_hint: 1, None
+28 -43
View File
@@ -8,17 +8,17 @@ from kivy.app import App
from kivy.clock import Clock
from kivy.core.clipboard import Clipboard
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.storage.jsonstore import JsonStore
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from constants import OUTPUT_DEBUG, OUTPUT_ERROR, OUTPUT_EXTRA_DEBUG, OUTPUT_INFO
from engine import KataGoEngine
from game import Game, IllegalMoveException, KaTrainSGF, Move
from gui import *
from gui.popups import NewGamePopup, ConfigPopup
from kivy.lang import Builder
from gui.popups import ConfigPopup, NewGamePopup
class KaTrainGui(BoxLayout):
@@ -27,12 +27,12 @@ class KaTrainGui(BoxLayout):
def __init__(self, **kwargs):
super(KaTrainGui, self).__init__(**kwargs)
self.debug_level = 0
self._load_config()
self.debug_level = self.config("debug/level", OUTPUT_INFO)
self.logger = lambda message, level=OUTPUT_INFO: self.log(message, level)
self.engine = None
self.game = None
self.logger = lambda message, level=OUTPUT_INFO: self.log(message, level)
self._load_config()
self.debug_level = self.config("debug/level", OUTPUT_INFO)
self.message_queue = Queue()
self._keyboard = Window.request_keyboard(None, self, "")
@@ -124,7 +124,6 @@ class KaTrainGui(BoxLayout):
def _do_new_game(self, board_size=None, move_tree=None):
self.game = Game(self, self.engine, self.config("game"), board_size=board_size, move_tree=move_tree)
self.controls.unlock()
self.controls.select_mode("analyze" if move_tree and len(move_tree.nodes_in_tree) > 1 else "play")
self.update_state(redraw_board=True) # TODO: just board here/redraw is in all anyway?
@@ -134,11 +133,6 @@ class KaTrainGui(BoxLayout):
self.update_state()
def _do_undo(self, n_times=1):
if self.controls.ai_lock.active and self.contols.auto_undo.active(self.game.current_node.player) and self.config("trainer/lock_undos"):
self.controls.set_status(
f"Can't undo manually when Automatic Undo and Lock AI are both set. (Change the `lock_undos` setting to false to allow this regardless)", self.game.current_node
)
return
self.game.undo(n_times)
self.update_state()
@@ -181,7 +175,7 @@ class KaTrainGui(BoxLayout):
def _do_config_popup(self):
config_popup = Popup(title="Edit Settings", size_hint=(0.9, 0.9))
popup_contents = ConfigPopup(self, config_popup, dict(self._config))
popup_contents = ConfigPopup(self, config_popup, dict(self._config), ignore_cats=("board_ui"))
config_popup.add_widget(popup_contents)
config_popup.open()
@@ -198,37 +192,28 @@ class KaTrainGui(BoxLayout):
if isinstance(App.get_running_app().root_window.children[0], Popup):
return # if in new game or load, don't allow keyboard shortcuts
if keycode[1] == "up":
shortcuts = {
"u": self.controls.eval,
"i": self.controls.hints,
"p": self.controls.policy,
"o": self.controls.ownership,
"a": ("ai-move",),
"right": ("switch-branch", 1),
"left": ("switch-branch", -1),
"z": ("analyze-extra", "sweep"),
"x": ("analyze-extra", "extra"),
"c": ("analyze-extra", "refine"),
}
if keycode[1] in shortcuts.keys():
shortcut = shortcuts[keycode[1]]
if isinstance(shortcut, Widget):
shortcut.trigger_action(duration=0)
else:
self(*shortcut)
elif keycode[1] == "up":
self("undo", 1 + ("shift" in modifiers) * 9 + ("ctrl" in modifiers) * 999)
elif keycode[1] == "down":
self("redo", 1 + ("shift" in modifiers) * 9 + ("ctrl" in modifiers) * 999)
elif keycode[1] == "right":
self("switch-branch", 1)
elif keycode[1] == "left":
self("switch-branch", -1)
elif keycode[1] == "s":
self("analyze-extra", "sweep")
elif keycode[1] == "x":
self("analyze-extra", "extra")
elif keycode[1] == "r":
self("analyze-extra", "refine")
elif keycode[1] == "a":
if not self.controls.ai_thinking:
self.controls.ai_move.trigger_action(duration=0)
elif keycode[1] == "p": # TODO: clean repetitive shortcuts
self.controls.policy.label.trigger_action(duration=0)
elif keycode[1] == "f":
self.controls.ai_fast.label.trigger_action(duration=0)
elif keycode[1] == "h":
self.controls.hints.label.trigger_action(duration=0)
elif keycode[1] == "e":
self.controls.eval.label.trigger_action(duration=0)
elif keycode[1] == "u":
self.controls.auto_undo.label.trigger_action(duration=0)
elif keycode[1] == "b":
self.controls.ai_balance.label.trigger_action(duration=0)
elif keycode[1] == "o":
self.controls.ownership.label.trigger_action(duration=0)
elif keycode[1] == "n" and "ctrl" in modifiers:
self("new-game-popup")
elif keycode[1] == "l" and "ctrl" in modifiers:
@@ -238,7 +223,7 @@ class KaTrainGui(BoxLayout):
elif keycode[1] == "c" and "ctrl" in modifiers:
Clipboard.copy(self.game.root.sgf())
self.controls.set_status("Copied SGF to clipboard.")
elif keycode[1] == "v" and "ctrl" in modifiers:
elif keycode[1] == "v" and "ctrl" in modifiers: # TODO: refactor
clipboard = Clipboard.paste()
if not clipboard:
self.controls.set_status(f"Ctrl-V pressed but clipboard is empty.")
+8 -8
View File
@@ -17,18 +17,18 @@ class Move:
PLAYERS = "BW"
SGF_COORD = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ".lower()) + list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
@staticmethod
def from_gtp(gtp_coords, player="B"):
@classmethod
def from_gtp(cls, gtp_coords, player="B"):
if "pass" in gtp_coords:
return Move(coords=None, player=player)
return cls(coords=None, player=player)
match = re.match(r"([A-Z]+)(\d+)", gtp_coords)
return Move(coords=(Move.GTP_COORD.index(match[1]), int(match[2]) - 1), player=player)
return cls(coords=(Move.GTP_COORD.index(match[1]), int(match[2]) - 1), player=player)
@staticmethod
def from_sgf(sgf_coords, board_size, player="B"):
@classmethod
def from_sgf(cls, sgf_coords, board_size, player="B"):
if sgf_coords == "" or Move.SGF_COORD.index(sgf_coords[0]) == board_size: # some servers use [tt] for pass
return Move(coords=None, player=player)
return Move(coords=(Move.SGF_COORD.index(sgf_coords[0]), board_size - Move.SGF_COORD.index(sgf_coords[1]) - 1), player=player)
return cls(coords=None, player=player)
return cls(coords=(Move.SGF_COORD.index(sgf_coords[0]), board_size - Move.SGF_COORD.index(sgf_coords[1]) - 1), player=player)
def __init__(self, coords: Optional[Tuple[int, int]] = None, player: str = "B"):
self.player = player