1.11.1 (#520)
* 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:
16 files changed
+311
-201
No files matched your search
@@ -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.
|
||||
* "electricRGB" for help with adding configurable keyboard shortcuts.
|
||||
* "milescrawford" for work on restyling the territory estimate.
|
||||
* "Funkenschlag1" for capturing stones sound and implementation, and board rotation.
|
||||
|
||||
## Translators
|
||||
|
||||
|
||||
@@ -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:
|
||||
- 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
|
||||
- Prune icon: derived from work by Pixelmeetup 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.
|
||||
|
||||
|
||||
+26
-8
@@ -1,10 +1,9 @@
|
||||
"""isort:skip_file"""
|
||||
# first, logging level lower
|
||||
import os
|
||||
import sys
|
||||
|
||||
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
|
||||
|
||||
@@ -17,13 +16,19 @@ from kivy.utils import platform
|
||||
|
||||
ICON = find_package_resource("katrain/img/icon.ico")
|
||||
Config.set("kivy", "window_icon", ICON)
|
||||
|
||||
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 signal
|
||||
import json
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
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.add_widget(bot)
|
||||
self.board_controls.mid_circles_container.add_widget(top)
|
||||
|
||||
self.controls.players["W"].captures = prisoners["W"]
|
||||
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)
|
||||
): # cn mismatch stops this if undo fired. avoid message loop here or fires repeatedly.
|
||||
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.pondering:
|
||||
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):
|
||||
self.play_mode.switch_ui_mode() # for new game, go to play, for loaded, analyze
|
||||
self.board_gui.animating_pv = None
|
||||
self.board_gui.rotation_degree = 0
|
||||
self.engine.on_new_game() # clear queries
|
||||
self.game = Game(
|
||||
self,
|
||||
@@ -394,6 +401,8 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
def _do_redo(self, n_times=1):
|
||||
self.board_gui.animating_pv = None
|
||||
self.game.redo(n_times)
|
||||
def _do_rotate(self):
|
||||
self.board_gui.rotate_gridpos()
|
||||
|
||||
def _do_find_mistake(self, fn="redo"):
|
||||
self.board_gui.animating_pv = None
|
||||
@@ -403,10 +412,19 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
self.board_gui.animating_pv = None
|
||||
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):
|
||||
self.board_gui.animating_pv = None
|
||||
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))
|
||||
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:
|
||||
self.controls.set_status(f"Illegal Move: {str(e)}", STATUS_ERROR)
|
||||
|
||||
@@ -443,7 +461,7 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
self.controls.timer.paused = True
|
||||
if not self.teacher_settings_popup:
|
||||
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.teacher_settings_popup.content.popup = self.teacher_settings_popup
|
||||
self.teacher_settings_popup.open()
|
||||
@@ -513,8 +531,8 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
self.update_state(redraw_board=True)
|
||||
|
||||
def play_mistake_sound(self, node):
|
||||
if self.config("timer/sound") and node.played_sound is None and Theme.MISTAKE_SOUNDS:
|
||||
node.played_sound = True
|
||||
if self.config("timer/sound") and node.played_mistake_sound is None and Theme.MISTAKE_SOUNDS:
|
||||
node.played_mistake_sound = True
|
||||
play_sound(random.choice(Theme.MISTAKE_SOUNDS))
|
||||
|
||||
def load_sgf_file(self, file, fast=False, rewind=True):
|
||||
|
||||
+1
-1
@@ -244,7 +244,7 @@ def request_ai_analysis(game: Game, cn: GameNode, extra_settings: Dict) -> Optio
|
||||
extra_settings=extra_settings,
|
||||
)
|
||||
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)
|
||||
return analysis
|
||||
|
||||
|
||||
@@ -137,7 +137,6 @@ class KataGoContributeEngine(BaseEngine):
|
||||
def queries_remaining(self):
|
||||
return 1
|
||||
|
||||
|
||||
def start(self):
|
||||
try:
|
||||
self.katrain.log(f"Starting Distributed KataGo with {self.command}", OUTPUT_INFO)
|
||||
|
||||
@@ -62,6 +62,7 @@ class BaseGame:
|
||||
|
||||
self.insert_mode = False
|
||||
self.external_game = False # not generated by katrain at some point
|
||||
|
||||
if move_tree:
|
||||
self.root = move_tree
|
||||
self.external_game = PROGRAM_NAME not in self.root.get_property("AP", "")
|
||||
|
||||
@@ -41,7 +41,7 @@ class GameNode(SGFNode):
|
||||
def __init__(self, parent=None, properties=None, move=None):
|
||||
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.played_sound = None
|
||||
self.played_mistake_sound = None
|
||||
self.ai_thoughts = ""
|
||||
self.note = ""
|
||||
self.move_number = 0
|
||||
|
||||
@@ -373,6 +373,8 @@ class SGFNode:
|
||||
|
||||
def place_handicap_stones(self, n_handicaps, tygem=False):
|
||||
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_y = 3 if board_size_y >= 13 else min(2, board_size_y - 1)
|
||||
far_x = board_size_x - 1 - near_x
|
||||
|
||||
@@ -272,6 +272,9 @@
|
||||
icon: 'Next-Mistake.png'
|
||||
color: Theme.MISTAKE_BUTTON_COLOR
|
||||
on_press: root.katrain('find-mistake','redo')
|
||||
NavIconButton:
|
||||
icon: 'Rotate.png'
|
||||
on_press: root.katrain('rotate')
|
||||
ClickableLabel:
|
||||
pos_hint: {'center_x': 0.85,'center_y':0.5}
|
||||
size_hint: None, 1
|
||||
|
||||
+269
-78
@@ -1,7 +1,7 @@
|
||||
import math
|
||||
import random
|
||||
import time
|
||||
from typing import List, Optional
|
||||
import numpy as np
|
||||
|
||||
from kivy.clock import Clock
|
||||
from kivy.core.window import Window
|
||||
@@ -14,7 +14,6 @@ from kivy.uix.widget import Widget
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.floatlayout import MDFloatLayout
|
||||
from katrain.gui.sound import play_sound
|
||||
|
||||
from katrain.core.constants import (
|
||||
MODE_PLAY,
|
||||
@@ -40,11 +39,12 @@ from katrain.gui.theme import Theme
|
||||
class BadukPanWidget(Widget):
|
||||
def __init__(self, **kwargs):
|
||||
super(BadukPanWidget, self).__init__(**kwargs)
|
||||
self.stones_sounds = []
|
||||
self.trainer_config = {}
|
||||
self.ghost_stone = []
|
||||
self.gridpos_x = []
|
||||
self.gridpos_y = []
|
||||
self.gridpos = np.zeros((0,0,2))
|
||||
self.initial_gridpos_x = []
|
||||
self.initial_gridpos_y = []
|
||||
self.rotation_degree = 0
|
||||
self.grid_size = 0
|
||||
self.stone_size = 0
|
||||
self.selecting_region_of_interest = False
|
||||
@@ -70,18 +70,25 @@ class BadukPanWidget(Widget):
|
||||
def get_enable_coordinates(self):
|
||||
return self.draw_coords_enabled
|
||||
|
||||
def play_stone_sound(self, *_args):
|
||||
play_sound(random.choice(Theme.STONE_SOUNDS))
|
||||
|
||||
# stone placement functions
|
||||
def _find_closest(self, pos, gridpos):
|
||||
return sorted([(abs(p - pos), i) for i, p in enumerate(gridpos)])[0]
|
||||
def _find_closest(self, pos_x, pos_y):
|
||||
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):
|
||||
if not self.gridpos_x:
|
||||
if not self.initial_gridpos_x:
|
||||
return
|
||||
xd, xp = self._find_closest(touch.x, self.gridpos_x)
|
||||
yd, yp = self._find_closest(touch.y, self.gridpos_y)
|
||||
xd, xp, yd, yp = self._find_closest(touch.x, touch.y)
|
||||
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]:
|
||||
self.ghost_stone = (xp, yp)
|
||||
@@ -91,10 +98,9 @@ class BadukPanWidget(Widget):
|
||||
self.redraw_hover_contents_trigger()
|
||||
|
||||
def update_box_selection(self, touch, second_point=True):
|
||||
if not self.gridpos_x:
|
||||
if not self.initial_gridpos_x:
|
||||
return
|
||||
_, xp = self._find_closest(touch.x, self.gridpos_x)
|
||||
_, yp = self._find_closest(touch.y, self.gridpos_y)
|
||||
_, xp, _, yp = self._find_closest(touch.x, touch.y)
|
||||
if second_point and len(self.region_of_interest) == 4:
|
||||
self.region_of_interest[1] = xp
|
||||
self.region_of_interest[3] = yp
|
||||
@@ -140,10 +146,10 @@ class BadukPanWidget(Widget):
|
||||
near_move = [
|
||||
(pv, node)
|
||||
for move, pv, node in self.active_pv_moves
|
||||
if move[0] < len(self.gridpos_x)
|
||||
and move[1] < len(self.gridpos_y)
|
||||
and abs(rel_pos[0] - self.gridpos_x[move[0]]) < self.grid_size / 2
|
||||
and abs(rel_pos[1] - self.gridpos_y[move[1]]) < self.grid_size / 2
|
||||
if move[0] < len(self.gridpos[0])
|
||||
and move[1] < len(self.gridpos)
|
||||
and abs(rel_pos[0] - self.gridpos[move[1],move[0],0]) < self.grid_size / 2
|
||||
and abs(rel_pos[1] - self.gridpos[move[1],move[0],1]) < self.grid_size / 2
|
||||
]
|
||||
if near_move:
|
||||
self.set_animating_pv(near_move[0][0], near_move[0][1])
|
||||
@@ -156,7 +162,7 @@ class BadukPanWidget(Widget):
|
||||
self.last_mouse_pos = pos
|
||||
|
||||
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
|
||||
katrain = self.katrain
|
||||
if self.selecting_region_of_interest:
|
||||
@@ -182,11 +188,8 @@ class BadukPanWidget(Widget):
|
||||
)
|
||||
else:
|
||||
katrain("play", self.ghost_stone)
|
||||
self.play_stone_sound()
|
||||
elif not self.ghost_stone:
|
||||
xd, xp = self._find_closest(touch.x, self.gridpos_x)
|
||||
yd, yp = self._find_closest(touch.y, self.gridpos_y)
|
||||
|
||||
xd, xp, yd, yp = self._find_closest(touch.x, touch.y)
|
||||
nodes_here = [
|
||||
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
|
||||
Color(1, 1, 1, alpha)
|
||||
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),
|
||||
texture=cached_texture(Theme.STONE_TEXTURE[player]),
|
||||
)
|
||||
@@ -233,7 +236,7 @@ class BadukPanWidget(Widget):
|
||||
)
|
||||
Color(*evalcol)
|
||||
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),
|
||||
texture=cached_texture(Theme.EVAL_DOT_TEXTURE),
|
||||
)
|
||||
@@ -241,7 +244,7 @@ class BadukPanWidget(Widget):
|
||||
Color(*innercol)
|
||||
inner_size = stone_size * 0.8
|
||||
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),
|
||||
texture=cached_texture(Theme.LAST_MOVE_TEXTURE),
|
||||
)
|
||||
@@ -260,53 +263,171 @@ class BadukPanWidget(Widget):
|
||||
|
||||
with self.canvas.before:
|
||||
self.canvas.before.clear()
|
||||
# set up margins and grid lines
|
||||
|
||||
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)
|
||||
self.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, self.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)
|
||||
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 (self.rotation_degree == 90 or self.rotation_degree == 270) and board_size_x != board_size_y:
|
||||
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:
|
||||
current_gridpos_x = self.initial_gridpos_x[:]
|
||||
current_gridpos_y = self.initial_gridpos_y[:]
|
||||
|
||||
# if window size got changed
|
||||
if (
|
||||
(self.gridpos[0,0,0] not in current_gridpos_x or self.gridpos[0,0,1] not in current_gridpos_y
|
||||
or (self.gridpos[len(self.gridpos) - 1,len(self.gridpos[0]) - 1,0] in current_gridpos_x
|
||||
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
|
||||
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
|
||||
return math.floor(min(width / x_grid_spaces, height / y_grid_spaces) + 0.1)
|
||||
|
||||
self.gridpos_x = [
|
||||
self.pos[0] + extra_px_margin_x + math.floor((grid_spaces_margin_x[0] + i) * self.grid_size + 0.5)
|
||||
|
||||
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)
|
||||
]
|
||||
self.gridpos_y = [
|
||||
self.pos[1] + extra_px_margin_y + math.floor((grid_spaces_margin_y[0] + i) * self.grid_size + 0.5)
|
||||
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=(
|
||||
self.gridpos_x[0] - self.grid_size * grid_spaces_margin_x[0],
|
||||
self.gridpos_y[0] - self.grid_size * grid_spaces_margin_y[0],
|
||||
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(board_size_x):
|
||||
Line(points=[(self.gridpos_x[i], self.gridpos_y[0]), (self.gridpos_x[i], self.gridpos_y[-1])])
|
||||
for i in range(board_size_y):
|
||||
Line(points=[(self.gridpos_x[0], self.gridpos_y[i]), (self.gridpos_x[-1], self.gridpos_y[i])])
|
||||
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])])
|
||||
|
||||
# star points
|
||||
|
||||
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:
|
||||
@@ -318,33 +439,63 @@ class BadukPanWidget(Widget):
|
||||
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)
|
||||
draw_circle((self.gridpos[y,x,0], self.gridpos[y,x,1]), starpt_size, Theme.LINE_COLOR)
|
||||
|
||||
# coordinates
|
||||
|
||||
def draw_coordinates(self, gridpos_x, gridpos_y):
|
||||
if self.draw_coords_enabled:
|
||||
board_size_x, board_size_y = self.katrain.game.board_size
|
||||
Color(0.25, 0.25, 0.25)
|
||||
coord_offset = self.grid_size * 1.5 / 2
|
||||
coord_offset = round(self.grid_size * 1.5 / 2, 12)
|
||||
|
||||
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=(self.gridpos_x[i], self.gridpos_y[0] - coord_offset),
|
||||
text=Move.GTP_COORD[i],
|
||||
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=(self.gridpos_x[0] - coord_offset, self.gridpos_y[i]),
|
||||
text=str(i + 1),
|
||||
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",
|
||||
)
|
||||
|
||||
|
||||
def get_x_coordinate_text(self, i, board_size_x):
|
||||
x_text = Move.GTP_COORD[i]
|
||||
if (self.rotation_degree == 90):
|
||||
x_text = str(i + 1)
|
||||
elif (self.rotation_degree == 180):
|
||||
x_text = Move.GTP_COORD[board_size_x - i - 1]
|
||||
elif (self.rotation_degree == 270):
|
||||
x_text = str(board_size_x - i)
|
||||
return x_text
|
||||
|
||||
|
||||
def get_y_coordinate_text(self, i, board_size_y):
|
||||
y_text = str(i + 1)
|
||||
if (self.rotation_degree == 90):
|
||||
y_text = Move.GTP_COORD[board_size_y - i - 1]
|
||||
elif (self.rotation_degree == 180):
|
||||
|
||||
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):
|
||||
if not (self.katrain and self.katrain.game):
|
||||
return
|
||||
katrain = self.katrain
|
||||
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
|
||||
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)
|
||||
Rectangle(
|
||||
pos=(
|
||||
self.gridpos_x[x] - self.grid_size / 2,
|
||||
self.gridpos_y[y] - self.grid_size / 2,
|
||||
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),
|
||||
)
|
||||
@@ -388,7 +539,7 @@ class BadukPanWidget(Widget):
|
||||
*Theme.STONE_COLORS[ix_owner][:3], abs(ownership_grid[y][x]) * Theme.OWNERSHIP_MAX_ALPHA
|
||||
)
|
||||
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),
|
||||
)
|
||||
|
||||
@@ -469,7 +620,7 @@ class BadukPanWidget(Widget):
|
||||
pol_order = max(0, 5 + int(math.log10(max(1e-9, move_policy - 1e-9))))
|
||||
if move_policy > text_lb:
|
||||
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,
|
||||
Theme.APPROX_BOARD_COLOR,
|
||||
)
|
||||
@@ -477,14 +628,14 @@ class BadukPanWidget(Widget):
|
||||
else:
|
||||
scale = 0.5
|
||||
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,
|
||||
(*colors[pol_order][:3], Theme.POLICY_ALPHA),
|
||||
)
|
||||
if move_policy > text_lb:
|
||||
Color(*Theme.HINT_TEXT_COLOR)
|
||||
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] + "%",
|
||||
font_name="Roboto",
|
||||
font_size=self.grid_size / 4,
|
||||
@@ -494,8 +645,8 @@ class BadukPanWidget(Widget):
|
||||
Color(*Theme.TOP_MOVE_BORDER_COLOR[:3], Theme.POLICY_ALPHA)
|
||||
Line(
|
||||
circle=(
|
||||
self.gridpos_x[x],
|
||||
self.gridpos_y[y],
|
||||
self.gridpos[y,x,0],
|
||||
self.gridpos[y,x,1],
|
||||
self.stone_size - dp(1.2),
|
||||
),
|
||||
width=dp(2),
|
||||
@@ -513,13 +664,13 @@ class BadukPanWidget(Widget):
|
||||
|
||||
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
|
||||
Color(*Theme.REGION_BORDER_COLOR)
|
||||
Line(
|
||||
rectangle=(
|
||||
self.gridpos_x[xmin] - self.grid_size / 3,
|
||||
self.gridpos_y[ymin] - self.grid_size / 3,
|
||||
self.gridpos[ymin,xmin,0] - self.grid_size / 3,
|
||||
self.gridpos[ymin,xmin,1] - self.grid_size / 3,
|
||||
(xmax - xmin + 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
|
||||
|
||||
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
|
||||
|
||||
with self.canvas.after:
|
||||
@@ -603,14 +754,14 @@ class BadukPanWidget(Widget):
|
||||
evalcol = self.eval_color(move_dict["pointsLost"])
|
||||
if text_on and top_moves_show: # remove grid lines using a board colored 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,
|
||||
Theme.APPROX_BOARD_COLOR,
|
||||
)
|
||||
|
||||
Color(*evalcol[:3], alpha)
|
||||
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),
|
||||
texture=cached_texture(Theme.TOP_MOVE_TEXTURE),
|
||||
)
|
||||
@@ -640,7 +791,7 @@ class BadukPanWidget(Widget):
|
||||
|
||||
Color(*Theme.HINT_TEXT_COLOR)
|
||||
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),
|
||||
font_name="Roboto",
|
||||
markup=True,
|
||||
@@ -653,8 +804,8 @@ class BadukPanWidget(Widget):
|
||||
Color(*Theme.TOP_MOVE_BORDER_COLOR)
|
||||
Line(
|
||||
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 - dp(1.2),
|
||||
),
|
||||
width=dp(1.2),
|
||||
@@ -675,8 +826,8 @@ class BadukPanWidget(Widget):
|
||||
Color(*Theme.NEXT_MOVE_DASH_CONTRAST_COLORS[child_node.player])
|
||||
Line(
|
||||
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 - dp(1.2),
|
||||
),
|
||||
width=dp(1.2),
|
||||
@@ -687,8 +838,8 @@ class BadukPanWidget(Widget):
|
||||
for s in range(0, 360, 30):
|
||||
Line(
|
||||
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 - dp(1.2),
|
||||
s,
|
||||
s + dashed_width,
|
||||
@@ -721,7 +872,7 @@ class BadukPanWidget(Widget):
|
||||
else:
|
||||
text = i18n._("board-pass")
|
||||
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
|
||||
Ellipse(pos=(center[0] - size / 2, center[1] - size / 2), size=(size, size))
|
||||
Color(*Theme.PASS_CIRCLE_TEXT_COLOR)
|
||||
@@ -739,7 +890,7 @@ class BadukPanWidget(Widget):
|
||||
hide_node = cn
|
||||
while hide_node and hide_node.move and hide_node != node:
|
||||
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])
|
||||
hide_node = hide_node.parent
|
||||
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,
|
||||
]
|
||||
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
|
||||
|
||||
stone_size = self.stone_size * sizefac
|
||||
@@ -793,6 +944,46 @@ class BadukPanWidget(Widget):
|
||||
|
||||
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):
|
||||
self.set_animating_pv(pv_str[1:].split(" "), self.katrain.controls.active_comment_node.parent)
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ class Theme:
|
||||
GRAPH_TEXTURE = "graph_bg.png"
|
||||
# sounds
|
||||
STONE_SOUNDS = [f"stone{i}.wav" for i in [1, 2, 3, 4, 5]]
|
||||
CAPTURING_SOUND = "capturing.wav"
|
||||
COUNTDOWN_SOUND = "countdownbeep.wav"
|
||||
MINIMUM_TIME_PASSED_SOUND = "boing.wav"
|
||||
MISTAKE_SOUNDS = []
|
||||
|
||||
@@ -275,7 +275,7 @@ class LinkTree(TreeView):
|
||||
sig_new = []
|
||||
for path, name in get_drives():
|
||||
if platform == "win":
|
||||
text = u"{}({})".format((name + " ") if name else "", path)
|
||||
text = "{}({})".format((name + " ") if name else "", path)
|
||||
else:
|
||||
text = name
|
||||
nodes_new.append((text, path))
|
||||
@@ -351,7 +351,7 @@ class I18NFileBrowser(BoxLayout):
|
||||
.. versionchanged:: 1.1
|
||||
"""
|
||||
|
||||
path = StringProperty(u"/")
|
||||
path = StringProperty("/")
|
||||
"""
|
||||
:class:`~kivy.properties.StringProperty`, defaults to the current working
|
||||
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
@@ -439,10 +439,10 @@
|
||||
DescriptionLabel:
|
||||
text: i18n._("save dots")
|
||||
AnchorLayout:
|
||||
size_hint: 1, 4.25
|
||||
size_hint: 1, 5
|
||||
GridLayout:
|
||||
cols: 2
|
||||
rows: 6
|
||||
rows: 7
|
||||
padding: 4
|
||||
size_hint: 0.95, 1
|
||||
DescriptionLabel:
|
||||
|
||||
Binary file not shown.
-107
@@ -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()
|
||||
Reference in new issue
Block a user