simple
This commit is contained in:
1 parent
8e0001b771
commit
28b8748e5f
23 files changed
+171
-43
No files matched your search
+1
-1
@@ -351,7 +351,7 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
self.controls.timer.paused = True
|
||||
if not self.ai_settings_popup:
|
||||
self.ai_settings_popup = I18NPopup(
|
||||
title_key="ai settings", size=[dp(600), dp(650)], content=ConfigAIPopup(self)
|
||||
title_key="ai settings", size=[dp(750), dp(750)], content=ConfigAIPopup(self)
|
||||
).__self__
|
||||
self.ai_settings_popup.content.popup = self.ai_settings_popup
|
||||
self.ai_settings_popup.open()
|
||||
|
||||
+3
-1
@@ -92,7 +92,9 @@
|
||||
"max_points_lost": 1.75,
|
||||
"settled_weight": 1.0,
|
||||
"opponent_fac": 0.5,
|
||||
"min_visits": 3
|
||||
"min_visits": 3,
|
||||
"attach_penalty": 1,
|
||||
"tenuki_penalty": 0.5
|
||||
},
|
||||
"ai:p:weighted": {
|
||||
"weaken_fac": 1.25,
|
||||
|
||||
+32
-31
@@ -18,7 +18,6 @@ from katrain.core.constants import (
|
||||
AI_RANK,
|
||||
AI_SCORELOSS,
|
||||
AI_SCORELOSS_ELO,
|
||||
AI_SIMPLE,
|
||||
AI_SIMPLE_OWNERSHIP,
|
||||
AI_STRATEGIES_PICK,
|
||||
AI_STRATEGIES_POLICY,
|
||||
@@ -205,13 +204,6 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
|
||||
game.katrain.log(f"Error getting handicap-based move", OUTPUT_ERROR)
|
||||
ai_mode = AI_DEFAULT
|
||||
|
||||
if ai_mode == AI_SIMPLE:
|
||||
simple_moves = ai_settings["simple_moves"]
|
||||
simple_analysis = request_ai_analysis(game, cn, {"simpleMovesBias": simple_moves, "wideRootNoise": 0.10})
|
||||
if not simple_analysis:
|
||||
game.katrain.log(f"Error getting simple-biased move", OUTPUT_ERROR)
|
||||
ai_mode = AI_DEFAULT
|
||||
|
||||
while not cn.analysis_ready:
|
||||
time.sleep(0.01)
|
||||
game.engines[cn.next_player].check_alive(exception_if_dead=True)
|
||||
@@ -335,18 +327,6 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
|
||||
candidate_ai_moves = cn.candidate_moves
|
||||
if ai_mode == AI_HANDICAP:
|
||||
candidate_ai_moves = handicap_analysis["moveInfos"]
|
||||
if ai_mode == AI_SIMPLE:
|
||||
candidate_ai_moves = simple_analysis["moveInfos"]
|
||||
for data in candidate_ai_moves:
|
||||
print(
|
||||
"{order} {move}: visits {visits} utility {util} utilityLcb {lcb}".format(
|
||||
visits=data["visits"],
|
||||
order=data["order"],
|
||||
move=data["move"],
|
||||
util=data["utility"],
|
||||
lcb=data["utilityLcb"],
|
||||
)
|
||||
)
|
||||
|
||||
top_cand = Move.from_gtp(candidate_ai_moves[0]["move"], player=cn.next_player)
|
||||
if top_cand.is_pass and ai_mode not in [
|
||||
@@ -377,31 +357,54 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
|
||||
aimove = topmove[2]
|
||||
ai_thoughts += f"ScoreLoss strategy found {len(candidate_ai_moves)} candidate moves (best {top_cand.gtp()}) and chose {aimove.gtp()} (weight {topmove[1]:.3f}, point loss {topmove[0]:.1f}) based on score weights."
|
||||
elif ai_mode == AI_SIMPLE_OWNERSHIP:
|
||||
|
||||
def settledness(d, player_fac):
|
||||
return sum([abs(o) for o in d["ownership"] if player_fac * o > 0])
|
||||
|
||||
last_player_stones = {s.coords for s in game.stones if s.player == cn.player}
|
||||
next_player_sign = cn.player_sign(cn.next_player)
|
||||
|
||||
def settledness(d, player_sign):
|
||||
return sum([abs(o) for o in d["ownership"] if player_sign * o > 0])
|
||||
|
||||
def is_attachment(d):
|
||||
return any(
|
||||
(d.coords[0] + dx, d.coords[1] + dy) in last_player_stones
|
||||
for dx in [-1, 0, 1]
|
||||
for dy in [-1, 0, 1]
|
||||
if abs(dx) + abs(dy) == 1
|
||||
)
|
||||
|
||||
def is_tenuki(d):
|
||||
return not any(
|
||||
not node
|
||||
or not node.move
|
||||
or node.move.is_pass
|
||||
or max(abs(last_c - cand_c) for last_c, cand_c in zip(node.move.coords, d.coords)) < 5
|
||||
for node in [cn, cn.parent]
|
||||
)
|
||||
|
||||
moves_with_settledness = sorted(
|
||||
[
|
||||
(
|
||||
Move.from_gtp(d["move"], player=cn.next_player),
|
||||
move,
|
||||
settledness(d, next_player_sign),
|
||||
settledness(d, -next_player_sign),
|
||||
is_attachment(move),
|
||||
is_tenuki(move),
|
||||
d,
|
||||
)
|
||||
for d in candidate_ai_moves
|
||||
if d["pointsLost"] < ai_settings["max_points_lost"]
|
||||
and "ownership" in d
|
||||
and (d["order"] < 5 or d["visits"] >= ai_settings.get("min_visits", 1))
|
||||
for move in [Move.from_gtp(d["move"], player=cn.next_player)]
|
||||
],
|
||||
key=lambda t: t[3]["pointsLost"]
|
||||
key=lambda t: t[5]["pointsLost"]
|
||||
+ ai_settings["attach_penalty"] * t[3]
|
||||
+ ai_settings["tenuki_penalty"] * t[4]
|
||||
- ai_settings["settled_weight"] * (t[1] + ai_settings["opponent_fac"] * t[2]),
|
||||
)
|
||||
if moves_with_settledness:
|
||||
cands = [
|
||||
f"{move.gtp()} ({d['pointsLost']:.1f} pt lost, {d['visits']} visits, {settled:.1f} settledness, {oppsettled:.1f} opponent settledness)"
|
||||
for move, settled, oppsettled, d in moves_with_settledness[:5]
|
||||
f"{move.gtp()} ({d['pointsLost']:.1f} pt lost, {d['visits']} visits, {settled:.1f} settledness, {oppsettled:.1f} opponent settledness{', attachment' if isattach else ''}{', tenuki' if istenuki else ''})"
|
||||
for move, settled, oppsettled, isattach, istenuki, d in moves_with_settledness[:5]
|
||||
]
|
||||
ai_thoughts += f"Simple ownership strategy. Top 5 Candidates {', '.join(cands)} "
|
||||
aimove = moves_with_settledness[0][0]
|
||||
@@ -411,14 +414,12 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
|
||||
)
|
||||
aimove = top_cand
|
||||
else:
|
||||
if ai_mode not in [AI_DEFAULT, AI_HANDICAP, AI_SIMPLE]:
|
||||
if ai_mode not in [AI_DEFAULT, AI_HANDICAP]:
|
||||
game.katrain.log(f"Unknown AI mode {ai_mode} or policy missing, using default.", OUTPUT_INFO)
|
||||
ai_thoughts += f"Strategy {ai_mode} not found or unexpected fallback."
|
||||
aimove = top_cand
|
||||
if ai_mode == AI_HANDICAP:
|
||||
ai_thoughts += f"Handicap strategy found {len(candidate_ai_moves)} moves returned from the engine and chose {aimove.gtp()} as top move. PDA based score {cn.format_score(handicap_analysis['rootInfo']['scoreLead'])} and win rate {cn.format_winrate(handicap_analysis['rootInfo']['winrate'])}"
|
||||
elif ai_mode == AI_SIMPLE:
|
||||
ai_thoughts += f"Simple moves strategy found {len(candidate_ai_moves)} moves returned from the engine and chose {aimove.gtp()} as top move. "
|
||||
else:
|
||||
ai_thoughts += f"Default strategy found {len(candidate_ai_moves)} moves returned from the engine and chose {aimove.gtp()} as top move"
|
||||
game.katrain.log(f"AI thoughts: {ai_thoughts}", OUTPUT_DEBUG)
|
||||
|
||||
@@ -33,12 +33,11 @@ AI_TENUKI = "ai:p:tenuki"
|
||||
AI_INFLUENCE = "ai:p:influence"
|
||||
AI_TERRITORY = "ai:p:territory"
|
||||
AI_RANK = "ai:p:rank"
|
||||
AI_SIMPLE = "ai:disabled"
|
||||
AI_SIMPLE_OWNERSHIP = "ai:simple"
|
||||
|
||||
AI_CONFIG_DEFAULT = AI_RANK
|
||||
|
||||
AI_STRATEGIES_ENGINE = [AI_DEFAULT, AI_HANDICAP, AI_SIMPLE, AI_SCORELOSS, AI_JIGO]
|
||||
AI_STRATEGIES_ENGINE = [AI_DEFAULT, AI_HANDICAP, AI_SCORELOSS, AI_JIGO]
|
||||
AI_STRATEGIES_PICK = [AI_PICK, AI_LOCAL, AI_TENUKI, AI_INFLUENCE, AI_TERRITORY, AI_RANK]
|
||||
AI_STRATEGIES_POLICY = [AI_WEIGHTED, AI_POLICY] + AI_STRATEGIES_PICK
|
||||
AI_STRATEGIES = AI_STRATEGIES_ENGINE + AI_STRATEGIES_POLICY
|
||||
@@ -90,11 +89,13 @@ AI_OPTION_VALUES = {
|
||||
"automatic": "bool",
|
||||
"pda": [(x / 10, f"{'W' if x<0 else 'B'}+{abs(x/10):.1f}") for x in range(-30, 31)],
|
||||
"max_points_lost": [x / 10 for x in range(51)],
|
||||
"settled_weight": [x / 4 for x in range(1, 17)],
|
||||
"settled_weight": [x / 4 for x in range(0, 17)],
|
||||
"opponent_fac": [x / 10 for x in range(-20, 11)],
|
||||
"min_visits": range(1,10)
|
||||
"min_visits": range(1, 10),
|
||||
"attach_penalty": [x / 10 for x in range(-10, 51)],
|
||||
"tenuki_penalty": [x / 10 for x in range(-10, 51)],
|
||||
}
|
||||
AI_KEY_PROPERTIES = {"kyu_rank", "strength", "weaken_fac", "pick_frac", "pick_n", "automatic"}
|
||||
AI_KEY_PROPERTIES = {"kyu_rank", "strength", "weaken_fac", "pick_frac", "pick_n", "automatic","max_points_lost","min_visits"}
|
||||
|
||||
|
||||
CALIBRATED_RANK_ELO = [
|
||||
|
||||
@@ -211,11 +211,12 @@ class GameNode(SGFNode):
|
||||
top_pv = self.analysis_ready and self.candidate_moves[0].get("pv")
|
||||
if top_pv:
|
||||
text += i18n._("Info:undo predicted PV").format(pv=f"{self.next_player}{' '.join(top_pv)}") + "\n"
|
||||
if self.ai_thoughts and (sgf or details):
|
||||
text += "\n" + i18n._("Info:AI thoughts").format(thoughts=self.ai_thoughts)
|
||||
else:
|
||||
text = i18n._("No analysis available") if sgf else i18n._("Analyzing move...")
|
||||
|
||||
if self.ai_thoughts and (sgf or details):
|
||||
text += "\n" + i18n._("Info:AI thoughts").format(thoughts=self.ai_thoughts)
|
||||
|
||||
if "C" in self.properties:
|
||||
text += "\n[u]SGF Comments:[/u]\n" + "\n".join(self.properties["C"])
|
||||
|
||||
|
||||
@@ -342,7 +342,7 @@ class ConfigAIPopup(QuickConfigGui):
|
||||
self.options_grid.clear_widgets()
|
||||
self.help_label.text = i18n._(strategy.replace("ai:", "aihelp:"))
|
||||
for k, v in sorted(mode_settings.items(), key=lambda kv: (kv[0] not in AI_KEY_PROPERTIES, kv[0])):
|
||||
self.options_grid.add_widget(DescriptionLabel(text=k, size_hint_x=0.25))
|
||||
self.options_grid.add_widget(DescriptionLabel(text=k, size_hint_x=0.275))
|
||||
if k in AI_OPTION_VALUES:
|
||||
values = AI_OPTION_VALUES[k]
|
||||
if values == "bool":
|
||||
|
||||
Binary file not shown.
@@ -652,3 +652,20 @@ msgstr "Think for at least {num} seconds before playing."
|
||||
#. TODO - in minutes
|
||||
msgid "main time"
|
||||
msgstr "Main time (min.)"
|
||||
|
||||
#. TODO - TODO
|
||||
msgid "aihelp:simple"
|
||||
msgstr ""
|
||||
"Plays moves that lead to simplifying the board state. Of moves that lose at "
|
||||
"most 'max_points_lost' points and with at least 'min_visits', prefer moves "
|
||||
"that lose fewer points, settle own territory more (with importance "
|
||||
"settled_weight), settle opponent's territory more (importance settled_weight"
|
||||
" * opponent fac), avoid tenuki (more than 5 spaces, importance "
|
||||
"tenuki_penalty), and avoid attachments (importance attachment_penalty). "
|
||||
"Weights can be negative to make it complicate the game instead, andwide root"
|
||||
" noise (in general settings) can be used to further play according to the "
|
||||
"style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:simple"
|
||||
msgstr "Simple Style"
|
||||
Binary file not shown.
@@ -696,8 +696,25 @@ msgstr "Minimal time use in byo-yomi (s)"
|
||||
|
||||
#. TODO
|
||||
msgid "move too fast"
|
||||
msgstr "Think for at least %d seconds before playing."
|
||||
msgstr "Think for at least {num} seconds before playing."
|
||||
|
||||
#. TODO - in minutes
|
||||
msgid "main time"
|
||||
msgstr "Main time (min.)"
|
||||
|
||||
#. TODO - TODO
|
||||
msgid "aihelp:simple"
|
||||
msgstr ""
|
||||
"Plays moves that lead to simplifying the board state. Of moves that lose at "
|
||||
"most 'max_points_lost' points and with at least 'min_visits', prefer moves "
|
||||
"that lose fewer points, settle own territory more (with importance "
|
||||
"settled_weight), settle opponent's territory more (importance settled_weight"
|
||||
" * opponent fac), avoid tenuki (more than 5 spaces, importance "
|
||||
"tenuki_penalty), and avoid attachments (importance attachment_penalty). "
|
||||
"Weights can be negative to make it complicate the game instead, andwide root"
|
||||
" noise (in general settings) can be used to further play according to the "
|
||||
"style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:simple"
|
||||
msgstr "Simple Style"
|
||||
Binary file not shown.
@@ -571,6 +571,22 @@ msgstr ""
|
||||
"perspective. When `automatic` is set, KaTrain will find a suitable value "
|
||||
"automatically."
|
||||
|
||||
msgid "ai:simple"
|
||||
msgstr "Simple Style"
|
||||
|
||||
#. TODO - TODO
|
||||
msgid "aihelp:simple"
|
||||
msgstr ""
|
||||
"Plays moves that lead to simplifying the board state. Of moves that lose at "
|
||||
"most 'max_points_lost' points and with at least 'min_visits', prefer moves "
|
||||
"that lose fewer points, settle own territory more (with importance "
|
||||
"settled_weight), settle opponent's territory more (importance settled_weight"
|
||||
" * opponent fac), avoid tenuki (more than 5 spaces, importance "
|
||||
"tenuki_penalty), and avoid attachments (importance attachment_penalty). "
|
||||
"Weights can be negative to make it complicate the game instead, andwide root"
|
||||
" noise (in general settings) can be used to further play according to the "
|
||||
"style at the cost of strength. "
|
||||
|
||||
msgid "ai:jigo"
|
||||
msgstr "KataJigo"
|
||||
|
||||
|
||||
Binary file not shown.
@@ -709,3 +709,11 @@ msgstr "Think for at least {num} seconds before playing."
|
||||
#. TODO - in minutes
|
||||
msgid "main time"
|
||||
msgstr "Main time (min.)"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:simple"
|
||||
msgstr "Plays moves that lead to simplifying the board state."
|
||||
|
||||
#. TODO
|
||||
msgid "ai:simple"
|
||||
msgstr "Simple Style"
|
||||
Binary file not shown.
@@ -733,3 +733,20 @@ msgstr "Think for at least {num} seconds before playing."
|
||||
#. TODO - in minutes
|
||||
msgid "main time"
|
||||
msgstr "Main time (min.)"
|
||||
|
||||
#. TODO - TODO
|
||||
msgid "aihelp:simple"
|
||||
msgstr ""
|
||||
"Plays moves that lead to simplifying the board state. Of moves that lose at "
|
||||
"most 'max_points_lost' points and with at least 'min_visits', prefer moves "
|
||||
"that lose fewer points, settle own territory more (with importance "
|
||||
"settled_weight), settle opponent's territory more (importance settled_weight"
|
||||
" * opponent fac), avoid tenuki (more than 5 spaces, importance "
|
||||
"tenuki_penalty), and avoid attachments (importance attachment_penalty). "
|
||||
"Weights can be negative to make it complicate the game instead, andwide root"
|
||||
" noise (in general settings) can be used to further play according to the "
|
||||
"style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:simple"
|
||||
msgstr "Simple Style"
|
||||
Binary file not shown.
@@ -691,3 +691,20 @@ msgstr "Think for at least {num} seconds before playing."
|
||||
#. TODO - in minutes
|
||||
msgid "main time"
|
||||
msgstr "Main time (min.)"
|
||||
|
||||
#. TODO - TODO
|
||||
msgid "aihelp:simple"
|
||||
msgstr ""
|
||||
"Plays moves that lead to simplifying the board state. Of moves that lose at "
|
||||
"most 'max_points_lost' points and with at least 'min_visits', prefer moves "
|
||||
"that lose fewer points, settle own territory more (with importance "
|
||||
"settled_weight), settle opponent's territory more (importance settled_weight"
|
||||
" * opponent fac), avoid tenuki (more than 5 spaces, importance "
|
||||
"tenuki_penalty), and avoid attachments (importance attachment_penalty). "
|
||||
"Weights can be negative to make it complicate the game instead, andwide root"
|
||||
" noise (in general settings) can be used to further play according to the "
|
||||
"style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:simple"
|
||||
msgstr "Simple Style"
|
||||
Binary file not shown.
@@ -666,3 +666,17 @@ msgstr "Think for at least {num} seconds before playing."
|
||||
#. TODO - in minutes
|
||||
msgid "main time"
|
||||
msgstr "Main time (min.)"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:simple"
|
||||
msgstr ""
|
||||
"Plays moves that lead to simplifying the board state. Of moves that lose at "
|
||||
"most 'max_points_lost' points and with at least 'min_visits', prefer moves "
|
||||
"that lose fewer points, settle own territory more (with importance "
|
||||
"settled_weight), settle opponent's territory more (importance settled_weight"
|
||||
" * opponent fac), and avoid attachments (importance attachment_penalty). "
|
||||
"Weights can be negative to make it complicate the game instead. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:simple"
|
||||
msgstr "Simple Style"
|
||||
Binary file not shown.
@@ -696,3 +696,20 @@ msgstr "Think for at least {num} seconds before playing."
|
||||
#. TODO - in minutes
|
||||
msgid "main time"
|
||||
msgstr "Main time (min.)"
|
||||
|
||||
#. TODO - TODO
|
||||
msgid "aihelp:simple"
|
||||
msgstr ""
|
||||
"Plays moves that lead to simplifying the board state. Of moves that lose at "
|
||||
"most 'max_points_lost' points and with at least 'min_visits', prefer moves "
|
||||
"that lose fewer points, settle own territory more (with importance "
|
||||
"settled_weight), settle opponent's territory more (importance settled_weight"
|
||||
" * opponent fac), avoid tenuki (more than 5 spaces, importance "
|
||||
"tenuki_penalty), and avoid attachments (importance attachment_penalty). "
|
||||
"Weights can be negative to make it complicate the game instead, andwide root"
|
||||
" noise (in general settings) can be used to further play according to the "
|
||||
"style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:simple"
|
||||
msgstr "Simple Style"
|
||||
+1
-1
@@ -469,7 +469,7 @@
|
||||
textbox: textbox
|
||||
SelectionSlider:
|
||||
id: slider
|
||||
size_hint: 2,1
|
||||
size_hint: 2.25,1
|
||||
values: root.values
|
||||
on_change: textbox.text = str(slider.value)
|
||||
track_color: LIGHTGREY
|
||||
|
||||
Reference in new issue
Block a user