31 files changed
+423
-112
No files matched your search
@@ -16,6 +16,8 @@ tmp.pickle
|
||||
my
|
||||
logs
|
||||
callgrind.*
|
||||
cpp/out
|
||||
.vs
|
||||
|
||||
# debug
|
||||
outdated_log.txt
|
||||
|
||||
@@ -177,9 +177,10 @@ In addition to shortcuts mentioned above and those shown in the main menu:
|
||||
* **[~]** or **[ ` ]** or **[m]**: Cycles through more minimalistic UI modes.
|
||||
* **[p]**: Pass
|
||||
* **[b]**: Pause/Resume timer
|
||||
* **[arrow left]** or **[z]**: Undo move. Hold alt for 10 moves at a time, or ctrl to skip to the start.
|
||||
* **[arrow right]** or **[x]**: Redo move. Hold alt for 10 moves at a time, or ctrl to skip to the start.
|
||||
* **[arrow left]** or **[z]**: Undo move. Hold alt for 5 moves at a time, or ctrl to skip to the start.
|
||||
* **[arrow right]** or **[x]**: Redo move. Hold alt for 5 moves at a time, or ctrl to skip to the start.
|
||||
* **[arrow up/down]** Switch branch, as would be expected from the move tree.
|
||||
* **[home/end]** Go to the beginning/end of the game.
|
||||
* **[scroll up]**: Undo move. Only works when hovering the cursor over the board.
|
||||
* **[scroll down]**: Redo move. Only works when hovering the cursor over the board.
|
||||
* **[click on a move]**: See detailed statistics for a previous move, along with expected variation that was best instead of this move.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+20
-15
@@ -194,14 +194,14 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
teaching_undo = cn.player and last_player.being_taught and cn.parent
|
||||
if (
|
||||
teaching_undo
|
||||
and cn.analysis_ready
|
||||
and cn.parent.analysis_ready
|
||||
and cn.analysis_complete
|
||||
and cn.parent.analysis_complete
|
||||
and not cn.children
|
||||
and not self.game.end_result
|
||||
):
|
||||
self.game.analyze_undo(cn) # not via message loop
|
||||
if (
|
||||
cn.analysis_ready
|
||||
cn.analysis_complete
|
||||
and next_player.ai
|
||||
and not cn.children
|
||||
and not self.game.end_result
|
||||
@@ -352,7 +352,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()
|
||||
@@ -468,7 +468,8 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
return
|
||||
else:
|
||||
return
|
||||
|
||||
ctrl_pressed = "ctrl" in modifiers
|
||||
alt_pressed = "alt" in modifiers
|
||||
shortcuts = self.shortcuts
|
||||
if keycode[1] == "tab":
|
||||
self.play_mode.switch_ui_mode()
|
||||
@@ -476,26 +477,30 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
self.nav_drawer.set_state("toggle")
|
||||
elif keycode[1] == "spacebar":
|
||||
self.toggle_continuous_analysis()
|
||||
elif keycode[1] == "b" and "ctrl" not in modifiers:
|
||||
elif keycode[1] == "b" and ctrl_pressed:
|
||||
self.controls.timer.paused = not self.controls.timer.paused
|
||||
elif keycode[1] in ["`", "~", "m"] and "ctrl" not in modifiers:
|
||||
elif keycode[1] in ["`", "~", "m"] and ctrl_pressed:
|
||||
self.zen = (self.zen + 1) % 3
|
||||
elif keycode[1] in ["left", "z"]:
|
||||
self("undo", 1 + ("alt" in modifiers) * 9 + ("ctrl" in modifiers) * 999)
|
||||
self("undo", 1 + alt_pressed * 4 + (ctrl_pressed and not alt_pressed) * 999)
|
||||
elif keycode[1] in ["right", "x"]:
|
||||
self("redo", 1 + ("alt" in modifiers) * 9 + ("ctrl" in modifiers) * 999)
|
||||
elif keycode[1] == "n" and "ctrl" in modifiers:
|
||||
self("redo", 1 + alt_pressed * 4 + (ctrl_pressed and not alt_pressed) * 999)
|
||||
elif keycode[1] == "home":
|
||||
self("undo", 999)
|
||||
elif keycode[1] == "end":
|
||||
self("redo", 999)
|
||||
elif keycode[1] == "n" and ctrl_pressed:
|
||||
self("new-game-popup")
|
||||
elif keycode[1] == "l" and "ctrl" in modifiers:
|
||||
elif keycode[1] == "l" and ctrl_pressed:
|
||||
self("analyze-sgf-popup")
|
||||
elif keycode[1] == "s" and "ctrl" in modifiers:
|
||||
elif keycode[1] == "s" and ctrl_pressed:
|
||||
self("output-sgf")
|
||||
elif keycode[1] == "c" and "ctrl" in modifiers:
|
||||
elif keycode[1] == "c" and ctrl_pressed:
|
||||
Clipboard.copy(self.game.root.sgf())
|
||||
self.controls.set_status(i18n._("Copied SGF to clipboard."), STATUS_INFO)
|
||||
elif keycode[1] == "v" and "ctrl" in modifiers:
|
||||
elif keycode[1] == "v" and ctrl_pressed:
|
||||
self.load_sgf_from_clipboard()
|
||||
elif keycode[1] in shortcuts.keys() and "ctrl" not in modifiers:
|
||||
elif keycode[1] in shortcuts.keys() and not ctrl_pressed:
|
||||
shortcut = shortcuts[keycode[1]]
|
||||
if isinstance(shortcut, Widget):
|
||||
shortcut.trigger_action(duration=0)
|
||||
|
||||
+11
-1
@@ -92,7 +92,17 @@
|
||||
"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:settle": {
|
||||
"max_points_lost": 1.75,
|
||||
"settled_weight": 1.0,
|
||||
"opponent_fac": 0.5,
|
||||
"min_visits": 3,
|
||||
"attach_penalty": 1,
|
||||
"tenuki_penalty": 0.5
|
||||
},
|
||||
"ai:p:weighted": {
|
||||
"weaken_fac": 1.25,
|
||||
|
||||
+63
-43
@@ -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,
|
||||
@@ -33,6 +32,7 @@ from katrain.core.constants import (
|
||||
OUTPUT_DEBUG,
|
||||
OUTPUT_ERROR,
|
||||
OUTPUT_INFO,
|
||||
AI_SETTLE_STONES,
|
||||
)
|
||||
from katrain.core.game import Game, GameNode, Move
|
||||
from katrain.core.utils import var_to_grid
|
||||
@@ -164,9 +164,10 @@ def request_ai_analysis(game: Game, cn: GameNode, extra_settings: Dict) -> Optio
|
||||
error = False
|
||||
analysis = None
|
||||
|
||||
def set_analysis(a):
|
||||
def set_analysis(a, partial_result):
|
||||
nonlocal analysis
|
||||
analysis = a
|
||||
if not partial_result:
|
||||
analysis = a
|
||||
|
||||
def set_error(a):
|
||||
nonlocal error
|
||||
@@ -205,14 +206,7 @@ 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:
|
||||
while not cn.analysis_complete:
|
||||
time.sleep(0.01)
|
||||
game.engines[cn.next_player].check_alive(exception_if_dead=True)
|
||||
|
||||
@@ -335,18 +329,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 [
|
||||
@@ -376,49 +358,87 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
|
||||
topmove = weighted_selection_without_replacement(moves, 1)[0]
|
||||
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])
|
||||
|
||||
elif ai_mode in [AI_SIMPLE_OWNERSHIP, AI_SETTLE_STONES]:
|
||||
stones_with_player = {(*s.coords, s.player) for s in game.stones}
|
||||
next_player_sign = cn.player_sign(cn.next_player)
|
||||
if ai_mode == AI_SIMPLE_OWNERSHIP:
|
||||
|
||||
def settledness(d, player_sign, player):
|
||||
return sum([abs(o) for o in d["ownership"] if player_sign * o > 0])
|
||||
|
||||
else:
|
||||
board_size_x, board_size_y = game.board_size
|
||||
|
||||
def settledness(d, player_sign, player):
|
||||
ownership_grid = var_to_grid(d["ownership"], (board_size_x, board_size_y))
|
||||
return sum(
|
||||
[abs(ownership_grid[s.coords[0]][s.coords[1]]) for s in game.stones if s.player == player]
|
||||
)
|
||||
|
||||
def is_attachment(move):
|
||||
if move.is_pass:
|
||||
return False
|
||||
attach_opponent_stones = sum(
|
||||
(move.coords[0] + dx, move.coords[1] + dy, cn.player) in stones_with_player
|
||||
for dx in [-1, 0, 1]
|
||||
for dy in [-1, 0, 1]
|
||||
if abs(dx) + abs(dy) == 1
|
||||
)
|
||||
nearby_own_stones = sum(
|
||||
(move.coords[0] + dx, move.coords[1] + dy, cn.next_player) in stones_with_player
|
||||
for dx in [-2, 0, 1, 2]
|
||||
for dy in [-2 - 1, 0, 1, 2]
|
||||
if abs(dx) + abs(dy) <= 2 # allows clamps/jumps
|
||||
)
|
||||
return attach_opponent_stones >= 1 and nearby_own_stones == 0
|
||||
|
||||
def is_tenuki(d):
|
||||
return not d.is_pass and 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),
|
||||
settledness(d, next_player_sign),
|
||||
settledness(d, -next_player_sign),
|
||||
move,
|
||||
settledness(d, next_player_sign, cn.next_player),
|
||||
settledness(d, -next_player_sign, cn.player),
|
||||
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))
|
||||
and (d["order"] <= 1 or d["visits"] >= ai_settings.get("min_visits", 1))
|
||||
for move in [Move.from_gtp(d["move"], player=cn.next_player)]
|
||||
if not (move.is_pass and d["pointsLost"] > 0.75)
|
||||
],
|
||||
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)} "
|
||||
ai_thoughts += f"{ai_mode} strategy. Top 5 Candidates {', '.join(cands)} "
|
||||
aimove = moves_with_settledness[0][0]
|
||||
else:
|
||||
game.katrain.log(
|
||||
"No moves found - are you using an older KataGo with no per-move ownership info?", OUTPUT_ERROR
|
||||
)
|
||||
aimove = top_cand
|
||||
raise (Exception("No moves found - are you using an older KataGo with no per-move ownership info?"))
|
||||
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)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
VERSION = "1.5.1"
|
||||
VERSION = "1.6.0"
|
||||
HOMEPAGE = "https://github.com/sanderland/katrain"
|
||||
CONFIG_MIN_VERSION = "1.5.1" # keep config files from this version
|
||||
CONFIG_MIN_VERSION = "1.6.0" # keep config files from this version
|
||||
|
||||
OUTPUT_ERROR = -1
|
||||
OUTPUT_KATAGO_STDERR = -0.5
|
||||
@@ -33,20 +33,21 @@ 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_SETTLE_STONES = "ai:settle"
|
||||
|
||||
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_SIMPLE_OWNERSHIP, AI_SETTLE_STONES, 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
|
||||
AI_STRATEGIES_RECOMMENDED_ORDER = [
|
||||
AI_DEFAULT,
|
||||
AI_RANK,
|
||||
AI_SIMPLE_OWNERSHIP,
|
||||
AI_HANDICAP,
|
||||
AI_SIMPLE_OWNERSHIP,
|
||||
AI_SETTLE_STONES,
|
||||
AI_SCORELOSS,
|
||||
AI_POLICY,
|
||||
AI_WEIGHTED,
|
||||
@@ -70,7 +71,8 @@ AI_STRENGTH = { # dan ranks, backup if model is missing. TODO: remove some?
|
||||
AI_INFLUENCE: -7,
|
||||
AI_TERRITORY: -7,
|
||||
AI_RANK: float("nan"),
|
||||
AI_SIMPLE_OWNERSHIP: 3,
|
||||
AI_SIMPLE_OWNERSHIP: 2,
|
||||
AI_SETTLE_STONES: 2,
|
||||
}
|
||||
|
||||
AI_OPTION_VALUES = {
|
||||
@@ -90,11 +92,22 @@ 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),
|
||||
"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",
|
||||
"max_points_lost",
|
||||
"min_visits",
|
||||
}
|
||||
AI_KEY_PROPERTIES = {"kyu_rank", "strength", "weaken_fac", "pick_frac", "pick_n", "automatic"}
|
||||
|
||||
|
||||
CALIBRATED_RANK_ELO = [
|
||||
|
||||
@@ -200,11 +200,13 @@ class KataGoEngine:
|
||||
elif "warning" in analysis:
|
||||
self.katrain.log(f"{analysis} received from KataGo", OUTPUT_DEBUG)
|
||||
else:
|
||||
if not analysis.get("isDuringSearch", False):
|
||||
partial_result = analysis.get("isDuringSearch", False)
|
||||
if not partial_result:
|
||||
del self.queries[query_id]
|
||||
time_taken = time.time() - start_time
|
||||
self.katrain.log(
|
||||
f"[{time_taken:.1f}][{query_id}] KataGo Analysis Received: {analysis.keys()}", OUTPUT_DEBUG,
|
||||
f"[{time_taken:.1f}][{query_id}][{'....' if partial_result else 'done'}] KataGo Analysis Received: {analysis.keys()}",
|
||||
OUTPUT_DEBUG,
|
||||
)
|
||||
self.katrain.log(line, OUTPUT_EXTRA_DEBUG)
|
||||
try:
|
||||
@@ -256,7 +258,7 @@ class KataGoEngine:
|
||||
ownership: Optional[bool] = None,
|
||||
next_move: Optional[GameNode] = None,
|
||||
extra_settings: Optional[Dict] = None,
|
||||
report_during_search: bool = False,
|
||||
report_every: Optional[float] = None,
|
||||
):
|
||||
nodes = analysis_node.nodes_from_root
|
||||
moves = [m for node in nodes for m in node.moves]
|
||||
@@ -305,5 +307,7 @@ class KataGoEngine:
|
||||
"moves": [[m.player, m.gtp()] for m in moves],
|
||||
"overrideSettings": {**settings, **(extra_settings or {})},
|
||||
}
|
||||
self.send_query(query, callback, error_callback, next_move, report_during_search)
|
||||
if report_every is not None:
|
||||
query["reportDuringSearchEvery"] = report_every
|
||||
self.send_query(query, callback, error_callback, next_move)
|
||||
analysis_node.analysis_visits_requested = max(analysis_node.analysis_visits_requested, visits)
|
||||
@@ -86,7 +86,9 @@ class Game:
|
||||
|
||||
def analyze_all_nodes(self, priority=0, analyze_fast=False):
|
||||
for node in self.root.nodes_in_tree:
|
||||
node.analyze(self.engines[node.next_player], priority=priority, analyze_fast=analyze_fast)
|
||||
node.analyze(
|
||||
self.engines[node.next_player], priority=priority, analyze_fast=analyze_fast #, report_every=None
|
||||
)
|
||||
|
||||
# -- move tree functions --
|
||||
def _calculate_groups(self):
|
||||
@@ -337,13 +339,13 @@ class Game:
|
||||
min_visits = min(node.analysis_visits_requested for node in nodes)
|
||||
visits = min_visits + engine.config["max_visits"]
|
||||
for node in nodes:
|
||||
node.analyze(engine, visits=visits, priority=-1_000_000, time_limit=False)
|
||||
node.analyze(engine, visits=visits, priority=-1_000_000, time_limit=False, report_every=None)
|
||||
self.katrain.controls.set_status(i18n._("game re-analysis").format(visits=visits), STATUS_ANALYSIS)
|
||||
return
|
||||
|
||||
elif mode == "sweep":
|
||||
board_size_x, board_size_y = self.board_size
|
||||
if cn.analysis_ready:
|
||||
if cn.analysis_exists:
|
||||
policy_grid = (
|
||||
var_to_grid(self.current_node.policy, size=(board_size_x, board_size_y))
|
||||
if self.current_node.policy
|
||||
@@ -369,7 +371,7 @@ class Game:
|
||||
self.katrain.controls.set_status(i18n._("sweep analysis").format(visits=visits), STATUS_ANALYSIS)
|
||||
priority = -1_000_000_000
|
||||
elif mode in ["equalize", "alternative"]:
|
||||
if not cn.analysis_ready:
|
||||
if not cn.analysis_complete:
|
||||
self.katrain.controls.set_status(i18n._("wait-before-equalize"), STATUS_INFO, self.current_node)
|
||||
return
|
||||
|
||||
@@ -387,13 +389,13 @@ class Game:
|
||||
for move in analyze_moves:
|
||||
if cn.analysis["moves"].get(move.gtp(), {"visits": 0})["visits"] < visits:
|
||||
cn.analyze(
|
||||
engine, priority, visits=visits, refine_move=move, time_limit=False
|
||||
engine, priority, visits=visits, refine_move=move, time_limit=False, report_every=None
|
||||
) # explicitly requested so take as long as you need
|
||||
|
||||
def analyze_undo(self, node):
|
||||
train_config = self.katrain.config("trainer")
|
||||
move = node.move
|
||||
if node != self.current_node or node.auto_undo is not None or not node.analysis_ready or not move:
|
||||
if node != self.current_node or node.auto_undo is not None or not node.analysis_complete or not move:
|
||||
return
|
||||
points_lost = node.points_lost
|
||||
thresholds = train_config["eval_thresholds"]
|
||||
|
||||
+29
-20
@@ -13,7 +13,7 @@ class GameNode(SGFNode):
|
||||
|
||||
def __init__(self, parent=None, properties=None, move=None):
|
||||
super().__init__(parent=parent, properties=properties, move=move)
|
||||
self.analysis = {"moves": {}, "root": None}
|
||||
self.analysis = {"moves": {}, "root": None, "completed": False}
|
||||
self.ownership = None
|
||||
self.policy = None
|
||||
self.auto_undo = None # None = not analyzed. False: not undone (good move). True: undone (bad move)
|
||||
@@ -34,8 +34,8 @@ class GameNode(SGFNode):
|
||||
show_class = False
|
||||
if (
|
||||
self.parent
|
||||
and self.parent.analysis_ready
|
||||
and self.analysis_ready
|
||||
and self.parent.analysis_exists
|
||||
and self.analysis_exists
|
||||
and (note or ((save_comments_player or {}).get(self.player, False) and show_class))
|
||||
):
|
||||
candidate_moves = self.parent.candidate_moves
|
||||
@@ -77,18 +77,18 @@ class GameNode(SGFNode):
|
||||
refine_move=None,
|
||||
analyze_fast=False,
|
||||
find_alternatives=False,
|
||||
report_during_search=False,
|
||||
report_every=0.25,
|
||||
):
|
||||
engine.request_analysis(
|
||||
self,
|
||||
lambda result: self.set_analysis(result, refine_move, find_alternatives),
|
||||
lambda result, partial_result: self.set_analysis(result, refine_move, find_alternatives, partial_result),
|
||||
priority=priority,
|
||||
visits=visits,
|
||||
analyze_fast=analyze_fast,
|
||||
time_limit=time_limit,
|
||||
next_move=refine_move,
|
||||
find_alternatives=find_alternatives,
|
||||
report_during_search=report_during_search,
|
||||
report_every=report_every,
|
||||
)
|
||||
|
||||
def update_move_analysis(self, move_analysis, move_gtp):
|
||||
@@ -104,7 +104,9 @@ class GameNode(SGFNode):
|
||||
if cur["visits"] < move_analysis["visits"]:
|
||||
cur.update(move_analysis)
|
||||
|
||||
def set_analysis(self, analysis_json: Dict, refine_move: Optional[Move], alternatives_mode: bool):
|
||||
def set_analysis(
|
||||
self, analysis_json: Dict, refine_move: Optional[Move], alternatives_mode: bool, partial_result: bool = False
|
||||
):
|
||||
if refine_move:
|
||||
pvtail = analysis_json["moveInfos"][0]["pv"] if analysis_json["moveInfos"] else []
|
||||
self.update_move_analysis(
|
||||
@@ -113,7 +115,7 @@ class GameNode(SGFNode):
|
||||
else:
|
||||
if alternatives_mode:
|
||||
for m in analysis_json["moveInfos"]:
|
||||
m["order"] += 10 # offset for not making this top
|
||||
m["order"] += 100 # offset for not making this top
|
||||
if refine_move is None and not alternatives_mode:
|
||||
for move_dict in self.analysis["moves"].values():
|
||||
move_dict["order"] = 999 # old moves to end
|
||||
@@ -130,14 +132,20 @@ class GameNode(SGFNode):
|
||||
self.parent.update_move_analysis(
|
||||
analysis_json["rootInfo"], self.move.gtp()
|
||||
) # update analysis in parent for consistency
|
||||
is_normal_query = refine_move is None and not alternatives_mode
|
||||
self.analysis["completed"] = self.analysis["completed"] or (is_normal_query and not partial_result)
|
||||
|
||||
@property
|
||||
def analysis_ready(self):
|
||||
def analysis_exists(self):
|
||||
return self.analysis["root"] is not None
|
||||
|
||||
@property
|
||||
def analysis_complete(self):
|
||||
return self.analysis["completed"] and self.analysis["root"] is not None
|
||||
|
||||
@property
|
||||
def score(self) -> Optional[float]:
|
||||
if self.analysis_ready:
|
||||
if self.analysis_exists:
|
||||
return self.analysis["root"].get("scoreLead")
|
||||
|
||||
def format_score(self, score=None):
|
||||
@@ -147,7 +155,7 @@ class GameNode(SGFNode):
|
||||
|
||||
@property
|
||||
def winrate(self) -> Optional[float]:
|
||||
if self.analysis_ready:
|
||||
if self.analysis_exists:
|
||||
return self.analysis["root"].get("winrate")
|
||||
|
||||
def format_winrate(self, win_rate=None):
|
||||
@@ -178,12 +186,12 @@ class GameNode(SGFNode):
|
||||
return ""
|
||||
|
||||
text = i18n._("move").format(number=self.depth) + f": {single_move.player} {single_move.gtp()}\n"
|
||||
if self.analysis_ready:
|
||||
if self.analysis_exists:
|
||||
score = self.score
|
||||
if sgf:
|
||||
text += i18n._("Info:score").format(score=self.format_score(score)) + "\n"
|
||||
text += i18n._("Info:winrate").format(winrate=self.format_winrate()) + "\n"
|
||||
if self.parent and self.parent.analysis_ready:
|
||||
if self.parent and self.parent.analysis_exists:
|
||||
previous_top_move = self.parent.candidate_moves[0]
|
||||
if sgf or details:
|
||||
if previous_top_move["move"] != single_move.gtp():
|
||||
@@ -210,14 +218,15 @@ class GameNode(SGFNode):
|
||||
text += policy_best_msg.format(move=pol_move, probability=pol_prob) + "\n"
|
||||
if self.auto_undo and sgf:
|
||||
text += i18n._("Info:teaching undo") + "\n"
|
||||
top_pv = self.analysis_ready and self.candidate_moves[0].get("pv")
|
||||
top_pv = self.analysis_exists 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"])
|
||||
|
||||
@@ -226,7 +235,7 @@ class GameNode(SGFNode):
|
||||
@property
|
||||
def points_lost(self) -> Optional[float]:
|
||||
single_move = self.move
|
||||
if single_move and self.parent and self.analysis_ready and self.parent.analysis_ready:
|
||||
if single_move and self.parent and self.analysis_exists and self.parent.analysis_exists:
|
||||
parent_score = self.parent.score
|
||||
score = self.score
|
||||
return self.player_sign(single_move.player) * (parent_score - score)
|
||||
@@ -238,8 +247,8 @@ class GameNode(SGFNode):
|
||||
single_move
|
||||
and self.parent
|
||||
and self.parent.parent
|
||||
and self.analysis_ready
|
||||
and self.parent.parent.analysis_ready
|
||||
and self.analysis_exists
|
||||
and self.parent.parent.analysis_exists
|
||||
):
|
||||
parent_parent_score = self.parent.parent.score
|
||||
score = self.score
|
||||
@@ -251,7 +260,7 @@ class GameNode(SGFNode):
|
||||
|
||||
@property
|
||||
def candidate_moves(self) -> List[Dict]:
|
||||
if not self.analysis_ready:
|
||||
if not self.analysis_exists:
|
||||
return []
|
||||
if not self.analysis["moves"]:
|
||||
polmoves = self.policy_ranking
|
||||
|
||||
@@ -140,7 +140,7 @@ class BadukPanWidget(Widget):
|
||||
katrain.log(f"\nRoot Stats:\n{nodes_here[-1].analysis['root']}", OUTPUT_DEBUG)
|
||||
katrain.controls.info.text = nodes_here[-1].comment(sgf=True)
|
||||
katrain.controls.active_comment_node = nodes_here[-1]
|
||||
if nodes_here[-1].parent.analysis_ready:
|
||||
if nodes_here[-1].parent.analysis_exists:
|
||||
self.set_animating_pv(nodes_here[-1].parent.candidate_moves[0]["pv"], nodes_here[-1].parent)
|
||||
|
||||
self.ghost_stone = None
|
||||
@@ -499,7 +499,7 @@ class BadukPanWidget(Widget):
|
||||
for child_node in current_node.children:
|
||||
move = child_node.move
|
||||
if move and move.coords is not None:
|
||||
if child_node.analysis_ready:
|
||||
if child_node.analysis_exists:
|
||||
self.active_pv_moves.append(
|
||||
(move.coords, [move.gtp()] + child_node.candidate_moves[0]["pv"], current_node)
|
||||
)
|
||||
|
||||
@@ -123,7 +123,7 @@ class ControlsPanel(BoxLayout):
|
||||
self.active_comment_node = current_node.children[-1]
|
||||
elif current_node.parent:
|
||||
self.active_comment_node = current_node.parent
|
||||
elif both_players_are_robots and not current_node.analysis_ready and current_node.parent:
|
||||
elif both_players_are_robots and not current_node.analysis_exists and current_node.parent:
|
||||
self.active_comment_node = current_node.parent
|
||||
|
||||
lock_ai = katrain.config("trainer/lock_ai") and katrain.play_analyze_mode == MODE_PLAY
|
||||
@@ -134,7 +134,7 @@ class ControlsPanel(BoxLayout):
|
||||
teach=katrain.players_info[self.active_comment_node.player].being_taught, details=details
|
||||
)
|
||||
|
||||
if self.active_comment_node.analysis_ready:
|
||||
if self.active_comment_node.analysis_exists:
|
||||
self.stats.score = self.active_comment_node.format_score() or ""
|
||||
self.stats.winrate = self.active_comment_node.format_winrate() or ""
|
||||
self.stats.points_lost = self.active_comment_node.points_lost
|
||||
|
||||
@@ -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,37 @@ 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), 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, and wide "
|
||||
"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"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:settle"
|
||||
msgstr ""
|
||||
"Plays moves that lead to clarity on stones' status, favouring solid moves "
|
||||
"rather than complex sabaki. Of moves that lose at most 'max_points_lost' "
|
||||
"points and with at least 'min_visits', prefer moves that lose fewer points, "
|
||||
"settle own stones more (with importance settled_weight), settle opponent's "
|
||||
"stones 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, and wide root noise (in general settings) can be used to further "
|
||||
"play according to the style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Style"
|
||||
Binary file not shown.
@@ -696,8 +696,42 @@ 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
|
||||
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, and wide "
|
||||
"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"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:settle"
|
||||
msgstr ""
|
||||
"Plays moves that lead to clarity on stones' status, favouring solid moves "
|
||||
"rather than complex sabaki. Of moves that lose at most 'max_points_lost' "
|
||||
"points and with at least 'min_visits', prefer moves that lose fewer points, "
|
||||
"settle own stones more (with importance settled_weight), settle opponent's "
|
||||
"stones 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, and wide root noise (in general settings) can be used to further "
|
||||
"play according to the style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Style"
|
||||
Binary file not shown.
@@ -571,6 +571,36 @@ msgstr ""
|
||||
"perspective. When `automatic` is set, KaTrain will find a suitable value "
|
||||
"automatically."
|
||||
|
||||
msgid "ai:simple"
|
||||
msgstr "Simple Style"
|
||||
|
||||
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, and wide "
|
||||
"root noise (in general settings) can be used to further play according to "
|
||||
"the style at the cost of strength. "
|
||||
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Style"
|
||||
|
||||
msgid "aihelp:settle"
|
||||
msgstr ""
|
||||
"Plays moves that lead to clarity on stones' status, favouring solid moves "
|
||||
"rather than complex sabaki. Of moves that lose at most 'max_points_lost' "
|
||||
"points and with at least 'min_visits', prefer moves that lose fewer points, "
|
||||
"settle own stones more (with importance settled_weight), settle opponent's "
|
||||
"stones 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, and wide 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,28 @@ 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"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:settle"
|
||||
msgstr ""
|
||||
"Plays moves that lead to clarity on stones' status, favouring solid moves "
|
||||
"rather than complex sabaki. Of moves that lose at most 'max_points_lost' "
|
||||
"points and with at least 'min_visits', prefer moves that lose fewer points, "
|
||||
"settle own stones more (with importance settled_weight), settle opponent's "
|
||||
"stones 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, and wide root noise (in general settings) can be used to further "
|
||||
"play according to the style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Style"
|
||||
Binary file not shown.
@@ -733,3 +733,37 @@ 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), 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, and wide "
|
||||
"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"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:settle"
|
||||
msgstr ""
|
||||
"Plays moves that lead to clarity on stones' status, favouring solid moves "
|
||||
"rather than complex sabaki. Of moves that lose at most 'max_points_lost' "
|
||||
"points and with at least 'min_visits', prefer moves that lose fewer points, "
|
||||
"settle own stones more (with importance settled_weight), settle opponent's "
|
||||
"stones 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, and wide root noise (in general settings) can be used to further "
|
||||
"play according to the style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Stones Style"
|
||||
Binary file not shown.
@@ -680,14 +680,37 @@ msgstr ""
|
||||
msgid "alternative analysis"
|
||||
msgstr "他の候補手を分析中"
|
||||
|
||||
#. TODO - ignore moves if timer is on and less than this is used
|
||||
#. ignore moves if timer is on and less than this is used
|
||||
msgid "minimal time use"
|
||||
msgstr "Minimal time use in byo-yomi (s)"
|
||||
msgstr "早打ち禁止秒数"
|
||||
|
||||
#. TODO
|
||||
msgid "move too fast"
|
||||
msgstr "Think for at least {num} seconds before playing."
|
||||
msgstr "早打ちすぎです。{num}秒は考えてください。"
|
||||
|
||||
#. TODO - in minutes
|
||||
#. in minutes
|
||||
msgid "main time"
|
||||
msgstr "Main time (min.)"
|
||||
msgstr "持ち時間(分)"
|
||||
|
||||
msgid "aihelp:simple"
|
||||
msgstr ""
|
||||
"盤面の単純化をめざして, 次の基準で手を選びます (括弧内は重視度): 損する目数が少ない, 自分の確定地が多い (settled_weight), "
|
||||
"相手も確定地が多い (settled_weight × opponent fac), 相手が打った手の近辺…縦横とも3間以内 "
|
||||
"(tenuki_penalty), 相手の石に接触しない (attachment_penalty). ただし, 損が大きすぎたり "
|
||||
"(max_points_lost目以上) 探索手数が少なすぎたり (min_visits手未満) する候補手は除きます. 重視度を負にすれば, "
|
||||
"逆に盤面の複雑化をめざします. また, 一般設定のwide root noiseを大きくすれば, 強さは犠牲になりますが, "
|
||||
"より前述の基準に忠実な打ち方をします."
|
||||
|
||||
msgid "ai:simple"
|
||||
msgstr "簡明派"
|
||||
|
||||
msgid "aihelp:settle"
|
||||
msgstr ""
|
||||
"複雑なサバキよりも, 石の生死をはっきりさせる堅実な手を好みます. 具体的には, 次の基準で手を選びます (括弧内は重視度): 損する目数が少ない, "
|
||||
"自分の生死が明確 (settled_weight), 相手の生死も明確 (settled_weight × opponent fac), "
|
||||
"相手が打った手の近辺…縦横とも3間以内 (tenuki_penalty), 相手の石に接触しない (attachment_penalty). ただし, "
|
||||
"損が大きすぎたり (max_points_lost目以上) 探索手数が少なすぎたり (min_visits手未満) する候補手は除きます. "
|
||||
"重視度を負にすれば, 逆に盤面の複雑化をめざします. また, 一般設定のwide root noiseを大きくすれば, 強さは犠牲になりますが, "
|
||||
"より前述の基準に忠実な打ち方をします."
|
||||
|
||||
msgid "ai:settle"
|
||||
msgstr "堅実派"
|
||||
Binary file not shown.
@@ -666,3 +666,34 @@ 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"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:settle"
|
||||
msgstr ""
|
||||
"Plays moves that lead to clarity on stones' status, favouring solid moves "
|
||||
"rather than complex sabaki. Of moves that lose at most 'max_points_lost' "
|
||||
"points and with at least 'min_visits', prefer moves that lose fewer points, "
|
||||
"settle own stones more (with importance settled_weight), settle opponent's "
|
||||
"stones 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, and wide root noise (in general settings) can be used to further "
|
||||
"play according to the style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle Style"
|
||||
Binary file not shown.
@@ -696,3 +696,37 @@ 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), 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, and wide "
|
||||
"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"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:settle"
|
||||
msgstr ""
|
||||
"Plays moves that lead to clarity on stones' status, favouring solid moves "
|
||||
"rather than complex sabaki. Of moves that lose at most 'max_points_lost' "
|
||||
"points and with at least 'min_visits', prefer moves that lose fewer points, "
|
||||
"settle own stones more (with importance settled_weight), settle opponent's "
|
||||
"stones 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, and wide root noise (in general settings) can be used to further "
|
||||
"play according to the style at the cost of strength. "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:settle"
|
||||
msgstr "Settle 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