"""A live room (docs/games.md ยง8): one game of Gomoku on an empty 15x15 board, standard rule, black first, with people, agents and brains at the two seats, speaking the room protocol through kit/room.py. A brain (a route's or a field folder's, as in the checks) is told the whole position by BOARD before each move, 1 s a move; a seat on auto, a person out of time and a brain that fails are played by the field's threats brain. At the end the game is left in replays/ in the practice replays' format with a matches.json entry, so it lists as a match. SS_ROOM='{"seats": {"0": {"kind": "human", "name": "ada"}, "1": {"kind": "bot", "field": "threats"}}}' \ python3 -I referee/room.py Messages (a point is "x,y", 0-based, x across): event {type: start, size, seats} request {to, version, obs: {seat, colour, size, moves: [[x, y, seat]], last}, legal: every empty point} event {type: move, seat, x, y} event {type: over, winner, reason, five}, then end {result, scores} """ import os, queue, sys ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path[:0] = [os.path.join(ROOT, 'kit'), os.path.join(ROOT, 'referee')] # kit's room.py, not this file from room import Room, Stop # noqa: E402 (first: play.py puts referee/ ahead of kit/ as it loads) import league # noqa: E402 from gomoku import Brain, base, run_of # noqa: E402 from play import brain # noqa: E402 SIZE, TURN = 15, 1.0 COLOUR = {'0': 'black', '1': 'white'} class Seat: """A brain at a seat: started once, told the position by BOARD for each move (1 its own stones, 2 the other's).""" def __init__(self, folder): self.b = Brain(*brain(folder)) self.b.send(f'START {SIZE}', f'INFO timeout_turn {int(TURN * 1000)}', 'INFO timeout_match 0', 'INFO rule 1') if not self.b.reply(5).upper().startswith('OK'): raise ValueError('did not start') def ask(self, msg): me = int(msg['seat']) self.b.send('BOARD', *[f'{x},{y},{1 if s == me else 2}' for x, y, s in msg['moves']], 'DONE') try: return self.b.reply(TURN + 0.5).replace(' ', '') except queue.Empty: return None def close(self): try: self.b.send('END') self.b.p.wait(1) except Exception: self.b.p.kill() def main(): room = Room(Seat, field=os.path.join(ROOT, 'field')) names = [room.label(s) for s in '01'] stand_in = room._bot({'field': 'threats'}) board, moves, out = {}, [], {'winner': None, 'reason': 'board full', 'five': None} room.event({'type': 'start', 'size': SIZE, 'seats': {s: {'name': names[int(s)], 'kind': room.kind(s), 'colour': COLOUR[s]} for s in '01'}}) def auto(obs, legal): move = stand_in.ask(obs) if stand_in else None return move if move in legal else min(legal, key=lambda p: abs(int(p.split(',')[0]) - 7) + abs(int(p.split(',')[1]) - 7)) side, stopped = 0, False try: while len(board) < SIZE * SIZE: s = str(side) legal = [f'{x},{y}' for y in range(SIZE) for x in range(SIZE) if (x, y) not in board] obs = {'seat': s, 'colour': COLOUR[s], 'size': SIZE, 'moves': [m[:2] + [m[3]] for m in moves], 'last': moves[-1][:2] if moves else None} move = room.ask(s, obs, legal, auto=lambda: auto(obs, legal), bot=obs) x, y = map(int, move.split(',')) board[(x, y)] = side moves.append([x, y, 0, s]) room.event({'type': 'move', 'seat': s, 'x': x, 'y': y}) n, line = run_of(board, x, y, side) if n == 5: out = {'winner': side, 'reason': 'five', 'five': line} break side = 1 - side except Stop: stopped, out = True, {'winner': None, 'reason': 'stopped', 'five': None} w = out['winner'] result = 'stopped' if stopped else 'a draw' if w is None else f'{names[w]} ({COLOUR[str(w)]}) wins' room.event({'type': 'over', 'winner': None if w is None else str(w), 'reason': out['reason'], 'five': out['five']}) replay = {**base(SIZE, [], [m[:3] for m in moves]), **out, 'players': names} league.Replays().add('room.json', replay, f'Room: {names[0]} (black) vs {names[1]} (white)', names, f'{result} in {len(moves)} moves') room.end(result, {s: 0.5 if w is None else float(w == int(s)) for s in '01'}) room.close() if __name__ == '__main__': main()