diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 1f37d7a..e1b4b25 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -5,6 +5,7 @@ Many thanks to these additional authors: Matthew Allred ("Kameone") for design of the v1.1 UI and providing MacOS installation instructions. "isty2e" for Korean translation. "Dontbtme" for detailed feedback and early testing of v1.0, and French translation. +"bale-go" for implementing and calibrating the 'calibrated rank' AI. Additional thanks to: "Seventeen" for explanatory youtube videos. diff --git a/INSTALL.md b/INSTALL.md index 76046cf..7cc515d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -2,11 +2,12 @@ # Installation for MacOS users ## Installation pre-requisites -* Download and install [Python 3.7.5](https://www.python.org/downloads/release/python-375/) * Install [Homebrew](https://brew.sh) by running the following command in terminal: * ``` /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" ``` +* Install Python using [Homebrew](https://brew.sh/) by executing `brew install python3` + * Alternatively download and install [Python 3.7.5](https://www.python.org/downloads/release/python-375/) * Install Katago using [Homebrew](https://brew.sh/) by executing `brew install katago` * You can also follow instructions [here](https://github.com/lightvector/KataGo) to compile KataGo yourself. diff --git a/README.md b/README.md index 1f50bd3..047275b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# KaTrain v1.1 +# KaTrain v1.1 [![Latest Release](https://img.shields.io/github/release/sanderland/katrain?label=download)](https://github.com/sanderland/katrain/releases) [![License:MIT](https://img.shields.io/pypi/l/katrain)](https://en.wikipedia.org/wiki/MIT_License) ![Build Status](https://github.com/sanderland/katrain/workflows/release/badge.svg) diff --git a/katrain/__main__.py b/katrain/__main__.py index a7117cb..efd0895 100644 --- a/katrain/__main__.py +++ b/katrain/__main__.py @@ -64,7 +64,7 @@ from katrain.core.game import Game, IllegalMoveException, KaTrainSGF from katrain.core.sgf_parser import Move, ParseError from katrain.gui.kivyutils import * from katrain.gui.popups import ConfigPopup, LoadSGFPopup, NewGamePopup, AIPopup -from katrain.gui.style import ENGINE_BUSY_COL, ENGINE_DOWN_COL, ENGINE_READY_COL +from katrain.gui.style import ENGINE_BUSY_COL, ENGINE_DOWN_COL, ENGINE_READY_COL, LIGHTGREY from katrain.gui.widgets.graph import ScoreGraph from katrain.gui.widgets.filebrowser import I18NFileBrowser from katrain.gui.badukpan import AnalysisControls, BadukPanControls, BadukPanWidget @@ -431,21 +431,28 @@ class KaTrainGui(Screen, KaTrainBase): class KaTrainApp(MDApp): gui = ObjectProperty(None) language = StringProperty(DEFAULT_LANGUAGE) + + def __init__(self): + super().__init__() +# self.theme_cls.selected_color = WHITE +# self.theme_cls.secondary_text_color = LIGHTGREY + def build(self): self.icon = ICON # how you're supposed to set an icon + self.title = f"KaTrain v{VERSION}" + self.theme_cls.theme_style = "Dark" + self.theme_cls.primary_palette = "Gray" + self.theme_cls.primary_hue = "200" + kv_file = find_package_resource("katrain/gui.kv") popup_kv_file = find_package_resource("katrain/popups.kv") resource_add_path(os.path.split(kv_file)[0]) Builder.load_file(kv_file) Builder.load_file(popup_kv_file) - self.gui = KaTrainGui() - self.title = f"KaTrain v{VERSION}" - self.theme_cls.theme_style = "Dark" - self.theme_cls.primary_palette = "Blue" - Window.bind(on_request_close=self.on_request_close) + self.gui = KaTrainGui() return self.gui def on_language(self, _instance, language): @@ -459,7 +466,7 @@ class KaTrainApp(MDApp): self.gui.controls.set_status("") def webbrowser(self, site_key): - WEBSITES = {"homepage": HOMEPAGE, "support": HOMEPAGE + "#support"} + WEBSITES = {"homepage": HOMEPAGE + "#manual", "support": HOMEPAGE + "#support"} if site_key in WEBSITES: webbrowser.open(WEBSITES[site_key]) diff --git a/katrain/core/game_node.py b/katrain/core/game_node.py index 527b5d3..d832775 100644 --- a/katrain/core/game_node.py +++ b/katrain/core/game_node.py @@ -29,6 +29,7 @@ class GameNode(SGFNode): def sgf_properties(self, save_comments_player=None, save_comments_class=None, eval_thresholds=None): properties = copy.copy(super().sgf_properties()) + note = self.note.strip() if self.points_lost and save_comments_class is not None and eval_thresholds is not None: show_class = save_comments_class[evaluation_class(self.points_lost, eval_thresholds)] else: @@ -39,7 +40,7 @@ class GameNode(SGFNode): and self.analysis_ready and self.parent and self.parent.analysis_ready - ): + ) or note: candidate_moves = self.parent.candidate_moves top_x = Move.from_gtp(candidate_moves[0]["move"]).sgf(self.board_size) best_sq = [ @@ -59,7 +60,7 @@ class GameNode(SGFNode): + "\n".join(properties.get("C", "")) + "\nSGF with review generated by KaTrain." ] - if self.note.strip(): + if note: properties["C"] = ["\n".join(properties.get("C", "")) + f"\nNote: {self.note}"] return properties @@ -154,7 +155,7 @@ class GameNode(SGFNode): pvtext = f"[u][ref={pvtext}][color={INFO_PV_COLOR}]{pvtext}[/color][/ref][/u]" return pvtext - def comment(self, sgf=False, teach=False, hints=False, interactive=True): + def comment(self, sgf=False, teach=False, details=False, interactive=True): single_move = self.move if not self.parent or not single_move: # root return "" @@ -167,7 +168,7 @@ class GameNode(SGFNode): text += i18n._("Info:winrate").format(winrate=self.format_winrate()) + "\n" if self.parent and self.parent.analysis_ready: previous_top_move = self.parent.candidate_moves[0] - if sgf or hints: + if sgf or details: if previous_top_move["move"] != single_move.gtp(): points_lost = self.points_lost if sgf and points_lost > 0.5: @@ -181,7 +182,7 @@ class GameNode(SGFNode): ) else: text += i18n._("Info:best move") + "\n" - if previous_top_move.get("pv") and (sgf or hints): + if previous_top_move.get("pv") and (sgf or details): text += ( i18n._("Info:PV").format( pv=self.make_pv(single_move.player, previous_top_move["pv"], interactive) @@ -189,7 +190,7 @@ class GameNode(SGFNode): + "\n" ) - if sgf or hints or teach: + if sgf or details or teach: policy_ranking = self.parent.policy_ranking currmove_policy_with_ix = [ (ix + 1, p) @@ -203,7 +204,7 @@ class GameNode(SGFNode): ) + "\n" ) - if not currmove_policy_with_ix or currmove_policy_with_ix[0] != 1 and (sgf or hints): + if not currmove_policy_with_ix or currmove_policy_with_ix[0] != 1 and (sgf or details): text += ( i18n._("Info:policy best").format( move=policy_ranking[0][1].gtp(), probability=policy_ranking[0][0] @@ -215,7 +216,7 @@ class GameNode(SGFNode): top_pv = self.analysis_ready and self.candidate_moves[0].get("pv") if top_pv: text += i18n._("Info:undo predicted PV").format(pv=f"{self.next_player}{' '.join(top_pv)}") + "\n" - if self.ai_thoughts and (sgf or hints): + if self.ai_thoughts and (sgf or details): text += "\n" + i18n._("Info:AI thoughts").format(thoughts=self.ai_thoughts) else: text = i18n._("No analysis available") if sgf else i18n._("Analyzing move...") diff --git a/katrain/gui.kv b/katrain/gui.kv index 3998790..c7471f7 100644 --- a/katrain/gui.kv +++ b/katrain/gui.kv @@ -12,6 +12,7 @@ #:import RED katrain.gui.style.RED #:import WHITE katrain.gui.style.WHITE +#:import LIGHTGREY katrain.gui.style.LIGHTGREY #:import BACKGROUND_COLOR katrain.gui.style.BACKGROUND_COLOR #:import BOX_BACKGROUND_COLOR katrain.gui.style.BOX_BACKGROUND_COLOR #:import TEXT_COLOR katrain.gui.style.TEXT_COLOR @@ -19,14 +20,13 @@ #:import BOX_BACKGROUND_COLOR katrain.gui.style.BOX_BACKGROUND_COLOR #:set SPINNER_OPTION_COLOR [64 / 255, 85 / 255, 110 / 255, 1] -#:set LIGHTGREY [0.7,0.7,0.7,1] #:set BLACK [0.05,0.05,0.05,1] #:set GREY [0.5,0.5,0.5,1] #:set GREEN [0.1,0.8,0.1,1] #:set LIGHTBLUE [0.3,0.7,0.9,1] #:set YELLOW [0.8,0.8,0.1,1] #:set DARKRED [0.3,0.1,0.1,1] - +#:set ORANGE [242/255,96/255,34/255,1] #:set SCORE_COLOR LIGHTBLUE #:set POINTLOSS_COLOR YELLOW @@ -65,6 +65,7 @@ # new widgets + : ripple_duration_in_slow: 0.6 text_color: TEXT_COLOR @@ -301,7 +302,7 @@ min_size: min(self.height,self.width) inactive_line_color: WHITE inactive_fill_color: [1,1,1,0] - active_line_color: [242/255,96/255,34/255,1] + active_line_color: ORANGE active_fill_color: [1,1,1,0] line_width: max(1.1,self.min_size/20) line_color: root.active_line_color if self.active else root.inactive_line_color @@ -332,7 +333,8 @@ - color: GREEN + timeout: False + color: ORANGE if self.timeout else GREEN markup: True multiline: False font_name: 'fonts/digital-7 (mono).ttf' @@ -341,6 +343,7 @@ spacing: CP_SPACING padding: CP_PADDING * 4, CP_PADDING * 2 paused: True + timeout: self.state[1] <= 0 MDBoxLayout: size_hint: 0.7,1 MDFloatLayout: @@ -355,6 +358,7 @@ valign: 'middle' TimerLabel: id: timer + timeout: root.timeout text: "{:01d}:{:02d}".format(int(root.state[0]+0.99)//60,int((root.state[0]+0.99) % 60) ) text_size: self.size font_size: 0.85*self.height @@ -368,6 +372,7 @@ text_size: self.width, self.height * 0.95 halign: 'right' valign: 'top' + timeout: root.timeout TimerLabel: size_hint: 0.1,1 font_size: self.height * 0.35 @@ -377,6 +382,7 @@ id: periods text: str(root.state[1]) multiline: False + timeout: root.timeout MDBoxLayout: size_hint: 0.2,1 PauseButton: @@ -631,14 +637,16 @@ CollapsablePanel: size_hint_y_open: 1 closed_label: 'closedlabel:info¬es' - options: ['info','notes'] - option_colors: [WHITE,WHITE] - option_active: [True,False] + options: ['info','info-details','notes'] + option_colors: [WHITE,WHITE,YELLOW] + option_active: [True,False,False] on_option_state: info.size_hint_y = int(args[1]['info'])+1e-9 info.opacity = int(args[1]['info']) + info.detailed = args[1]['info-details'] notebox.size_hint_y = int(args[1]['notes'])*0.66+1e-9 notebox.opacity = int(args[1]['notes']) + app.gui.update_state() options_height: root.tab_option_height BGBoxLayout: background_color: BOX_BACKGROUND_COLOR @@ -651,6 +659,7 @@ background_color: DARKRED ScrollableLabel: id: info + detailed: False line_height: 1.25 markup: True on_ref_press: root.katrain.board_gui.show_pv_from_comments(args[1]) @@ -700,8 +709,6 @@ pos_hint: {'center_y': 0.5} width: self.height active: root.default_active - selected_color: WHITE - unselected_color: LIGHTGREY on_active: if app.gui: app.gui.update_state() : diff --git a/katrain/gui/controlspanel.py b/katrain/gui/controlspanel.py index 8b35aac..35dc18c 100644 --- a/katrain/gui/controlspanel.py +++ b/katrain/gui/controlspanel.py @@ -84,11 +84,11 @@ class ControlsPanel(BoxLayout): self.active_comment_node = current_node.parent lock_ai = katrain.config("trainer/lock_ai") and katrain.play_analyze_mode == MODE_PLAY - hints = katrain.analysis_controls.hints.active and not lock_ai + details = self.info.detailed and not lock_ai info = "" if current_node.move and not current_node.is_root: info = self.active_comment_node.comment( - teach=katrain.players_info[self.active_comment_node.player].being_taught, hints=hints + teach=katrain.players_info[self.active_comment_node.player].being_taught, details=details ) if self.active_comment_node.analysis_ready: diff --git a/katrain/gui/kivyutils.py b/katrain/gui/kivyutils.py index ae52b16..be9060a 100644 --- a/katrain/gui/kivyutils.py +++ b/katrain/gui/kivyutils.py @@ -302,7 +302,7 @@ class PlayerInfo(MDBoxLayout, BackgroundMixin): class Timer(BGBoxLayout): state = ListProperty([30, 5, 1]) - + timeout = BooleanProperty(False) class AnalysisToggle(MDBoxLayout): text = StringProperty("") diff --git a/katrain/gui/style.py b/katrain/gui/style.py index 53eddc9..cd35d55 100644 --- a/katrain/gui/style.py +++ b/katrain/gui/style.py @@ -7,6 +7,7 @@ DEFAULT_FONT = "fonts/NotoSans-Regular.ttf" # basic color definitions RED = [0.8, 0.1, 0.1, 1] WHITE = [0.95, 0.95, 0.95, 1] +LIGHTGREY = [0.7,0.7,0.7,1] # gui colors diff --git a/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo index 88acc05..9f3add4 100644 Binary files a/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/en/LC_MESSAGES/katrain.mo differ diff --git a/katrain/i18n/locales/en/LC_MESSAGES/katrain.po b/katrain/i18n/locales/en/LC_MESSAGES/katrain.po index 7e43428..c5ea05a 100644 --- a/katrain/i18n/locales/en/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/en/LC_MESSAGES/katrain.po @@ -133,6 +133,9 @@ msgstr "Point Loss" msgid "tab:info" msgstr "Info" +msgid "tab:info-details" +msgstr "Details" + msgid "tab:notes" msgstr "Notes" diff --git a/katrain/i18n/locales/fr/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/fr/LC_MESSAGES/katrain.mo index 8df582d..6d3bc57 100644 Binary files a/katrain/i18n/locales/fr/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/fr/LC_MESSAGES/katrain.mo differ diff --git a/katrain/i18n/locales/fr/LC_MESSAGES/katrain.po b/katrain/i18n/locales/fr/LC_MESSAGES/katrain.po index 06f18df..5a4cd90 100644 --- a/katrain/i18n/locales/fr/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/fr/LC_MESSAGES/katrain.po @@ -608,3 +608,7 @@ msgstr "" #. TODO msgid "ai:p:rank" msgstr "Calibrated Rank Bot" + +#. TODO +msgid "tab:info-details" +msgstr "+Analysis" diff --git a/katrain/i18n/locales/haha/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/haha/LC_MESSAGES/katrain.mo index 801b4f4..6dc15a2 100644 Binary files a/katrain/i18n/locales/haha/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/haha/LC_MESSAGES/katrain.mo differ diff --git a/katrain/i18n/locales/haha/LC_MESSAGES/katrain.po b/katrain/i18n/locales/haha/LC_MESSAGES/katrain.po index 41118a5..55f8578 100644 --- a/katrain/i18n/locales/haha/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/haha/LC_MESSAGES/katrain.po @@ -538,3 +538,6 @@ msgstr "" msgid "ai:p:rank" msgstr "ㅋㅋCalibrated Rank Bot" + +msgid "tab:info-details" +msgstr "ㅋㅋ+Analysis" diff --git a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo index ecd9e06..edd3fa5 100644 Binary files a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo and b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.mo differ diff --git a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po index 8eb7175..d5e073a 100644 --- a/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po +++ b/katrain/i18n/locales/ko/LC_MESSAGES/katrain.po @@ -549,3 +549,7 @@ msgstr "" #. TODO msgid "ai:p:rank" msgstr "Calibrated Rank Bot" + +#. TODO +msgid "tab:info-details" +msgstr "+Analysis" diff --git a/katrain/popups.kv b/katrain/popups.kv index c3ec740..8bb3904 100644 --- a/katrain/popups.kv +++ b/katrain/popups.kv @@ -28,10 +28,12 @@ helper_text_mode: "none" hint_text: "" helper_text: "" + color_mode: 'custom' + line_color_focus: TEXT_COLOR : font_size: dp(16) - multiline: True # just for wrapping + multiline: False helper_text_mode: "on_error" helper_text: i18n._("file not found") @@ -47,6 +49,7 @@ size: DESC_FONT_SIZE*2,DESC_FONT_SIZE*2 size_hint: None,None + on_left_press: if self.target: self.target.text = self.text size: DESC_FONT_SIZE * 2, DESC_FONT_SIZE * 2 @@ -78,17 +81,22 @@ : configfile: configfile + BoxLayout: + size_hint: 1,1 + orientation: 'horizontal' + DescriptionLabel: + text: i18n._('general settings') + font_size: DESC_FONT_SIZE * 1.5 + DescriptionLabel: + font_size: DESC_FONT_SIZE * 1.5 + text: i18n._('engine settings') BoxLayout: size_hint: 1,9 orientation: 'horizontal' GridLayout: cols: 2 - rows: 8 + rows: 7 spacing: CP_SPACING - Label: - DescriptionLabel: - text: i18n._('general settings') - font_size: DESC_FONT_SIZE * 1.5 DescriptionLabel: text: i18n._("general:sgf_load") AnchorLayout: @@ -126,12 +134,8 @@ width: 2 GridLayout: cols: 2 - rows: 8 + rows: 7 spacing: CP_SPACING - Label: - DescriptionLabel: - font_size: DESC_FONT_SIZE * 1.5 - text: i18n._('engine settings') DescriptionLabel: text: i18n._("engine:katago") AnchorLayout: