extra analysis options

This commit is contained in:
Sander Land committed 2020-06-25 01:17:58 +02:00
1 parent 57922ddcb4
commit f57dbcdee9
24 files changed
+145 -24

No files matched your search

+3 -1
View File
@@ -103,6 +103,8 @@ Keyboard shortcuts are shown with **[key]**.
* **[a]**: Deeper analysis: Re-evaluate the position using more visits, usually resulting in a more accurate evaluation.
* **[s]**: Equalize visits: Re-evaluate all currently shown next moves with the same visits as the current top move. Useful to increase confidence in the suggestions with high uncertainty.
* **[d]**: Analyze all moves: Evaluate all possible next moves. This can take a bit of time even though 'fast_visits' is used, but can be useful to see how many reasonable next moves are available.
* **[f]**: Re-analyze game: ...
* **[spacebar]**: Turn continuous analysis on/off.
* **[enter]** AI move. Makes the AI move for the current player regardless of current player selection.
## Keyboard and mouse shortcuts
@@ -112,7 +114,7 @@ In addition to shortcuts mentioned above and those shown in the main menu:
* **[Shift]**: Open the main menu.
* **[~]** or **[ ` ]** or **[m]**: Cycles through more minimalistic UI modes.
* **[p]**: Pass
* **[spacebar]**: Pause/Resume timer
* **[b]**: Pause/Resume timer
* **[arrow left]** or **[z]**: Undo move. Hold shift for 10 moves at a time, or ctrl to skip to the start.
* **[arrow right]** or **[x]**: Redo move. Hold shift for 10 moves at a time, or ctrl to skip to the start.
* **[arrow up/down]** Switch branch, as would be expected from the move tree.
+19 -9
View File
@@ -89,6 +89,7 @@ class KaTrainGui(Screen, KaTrainBase):
self.teacher_settings_popup = None
self.timer_settings_popup = None
self.idle_analysis = False
self.message_queue = Queue()
self._keyboard = Window.request_keyboard(None, self, "")
@@ -113,6 +114,10 @@ class KaTrainGui(Screen, KaTrainBase):
def play_analyze_mode(self):
return self.play_mode.mode
def toggle_continuous_analysis(self):
self.idle_analysis = not self.idle_analysis
self.update_state()
def start(self):
if self.engine:
return
@@ -182,7 +187,9 @@ class KaTrainGui(Screen, KaTrainBase):
and not (teaching_undo and cn.auto_undo is None)
): # cn mismatch stops this if undo fired. avoid message loop here or fires repeatedly.
self._do_ai_move(cn)
Clock.schedule_once(lambda _dt: self.update_gui(cn, redraw_board=redraw_board), -1)
if len(self.engine.queries) == 0 and self.idle_analysis:
self("analyze-extra", "extra", continuous=True)
Clock.schedule_once(lambda _dt: self.update_gui(cn, redraw_board=redraw_board), -1) # trigger?
def update_player(self, bw, **kwargs):
super().update_player(bw, **kwargs)
@@ -197,7 +204,7 @@ class KaTrainGui(Screen, KaTrainBase):
def _message_loop_thread(self):
while True:
game, msg, *args = self.message_queue.get()
game, msg, args, kwargs = self.message_queue.get()
try:
self.log(f"Message Loop Received {msg}: {args} for Game {game}", OUTPUT_EXTRA_DEBUG)
if game != self.game.game_id:
@@ -206,19 +213,19 @@ class KaTrainGui(Screen, KaTrainBase):
)
continue
fn = getattr(self, f"_do_{msg.replace('-','_')}")
fn(*args)
fn(*args, **kwargs)
self.update_state()
except Exception as exc:
self.log(f"Exception in processing message {msg} {args}: {exc}", OUTPUT_ERROR)
traceback.print_exc()
def __call__(self, message, *args):
def __call__(self, message, *args, **kwargs):
if self.game:
if message.endswith("popup"): # gui code needs to run in main kivy thread.
fn = getattr(self, f"_do_{message.replace('-', '_')}")
Clock.schedule_once(lambda _dt: fn(*args), -1)
Clock.schedule_once(lambda _dt: fn(*args, **kwargs), -1)
else: # game related actions
self.message_queue.put([self.game.game_id, message, *args])
self.message_queue.put([self.game.game_id, message, args, kwargs])
def _do_new_game(self, move_tree=None, analyze_fast=False):
mode = self.play_analyze_mode
@@ -266,8 +273,8 @@ class KaTrainGui(Screen, KaTrainBase):
except IllegalMoveException as e:
self.controls.set_status(f"Illegal Move: {str(e)}")
def _do_analyze_extra(self, mode):
self.game.analyze_extra(mode)
def _do_analyze_extra(self, mode, **kwargs):
self.game.analyze_extra(mode, **kwargs)
def _do_new_game_popup(self):
self.controls.timer.paused = True
@@ -397,6 +404,7 @@ class KaTrainGui(Screen, KaTrainBase):
"a": ("analyze-extra", "extra"),
"s": ("analyze-extra", "equalize"),
"d": ("analyze-extra", "sweep"),
"f": ("analyze-extra", "game"),
"p": ("play", None),
"down": ("switch-branch", 1),
"up": ("switch-branch", -1),
@@ -429,8 +437,10 @@ class KaTrainGui(Screen, KaTrainBase):
elif keycode[1] == "shift":
self.nav_drawer.set_state("toggle")
elif keycode[1] == "spacebar":
self.toggle_continuous_analysis()
elif keycode[1] == "b" and "ctrl" not in modifiers:
self.controls.timer.paused = not self.controls.timer.paused
elif keycode[1] in ["`", "~", "m"]:
elif keycode[1] in ["`", "~", "m"] and "ctrl" not in modifiers:
self.zen = (self.zen + 1) % 3
elif keycode[1] in ["left", "z"]:
self("undo", 1 + ("shift" in modifiers) * 9 + ("ctrl" in modifiers) * 999)
+18 -3
View File
@@ -317,16 +317,29 @@ class Game:
f.write(sgf)
return i18n._("sgf written").format(file_name=file_name)
def analyze_extra(self, mode):
def analyze_extra(self, mode, **kwargs):
stones = {s.coords for s in self.stones}
cn = self.current_node
engine = self.engines[cn.next_player]
if mode == "extra":
visits = cn.analysis_visits_requested + engine.config["max_visits"]
if kwargs.get("continuous", False):
visits = max(engine.config["fast_visits"], math.ceil(cn.analysis_visits_requested * 1.1))
else:
visits = cn.analysis_visits_requested + engine.config["fast_visits"]
self.katrain.controls.set_status(i18n._("extra analysis").format(visits=visits))
cn.analyze(engine, visits=visits, priority=-1_000, time_limit=False)
return
if mode == "game":
nodes = self.root.nodes_in_tree
min_visits = min(node.analysis_visits_requested for node in nodes)
visits = min_visits + engine.config["max_visits"]
for node in nodes:
node.analyze(engine, visits=visits, priority=-1_000_000, time_limit=False)
self.katrain.controls.set_status(i18n._("game re-analysis").format(visits=visits))
return
elif mode == "sweep":
board_size_x, board_size_y = self.board_size
if cn.analysis_ready:
@@ -354,7 +367,7 @@ class Game:
visits = engine.config["fast_visits"]
self.katrain.controls.set_status(i18n._("sweep analysis").format(visits=visits))
priority = -1_000_000_000
else: # mode=='equalize':
elif mode == "equalize":
if not cn.analysis_ready:
self.katrain.controls.set_status(i18n._("wait-before-equalize"), self.current_node)
return
@@ -363,6 +376,8 @@ class Game:
visits = max(d["visits"] for d in cn.analysis["moves"].values())
self.katrain.controls.set_status(i18n._("equalizing analysis").format(visits=visits))
priority = -1_000
else:
raise ValueError("Invalid analysis mode")
for move in analyze_moves:
cn.analyze(
engine, priority, visits=visits, refine_move=move, time_limit=False
+3 -2
View File
@@ -69,8 +69,9 @@ class GameNode(SGFNode):
# various analysis functions
def analyze(self, engine, priority=0, visits=None, time_limit=True, refine_move=None, analyze_fast=False):
if visits and not refine_move:
self.analysis_visits_requested = max(visits, engine.config["max_visits"])
if not refine_move:
req_visits = visits or (engine.config["fast_visits"] if analyze_fast else engine.config["max_visits"])
self.analysis_visits_requested = max(req_visits, self.analysis_visits_requested)
engine.request_analysis(
self,
lambda result: self.set_analysis(result, refine_move),
+10
View File
@@ -721,6 +721,16 @@
icon: 'img/Sweep.png'
shortcut: 'd'
on_action: root.katrain("analyze-extra", "sweep")
AnalysisMenuItem:
text: i18n._("analysis:game")
icon: 'img/analysis.png'
shortcut: 'f'
on_action: root.katrain("analyze-extra", "game")
AnalysisMenuItem:
text: i18n._("analysis:continuous")
icon: 'img/off.png'
shortcut: 'space'
on_action: root.katrain.toggle_continuous_analysis()
AnalysisMenuItem:
text: i18n._("analysis:aimove")
icon: 'img/ai.png'
+3 -4
View File
@@ -383,15 +383,15 @@ class CollapsablePanel(MDBoxLayout):
option_active=self.build_options,
options_spacing=self.build_options,
)
self.bind(state=self._on_state,size_hint_y_open=self._on_size, height_open=self._on_size)
self.bind(state=self._on_state, size_hint_y_open=self._on_size, height_open=self._on_size)
MDApp.get_running_app().bind(language=lambda *_: Clock.schedule_once(self.build_options, 0))
self.build_options()
def _on_state(self,*_args):
def _on_state(self, *_args):
self.build()
self.trigger_select(ix=None)
def _on_size(self,*_args):
def _on_size(self, *_args):
height, size_hint_y = 1, None
if self.state == "open" and self.contents:
if self.height_open:
@@ -402,7 +402,6 @@ class CollapsablePanel(MDBoxLayout):
height = self.header.height
self.height, self.size_hint_y = height, size_hint_y
@property
def option_state(self):
return {option: active for option, active in zip(self.options, self.option_active)}
+1 -1
View File
@@ -59,4 +59,4 @@ ENGINE_BUSY_COL = EVAL_COLORS[2]
ENGINE_READY_COL = EVAL_COLORS[-1]
# info
INFO_PV_COLOR = to_hexcol(RED)
INFO_PV_COLOR = to_hexcol(YELLOW)
+3 -4
View File
@@ -222,13 +222,12 @@ class RankGraph(Graph):
half_seg = self.segment_length // 2
ranks = {"B": [], "W": []}
dx = self.segment_length//4
dx = self.segment_length // 4
for segment_mid in range(0, len(nodes), dx):
bounds = (max(0,segment_mid - half_seg), min(segment_mid + half_seg, len(nodes)))
for pl, rank in self.calculate_ranks(policy_stats[bounds[0] : bounds[1]+1], num_intersec).items():
bounds = (max(0, segment_mid - half_seg), min(segment_mid + half_seg, len(nodes)))
for pl, rank in self.calculate_ranks(policy_stats[bounds[0] : bounds[1] + 1], num_intersec).items():
ranks[pl].append((segment_mid, rank))
self.rank_by_player = ranks
print(ranks)
self.redraw_trigger()
def update_value(self, node):
Binary file not shown.
@@ -572,3 +572,15 @@ msgstr "All available models already downloaded"
#. TODO
msgid "tab:rank_est"
msgstr "Rank Estimate"
#. TODO
msgid "game re-analysis"
msgstr "Re-analyzing entire game using {visits} visits"
#. TODO
msgid "analysis:game"
msgstr "Re-analyze entire game"
#. TODO
msgid "analysis:continuous"
msgstr "Continuously analyze"
Binary file not shown.
@@ -615,3 +615,15 @@ msgstr "All available models already downloaded"
#. TODO
msgid "tab:rank_est"
msgstr "Rank Estimate"
#. TODO
msgid "game re-analysis"
msgstr "Re-analyzing entire game using {visits} visits"
#. TODO
msgid "analysis:game"
msgstr "Re-analyze entire game"
#. TODO
msgid "analysis:continuous"
msgstr "Continuously analyze"
Binary file not shown.
@@ -98,6 +98,15 @@ msgstr "Equalize visits of potential moves"
msgid "analysis:sweep"
msgstr "Fast analysis of all moves"
msgid "analysis:sweep"
msgstr "Fast analysis of all moves"
msgid "analysis:game"
msgstr "Re-analyze entire game"
msgid "analysis:continuous"
msgstr "Continuously analyze"
msgid "analysis:aimove"
msgstr "Force AI Move"
@@ -113,6 +122,10 @@ msgstr "Analyzing entire board using {visits} visits/move"
msgid "equalizing analysis"
msgstr "Equalizing analysis of candidate moves to {visits} visits"
#. status message
msgid "game re-analysis"
msgstr "Re-analyzing entire game using {visits} visits"
#. right panel buttons and below board
msgid "pass-button-text"
msgstr "Pass"
Binary file not shown.
@@ -610,3 +610,15 @@ msgstr "All available models already downloaded"
#. TODO
msgid "tab:rank_est"
msgstr "Rank Estimate"
#. TODO
msgid "game re-analysis"
msgstr "Re-analyzing entire game using {visits} visits"
#. TODO
msgid "analysis:game"
msgstr "Re-analyze entire game"
#. TODO
msgid "analysis:continuous"
msgstr "Continuously analyze"
Binary file not shown.
@@ -649,3 +649,15 @@ msgstr "All available models already downloaded"
#. TODO
msgid "tab:rank_est"
msgstr "Rank Estimate"
#. TODO
msgid "game re-analysis"
msgstr "Re-analyzing entire game using {visits} visits"
#. TODO
msgid "analysis:game"
msgstr "Re-analyze entire game"
#. TODO
msgid "analysis:continuous"
msgstr "Continuously analyze"
Binary file not shown.
@@ -587,3 +587,15 @@ msgstr "All available models already downloaded"
#. TODO
msgid "tab:rank_est"
msgstr "Rank Estimate"
#. TODO
msgid "game re-analysis"
msgstr "Re-analyzing entire game using {visits} visits"
#. TODO
msgid "analysis:game"
msgstr "Re-analyze entire game"
#. TODO
msgid "analysis:continuous"
msgstr "Continuously analyze"
Binary file not shown.
@@ -612,3 +612,15 @@ msgstr "All available models already downloaded"
#. TODO
msgid "tab:rank_est"
msgstr "Rank Estimate"
#. TODO
msgid "game re-analysis"
msgstr "Re-analyzing entire game using {visits} visits"
#. TODO
msgid "analysis:game"
msgstr "Re-analyze entire game"
#. TODO
msgid "analysis:continuous"
msgstr "Continuously analyze"
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB