hints for player names, flake8, rankgraph ded

This commit is contained in:
Sander Land committed 2020-12-09 22:15:14 +01:00
1 parent 0276e4b2a4
commit 2499488a20
35 files changed
+100 -231

No files matched your search

+5 -5
View File
@@ -74,7 +74,7 @@ from katrain.core.game import Game, IllegalMoveException, KaTrainSGF
from katrain.core.sgf_parser import Move, ParseError
from katrain.gui.kivyutils import *
from katrain.gui.popups import ConfigPopup, LoadSGFPopup, NewGamePopup, ConfigAIPopup
from katrain.gui.style import ENGINE_BUSY_COL, ENGINE_DOWN_COL, ENGINE_READY_COL, LIGHTGREY
from katrain.gui.style import ENGINE_BUSY_COL, ENGINE_DOWN_COL, ENGINE_READY_COL
from katrain.gui.widgets import *
from katrain.gui.badukpan import AnalysisControls, BadukPanControls, BadukPanWidget
from katrain.gui.controlspanel import ControlsPanel
@@ -108,14 +108,14 @@ class KaTrainGui(Screen, KaTrainBase):
super().log(message, level)
if level == OUTPUT_KATAGO_STDERR and "ERROR" not in self.controls.status.text:
if "starting" in message.lower():
self.controls.set_status(f"KataGo engine starting...", STATUS_INFO)
self.controls.set_status("KataGo engine starting...", STATUS_INFO)
if message.startswith("Tuning"):
self.controls.set_status(
f"KataGo is tuning settings for first startup, please wait." + message, STATUS_INFO
"KataGo is tuning settings for first startup, please wait." + message, STATUS_INFO
)
return
if "ready" in message.lower():
self.controls.set_status(f"KataGo engine ready.", STATUS_INFO)
self.controls.set_status("KataGo engine ready.", STATUS_INFO)
if (
level == OUTPUT_ERROR
or (level == OUTPUT_KATAGO_STDERR and "error" in message.lower() and "tuning" not in message.lower())
@@ -450,7 +450,7 @@ class KaTrainGui(Screen, KaTrainBase):
def load_sgf_from_clipboard(self):
clipboard = Clipboard.paste()
if not clipboard:
self.controls.set_status(f"Ctrl-V pressed but clipboard is empty.", STATUS_INFO)
self.controls.set_status("Ctrl-V pressed but clipboard is empty.", STATUS_INFO)
return
url_match = re.match(r"(?P<url>https?://[^\s]+)", clipboard)
+3 -3
View File
@@ -203,7 +203,7 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
game, cn, {"playoutDoublingAdvantage": pda, "playoutDoublingAdvantagePla": "BLACK"}
)
if not handicap_analysis:
game.katrain.log(f"Error getting handicap-based move", OUTPUT_ERROR)
game.katrain.log("Error getting handicap-based move", OUTPUT_ERROR)
ai_mode = AI_DEFAULT
while not cn.analysis_complete:
@@ -225,7 +225,7 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
ai_mode in [AI_LOCAL, AI_TENUKI] and not (cn.move and cn.move.coords)
):
ai_mode = AI_WEIGHTED
ai_thoughts += f"Strategy override, using policy-weighted strategy instead. "
ai_thoughts += "Strategy override, using policy-weighted strategy instead. "
ai_settings = {"pick_override": 0.9, "weaken_fac": 1, "lower_bound": 0.02}
if top_5_pass:
@@ -336,7 +336,7 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
AI_HANDICAP,
]: # don't play suicidal to balance score
aimove = top_cand
ai_thoughts += f"Top move is pass, so passing regardless of strategy. "
ai_thoughts += "Top move is pass, so passing regardless of strategy. "
else:
if ai_mode == AI_JIGO:
sign = cn.player_sign(cn.next_player)
-1
View File
@@ -10,7 +10,6 @@ from kivy.clock import Clock
from katrain.core.constants import (
ANALYSIS_FORMAT_VERSION,
HOMEPAGE,
OUTPUT_DEBUG,
OUTPUT_INFO,
PLAYER_AI,
+2 -2
View File
@@ -280,7 +280,7 @@ class SGFNode:
@property
def next_player(self):
"""Returns player to move"""
if "B" in self.properties or ("AB" in self.properties and not "W" in self.properties): # root or black moved
if "B" in self.properties or ("AB" in self.properties and "W" not in self.properties): # root or black moved
return "W"
else:
return "B"
@@ -288,7 +288,7 @@ class SGFNode:
@property
def player(self):
"""Returns player that moved last. nb root is considered white played if no handicap stones are placed"""
if "B" in self.properties or ("AB" in self.properties and not "W" in self.properties):
if "B" in self.properties or ("AB" in self.properties and "W" not in self.properties):
return "B"
else:
return "W"
+1 -1
View File
@@ -5,7 +5,7 @@ from typing import List, Tuple, TypeVar
try:
import importlib.resources as pkg_resources
except:
except ImportError:
import importlib_resources as pkg_resources
T = TypeVar("T")
+5 -6
View File
@@ -1,4 +1,3 @@
import copy
import math
import random
import time
@@ -33,7 +32,7 @@ from katrain.core.constants import (
from katrain.core.game import Move
from katrain.core.lang import i18n
from katrain.core.utils import evaluation_class, format_visits, var_to_grid
from katrain.gui.kivyutils import BackgroundMixin, draw_circle, draw_text, cached_texture
from katrain.gui.kivyutils import draw_circle, draw_text, cached_texture
from katrain.gui.popups import I18NPopup, ReAnalyzeGamePopup
from katrain.gui.style import *
@@ -209,7 +208,7 @@ class BadukPanWidget(Widget):
Rectangle(
pos=(self.gridpos_x[x] - evalsize, self.gridpos_y[y] - evalsize),
size=(2 * evalsize, 2 * evalsize),
texture=cached_texture(f"img/dot.png"),
texture=cached_texture("img/dot.png"),
)
if innercol:
Color(*innercol)
@@ -217,7 +216,7 @@ class BadukPanWidget(Widget):
Rectangle(
pos=(self.gridpos_x[x] - inner_size, self.gridpos_y[y] - inner_size),
size=(2 * inner_size, 2 * inner_size),
texture=cached_texture(f"img/inner.png"),
texture=cached_texture("img/inner.png"),
)
def eval_color(self, points_lost, show_dots_for_class: List[bool] = None) -> Optional[List[float]]:
@@ -504,7 +503,7 @@ class BadukPanWidget(Widget):
katrain = self.katrain
game_ended = katrain.game.end_result
current_node = katrain.game.current_node
player, next_player = current_node.player, current_node.next_player
next_player = current_node.next_player
board_size_x, board_size_y = katrain.game.board_size
if len(self.gridpos_x) < board_size_x or len(self.gridpos_y) < board_size_y:
@@ -580,7 +579,7 @@ class BadukPanWidget(Widget):
)
if text_on and top_moves_show: # TODO: faster if not sized?
keys = {"size": self.grid_size / 3, "smallsize": self.grid_size / 3.33}
player_sign = current_node.player_sign(current_node.next_player)
player_sign = current_node.player_sign(next_player)
if len(top_moves_show) == 1:
fmt = "[size={size:.0f}]{" + top_moves_show[0] + "}[/size]"
else:
+1 -1
View File
@@ -135,7 +135,7 @@ class ControlsPanel(BoxLayout):
lock_ai = katrain.config("trainer/lock_ai") and katrain.play_analyze_mode == MODE_PLAY
details = self.info.detailed and not lock_ai
info = ""
if current_node.move or current_node.is_root:
if move or current_node.is_root:
info = self.active_comment_node.comment(
teach=katrain.players_info[self.active_comment_node.player].being_taught, details=details
)
+2 -6
View File
@@ -1,6 +1,3 @@
import functools
from kivy.cache import Cache
from kivy.clock import Clock
from kivy.core.image import Image
from kivy.core.text import Label as CoreLabel
@@ -16,13 +13,12 @@ from kivy.properties import (
StringProperty,
)
from kivy.resources import resource_find
from kivy.uix.behaviors import ButtonBehavior, FocusBehavior, ToggleButtonBehavior
from kivy.uix.behaviors import ButtonBehavior, ToggleButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.spinner import Spinner
from kivy.uix.textinput import TextInput
from kivy.uix.widget import Widget
from kivymd.app import MDApp
from kivymd.uix.behaviors import CircularRippleBehavior, RectangularRippleBehavior
@@ -229,7 +225,7 @@ class KeyValueSpinner(Spinner):
def input_value(self):
try:
return self.value_refs[self.selected_index]
except:
except KeyError:
return ""
@property
+1 -1
View File
@@ -207,7 +207,7 @@ class QuickConfigGui(MDBoxLayout):
selected = 0
try:
selected = widget.value_refs.index(value)
except:
except: # noqa: E722
pass
widget.text = widget.values[selected]
else:
+2 -2
View File
@@ -1,6 +1,6 @@
from katrain.gui.widgets.filebrowser import I18NFileBrowser
from katrain.gui.widgets.graph import RankGraph, ScoreGraph
from katrain.gui.widgets.graph import ScoreGraph
from katrain.gui.widgets.movetree import MoveTree
from katrain.gui.widgets.selection_slider import SelectionSlider
__all__ = ["ScoreGraph", "RankGraph", "MoveTree", "I18NFileBrowser", "SelectionSlider"]
__all__ = ["ScoreGraph", "MoveTree", "I18NFileBrowser", "SelectionSlider"]
+6 -9
View File
@@ -38,7 +38,6 @@ a shortcut to the Documents directory added to the favorites bar::
:align: right
"""
import string
import warnings
from functools import partial
from os import walk
from os.path import dirname, expanduser, getmtime, isdir, join, sep
@@ -81,27 +80,25 @@ def get_drives():
drives = []
if platform == "win":
bitmask = windll.kernel32.GetLogicalDrives()
GetVolumeInformationW = windll.kernel32.GetVolumeInformationW
for letter in string.ascii_uppercase:
if bitmask & 1:
name = create_unicode_buffer(64)
# get name of the drive
drive = letter + u":"
res = GetVolumeInformationW(drive + sep, name, 64, None, None, None, None, 0)
drive = letter + ":"
if isdir(drive):
drives.append((drive, name.value))
bitmask >>= 1
elif platform == "linux":
drives.append((sep, sep))
drives.append((expanduser(u"~"), "~/"))
places = (sep + u"mnt", sep + u"media")
drives.append((expanduser("~"), "~/"))
places = (sep + "mnt", sep + "media")
for place in places:
if isdir(place):
for directory in next(walk(place))[1]:
drives.append((place + sep + directory, directory))
elif platform == "macosx" or platform == "ios":
drives.append((expanduser(u"~"), "~/"))
vol = sep + u"Volume"
drives.append((expanduser("~"), "~/"))
vol = sep + "Volume"
if isdir(vol):
for drive in next(walk(vol))[1]:
drives.append((vol + sep + drive, drive))
@@ -154,7 +151,7 @@ Builder.load_string(
layout: layout
I18NFileChooserListLayout:
id: layout
controller: root
controller: root
"""
)
Config.set("kivy", "log_level", log_level)
+3 -177
View File
@@ -7,9 +7,6 @@ from kivy.properties import BooleanProperty, Clock, ListProperty, NumericPropert
from kivy.uix.widget import Widget
from kivymd.app import MDApp
from katrain.core.constants import OUTPUT_ERROR
from katrain.core.lang import rank_label
class Graph(Widget):
marker_font_size = NumericProperty(0)
@@ -128,11 +125,11 @@ class ScoreGraph(Graph):
xscale = self.width / max(len(score_values) - 1, 15)
available_height = self.height
score_line_points = [
[self.x + i * xscale, self.y + self.height / 2 + available_height / 2 * (val / self.score_scale),]
[self.x + i * xscale, self.y + self.height / 2 + available_height / 2 * (val / self.score_scale)]
for i, val in enumerate(score_values)
]
winrate_line_points = [
[self.x + i * xscale, self.y + self.height / 2 + available_height / 2 * (val / self.winrate_scale),]
[self.x + i * xscale, self.y + self.height / 2 + available_height / 2 * (val / self.winrate_scale)]
for i, val in enumerate(winrate_values)
]
self.score_points = sum(score_line_points, [])
@@ -158,144 +155,6 @@ class ScoreGraph(Graph):
self.winrate_dot_pos = winrate_dot_point
def averagemod(data):
sorteddata = sorted(data)
lendata = len(data)
return sum(sorteddata[int(lendata * 0.2) : int(lendata * 0.8) + 1]) / (
(int(lendata * 0.8) + 1) - int(lendata * 0.2)
) # average without the best and worst 20% of ranks
def gauss(data):
return math.exp(-1 * (data) ** 2)
class RankGraph(Graph):
black_rank_points = ListProperty([])
white_rank_points = ListProperty([])
segment_length = NumericProperty(80)
RANK_CAP = 5
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.calculate_trigger = Clock.create_trigger(lambda *args: self.rank_game(), 0.25)
self.rank_by_player = {}
@staticmethod
def calculate_rank_for_player(segment_stats, num_intersec, player):
non_obvious_moves = [
(nl, r, val)
for nl, r, val, pl in segment_stats
if nl is not None and val < (0.8 * (1 - (num_intersec - nl) / num_intersec * 0.5)) and pl == player
]
if not non_obvious_moves:
return None
num_legal, rank, value = zip(*non_obvious_moves)
rank = [min(r, nl * 0.09) for r, nl in zip(rank, num_legal)]
averagemod_rank = averagemod(rank)
averagemod_len_legal = averagemod(num_legal)
norm_avemod_len_legal = averagemod_len_legal / num_intersec
if averagemod_rank > 0.1:
rank_kyu = (
-0.97222
* math.log(averagemod_rank)
/ (0.24634 + averagemod_rank * gauss(3.3208 * (norm_avemod_len_legal)))
+ 12.703 * (norm_avemod_len_legal)
+ 11.198 * math.log(averagemod_rank)
+ 12.28 * gauss(2.379 * (norm_avemod_len_legal))
- 16.544
)
else:
rank_kyu = -4
return min(RankGraph.RANK_CAP, 1 - rank_kyu) # dan rank
@staticmethod
def calculate_ranks(segment_stats, num_intersec):
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 = [
sum(pv >= 0 for pv, _ in policy_ranking) if policy_ranking else 0
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 == node.move
][0]
if policy_ranking and node.move
else (None, None, None, None)
for node, policy_ranking, num_mv in zip(nodes, parent_policy_per_move, num_legal_moves)
]
size = self.nodes[0].board_size
num_intersec = size[0] * size[1]
half_seg = self.segment_length // 2
ranks = {"B": [], "W": []}
dx = self.segment_length // 4
for segment_mid in range(dx, len(nodes), dx):
bounds = (max(0, segment_mid - half_seg), min(segment_mid + half_seg, len(nodes)))
num_analyzed = sum(num_mv is not None for num_mv, _, _, _ in policy_stats[bounds[0] : bounds[1] + 1])
if num_analyzed >= self.segment_length * 0.75:
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
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.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:
xscale = self.width / max(len(self.nodes) - 1, 15)
available_height = self.height
all_ranks = [rank for lst in self.rank_by_player.values() for seg, rank in lst if rank is not None]
if not all_ranks:
return
min_rank = math.floor(min(all_ranks))
max_rank = math.ceil(max(all_ranks))
if max_rank == min_rank:
min_rank -= 1
if (max_rank - min_rank) % 2 != 0: # make midpoint whole integer
if abs(max_rank - max(all_ranks)) < abs(min(all_ranks) - min_rank) and max_rank < self.RANK_CAP:
max_rank += 1
else:
min_rank -= 1
rank_range = max_rank - min_rank
self.ids.mid_marker.text = rank_label((max_rank + min_rank) / 2)
self.ids.top_marker.text = rank_label(max_rank) + ("+" if max_rank == self.RANK_CAP else "")
self.ids.bottom_marker.text = rank_label(min_rank)
graph_points = {}
for pl, rank_points in self.rank_by_player.items():
graph_points[pl] = [
[
self.x + i * xscale,
self.y + available_height * (val - min_rank) / rank_range if val is not None else math.nan,
]
for i, val in rank_points
]
self.black_rank_points = sum(graph_points["B"], [])
self.white_rank_points = sum(graph_points["W"], [])
else:
self.black_rank_points = []
self.white_rank_points = []
Builder.load_string(
"""
#:set GRAPH_CENTER_COLOR [0.5,0.5,0.5]
@@ -387,39 +246,6 @@ Builder.load_string(
color: WINRATE_MARKER_COLOR
pos:root.pos[0]+1, root.pos[1]
text: "{}%".format(50 - root.winrate_scale)
opacity: int(root.show_winrate)
<RankGraph>:
background_color: LIGHTER_BACKGROUND_COLOR
canvas:
Color:
rgba: WHITE
Line:
points: root.white_rank_points
width: dp(1.1)
Color:
rgba: BLACK
Line:
points: root.black_rank_points
width: dp(1.1)
# rank ticks
GraphMarkerLabel:
id: mid_marker
font_size: root.marker_font_size
color: YELLOW
pos: root.x + root.width - self.width-1, root.y + root.height*0.5 - self.height/2 + 2
text: '?' + i18n._('strength:kyu')
GraphMarkerLabel:
id: top_marker
font_size: root.marker_font_size
color: YELLOW
pos: root.x + root.width - self.width-1, root.pos[1]+root.height - self.font_size - 1
text: '?' + i18n._('strength:kyu')
GraphMarkerLabel:
id: bottom_marker
font_size: root.marker_font_size
color: YELLOW
pos: root.x + root.width - self.width-1, root.pos[1]
text: '?' + i18n._('strength:kyu')
opacity: int(root.show_winrate)
"""
)
+1 -7
View File
@@ -12,20 +12,14 @@ from kivymd.app import MDApp
from katrain.gui.kivyutils import BackgroundMixin, draw_circle, draw_text
from katrain.gui.style import (
BACKGROUND_COLOR,
DARKRED,
GREEN,
LIGHT_ORANGE,
LIGHTER_BACKGROUND_COLOR,
LIGHTGREY,
ORANGE,
OUTLINE_COLORS,
RED,
STONE_COLORS,
STONE_TEXT_COLORS,
WHITE,
YELLOW,
GREY,
)
@@ -268,7 +262,7 @@ Builder.load_string(
scroll_view_widget: root
id: move_tree_canvas
size_hint: None, None
<MoveTreeDropdownItem@MenuItem>:
canvas.before:
Color:
+1 -3
View File
@@ -1,8 +1,6 @@
from kivy.lang import Builder
from kivy.properties import BooleanProperty, ListProperty, NumericProperty
from kivy.uix.widget import Widget
from kivymd.uix.label import MDLabel
from kivymd.uix.selectioncontrol import Thumb
class SelectionSlider(Widget):
@@ -122,7 +120,7 @@ KV = """
size:
(max(dp(28), label.texture_size[0]+4) , dp(28))
pos:
(slider.px_pos - dp(9), slider.center_y - hint_box.height / 2 + dp(30))
(slider.px_pos - dp(9), slider.center_y - hint_box.height / 2 + dp(30))
Label:
id: label
Binary file not shown.
@@ -762,3 +762,10 @@ msgstr "Expand/Collapse"
msgid "analysis:playtoend"
msgstr "Fast playout to game end"
#. TODO - hint on player name input
msgid "black player name hint"
msgstr "Black player's name"
#. TODO - hint on player name input
msgid "white player name hint"
msgstr "White player's name"
Binary file not shown.
@@ -814,3 +814,10 @@ msgstr "Expand/Collapse"
msgid "analysis:playtoend"
msgstr "Fast playout to game end"
#. TODO - hint on player name input
msgid "black player name hint"
msgstr "Black player's name"
#. TODO - hint on player name input
msgid "white player name hint"
msgstr "White player's name"
Binary file not shown.
@@ -360,6 +360,14 @@ msgstr "Setup New Game"
msgid "player names"
msgstr "Player Names"
#. hint on player name input
msgid "black player name hint"
msgstr "Black player's name"
#. hint on player name input
msgid "white player name hint"
msgstr "White player's name"
msgid "board size"
msgstr "Board size"
Binary file not shown.
@@ -821,3 +821,10 @@ msgstr "Expand/Collapse"
msgid "analysis:playtoend"
msgstr "Fast playout to game end"
#. TODO - hint on player name input
msgid "black player name hint"
msgstr "Black player's name"
#. TODO - hint on player name input
msgid "white player name hint"
msgstr "White player's name"
Binary file not shown.
@@ -847,3 +847,11 @@ msgstr "Expand/Collapse"
#. TODO - Menu option
msgid "analysis:playtoend"
msgstr "Fast playout to game end"
#. TODO - hint on player name input
msgid "black player name hint"
msgstr "Black player's name"
#. TODO - hint on player name input
msgid "white player name hint"
msgstr "White player's name"
Binary file not shown.
@@ -804,3 +804,11 @@ msgstr "Expand/Collapse"
#. TODO - Menu option
msgid "analysis:playtoend"
msgstr "Fast playout to game end"
#. TODO - hint on player name input
msgid "black player name hint"
msgstr "Black player's name"
#. TODO - hint on player name input
msgid "white player name hint"
msgstr "White player's name"
Binary file not shown.
@@ -775,3 +775,10 @@ msgstr "Expand/Collapse"
msgid "analysis:playtoend"
msgstr "Fast playout to game end"
#. TODO - hint on player name input
msgid "black player name hint"
msgstr "Black player's name"
#. TODO - hint on player name input
msgid "white player name hint"
msgstr "White player's name"
Binary file not shown.
@@ -811,3 +811,10 @@ msgstr "Expand/Collapse"
msgid "analysis:playtoend"
msgstr "Fast playout to game end"
#. TODO - hint on player name input
msgid "black player name hint"
msgstr "Black player's name"
#. TODO - hint on player name input
msgid "white player name hint"
msgstr "White player's name"
+2
View File
@@ -393,11 +393,13 @@
LabelledTextInput:
text: ''
id: black_player_name
hint_text: i18n._("black player name hint")
size_hint: 0.75, None
AnchorLayout:
LabelledTextInput:
text: ''
id: white_player_name
hint_text: i18n._("white player name hint")
size_hint: 0.75, None
DescriptionLabel:
text: i18n._("board size")