graph working

This commit is contained in:
Sander Land committed 2020-04-21 20:34:14 +02:00
1 parent 18044dce44
commit 8171a6d5fd
8 files changed
+156 -102

No files matched your search

+3 -3
View File
@@ -87,7 +87,7 @@ class KataGoEngine:
if ownership is None:
ownership = self.config["enable_ownership"] and not next_move
size_x, size_y = analysis_node.board_size
query = {
"id": query_id,
"rules": self.get_rules(analysis_node),
@@ -95,8 +95,8 @@ class KataGoEngine:
"analyzeTurns": [len(moves)],
"maxVisits": visits or self.config["visits"],
"komi": analysis_node.komi,
"boardXSize": analysis_node.board_size,
"boardYSize": analysis_node.board_size,
"boardXSize": size_x,
"boardYSize": size_y,
"includeOwnership": ownership,
"includePolicy": not next_move,
"moves": [[m.player, m.gtp()] for m in moves],
+1 -1
View File
@@ -210,7 +210,7 @@ class Game:
cn = self.current_node
while not cn.analysis_ready:
self.katrain.controls.set_status("Thinking...") # TODO: non blocking somehow?
time.sleep(0.05)
time.sleep(0.01)
# select move
candidate_ai_moves = cn.candidate_moves
ai_mode = self.katrain.controls.ai_mode(cn.next_player)
+2 -2
View File
@@ -196,7 +196,7 @@ class BadukPanWidget(Widget):
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()
if katrain.controls.policy.active and policy and not katrain.controls.ownership.active:
if katrain.controls.policy.active and policy:
ix = 0
best_move_policy = max(*policy)
for y in range(board_size_y - 1, -1, -1):
@@ -211,7 +211,7 @@ class BadukPanWidget(Widget):
ix = ix + 1
polsize = math.sqrt(policy[ix])
with pass_btn.canvas.after:
draw_circle((pass_btn.pos[0] + pass_btn.width / 2, pass_btn.pos[1] + pass_btn.height / 2), polsize * pass_btn.height / 2, (1, 0, 0, 0.5))
draw_circle((pass_btn.pos[0] + pass_btn.width / 2, pass_btn.pos[1] + pass_btn.height / 2), polsize * pass_btn.height / 2, self.ui_config["policy_color"])
# children of current moves in undo / review
undo_coords = set()
+2 -1
View File
@@ -68,6 +68,7 @@ class Controls(BoxLayout):
if current_player_is_human_or_both_robots:
self.show_evaluation_stats(current_node)
self.graph.update_value(current_node.depth, current_node.score)
if current_node.score:
self.graph.update_value(current_node.depth, current_node.score)
self.info.text = info
+23 -21
View File
@@ -1,3 +1,4 @@
import math
import random
from kivy.clock import Clock
@@ -11,6 +12,7 @@ import re
from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
from kivy.uix.dropdown import DropDown
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.spinner import Spinner
@@ -80,7 +82,7 @@ class ToggleButtonContainer(GridLayout):
for i, opt in enumerate(self.options):
state = "down" if opt == self.selected else "normal"
self.add_widget(StyledToggleButton(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):
@@ -173,30 +175,32 @@ class CensorableLabel(BoxLayout):
class ScoreGraph(Label):
values = ListProperty([])
line_points = ListProperty([])
line = ObjectProperty(None)
dot = ObjectProperty(None)
highlighted_index = NumericProperty(0)
dot_pos = ListProperty([0, 0])
highlighted_index = NumericProperty(None)
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))
Clock.schedule_once(self.on_size, 0)
def clear_values(self):
def clear(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]]
values = self.values
if values:
val_range = min(values or [0]), max(values or [0])
scale = math.ceil(max(3, max(-val_range[0], val_range[1]) * 1.05))
xscale = self.width * 0.9 / max(len(values), 20)
available_height = self.height * (1 - 2 * self.marginy)
line_points = [[self.pos[0] + self.marginx * self.width + i * xscale, self.pos[1] + available_height / 2 * (1 + val / scale)] for i, val in enumerate(values)]
self.line_points = sum(line_points, [])
self.range_label_top.text = f"B+{scale:.0f}"
self.range_label_bottom.text = f"W+{scale:.0f}"
if self.highlighted_index is not None:
self.highlighted_index = min(self.highlighted_index, len(values) - 1)
self.dot_pos = [c - self.highlight_size / 2 for c in line_points[self.highlighted_index]]
def update_value(self, index, value):
self.values.extend([0] * max(0, index - (len(self.values) - 1)))
@@ -208,9 +212,7 @@ class ScoreGraph(Label):
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):
+11 -6
View File
@@ -1,5 +1,6 @@
from collections import defaultdict
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout
import os
from constants import OUTPUT_DEBUG, OUTPUT_ERROR
@@ -84,12 +85,16 @@ class ConfigPopup(QuickConfigGui):
def __init__(self, katrain, popup, config, ignore_cats):
self.config = config
self.ignore_cats = ignore_cats
self.orientation = "vertical"
super().__init__(katrain, popup)
Clock.schedule_once(self._build, 0)
def _build(self, _):
cols = [BoxLayout(orientation="vertical"), BoxLayout(orientation="vertical")]
props_in_col = [0, 0]
for k1, all_d in config.items():
if k1 in ignore_cats:
for k1, all_d in self.config.items():
if k1 in self.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))
@@ -105,14 +110,14 @@ class ConfigPopup(QuickConfigGui):
cols[1].add_widget(cat)
props_in_col[1] += len(d)
col_container = BoxLayout(size_hint=(1, 0.95))
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()
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))
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))
btn_container.add_widget(self.info_label)
btn_container.add_widget(self.apply_button)
btn_container.add_widget(self.save_button)
+109 -64
View File
@@ -6,6 +6,9 @@
#: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]
#:set BLACK [0.05,0.05,0.05,1]
#:set WHITE [0.95,0.95,0.95,1]
#:set GREY [0.5,0.5,0.5,1]
<StyledButton>:
font_size: self.size[1] * 0.45
@@ -38,6 +41,7 @@
option_cls: 'StyledSpinnerOption'
<StyledToggleButton>:
max_lines: 1
<StyledTabButton@StyledToggleButton>:
bold: True
@@ -70,39 +74,36 @@
<DarkLabel>:
color: (0.05,0.05,0.05,1)
color: BLACK
text_size: self.size # don't overflow these
halign: 'center'
valign: 'center'
<BoxedDarkLabel@Label>:
halign: 'left'
valign: 'top'
canvas.before:
Color:
rgba: BLACK
Line:
rectangle: [self.pos[0],self.pos[1]+1,self.width-1,self.height]
width:1.5
<LargeLabel@DarkLabel>:
bold: True
<CheckBox>:
color: (0.05,0.05,0.05,1)
color: BLACK
<LabelledCheckBox>:
color: (0.95,0.95,0.95,1)
color: WHITE
<CheckBoxHintLabel@ButtonBehavior+DarkLabel>:
<CheckBoxHintLabel@Label+ButtonBehavior>:
color: BLACK
halign: 'center'
valign: 'center'
<CheckBoxHint>:
orientation: 'vertical'
checkbox: checkbox
label: label
text: ''
default_active: False
position: (0,0)
CheckBoxHintLabel:
size_hint: 1,0.45 if root.orientation=='vertical' else (0.8,1)
font_size: root.height * 0.2 if root.orientation=='vertical' else root.height
text: root.text
on_press: if not checkbox.disabled: checkbox._do_press()
id: label
CheckBox:
size_hint: 1,0.55 if root.orientation=='vertical' else (0.2,1)
id: checkbox
on_active: root.dispatch('on_active')
active: root.default_active
size: self.texture_size
<CensorableLabel>:
orientation: 'horizontal'
@@ -122,7 +123,7 @@
<BaseCircleWithText>:
text: ''
color: 0.95,0.95,0.95,1
color: WHITE
font_size: min(self.height,self.width) * 0.6 / (0.5 + 0.5 * len(self.text))
halign: 'center'
valign: 'center'
@@ -131,27 +132,73 @@
<BlackCircleWithText@BaseCircleWithText>:
canvas.before:
Color:
rgba: 0.05,0.05,0.05,1
rgba: BLACK
Ellipse:
pos: self.pos[0] + self.width/2 - min(self.height,self.width) * root.radius, self.pos[1] + self.height/2 - min(self.height,self.width) * root.radius
size: min(self.height,self.width) * 2 * root.radius, min(self.height,self.width) * 2* root.radius
<WhiteCircleWithText@BaseCircleWithText>:
color: 0.05,0.05,0.05,1
color: BLACK
outline: True
canvas.before:
Color:
rgba: 0.95,0.95,0.95,1
rgba: WHITE
Ellipse:
pos: self.pos[0] + self.width/2 - min(self.height,self.width) * root.radius, self.pos[1] + self.height/2 - min(self.height,self.width) * root.radius
size: min(self.height,self.width) * 2 * root.radius, min(self.height,self.width) * 2 * root.radius
canvas.after:
Color:
rgba: 0.05,0.05,0.05,1
rgba: BLACK
Line:
circle: self.pos[0] + self.width/2, self.pos[1] + self.height/2, min(self.height,self.width) * root.radius - 1
width: 1.1 if root.outline else 0.0001
<ScoreGraph>:
range_label_bottom: range_label_bottom
range_label_top: range_label_top
highlight_size: 5
marginx: 0.05
marginy: 0.01
canvas.before:
Color:
rgba: GREY
Line:
points: [root.pos[0]+self.width*root.marginx,root.pos[1]+self.height*0.5,root.pos[0]+self.width*0.95,root.pos[1]+self.height*0.5]
width: 1.0
dashes: [2,2]
Line:
rectangle: [root.pos[0] + root.marginx * self.width, root.pos[1] + root.marginy*self.height, root.width*0.9, root.height*(1-2*root.marginy)]
canvas:
Color:
rgba: BLACK
Line:
points: root.line_points
width: 1.0
Color:
rgba: BUTTON_COLOR
Ellipse:
id: dot
pos: root.dot_pos
size: self.highlight_size,self.highlight_size
CheckBoxHintLabel:
id: range_label_top
font_size: 0.15 * root.height
pos: root.pos[0] + root.marginx * root.width + 3, root.pos[1] + root.height * (1 - 2 * root.marginy) - 0.15 * root.height - 1
valign: 'bottom'
halign: 'left'
text: 'B+0'
color: GREY
CheckBoxHintLabel:
id: range_label_bottom
font_size: 0.15 * root.height
pos: root.pos[0] + root.marginx * root.width + 3, root.pos[1]
valign: 'bottom'
halign: 'left'
text: 'W+0'
color: GREY
<BadukPanWidget>:
size: self.parent.height, self.parent.height
katrain: self.parent.parent
@@ -220,7 +267,6 @@
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)
@@ -253,10 +299,23 @@
BoxLayout:
size_hint: 1,0.05
orientation: 'horizontal'
StyledTabButton:
text: 'Play'
group: 'tabs'
state: 'down'
id: play_tab_button
margin: (2,0,1,3)
on_press:
play_tab.size_hint[0] = 1
play_tab.opacity = 1
analyze_tab.size_hint_x = None
analyze_tab.width = 0
analyze_tab.opacity = 0
self.state = 'down'
StyledTabButton:
text: 'Analyze'
id: analyze_tab_button
margin: (3,0,1,3)
margin: (1,0,2,3)
group: 'tabs'
on_press:
analyze_tab.size_hint[0] = 1
@@ -265,19 +324,6 @@
play_tab.width = 0
play_tab.opacity = 0
self.state = 'down'
StyledTabButton:
text: 'Play'
group: 'tabs'
state: 'down'
id: play_tab_button
margin: (1,0,3,3)
on_press:
play_tab.size_hint[0] = 1
play_tab.opacity = 1
analyze_tab.size_hint_x = None
analyze_tab.width = 0
analyze_tab.opacity = 0
self.state = 'down'
BoxLayout:
size_hint: 1,0.25
orientation: 'horizontal'
@@ -289,34 +335,34 @@
Label:
size_hint: 1,0.02
GridLayout:
cols: 6
rows: 1
cols: 4
rows: 2
size_hint: 1, 0.5
Label:
size_hint: 0.05, 1
CheckBoxHint:
size_hint: 0.2, 0.5
id: eval
CheckBoxHintLabel:
text: 'show\nall dots'
CheckBoxHintLabel:
text: 'show\ntop move'
CheckBoxHintLabel:
text: 'show\nowner'
CheckBoxHintLabel:
text: 'show\npolicy'
CheckBox:
id: eval
default_active: False
on_active: root.katrain.update_state()
CheckBoxHint:
size_hint: 0.2, 0.5
CheckBox:
id: hints
text: 'show\ntop move'
default_active: False
on_active: root.katrain.update_state()
CheckBoxHint:
size_hint: 0.2, 0.5
CheckBox:
id: ownership
text: 'show\nowner'
default_active: False
on_active: root.katrain.update_state()
CheckBoxHint:
size_hint: 0.2, 0.5
CheckBox:
id: policy
text: 'show\npolicy'
default_active: False
on_active: root.katrain.update_state()
Label:
size_hint: 0.05, 1
ScoreGraph:
id: graph
size_hint: 1,0.5
@@ -375,7 +421,7 @@
width: 3
LargeLabel:
text: ''
size_hint: 1,0.25
size_hint: 1,0.3
CensorableLabel:
id: points_lost
size_hint: 1, 0.03
@@ -383,8 +429,7 @@
text: ''
TextInput:
id: info
size_hint: 1, 0.25
valign: 'middle'
size_hint: 1, 0.2
BoxLayout:
orientation: 'horizontal'
size_hint: 1, None
+5 -4
View File
@@ -125,6 +125,7 @@ class KaTrainGui(BoxLayout):
def _do_new_game(self, move_tree=None):
self.game = Game(self, self.engine, self.config("game"), move_tree=move_tree)
self.controls.select_mode("analyze" if move_tree and len(move_tree.nodes_in_tree) > 1 else "play")
self.controls.graph.clear()
self.update_state(redraw_board=True) # TODO: just board here/redraw is in all anyway?
def _do_ai_move(self, node=None):
@@ -193,10 +194,10 @@ class KaTrainGui(BoxLayout):
return # if in new game or load, don't allow keyboard shortcuts
shortcuts = {
"u": self.controls.eval,
"i": self.controls.hints,
"p": self.controls.policy,
"o": self.controls.ownership,
"u": self.controls.eval.checkbox,
"i": self.controls.hints.checkbox,
"p": self.controls.policy.checkbox,
"o": self.controls.ownership.checkbox,
"a": ("ai-move",),
"right": ("switch-branch", 1),
"left": ("switch-branch", -1),