fix es
This commit is contained in:
commit
48c4c81b8a
24 files changed
+245
-59
No files matched your search
+4
-2
@@ -48,15 +48,17 @@ The following packages may help resolve missing OS packages for Kivy or KataGo.
|
||||
```
|
||||
sudo apt-get install python3-pip build-essential git python3 python3-dev ffmpeg libsdl2-dev libsdl2-image-dev\
|
||||
libsdl2-mixer-dev libsdl2-ttf-dev libportmidi-dev libswscale-dev libavformat-dev libavcodec-dev zlib1g-dev\
|
||||
libgstreamer1.0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good\
|
||||
libgstreamer1.0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good libpulse\
|
||||
pkg-config libgl-dev opencl-headers ocl-icd-opencl-dev python3-pygame
|
||||
```
|
||||
Then, try installing python package dependencies using:
|
||||
```
|
||||
pip3 install -U cython wheel setuptools
|
||||
pip3 install kivy==2.0.0rc2 kivymd==1.104.1
|
||||
pip3 install kivy==2.0.0rc2 kivymd==0.104.1
|
||||
```
|
||||
|
||||
You can also install kivy from source using `pip install git+https://github.com/kivy/kivy.git@2.0.0rc3` which may help issues with audio on linux.
|
||||
|
||||
In case KataGo does not start, an alternative is to go [here](https://github.com/lightvector/KataGo) and compile KataGo yourself.
|
||||
|
||||
# Configuring the GPU(s) KataGo uses
|
||||
|
||||
+8
-3
@@ -3,7 +3,8 @@
|
||||
import os
|
||||
|
||||
os.environ["KCFG_KIVY_LOG_LEVEL"] = os.environ.get("KCFG_KIVY_LOG_LEVEL", "warning")
|
||||
os.environ["KIVY_AUDIO"] = "sdl2" # force working audio
|
||||
if "KIVY_AUDIO" not in os.environ:
|
||||
os.environ["KIVY_AUDIO"] = "sdl2" # seems to be most stable / some players hard crash
|
||||
|
||||
# next, icon
|
||||
from katrain.core.utils import find_package_resource, PATHS
|
||||
@@ -161,7 +162,10 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
# update move tree
|
||||
self.controls.move_tree.current_node = self.game.current_node
|
||||
|
||||
def update_state(
|
||||
def update_state(self, redraw_board=False): # redirect to message queue thread
|
||||
self("update_state", redraw_board=redraw_board)
|
||||
|
||||
def _do_update_state(
|
||||
self, redraw_board=False
|
||||
): # is called after every message and on receiving analyses and config changes
|
||||
# AI and Trainer/auto-undo handlers
|
||||
@@ -214,7 +218,8 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
continue
|
||||
fn = getattr(self, f"_do_{msg.replace('-','_')}")
|
||||
fn(*args, **kwargs)
|
||||
self.update_state()
|
||||
if msg != "update_state":
|
||||
self._do_update_state()
|
||||
except Exception as exc:
|
||||
self.log(f"Exception in processing message {msg} {args}: {exc}", OUTPUT_ERROR)
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -69,6 +69,10 @@
|
||||
},
|
||||
"ai": {
|
||||
"ai:default": {},
|
||||
"ai:handicap": {
|
||||
"automatic": true,
|
||||
"pda": 0
|
||||
},
|
||||
"ai:jigo": {
|
||||
"target_score": 0.5
|
||||
},
|
||||
|
||||
+90
-3
@@ -21,6 +21,8 @@ from katrain.core.constants import (
|
||||
AI_TERRITORY,
|
||||
AI_PICK,
|
||||
AI_RANK,
|
||||
AI_HANDICAP,
|
||||
OUTPUT_ERROR,
|
||||
)
|
||||
from katrain.core.game import Game, GameNode, Move
|
||||
|
||||
@@ -93,8 +95,51 @@ def generate_local_tenuki_weights(ai_mode, ai_settings, policy_grid, cn, size):
|
||||
return weighted_coords, ai_thoughts
|
||||
|
||||
|
||||
def request_ai_analysis(game: Game, cn: GameNode, extra_settings: Dict) -> Dict:
|
||||
error = False
|
||||
analysis = None
|
||||
|
||||
def set_analysis(a):
|
||||
nonlocal analysis
|
||||
analysis = a
|
||||
|
||||
def set_error(a):
|
||||
nonlocal error
|
||||
game.katrain.log("Error in PDA-based analysis", a)
|
||||
error = True
|
||||
|
||||
engine = game.engines[cn.player]
|
||||
engine.request_analysis(
|
||||
cn,
|
||||
callback=set_analysis,
|
||||
error_callback=set_error,
|
||||
priority=1_000,
|
||||
ownership=False,
|
||||
extra_settings=extra_settings,
|
||||
)
|
||||
while not (error or analysis):
|
||||
time.sleep(0.01)
|
||||
engine.check_alive(exception_if_dead=True)
|
||||
return analysis
|
||||
|
||||
|
||||
def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move, GameNode]:
|
||||
cn = game.current_node
|
||||
|
||||
if ai_mode == AI_HANDICAP:
|
||||
pda = ai_settings["pda"]
|
||||
if ai_settings["automatic"]:
|
||||
n_handicaps = len(game.root.get_list_property("AB", []))
|
||||
MOVE_VALUE = 14 # could be rules dependent
|
||||
b_stones_advantage = max(n_handicaps - 1, 0) - (cn.komi - MOVE_VALUE / 2) / MOVE_VALUE
|
||||
pda = min(3, max(-3, -b_stones_advantage * (3 / 8))) # max PDA at 8 stone adv, normal 9 stone game is 8.46
|
||||
handicap_analysis = request_ai_analysis(
|
||||
game, cn, {"playoutDoublingAdvantage": pda, "playoutDoublingAdvantagePla": "BLACK"}
|
||||
)
|
||||
if not handicap_analysis:
|
||||
game.katrain.log(f"Error getting handicap-based 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)
|
||||
@@ -143,7 +188,43 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
|
||||
if ai_mode != AI_RANK:
|
||||
n_moves = int(ai_settings["pick_frac"] * len(legal_policy_moves) + ai_settings["pick_n"])
|
||||
else:
|
||||
n_moves = int(round(board_squares / 361 * 10 ** (-0.05737 * ai_settings["kyu_rank"] + 1.9482)))
|
||||
n_moves = int(
|
||||
round(
|
||||
board_squares
|
||||
/ 361
|
||||
* len(legal_policy_moves)
|
||||
/ (
|
||||
1.311648546930214
|
||||
* (
|
||||
(
|
||||
0.31164467
|
||||
+ 0.55726218
|
||||
* (len(legal_policy_moves) / board_squares)
|
||||
* math.exp(
|
||||
-1
|
||||
* (
|
||||
3.0308747
|
||||
* (len(legal_policy_moves) / board_squares)
|
||||
* (len(legal_policy_moves) / board_squares)
|
||||
- (len(legal_policy_moves) / board_squares)
|
||||
- 0.045792218 * ai_settings["kyu_rank"]
|
||||
- 0.31164467
|
||||
)
|
||||
** 2
|
||||
)
|
||||
- 0.0064860256 * ai_settings["kyu_rank"]
|
||||
)
|
||||
* (
|
||||
0.0630149
|
||||
+ 0.762399
|
||||
* board_squares
|
||||
/ (10 ** (-0.05737 * ai_settings["kyu_rank"] + 1.9482))
|
||||
)
|
||||
)
|
||||
- 0.08265346672884874
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if ai_mode in [AI_INFLUENCE, AI_TERRITORY, AI_LOCAL, AI_TENUKI]:
|
||||
if cn.depth > ai_settings["endgame"] * board_squares:
|
||||
@@ -188,6 +269,9 @@ def generate_ai_move(game: Game, ai_mode: str, ai_settings: Dict) -> Tuple[Move,
|
||||
raise ValueError(f"Unknown Policy-based AI mode {ai_mode}")
|
||||
else: # Engine based move
|
||||
candidate_ai_moves = cn.candidate_moves
|
||||
if ai_mode == AI_HANDICAP:
|
||||
candidate_ai_moves = handicap_analysis["moveInfos"]
|
||||
|
||||
top_cand = Move.from_gtp(candidate_ai_moves[0]["move"], player=cn.next_player)
|
||||
if top_cand.is_pass: # don't play suicidal to balance score - pass when it's best
|
||||
aimove = top_cand
|
||||
@@ -214,11 +298,14 @@ 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."
|
||||
else:
|
||||
if ai_mode != AI_DEFAULT:
|
||||
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
|
||||
ai_thoughts += f"Default strategy found {len(candidate_ai_moves)} moves returned from the engine and chose {aimove.gtp()} as top move"
|
||||
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'])}"
|
||||
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)
|
||||
played_node = game.play(aimove)
|
||||
played_node.ai_thoughts = ai_thoughts
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
VERSION = "1.3.0"
|
||||
HOMEPAGE = "https://github.com/sanderland/katrain"
|
||||
CONFIG_MIN_VERSION = "1.2.0" # keep config files from this version
|
||||
CONFIG_MIN_VERSION = "1.3.0" # keep config files from this version
|
||||
|
||||
OUTPUT_ERROR = -1
|
||||
OUTPUT_KATAGO_STDERR = -0.5
|
||||
@@ -17,6 +17,7 @@ GAME_TYPES = [PLAYING_NORMAL, PLAYING_TEACHING]
|
||||
MODE_PLAY, MODE_ANALYZE = "play", "analyze"
|
||||
|
||||
AI_DEFAULT = "ai:default"
|
||||
AI_HANDICAP = "ai:handicap"
|
||||
AI_SCORELOSS = "ai:scoreloss"
|
||||
AI_WEIGHTED = "ai:p:weighted"
|
||||
AI_JIGO = "ai:jigo"
|
||||
@@ -30,13 +31,14 @@ AI_RANK = "ai:p:rank"
|
||||
|
||||
AI_CONFIG_DEFAULT = AI_SCORELOSS
|
||||
|
||||
AI_STRATEGIES_ENGINE = [AI_DEFAULT, 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
|
||||
AI_STRATEGIES_RECOMMENDED_ORDER = [
|
||||
AI_DEFAULT,
|
||||
AI_RANK,
|
||||
AI_HANDICAP,
|
||||
AI_SCORELOSS,
|
||||
AI_POLICY,
|
||||
AI_WEIGHTED,
|
||||
@@ -49,20 +51,6 @@ AI_STRATEGIES_RECOMMENDED_ORDER = [
|
||||
]
|
||||
|
||||
|
||||
AI_STRENGTH = { # not used
|
||||
AI_DEFAULT: "9d",
|
||||
AI_POLICY: "4d",
|
||||
AI_JIGO: "?d",
|
||||
AI_SCORELOSS: "5k",
|
||||
AI_WEIGHTED: "5k",
|
||||
AI_PICK: "8k",
|
||||
AI_LOCAL: "5k",
|
||||
AI_TENUKI: "8k",
|
||||
AI_INFLUENCE: "8k",
|
||||
AI_TERRITORY: "5k",
|
||||
AI_RANK: "15k - 3d",
|
||||
}
|
||||
|
||||
AI_OPTION_VALUES = {
|
||||
"kyu_rank": [(k, f"{k}[strength:kyu]") for k in range(15, 0, -1)]
|
||||
+ [(k, f"{1-k}[strength:dan]") for k in range(0, -3, -1)],
|
||||
@@ -77,4 +65,6 @@ AI_OPTION_VALUES = {
|
||||
"stddev": [x / 2 for x in range(21)],
|
||||
"line_weight": range(0, 11),
|
||||
"threshold": [2, 2.5, 3, 3.5, 4, 4.5],
|
||||
"automatic": "bool",
|
||||
"pda": [(x / 10, f"{'W' if x<0 else 'B'}+{abs(x/10):.1f}") for x in range(-30, 31)],
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import subprocess
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
from typing import Callable, Optional
|
||||
from typing import Callable, Optional, Dict
|
||||
|
||||
from katrain.core.constants import OUTPUT_DEBUG, OUTPUT_ERROR, OUTPUT_EXTRA_DEBUG, OUTPUT_KATAGO_STDERR
|
||||
from katrain.core.game_node import GameNode
|
||||
@@ -206,9 +206,11 @@ class KataGoEngine:
|
||||
time_limit=True,
|
||||
priority: int = 0,
|
||||
ownership: Optional[bool] = None,
|
||||
next_move=None,
|
||||
next_move: Optional[GameNode] = None,
|
||||
extra_settings: Optional[Dict] = None,
|
||||
):
|
||||
moves = [m for node in analysis_node.nodes_from_root for m in node.move_with_placements]
|
||||
moves = [m for node in analysis_node.nodes_from_root for m in node.moves]
|
||||
initial_stones = analysis_node.root.placements
|
||||
if next_move:
|
||||
moves.append(next_move)
|
||||
if ownership is None:
|
||||
@@ -233,10 +235,11 @@ class KataGoEngine:
|
||||
"komi": analysis_node.komi,
|
||||
"boardXSize": size_x,
|
||||
"boardYSize": size_y,
|
||||
"includeOwnership": ownership,
|
||||
"includeOwnership": ownership and not next_move,
|
||||
"includePolicy": not next_move,
|
||||
"initialStones": [[m.player, m.gtp()] for m in initial_stones],
|
||||
"moves": [[m.player, m.gtp()] for m in moves],
|
||||
"overrideSettings": settings,
|
||||
"overrideSettings": {**settings, **(extra_settings or {})},
|
||||
}
|
||||
self.send_query(query, callback, error_callback, next_move)
|
||||
analysis_node.analysis_visits_requested = max(analysis_node.analysis_visits_requested,visits)
|
||||
analysis_node.analysis_visits_requested = max(analysis_node.analysis_visits_requested, visits)
|
||||
+10
-6
@@ -322,12 +322,16 @@ class ConfigAIPopup(QuickConfigGui):
|
||||
self.options_grid.add_widget(DescriptionLabel(text=k, size_hint_x=0.25))
|
||||
if k in AI_OPTION_VALUES:
|
||||
values = AI_OPTION_VALUES[k]
|
||||
if isinstance(values[0], Tuple): # with descriptions, possibly language-specific
|
||||
fixed_values = [(v, re.sub(r"\[(.*?)\]", lambda m: i18n._(m[1]), l)) for v, l in values]
|
||||
else: # just numbers
|
||||
fixed_values = [(v, str(v)) for v in values]
|
||||
widget = LabelledSelectionSlider(values=fixed_values, input_property=f"ai/{strategy}/{k}")
|
||||
widget.set_value(v)
|
||||
if values == "bool":
|
||||
widget = LabelledCheckBox(input_property=f"ai/{strategy}/{k}")
|
||||
widget.active = v
|
||||
else:
|
||||
if isinstance(values[0], Tuple): # with descriptions, possibly language-specific
|
||||
fixed_values = [(v, re.sub(r"\[(.*?)\]", lambda m: i18n._(m[1]), l)) for v, l in values]
|
||||
else: # just numbers
|
||||
fixed_values = [(v, str(v)) for v in values]
|
||||
widget = LabelledSelectionSlider(values=fixed_values, input_property=f"ai/{strategy}/{k}")
|
||||
widget.set_value(v)
|
||||
self.options_grid.add_widget(wrap_anchor(widget))
|
||||
else:
|
||||
self.options_grid.add_widget(
|
||||
|
||||
@@ -163,11 +163,13 @@ def averagemod(data):
|
||||
(int(lendata * 0.8) + 1) - int(lendata * 0.2)
|
||||
) # average without the best and worst 20% of ranks
|
||||
|
||||
def gauss(data):
|
||||
return math.exp(-1*(data)**2)
|
||||
|
||||
class RankGraph(Graph):
|
||||
black_rank_points = ListProperty([])
|
||||
white_rank_points = ListProperty([])
|
||||
segment_length = NumericProperty(60)
|
||||
segment_length = NumericProperty(80)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
@@ -191,12 +193,16 @@ class RankGraph(Graph):
|
||||
if not non_obvious_moves:
|
||||
return None
|
||||
num_legal, rank, value = zip(*non_obvious_moves)
|
||||
averagemod_rank = averagemod(rank)
|
||||
rank = list(rank)
|
||||
for (i, item) in enumerate(rank):
|
||||
if item > num_legal[i]*0.09:
|
||||
rank[i] = num_legal[i]*0.09
|
||||
rank = tuple(rank)
|
||||
averagemod_rank = averagemod(rank)+1
|
||||
averagemod_len_legal = averagemod(num_legal)
|
||||
# the averagemod_rank is the outlier free average of the best move from a selection of n_moves with averagemod_len_legal of total legal moves
|
||||
n_moves = math.floor(0.40220696 + averagemod_len_legal / (1.313341 * (averagemod_rank + 1) - 0.088646986))
|
||||
# using the calibration curve of p:pick:rank
|
||||
rank_kyu = (math.log10(n_moves * 361 / num_intersec) - 1.9482) / -0.05737
|
||||
norm_avemod_len_legal = (averagemod_len_legal/num_intersec)
|
||||
rank_kyu = -0.6284*math.log(averagemod_rank)/(0.1705+averagemod_rank*gauss(3.374*(norm_avemod_len_legal)))+13.59*(norm_avemod_len_legal)+10.41*math.log(averagemod_rank)+12.42*gauss(2.519*(norm_avemod_len_legal))-14.58
|
||||
|
||||
return 1 - rank_kyu # dan rank
|
||||
|
||||
@staticmethod
|
||||
@@ -226,7 +232,8 @@ class RankGraph(Graph):
|
||||
for segment_mid in range(0, len(nodes), dx):
|
||||
bounds = (max(0, segment_mid - half_seg), min(segment_mid + half_seg, len(nodes)))
|
||||
for pl, rank in self.calculate_ranks(policy_stats[bounds[0] : bounds[1] + 1], num_intersec).items():
|
||||
ranks[pl].append((segment_mid, rank))
|
||||
if bounds[1] - bounds[0] > self.segment_length * 0.75:
|
||||
ranks[pl].append((segment_mid, rank))
|
||||
self.rank_by_player = ranks
|
||||
self.redraw_trigger()
|
||||
|
||||
|
||||
Binary file not shown.
@@ -584,3 +584,15 @@ msgstr "Re-analyze entire game"
|
||||
#. TODO
|
||||
msgid "analysis:continuous"
|
||||
msgstr "Continuously analyze"
|
||||
|
||||
#. TODO
|
||||
msgid "ai:handicap"
|
||||
msgstr "KataGo Handicap"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:handicap"
|
||||
msgstr ""
|
||||
"KataGo optimized for playing higher handicap games.The `pda` setting "
|
||||
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
|
||||
"perspective.When `automatic` is set, KaTrain will find a suitable value "
|
||||
"automatically."
|
||||
Binary file not shown.
@@ -627,3 +627,15 @@ msgstr "Re-analyze entire game"
|
||||
#. TODO
|
||||
msgid "analysis:continuous"
|
||||
msgstr "Continuously analyze"
|
||||
|
||||
#. TODO
|
||||
msgid "ai:handicap"
|
||||
msgstr "KataGo Handicap"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:handicap"
|
||||
msgstr ""
|
||||
"KataGo optimized for playing higher handicap games.The `pda` setting "
|
||||
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
|
||||
"perspective.When `automatic` is set, KaTrain will find a suitable value "
|
||||
"automatically."
|
||||
Binary file not shown.
@@ -518,6 +518,16 @@ msgstr ""
|
||||
"in the general settings `engine` section and engine configuration file. No "
|
||||
"options are available here."
|
||||
|
||||
msgid "ai:handicap"
|
||||
msgstr "KataGo Handicap"
|
||||
|
||||
msgid "aihelp:handicap"
|
||||
msgstr ""
|
||||
"KataGo optimized for playing higher handicap games.The `pda` setting "
|
||||
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
|
||||
"perspective. When `automatic` is set, KaTrain will find a suitable value "
|
||||
"automatically."
|
||||
|
||||
msgid "ai:jigo"
|
||||
msgstr "KataJigo"
|
||||
|
||||
|
||||
Binary file not shown.
@@ -615,3 +615,15 @@ msgstr "Re-analizar juego"
|
||||
|
||||
msgid "analysis:continuous"
|
||||
msgstr "Analizar continuamente "
|
||||
|
||||
#. TODO
|
||||
msgid "ai:handicap"
|
||||
msgstr "KataGo Handicap"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:handicap"
|
||||
msgstr ""
|
||||
"KataGo optimized for playing higher handicap games.The `pda` setting "
|
||||
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
|
||||
"perspective.When `automatic` is set, KaTrain will find a suitable value "
|
||||
"automatically."
|
||||
Binary file not shown.
@@ -661,3 +661,15 @@ msgstr "Re-analyze entire game"
|
||||
#. TODO
|
||||
msgid "analysis:continuous"
|
||||
msgstr "Continuously analyze"
|
||||
|
||||
#. TODO
|
||||
msgid "ai:handicap"
|
||||
msgstr "KataGo Handicap"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:handicap"
|
||||
msgstr ""
|
||||
"KataGo optimized for playing higher handicap games.The `pda` setting "
|
||||
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
|
||||
"perspective.When `automatic` is set, KaTrain will find a suitable value "
|
||||
"automatically."
|
||||
Binary file not shown.
@@ -599,3 +599,15 @@ msgstr "Re-analyze entire game"
|
||||
#. TODO
|
||||
msgid "analysis:continuous"
|
||||
msgstr "Continuously analyze"
|
||||
|
||||
#. TODO
|
||||
msgid "ai:handicap"
|
||||
msgstr "KataGo Handicap"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:handicap"
|
||||
msgstr ""
|
||||
"KataGo optimized for playing higher handicap games.The `pda` setting "
|
||||
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
|
||||
"perspective.When `automatic` is set, KaTrain will find a suitable value "
|
||||
"automatically."
|
||||
Binary file not shown.
@@ -624,3 +624,15 @@ msgstr "Re-analyze entire game"
|
||||
#. TODO
|
||||
msgid "analysis:continuous"
|
||||
msgstr "Continuously analyze"
|
||||
|
||||
#. TODO
|
||||
msgid "ai:handicap"
|
||||
msgstr "KataGo Handicap"
|
||||
|
||||
#. TODO
|
||||
msgid "aihelp:handicap"
|
||||
msgstr ""
|
||||
"KataGo optimized for playing higher handicap games.The `pda` setting "
|
||||
"corresponds to `playoutDoublingAdvantage` in KataGo, from black's "
|
||||
"perspective.When `automatic` is set, KaTrain will find a suitable value "
|
||||
"automatically."
|
||||
@@ -41,12 +41,12 @@ setup(
|
||||
"wheel",
|
||||
"setuptools",
|
||||
"importlib_resources ;python_version<'3.7'",
|
||||
"pygame", # some versions need this for kivy
|
||||
"pygame", # some mac versions need this for kivy
|
||||
"cython>=0.24,<=0.29.14,!=0.27,!=0.27.2", # kivy wants this
|
||||
"kivy_deps.glew;platform_system=='Windows'",
|
||||
"kivy_deps.sdl2;platform_system=='Windows'",
|
||||
"kivy_deps.gstreamer;platform_system=='Windows'",
|
||||
"kivy>=2.0.0rc2",
|
||||
"kivy==2.0.0rc2", # rc3 failing on mac
|
||||
"kivymd>=0.104.1",
|
||||
"screeninfo;platform_system!='Darwin'", # for screen resolution, has problems on macos
|
||||
],
|
||||
|
||||
+16
-14
@@ -2,27 +2,29 @@ import pytest
|
||||
|
||||
from katrain.core.game import Game, IllegalMoveException, Move
|
||||
from katrain.core.base_katrain import KaTrainBase, OUTPUT_INFO
|
||||
from katrain.core.game_node import GameNode
|
||||
|
||||
|
||||
class MockKaTrain(KaTrainBase):
|
||||
pass
|
||||
|
||||
|
||||
# def log(self, message, level=OUTPUT_INFO):
|
||||
# pass
|
||||
|
||||
|
||||
class MockEngine:
|
||||
def request_analysis(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def new_game():
|
||||
return GameNode(properties={"SZ": 19})
|
||||
|
||||
|
||||
class TestBoard:
|
||||
def nonempty_chains(self, b):
|
||||
return [c for c in b.chains if c]
|
||||
|
||||
def test_merge(self):
|
||||
b = Game(MockKaTrain(), MockEngine())
|
||||
def test_merge(self, new_game):
|
||||
b = Game(MockKaTrain(force_package_config=True), MockEngine(), move_tree=new_game)
|
||||
b.play(Move.from_gtp("B9", player="B"))
|
||||
b.play(Move.from_gtp("A3", player="B"))
|
||||
b.play(Move.from_gtp("A9", player="B"))
|
||||
@@ -30,8 +32,8 @@ class TestBoard:
|
||||
assert 3 == len(b.stones)
|
||||
assert 0 == len(b.prisoners)
|
||||
|
||||
def test_collide(self):
|
||||
b = Game(MockKaTrain(), MockEngine())
|
||||
def test_collide(self, new_game):
|
||||
b = Game(MockKaTrain(force_package_config=True), MockEngine(), move_tree=new_game)
|
||||
b.play(Move.from_gtp("B9", player="B"))
|
||||
with pytest.raises(IllegalMoveException):
|
||||
b.play(Move.from_gtp("B9", player="W"))
|
||||
@@ -39,8 +41,8 @@ class TestBoard:
|
||||
assert 1 == len(b.stones)
|
||||
assert 0 == len(b.prisoners)
|
||||
|
||||
def test_capture(self):
|
||||
b = Game(MockKaTrain(), MockEngine())
|
||||
def test_capture(self, new_game):
|
||||
b = Game(MockKaTrain(force_package_config=True), MockEngine(), move_tree=new_game)
|
||||
b.play(Move.from_gtp("A2", player="B"))
|
||||
b.play(Move.from_gtp("B1", player="W"))
|
||||
b.play(Move.from_gtp("A1", player="W"))
|
||||
@@ -60,8 +62,8 @@ class TestBoard:
|
||||
assert 4 == len(b.stones)
|
||||
assert 2 == len(b.prisoners)
|
||||
|
||||
def test_snapback(self):
|
||||
b = Game(MockKaTrain(), MockEngine())
|
||||
def test_snapback(self, new_game):
|
||||
b = Game(MockKaTrain(force_package_config=True), MockEngine(), move_tree=new_game)
|
||||
for move in ["C1", "D1", "E1", "C2", "D3", "E4", "F2", "F3", "F4"]:
|
||||
b.play(Move.from_gtp(move, player="B"))
|
||||
for move in ["D2", "E2", "C3", "D4", "C4"]:
|
||||
@@ -78,8 +80,8 @@ class TestBoard:
|
||||
assert 12 == len(b.stones)
|
||||
assert 4 == len(b.prisoners)
|
||||
|
||||
def test_ko(self):
|
||||
b = Game(MockKaTrain(), MockEngine())
|
||||
def test_ko(self, new_game):
|
||||
b = Game(MockKaTrain(force_package_config=True), MockEngine(), move_tree=new_game)
|
||||
for move in ["A2", "B1"]:
|
||||
b.play(Move.from_gtp(move, player="B"))
|
||||
|
||||
|
||||
Reference in new issue
Block a user