This commit is contained in:
Sander Land committed 2020-04-18 20:46:14 +02:00
1 parent 717b1de032
commit 8aaa85e73d
6 files changed
+67 -70

No files matched your search

+3 -2
View File
@@ -79,8 +79,9 @@ class KataGoEngine:
else: else:
self.katrain.log(f"Query result {analysis['id']} discarded -- recent new game?", OUTPUT_DEBUG) 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, def request_analysis(
refine_move=None): 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 fast = self.katrain.controls.ai_fast.active
query_id = f"QUERY:{str(self.query_counter)}" query_id = f"QUERY:{str(self.query_counter)}"
self.query_counter += 1 self.query_counter += 1
+10 -10
View File
@@ -194,7 +194,7 @@ class Game:
f.write(self.root.sgf()) f.write(self.root.sgf())
return f"SGF with analysis written to {file_name}" return f"SGF with analysis written to {file_name}"
def ai_move(self,train_settings): def ai_move(self, train_settings):
while not self.current_node.analysis_ready: while not self.current_node.analysis_ready:
self.katrain.set_status("Thinking...") self.katrain.set_status("Thinking...")
time.sleep(0.05) time.sleep(0.05)
@@ -242,23 +242,23 @@ class Game:
stones = {s.coords for s in self.stones} stones = {s.coords for s in self.stones}
cn = self.current_node cn = self.current_node
if not cn.analysis: if not cn.analysis:
self.katrain.controls.set_status("Wait for initial analysis to complete before doing a board-sweep or refinement",self.current_node) self.katrain.controls.set_status("Wait for initial analysis to complete before doing a board-sweep or refinement", self.current_node)
return return
if mode == "extra": if mode == "extra":
visits = sum([d["visits"] for d in cn.analysis]) + self.engine.config['visits'] visits = sum([d["visits"] for d in cn.analysis]) + self.engine.config["visits"]
self.katrain.controls.set_status(f"Performing additional analysis to {visits} visits") self.katrain.controls.set_status(f"Performing additional analysis to {visits} visits")
cn.analyze(self.engine, visits=visits, priority=- 1_000) cn.analyze(self.engine, visits=visits, priority=-1_000)
return return
elif mode == "sweep": elif mode == "sweep":
analyze_moves = [Move(coords=(x, y),player=cn.next_player) for x in range(self.board_size) for y in range(self.board_size) if (x, y) not in stones] analyze_moves = [Move(coords=(x, y), player=cn.next_player) for x in range(self.board_size) for y in range(self.board_size) if (x, y) not in stones]
visits = self.engine.config['visits_fast'] visits = self.engine.config["visits_fast"]
self.katrain.controls.set_status(f"Refining analysis of entire board to {visits} visits") self.katrain.controls.set_status(f"Refining analysis of entire board to {visits} visits")
priority =- 1_000_000_000 priority = -1_000_000_000
else: # mode=='refine': else: # mode=='refine':
analyze_moves = [Move.from_gtp(a["move"],player=cn.next_player) for a in cn.analysis] analyze_moves = [Move.from_gtp(a["move"], player=cn.next_player) for a in cn.analysis]
visits = cn.analysis[0]["visits"] + self.engine.config['visits_fast'] visits = cn.analysis[0]["visits"] + self.engine.config["visits_fast"]
self.katrain.controls.set_status(f"Refining analysis of candidate moves to {visits} visits") self.katrain.controls.set_status(f"Refining analysis of candidate moves to {visits} visits")
priority = - 1_000 priority = -1_000
for move in analyze_moves: for move in analyze_moves:
cn.analyze(self.engine, priority, visits=visits, refine_move=move) cn.analyze(self.engine, priority, visits=visits, refine_move=move)
+6 -7
View File
@@ -29,19 +29,18 @@ class GameNode(SGFNode):
# various analysis functions # various analysis functions
def analyze(self, engine, priority=0, visits=None, refine_move=None): 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, engine.request_analysis(self, lambda result: self.set_analysis(result, refine_move), priority=priority, visits=visits, refine_move=refine_move)
visits=visits, refine_move=refine_move)
def set_analysis(self, analysis_blob, refine_move): def set_analysis(self, analysis_blob, refine_move):
if refine_move: if refine_move:
gtp = refine_move.gtp() gtp = refine_move.gtp()
for d in self.analysis: for d in self.analysis:
if d['move']==gtp: if d["move"] == gtp:
if d['visits'] < analysis_blob['rootInfo']['visits']: if d["visits"] < analysis_blob["rootInfo"]["visits"]:
d.update(analysis_blob['rootInfo']) d.update(analysis_blob["rootInfo"])
return return
self.analysis.append({'move':gtp,'order':999,**analysis_blob['rootInfo']}) self.analysis.append({"move": gtp, "order": 999, **analysis_blob["rootInfo"]})
else: # TODO root info / to dict? else: # TODO root info / to dict?
self.analysis = analysis_blob["moveInfos"] # TODO: fix when rootInfos comes in self.analysis = analysis_blob["moveInfos"] # TODO: fix when rootInfos comes in
self.ownership = analysis_blob["ownership"] self.ownership = analysis_blob["ownership"]
+6 -1
View File
@@ -20,12 +20,17 @@ class Controls(BoxLayout):
self.status = None self.status = None
self.status_node = None self.status_node = None
def set_status(self, msg,at_node=None): def set_status(self, msg, at_node=None):
self.status = msg self.status = msg
self.status_node = at_node or self.parent.game.current_node self.status_node = at_node or self.parent.game.current_node
self.info.text = msg self.info.text = msg
self.update_evaluation() self.update_evaluation()
def select_mode(self, mode):
if mode == "analyze":
self.analyze_tab_button.trigger_action(duration=0)
else:
self.play_tab_button.trigger_action(duration=0)
def show_evaluation_stats(self, node): def show_evaluation_stats(self, node):
if node.analysis_ready: if node.analysis_ready:
+33 -44
View File
@@ -1,14 +1,15 @@
#:kivy 1.11.0 #:kivy 1.11.0
#:import ew kivy.uix.effectwidget #:import ew kivy.uix.effectwidget
# margin left bottom right top
<StyledButton>: <StyledButton>:
text_color: 0.95,0.95,0.95,1 text_color: 0.95,0.95,0.95,1
button_color: 0.21, 0.28, 0.31, 1 button_color: 0.21, 0.28, 0.31, 1
button_color_down: (*[c/2 for c in self.button_color[:3]],1) button_color_down: (*[c/2 for c in self.button_color[:3]],1)
margin: (2,1) margin: (1,1,1,1)
bold: False bold: False
font_size: self.size[1] * 0.35 font_size: self.size[1] * 0.35
radius: 10 radius: (self.size[1]/7,)
# dont set these # dont set these
disabled_mask: (0.5,0.5,0.5,1) if self.disabled else (1,1,1,1) disabled_mask: (0.5,0.5,0.5,1) if self.disabled else (1,1,1,1)
color: self.disabled_mask if self.disabled else self.text_color color: self.disabled_mask if self.disabled else self.text_color
@@ -19,30 +20,14 @@
Color: Color:
rgba: root.face_color or [0,0,0,0] rgba: root.face_color or [0,0,0,0]
RoundedRectangle: RoundedRectangle:
size: self.size[0]-2*self.margin[0],self.size[1]-2*self.margin[1] size: self.size[0]-self.margin[0]-self.margin[2],self.size[1]-self.margin[1]-self.margin[3]
pos: (self.pos[0]+self.margin[0],self.pos[1]+self.margin[1]) pos: (self.pos[0]+self.margin[0],self.pos[1]+self.margin[1])
radius: (root.radius or 0,) radius: root.radius or (0.0,)
<StyledToggleButton@ToggleButton>: <StyledToggleButton@StyledButton+ToggleButtonBehavior>:
color: 1,1,1,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)
bold: True bold: True
font_size: self.size[1] * 0.45 font_size: self.size[1] * 0.45
radius: self.size[1]/3 radius: (self.size[1]/3,self.size[1]/3,0,0)
# dont set these
disabled_mask: (0.5,0.5,0.5,1) if self.disabled else (1,1,1,1)
background_color: 0,0,0,0
background_normal: ''
face_color: [c*m for c,m in zip(self.button_color if root.state=='normal' else self.button_color_down,self.disabled_mask)]
canvas.before:
Color:
rgba: root.face_color or [0,0,0,0]
RoundedRectangle:
size: self.size[0]-2*self.margin[0],self.size[1]-2*self.margin[1]
pos: (self.pos[0]+self.margin[0],self.pos[1]+self.margin[1])
radius: (root.radius or 0,root.radius or 0,0,0)
<StyledIconButton@StyledButton> <StyledIconButton@StyledButton>
button_color: 0.71, 0.78, 0.81, 1 button_color: 0.71, 0.78, 0.81, 1
@@ -283,6 +268,8 @@
<Controls> <Controls>
orientation: 'vertical' orientation: 'vertical'
play_tab_button: play_tab_button
analyze_tab_button: analyze_tab_button
info: info info: info
win_rate: win_rate win_rate: win_rate
points_lost: points_lost points_lost: points_lost
@@ -303,7 +290,7 @@
StyledToggleButton: StyledToggleButton:
text: 'Analyze' text: 'Analyze'
id: analyze_tab_button id: analyze_tab_button
state: 'down' margin: (3,0,1,3)
group: 'tabs' group: 'tabs'
on_press: on_press:
analyze_tab.size_hint[0] = 1 analyze_tab.size_hint[0] = 1
@@ -311,28 +298,30 @@
play_tab.size_hint_x = None play_tab.size_hint_x = None
play_tab.width = 0 play_tab.width = 0
play_tab.opacity = 0 play_tab.opacity = 0
print(play_tab,play_tab.size_hint,analyze_tab.size_hint)
StyledToggleButton: StyledToggleButton:
text: 'Play' text: 'Play'
group: 'tabs' group: 'tabs'
state: 'down'
id: play_tab_button
margin: (1,0,3,3)
on_press: on_press:
play_tab.size_hint[0] = 1 play_tab.size_hint[0] = 1
play_tab.opacity = 1 play_tab.opacity = 1
analyze_tab.size_hint_x = None analyze_tab.size_hint_x = None
analyze_tab.width = 0 analyze_tab.width = 0
analyze_tab.opacity = 0 analyze_tab.opacity = 0
print(play_tab.size_hint,analyze_tab.size_hint)
BoxLayout: BoxLayout:
size_hint: 1,0.25 size_hint: 1,0.25
orientation: 'horizontal' orientation: 'horizontal'
BoxLayout: BoxLayout: # ----------- analyze -----------
size_hint: 1,1 size_hint: 0,1
id: analyze_tab id: analyze_tab
opacity: 0
orientation: 'vertical' orientation: 'vertical'
GridLayout: GridLayout:
cols: 7 cols: 6
rows: 1 rows: 1
size_hint: 1, 0.25 size_hint: 1, 0.35
Label: Label:
size_hint: 0.01, 1 size_hint: 0.01, 1
BoxLayout: BoxLayout:
@@ -355,11 +344,6 @@
id: hints id: hints
text: 'top move' text: 'top move'
on_active: root.parent.update_state() on_active: root.parent.update_state()
BWCheckBoxHint:
size_hint: 0.2, 0.5
id: auto_undo
text: 'undo'
on_active: root.parent.update_state()
CheckBoxHint: CheckBoxHint:
size_hint: 0.2, 0.5 size_hint: 0.2, 0.5
id: ownership id: ownership
@@ -383,18 +367,13 @@
size_hint: 1, 0.15 size_hint: 1, 0.15
label: 'Score' label: 'Score'
text: '...' text: '...'
CensorableLabel: BoxLayout: # ----------- play -----------
id: points_lost
size_hint: 1, 0.15
label: 'Point loss'
text: '...'
BoxLayout:
orientation: 'vertical' orientation: 'vertical'
size_hint: 0,1 size_hint: 1,1
opacity: 0 opacity: 1
id: play_tab id: play_tab
GridLayout: GridLayout:
cols: 4 cols: 5
rows: 1 rows: 1
size_hint: 1, 0.1 size_hint: 1, 0.1
BoxLayout: BoxLayout:
@@ -411,6 +390,11 @@
id: ai_auto id: ai_auto
default_active: False default_active: False
size_hint: 0.25, 1 size_hint: 0.25, 1
BWCheckBoxHint:
size_hint: 0.2, 0.5
id: auto_undo
text: 'undo'
on_active: root.parent.update_state()
CheckBoxHint: CheckBoxHint:
size_hint: 0.25, 1 size_hint: 0.25, 1
text: 'fast' text: 'fast'
@@ -438,6 +422,11 @@
LargeLabel: LargeLabel:
text: 'free real estate' text: 'free real estate'
size_hint: 1,0.5 size_hint: 1,0.5
CensorableLabel:
id: points_lost
size_hint: 1, 0.03
label: 'Point loss'
text: '...'
GridLayout: GridLayout:
cols: 2 cols: 2
rows: 1 rows: 1
@@ -449,7 +438,7 @@
default_active: True default_active: True
StyledButton: StyledButton:
text: 'Pass' text: 'Pass'
on_press: root.katrain("play",(None,None)) on_press: root.katrain("play",None)
TextInput: TextInput:
id: info id: info
size_hint: 1, 0.2 size_hint: 1, 0.2
+9 -6
View File
@@ -39,7 +39,7 @@ class KaTrainGui(BoxLayout):
def log(self, message, level=OUTPUT_INFO): def log(self, message, level=OUTPUT_INFO):
if level == OUTPUT_ERROR: if level == OUTPUT_ERROR:
self.controls.set_status(f"ERROR: {message}",self.game.current_node) self.controls.set_status(f"ERROR: {message}", self.game.current_node)
print(f"ERROR: {message}") print(f"ERROR: {message}")
elif self.debug_level >= level: elif self.debug_level >= level:
print(message) print(message)
@@ -108,16 +108,19 @@ class KaTrainGui(BoxLayout):
def _do_new_game(self, board_size=None, move_tree=None): 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.game = Game(self, self.engine, self.config("game"), board_size=board_size, move_tree=move_tree)
self.controls.unlock() self.controls.unlock()
self.update_state(redraw_board=True) 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?
def _do_ai_move(self, node=None): def _do_ai_move(self, node=None):
if node is None or self.game.current_node == node: if node is None or self.game.current_node == node:
self.game.ai_move(self.config('trainer')) self.game.ai_move(self.config("trainer"))
self.update_state() self.update_state()
def _do_undo(self, n_times=1): 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"): 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) 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 return
self.game.undo(n_times) self.game.undo(n_times)
self.update_state() self.update_state()
@@ -225,8 +228,8 @@ class KaTrainGui(BoxLayout):
self.controls.set_status(f"Failed to imported game from clipboard: {e}") self.controls.set_status(f"Failed to imported game from clipboard: {e}")
return return
self._do_new_game(move_tree=move_tree) self._do_new_game(move_tree=move_tree)
self('redo',999) self("redo", 999)
self.log("Imported game from clipboard.",OUTPUT_INFO) self.log("Imported game from clipboard.", OUTPUT_INFO)
return True return True