Merge branch 'v1.0' of github.com:sanderland/katrain into v1.0
This commit is contained in:
commit
be05459a48
13 files changed
+52
-46
No files matched your search
@@ -9,6 +9,7 @@ log.txt
|
||||
*.sgf
|
||||
sgfout
|
||||
sgf_selfplay
|
||||
sgf_ogs
|
||||
log*
|
||||
tmp.pickle
|
||||
my
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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", {}),
|
||||
|
||||
Reference in new issue
Block a user