"""What every game's referee shares: where the bots are, how a match is seated, and the files a run leaves. check: the route's bot plays every bot of the season's field (field//), seats swapped; the score is the mean over the field and each opponent is a subtask. metrics.json is what the site keeps (scienceslack.json names it). practice: a few matches against the field, each leaving a replay in replays/ and an entry in replays/matches.json. """ import json, os, sys def bot_dir(arg): """The route's folder the check was given, absolute (bots run in their own folder); a missing bot is the route's mistake, said plainly.""" if not os.path.isdir(arg): sys.exit(f'No route folder at {arg}') return os.path.abspath(arg) def field(root='field'): return sorted(d for d in os.listdir(root) if os.path.isdir(os.path.join(root, d)) and not d.startswith('.')) def route_name(): d = os.environ.get('SS_ROUTE_DIR', 'routes').rstrip('/') return 'main' if d == 'routes' else d.split('/', 1)[-1] def write_metrics(score, subtasks, cost=None, path='metrics.json'): out = {'score': round(score, 6), 'subtasks': {k: round(v, 6) for k, v in subtasks.items()}} if cost is not None: out['cost'] = round(cost, 3) with open(path, 'w') as f: json.dump(out, f, indent=2, sort_keys=True) print(f'score {out["score"]} over {len(subtasks)}: ' + ', '.join(f'{k} {v:.3g}' for k, v in out['subtasks'].items())) class Replays: """replays/ per match, and replays/matches.json saying who sat where and how it ended.""" def __init__(self, root='replays'): self.root, self.index = root, [] os.makedirs(root, exist_ok=True) def add(self, file, data, title, seats, result): with open(os.path.join(self.root, file), 'w') as f: f.write(data if isinstance(data, str) else json.dumps(data, separators=(',', ':'))) self.index.append({'file': file, 'title': title, 'seats': seats, 'result': result}) with open(os.path.join(self.root, 'matches.json'), 'w') as f: json.dump(self.index, f, indent=2)