Merge pull request #142 from sanderland/1.3.3

1.3.3
This commit is contained in:
sanderland authored and GitHub committed 2020-08-02 12:36:37 +02:00
commit b47a7156bc
40 files changed
+425 -103

No files matched your search

+5 -2
View File
@@ -1,7 +1,8 @@
# Contributing
If you are a new contributor, please first discuss the change you wish to make via
an issue, reddit or discord before making a change.
If you are a new contributor wanting to make a larger contribution,
please first discuss the change you wish to make via
an issue, reddit or discord before making a pull request.
## Python contributions
@@ -49,6 +50,8 @@ Many thanks to these additional authors:
* "nimets123" for the timer sound effects and board/stone graphics.
* Jordan Seaward for the stone sound effects.
* "fohristiwhirl" for the Gibo and NGF formats parsing code.
* "kaorahi" for bug fixes and SGF parser improvements.
* "ajkenny84" for the red-green colourblind theme.
## Translators
+1 -3
View File
@@ -7,18 +7,16 @@ For on related licenses for these binaries and libraries see https://github.com/
- Equalize icon and Thrash Icon: derived from work by bqlqn from www.flaticon.com
- Other Menu icons and Flag icons: derived from work by Freepik from www.flaticon.com
3. The True Type Font DIGITAL-7 version 1.02 by Sizenko Alexander, which is free for non-commercial use.
4. The Noto Sans fonts from google which are covered by the SIL open font license v1.1 included in the katrain/fonts directory.
-----------------------------------------------------------------------------------------
Aside from the above, the license for all other content in this repository is as follows:
-----------------------------------------------------------------------------------------
Copyright 2020 Sander Land and/or other authors of the content in this repository.
(See 'CONTRIBUTORS' file for a list of authors as well as other indirect contributors).
(See 'CONTRIBUTIONS.md' file for a list of authors as well as other indirect contributors).
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
+2 -2
View File
@@ -4,7 +4,7 @@
[![License:MIT](https://img.shields.io/pypi/l/katrain)](https://en.wikipedia.org/wiki/MIT_License)
[![GitHub Downloads](https://img.shields.io/github/downloads/sanderland/katrain/total?color=%23336699&label=github%20downloads)](https://github.com/sanderland/katrain/releases)
[![PyPI Downloads](https://pepy.tech/badge/katrain)](https://pepy.tech/project/katrain)
[![Github sponsors](https://img.shields.io/static/v1?label=sponsor&message=4&logo=GitHub&color=dcb424&link=https://github.com/sponsors/sanderland/)](https://github.com/sponsors/sanderland)
[![Github sponsors](https://img.shields.io/static/v1?label=sponsor&message=6&logo=GitHub&color=dcb424&link=https://github.com/sponsors/sanderland/)](https://github.com/sponsors/sanderland)
[![Discord](https://img.shields.io/discord/417022162348802048?logo=discord)](https://discord.com/channels/417022162348802048/629446365688365067)
<table>
@@ -191,7 +191,7 @@ In addition to shortcuts mentioned above and those shown in the main menu:
[![GitHub issues](https://img.shields.io/github/issues/sanderland/katrain)](https://github.com/sanderland/katrain/issues)
[![Contributors](https://img.shields.io/static/v1?label=contributors&message=17&color=dcb424)](CONTRIBUTIONS.md)
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/sanderbaduk)](https://liberapay.com/sanderbaduk/)
[![Github sponsors](https://img.shields.io/static/v1?label=sponsor&message=4&logo=GitHub&color=dcb424&link=https://github.com/sponsors/sanderland/)](https://github.com/sponsors/sanderland)
[![Github sponsors](https://img.shields.io/static/v1?label=sponsor&message=6&logo=GitHub&color=dcb424&link=https://github.com/sponsors/sanderland/)](https://github.com/sponsors/sanderland)
* Ideas, feedback, and contributions to code or translations are all very welcome.
* For suggestions and planned improvements, see [open issues](https://github.com/sanderland/katrain/issues) on github to check if the functionality is already planned.
+7 -7
View File
@@ -4,7 +4,7 @@ import os
os.environ["KCFG_KIVY_LOG_LEVEL"] = os.environ.get("KCFG_KIVY_LOG_LEVEL", "warning")
if "KIVY_AUDIO" not in os.environ:
os.environ["KIVY_AUDIO"] = "sdl2" # seems to be most stable / some players hard crash
os.environ["KIVY_AUDIO"] = "sdl2" # some backends hard crash / this seems to be most stable
# next, icon
from katrain.core.utils import find_package_resource, PATHS
@@ -365,15 +365,15 @@ class KaTrainGui(Screen, KaTrainBase):
).__self__
def readfile(*_args):
files = popup_contents.filesel.selection
filename = popup_contents.filesel.filename
self.fileselect_popup.dismiss()
path, file = os.path.split(files[0])
path, file = os.path.split(filename)
settings_path = self.config("general/sgf_load")
if path != settings_path:
self.log(f"Updating sgf load path default to {path}", OUTPUT_INFO)
self._config["general"]["sgf_load"] = path
self.save_config("general")
self.load_sgf_file(files[0], popup_contents.fast.active, popup_contents.rewind.active)
self.load_sgf_file(filename, popup_contents.fast.active, popup_contents.rewind.active)
popup_contents.filesel.on_success = readfile
popup_contents.filesel.on_submit = readfile
@@ -382,18 +382,18 @@ class KaTrainGui(Screen, KaTrainBase):
def _do_output_sgf(self):
msg = self.game.write_sgf(self.config("general/sgf_save"))
self.log(msg, OUTPUT_INFO)
self.controls.set_status(msg, OUTPUT_INFO)
self.controls.set_status(msg, STATUS_INFO)
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_ERROR)
self.controls.set_status(f"Ctrl-V pressed but clipboard is empty.", STATUS_INFO)
return
try:
move_tree = KaTrainSGF.parse_sgf(clipboard)
except Exception as exc:
self.controls.set_status(
i18n._("Failed to import from clipboard").format(error=exc, contents=clipboard[:50])
i18n._("Failed to import from clipboard").format(error=exc, contents=clipboard[:50]), STATUS_INFO
)
return
move_tree.nodes_in_tree[-1].analyze(
+2 -1
View File
@@ -16,7 +16,7 @@
"anim_pv_time": 0.5,
"debug_level": 0,
"lang": "en",
"version": "1.3.0"
"version": "1.3.3"
},
"timer": {
"byo_length": 30,
@@ -31,6 +31,7 @@
"clear_cache": false
},
"trainer": {
"theme": "theme:normal",
"num_undo_prompts": [
1,
1,
+3 -3
View File
@@ -1,6 +1,6 @@
VERSION = "1.3.2"
VERSION = "1.3.3"
HOMEPAGE = "https://github.com/sanderland/katrain"
CONFIG_MIN_VERSION = "1.3.0" # keep config files from this version
CONFIG_MIN_VERSION = "1.3.3" # keep config files from this version
OUTPUT_ERROR = -1
OUTPUT_KATAGO_STDERR = -0.5
@@ -34,7 +34,7 @@ AI_INFLUENCE = "ai:p:influence"
AI_TERRITORY = "ai:p:territory"
AI_RANK = "ai:p:rank"
AI_CONFIG_DEFAULT = AI_SCORELOSS
AI_CONFIG_DEFAULT = AI_RANK
AI_STRATEGIES_ENGINE = [AI_DEFAULT, AI_HANDICAP, AI_SCORELOSS, AI_JIGO]
AI_STRATEGIES_PICK = [AI_PICK, AI_LOCAL, AI_TENUKI, AI_INFLUENCE, AI_TERRITORY, AI_RANK]
+10 -8
View File
@@ -5,15 +5,15 @@ import subprocess
import threading
import time
import traceback
from typing import Callable, Optional, Dict
from typing import Callable, Dict, Optional
from kivy.utils import platform
from katrain.core.constants import OUTPUT_DEBUG, OUTPUT_ERROR, OUTPUT_EXTRA_DEBUG, OUTPUT_KATAGO_STDERR
from katrain.core.game_node import GameNode
from katrain.core.lang import i18n
from katrain.core.utils import find_package_resource
from kivy.utils import platform
class EngineDiedException(Exception):
pass
@@ -164,10 +164,13 @@ class KataGoEngine:
continue
try:
analysis = json.loads(line)
if analysis["id"] not in self.queries:
self.katrain.log(f"Query result {analysis['id']} discarded -- recent new game?", OUTPUT_DEBUG)
if "id" not in analysis:
self.katrain.log(f"Error without ID {analysis} received from KataGo", OUTPUT_ERROR)
continue
query_id = analysis["id"]
if query_id not in self.queries:
self.katrain.log(f"Query result {query_id} discarded -- recent new game?", OUTPUT_DEBUG)
continue
callback, error_callback, start_time, next_move = self.queries[query_id]
if "error" in analysis:
del self.queries[query_id]
@@ -181,8 +184,7 @@ class KataGoEngine:
del self.queries[query_id]
time_taken = time.time() - start_time
self.katrain.log(
f"[{time_taken:.1f}][{analysis['id']}] KataGo Analysis Received: {analysis.keys()}",
OUTPUT_DEBUG,
f"[{time_taken:.1f}][{query_id}] KataGo Analysis Received: {analysis.keys()}", OUTPUT_DEBUG,
)
self.katrain.log(line, OUTPUT_EXTRA_DEBUG)
try:
@@ -192,8 +194,8 @@ class KataGoEngine:
if getattr(self.katrain, "update_state", None): # easier mocking etc
self.katrain.update_state()
except Exception as e:
traceback.print_exc(e)
self.katrain.log(f"Unexpected exception {e} while processing KataGo output {line}", OUTPUT_ERROR)
traceback.print_exc()
def send_query(self, query, callback, error_callback, next_move=None):
with self._lock:
+2 -2
View File
@@ -11,10 +11,10 @@ from katrain.core.constants import (
HOMEPAGE,
OUTPUT_DEBUG,
OUTPUT_INFO,
PLAYER_AI,
STATUS_ANALYSIS,
STATUS_INFO,
STATUS_TEACHING,
PLAYER_AI,
)
from katrain.core.engine import KataGoEngine
from katrain.core.game_node import GameNode
@@ -55,7 +55,7 @@ class Game:
self.root = move_tree
self.komi = self.root.komi
handicap = int(self.root.get_property("HA", 0))
if handicap and not self.root.placements:
if handicap >= 2 and not self.root.placements:
self.root.place_handicap_stones(handicap)
else:
board_size = katrain.config("game/size")
+6 -5
View File
@@ -32,12 +32,11 @@ class GameNode(SGFNode):
else:
show_class = False
if (
(save_comments_player or {}).get(self.player, False)
and show_class
and self.analysis_ready
and self.parent
self.parent
and self.parent.analysis_ready
) or note:
and self.analysis_ready
and (note or ((save_comments_player or {}).get(self.player, False) and show_class))
):
candidate_moves = self.parent.candidate_moves
top_x = Move.from_gtp(candidate_moves[0]["move"]).sgf(self.board_size)
best_sq = [
@@ -194,6 +193,8 @@ class GameNode(SGFNode):
text += "\n" + i18n._("Info:AI thoughts").format(thoughts=self.ai_thoughts)
else:
text = i18n._("No analysis available") if sgf else i18n._("Analyzing move...")
if "C" in self.properties:
text += "\n[u]SGF Comments:[/u]\n" + "\n".join(self.properties["C"])
return text
@property
+1 -5
View File
@@ -11,11 +11,7 @@ from katrain.gui.style import DEFAULT_FONT
class Lang(Observable):
observers = []
callbacks = []
FONTS = {
"ko": "fonts/NotoSansKR-Regular.otf",
"cn": "fonts/NotoSansCJKsc-Regular.otf",
"jp": "fonts/NotoSansJP-Regular.otf",
}
FONTS = {"jp": "fonts/NotoSansJP-Regular.otf"}
def __init__(self, lang):
super(Lang, self).__init__()
+11 -3
View File
@@ -132,7 +132,9 @@ class SGFNode:
def add_list_property(self, property: str, values: List):
"""Add some values to the property list."""
self.properties[property] += values
# SiZe[19] ==> SZ[19] etc. for old SGF
normalized_property = re.sub("[a-z]", "", property)
self.properties[normalized_property] += values
def get_list_property(self, property, default=None) -> Any:
"""Get the list of values for a property."""
@@ -323,11 +325,14 @@ class SGF:
_NODE_CLASS = SGFNode # Class used for SGF Nodes, can change this to something that inherits from SGFNode
# https://xkcd.com/1171/
SGFPROP_PAT = re.compile(r"\s*(?:\(|\)|;|(\w+)((\s*\[([^\]\\]|\\.)*\])+))", flags=re.DOTALL)
SGF_PAT = re.compile(r"\(;.*\)", flags=re.DOTALL)
@classmethod
def parse_sgf(cls, input_str) -> SGFNode:
"""Parse a string as SGF."""
return cls(input_str).root
match = re.search(cls.SGF_PAT, input_str)
clipped_str = match.group() if match else input_str
return cls(clipped_str).root
@classmethod
def parse_file(cls, filename, encoding=None) -> SGFNode:
@@ -375,7 +380,10 @@ class SGF:
if matched_item == "(":
self._parse_branch(self._NODE_CLASS(parent=current_move))
elif matched_item == ";":
if not current_move.empty: # ignore ; that generate empty nodes
# ignore ;) for old SGF
useless = self.ix < len(self.contents) and self.contents[self.ix] == ")"
# ignore ; that generate empty nodes
if not (current_move.empty or useless):
current_move = self._NODE_CLASS(parent=current_move)
else:
property, value = match[1], match[2].strip()[1:-1]
Binary file not shown.
Binary file not shown.
+4 -1
View File
@@ -146,6 +146,9 @@
background_color: [*[c*255/88 for c in BACKGROUND_COLOR[:3]], 1] # compensate for texture
option_cls: 'I18NSpinnerOption'
<Button>:
track_lang: i18n._('')
<I18NSpinner>
sync_height_frac: 1.0
@@ -626,7 +629,7 @@
padding: 0
id: notebox
size_hint: 1,0
MDTextField:
IMETextField:
id: note
font_size: NOTES_FONT_SIZE
multiline: True
+2 -1
View File
@@ -164,8 +164,9 @@ class BadukPanWidget(Widget):
def eval_color(self, points_lost, show_dots_for_class: List[bool] = None) -> Optional[List[float]]:
i = evaluation_class(points_lost, self.trainer_config["eval_thresholds"])
colors = EVAL_COLORS[self.trainer_config["theme"]]
if show_dots_for_class is None or show_dots_for_class[i]:
return EVAL_COLORS[i]
return colors[i]
def draw_board(self, *_args):
if not (self.katrain and self.katrain.game):
+43 -1
View File
@@ -1,5 +1,6 @@
from kivy.clock import Clock
from kivy.core.text import Label as CoreLabel
from kivy.core.window import Window
from kivy.graphics import *
from kivy.properties import (
BooleanProperty,
@@ -9,18 +10,20 @@ from kivy.properties import (
OptionProperty,
StringProperty,
)
from kivy.uix.behaviors import ButtonBehavior, ToggleButtonBehavior
from kivy.uix.behaviors import ButtonBehavior, ToggleButtonBehavior, FocusBehavior
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
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import BaseFlatButton, BasePressedButton
from kivymd.uix.navigationdrawer import MDNavigationDrawer
from kivymd.uix.textfield import MDTextField
from katrain.core.constants import AI_STRATEGIES_RECOMMENDED_ORDER, GAME_TYPES, MODE_PLAY, PLAYER_AI
from katrain.core.lang import i18n
@@ -163,6 +166,45 @@ class BGBoxLayout(BoxLayout, BackgroundMixin):
# -- gui elements
class IMETextField(MDTextField):
_imo_composition = StringProperty("")
_imo_cursor = ListProperty(None, allownone=True)
def _bind_keyboard(self):
super()._bind_keyboard()
Window.bind(on_textedit=self.window_on_textedit)
def _unbind_keyboard(self):
super()._unbind_keyboard()
Window.unbind(on_textedit=self.window_on_textedit)
def do_backspace(self, from_undo=False, mode="bkspc"):
if self._imo_composition == "": # IMO handles sub-character backspaces
return super().do_backspace(from_undo, mode)
def window_on_textedit(self, window, imo_input):
text_lines = self._lines
if self._imo_composition:
pcc, pcr = self._imo_cursor
text = text_lines[pcr]
if text[pcc - len(self._imo_composition) : pcc] == self._imo_composition: # should always be true
remove_old_imo_text = text[: pcc - len(self._imo_composition)] + text[pcc:]
ci = self.cursor_index()
self._refresh_text_from_property("insert", *self._get_line_from_cursor(pcr, remove_old_imo_text))
self.cursor = self.get_cursor_from_index(ci - len(self._imo_composition))
if imo_input:
if self._selection:
self.delete_selection()
cc, cr = self.cursor
text = text_lines[cr]
new_text = text[:cc] + imo_input + text[cc:]
self._refresh_text_from_property("insert", *self._get_line_from_cursor(cr, new_text))
self.cursor = self.get_cursor_from_index(self.cursor_index() + len(imo_input))
self._imo_composition = imo_input
self._imo_cursor = self.cursor
class I18NSpinner(Spinner):
__events__ = ["on_select"]
sync_height_frac = NumericProperty(1.0)
+11 -21
View File
@@ -19,13 +19,13 @@ from katrain.core.ai import ai_rank_estimation
from katrain.core.constants import (
AI_CONFIG_DEFAULT,
AI_DEFAULT,
AI_KEY_PROPERTIES,
AI_OPTION_VALUES,
AI_STRATEGIES_RECOMMENDED_ORDER,
OUTPUT_DEBUG,
OUTPUT_ERROR,
OUTPUT_INFO,
STATUS_INFO,
AI_KEY_PROPERTIES,
)
from katrain.core.engine import KataGoEngine
from katrain.core.lang import i18n, rank_label
@@ -104,21 +104,7 @@ class LabelledSpinner(I18NSpinner):
class LabelledFloatInput(LabelledTextInput):
signed = BooleanProperty(True)
pat = re.compile("[^0-9-]")
def insert_text(self, substring, from_undo=False):
pat = self.pat
if "." in self.text:
s = re.sub(pat, "", substring)
else:
s = ".".join([re.sub(pat, "", s) for s in substring.split(".", 1)])
r = super().insert_text(s, from_undo=from_undo)
if not self.signed and "-" in self.text:
self.text = self.text.replace("-", "")
elif self.text and "-" in self.text[1:]:
self.text = self.text[0] + self.text[1:].replace("-", "")
return r
input_filter = ObjectProperty("float")
@property
def input_value(self):
@@ -126,10 +112,7 @@ class LabelledFloatInput(LabelledTextInput):
class LabelledIntInput(LabelledTextInput):
pat = re.compile("[^0-9]")
def insert_text(self, substring, from_undo=False):
return super().insert_text(re.sub(self.pat, "", substring), from_undo=from_undo)
input_filter = ObjectProperty("int")
@property
def input_value(self):
@@ -286,13 +269,16 @@ class ConfigTeacherPopup(QuickConfigGui):
self.options_grid.add_widget(wrap_anchor(widget))
def build_and_set_properties(self, *_args):
theme = self.katrain.config("trainer/theme")
undos = self.katrain.config("trainer/num_undo_prompts")
thresholds = self.katrain.config("trainer/eval_thresholds")
savesgfs = self.katrain.config("trainer/save_feedback")
show_dots = self.katrain.config("trainer/show_dots")
self.themes_spinner.value_refs = list(EVAL_COLORS.keys())
self.options_grid.clear_widgets()
for i, (color, threshold, undo, show_dot, savesgf) in enumerate(
zip(EVAL_COLORS, thresholds, undos, show_dots, savesgfs)
zip(EVAL_COLORS[theme], thresholds, undos, show_dots, savesgfs)
):
self.add_option_widgets(
[
@@ -305,6 +291,10 @@ class ConfigTeacherPopup(QuickConfigGui):
)
super().build_and_set_properties()
def update_config(self, save_to_file=True):
super().update_config(save_to_file=save_to_file)
self.build_and_set_properties()
class DescriptionLabel(Label):
pass
+24 -12
View File
@@ -2,7 +2,7 @@ def to_hexcol(kivycol):
return "#" + "".join(f"{round(c * 255):02x}" for c in kivycol[:3])
DEFAULT_FONT = "fonts/NotoSans-Regular.ttf"
DEFAULT_FONT = "fonts/NotoSansCJKsc-Regular.otf"
# basic color definitions
WHITE = [0.95, 0.95, 0.95, 1]
@@ -26,14 +26,26 @@ WINRATE_COLOR = GREEN
POINTLOSS_COLOR = YELLOW
# eval dots
EVAL_COLORS = [
[0.447, 0.129, 0.42, 1],
[0.8, 0, 0, 1],
[0.9, 0.4, 0.1, 1],
[0.85, 0.89, 0.3, 1],
[0.67, 0.9, 0.18, 1],
[0.117, 0.588, 0, 1],
]
EVAL_COLORS = {
"theme:normal": [
[0.447, 0.129, 0.42, 1],
[0.8, 0, 0, 1],
[0.9, 0.4, 0.1, 1],
[0.85, 0.89, 0.3, 1],
[0.67, 0.9, 0.18, 1],
[0.117, 0.588, 0, 1],
],
"theme:red-green-colourblind": [
[1, 0, 1, 1],
[1, 0, 0, 1],
[1, 0.5, 0, 1],
[0.85, 0.89, 0.3, 1],
[0, 1, 1, 1],
[0, 0, 1, 1],
],
}
EVAL_DOT_MAX_SIZE = 0.5
EVAL_DOT_MIN_SIZE = 0.25
@@ -55,9 +67,9 @@ TOP_MOVE_BORDER_COLOR = [46 / 255, 65 / 255, 88 / 255, 0.9]
CHILD_SCALE = 0.95
# ponder light
ENGINE_DOWN_COL = EVAL_COLORS[1]
ENGINE_BUSY_COL = EVAL_COLORS[2]
ENGINE_READY_COL = EVAL_COLORS[-1]
ENGINE_DOWN_COL = EVAL_COLORS["theme:normal"][1]
ENGINE_BUSY_COL = EVAL_COLORS["theme:normal"][2]
ENGINE_READY_COL = EVAL_COLORS["theme:normal"][-1]
# info
INFO_PV_COLOR = to_hexcol(YELLOW)
-3
View File
@@ -165,9 +165,6 @@ Builder.load_string(
#:import abspath os.path.abspath
#:import i18n katrain.core.lang.i18n
<Button>:
track_lang: i18n._('')
<TreeLabel>:
on_touch_down:
self.parent.browser.path = self.path if self.collide_point(*args[1].pos) and self.path else self.parent.browser.path
Binary file not shown.
@@ -595,3 +595,15 @@ msgstr "每步棋计算深度"
#. TODO - on move tree editing
msgid "Delete Node"
msgstr "Delete Node"
#. TODO - theme name
msgid "theme:red-green-colourblind"
msgstr "Red-Green Colourblind"
#. TODO - theme name
msgid "theme:normal"
msgstr "Default Theme"
#. TODO - theme label
msgid "theme"
msgstr "Colour Theme"
Binary file not shown.
+15 -3
View File
@@ -559,9 +559,9 @@ msgstr ""
msgid "aihelp:p:rank"
msgstr ""
"Wählt zufällige Züge aus einer beschränkten Auswahl an Zügen und spieltden "
"Besten. Stärkere Einstellungen wählen den besten Zug aus einer grösseren "
"Auswahl. Da es keinen 0 Kyu/Dan gibt, ist 3 Dan = -2 Kyu."
"Wählt eine begrenzte Anzahl zufälliger Züge und spielt den Besten. Stärkere "
"Einstellungen wählen den besten Zug aus einer grösseren Auswahl. Da es "
"keinen 0 Kyu/Dan gibt, ist 3 Dan = -2 Kyu."
msgid "ai:p:rank"
msgstr "Kalibrierter Rang"
@@ -639,3 +639,15 @@ msgstr "Anzahl der Visits pro Zug"
#. TODO - on move tree editing
msgid "Delete Node"
msgstr "Delete Node"
#. TODO - theme name
msgid "theme:red-green-colourblind"
msgstr "Red-Green Colourblind"
#. TODO - theme name
msgid "theme:normal"
msgstr "Default Theme"
#. TODO - theme label
msgid "theme"
msgstr "Colour Theme"
Binary file not shown.
+15 -3
View File
@@ -616,9 +616,9 @@ msgstr "Calibrated Rank"
msgid "aihelp:p:rank"
msgstr ""
"Picks moves at random from a limited selection of moves and plays the best "
"one. Stronger settings select the best move from a larger selection. Since "
"there is no 0 kyu/dan, 3 dan = -2 kyu."
"Picks a limited number of legal moves at random and plays the best one. "
"Stronger settings select the best move from a larger selection. Since there "
"is no 0 kyu/dan, 3 dan = -2 kyu."
#. in AI settings
msgid "estimated strength"
@@ -643,3 +643,15 @@ msgstr "Number of visits per move"
#. on move tree editing
msgid "Delete Node"
msgstr "Delete Node"
#. theme label
msgid "theme"
msgstr "Colour Theme"
#. theme name
msgid "theme:normal"
msgstr "Default Theme"
#. theme name
msgid "theme:red-green-colourblind"
msgstr "Red-Green Colourblind"
Binary file not shown.
+15 -3
View File
@@ -557,9 +557,9 @@ msgstr ""
msgid "aihelp:p:rank"
msgstr ""
"Seleciona jugadas al azar de una limitada selección y juega la mejor. "
"Configuraciones más fuertes toma la mejor jugada de una selección más amplia"
" de jugadas. Como no hay 0 kyu/dan, 3 dan = -2 kyu."
"Elige una selección limitada de jugadas y juega la mejor. Configuraciones "
"más fuertes toma la mejor jugada de una selección más amplia de jugadas. "
"Como no hay 0 kyu/dan, 3 dan = -2 kyu."
msgid "Kata model not found"
msgstr "El modelo KataGo {model} no existe, revisar conf general."
@@ -635,3 +635,15 @@ msgstr "Número de visitas por jugada"
#. TODO - on move tree editing
msgid "Delete Node"
msgstr "Delete Node"
#. TODO - theme name
msgid "theme:red-green-colourblind"
msgstr "Red-Green Colourblind"
#. TODO - theme name
msgid "theme:normal"
msgstr "Default Theme"
#. TODO - theme label
msgid "theme"
msgstr "Colour Theme"
Binary file not shown.
@@ -676,3 +676,15 @@ msgstr ""
#. TODO - on move tree editing
msgid "Delete Node"
msgstr "Delete Node"
#. TODO - theme name
msgid "theme:red-green-colourblind"
msgstr "Red-Green Colourblind"
#. TODO - theme name
msgid "theme:normal"
msgstr "Default Theme"
#. TODO - theme label
msgid "theme"
msgstr "Colour Theme"
Binary file not shown.
+20 -7
View File
@@ -541,9 +541,10 @@ msgstr "第一感(比重)"
msgid "aihelp:p:weighted"
msgstr ""
"第一感での確率にしたがって手を選びます. `weaken_fac`は弱そうな手を選ぶ度合を調整します. これを非常に小さな値にすれば`Policy` "
"AIと似たような打ち方になります. なお, 最有力手の第一感での確率が`pick_override`より大きい場合はその手が無条件で選ばれます. 逆に,"
" 第一感での確率が`lower_bound`より小さい手は決して選ばれません."
"第一感での確率にしたがって手を選びます. `weaken_fac`は弱そうな手を選ぶ度合を調整します. これを非常に小さな値にすれば, "
"本来の「第一感」と似たような打ち方になります. なお, "
"最有力手の第一感での確率が`pick_override`より大きい場合はその手が無条件で選ばれます. 逆に, "
"第一感での確率が`lower_bound`より小さい手は決して選ばれません."
msgid "ai:p:pick"
msgstr "第一感(盲点)"
@@ -551,7 +552,7 @@ msgstr "第一感(盲点)"
msgid "aihelp:p:pick"
msgstr ""
"`pick_n + pick_frac * <合法手の個数>`個の手をランダムに選んだ上で, その中での最善手を打ちます. "
"`pick_frac=1`にすれば`Policy` AIと同じような打ち方になります. なお, 明らかなミスを避けるために, "
"`pick_frac=1`にすれば, 本来の「第一感」と同じような打ち方になります. なお, 明らかなミスを避けるために, "
"最有力手の第一感での確率が`pick_override`より大きい場合は無条件でその手を打ちます."
msgid "ai:p:local"
@@ -595,8 +596,8 @@ msgstr "段級位相応"
msgid "aihelp:p:rank"
msgstr ""
"制限された手のうちからランダムに何手か選んだ上で, その中での最善手を打ちます. 強い設定にするほどより広い候補の中から最善手を選ぶようになります. "
"なお, 3段は-2 kyuに対応します(0級/段はないので数字がずれる). "
"すべての合法手の中からランダムに何個かの候補手を選んで, その中での再有力手を打ちます. "
"強い設定にするほどより多くの候補手の中から再有力手を選ぶようになります. なお, 3段は-2 kyuに対応します(0級/段はないので数字がずれる). "
#. in AI settings
msgid "estimated strength"
@@ -620,4 +621,16 @@ msgstr "着手毎の読み手数"
#. TODO - on move tree editing
msgid "Delete Node"
msgstr "Delete Node"
msgstr "この手以降を削除"
#. TODO - theme name
msgid "theme:red-green-colourblind"
msgstr "赤緑色弱"
#. TODO - theme name
msgid "theme:normal"
msgstr "デフォルト"
#. TODO - theme label
msgid "theme"
msgstr "カラーテーマ"
Binary file not shown.
@@ -611,3 +611,15 @@ msgstr "한 수당 visit 수"
#. TODO - on move tree editing
msgid "Delete Node"
msgstr "Delete Node"
#. TODO - theme name
msgid "theme:red-green-colourblind"
msgstr "Red-Green Colourblind"
#. TODO - theme name
msgid "theme:normal"
msgstr "Default Theme"
#. TODO - theme label
msgid "theme"
msgstr "Colour Theme"
Binary file not shown.
@@ -636,3 +636,15 @@ msgstr "Количество посещений на ход"
#. TODO - on move tree editing
msgid "Delete Node"
msgstr "Delete Node"
#. TODO - theme name
msgid "theme:red-green-colourblind"
msgstr "Red-Green Colourblind"
#. TODO - theme name
msgid "theme:normal"
msgstr "Default Theme"
#. TODO - theme label
msgid "theme"
msgstr "Colour Theme"
+14 -6
View File
@@ -213,6 +213,7 @@
<ConfigTeacherPopup>
options_grid: options_grid
themes_spinner: themes_spinner
GridLayout:
cols: 5
rows: 7
@@ -229,22 +230,29 @@
DescriptionLabel:
text: i18n._("save dots")
AnchorLayout:
size_hint: 1, 2
size_hint: 1, 3
GridLayout:
cols: 2
rows: 3
rows: 4
padding: 4
size_hint: 0.66, 1
size_hint: 0.8, 1
DescriptionLabel:
font_size: DESC_FONT_SIZE * 0.8
text: i18n._("theme")
AnchorLayout:
LabelledSpinner:
size_hint: 0.8,0.8
input_property: 'trainer/theme'
font_size: DESC_FONT_SIZE * 0.8
id: themes_spinner
DescriptionLabel:
font_size: DESC_FONT_SIZE * 0.8
text: i18n._("show last n dots")
size_hint: 2,1
AnchorLayout:
LabelledIntInput:
size_hint_x: 0.33
size_hint_x: 0.15
input_property: "trainer/eval_off_show_last"
DescriptionLabel:
size_hint: 2,1
font_size: DESC_FONT_SIZE * 0.8
text: i18n._("show ai dots")
AnchorLayout:
+1
View File
@@ -0,0 +1 @@
(;GM[1]FF[4] SZ[19] GN[] DT[2020-07-20] PB[whatiskale] PW[V259081699] BR[15级] WR[14级] KM[0]HA[1]RU[Japanese]AP[GNU Go:3.8]RE[W+R]TM[1200]TC[3]TT[30]AP[foxwq]RL[0] ;B[pd];W[dp];B[pq];W[dd];B[fc];W[qo];B[np];W[nc];B[qf];W[pc];B[qc];W[qb];B[rc];W[od];B[pb];W[oc];B[rb];W[jd];B[cc];W[dc];B[cd];W[de];B[db];W[eb];B[cb];W[fb];B[gc];W[ec];B[gb];W[ce];B[be];W[bf];B[bd];W[ge];B[cf];W[dg];B[bg];W[kc];B[df];W[ef];B[cg];W[eg];B[fe];W[ff];B[fq];W[hq];B[cq];W[dq];B[cp];W[do];B[dr];W[er];B[cr];W[fr];B[eq];W[gq];B[fp];W[co];B[bo];W[bn];B[bp];W[go];B[fo];W[fn];B[cn];W[cm];B[gn];W[fm];B[qm];W[ki];B[oe];W[pe];B[qd];W[ne];B[of];W[nf];B[ng];W[og];B[pf];W[mg];B[nh];W[mi];B[ni];W[nj];B[mj];W[mh];B[ph];W[oj];B[pi];W[mk];B[lj];W[kl];B[nk];W[ok];B[nl];W[ol];B[ll];W[ml];B[nm];W[mm];B[lm];W[mn];B[nn];W[lk];B[kk];W[kj];B[jk];W[mo];B[ln];W[lo];B[jm];W[gm];B[hn];W[hm];B[in];W[jo];B[gp];W[hp];B[ho];W[lq];B[jj];W[hj];B[hk];W[gj];B[gk];W[fj];B[ji];W[dj];B[dh];W[ch];B[ci];W[ag];B[bh];W[rq];B[rn];W[pn];B[om];W[pm];B[no];W[ro];B[pl];W[ql];B[pk];W[pj];B[qk];W[qj];B[rk];W[qr];B[pr];W[mr];B[so];W[sp];B[sn];W[qq];B[ip];W[iq];B[jp];W[jq];B[lp];W[ko];B[mp];W[qn];B[rl];W[ob];B[qa];W[dn];B[jb];W[kb];B[fd];W[rj];B[ee];W[rh];B[rg];W[qh];B[sh];W[qg];B[pg];W[rf];B[sg];W[si];B[qi];W[sf];B[sg];W[re];B[qe];W[li])
+1
View File
@@ -0,0 +1 @@
(;GM[1]FF[4] SZ[19] GN[] DT[2020-07-25] PB[whatiskale] PW[注号用名] BR[15级] WR[15级] KM[0]HA[0]RU[Japanese]AP[GNU Go:3.8]RE[B+R]TM[1200]TC[3]TT[30]AP[foxwq]RL[0] ;B[pd];W[dp];B[pp];W[dd];B[fc];W[qq];B[pq];W[qp];B[qn];W[qo];B[pn];W[po];B[oo];W[rn];B[rm];W[sn];B[ql];W[qf];B[nc];W[qi];B[fq];W[hq];B[fo];W[lq];B[cn];W[dn];B[dm];W[en];B[dq];W[cp];B[cq];W[bp];B[bq];W[bn];B[cm];W[bm];B[ck];W[fn];B[cf];W[ee];B[cc];W[dc];B[cd];W[db];B[id];W[hd];B[hc];W[he];B[jq];W[fp];B[ep];W[gp];B[eo];W[do];B[go];W[ho];B[gn];W[fl];B[jo];W[lo];B[hn];W[er];B[fr];W[jn];B[io];W[hp];B[kn];W[jm];B[ln];W[ko];B[mn];W[jp];B[ip];W[kp];B[iq];W[hr];B[ir];W[is];B[js];W[hs];B[gs];W[in];B[gr];W[kr];B[mr];W[jr];B[gq];W[ks];B[mq];W[pr];B[or];W[mp])
+133
View File
@@ -0,0 +1,133 @@
(
;
FileFormat[1]GaMe[1]
VieW[]
SiZe[9]
PlayerBlack[hi]
PlayerWhite[ka]
KoMi[-1.5]
Black[ff]
;
White[gd]
;
Black[fd]
;
White[fe]
;
Black[ge]
;
White[ee]
;
Black[gc]
;
White[hd]
;
Black[hc]
;
White[he]
;
Black[gf]
;
White[hf]
;
Black[ed]
;
White[ef]
;
Black[eg]
;
White[dg]
;
Black[fg]
;
White[gh]
;
Black[cg]
;
White[dh]
;
Black[df]
;
White[de]
;
Black[cf]
;
White[dd]
;
Black[dc]
;
White[ec]
;
Black[fc]
;
White[cc]
;
Black[eb]
;
White[be]
;
Black[bf]
;
White[ce]
;
Black[bd]
;
White[ae]
;
Black[af]
;
White[db]
;
Black[ec]
;
White[bc]
;
Black[eh]
;
White[gg]
;
Black[ch]
;
White[fh]
;
Black[di]
;
White[ic]
;
Black[cb]
;
White[bb]
;
Black[da]
;
White[hb]
;
Black[gb]
;
White[ba]
;
Black[ca]
;
White[fi]
;
Black[id]
;
White[ib]
;
Black[ha]
;
White[ie]
;
Black[ga]
;
White[hg]
;
Black[ia]
;
Comment[Black: 13 + 4 = 17
White: 13 + 2 + -1.5 = 13.5
Black wins by 3.5]
White[id]
)
+29 -1
View File
@@ -1,5 +1,9 @@
import os
from unittest.mock import MagicMock, patch
from katrain.core.base_katrain import KaTrainBase
from katrain.core.engine import KataGoEngine
from katrain.core.game import Game, KaTrainSGF
from katrain.core.sgf_parser import SGF, SGFNode
@@ -59,7 +63,7 @@ def test_pandanet():
"GM",
"EV",
"US",
"CoPyright",
"CP",
"GN",
"RE",
"PW",
@@ -91,6 +95,16 @@ def test_pandanet():
assert move is root
def test_old_long_properties():
file = os.path.join(os.path.dirname(__file__), "data/xmgt97.sgf")
SGF.parse_file(file)
def test_old_server_style():
input_sgf = "... 01:23:45 +0900 (JST) ... (;SZ[19];B[aa];W[ba];)"
SGF.parse_sgf(input_sgf)
def test_ogs():
file = os.path.join(os.path.dirname(__file__), "data/ogs.sgf")
tree = SGF.parse_file(file)
@@ -125,3 +139,17 @@ def test_ngf():
"SZ": [19],
} == root.properties
assert "pq" == root.children[0].get_property("W")
def test_foxwq():
for sgf in ["data/fox sgf error.sgf", "data/fox sgf works.sgf"]:
file = os.path.join(os.path.dirname(__file__), sgf)
move_tree = KaTrainSGF.parse_file(file)
katrain = KaTrainBase(force_package_config=True, debug_level=0)
game = Game(katrain, MagicMock(), move_tree)
assert [] == move_tree.placements
assert [] == game.root.placements
while game.current_node.children:
assert 1 == len(game.current_node.children)
game.redo(1)