p+weighted
This commit is contained in:
1 parent
ba0a1548ee
commit
23077f0805
9 files changed
+110
-46
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -24,6 +24,8 @@ class DarkLabel(Label):
|
||||
class ScaledLightLabel(DarkLabel):
|
||||
pass
|
||||
|
||||
class LightHelpLabel(ScaledLightLabel):
|
||||
pass
|
||||
|
||||
class StyledButton(Button):
|
||||
button_color = ListProperty([])
|
||||
|
||||
+41
-14
@@ -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
@@ -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
@@ -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
@@ -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}),
|
||||
|
||||
Reference in new issue
Block a user