* certificate fix

* Added a sound effect for the capturing of stones (#523)

* fix cache missing

* Fixes for some edge cases for the capturing sound effect (#526)

* Added a sound effect for the capturing of stones

* Fixed edge cases for the capturing sound effect

* minor bug fix

* Refactoring sounds for playing a move

* simplify capture sound, upgrade black

* fix handicap

* fix stone sound bug, note on sleep deadlock

* board rotation (#532)

* implementation of the rotation of the board view and some refactoring (#531)

* minor cleanup

Co-authored-by: Patrick <63347771+Funkenschlag1@users.noreply.github.com>
Co-authored-by: Sander Land <sander.land@cognite.com>

Co-authored-by: Sander Land <sander@chatdesk.com>
Co-authored-by: Patrick <63347771+Funkenschlag1@users.noreply.github.com>
Co-authored-by: Sander Land <sander.land@cognite.com>
This commit is contained in:
authored and GitHub committed 2022-06-23 19:14:56 +02:00
1 parent 2ffb79afa2
commit 485b55c42d
16 files changed
+356 -246

No files matched your search

+1
View File
@@ -55,6 +55,7 @@ Many thanks to these additional authors:
* "xiaoyifang" for adding the reset analysis option, feature to save options on the loading screen, and scrolling through variations. * "xiaoyifang" for adding the reset analysis option, feature to save options on the loading screen, and scrolling through variations.
* "electricRGB" for help with adding configurable keyboard shortcuts. * "electricRGB" for help with adding configurable keyboard shortcuts.
* "milescrawford" for work on restyling the territory estimate. * "milescrawford" for work on restyling the territory estimate.
* "Funkenschlag1" for capturing stones sound and implementation, and board rotation.
## Translators ## Translators
+2 -1
View File
@@ -5,10 +5,11 @@ For on related licenses for these binaries and libraries see https://github.com/
2. Icons from www.flaticon.com, used with permission with the following attributions: 2. Icons from www.flaticon.com, used with permission with the following attributions:
- Equalize icon and Thrash Icon: derived from work by bqlqn from www.flaticon.com - Equalize icon and Thrash Icon: derived from work by bqlqn from www.flaticon.com
- Other Menu icons, Finish, Collaboration, and Flag icons: derived from work by Freepik from www.flaticon.com - Other Menu icons, Finish, Collaboration and Flag icons: derived from work by Freepik from www.flaticon.com
- Collapse branch icon: derived from work by Kirill Kazachek from www.flaticon.com - Collapse branch icon: derived from work by Kirill Kazachek from www.flaticon.com
- Prune icon: derived from work by Pixelmeetup from www.flaticon.com - Prune icon: derived from work by Pixelmeetup from www.flaticon.com
- Reset icon: derived from work by Pixel Perfect from www.flaticon.com - Reset icon: derived from work by Pixel Perfect from www.flaticon.com
- Rotate icon: derived from work by Frey Wazza from www.flaticon.com
3. The True Type Font DIGITAL-7 version 1.02 by Sizenko Alexander, which is free for non-commercial use. 3. The True Type Font DIGITAL-7 version 1.02 by Sizenko Alexander, which is free for non-commercial use.
+26 -8
View File
@@ -1,10 +1,9 @@
"""isort:skip_file""" """isort:skip_file"""
# first, logging level lower # first, logging level lower
import os import os
import sys
os.environ["KCFG_KIVY_LOG_LEVEL"] = os.environ.get("KCFG_KIVY_LOG_LEVEL", "warning") os.environ["KCFG_KIVY_LOG_LEVEL"] = os.environ.get("KCFG_KIVY_LOG_LEVEL", "warning")
# if "KIVY_AUDIO" not in os.environ: # trying default again
# os.environ["KIVY_AUDIO"] = "sdl2" # some backends hard crash / this seems to be most stable
import kivy import kivy
@@ -17,13 +16,19 @@ from kivy.utils import platform
ICON = find_package_resource("katrain/img/icon.ico") ICON = find_package_resource("katrain/img/icon.ico")
Config.set("kivy", "window_icon", ICON) Config.set("kivy", "window_icon", ICON)
Config.set("input", "mouse", "mouse,multitouch_on_demand") Config.set("input", "mouse", "mouse,multitouch_on_demand")
# next, certificates on package builds https://github.com/sanderland/katrain/issues/414
if getattr(sys, "frozen", False):
import ssl
if ssl.get_default_verify_paths().cafile is None and hasattr(sys, "_MEIPASS"):
os.environ["SSL_CERT_FILE"] = os.path.join(sys._MEIPASS, "certifi", "cacert.pem")
import re import re
import signal import signal
import json import json
import sys
import threading import threading
import traceback import traceback
from queue import Queue from queue import Queue
@@ -198,6 +203,7 @@ class KaTrainGui(Screen, KaTrainBase):
self.board_controls.mid_circles_container.clear_widgets() self.board_controls.mid_circles_container.clear_widgets()
self.board_controls.mid_circles_container.add_widget(bot) self.board_controls.mid_circles_container.add_widget(bot)
self.board_controls.mid_circles_container.add_widget(top) self.board_controls.mid_circles_container.add_widget(top)
self.controls.players["W"].captures = prisoners["W"] self.controls.players["W"].captures = prisoners["W"]
self.controls.players["B"].captures = prisoners["B"] self.controls.players["B"].captures = prisoners["B"]
@@ -257,7 +263,7 @@ class KaTrainGui(Screen, KaTrainBase):
and not (teaching_undo and cn.auto_undo is None) and not (teaching_undo and cn.auto_undo is None)
): # cn mismatch stops this if undo fired. avoid message loop here or fires repeatedly. ): # cn mismatch stops this if undo fired. avoid message loop here or fires repeatedly.
self._do_ai_move(cn) self._do_ai_move(cn)
Clock.schedule_once(self.board_gui.play_stone_sound, 0.25) Clock.schedule_once(self._play_stone_sound, 0.25)
if self.engine: if self.engine:
if self.pondering: if self.pondering:
self.game.analyze_extra("ponder") self.game.analyze_extra("ponder")
@@ -331,6 +337,7 @@ class KaTrainGui(Screen, KaTrainBase):
if (move_tree is not None and mode == MODE_PLAY) or (move_tree is None and mode == MODE_ANALYZE): if (move_tree is not None and mode == MODE_PLAY) or (move_tree is None and mode == MODE_ANALYZE):
self.play_mode.switch_ui_mode() # for new game, go to play, for loaded, analyze self.play_mode.switch_ui_mode() # for new game, go to play, for loaded, analyze
self.board_gui.animating_pv = None self.board_gui.animating_pv = None
self.board_gui.rotation_degree = 0
self.engine.on_new_game() # clear queries self.engine.on_new_game() # clear queries
self.game = Game( self.game = Game(
self, self,
@@ -394,6 +401,8 @@ class KaTrainGui(Screen, KaTrainBase):
def _do_redo(self, n_times=1): def _do_redo(self, n_times=1):
self.board_gui.animating_pv = None self.board_gui.animating_pv = None
self.game.redo(n_times) self.game.redo(n_times)
def _do_rotate(self):
self.board_gui.rotate_gridpos()
def _do_find_mistake(self, fn="redo"): def _do_find_mistake(self, fn="redo"):
self.board_gui.animating_pv = None self.board_gui.animating_pv = None
@@ -403,10 +412,19 @@ class KaTrainGui(Screen, KaTrainBase):
self.board_gui.animating_pv = None self.board_gui.animating_pv = None
self.controls.move_tree.switch_branch(*args) self.controls.move_tree.switch_branch(*args)
def _play_stone_sound(self,_dt=None):
play_sound(random.choice(Theme.STONE_SOUNDS))
def _do_play(self, coords): def _do_play(self, coords):
self.board_gui.animating_pv = None self.board_gui.animating_pv = None
try: try:
old_prisoner_count = self.game.prisoner_count["W"] + self.game.prisoner_count["B"]
self.game.play(Move(coords, player=self.next_player_info.player)) self.game.play(Move(coords, player=self.next_player_info.player))
if old_prisoner_count < self.game.prisoner_count["W"] + self.game.prisoner_count["B"]:
play_sound(Theme.CAPTURING_SOUND)
elif not self.game.current_node.is_pass:
self._play_stone_sound()
except IllegalMoveException as e: except IllegalMoveException as e:
self.controls.set_status(f"Illegal Move: {str(e)}", STATUS_ERROR) self.controls.set_status(f"Illegal Move: {str(e)}", STATUS_ERROR)
@@ -443,7 +461,7 @@ class KaTrainGui(Screen, KaTrainBase):
self.controls.timer.paused = True self.controls.timer.paused = True
if not self.teacher_settings_popup: if not self.teacher_settings_popup:
self.teacher_settings_popup = I18NPopup( self.teacher_settings_popup = I18NPopup(
title_key="teacher settings", size=[dp(800), dp(800)], content=ConfigTeacherPopup(self) title_key="teacher settings", size=[dp(800), dp(825)], content=ConfigTeacherPopup(self)
).__self__ ).__self__
self.teacher_settings_popup.content.popup = self.teacher_settings_popup self.teacher_settings_popup.content.popup = self.teacher_settings_popup
self.teacher_settings_popup.open() self.teacher_settings_popup.open()
@@ -513,8 +531,8 @@ class KaTrainGui(Screen, KaTrainBase):
self.update_state(redraw_board=True) self.update_state(redraw_board=True)
def play_mistake_sound(self, node): def play_mistake_sound(self, node):
if self.config("timer/sound") and node.played_sound is None and Theme.MISTAKE_SOUNDS: if self.config("timer/sound") and node.played_mistake_sound is None and Theme.MISTAKE_SOUNDS:
node.played_sound = True node.played_mistake_sound = True
play_sound(random.choice(Theme.MISTAKE_SOUNDS)) play_sound(random.choice(Theme.MISTAKE_SOUNDS))
def load_sgf_file(self, file, fast=False, rewind=True): def load_sgf_file(self, file, fast=False, rewind=True):
+1 -1
View File
@@ -244,7 +244,7 @@ def request_ai_analysis(game: Game, cn: GameNode, extra_settings: Dict) -> Optio
extra_settings=extra_settings, extra_settings=extra_settings,
) )
while not (error or analysis): while not (error or analysis):
time.sleep(0.01) time.sleep(0.01) # TODO: prevent deadlock if esc, check node in queries?
engine.check_alive(exception_if_dead=True) engine.check_alive(exception_if_dead=True)
return analysis return analysis
-1
View File
@@ -137,7 +137,6 @@ class KataGoContributeEngine(BaseEngine):
def queries_remaining(self): def queries_remaining(self):
return 1 return 1
def start(self): def start(self):
try: try:
self.katrain.log(f"Starting Distributed KataGo with {self.command}", OUTPUT_INFO) self.katrain.log(f"Starting Distributed KataGo with {self.command}", OUTPUT_INFO)
+1
View File
@@ -62,6 +62,7 @@ class BaseGame:
self.insert_mode = False self.insert_mode = False
self.external_game = False # not generated by katrain at some point self.external_game = False # not generated by katrain at some point
if move_tree: if move_tree:
self.root = move_tree self.root = move_tree
self.external_game = PROGRAM_NAME not in self.root.get_property("AP", "") self.external_game = PROGRAM_NAME not in self.root.get_property("AP", "")
+1 -1
View File
@@ -41,7 +41,7 @@ class GameNode(SGFNode):
def __init__(self, parent=None, properties=None, move=None): def __init__(self, parent=None, properties=None, move=None):
super().__init__(parent=parent, properties=properties, move=move) super().__init__(parent=parent, properties=properties, move=move)
self.auto_undo = None # None = not analyzed. False: not undone (good move). True: undone (bad move) self.auto_undo = None # None = not analyzed. False: not undone (good move). True: undone (bad move)
self.played_sound = None self.played_mistake_sound = None
self.ai_thoughts = "" self.ai_thoughts = ""
self.note = "" self.note = ""
self.move_number = 0 self.move_number = 0
+2
View File
@@ -373,6 +373,8 @@ class SGFNode:
def place_handicap_stones(self, n_handicaps, tygem=False): def place_handicap_stones(self, n_handicaps, tygem=False):
board_size_x, board_size_y = self.board_size board_size_x, board_size_y = self.board_size
if min(board_size_x, board_size_y) < 3:
return # No
near_x = 3 if board_size_x >= 13 else min(2, board_size_x - 1) near_x = 3 if board_size_x >= 13 else min(2, board_size_x - 1)
near_y = 3 if board_size_y >= 13 else min(2, board_size_y - 1) near_y = 3 if board_size_y >= 13 else min(2, board_size_y - 1)
far_x = board_size_x - 1 - near_x far_x = board_size_x - 1 - near_x
+3
View File
@@ -272,6 +272,9 @@
icon: 'Next-Mistake.png' icon: 'Next-Mistake.png'
color: Theme.MISTAKE_BUTTON_COLOR color: Theme.MISTAKE_BUTTON_COLOR
on_press: root.katrain('find-mistake','redo') on_press: root.katrain('find-mistake','redo')
NavIconButton:
icon: 'Rotate.png'
on_press: root.katrain('rotate')
ClickableLabel: ClickableLabel:
pos_hint: {'center_x': 0.85,'center_y':0.5} pos_hint: {'center_x': 0.85,'center_y':0.5}
size_hint: None, 1 size_hint: None, 1
+314 -123
View File
@@ -1,7 +1,7 @@
import math import math
import random
import time import time
from typing import List, Optional from typing import List, Optional
import numpy as np
from kivy.clock import Clock from kivy.clock import Clock
from kivy.core.window import Window from kivy.core.window import Window
@@ -14,7 +14,6 @@ from kivy.uix.widget import Widget
from kivymd.app import MDApp from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.floatlayout import MDFloatLayout from kivymd.uix.floatlayout import MDFloatLayout
from katrain.gui.sound import play_sound
from katrain.core.constants import ( from katrain.core.constants import (
MODE_PLAY, MODE_PLAY,
@@ -40,11 +39,12 @@ from katrain.gui.theme import Theme
class BadukPanWidget(Widget): class BadukPanWidget(Widget):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(BadukPanWidget, self).__init__(**kwargs) super(BadukPanWidget, self).__init__(**kwargs)
self.stones_sounds = []
self.trainer_config = {} self.trainer_config = {}
self.ghost_stone = [] self.ghost_stone = []
self.gridpos_x = [] self.gridpos = np.zeros((0,0,2))
self.gridpos_y = [] self.initial_gridpos_x = []
self.initial_gridpos_y = []
self.rotation_degree = 0
self.grid_size = 0 self.grid_size = 0
self.stone_size = 0 self.stone_size = 0
self.selecting_region_of_interest = False self.selecting_region_of_interest = False
@@ -70,18 +70,25 @@ class BadukPanWidget(Widget):
def get_enable_coordinates(self): def get_enable_coordinates(self):
return self.draw_coords_enabled return self.draw_coords_enabled
def play_stone_sound(self, *_args):
play_sound(random.choice(Theme.STONE_SOUNDS))
# stone placement functions # stone placement functions
def _find_closest(self, pos, gridpos): def _find_closest(self, pos_x, pos_y):
return sorted([(abs(p - pos), i) for i, p in enumerate(gridpos)])[0] xd = abs(self.gridpos[0,0,0] - pos_x)
xp = 0
yd = abs(self.gridpos[0,0,1] - pos_y)
yp = 0
for y in range(0, len(self.gridpos)):
for x in range(0, len(self.gridpos[0])):
if abs(self.gridpos[y,x,0] - pos_x) <= xd and abs(self.gridpos[y,x,1] - pos_y) <= yd:
xd = abs(self.gridpos[y,x,0] - pos_x)
xp = x
yd = abs(self.gridpos[y,x,1] - pos_y)
yp = y
return xd, xp, yd, yp
def check_next_move_ghost(self, touch): def check_next_move_ghost(self, touch):
if not self.gridpos_x: if not self.initial_gridpos_x:
return return
xd, xp = self._find_closest(touch.x, self.gridpos_x) xd, xp, yd, yp = self._find_closest(touch.x, touch.y)
yd, yp = self._find_closest(touch.y, self.gridpos_y)
prev_ghost = self.ghost_stone prev_ghost = self.ghost_stone
if max(yd, xd) < self.grid_size / 2 and (xp, yp) not in [m.coords for m in self.katrain.game.stones]: if max(yd, xd) < self.grid_size / 2 and (xp, yp) not in [m.coords for m in self.katrain.game.stones]:
self.ghost_stone = (xp, yp) self.ghost_stone = (xp, yp)
@@ -91,10 +98,9 @@ class BadukPanWidget(Widget):
self.redraw_hover_contents_trigger() self.redraw_hover_contents_trigger()
def update_box_selection(self, touch, second_point=True): def update_box_selection(self, touch, second_point=True):
if not self.gridpos_x: if not self.initial_gridpos_x:
return return
_, xp = self._find_closest(touch.x, self.gridpos_x) _, xp, _, yp = self._find_closest(touch.x, touch.y)
_, yp = self._find_closest(touch.y, self.gridpos_y)
if second_point and len(self.region_of_interest) == 4: if second_point and len(self.region_of_interest) == 4:
self.region_of_interest[1] = xp self.region_of_interest[1] = xp
self.region_of_interest[3] = yp self.region_of_interest[3] = yp
@@ -140,10 +146,10 @@ class BadukPanWidget(Widget):
near_move = [ near_move = [
(pv, node) (pv, node)
for move, pv, node in self.active_pv_moves for move, pv, node in self.active_pv_moves
if move[0] < len(self.gridpos_x) if move[0] < len(self.gridpos[0])
and move[1] < len(self.gridpos_y) and move[1] < len(self.gridpos)
and abs(rel_pos[0] - self.gridpos_x[move[0]]) < self.grid_size / 2 and abs(rel_pos[0] - self.gridpos[move[1],move[0],0]) < self.grid_size / 2
and abs(rel_pos[1] - self.gridpos_y[move[1]]) < self.grid_size / 2 and abs(rel_pos[1] - self.gridpos[move[1],move[0],1]) < self.grid_size / 2
] ]
if near_move: if near_move:
self.set_animating_pv(near_move[0][0], near_move[0][1]) self.set_animating_pv(near_move[0][0], near_move[0][1])
@@ -151,12 +157,12 @@ class BadukPanWidget(Widget):
self.set_animating_pv(None, None) # any click kills PV from label/move self.set_animating_pv(None, None) # any click kills PV from label/move
if inside and self.animating_pv is not None: if inside and self.animating_pv is not None:
d_sq = (pos[0] - self.animating_pv[3][0]) ** 2 + (pos[1] - self.animating_pv[3][1]) d_sq = (pos[0] - self.animating_pv[3][0]) ** 2 + (pos[1] - self.animating_pv[3][1])
if d_sq > 2 * self.stone_size ** 2: # move too far from where it was activated if d_sq > 2 * self.stone_size**2: # move too far from where it was activated
self.set_animating_pv(None, None) # any click kills PV from label/move self.set_animating_pv(None, None) # any click kills PV from label/move
self.last_mouse_pos = pos self.last_mouse_pos = pos
def on_touch_up(self, touch): def on_touch_up(self, touch):
if ("button" in touch.profile and touch.button != "left") or not self.gridpos_x: if ("button" in touch.profile and touch.button != "left") or not self.initial_gridpos_x:
return return
katrain = self.katrain katrain = self.katrain
if self.selecting_region_of_interest: if self.selecting_region_of_interest:
@@ -182,11 +188,8 @@ class BadukPanWidget(Widget):
) )
else: else:
katrain("play", self.ghost_stone) katrain("play", self.ghost_stone)
self.play_stone_sound()
elif not self.ghost_stone: elif not self.ghost_stone:
xd, xp = self._find_closest(touch.x, self.gridpos_x) xd, xp, yd, yp = self._find_closest(touch.x, touch.y)
yd, yp = self._find_closest(touch.y, self.gridpos_y)
nodes_here = [ nodes_here = [
node for node in katrain.game.current_node.nodes_from_root if node.move and node.move.coords == (xp, yp) node for node in katrain.game.current_node.nodes_from_root if node.move and node.move.coords == (xp, yp)
] ]
@@ -222,7 +225,7 @@ class BadukPanWidget(Widget):
stone_size = self.stone_size * scale stone_size = self.stone_size * scale
Color(1, 1, 1, alpha) Color(1, 1, 1, alpha)
Rectangle( Rectangle(
pos=(self.gridpos_x[x] - stone_size, self.gridpos_y[y] - stone_size), pos=(self.gridpos[y,x,0] - stone_size, self.gridpos[y,x,1] - stone_size),
size=(2 * stone_size, 2 * stone_size), size=(2 * stone_size, 2 * stone_size),
texture=cached_texture(Theme.STONE_TEXTURE[player]), texture=cached_texture(Theme.STONE_TEXTURE[player]),
) )
@@ -233,7 +236,7 @@ class BadukPanWidget(Widget):
) )
Color(*evalcol) Color(*evalcol)
Rectangle( Rectangle(
pos=(self.gridpos_x[x] - evalsize, self.gridpos_y[y] - evalsize), pos=(self.gridpos[y,x,0] - evalsize, self.gridpos[y,x,1] - evalsize),
size=(2 * evalsize, 2 * evalsize), size=(2 * evalsize, 2 * evalsize),
texture=cached_texture(Theme.EVAL_DOT_TEXTURE), texture=cached_texture(Theme.EVAL_DOT_TEXTURE),
) )
@@ -241,7 +244,7 @@ class BadukPanWidget(Widget):
Color(*innercol) Color(*innercol)
inner_size = stone_size * 0.8 inner_size = stone_size * 0.8
Rectangle( Rectangle(
pos=(self.gridpos_x[x] - inner_size, self.gridpos_y[y] - inner_size), pos=(self.gridpos[y,x,0] - inner_size, self.gridpos[y,x,1] - inner_size),
size=(2 * inner_size, 2 * inner_size), size=(2 * inner_size, 2 * inner_size),
texture=cached_texture(Theme.LAST_MOVE_TEXTURE), texture=cached_texture(Theme.LAST_MOVE_TEXTURE),
) )
@@ -260,91 +263,239 @@ class BadukPanWidget(Widget):
with self.canvas.before: with self.canvas.before:
self.canvas.before.clear() self.canvas.before.clear()
# set up margins and grid lines
if self.draw_coords_enabled:
grid_spaces_margin_x = [1.5, 0.75] # left, right
grid_spaces_margin_y = [1.5, 0.75] # bottom, top
else: # no coordinates means remove the offset
grid_spaces_margin_x = [0.75, 0.75] # left, right
grid_spaces_margin_y = [0.75, 0.75] # bottom, top
x_grid_spaces = board_size_x - 1 + sum(grid_spaces_margin_x)
y_grid_spaces = board_size_y - 1 + sum(grid_spaces_margin_y)
# grid size is rounded to an integer to avoid rounding errors that
# produce tiny gaps between shaded grid squares
self.grid_size = math.floor(min(self.width / x_grid_spaces, self.height / y_grid_spaces) + 0.1)
board_width_with_margins = x_grid_spaces * self.grid_size
board_height_with_margins = y_grid_spaces * self.grid_size
extra_px_margin_x = (self.width - board_width_with_margins) / 2
extra_px_margin_y = (self.height - board_height_with_margins) / 2
self.stone_size = self.grid_size * Theme.STONE_SIZE
self.gridpos_x = [ grid_spaces_margin_x, grid_spaces_margin_y = self.get_grid_spaces_margins()
self.pos[0] + extra_px_margin_x + math.floor((grid_spaces_margin_x[0] + i) * self.grid_size + 0.5) h = round(self.height, 4)
for i in range(board_size_x) w = round(self.width, 4)
] x_grid_spaces, y_grid_spaces = self.calculate_grid_spaces(board_size_x, board_size_y, grid_spaces_margin_x, grid_spaces_margin_y)
self.gridpos_y = [ self.grid_size = self.calculate_grid_size(w, h, x_grid_spaces, y_grid_spaces)
self.pos[1] + extra_px_margin_y + math.floor((grid_spaces_margin_y[0] + i) * self.grid_size + 0.5) board_width_with_margins, board_height_with_margins = self.calculate_board_margins(x_grid_spaces, y_grid_spaces, self.grid_size)
for i in range(board_size_y) extra_px_margin_x, extra_px_margin_y = self.calculate_extra_px_margins(w, h, board_width_with_margins, board_height_with_margins)
] self.stone_size = self.calculate_stone_size(self.grid_size)
# if not initiated or if changed
if (
(len(self.gridpos) == 0 or abs(self.pos[0] + extra_px_margin_x
+ math.floor(grid_spaces_margin_x[0] * self.grid_size + 0.5) - self.initial_gridpos_x[0]) > 0.001)
):
self.initial_gridpos_x, self.initial_gridpos_y = self.initialize_gridpos_x_y(board_size_x, board_size_y, grid_spaces_margin_x, grid_spaces_margin_y, extra_px_margin_x, extra_px_margin_y, self.grid_size)
if self.rotation_degree == 0:
self.initialize_gridpos()
if katrain.game.insert_mode: if (self.rotation_degree == 90 or self.rotation_degree == 270) and board_size_x != board_size_y:
Color(*Theme.INSERT_BOARD_COLOR_TINT) # dreamy board_size_y, board_size_x = katrain.game.board_size
x_grid_spaces, y_grid_spaces = self.calculate_grid_spaces(board_size_x, board_size_y, grid_spaces_margin_x, grid_spaces_margin_y)
self.grid_size = self.calculate_grid_size(w, h, x_grid_spaces, y_grid_spaces)
self.stone_size = self.calculate_stone_size(self.grid_size)
current_gridpos_x, current_gridpos_y = self.calculate_rotated_gridpos()
else: else:
Color(*Theme.BOARD_COLOR_TINT) # image is a bit too light current_gridpos_x = self.initial_gridpos_x[:]
Rectangle( current_gridpos_y = self.initial_gridpos_y[:]
pos=(
self.gridpos_x[0] - self.grid_size * grid_spaces_margin_x[0], # if window size got changed
self.gridpos_y[0] - self.grid_size * grid_spaces_margin_y[0], if (
), (self.gridpos[0,0,0] not in current_gridpos_x or self.gridpos[0,0,1] not in current_gridpos_y
size=(self.grid_size * x_grid_spaces, self.grid_size * y_grid_spaces), or (self.gridpos[len(self.gridpos) - 1,len(self.gridpos[0]) - 1,0] in current_gridpos_x
texture=cached_texture(Theme.BOARD_TEXTURE), or self.gridpos[len(self.gridpos) - 1,len(self.gridpos[0]) - 1,1] in current_gridpos_y))
):
self.resize_board()
self.draw_board_background(katrain, current_gridpos_x, current_gridpos_y, x_grid_spaces, y_grid_spaces, grid_spaces_margin_x, grid_spaces_margin_y)
self.draw_lines(current_gridpos_x, current_gridpos_y)
self.draw_star_points(board_size_x, board_size_y)
self.draw_coordinates(current_gridpos_x, current_gridpos_y)
def get_grid_spaces_margins(self):
if self.draw_coords_enabled:
grid_spaces_margin_x = [1.5, 0.75] # left, right
grid_spaces_margin_y = [1.5, 0.75] # bottom, top
else: # no coordinates means remove the offset
grid_spaces_margin_x = [0.75, 0.75] # left, right
grid_spaces_margin_y = [0.75, 0.75] # bottom, top
return grid_spaces_margin_x, grid_spaces_margin_y
def calculate_grid_spaces(self, board_size_x, board_size_y, grid_spaces_margin_x, grid_spaces_margin_y):
x_grid_spaces = board_size_x - 1 + sum(grid_spaces_margin_x)
y_grid_spaces = board_size_y - 1 + sum(grid_spaces_margin_y)
return x_grid_spaces, y_grid_spaces
def calculate_grid_size(self, width, height, x_grid_spaces, y_grid_spaces):
# grid size is rounded to an integer to avoid rounding errors that
# produce tiny gaps between shaded grid squares
return math.floor(min(width / x_grid_spaces, height / y_grid_spaces) + 0.1)
def calculate_board_margins(self, x_grid_spaces, y_grid_spaces, grid_size):
board_width_with_margins = x_grid_spaces * grid_size
board_height_with_margins = y_grid_spaces * grid_size
return board_width_with_margins, board_height_with_margins
def calculate_extra_px_margins(self, width, height, board_width_with_margins, board_height_with_margins):
extra_px_margin_x = round((width - board_width_with_margins) / 2, 4)
extra_px_margin_y = round((height - board_height_with_margins) / 2, 4)
return extra_px_margin_x, extra_px_margin_y
def calculate_stone_size(self, grid_size):
return grid_size * Theme.STONE_SIZE
def initialize_gridpos(self):
self.gridpos = np.zeros((len(self.initial_gridpos_y), len(self.initial_gridpos_x),2))
for y in range(len(self.initial_gridpos_y)):
for x in range(len(self.initial_gridpos_x)):
self.gridpos[y][x] = [self.initial_gridpos_x[x], self.initial_gridpos_y[y]]
def initialize_gridpos_x_y(self, board_size_x, board_size_y, grid_spaces_margin_x, grid_spaces_margin_y, extra_px_margin_x, extra_px_margin_y, grid_size):
gridpos_x = [
round(self.pos[0] + extra_px_margin_x + math.floor((grid_spaces_margin_x[0] + i) * grid_size + 0.5), 4)
for i in range(board_size_x)
]
gridpos_y = [
round(self.pos[1] + extra_px_margin_y + math.floor((grid_spaces_margin_y[0] + i) * grid_size + 0.5), 4)
for i in range(board_size_y)
]
return gridpos_x, gridpos_y
def calculate_rotated_gridpos(self):
board_size_y, board_size_x = self.katrain.game.board_size
grid_spaces_margin_x, grid_spaces_margin_y = self.get_grid_spaces_margins()
h = round(self.height, 4)
w = round(self.width, 4)
x_grid_spaces, y_grid_spaces = self.calculate_grid_spaces(board_size_x, board_size_y, grid_spaces_margin_x, grid_spaces_margin_y)
grid_size = self.calculate_grid_size(w, h , x_grid_spaces, y_grid_spaces)
board_width_with_margins, board_height_with_margins = self.calculate_board_margins(x_grid_spaces, y_grid_spaces, grid_size)
extra_px_margin_x, extra_px_margin_y = self.calculate_extra_px_margins(w, h, board_width_with_margins, board_height_with_margins)
return self.initialize_gridpos_x_y(board_size_x, board_size_y, grid_spaces_margin_x, grid_spaces_margin_y, extra_px_margin_x, extra_px_margin_y, grid_size)
def resize_board(self):
rotated_gridpos_x, rotated_gridpos_y = self.calculate_rotated_gridpos()
current_gridpos_x = []
current_gridpos_y = []
for yi in range(len(self.gridpos)):
for xi in range(len(self.gridpos[0])):
current_gridpos_x.append(self.gridpos[yi,xi,0])
current_gridpos_y.append(self.gridpos[yi,xi,1])
sorted_current_gridpos_x = list(set(current_gridpos_x))
sorted_current_gridpos_x.sort()
sorted_current_gridpos_y = list(set(current_gridpos_y))
sorted_current_gridpos_y.sort()
for yi in range(len(self.gridpos)):
for xi in range(len(self.gridpos[0])):
index_x = sorted_current_gridpos_x.index(self.gridpos[yi,xi,0])
index_y = sorted_current_gridpos_y.index(self.gridpos[yi,xi,1])
if self.rotation_degree == 90 or self.rotation_degree == 270:
self.gridpos[yi][xi] = [rotated_gridpos_x[index_x], rotated_gridpos_y[index_y]]
else:
self.gridpos[yi][xi] = [self.initial_gridpos_x[index_x], self.initial_gridpos_y[index_y]]
def draw_board_background(self, katrain, gridpos_x, gridpos_y, x_grid_spaces, y_grid_spaces, grid_spaces_margin_x, grid_spaces_margin_y):
if katrain.game.insert_mode:
Color(*Theme.INSERT_BOARD_COLOR_TINT) # dreamy
else:
Color(*Theme.BOARD_COLOR_TINT) # image is a bit too light
Rectangle(
pos=(
gridpos_x[0] - self.grid_size * grid_spaces_margin_x[0],
gridpos_y[0] - self.grid_size * grid_spaces_margin_y[0],
),
size=(self.grid_size * x_grid_spaces, self.grid_size * y_grid_spaces),
texture=cached_texture(Theme.BOARD_TEXTURE),
)
def draw_lines(self, gridpos_x, gridpos_y):
Color(*Theme.LINE_COLOR)
for i in range(len(gridpos_x)):
Line(points=[(gridpos_x[i], gridpos_y[0]), (gridpos_x[i], gridpos_y[-1])])
for i in range(len(gridpos_y)):
Line(points=[(gridpos_x[0], gridpos_y[i]), (gridpos_x[-1], gridpos_y[i])])
def draw_star_points(self, board_size_x, board_size_y):
def star_point_coords(size):
star_point_pos = 3 if size <= 11 else 4
if size < 7:
return []
return [star_point_pos - 1, size - star_point_pos] + (
[int(size / 2)] if size % 2 == 1 and size > 7 else []
) )
Color(*Theme.LINE_COLOR) starpt_size = self.grid_size * Theme.STARPOINT_SIZE
for i in range(board_size_x): for x in star_point_coords(board_size_x):
Line(points=[(self.gridpos_x[i], self.gridpos_y[0]), (self.gridpos_x[i], self.gridpos_y[-1])]) for y in star_point_coords(board_size_y):
for i in range(board_size_y): draw_circle((self.gridpos[y,x,0], self.gridpos[y,x,1]), starpt_size, Theme.LINE_COLOR)
Line(points=[(self.gridpos_x[0], self.gridpos_y[i]), (self.gridpos_x[-1], self.gridpos_y[i])])
# star points
def star_point_coords(size): def draw_coordinates(self, gridpos_x, gridpos_y):
star_point_pos = 3 if size <= 11 else 4 if self.draw_coords_enabled:
if size < 7: board_size_x, board_size_y = self.katrain.game.board_size
return [] Color(0.25, 0.25, 0.25)
return [star_point_pos - 1, size - star_point_pos] + ( coord_offset = round(self.grid_size * 1.5 / 2, 12)
[int(size / 2)] if size % 2 == 1 and size > 7 else []
if (self.rotation_degree == 90 or self.rotation_degree == 270) and board_size_x != board_size_y:
board_size_y, board_size_x = self.katrain.game.board_size
for i in range(board_size_x):
draw_text(
pos=(gridpos_x[i], gridpos_y[0] - coord_offset),
text=self.get_x_coordinate_text(i, board_size_x),
font_size=self.grid_size / 1.5,
font_name="Roboto",
)
for i in range(board_size_y):
draw_text(
pos=(gridpos_x[0] - coord_offset, gridpos_y[i]),
text=self.get_y_coordinate_text(i, board_size_y),
font_size=self.grid_size / 1.5,
font_name="Roboto",
) )
starpt_size = self.grid_size * Theme.STARPOINT_SIZE
for x in star_point_coords(board_size_x):
for y in star_point_coords(board_size_y):
draw_circle((self.gridpos_x[x], self.gridpos_y[y]), starpt_size, Theme.LINE_COLOR)
# coordinates def get_x_coordinate_text(self, i, board_size_x):
if self.draw_coords_enabled: x_text = Move.GTP_COORD[i]
Color(0.25, 0.25, 0.25) if (self.rotation_degree == 90):
coord_offset = self.grid_size * 1.5 / 2 x_text = str(i + 1)
for i in range(board_size_x): elif (self.rotation_degree == 180):
draw_text( x_text = Move.GTP_COORD[board_size_x - i - 1]
pos=(self.gridpos_x[i], self.gridpos_y[0] - coord_offset), elif (self.rotation_degree == 270):
text=Move.GTP_COORD[i], x_text = str(board_size_x - i)
font_size=self.grid_size / 1.5, return x_text
font_name="Roboto",
)
for i in range(board_size_y): def get_y_coordinate_text(self, i, board_size_y):
draw_text( y_text = str(i + 1)
pos=(self.gridpos_x[0] - coord_offset, self.gridpos_y[i]), if (self.rotation_degree == 90):
text=str(i + 1), y_text = Move.GTP_COORD[board_size_y - i - 1]
font_size=self.grid_size / 1.5, elif (self.rotation_degree == 180):
font_name="Roboto",
) y_text = str(board_size_y - i)
elif (self.rotation_degree == 270):
y_text = Move.GTP_COORD[i]
return y_text
def draw_board_contents(self, *_args): def draw_board_contents(self, *_args):
if not (self.katrain and self.katrain.game): if not (self.katrain and self.katrain.game):
return return
katrain = self.katrain katrain = self.katrain
board_size_x, board_size_y = katrain.game.board_size 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: if self.gridpos.shape[0] < board_size_x or self.gridpos.shape[1] < board_size_y:
return # race condition return # race condition
show_n_eval = self.trainer_config.get("eval_on_show_last", 3) show_n_eval = self.trainer_config.get("eval_on_show_last", 3)
@@ -374,8 +525,8 @@ class BadukPanWidget(Widget):
Color(*Theme.EVAL_COLORS[self.trainer_config["theme"]][1][:3], loss) Color(*Theme.EVAL_COLORS[self.trainer_config["theme"]][1][:3], loss)
Rectangle( Rectangle(
pos=( pos=(
self.gridpos_x[x] - self.grid_size / 2, self.gridpos[y,x,0] - self.grid_size / 2,
self.gridpos_y[y] - self.grid_size / 2, self.gridpos[y,x,1] - self.grid_size / 2,
), ),
size=(self.grid_size, self.grid_size), size=(self.grid_size, self.grid_size),
) )
@@ -388,7 +539,7 @@ class BadukPanWidget(Widget):
*Theme.STONE_COLORS[ix_owner][:3], abs(ownership_grid[y][x]) * Theme.OWNERSHIP_MAX_ALPHA *Theme.STONE_COLORS[ix_owner][:3], abs(ownership_grid[y][x]) * Theme.OWNERSHIP_MAX_ALPHA
) )
Rectangle( Rectangle(
pos=(self.gridpos_x[x] - self.grid_size / 2, self.gridpos_y[y] - self.grid_size / 2), pos=(self.gridpos[y,x,0] - self.grid_size / 2, self.gridpos[y,x,1] - self.grid_size / 2),
size=(self.grid_size, self.grid_size), size=(self.grid_size, self.grid_size),
) )
@@ -469,7 +620,7 @@ class BadukPanWidget(Widget):
pol_order = max(0, 5 + int(math.log10(max(1e-9, move_policy - 1e-9)))) pol_order = max(0, 5 + int(math.log10(max(1e-9, move_policy - 1e-9))))
if move_policy > text_lb: if move_policy > text_lb:
draw_circle( draw_circle(
(self.gridpos_x[x], self.gridpos_y[y]), (self.gridpos[y,x,0], self.gridpos[y,x,1]),
self.stone_size * Theme.HINT_SCALE * 0.98, self.stone_size * Theme.HINT_SCALE * 0.98,
Theme.APPROX_BOARD_COLOR, Theme.APPROX_BOARD_COLOR,
) )
@@ -477,14 +628,14 @@ class BadukPanWidget(Widget):
else: else:
scale = 0.5 scale = 0.5
draw_circle( draw_circle(
(self.gridpos_x[x], self.gridpos_y[y]), (self.gridpos[y,x,0], self.gridpos[y,x,1]),
Theme.HINT_SCALE * self.stone_size * scale, Theme.HINT_SCALE * self.stone_size * scale,
(*colors[pol_order][:3], Theme.POLICY_ALPHA), (*colors[pol_order][:3], Theme.POLICY_ALPHA),
) )
if move_policy > text_lb: if move_policy > text_lb:
Color(*Theme.HINT_TEXT_COLOR) Color(*Theme.HINT_TEXT_COLOR)
draw_text( draw_text(
pos=(self.gridpos_x[x], self.gridpos_y[y]), pos=(self.gridpos[y,x,0], self.gridpos[y,x,1]),
text=f"{100 * move_policy :.2f}"[:4] + "%", text=f"{100 * move_policy :.2f}"[:4] + "%",
font_name="Roboto", font_name="Roboto",
font_size=self.grid_size / 4, font_size=self.grid_size / 4,
@@ -494,8 +645,8 @@ class BadukPanWidget(Widget):
Color(*Theme.TOP_MOVE_BORDER_COLOR[:3], Theme.POLICY_ALPHA) Color(*Theme.TOP_MOVE_BORDER_COLOR[:3], Theme.POLICY_ALPHA)
Line( Line(
circle=( circle=(
self.gridpos_x[x], self.gridpos[y,x,0],
self.gridpos_y[y], self.gridpos[y,x,1],
self.stone_size - dp(1.2), self.stone_size - dp(1.2),
), ),
width=dp(2), width=dp(2),
@@ -513,13 +664,13 @@ class BadukPanWidget(Widget):
self.redraw_hover_contents_trigger() self.redraw_hover_contents_trigger()
def draw_roi_box(self, region_of_interest, width=2): def draw_roi_box(self, region_of_interest, width:float=2):
xmin, xmax, ymin, ymax = region_of_interest xmin, xmax, ymin, ymax = region_of_interest
Color(*Theme.REGION_BORDER_COLOR) Color(*Theme.REGION_BORDER_COLOR)
Line( Line(
rectangle=( rectangle=(
self.gridpos_x[xmin] - self.grid_size / 3, self.gridpos[ymin,xmin,0] - self.grid_size / 3,
self.gridpos_y[ymin] - self.grid_size / 3, self.gridpos[ymin,xmin,1] - self.grid_size / 3,
(xmax - xmin + 2 / 3) * self.grid_size, (xmax - xmin + 2 / 3) * self.grid_size,
(ymax - ymin + 2 / 3) * self.grid_size, (ymax - ymin + 2 / 3) * self.grid_size,
), ),
@@ -544,7 +695,7 @@ class BadukPanWidget(Widget):
next_player = current_node.next_player next_player = current_node.next_player
board_size_x, board_size_y = katrain.game.board_size 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: if len(self.gridpos[0]) < board_size_x or len(self.gridpos) < board_size_y:
return # race condition return # race condition
with self.canvas.after: with self.canvas.after:
@@ -603,14 +754,14 @@ class BadukPanWidget(Widget):
evalcol = self.eval_color(move_dict["pointsLost"]) evalcol = self.eval_color(move_dict["pointsLost"])
if text_on and top_moves_show: # remove grid lines using a board colored circle if text_on and top_moves_show: # remove grid lines using a board colored circle
draw_circle( draw_circle(
(self.gridpos_x[move.coords[0]], self.gridpos_y[move.coords[1]]), (self.gridpos[move.coords[1],move.coords[0],0], self.gridpos[move.coords[1],move.coords[0],1]),
self.stone_size * scale * 0.98, self.stone_size * scale * 0.98,
Theme.APPROX_BOARD_COLOR, Theme.APPROX_BOARD_COLOR,
) )
Color(*evalcol[:3], alpha) Color(*evalcol[:3], alpha)
Rectangle( Rectangle(
pos=(self.gridpos_x[move.coords[0]] - evalsize, self.gridpos_y[move.coords[1]] - evalsize), pos=(self.gridpos[move.coords[1],move.coords[0],0] - evalsize, self.gridpos[move.coords[1],move.coords[0],1] - evalsize),
size=(2 * evalsize, 2 * evalsize), size=(2 * evalsize, 2 * evalsize),
texture=cached_texture(Theme.TOP_MOVE_TEXTURE), texture=cached_texture(Theme.TOP_MOVE_TEXTURE),
) )
@@ -640,7 +791,7 @@ class BadukPanWidget(Widget):
Color(*Theme.HINT_TEXT_COLOR) Color(*Theme.HINT_TEXT_COLOR)
draw_text( draw_text(
pos=(self.gridpos_x[move.coords[0]], self.gridpos_y[move.coords[1]]), pos=(self.gridpos[move.coords[1],move.coords[0],0], self.gridpos[move.coords[1],move.coords[0],1]),
text=fmt.format(**keys), text=fmt.format(**keys),
font_name="Roboto", font_name="Roboto",
markup=True, markup=True,
@@ -653,8 +804,8 @@ class BadukPanWidget(Widget):
Color(*Theme.TOP_MOVE_BORDER_COLOR) Color(*Theme.TOP_MOVE_BORDER_COLOR)
Line( Line(
circle=( circle=(
self.gridpos_x[move.coords[0]], self.gridpos[move.coords[1],move.coords[0],0],
self.gridpos_y[move.coords[1]], self.gridpos[move.coords[1],move.coords[0],1],
self.stone_size - dp(1.2), self.stone_size - dp(1.2),
), ),
width=dp(1.2), width=dp(1.2),
@@ -675,8 +826,8 @@ class BadukPanWidget(Widget):
Color(*Theme.NEXT_MOVE_DASH_CONTRAST_COLORS[child_node.player]) Color(*Theme.NEXT_MOVE_DASH_CONTRAST_COLORS[child_node.player])
Line( Line(
circle=( circle=(
self.gridpos_x[move.coords[0]], self.gridpos[move.coords[1],move.coords[0],0],
self.gridpos_y[move.coords[1]], self.gridpos[move.coords[1],move.coords[0],1],
self.stone_size - dp(1.2), self.stone_size - dp(1.2),
), ),
width=dp(1.2), width=dp(1.2),
@@ -687,8 +838,8 @@ class BadukPanWidget(Widget):
for s in range(0, 360, 30): for s in range(0, 360, 30):
Line( Line(
circle=( circle=(
self.gridpos_x[move.coords[0]], self.gridpos[move.coords[1],move.coords[0],0],
self.gridpos_y[move.coords[1]], self.gridpos[move.coords[1],move.coords[0],1],
self.stone_size - dp(1.2), self.stone_size - dp(1.2),
s, s,
s + dashed_width, s + dashed_width,
@@ -721,7 +872,7 @@ class BadukPanWidget(Widget):
else: else:
text = i18n._("board-pass") text = i18n._("board-pass")
Color(*Theme.PASS_CIRCLE_COLOR) Color(*Theme.PASS_CIRCLE_COLOR)
center = (self.gridpos_x[int(board_size_x / 2)], self.gridpos_y[int(board_size_y / 2)]) center = (self.initial_gridpos_x[int(board_size_x / 2)], self.initial_gridpos_y[int(board_size_y / 2)])
size = min(self.width, self.height) * 0.227 size = min(self.width, self.height) * 0.227
Ellipse(pos=(center[0] - size / 2, center[1] - size / 2), size=(size, size)) Ellipse(pos=(center[0] - size / 2, center[1] - size / 2), size=(size, size))
Color(*Theme.PASS_CIRCLE_TEXT_COLOR) Color(*Theme.PASS_CIRCLE_TEXT_COLOR)
@@ -739,7 +890,7 @@ class BadukPanWidget(Widget):
hide_node = cn hide_node = cn
while hide_node and hide_node.move and hide_node != node: while hide_node and hide_node.move and hide_node != node:
if not hide_node.move.is_pass: if not hide_node.move.is_pass:
pos = (self.gridpos_x[hide_node.move.coords[0]], self.gridpos_y[hide_node.move.coords[1]]) pos = (self.gridpos[hide_node.move.coords[1],hide_node.move.coords[0],0], self.gridpos[hide_node.move.coords[1],hide_node.move.coords[0],1])
draw_circle(pos, self.stone_size, [0.85, 0.68, 0.40, 0.8]) draw_circle(pos, self.stone_size, [0.85, 0.68, 0.40, 0.8])
hide_node = hide_node.parent hide_node = hide_node.parent
for i, gtpmove in enumerate(pv): for i, gtpmove in enumerate(pv):
@@ -756,7 +907,7 @@ class BadukPanWidget(Widget):
katrain.board_controls.pass_btn.pos[1] + katrain.board_controls.pass_btn.size[1] / 2, katrain.board_controls.pass_btn.pos[1] + katrain.board_controls.pass_btn.size[1] / 2,
] ]
else: else:
board_coords = (self.gridpos_x[coords[0]], self.gridpos_y[coords[1]]) board_coords = (self.gridpos[coords[1],coords[0],0], self.gridpos[coords[1],coords[0],1])
sizefac = 1 sizefac = 1
stone_size = self.stone_size * sizefac stone_size = self.stone_size * sizefac
@@ -793,6 +944,46 @@ class BadukPanWidget(Widget):
return self.animating_pv_index return self.animating_pv_index
def rotate_gridpos(self):
board_size_x, board_size_y = self.katrain.game.board_size
if board_size_x != board_size_y:
if self.rotation_degree == 90 or self.rotation_degree == 270:
rotated_gridpos_x, rotated_gridpos_y = self.calculate_rotated_gridpos()
diff = round(abs(rotated_gridpos_x[0] - rotated_gridpos_y[0]), 4)
x0 = rotated_gridpos_x[0]
y0 = rotated_gridpos_y[0]
else:
diff = round(abs(self.initial_gridpos_x[0] - self.initial_gridpos_y[0]), 4)
x0 = self.initial_gridpos_x[0]
y0 = self.initial_gridpos_y[0]
pos = self.gridpos.tolist()
for yi in range(len(self.gridpos)):
for xi in range(len(self.gridpos[0])):
if (self.rotation_degree == 90 or self.rotation_degree == 270):
gridpos_x = pos[len(self.gridpos) - 1 - yi][:]
else:
gridpos_x = pos[yi][:]
gridpos_x.reverse()
x = pos[yi][xi][1]
y = gridpos_x[xi][0]
if x0 > y0:
x = round(x + diff, 4)
y = round(y - diff, 4)
elif y0 > x0:
x = round(x - diff, 4)
y = round(y + diff, 4)
self.gridpos[yi][xi] = (x, y)
else:
self.gridpos = np.rot90(self.gridpos, -1, (0, 1))
self.rotation_degree += 90
if self.rotation_degree == 360:
self.rotation_degree = 0
if board_size_x != board_size_y:
self.resize_board()
Clock.schedule_once(self.redraw)
def show_pv_from_comments(self, pv_str): def show_pv_from_comments(self, pv_str):
self.set_animating_pv(pv_str[1:].split(" "), self.katrain.controls.active_comment_node.parent) self.set_animating_pv(pv_str[1:].split(" "), self.katrain.controls.active_comment_node.parent)
+1
View File
@@ -68,6 +68,7 @@ class Theme:
GRAPH_TEXTURE = "graph_bg.png" GRAPH_TEXTURE = "graph_bg.png"
# sounds # sounds
STONE_SOUNDS = [f"stone{i}.wav" for i in [1, 2, 3, 4, 5]] STONE_SOUNDS = [f"stone{i}.wav" for i in [1, 2, 3, 4, 5]]
CAPTURING_SOUND = "capturing.wav"
COUNTDOWN_SOUND = "countdownbeep.wav" COUNTDOWN_SOUND = "countdownbeep.wav"
MINIMUM_TIME_PASSED_SOUND = "boing.wav" MINIMUM_TIME_PASSED_SOUND = "boing.wav"
MISTAKE_SOUNDS = [] MISTAKE_SOUNDS = []
+2 -2
View File
@@ -275,7 +275,7 @@ class LinkTree(TreeView):
sig_new = [] sig_new = []
for path, name in get_drives(): for path, name in get_drives():
if platform == "win": if platform == "win":
text = u"{}({})".format((name + " ") if name else "", path) text = "{}({})".format((name + " ") if name else "", path)
else: else:
text = name text = name
nodes_new.append((text, path)) nodes_new.append((text, path))
@@ -351,7 +351,7 @@ class I18NFileBrowser(BoxLayout):
.. versionchanged:: 1.1 .. versionchanged:: 1.1
""" """
path = StringProperty(u"/") path = StringProperty("/")
""" """
:class:`~kivy.properties.StringProperty`, defaults to the current working :class:`~kivy.properties.StringProperty`, defaults to the current working
directory as a unicode string. It specifies the path on the filesystem that directory as a unicode string. It specifies the path on the filesystem that
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

+2 -2
View File
@@ -439,10 +439,10 @@
DescriptionLabel: DescriptionLabel:
text: i18n._("save dots") text: i18n._("save dots")
AnchorLayout: AnchorLayout:
size_hint: 1, 4.25 size_hint: 1, 5
GridLayout: GridLayout:
cols: 2 cols: 2
rows: 6 rows: 7
padding: 4 padding: 4
size_hint: 0.95, 1 size_hint: 0.95, 1
DescriptionLabel: DescriptionLabel:
Binary file not shown.
-107
View File
@@ -1,107 +0,0 @@
import os
import time
import pandas as pd
import numpy as np
from unidecode import unidecode
from katrain.core.ai import game_report
from katrain.core.base_katrain import KaTrainBase
from katrain.core.engine import KataGoEngine
from katrain.core.game import KaTrainSGF, Game
import matplotlib.pyplot as plt
pd.set_option("display.max_rows", 5000)
settings = {
"fast_visits": 25,
"max_visits": 500,
"threads": 64,
"model": "C:\\Users\\sande\\.katrain\\kata1-b40c256-s7907049728-d1917596640.bin.gz",
}
# settings["model"] = "C:\\Users\\sande\\.katrain\\g170e-b20c256x2-s5303129600-d1228401921.bin.gz"
def dan(rank):
rank = rank.lower()
if rank[-1] in ["d", "p"]:
return int(rank[:-1])
elif rank == "?":
return np.nan
else:
assert rank[-1] == "k", f"unexpected rank {rank}"
return 1 - int(rank[:-1])
def polyfit(x, y, degree=1):
coeffs = np.polyfit(x, y, degree)
correlation = np.corrcoef(x, y)[0, 1]
results = {"coef": coeffs.tolist(), "r": correlation, "rsq": correlation ** 2}
return results
katrain = KaTrainBase(force_package_config=True, debug_level=0)
combined_settings = {**katrain.config("engine"), **settings}
engine = KataGoEngine(katrain, {**katrain.config("engine"), **settings})
thresholds = katrain.config("trainer/eval_thresholds")
games = []
n = 0
for sgf in os.listdir("sgftest/"):
if sgf.lower().endswith("sgf"):
print(sgf)
with open(os.path.join("sgftest", sgf)) as f:
move_tree = KaTrainSGF.parse_sgf(f.read())
games.append(Game(katrain, engine, move_tree=move_tree, analyze_fast=False))
n += 1
if n >= 30000: # small test=3
break
while not engine.is_idle():
print(f"waiting for engine to finish...{engine.queries_remaining()} queries left")
time.sleep(0.5)
engine.shutdown(finish=None)
reports = []
for game in games:
sum_stats, _, _ = game_report(game, thresholds=thresholds)
for bw in "BW":
oppbw = "B" if bw == "W" else "W"
info = {
f"name": game.root.get_property(f"P{bw}", "??"),
"rank": game.root.get_property(f"{bw}R", "9p"),
"opp_rank": game.root.get_property(f"{oppbw}R", "9p"),
**sum_stats[bw],
}
reports.append(info)
df = pd.DataFrame(reports).sort_values(by="accuracy", ascending=False).reset_index(drop=True)
df.name = [unidecode(n) for n in df.name]
print(df)
df["numrank"] = [dan(rank) for rank in df["rank"]]
def subplot(sp, ynames):
global df
plt.subplot(2, 2, sp)
legend = []
xfull = np.array(range(df["numrank"].min(), df["numrank"].max() + 1))
cols = "bgr"
for i, yname in enumerate(ynames):
plt.plot(df["numrank"], df[yname], cols[i] + "x")
for i, yname in enumerate(ynames):
fit = polyfit(df["numrank"], df[yname])
a, b = fit["coef"]
plt.plot(xfull, xfull * a + b, cols[i] + ":")
legend.append(f"{yname}: r^2 = {fit['rsq']:.3f}")
plt.xlabel("dan rank")
plt.legend(legend)
plt.subplots(2, 2)
subplot(1, ["accuracy"])
subplot(2, ["complexity"])
subplot(3, ["ai_top_move", "ai_top5_move"])
subplot(4, ["mean_ptloss", "weighted_ptloss"])
plt.show()