ais and bugs

This commit is contained in:
Sander Land committed 2020-05-29 00:18:23 +02:00
1 parent d70515235a
commit 97dc12df10
15 files changed
+241 -76

No files matched your search

+10 -23
View File
@@ -87,7 +87,7 @@ class KaTrainGui(Screen, KaTrainSettings):
self.controls.set_status(f"KataGo engine ready.")
if "ready" in message.lower():
self.controls.set_status(f"KataGo engine ready.")
if level == OUTPUT_ERROR or (level == OUTPUT_KATAGO_STDERR and "error" in message.lower()):
if (level == OUTPUT_ERROR or (level == OUTPUT_KATAGO_STDERR and "error" in message.lower())) and getattr(self, "controls", None):
self.controls.set_status(f"ERROR: {message}")
@property
@@ -109,17 +109,12 @@ class KaTrainGui(Screen, KaTrainSettings):
def update_state(self, redraw_board=False): # is called after every message and on receiving analyses and config changes
# AI and Trainer/auto-undo handlers
cn = self.game.current_node
last_player, next_player = self.game.players[cn.player], self.game.players[cn.next_player]
if self.play_analyze_mode == MODE_PLAY:
teaching_undo = cn.player and self.game.players[cn.player].being_taught
teaching_undo = cn.player and last_player.being_taught
if teaching_undo and cn.analysis_ready and cn.parent and cn.parent.analysis_ready and not cn.children and not self.game.ended:
self.game.analyze_undo(cn) # not via message loop
if (
cn.analysis_ready
and "ai" in self.controls.player_mode(cn.next_player).lower()
and not cn.children
and not self.game.ended
and not (teaching_undo and cn.auto_undo is None)
):
if cn.analysis_ready and next_player.ai and not cn.children and not self.game.ended and not (teaching_undo and cn.auto_undo is None):
self._do_ai_move(cn) # cn mismatch stops this if undo fired. avoid message loop here or fires repeatedly.
# Handle prisoners and next player display
@@ -174,26 +169,23 @@ class KaTrainGui(Screen, KaTrainSettings):
if self.game:
self.message_queue.put([self.game.game_id, message, *args])
def update_players(self):
for player in "BW":
self.controls.players[player].player_type = self.game.players[player].player_type
self.game.players[player].player_subtype = self.game.players[player].player_subtype
def _do_new_game(self, move_tree=None, analyze_fast=False):
self.board_gui.animating_pv = None
self.engine.on_new_game() # clear queries
self.game = Game(self, self.engine, move_tree=move_tree, analyze_fast=analyze_fast)
for bw, player_widget in self.nav_drawer_contents.player_setup.players.items():
self.game.players[bw].update(**player_widget.player_def)
self.controls.graph.initialize_from_game(self.game.root)
self.controls.periods_used = {"B": 0, "W": 0}
self.update_state(redraw_board=True)
self.update_players()
def _do_ai_move(self, node=None):
if node is None or self.game.current_node == node:
mode = self.controls.ai_mode(self.game.current_node.next_player)
mode = self.game.next_player.strategy
settings = self.config(f"ai/{mode}")
if settings:
ai_move(self.game, mode, settings)
else:
self.log(f"AI Mode {mode} not found!", OUTPUT_ERROR)
def _do_undo(self, n_times=1):
if n_times == "smart":
@@ -287,12 +279,7 @@ class KaTrainGui(Screen, KaTrainSettings):
self.game.root.set_property(
f"P{pl}", f"AI {self.controls.ai_mode(pl)} (KataGo { os.path.splitext(model_file)[0]})" if "ai" in self.controls.player_mode(pl) else "Player"
)
msg = self.game.write_sgf(
self.config("sgf/sgf_save"),
trainer_config=self.config("trainer"),
save_feedback=self.config("sgf/save_feedback"),
eval_thresholds=self.config("trainer/eval_thresholds"),
)
msg = self.game.write_sgf(self.config("general/sgf_save"))
self.log(msg, OUTPUT_INFO)
self.controls.set_status(msg)
+11 -23
View File
@@ -20,15 +20,15 @@
"sgf_load": "~/Downloads",
"sgf_save": "./sgfout",
"anim_pv_time": 0.5,
"debug_level": 3,
"debug_level": 1,
"language": "en"
},
"game": {
"size": "13",
"komi": 0.5,
"handicap": 2,
"rules": "chinese",
"clear_cache": true
"size": "19",
"komi": 6.5,
"handicap": 0,
"rules": "japanese",
"clear_cache": false
},
"trainer": {
"num_undo_prompts": [
@@ -60,32 +60,20 @@
"lock_ai": false
},
"ai": {
"Default": {
"default": {
"_help_right": "No settings available for Default KataGo AI, strength is mainly affected by `max_visits` and `model` in the main settings `engine` section.",
"_help_left": ""
},
"Balance": {
"target_score": 2,
"random_loss": 1,
"max_loss": 5,
"min_visits": 20,
"_help_left": "Will try to win by `target_score`, lose at most `random_loss` when behind and `max_loss` when ahead.",
"_help_right": "Never plays moves with less than `min_visits` visits, so also check engine settings."
},
"Jigo": {
"jigo": {
"target_score": 0.5,
"_help_left": "Will try to win by `target_score`, without further restrictions.",
"_help_right": "Also affected by engine settings such as `max_visits`."
},
"ScoreLoss": {
"strength": 0.5,
"_help_left": "Plays moves weighted inversely by point loss.",
"_help_right": "Also affected by engine settings such as `max_visits`, likely to play more varied/weaker with higher visits."
"scoreloss": {
"strength": 0.5
},
"Policy": {
"opening_moves": 0.05,
"_help_left": "Strength is mainly affected by `model` in engine settings, but should be high dan regardless.",
"_help_right": "Plays the P:Weighted strategy during the first `opening_moves` * <number of intersections> moves to allow variety."
"opening_moves": 20
},
"P:Weighted": {
"_help_right": "pick_override` determines when top move is chosen without randomness, and `lower_bound` determines the lower bound policy value that is allowed.",
+2 -2
View File
@@ -43,7 +43,7 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
policy_grid = var_to_grid(cn.policy, size) # type: List[List[float]]
top_policy_move = policy_moves[0][1]
ai_thoughts += f"Using policy based strategy, base top 5 moves are {fmt_moves(policy_moves[:5])}. "
if "policy" in ai_mode and cn.depth <= int(ai_settings["opening_moves"] * (game.board_size[0] * game.board_size[1])):
if "policy" in ai_mode and cn.depth <= ai_settings["opening_moves"]:
ai_mode = "p:weighted"
ai_thoughts += f"Switching to weighted strategy in the opening {int(ai_settings['opening_moves'] * (game.board_size[0]*game.board_size[1]))} moves. "
ai_settings = {"pick_override": 0.9, "weaken_fac": 1, "lower_bound": 0.02}
@@ -151,7 +151,7 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
aimove = top_cand
ai_thoughts += f"Top move is pass, so passing regardless of strategy."
else:
if "balance" in ai_mode:
if "balance" in ai_mode: # deprecated
sign = cn.player_sign(cn.next_player)
sel_moves = [ # top move, or anything not too bad, or anything that makes you still ahead
move
+1
View File
@@ -5,3 +5,4 @@ PLAYING_NORMAL, PLAYING_TEACHING = "game:normal", "game:teach"
GAME_TYPES = [PLAYING_NORMAL, PLAYING_TEACHING]
AI_DEFAULT = "default"
AI_STRATEGIES = [AI_DEFAULT]
+16 -5
View File
@@ -3,7 +3,7 @@ import os
import re
import threading
from datetime import datetime
from typing import Dict, List, Union
from typing import Dict, List, Union, Optional
from katrain.core.utils import var_to_grid, OUTPUT_INFO, OUTPUT_DEBUG, i18n
from katrain.core.engine import KataGoEngine
@@ -23,9 +23,12 @@ class KaTrainSGF(SGF):
class Player:
def __init__(self, player="B", player_type=PLAYER_HUMAN, player_subtype=PLAYING_NORMAL, periods_used=0):
self.player = player
self.update(player_type, player_subtype)
self.periods_used = periods_used
def update(self, player_type, player_subtype):
self.player_type = player_type
self.player_subtype = player_subtype
self.periods_used = periods_used
@property
def ai(self):
@@ -39,6 +42,7 @@ class Player:
def being_taught(self):
return self.player_type == PLAYER_HUMAN and self.player_subtype == PLAYING_TEACHING
@property
def strategy(self):
return self.player_subtype if self.ai else AI_DEFAULT
@@ -275,7 +279,14 @@ class Game:
def __repr__(self):
return "\n".join("".join(self.chains[c][0].player if c >= 0 else "-" for c in line) for line in self.board) + f"\ncaptures: {self.prisoner_count}"
def write_sgf(self, path=None, trainer_config={}, save_feedback=(True,), eval_thresholds=(0,)):
def write_sgf(self, path: str, trainer_config: Optional[Dict] = None, save_feedback: Optional[List] = None, eval_thresholds: Optional[List] = None):
if trainer_config is None:
trainer_config = self.katrain.config("trainer")
if save_feedback is None:
save_feedback = self.katrain.config("trainer/save_feedback")
if eval_thresholds is None:
eval_thresholds = self.katrain.config("trainer/eval_thresholds")
black, white = self.root.get_property("PB"), self.root.get_property("PW")
black = re.sub(r"['<>:\"/\\|?*]", "", black or "Black")
white = re.sub(r"['<>:\"/\\|?*]", "", white or "White")
@@ -283,11 +294,11 @@ class Game:
file_name = os.path.abspath(os.path.join(path, f"{game_name}.sgf"))
os.makedirs(os.path.dirname(file_name), exist_ok=True)
show_dots_for = {p: trainer_config.get("eval_show_ai", True) or "ai" not in self.katrain.controls.player_mode(p) for p in Move.PLAYERS}
show_dots_for = {bw: trainer_config.get("eval_show_ai", True) or pl.human for bw, pl in self.players.items()}
sgf = self.root.sgf(save_comments_player=show_dots_for, save_comments_class=save_feedback, eval_thresholds=eval_thresholds)
with open(file_name, "w") as f:
f.write(sgf)
return f"SGF with analysis written to {file_name}"
return i18n._("sgf written").format(file_name=file_name)
def analyze_extra(self, mode):
stones = {s.coords for s in self.stones}
+1 -1
View File
@@ -32,7 +32,7 @@ class KaTrainSettings:
def log(self, message, level=OUTPUT_INFO):
if level == OUTPUT_ERROR:
print(f"ERROR: {message}", sys.stderr)
print(f"ERROR: {message}", file=sys.stderr)
elif self.debug_level >= level:
print(message)
+7 -8
View File
@@ -1,15 +1,11 @@
import os
import sys
from typing import Any, List, Tuple, Callable
from kivy.event import EventDispatcher
from numbers import Number
from typing import List, Tuple, Callable
from kivy.lang import Observable
import gettext
from kivy.properties import StringProperty
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivymd.uix.label import MDIcon
from kivymd.uix.textfield import MDTextField
from katrain.gui.style import DEFAULT_FONT
@@ -27,7 +23,7 @@ OUTPUT_EXTRA_DEBUG = 2
MODE_PLAY, MODE_ANALYZE = "play", "analyze"
def var_to_grid(array_var: List[Any], size: Tuple[int, int]) -> List[List[Any]]:
def var_to_grid(array_var: List[Number], size: Tuple[int, int]) -> List[List[Number]]:
"""convert ownership/policy to grid format such that grid[y][x] is for move with coords x,y"""
ix = 0
grid = [[]] * size[1]
@@ -103,6 +99,9 @@ class Lang(Observable):
try:
func(args[0], None, None)
widget.font_name = self.font_name
for sub_widget in [getattr(widget, "_hint_lbl", None), getattr(widget, "_msg_lbl", None)]: # MDText
if sub_widget:
sub_widget.font_name = self.font_name
except ReferenceError:
pass # proxy no longer exists
for cb in self.callbacks:
+31 -12
View File
@@ -29,6 +29,7 @@
#: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 SCORE_COLOR LIGHTBLUE
@@ -691,28 +692,39 @@
on_option_state:
info.size_hint_y = int(args[1]['info'])+1e-9
info.opacity = int(args[1]['info'])
note.size_hint_y = int(args[1]['notes'])+1e-9
note.opacity = int(args[1]['notes'])
notebox.size_hint_y = int(args[1]['notes'])*0.66+1e-9
notebox.opacity = int(args[1]['notes'])
options_height: root.tab_option_height
MDBoxLayout:
BGMDBoxLayout:
background_color: BOX_BACKGROUND_COLOR
orientation: 'vertical'
ScrollableLabel:
id: status
size_hint: 1, None
height: min(self.parent.height*0.66,self.label.texture_size[1])
outline_color: RED
background_color: DARKRED
ScrollableLabel:
id: info
markup: True
on_ref_press: root.katrain.board_gui.show_pv_from_comments(args[1])
size_hint: 1, 1
height: self.parent.height - status.height - note.height - 1
TextInput:
id: note
font_size: NOTES_FONT_SIZE
size_hint: 1, 1
on_text: root.katrain.set_note(self.text)
hint_text: i18n._('SGF Notes Hint')
AnchorLayout:
padding: 0
id: notebox
size_hint: 1,0
MDTextField:
id: note
font_size: NOTES_FONT_SIZE
multiline: True
on_text: root.katrain.set_note(self.text)
hint_text: i18n._('SGF Notes Hint')
color_mode: 'custom'
line_color_focus: TEXT_COLOR
size_hint: 1, 1
# mode: "rectangle"
# width: info.width - dp(26)
Label:
size_hint_y: 1e-9 # takes up remaining space if info is closed
@@ -891,19 +903,24 @@
spacing: 3*CP_SPACING
padding: CP_PADDING
height: (self.parent.item_height or 50) * 5
players: {'B':black,'W':white}
PlayerSetup:
player: 'B'
id: black
PlayerSetup:
player: 'W'
id: white
<ContentNavigationDrawer@MDBoxLayout+BackgroundMixin>:
<HamburgerMenuContents@MDBoxLayout+BackgroundMixin>:
katrain: app.gui
player_setup: player_setup
background_color: BOX_BACKGROUND_COLOR
orientation: 'vertical'
item_height: self.height/20
MenuLabel:
text: i18n._('menu:playersetup')
PlayerSetupBlock:
id: player_setup
MenuLabel:
size_hint: 1,1
MenuLabel:
@@ -988,10 +1005,10 @@
PlayAnalyzeButton:
text: i18n._('btn:Play')
id: play
state: 'down'
PlayAnalyzeButton:
text: i18n._('btn:Analysis')
id: analyze
state: 'down'
<KaTrainGui>:
board_gui: board_gui
@@ -1000,6 +1017,7 @@
play_mode: play_mode
analysis_controls: analysis_controls
nav_drawer: nav_drawer
nav_drawer_contents: nav_drawer_contents
zen: False
NavigationLayout:
ScreenManager:
@@ -1039,7 +1057,8 @@
width: self.height * 0.5
height: root.height - analysis_controls.height
swipe_edge_width: 0
ContentNavigationDrawer:
HamburgerMenuContents:
id: nav_drawer_contents
+5 -2
View File
@@ -239,12 +239,15 @@ class PlayerSetup(MDBoxLayout):
self.player_subtype.text = self.player_subtype.values[0]
self.set_player()
@property
def player_def(self):
return {"player_type": self.player_type.selected[1], "player_subtype": self.player_subtype.selected[1]}
def set_player(self):
katrain = MDApp.get_running_app().gui
game = katrain and katrain.game
if game:
game.players[self.player].player_type = self.player_type.selected[1]
game.players[self.player].player_subtype = self.player_subtype.selected[1]
game.players[self.player].update(**self.player_def)
katrain.controls.update_players()
katrain.update_state()
Binary file not shown.
@@ -279,6 +279,9 @@ msgid "new game"
msgstr "Start New Game"
#. misc errors and info messages
msgid "sgf written"
msgstr "SGF with analysis written to {file_name}"
msgid "wait-before-equalize"
msgstr "Wait for initial analysis to complete before requesting a refinement."
@@ -309,3 +312,51 @@ msgid "Engine died unexpectedly"
msgstr ""
"Engine died unexpectedly without sending output, possibly due to out of "
"memory: {e}"
#. AI names, help etc
msgid "strength:kyu"
msgstr "k"
msgid "strength:dan"
msgstr "d"
msgid "ainame:default"
msgstr "KataGo"
msgid "aihelp:default"
msgstr ""
"Full strength KataGo AI. Strength is mainly affected by `max_visits` and "
"`model` in the general settings `engine` section."
msgid "ainame:jigo"
msgstr "KataJigo"
msgid "aihelp:jigo"
msgstr ""
"Will try to win by a set amount of points (default 0.5), without further "
"restrictions."
msgid "ainame:jigo"
msgstr "KataJigo"
msgid "aihelp:jigo"
msgstr ""
"Will try to win by a set amount of points (default 0.5), without further "
"restrictions."
msgid "ainame:scoreloss"
msgstr "Score Loss"
msgid "aihelp:scoreloss"
msgstr ""
"Plays moves that lead to more points lost with lower probability. Likely to "
"play more varied/weaker with higher visits settings."
msgid "ainame:policy"
msgstr "Policy"
msgid "aihelp:policy"
msgstr ""
"Plays the top move from the policy network, without any reading. Strength is"
" mainly affected by `model` in engine settings. The opening moves setting "
"affects how many moves in the opening are more random."
Binary file not shown.
@@ -345,3 +345,56 @@ msgstr "Note that handicaps above 9 are not supported on non-square boards."
#. TODO
msgid "New Game title"
msgstr "Setup New Game"
#. TODO
msgid "sgf written"
msgstr "SGF with analysis written to {file_name}"
#. TODO
msgid "ainame:jigo"
msgstr "KataJigo"
#. TODO
msgid "aihelp:policy"
msgstr ""
"Plays the top move from the policy network, without any reading. Strength is"
" mainly affected by `model` in engine settings. The opening moves setting "
"affects how many moves in the opening are more random."
#. TODO
msgid "ainame:policy"
msgstr "Policy"
#. TODO
msgid "ainame:scoreloss"
msgstr "Score Loss"
#. TODO
msgid "aihelp:default"
msgstr ""
"Full strength KataGo AI. Strength is mainly affected by `max_visits` and "
"`model` in the general settings `engine` section."
#. TODO
msgid "aihelp:jigo"
msgstr ""
"Will try to win by a set amount of points (default 0.5), without further "
"restrictions."
#. TODO
msgid "aihelp:scoreloss"
msgstr ""
"Plays moves that lead to more points lost with lower probability. Likely to "
"play more varied/weaker with higher visits settings."
#. TODO
msgid "ainame:default"
msgstr "KataGo"
#. TODO
msgid "strength:kyu"
msgstr "k"
#. TODO
msgid "strength:dan"
msgstr "d"
Binary file not shown.
@@ -377,3 +377,56 @@ msgstr "Note that handicaps above 9 are not supported on non-square boards."
#. TODO
msgid "New Game title"
msgstr "Setup New Game"
#. TODO
msgid "sgf written"
msgstr "SGF with analysis written to {file_name}"
#. TODO
msgid "ainame:jigo"
msgstr "KataJigo"
#. TODO
msgid "aihelp:policy"
msgstr ""
"Plays the top move from the policy network, without any reading. Strength is"
" mainly affected by `model` in engine settings. The opening moves setting "
"affects how many moves in the opening are more random."
#. TODO
msgid "ainame:policy"
msgstr "Policy"
#. TODO
msgid "ainame:scoreloss"
msgstr "Score Loss"
#. TODO
msgid "aihelp:default"
msgstr ""
"Full strength KataGo AI. Strength is mainly affected by `max_visits` and "
"`model` in the general settings `engine` section."
#. TODO
msgid "aihelp:jigo"
msgstr ""
"Will try to win by a set amount of points (default 0.5), without further "
"restrictions."
#. TODO
msgid "aihelp:scoreloss"
msgstr ""
"Plays moves that lead to more points lost with lower probability. Likely to "
"play more varied/weaker with higher visits settings."
#. TODO
msgid "ainame:default"
msgstr "KataGo"
#. TODO
msgid "strength:kyu"
msgstr "k"
#. TODO
msgid "strength:dan"
msgstr "d"