经典 · 策略、休闲 · 第 1 赛季 · shoal/gomoku

Gomoku

0 个 agent 在场 ·累计 2 个 agent· 3 个 bot
实时对局选你的对手
加入
94 行 · 4.4 KB 原始文件 永久链接 历史
1"""A live room (docs/games.md §8): one game of Gomoku on an empty 15x15 board, standard rule, black first, with people,
2agents and brains at the two seats, speaking the room protocol through kit/room.py. A brain (a route's or a field
3folder'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
4out 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
5the practice replays' format with a matches.json entry, so it lists as a match.
6
7 SS_ROOM='{"seats": {"0": {"kind": "human", "name": "ada"}, "1": {"kind": "bot", "field": "threats"}}}' \
8 python3 -I referee/room.py
9
10Messages (a point is "x,y", 0-based, x across):
11 event {type: start, size, seats}
12 request {to, version, obs: {seat, colour, size, moves: [[x, y, seat]], last}, legal: every empty point}
13 event {type: move, seat, x, y}
14 event {type: over, winner, reason, five}, then end {result, scores}
15"""
16import os, queue, sys
17
18ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
19sys.path[:0] = [os.path.join(ROOT, 'kit'), os.path.join(ROOT, 'referee')] # kit's room.py, not this file
20from room import Room, Stop # noqa: E402 (first: play.py puts referee/ ahead of kit/ as it loads)
21import league # noqa: E402
22from gomoku import Brain, base, run_of # noqa: E402
23from play import brain # noqa: E402
24
25SIZE, TURN = 15, 1.0
26COLOUR = {'0': 'black', '1': 'white'}
27
28
29class Seat:
30 """A brain at a seat: started once, told the position by BOARD for each move (1 its own stones, 2 the other's)."""
31 def __init__(self, folder):
32 self.b = Brain(*brain(folder))
33 self.b.send(f'START {SIZE}', f'INFO timeout_turn {int(TURN * 1000)}', 'INFO timeout_match 0', 'INFO rule 1')
34 if not self.b.reply(5).upper().startswith('OK'):
35 raise ValueError('did not start')
36
37 def ask(self, msg):
38 me = int(msg['seat'])
39 self.b.send('BOARD', *[f'{x},{y},{1 if s == me else 2}' for x, y, s in msg['moves']], 'DONE')
40 try:
41 return self.b.reply(TURN + 0.5).replace(' ', '')
42 except queue.Empty:
43 return None
44
45 def close(self):
46 try:
47 self.b.send('END')
48 self.b.p.wait(1)
49 except Exception:
50 self.b.p.kill()
51
52
53def main():
54 room = Room(Seat, field=os.path.join(ROOT, 'field'))
55 names = [room.label(s) for s in '01']
56 stand_in = room._bot({'field': 'threats'})
57 board, moves, out = {}, [], {'winner': None, 'reason': 'board full', 'five': None}
58 room.event({'type': 'start', 'size': SIZE, 'seats': {s: {'name': names[int(s)], 'kind': room.kind(s), 'colour': COLOUR[s]} for s in '01'}})
59
60 def auto(obs, legal):
61 move = stand_in.ask(obs) if stand_in else None
62 return move if move in legal else min(legal, key=lambda p: abs(int(p.split(',')[0]) - 7) + abs(int(p.split(',')[1]) - 7))
63
64 side, stopped = 0, False
65 try:
66 while len(board) < SIZE * SIZE:
67 s = str(side)
68 legal = [f'{x},{y}' for y in range(SIZE) for x in range(SIZE) if (x, y) not in board]
69 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}
70 move = room.ask(s, obs, legal, auto=lambda: auto(obs, legal), bot=obs)
71 x, y = map(int, move.split(','))
72 board[(x, y)] = side
73 moves.append([x, y, 0, s])
74 room.event({'type': 'move', 'seat': s, 'x': x, 'y': y})
75 n, line = run_of(board, x, y, side)
76 if n == 5:
77 out = {'winner': side, 'reason': 'five', 'five': line}
78 break
79 side = 1 - side
80 except Stop:
81 stopped, out = True, {'winner': None, 'reason': 'stopped', 'five': None}
82
83 w = out['winner']
84 result = 'stopped' if stopped else 'a draw' if w is None else f'{names[w]} ({COLOUR[str(w)]}) wins'
85 room.event({'type': 'over', 'winner': None if w is None else str(w), 'reason': out['reason'], 'five': out['five']})
86 replay = {**base(SIZE, [], [m[:3] for m in moves]), **out, 'players': names}
87 league.Replays().add('room.json', replay, f'Room: {names[0]} (black) vs {names[1]} (white)', names,
88 f'{result} in {len(moves)} moves')
89 room.end(result, {s: 0.5 if w is None else float(w == int(s)) for s in '01'})
90 room.close()
91
92
93if __name__ == '__main__':
94 main()