graph working
This commit is contained in:
1 parent
18044dce44
commit
8171a6d5fd
8 files changed
+156
-102
No files matched your search
@@ -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],
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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),
|
||||
|
||||
Reference in new issue
Block a user