Merge branch 'v1.0' of github.com:sanderland/katrain into v1.0

This commit is contained in:
Sander Land committed 2020-04-26 23:40:04 +02:00
commit be05459a48
13 files changed
+52 -46

No files matched your search

+1
View File
@@ -9,6 +9,7 @@ log.txt
*.sgf
sgfout
sgf_selfplay
sgf_ogs
log*
tmp.pickle
my
+6 -5
View File
@@ -56,8 +56,9 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
ai_thoughts += f"Top policy move has weight > {ai_settings['pick_override']:.1%}, so overriding other strategies."
elif top_5_pass or "weighted" in ai_mode:
weighted_coords = [(policy_grid[y][x], policy_grid[y][x], x, y) for x in range(size[0]) for y in range(size[1]) if policy_grid[y][x] > 0]
aimove = Move(weighted_selection_without_replacement(weighted_coords, 1)[0][2:], player=cn.next_player) # just take a random move by policy w/o noise
ai_thoughts += f"Playing policy-weighted random move {aimove.gtp()}" + (" because one of them is pass." if top_5_pass else " because strategy is weighted.")
best = weighted_selection_without_replacement(weighted_coords, 1)[0]
aimove = Move(best[2:], player=cn.next_player) # just take a random move by policy w/o noise
ai_thoughts += f"Playing policy-weighted random move {aimove.gtp()} ({best[0]:.1%})" + (" because one of them is pass." if top_5_pass else " because strategy is weighted.")
elif "noise" in ai_mode:
noise_str = ai_settings["noise_strength"]
d_noise = dirichlet_noise(len(legal_policy_moves))
@@ -69,9 +70,9 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
n_moves = int(ai_settings["pick_frac"] * len(legal_policy_moves) + ai_settings["pick_n"])
if "influence" in ai_mode or "territory" in ai_mode:
if "influence" in ai_mode:
weight = lambda x, y: (1/ai_settings["line_weight"]) ** max(0, 3 - min(size[0] - 1 - x, x, y, size[1] - 1 - y))
weight = lambda x, y: (1 / ai_settings["line_weight"]) ** max(0, 3 - min(size[0] - 1 - x, x, y, size[1] - 1 - y))
else:
weight = lambda x, y:(1/ai_settings["line_weight"]) ** max(0, min(size[0] - 1 - x, x, y, size[1] - 1 - y) - 2)
weight = lambda x, y: (1 / ai_settings["line_weight"]) ** max(0, min(size[0] - 1 - x, x, y, size[1] - 1 - y) - 2)
weighted_coords = [(policy_grid[y][x] * weight(x, y), weight(x, y), x, y) for x in range(size[0]) for y in range(size[1]) if policy_grid[y][x] > 0]
ai_thoughts += f"Generated weights for {ai_mode} according to weight factor {ai_settings['line_weight']} and distance from 4th line. "
elif "local" in ai_mode or "tenuki" in ai_mode:
@@ -120,7 +121,7 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
ai_thoughts += f"Balance strategy selected moves {sel_moves} based on target score and max points lost, and randomly chose {aimove.gtp()}."
elif "jigo" in ai_mode and candidate_ai_moves[0]["move"] != "pass":
sign = cn.player_sign(cn.next_player) # TODO check
jigo_move = min(candidate_ai_moves, key=lambda move: abs(sign * move["scoreLead"] - ai_settings['target_score']))
jigo_move = min(candidate_ai_moves, key=lambda move: abs(sign * move["scoreLead"] - ai_settings["target_score"]))
aimove = Move.from_gtp(jigo_move["move"], player=cn.next_player)
ai_thoughts += f"Jigo strategy found candidate moves {candidate_ai_moves} moves and chose {aimove.gtp()} as closest to 0.5 point win"
else:
+2 -9
View File
@@ -27,14 +27,7 @@ ENGINE_SETTINGS = {
"enable_ownership": False,
"threads": 1,
}
ai_settings = {
"noise_strength": 0.8,
"pick_n": 10,
"pick_frac": 0.2,
"stddev": 10,
"line_weight": 10,
"pick_override": 0.95,
}
ai_settings = {"noise_strength": 0.8, "pick_n": 10, "pick_frac": 0.2, "stddev": 10, "line_weight": 10, "pick_override": 0.95}
engine = KataGoEngine(logger, ENGINE_SETTINGS)
@@ -53,7 +46,7 @@ ai_settings["pick_n"] = 5 # dropping below 7k at 5/0.33
ai_strategy = "P+Weighted"
ai_settings = {"pick_override": 0.95}
ai_settings = {"pick_override": 0.95}
ai_strategy = "P+Local"
ai_settings["pick_frac"] = 0.0
Binary file not shown.
+1 -1
View File
@@ -80,7 +80,7 @@ class KataGoEngine:
self.katrain.log(f"Query result {analysis['id']} discarded -- recent new game?", OUTPUT_DEBUG)
continue
if "error" in analysis:
if not (next_move is None and "Illegal move" in analysis["error"]): # sweep
if not (next_move and "Illegal move" in analysis["error"]): # sweep
self.katrain.log(f"{analysis} received from KataGo", OUTPUT_ERROR)
continue
else:
+8 -1
View File
@@ -5,6 +5,7 @@ from datetime import datetime
from typing import List, Union, Dict
import threading
from common import var_to_grid
from engine import KataGoEngine
from game_node import GameNode
from sgf_parser import SGF, Move
@@ -224,7 +225,13 @@ class Game:
return
elif mode == "sweep":
board_size_x, board_size_y = self.board_size
analyze_moves = [Move(coords=(x, y), player=cn.next_player) for x in range(board_size_x) for y in range(board_size_y) if (x, y) not in stones]
policy_grid = var_to_grid(self.current_node.policy, size=(board_size_x, board_size_y)) if self.current_node.policy else None
analyze_moves = [
Move(coords=(x, y), player=cn.next_player)
for x in range(board_size_x)
for y in range(board_size_y)
if (policy_grid is None and (x, y) not in stones) or policy_grid[y][x] >= 0
]
visits = int(engine.config["max_visits"] * self.config["sweep_visits_frac"] + 0.5)
self.katrain.controls.set_status(f"Refining analysis of entire board to {visits} visits")
priority = -1_000_000_000
+3 -2
View File
@@ -43,7 +43,8 @@ class GameNode(SGFNode):
def set_analysis(self, analysis_json, refine_move):
if refine_move:
self.update_move_analysis(analysis_json["rootInfo"], refine_move.gtp())
pvtail = analysis_json["moveInfos"][0]["pv"] if analysis_json["moveInfos"] else []
self.update_move_analysis({"pv": [refine_move.gtp()] + pvtail, **analysis_json["rootInfo"]}, refine_move.gtp())
else:
for move_analysis in analysis_json["moveInfos"]:
self.update_move_analysis(move_analysis, move_analysis["move"])
@@ -134,7 +135,7 @@ class GameNode(SGFNode):
def policy_ranking(self) -> Optional[List[Tuple[float, Move]]]: # return moves from highest policy value to lowest
if self.policy:
szx, szy = self.board_size
policy_grid = var_to_grid(self.policy, size=[szx, szy])
policy_grid = var_to_grid(self.policy, size=(szx, szy))
moves = [(policy_grid[y][x], Move((x, y), player=self.next_player)) for x in range(szx) for y in range(szy)]
moves.append((self.policy[-1], Move(None, player=self.next_player)))
return sorted(moves, key=lambda mp: -mp[0])
+1 -1
View File
@@ -64,7 +64,7 @@ class Controls(BoxLayout):
self.win_rate.text = current_node.format_win_rate()
if move and next_player_is_human_or_both_robots: # don't immediately hide this when an ai moves comes in
points_lost = current_node.points_lost
self.score_change.label = f"Points lost for {move.player}" if points_lost and points_lost > 0 else f"Points gained for {move.player}"
self.score_change.label = f"Points lost ({move.player})" if points_lost and points_lost > 0 else f"Points gained ({move.player})"
self.score_change.text = f"{abs(points_lost):.1f}" if points_lost else "..."
elif not current_player_is_ai_playing_human:
self.score_change.label = f"Points lost"
+3 -1
View File
@@ -24,9 +24,11 @@ class DarkLabel(Label):
class ScaledLightLabel(DarkLabel):
pass
class LightHelpLabel(ScaledLightLabel):
pass
class StyledButton(Button):
button_color = ListProperty([])
button_color_down = ListProperty([])
@@ -79,7 +81,7 @@ class ToggleButtonContainer(GridLayout):
def _size(self, _dt):
if self.autosize:
for tb in self.children:
tb.size_hint = (tb.texture_size[0] + 25, 1)
tb.size_hint = (tb.texture_size[0] + 10, 1)
@property
def value(self):
+24 -14
View File
@@ -8,7 +8,17 @@ from kivy.uix.label import Label
from common import OUTPUT_DEBUG, OUTPUT_ERROR
from engine import KataGoEngine
from game import Game, GameNode
from gui.kivyutils import LabelledCheckBox, LabelledFloatInput, LabelledIntInput, LabelledObjectInputArea, LabelledSpinner, LabelledTextInput, ScaledLightLabel, StyledButton, LightHelpLabel
from gui.kivyutils import (
LabelledCheckBox,
LabelledFloatInput,
LabelledIntInput,
LabelledObjectInputArea,
LabelledSpinner,
LabelledTextInput,
ScaledLightLabel,
StyledButton,
LightHelpLabel,
)
class InputParseError(Exception):
@@ -16,7 +26,7 @@ class InputParseError(Exception):
class QuickConfigGui(BoxLayout):
def __init__(self, katrain, popup, initial_values=None,**kwargs):
def __init__(self, katrain, popup, initial_values=None, **kwargs):
super().__init__(**kwargs)
self.katrain = katrain
self.popup = popup
@@ -65,7 +75,7 @@ class LoadSGFPopup(BoxLayout):
class NewGamePopup(QuickConfigGui):
def __init__(self, katrain, popup, properties, **kwargs):
properties["RU"] = KataGoEngine.get_rules(katrain.game.root)
super().__init__(katrain, popup, properties,**kwargs)
super().__init__(katrain, popup, properties, **kwargs)
self.rules_spinner.values = list(set(self.katrain.engine.RULESETS.values()))
self.rules_spinner.text = properties["RU"]
@@ -82,11 +92,11 @@ class NewGamePopup(QuickConfigGui):
class ConfigPopup(QuickConfigGui):
def __init__(self, katrain, popup, config, ignore_cats,**kwargs):
def __init__(self, katrain, popup, config, ignore_cats, **kwargs):
self.config = config
self.ignore_cats = ignore_cats
self.orientation = "vertical"
super().__init__(katrain, popup,**kwargs)
super().__init__(katrain, popup, **kwargs)
Clock.schedule_once(self._build, 0)
def _build(self, _):
@@ -161,22 +171,22 @@ class ConfigPopup(QuickConfigGui):
class ConfigAIPopup(QuickConfigGui):
def __init__(self, katrain, popup, ai_modes, **kwargs):
super().__init__(katrain, popup, katrain.ai_settings, **kwargs)
super().__init__(katrain, popup, katrain.ai_settings, **kwargs)
self.settings = self.katrain.ai_settings
self.ai_modes = ai_modes
Clock.schedule_once(self._build, 0)
self.orientation ='vertical'
self.orientation = "vertical"
def _build(self,_dt):
def _build(self, _dt):
colbox = BoxLayout(spacing=5)
for mode in self.ai_modes:
mode_settings = self.settings[mode]
num_rows = len(mode_settings) - 2
column = GridLayout(cols=2, rows=max(num_rows,4) + 3,spacing=1,padding=3)
column = GridLayout(cols=2, rows=max(num_rows, 4) + 3, spacing=1, padding=3)
column.add_widget(ScaledLightLabel(text=f"Settings for AI"))
column.add_widget(ScaledLightLabel(text=f"{mode}", bold=True))
column.add_widget(LightHelpLabel(size_hint=(1,3),text=mode_settings.get('_help_left','')))
column.add_widget(LightHelpLabel(size_hint=(1,3),text=mode_settings.get('_help_right', '')))
column.add_widget(LightHelpLabel(size_hint=(1, 3), text=mode_settings.get("_help_left", "")))
column.add_widget(LightHelpLabel(size_hint=(1, 3), text=mode_settings.get("_help_right", "")))
for k, v in mode_settings.items():
if not k.startswith("_"):
column.add_widget(ScaledLightLabel(text=f"{k}"))
@@ -185,9 +195,9 @@ class ConfigAIPopup(QuickConfigGui):
column.add_widget(ScaledLightLabel(text=f""))
column.add_widget(ScaledLightLabel(text=f""))
colbox.add_widget(column)
if len(self.ai_modes)==1:
if len(self.ai_modes) == 1:
colbox.add_widget(ScaledLightLabel(text=f""))
bl = BoxLayout(size_hint=(1,0.2),spacing=2)
bl = BoxLayout(size_hint=(1, 0.2), spacing=2)
bl.add_widget(StyledButton(text=f"Apply", on_press=lambda _: self.update_config(False)))
bl.add_widget(StyledButton(text=f"Apply and Save", on_press=lambda _: self.update_config(True)))
self.add_widget(colbox)
@@ -197,7 +207,7 @@ class ConfigAIPopup(QuickConfigGui):
try:
for k, v in self.collect_properties(self).items():
k1, k2 = k.split("/")
print(k1,k2,v,self.settings[k1][k2])
print(k1, k2, v, self.settings[k1][k2])
if self.settings[k1][k2] != v:
self.katrain.log(f"Updating setting {k} = {v}", OUTPUT_DEBUG)
+1 -1
View File
@@ -36,7 +36,7 @@
<StyledSpinner>:
text: self.values[0] if self.values else ''
font_size: self.size[1] * 0.35
font_size: self.size[1] * 0.33
sync_height: True
background_color: [*[c*255/88 for c in BUTTON_COLOR[:3]], 1] # compensate for texture
option_cls: 'StyledSpinnerOption'
+1 -1
View File
@@ -193,7 +193,7 @@ class KaTrainGui(BoxLayout):
def _do_config_popup(self):
config_popup = Popup(title="Edit Settings", size_hint=(0.9, 0.9))
popup_contents = ConfigPopup(self, config_popup, dict(self._config), ignore_cats=("board_ui","ai"))
popup_contents = ConfigPopup(self, config_popup, dict(self._config), ignore_cats=("board_ui", "ai"))
config_popup.add_widget(popup_contents)
config_popup.open()
+1 -10
View File
@@ -143,18 +143,9 @@ test_ais = [
AI("P+Local", {"local_stddev": 5}),
AI("P+Local", {"local_stddev": 1}),
AI("P+Local", {"local_stddev": 1, "pick_frac": 0.0, "pick_n": 20}),
AI("P+Weighted", {"pick_override": 1.0}),
]
test_ais = [
AI("Policy", {}),
AI("P+Weighted", {'pick_override':1.0}),
AI("P+Noise", {"noise_strength": 0.6}),
AI("P+Noise", {"noise_strength": 0.7}),
AI("P+Noise", {"noise_strength": 0.8}),
AI("P+Noise", {"noise_strength": 0.9}),
]
# test_ais = [
# AI("Policy", {}),