This commit is contained in:
Sander Land committed 2020-04-28 18:44:00 +02:00
1 parent da2677983b
commit 11964617a7
19 files changed
+160 -93

No files matched your search

+5 -6
View File
@@ -14,25 +14,24 @@
[x] config player to sep. row/popups?
[x] dots: teacher settings fixes
[x] ogs bot connector
[x] clarify score change vs score
[x] List edit settings/object edit settings? -> Mostly superseded by popups.
[/] README
[] engine status
[] sgf review improvements -- Likewise, in the 0.9 version, better alternatives to the played move were shown with squares, which was also pretty useful when using the sgf outside of Katrain. I mean, having the top move mentioned is all and good, but when you see multiple squares shown on the board as better alternatives to the move played in the game, it makes obvious how far from perfect that move actually was :D
[] selective save dots / config
[] Release notes
[] clarify score change vs score
[] box to label ? split in status and comment?
[] pv with overlap?
Low priority
[] more teaching / groups in danger? hard
[] box to label ? split in status and comment?
[] dual engine support -- easily possible but has weird effects on win rate etc
[] When creating a new game, the 9 buttons on the right side aren't all that useful. Maybe the 9, 13 and 19 ones make sense since these three board sizes are the traditionally used ones, but why 2, 4 and 9 stones buttons? Why 0.5, 6.5 and -40pts komi buttons?
[] Score instead of game end
Wont do for now
[] List edit settings/object edit settings?
[] dual engine support -- easily possible but has weird effects on win rate etc
[] Currently, when saving an SGF after analyzing a game, Katrain stores data for every move by default. Like for dots, it would be great if it was possible to only store data for moves that we were interested in (e.g. only from this or that player, and/or only the most inefficient moves/most costly mistakes, etc.)
[] Typing something in the comments window freezes Katrain -> probably keyboard shortcuts / solved by label
[] Plus, you wouldn't need "B" and "W" beside these numbers if the top part of the window was black with the graph being white on top of it (and the bottom White with the graph being black as they are currently).
+6 -5
View File
@@ -2,13 +2,13 @@ import heapq
import math
import random
import time
from typing import Dict, List, Tuple, Any
from typing import Any, Dict, List, Tuple
import numpy as np
from common import OUTPUT_INFO, var_to_grid, OUTPUT_DEBUG, OUTPUT_ERROR
from common import OUTPUT_DEBUG, OUTPUT_ERROR, OUTPUT_INFO, var_to_grid
from engine import EngineDiedException
from game import Move, Game, IllegalMoveException, GameNode
from game import Game, GameNode, IllegalMoveException, Move
def weighted_selection_without_replacement(items: List[Tuple[float, float, int, int]], pick_n: int) -> List[Tuple[float, float, int, int]]:
@@ -71,10 +71,11 @@ def ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode
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:
thr_line = ai_settings["threshold"] - 1 # zero-based
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, thr_line - min(size[0] - 1 - x, x)) + max(0, thr_line - min(size[1] - 1 - y, 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, size[1] - 1 - y, y) - thr_line))
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:
+13 -6
View File
@@ -1,11 +1,13 @@
# This is a script that turns a KaTrain AI into a sort-of GTP compatible bot
import json
import time, sys
import sys
import time
import traceback
from game import Game, Move
from ai import ai_move
from engine import KataGoEngine, EngineDiedException
from common import OUTPUT_ERROR, OUTPUT_INFO, OUTPUT_DEBUG, bot_strategy_names
from common import OUTPUT_DEBUG, OUTPUT_ERROR, OUTPUT_INFO, bot_strategy_names
from engine import EngineDiedException, KataGoEngine
from game import Game, Move
from sgf_parser import Move
DB_FILENAME = "ai_performance.pickle"
@@ -14,6 +16,7 @@ if len(sys.argv) < 2:
bot = "dev"
else:
bot = sys.argv[1].strip()
port = int(sys.argv[2]) if len(sys.argv) > 2 else 8587
class Logger:
@@ -24,9 +27,10 @@ class Logger:
logger = Logger()
ENGINE_SETTINGS = {
# "katago": "../KataGo/cpp/katago",
"katago": "python engine_connector.py 2222", # actual engine settings in engine_server.py
"katago": f"python engine_connector.py {port}", # actual engine settings in engine_server.py
"model": "models/b15-1.3.2.txt.gz",
"config": "KataGo/analysis_config.cfg",
"max_visits": 5,
@@ -34,7 +38,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}
engine = KataGoEngine(logger, ENGINE_SETTINGS)
@@ -47,6 +51,9 @@ all_ai_settings["dev"] = all_ai_settings["P:Noise"]
ai_strategy = bot_strategy_names[bot]
ai_settings = all_ai_settings[ai_strategy]
print(f"starting bot {bot} using server port {port}", file=sys.stderr)
print(ENGINE_SETTINGS, file=sys.stderr)
print(ai_strategy, ai_settings, file=sys.stderr)
logger.log(f"STARTED ENGINE", OUTPUT_ERROR)
+8 -1
View File
@@ -1,4 +1,4 @@
from typing import List, Any, Tuple
from typing import Any, List, Tuple
OUTPUT_ERROR = -1
OUTPUT_INFO = 0
@@ -26,3 +26,10 @@ def var_to_grid(array_var: List[Any], size: Tuple[int, int]) -> List[List[Any]]:
grid[y] = array_var[ix : ix + size[0]]
ix += size[0]
return grid
def evaluation_class(points_lost, eval_thresholds):
i = 0
while i < len(eval_thresholds) - 1 and points_lost < eval_thresholds[i]:
i += 1
return i
+6 -2
View File
@@ -99,16 +99,18 @@
"pick_override": 0.95,
"pick_n": 5,
"pick_frac": 0.4,
"threshold": 3.5,
"line_weight": 10,
"_help_right": "Samples `pick_n + pick_frac * <number of legal moves>` and plays the best one, biased towards the center.",
"_help_right": "Samples `pick_n + pick_frac * <number of legal moves>` and plays the best one, biased to above the `threshold` line.",
"_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,
"threshold": 3.5,
"line_weight": 5,
"_help_right": "Samples `pick_n + pick_frac * <number of legal moves>` and plays the best one, biased towards the edge.",
"_help_right": "Samples `pick_n + pick_frac * <number of legal moves>` and plays the best one, biased to below the `threshold` line.",
"_help_left": "Increase `line_weight` to penalize moves closer to the center more."
}
},
@@ -116,6 +118,8 @@
"starpoint_size": 0.1,
"stone_size": 0.475,
"eval_dot_max_size": 0.5,
"eval_dot_min_size": 0.25,
"visit_frac_small": 0.1,
"stones": {
"B": [
0.05,
+7 -4
View File
@@ -75,8 +75,8 @@ class KataGoEngine:
continue
analysis = json.loads(line)
if analysis["id"] in self.queries:
id = analysis["id"]
callback, error_callback, start_time, next_move = self.queries[id]
query_id = analysis["id"]
callback, error_callback, start_time, next_move = self.queries[query_id]
else:
self.katrain.log(f"Query result {analysis['id']} discarded -- recent new game?", OUTPUT_DEBUG)
continue
@@ -87,12 +87,15 @@ class KataGoEngine:
self.katrain.log(f"{analysis} received from KataGo", OUTPUT_ERROR)
continue
else:
callback, error_callback, start_time, next_move = self.queries[id]
callback, error_callback, start_time, next_move = self.queries[query_id]
time_taken = time.time() - start_time
self.katrain.log(f"[{time_taken:.1f}][{analysis['id']}] KataGo Analysis Received: {analysis.keys()}", OUTPUT_DEBUG)
self.katrain.log(line, OUTPUT_EXTRA_DEBUG)
del self.queries[query_id]
try:
callback(analysis)
del self.queries[id]
except Exception as e:
self.katrain.log(f"Error in engine callback for query {query_id}: {e}", OUTPUT_ERROR)
if getattr(self.katrain, "update_state", None): # easier mocking etc
self.katrain.update_state()
+6 -3
View File
@@ -1,10 +1,13 @@
# used to scale bots
import socket, threading
import traceback, sys, json
import json
import random
import socket
import sys
import threading
import traceback
from common import OUTPUT_DEBUG, OUTPUT_ERROR, OUTPUT_INFO
from engine import KataGoEngine
from common import OUTPUT_ERROR, OUTPUT_INFO, OUTPUT_DEBUG
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8587
+7 -3
View File
@@ -1,9 +1,9 @@
import math
import os
import re
from datetime import datetime
from typing import List, Union, Dict
import threading
from datetime import datetime
from typing import Dict, List, Union
from common import var_to_grid
from engine import KataGoEngine
@@ -206,8 +206,12 @@ class Game:
game_name = f"katrain_{black} vs {white} {self.game_id}"
file_name = os.path.join(path, f"{game_name}.sgf")
os.makedirs(os.path.dirname(file_name), exist_ok=True)
show_dots_for = {p: self.katrain.config("trainer/eval_show_ai") or "ai" not in self.katrain.controls.player_mode(p) for p in Move.PLAYERS}
thresholds = self.katrain.config("trainer/eval_thresholds")
sgf = self.root.sgf(save_comments_player=show_dots_for, save_comments_class=self.katrain.config("sgf/save_feedback"), eval_thresholds=thresholds)
with open(file_name, "w") as f:
f.write(self.root.sgf())
f.write(sgf)
return f"SGF with analysis written to {file_name}"
def analyze_extra(self, mode):
+8 -4
View File
@@ -2,7 +2,7 @@ import copy
import random
from typing import Dict, List, Optional, Tuple
from common import var_to_grid
from common import evaluation_class, var_to_grid
from sgf_parser import Move, SGFNode
@@ -19,10 +19,14 @@ class GameNode(SGFNode):
self.move_number = 0
self.undo_threshold = random.random() # for fractional undos, store the random threshold in the move itself for consistency
@property
def sgf_properties(self):
def sgf_properties(self, save_comments_player, save_comments_player_class, eval_thresholds):
properties = copy.copy(super().sgf_properties())
if self.points_lost:
show_class = save_comments_player_class[evaluation_class(self.points_lost, eval_thresholds)]
else:
show_class = False
if save_comments_player.get(self.player, False) and show_class:
best_sq = []
properties = copy.copy(super().sgf_properties)
if best_sq and "SQ" not in properties:
properties["SQ"] = best_sq
comment = self.comment(sgf=True)
+13 -15
View File
@@ -6,7 +6,7 @@ from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from common import OUTPUT_DEBUG
from common import OUTPUT_DEBUG, evaluation_class
from game import Move
from gui.kivyutils import draw_circle, draw_text
from common import var_to_grid
@@ -92,7 +92,7 @@ class BadukPanWidget(Widget):
self.draw_hover_contents() # remove ghost
# drawing functions
def on_size(self, *args):
def on_size(self, *_args):
self.draw_board()
self.draw_board_contents()
@@ -112,13 +112,11 @@ class BadukPanWidget(Widget):
def eval_color(self, points_lost):
colors = self.ui_config["eval_colors"]
thresholds = self.trainer_config["eval_thresholds"]
i = 0
while i < len(thresholds) and points_lost < thresholds[i]:
i += 1
return colors[min(i, len(colors) - 1)]
i = evaluation_class(points_lost, self.trainer_config["eval_thresholds"])
print(i, colors)
return colors[i]
def draw_board(self, *args):
def draw_board(self, *_args):
if not self.ui_config:
return
katrain = self.katrain
@@ -167,7 +165,7 @@ class BadukPanWidget(Widget):
for i in range(board_size_y):
draw_text(pos=(self.gridpos_x[0] - coord_offset, self.gridpos_y[i]), text=str(i + 1), font_size=self.grid_size / 1.5)
def draw_board_contents(self, *args):
def draw_board_contents(self, *_args):
if not self.ui_config:
return
stone_color = self.ui_config["stones"]
@@ -194,7 +192,7 @@ class BadukPanWidget(Widget):
evalsize = 1
for m in node.move_with_placements:
if has_stone.get(m.coords) and not drawn_stone.get(m.coords): # skip captures, last only for
move_eval_on = full_eval_on or (i < show_n_eval and show_dots_for.get(m.player))
move_eval_on = show_dots_for.get(m.player) and (i < show_n_eval or full_eval_on)
if move_eval_on and points_lost is not None:
evalcol = self.eval_color(points_lost)
else:
@@ -212,7 +210,7 @@ class BadukPanWidget(Widget):
self.draw_stone(6, c, stone_color["B"], outline_color["B"], stone_color["W"], evalcol, evalsize)
self.draw_stone(7, c, stone_color["W"], outline_color["W"], None, evalcol, evalsize)
self.draw_stone(8, c, stone_color["W"], outline_color["W"], stone_color["B"], evalcol, evalsize)
self.draw_stone(9, c, [*evalcol, 0.5], scale=0.8)
self.draw_stone(9, c, [*evalcol[:3], 0.5], scale=0.8)
# ownership - allow one move out of date for smooth animation
ownership = current_node.ownership or (current_node.parent and current_node.parent.ownership)
@@ -234,7 +232,7 @@ class BadukPanWidget(Widget):
pass_btn = katrain.board_controls.pass_btn
pass_btn.canvas.after.clear()
if katrain.controls.policy.active and policy:
policy_grid = var_to_grid(policy, [board_size_x, board_size_y])
policy_grid = var_to_grid(policy, (board_size_x, board_size_y))
best_move_policy = max(*policy)
for y in range(board_size_y - 1, -1, -1):
for x in range(board_size_x):
@@ -278,7 +276,7 @@ class BadukPanWidget(Widget):
self.draw_hover_contents()
def draw_hover_contents(self, *args):
def draw_hover_contents(self, *_args):
ghost_alpha = self.ui_config["ghost_alpha"]
katrain = self.katrain
game_ended = katrain.game.ended
@@ -299,7 +297,7 @@ class BadukPanWidget(Widget):
alpha, scale = self.ui_config["ghost_alpha"], 1.0
if i == 0:
alpha += self.ui_config["top_move_x_alpha"]
elif d["visits"] < 0.1 * hint_moves[0]["visits"]: # TODO: config?
elif d["visits"] < self.ui_config["visit_frac_small"] * hint_moves[0]["visits"]:
scale = 0.8
self.active_hints.append(move.coords)
@@ -322,7 +320,7 @@ class BadukPanWidget(Widget):
Color(*stone_color[opp_player])
draw_text(pos=board_coords, text=str(i + 1), font_size=sizefac * self.grid_size / 1.45)
elif not self.show_pv_for:
self.draw_stone(move.coords[0], move.coords[1], [*self.eval_color(d["pointsLost"]), alpha], scale=scale)
self.draw_stone(move.coords[0], move.coords[1], [*self.eval_color(d["pointsLost"])[:3], alpha], scale=scale)
# hover next move ghost stone
if self.ghost_stone:
+5 -7
View File
@@ -1,5 +1,3 @@
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Line, SmoothLine
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
@@ -15,7 +13,7 @@ class Controls(BoxLayout):
def set_status(self, msg, at_node=None):
self.status = msg
self.status_node = at_node or self.katrain.game and self.katrain.game.current_node
self.info.text = msg
self.status_label.text = msg
self.update_evaluation()
def select_mode(self, mode):
@@ -44,12 +42,12 @@ class Controls(BoxLayout):
katrain = self.katrain
current_node = katrain.game and katrain.game.current_node
info = ""
if current_node is self.status_node or (self.status is not None and self.status_node is None and current_node.is_root): # startup errors on root
info += self.status + "\n"
else:
if current_node is not self.status_node and not (self.status is not None and self.status_node is None and current_node.is_root): # startup errors on root
self.status_label.text = ""
self.status_node = None
info = ""
if current_node:
move = current_node.single_move
next_player_is_human_or_both_robots = current_node.player and ("ai" not in self.player_mode(current_node.player) or "ai" in self.player_mode(current_node.next_player))
+6
View File
@@ -13,6 +13,7 @@ from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.spinner import Spinner
from kivy.uix.textinput import TextInput
from kivy.uix.widget import Widget
@@ -38,6 +39,11 @@ class BackgroundLabel(Label, BackgroundColor):
pass
class ScrollableLabel(ScrollView):
text = StringProperty("")
border_color = ListProperty([0, 0, 0, 1])
class StyledButton(Button):
button_color = ListProperty([])
button_color_down = ListProperty([])
+10 -5
View File
@@ -1,9 +1,11 @@
from collections import defaultdict
from typing import Dict, List, DefaultDict
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from common import OUTPUT_DEBUG, OUTPUT_ERROR
from engine import KataGoEngine
@@ -27,7 +29,7 @@ class InputParseError(Exception):
class QuickConfigGui(BoxLayout):
def __init__(self, katrain, popup, initial_values=None, **kwargs):
def __init__(self, katrain: "KaTrainGui", popup: Popup, initial_values: Dict = None, **kwargs):
super().__init__(**kwargs)
self.katrain = katrain
self.popup = popup
@@ -74,7 +76,7 @@ class LoadSGFPopup(BoxLayout):
class NewGamePopup(QuickConfigGui):
def __init__(self, katrain, popup, properties, **kwargs):
def __init__(self, katrain: "KaTrainGui", popup: Popup, properties: Dict, **kwargs):
properties["RU"] = KataGoEngine.get_rules(katrain.game.root)
super().__init__(katrain, popup, properties, **kwargs)
self.rules_spinner.values = list(set(self.katrain.engine.RULESETS.values()))
@@ -93,7 +95,7 @@ class NewGamePopup(QuickConfigGui):
class ConfigPopup(QuickConfigGui):
def __init__(self, katrain, popup, config, ignore_cats, **kwargs):
def __init__(self, katrain: "KaTrainGui", popup: Popup, config: Dict, ignore_cats: List, **kwargs):
self.config = config
self.ignore_cats = ignore_cats
self.orientation = "vertical"
@@ -134,7 +136,7 @@ class ConfigPopup(QuickConfigGui):
self.add_widget(btn_container)
def update_config(self, save_to_file=False):
updated_cat = defaultdict(list)
updated_cat = defaultdict(list) # type: DefaultDict[List[str]]
try:
for k, v in self.collect_properties(self).items():
k1, k2 = k.split("/")
@@ -157,7 +159,9 @@ class ConfigPopup(QuickConfigGui):
if {key for key in engine_updates if key not in {"max_visits", "max_time", "enable_ownership"}}:
self.katrain.log(f"Restarting Engine after {engine_updates} settings change")
self.katrain.controls.set_status(f"Restarting Engine after {engine_updates} settings change")
old_engine = self.katrain.engine
def restart_engine(_dt):
old_engine = self.katrain.engine # type: KataGoEngine
new_engine = KataGoEngine(self.katrain, self.config["engine"])
self.katrain.engine = {"B": new_engine, "W": new_engine}
self.katrain.game.engine = new_engine
@@ -165,6 +169,7 @@ class ConfigPopup(QuickConfigGui):
old_engine.shutdown(finish=True)
else:
self.katrain.game.analyze_all_nodes() # old engine was broken, so make sure we redo any failures
Clock.schedule_once(restart_engine, 0)
self.katrain.debug_level = self.config["debug"]["level"]
self.katrain.update_state(redraw_board=True)
+39 -4
View File
@@ -360,11 +360,37 @@
Label:
size_hint: 0.025, 1
<ScrollableLabel>:
do_scroll_x: False
scroll_type: ['bars']
bar_width: 5
bar_color: BUTTON_COLOR
canvas.before:
Color:
rgba: root.border_color
Line:
rectangle: [self.pos[0],self.pos[1]+1,self.width-1,self.height-2]
width:1
Color:
rgba: [*[(c+1)/2 for c in root.border_color[:3]], root.border_color[3]]
Line:
rectangle: [self.pos[0]+1,self.pos[1]+2,self.width-3,self.height-4]
width:1
Label:
padding: 5, 5
font_size: dp(20)
color: BLACK
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
text: root.text
<Controls>:
orientation: 'vertical'
play_tab_button: play_tab_button
analyze_tab_button: analyze_tab_button
info: info
status_label: status_label
win_rate: win_rate
score_change: score_change
score: score
@@ -509,7 +535,7 @@
StyledSpinner:
id: B_AI_mode
values: ['Default']
sync_height_frac: 0.66
sync_height_frac: 0.7
size_hint: 0.3, 1
on_text: if B_player_mode.children: B_player_mode.children[0].trigger_action(duration=0)
Label:
@@ -527,7 +553,7 @@
StyledSpinner:
id: W_AI_mode
size_hint: 0.3, 1
sync_height_frac: 0.66
sync_height_frac: 0.7
values: ['Default']
on_text: if W_player_mode.children: W_player_mode.children[0].trigger_action(duration=0)
BoxLayout:
@@ -549,9 +575,18 @@
size_hint: 1, 0.0225
label: 'Score change'
text: ''
TextInput:
id: info
BoxLayout:
size_hint: 1,0.2
orientation: 'vertical'
ScrollableLabel:
id: status_label
border_color: 0.5,0.1,0.1,1
opacity: 1 if self.text else 0
text: ''
size_hint_y: 0.00001 if not self.text else 0.66
ScrollableLabel:
id: info
size_hint: 1, 1 - status_label.size_hint_y
BoxLayout:
orientation: 'horizontal'
size_hint: 1, None
+3 -5
View File
@@ -1,5 +1,3 @@
import inspect
from kivy.config import Config # isort:skip
Config.set("input", "mouse", "mouse,multitouch_on_demand") # isort:skip # no red dots on right click
@@ -10,13 +8,13 @@ import sys
import threading
import traceback
from queue import Queue
from typing import Optional
from kivy.app import App
from kivy.core.clipboard import Clipboard
from kivy.core.window import Window
from kivy.storage.jsonstore import JsonStore
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from ai import ai_move
from common import OUTPUT_DEBUG, OUTPUT_ERROR, OUTPUT_EXTRA_DEBUG, OUTPUT_INFO
@@ -33,14 +31,14 @@ class KaTrainGui(BoxLayout):
def __init__(self, **kwargs):
super(KaTrainGui, self).__init__(**kwargs)
self.debug_level = 0
self.engine = None
self.engine = None # type: Optional[KataGoEngine]
self.game = None
self.logger = lambda message, level=OUTPUT_INFO: self.log(message, level)
self._load_config()
self.debug_level = self.config("debug/level", OUTPUT_INFO)
self.controls.ai_mode_groups["W"].values = self.controls.ai_mode_groups["B"].values = list(self.config("ai").keys()) + ["<Pause>"]
self.controls.ai_mode_groups["W"].values = self.controls.ai_mode_groups["B"].values = list(self.config("ai").keys())
self.message_queue = Queue()
self._keyboard = Window.request_keyboard(None, self, "")
+6 -5
View File
@@ -1,16 +1,17 @@
# This is a script I use to test the performance of AIs
import pickle
import sys
import threading
import time, sys
import time
import traceback
from collections import defaultdict
import pickle
from concurrent.futures.thread import ThreadPoolExecutor
from game import Game
from ai import ai_move
from engine import KataGoEngine
from common import OUTPUT_ERROR, OUTPUT_INFO, OUTPUT_DEBUG
from common import OUTPUT_DEBUG, OUTPUT_ERROR, OUTPUT_INFO
from elote import EloCompetitor
from engine import KataGoEngine
from game import Game
DB_FILENAME = "ai_performance.pickle"
+4 -4
View File
@@ -71,16 +71,16 @@ class SGFNode:
if parent and move:
self.set_property(move.player, move.sgf(self.board_size))
@property
def sgf_properties(self) -> Dict:
def sgf_properties(self, **xargs) -> Dict:
"""For hooking into in a subclass and overriding/formatting any additional properties to be output"""
return copy.deepcopy(self.properties)
def sgf(self) -> str:
def sgf(self, **xargs) -> str:
"""Generates an SGF, calling sgf_properties on each node with the given xargs, so it can filter relevant properties if needed."""
import sys
sys.setrecursionlimit(max(sys.getrecursionlimit(), 3 * 29 * 29)) # thanks to lightvector for causing stack overflows
sgf_str = "".join([prop + "".join(f"[{v}]" for v in values) for prop, values in self.sgf_properties.items() if values])
sgf_str = "".join([prop + "".join(f"[{v}]" for v in values) for prop, values in self.sgf_properties(**xargs).items() if values])
if self.children:
children = [c.sgf() for c in self.children]
if len(children) == 1:
+8 -3
View File
@@ -8,7 +8,10 @@ if len(sys.argv) < 2:
exit(0)
bot = sys.argv[1].strip()
port = int(sys.argv[2]) if len(sys.argv) > 2 else 8587
username = f"katrain-{bot}"
greetings = {
"dev": "Experimental!",
"strong": "Play top policy move.",
@@ -40,6 +43,8 @@ GREETING = (
)
BYEMSG = "Thank you for playing. If you have any feedback, please message my admin!"
MAXGAMES = 10
os.system(
f'gtp2ogs --apikey {APIKEY} --username {username} --greeting "{GREETING}" --rankedonly --farewell "{BYEMSG}" --ogspv katago --noclock --speeds blitz,live --maxconnectedgames {MAXGAMES} --persist --minrank 20k --noautohandicap --maxhandicap 0 --boardsizes 9,13,19 --komis automatic,6.5 -- python ai2gtp.py {bot}'
)
# --rankedonly
cmd = f'gtp2ogs --apikey {APIKEY} --username {username} --greeting "{GREETING}" --farewell "{BYEMSG}" --ogspv katago --noclock --nopause --speeds blitz,live --maxconnectedgames {MAXGAMES} --persist --minrank 20k --noautohandicap --maxhandicap 0 --boardsizes 19 --komis automatic,6.5 -- python ai2gtp.py {bot} {port}'
print(f"starting bot {username} using server port {port} --> {cmd}")
os.system(cmd)
-11
View File
@@ -1,11 +0,0 @@
screen echo 1
split -v
focus
screen echo 2
split
focus
screen echo 3
focus
split
focus
screen echo 4