p+weighted

This commit is contained in:
Sander Land committed 2020-04-26 21:09:40 +02:00
1 parent ba0a1548ee
commit 23077f0805
9 files changed
+110 -46

No files matched your search

+6 -6
View File
@@ -54,10 +54,10 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
elif policy_moves[0][0] > ai_settings["pick_override"]:
aimove = top_policy_move
ai_thoughts += f"Top policy move has weight > {ai_settings['pick_override']:.1%}, so overriding other strategies."
elif top_5_pass:
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."
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.")
elif "noise" in ai_mode:
noise_str = ai_settings["noise_strength"]
d_noise = dirichlet_noise(len(legal_policy_moves))
@@ -65,13 +65,13 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
new_top = heapq.nlargest(5, noisy_policy_moves)
aimove = new_top[0][1]
ai_thoughts += f"Noisy policy strategy (strength={noise_str:.2f}) generated 5 moves {fmt_moves(new_top)} so picked {aimove.gtp()}. "
elif any(keyword in ai_mode for keyword in ["influence", "territory", "local", "tenuki", "pick"]):
elif "p+" in ai_mode:
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: 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: 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 +120,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"] - 0.5))
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:
+1 -1
View File
@@ -44,7 +44,7 @@ engine = KataGoEngine(logger, ENGINE_SETTINGS)
ai_strategy = "P+Influence"
ai_settings["pick_frac"] = 0.5
ai_settings["line_weight"] = 0.1
ai_settings["line_weight"] = 10
ai_strategy = "P+Local"
ai_settings["pick_frac"] = 0.0
Binary file not shown.
+36 -8
View File
@@ -38,14 +38,42 @@
"eval_show_ai": false
},
"ai": {
"Default": {},
"Balance": {"target_score":2, "random_loss": 1, "max_loss": 5, "min_visits": 20},
"P+Noise" : {"noise_strength": 0.8},
"P+Pick": {"pick_override": 0.95, "pick_n":5, "pick_frac": 0.33},
"P+Local": {"pick_override": 0.95, "stddev": 1.5, "pick_n":15, "pick_frac": 0.0},
"P+Tenuki": {"pick_override": 0.95, "stddev": 10, "pick_n":5, "pick_frac": 0.33},
"P+Influence": {"pick_override": 0.95, "pick_n":5, "pick_frac": 0.5,"line_weight": 0.1},
"P+Territory": {"pick_override": 0.95, "pick_n":5, "pick_frac": 0.5,"line_weight": 0.1}
"Default": {"_help_right": "No settings available here, 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_right": "Will try to win by `target_score`, lose at most `random_loss` when behind and `max_loss` when ahead.",
"_help_left": "Never plays moves with less than `min_visits` visits, so also check engine settings."
},
"Jigo": {"target_score": 0.5,
"_help_right": "Will try to win by `target_score`, without further restrictions.",
"_help_left": "Also affected by engine settings such as `max_visits`."
},
"Policy": {"_help_right":"No settings available for this mode, strength is mainly affected by `model` in engine settings, but should be high dan regardless.","_help_left": "" },
"P+Weighted": {"_help_right":"Strength is mainly affected by `model` in engine settings.","_help_left": "`pick_override` determines when top move is chosen without randomness, and is effectively disabled by default (1.0).",
"pick_override": 1.0},
"P+Noise" : {"pick_override": 0.95,"noise_strength": 0.8,
"_help_right": "Adds `noise_strength` noise to the policy and plays the top move.",
"_help_left": "Plays top move if policy value is above `pick_override` to avoid obvious mistakes. Noise above 0.9 is near random, below 0.7 is fairly strong."
},
"P+Pick": {"pick_override": 0.95, "pick_n":5, "pick_frac": 0.33,
"_help_right": "Picks `pick_n + pick_frac * <number of legal moves>` at random and plays the best one. Change `pick_frac` to make it see more moves.",
"_help_left": "Plays top move if policy value is above `pick_override` to avoid obvious mistakes."
},
"P+Local": {"pick_override": 0.95, "stddev": 1.5, "pick_n":15, "pick_frac": 0.0,
"_help_right": "Samples `pick_n + pick_frac * <number of legal moves>` near the last move and plays the best one.",
"_help_left": "Lower `stddev` makes it prefer closer moves."
},
"P+Tenuki": {"pick_override": 0.95, "stddev": 10, "pick_n":5, "pick_frac": 0.25,
"_help_right": "Samples `pick_n + pick_frac * <number of legal moves>` away from the last move and plays the best one.",
"_help_left": "Increase `stddev` makes it prefer moves further away."
},
"P+Influence": {"pick_override": 0.95, "pick_n":5, "pick_frac": 0.4,"line_weight": 10,
"_help_right": "Samples `pick_n + pick_frac * <number of legal moves>` and plays the best one, biased towards the center.",
"_help_left": "Increase `line_weight` to penalize moves near the edge more."
},
"P+Territory": {"pick_override": 0.95, "pick_n":5, "pick_frac": 0.4,"line_weight": 10,
"_help_right": "Samples `pick_n + pick_frac * <number of legal moves>` and plays the best one, biased towards the edge.",
"_help_left": "Increase `line_weight` to penalize moves closer to the center more."
}
},
"board_ui": {
"starpoint_size": 0.1,
+2
View File
@@ -24,6 +24,8 @@ class DarkLabel(Label):
class ScaledLightLabel(DarkLabel):
pass
class LightHelpLabel(ScaledLightLabel):
pass
class StyledButton(Button):
button_color = ListProperty([])
+41 -14
View File
@@ -8,7 +8,7 @@ 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
from gui.kivyutils import LabelledCheckBox, LabelledFloatInput, LabelledIntInput, LabelledObjectInputArea, LabelledSpinner, LabelledTextInput, ScaledLightLabel, StyledButton, LightHelpLabel
class InputParseError(Exception):
@@ -16,8 +16,8 @@ class InputParseError(Exception):
class QuickConfigGui(BoxLayout):
def __init__(self, katrain, popup, initial_values=None):
super().__init__()
def __init__(self, katrain, popup, initial_values=None,**kwargs):
super().__init__(**kwargs)
self.katrain = katrain
self.popup = popup
if initial_values:
@@ -65,7 +65,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)
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 +82,11 @@ class NewGamePopup(QuickConfigGui):
class ConfigPopup(QuickConfigGui):
def __init__(self, katrain, popup, config, ignore_cats):
def __init__(self, katrain, popup, config, ignore_cats,**kwargs):
self.config = config
self.ignore_cats = ignore_cats
self.orientation = "vertical"
super().__init__(katrain, popup)
super().__init__(katrain, popup,**kwargs)
Clock.schedule_once(self._build, 0)
def _build(self, _):
@@ -161,23 +161,50 @@ class ConfigPopup(QuickConfigGui):
class ConfigAIPopup(QuickConfigGui):
def __init__(self, katrain, popup, ai_modes, **kwargs):
super().__init__(katrain, popup, katrain.ai_settings, **kwargs)
self.settings = self.katrain.ai_settings
super().__init__(katrain, popup, self.settings, **kwargs)
self.ai_modes = ai_modes
Clock.schedule_once(self._build, 0)
self.orientation ='vertical'
def _build(self):
def _build(self,_dt):
colbox = BoxLayout(spacing=5)
for mode in self.ai_modes:
mode_settings = self.settings[mode]
column = GridLayout(rows=2 + len(mode_settings), columns=2, size_hint=(0.5, 1))
column.add_widget(ScaledLightLabel(text=f"Settings for AI {mode}", bold=True))
num_rows = len(mode_settings) - 2
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', '')))
for k, v in mode_settings.items():
if not k.startswith("_"):
column.add_widget(ScaledLightLabel(text=f"{k}"))
column.add_widget(ConfigPopup.type_to_widget_class(v)(text=str(v), input_property=f"{mode}/{k}"))
for _ in range(4 - num_rows):
column.add_widget(ScaledLightLabel(text=f""))
column.add_widget(ScaledLightLabel(text=f""))
colbox.add_widget(column)
if len(self.ai_modes)==1:
colbox.add_widget(ScaledLightLabel(text=f""))
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)
self.add_widget(bl)
column.add_widget(Label(text=f"Settings for AI {mode}", bold=True))
def update_config(self, save_to_file=False):
try:
for k, v in self.collect_properties(self).items():
k1, k2 = k.split("/")
print(k1,k2,v,self.settings[k1][k2])
if self.settings[k1][k2] != v:
self.katrain.log(f"Updating setting {k} = {v}", OUTPUT_DEBUG)
self.popup.dismiss()
except InputParseError as e:
self.info_label.text = str(e)
self.katrain.log(e, OUTPUT_ERROR)
return
self.add_widget(column)
def on_submit(self):
self.popup.dismiss()
+9 -13
View File
@@ -1,8 +1,6 @@
#:kivy 1.11.0
#:import ew kivy.uix.effectwidget
#:set AI_MODES ['Default','Balance','Jigo','Policy','P+Pick','P+Local','P+Tenuki','P+Territory','P+Influence','P+Noise','<Pause>']
#:set PLAYER_MODES ['Human', 'Teach','AI:']
#:set PLAYER_MODE_VALUES ['human','human+undo','ai']
#:set BUTTON_COLOR [0.23, 0.30, 0.35, 1]
@@ -91,6 +89,12 @@
color: WHITE
font_size: 0.45*self.size[1]
<LightHelpLabel>:
font_size: self.size[1]/10
halign: 'center'
valign: 'center'
multiline: True
<LabelledTextArea>
font_size: 0.2*self.size[1]
@@ -478,7 +482,7 @@
StyledButton:
text: 'Configure AIs'
on_press: root.configure_ais()
size_hint: 0.45,1
size_hint: 0.44,1
Label:
size_hint: None,1
width: 3
@@ -657,21 +661,13 @@
input_property: 'RU'
id: rules_spinner
Label:
ScaledLightLabel:
font_size: self.size[1]/8
LightHelpLabel:
text: "Use x:y (e.g. 19:9) to play on a non-square board."
size_hint: 1,2
halign: 'center'
valign: 'center'
multiline: True
text_size: self.width-6, None
id: info
ScaledLightLabel:
LightHelpLabel:
text: "Note that handicaps above 9 are not supported on non-square boards."
font_size: self.size[1]/8
halign: 'center'
valign: 'center'
multiline: True
text_size: self.width-6, None
id: info
StyledButton:
+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"))
popup_contents = ConfigPopup(self, config_popup, dict(self._config), ignore_cats=("board_ui","ai"))
config_popup.add_widget(popup_contents)
config_popup.open()
+14 -3
View File
@@ -45,7 +45,7 @@ class AI:
"pick_n": 10,
"pick_frac": 0.2,
"local_stddev": 10,
"line_weight": 0.1,
"line_weight": 10,
"pick_override": 0.95,
}
IGNORE_SETTINGS_IN_TAG = {"threads", "enable_ownership", "katago"} # katago for switching from/to bs version
@@ -133,8 +133,8 @@ test_ais = [
AI("P+Pick", {"pick_frac": 0.5, "pick_n": 0}),
AI("P+Influence", {"pick_frac": 0.2, "pick_n": 20}),
AI("P+Territory", {"pick_frac": 0.2, "pick_n": 20}),
AI("P+Influence", {"pick_frac": 0.33, "line_weight": 0.05}),
AI("P+Territory", {"pick_frac": 0.33, "line_weight": 0.05}),
AI("P+Influence", {"pick_frac": 0.33, "line_weight": 20}),
AI("P+Territory", {"pick_frac": 0.33, "line_weight": 20}),
AI("P+Pick", {"pick_frac": 0.0, "pick_n": 1}),
AI("P+Tenuki", {"local_stddev": 20}),
AI("P+Tenuki", {"local_stddev": 10}),
@@ -145,6 +145,17 @@ test_ais = [
AI("P+Local", {"local_stddev": 1, "pick_frac": 0.0, "pick_n": 20}),
]
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", {}),
# AI("P+Noise", {"noise_strength": 0.4}),