analyze game popup

This commit is contained in:
Sander Land committed 2020-06-28 14:41:10 +02:00
1 parent 7a4a8e268b
commit d6be82ecba
26 files changed
+147 -30

No files matched your search

+17 -6
View File
@@ -34,7 +34,7 @@ for lang in locales:
print("duplicate", entry.msgid, "in", lang)
errors = True
lang_to_strings[lang].add(entry.msgid)
if todos[lang] and any('todo' in a for a in sys.argv):
if todos[lang] and any("todo" in a for a in sys.argv):
print(f"========== {lang} has {len(todos[lang])} TODO entries ========== ")
for item in todos[lang]:
print(item)
@@ -73,16 +73,27 @@ for lang in locales:
errors = True
for msgid, lang_entries in strings_to_langs.items():
if lang in lang_entries and 'TODO' in lang_entries[lang].comment:
if lang in lang_entries and "TODO" in lang_entries[lang].comment:
if any(e.msgstr == lang_entries[lang].msgstr for l, e in lang_entries.items() if l != lang):
todo_comment = f"TODO - {lang_entries[DEFAULT_LANG].comment}" if lang_entries[DEFAULT_LANG].comment else 'TODO' # update todo
if lang_entries[lang].msgstr != lang_entries[DEFAULT_LANG].msgstr or lang_entries[lang].comment.replace('\n',' ')!=todo_comment:
print([lang_entries[lang].msgstr, lang_entries[DEFAULT_LANG].msgstr, lang_entries[lang].comment, todo_comment])
todo_comment = (
f"TODO - {lang_entries[DEFAULT_LANG].comment}" if lang_entries[DEFAULT_LANG].comment else "TODO"
) # update todo
if (
lang_entries[lang].msgstr != lang_entries[DEFAULT_LANG].msgstr
or lang_entries[lang].comment.replace("\n", " ") != todo_comment
):
print(
[
lang_entries[lang].msgstr,
lang_entries[DEFAULT_LANG].msgstr,
lang_entries[lang].comment,
todo_comment,
]
)
lang_entries[lang].msgstr = lang_entries[DEFAULT_LANG].msgstr # update
lang_entries[lang].comment = todo_comment
print(f"{lang}/{msgid} todo entry updated")
po[lang].save(pofile[lang])
mofile = pofile[lang].replace(".po", ".mo")
po[lang].save_as_mofile(mofile)
+1 -2
View File
@@ -413,7 +413,6 @@ 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),
@@ -497,7 +496,7 @@ class KaTrainApp(MDApp):
Builder.load_file(kv_file)
Window.bind(on_request_close=self.on_request_close)
Window.bind(on_dropfile=lambda win, file: self.gui.load_sgf_file(file.decode('utf8')))
Window.bind(on_dropfile=lambda win, file: self.gui.load_sgf_file(file.decode("utf8")))
self.gui = KaTrainGui()
Builder.load_file(popup_kv_file)
+4 -2
View File
@@ -22,7 +22,8 @@ from katrain.core.constants import (
AI_PICK,
AI_RANK,
AI_HANDICAP,
OUTPUT_ERROR, AI_STRENGTH,
OUTPUT_ERROR,
AI_STRENGTH,
)
from katrain.core.game import Game, GameNode, Move
@@ -31,13 +32,14 @@ def ai_rank_estimation(strategy,settings) -> Tuple[int,bool]:
if strategy in [AI_DEFAULT, AI_HANDICAP, AI_JIGO]:
return 9, True
if strategy == AI_RANK:
return 1-settings['kyu_rank'], True
return 1 - settings["kyu_rank"], True
if strategy == AI_WEIGHTED:
dan_rank = -4
return dan_rank, True
else:
return AI_STRENGTH[strategy], False
def weighted_selection_without_replacement(items: List[Tuple], pick_n: int) -> List[Tuple]:
"""For a list of tuples where the second element is a weight, returns random items with those weights, without replacement."""
elt = [(math.log(random.random()) / item[1], item) for item in items] # magic
+2 -2
View File
@@ -58,7 +58,7 @@ AI_STRATEGIES_RECOMMENDED_ORDER = [
AI_STRENGTH = { # dan ranks
AI_DEFAULT: 9,
AI_POLICY: 4,
AI_JIGO: float('nan'),
AI_JIGO: float("nan"),
AI_SCORELOSS: -4,
AI_WEIGHTED: -4,
AI_PICK: -7,
@@ -66,7 +66,7 @@ AI_STRENGTH = { # dan ranks
AI_TENUKI: -7,
AI_INFLUENCE: -7,
AI_TERRITORY: -7,
AI_RANK: float('nan')
AI_RANK: float("nan"),
}
AI_OPTION_VALUES = {
+3 -2
View File
@@ -184,8 +184,6 @@ class Game:
ix = (ordered_children.index(cn) + len(ordered_children) + direction) % len(ordered_children)
self.set_current_node(ordered_children[ix])
@property
def board_size(self):
return self.root.board_size
@@ -310,6 +308,9 @@ class Game:
return
if mode == "game":
nodes = self.root.nodes_in_tree
if "visits" in kwargs:
visits = kwargs["visits"]
else:
min_visits = min(node.analysis_visits_requested for node in nodes)
visits = min_visits + engine.config["max_visits"]
for node in nodes:
+2 -3
View File
@@ -331,7 +331,7 @@ class SGF:
@classmethod
def parse_file(cls, filename, encoding=None) -> SGFNode:
is_gib = filename.lower().endswith('.gib')
is_gib = filename.lower().endswith(".gib")
"""Parse a file as SGF, encoding will be detected if not given."""
with open(filename, "rb") as f:
@@ -344,7 +344,7 @@ class SGF:
else:
encoding = "ISO-8859-1" # default
else:
encoding = 'utf8' # ?
encoding = "utf8" # ?
decoded = bin_contents.decode(encoding=encoding, errors="ignore")
if is_gib:
return cls.parse_gib(decoded)
@@ -382,7 +382,6 @@ class SGF:
raise ParseError(f"Parse Error: unexpected character at {self.contents[self.ix:self.ix+25]}")
raise ParseError("Parse Error: expected ')' at end of input.")
# GIB parser adapted from https://github.com/fohristiwhirl/gofish/
@classmethod
def parse_gib(cls, gib):
+3 -2
View File
@@ -553,6 +553,7 @@
graph.show_graphs(args[1])
graph.hidden = not (args[1]['score'] or args[1]['winrate'])
rank_graph.hidden = not args[1]['rank_est']
rank_graph.trigger_calculate()
ScoreGraph:
id: graph
opacity: 0 if self.hidden else 1
@@ -726,8 +727,8 @@
AnalysisMenuItem:
text: i18n._("analysis:game")
icon: 'img/analysis.png'
shortcut: 'f'
on_action: root.katrain("analyze-extra", "game")
shortcut: ''
on_action: root.open_game_analysis_popup()
AnalysisMenuItem:
text: i18n._("analysis:continuous")
icon: 'img/off.png'
+8 -1
View File
@@ -7,9 +7,11 @@ from kivy.clock import Clock
from kivy.core.window import Window
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Ellipse, Line, Rectangle
from kivy.metrics import dp
from kivy.properties import ListProperty, ObjectProperty, BooleanProperty
from kivy.uix.dropdown import DropDown
from kivy.uix.widget import Widget
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.floatlayout import MDFloatLayout
@@ -18,6 +20,7 @@ from katrain.core.game import Move
from katrain.core.lang import i18n
from katrain.core.utils import evaluation_class, var_to_grid
from katrain.gui.kivyutils import draw_circle, draw_text, BackgroundMixin
from katrain.gui.popups import I18NPopup, ReAnalyzeGamePopup
from katrain.gui.style import *
@@ -467,7 +470,11 @@ class BadukPanWidget(Widget):
class AnalysisDropDown(DropDown):
pass
def open_game_analysis_popup(self, *_args):
analysis_popup = I18NPopup(title_key="analysis:game", size=[dp(500), dp(300)], content=ReAnalyzeGamePopup())
analysis_popup.content.popup = analysis_popup
analysis_popup.content.katrain = MDApp.get_running_app().gui
analysis_popup.open()
class AnalysisControls(MDBoxLayout):
+9 -3
View File
@@ -4,7 +4,7 @@ import re
from typing import Any, Dict, List, Tuple, Union
from kivy.clock import Clock
from kivy.properties import BooleanProperty, NumericProperty, StringProperty, ListProperty
from kivy.properties import BooleanProperty, NumericProperty, StringProperty, ListProperty, ObjectProperty
from kivy.metrics import dp
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.boxlayout import BoxLayout
@@ -23,7 +23,8 @@ from katrain.core.constants import (
OUTPUT_DEBUG,
OUTPUT_ERROR,
OUTPUT_INFO,
AI_OPTION_VALUES, STATUS_INFO,
AI_OPTION_VALUES,
STATUS_INFO,
)
from katrain.core.engine import KataGoEngine
from katrain.core.lang import i18n
@@ -323,7 +324,7 @@ class ConfigAIPopup(QuickConfigGui):
options = {k[len(prefix) :]: v for k, v in options.items() if k.startswith(prefix)}
print(options)
dan_rank, model_based = ai_rank_estimation(strategy, options)
self.estimated_rank_label.text = ('' if model_based else '~') + RankGraph.rank_label(dan_rank)
self.estimated_rank_label.text = ("" if model_based else "~") + RankGraph.rank_label(dan_rank)
def build_ai_options(self, *_args):
strategy = self.ai_select.selected[1]
@@ -472,3 +473,8 @@ class LoadSGFPopup(BoxLayout):
]
self.filesel.path = os.path.abspath(os.path.expanduser(app.gui.config("general/sgf_load")))
self.filesel.select_string = i18n._("Load File")
class ReAnalyzeGamePopup(BoxLayout):
katrain = ObjectProperty(None)
popup = ObjectProperty(None)
+14 -3
View File
@@ -5,6 +5,7 @@ from kivy.metrics import dp
from kivy.properties import BooleanProperty, ListProperty, NumericProperty, Clock, StringProperty
from kivymd.app import MDApp
from katrain.core.constants import OUTPUT_ERROR
from katrain.core.lang import i18n
from katrain.gui.kivyutils import BackgroundMixin
import threading
@@ -224,7 +225,7 @@ class RankGraph(Graph):
return {pl: RankGraph.calculate_rank_for_player(segment_stats, num_intersec, pl) for pl in "BW"}
def rank_game(self):
try:
nodes = self.nodes
parent_policy_per_move = [node.parent.policy_ranking if node.parent else None for node in nodes]
num_legal_moves = [
@@ -232,7 +233,11 @@ class RankGraph(Graph):
for policy_ranking in parent_policy_per_move
]
policy_stats = [
[(num_mv, rank, value, mv.player) for rank, (value, mv) in enumerate(policy_ranking) if mv == move.move][0]
[
(num_mv, rank, value, mv.player)
for rank, (value, mv) in enumerate(policy_ranking)
if mv == move.move
][0]
if policy_ranking
else (None, None, None, None)
for move, policy_ranking, num_mv in zip(nodes, parent_policy_per_move, num_legal_moves)
@@ -249,11 +254,17 @@ class RankGraph(Graph):
if bounds[1] - bounds[0] > self.segment_length * 0.75:
ranks[pl].append((segment_mid, rank))
self.rank_by_player = ranks
except Exception as e:
MDApp.get_running_app().gui.log(f"Exception while calculating rank: {e}", OUTPUT_ERROR)
self.redraw_trigger()
def update_value(self, node):
super().update_value(node)
self.calculate_trigger() # recalc here on trigger for speed
self.trigger_calculate()
def trigger_calculate(self):
if self.opacity != 0: # recalc here on trigger and only if visible for speed
self.calculate_trigger()
def update_graph(self, *args):
if self.rank_by_player:
Binary file not shown.
@@ -597,3 +597,11 @@ msgstr ""
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
"perspective. When `automatic` is set, KaTrain will find a suitable value "
"automatically."
#. TODO - in AI settings
msgid "estimated strength"
msgstr "Estimated Strength"
#. TODO - label in little popup for analyzing entire game
msgid "reanalyze max visits"
msgstr "Number of visits per move"
Binary file not shown.
@@ -640,3 +640,11 @@ msgstr ""
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
"perspective. When `automatic` is set, KaTrain will find a suitable value "
"automatically."
#. TODO - in AI settings
msgid "estimated strength"
msgstr "Estimated Strength"
#. TODO - label in little popup for analyzing entire game
msgid "reanalyze max visits"
msgstr "Number of visits per move"
Binary file not shown.
@@ -635,3 +635,7 @@ msgstr "{num} available model(s) found"
#. error message on trying to download models when already done
msgid "All models downloaded"
msgstr "All available models already downloaded"
#. label in little popup for analyzing entire game
msgid "reanalyze max visits"
msgstr "Number of visits per move"
Binary file not shown.
@@ -627,3 +627,11 @@ msgstr ""
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
"perspective. When `automatic` is set, KaTrain will find a suitable value "
"automatically."
#. TODO - in AI settings
msgid "estimated strength"
msgstr "Estimated Strength"
#. TODO - label in little popup for analyzing entire game
msgid "reanalyze max visits"
msgstr "Number of visits per move"
Binary file not shown.
@@ -674,3 +674,11 @@ msgstr ""
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
"perspective.When `automatic` is set, KaTrain will find a suitable value "
"automatically."
#. TODO - in AI settings
msgid "estimated strength"
msgstr "Estimated Strength"
#. TODO - label in little popup for analyzing entire game
msgid "reanalyze max visits"
msgstr "Number of visits per move"
Binary file not shown.
@@ -612,3 +612,11 @@ msgstr ""
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
"perspective. When `automatic` is set, KaTrain will find a suitable value "
"automatically."
#. TODO - in AI settings
msgid "estimated strength"
msgstr "Estimated Strength"
#. TODO - label in little popup for analyzing entire game
msgid "reanalyze max visits"
msgstr "Number of visits per move"
Binary file not shown.
@@ -637,3 +637,11 @@ msgstr ""
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
"perspective. When `automatic` is set, KaTrain will find a suitable value "
"automatically."
#. TODO - in AI settings
msgid "estimated strength"
msgstr "Estimated Strength"
#. TODO - label in little popup for analyzing entire game
msgid "reanalyze max visits"
msgstr "Number of visits per move"
+23
View File
@@ -450,6 +450,29 @@
on_press: root.update_config(True)
<ReAnalyzeGamePopup>:
visits: visits
orientation: 'vertical'
BoxLayout:
size_hint: 1, 1.5
orientation: 'horizontal'
DescriptionLabel:
text: i18n._('reanalyze max visits')
AnchorLayout:
LabelledIntInput:
id: visits
text: '2500'
AnchorLayout:
size_hint: 1, 1
AutoSizedRoundedRectangleButton:
padding_x: 15
size_hint: None,0.5
text: i18n._("analysis:game") # re-use string
on_press:
root.katrain("analyze-extra", "game", visits=visits.input_value)
root.popup.dismiss()
<LoadSGFPopup>:
fast: fast
rewind: rewind
+9 -4
View File
@@ -99,8 +99,13 @@ def test_ogs():
def test_gibo():
file = os.path.join(os.path.dirname(__file__), "data/test.gib")
root = SGF.parse_file(file)
assert {'PW': ['wildsim1'], 'WR': ['2D'], 'PB': ['kim'], 'BR': ['2D'], 'RE': ['W+T'], 'KM': [6.5], 'DT': ['2020-06-14']} == root.properties
assert {
"PW": ["wildsim1"],
"WR": ["2D"],
"PB": ["kim"],
"BR": ["2D"],
"RE": ["W+T"],
"KM": [6.5],
"DT": ["2020-06-14"],
} == root.properties
assert "pp" == root.children[0].get_property("B")