-
Notifications
You must be signed in to change notification settings - Fork 1
/
hexgame.py
148 lines (124 loc) · 4.68 KB
/
hexgame.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/python3
"""
This module provides the core of the game engine for
the game of Hex.
Author: Sylvain B.
Version: 1.0
"""
EMPTY, BLUE, RED = 0, 1, 2
class InvalidMoveException(Exception):
"""This exception is raised when a move is invalid."""
pass
class Hex():
"""
The Hex class represents an Hex board of a give size
in a given state.
"""
def __init__(self, size):
self.size = size
[self.grid, self.winner, self.current, self.edges] = [None] * 4
self.reset()
@staticmethod
def create_from_str(serialized_string):
"""
Initializes an hex board using a string representing this board
Arguments:
- The string used to initialize the board.
"""
winner_str, grid_str = serialized_string.split('/')
grid = [[int(x) for x in row.split('-')]
for row in grid_str.split('#')]
assert len(grid) == len(grid[0]), 'Invalid grid dimension!'
hexboard = Hex(len(grid))
hexboard.grid = grid
hexboard.winner = None if winner_str == "" else int(winner_str)
return hexboard
def _2d_2_1d(self, i, j):
return i * self.size + j
def _1d_2_2d(self, val):
return val // self.size, val % self.size
def reset(self):
"""Resets the game."""
self.grid = [[EMPTY for _ in range(self.size)]
for _ in range(self.size)]
self._create_graph()
self.current = BLUE
self.winner = None
def _left(self):
return self.size ** 2
def _right(self):
return self.size ** 2 + 1
def _top(self):
return self.size ** 2 + 2
def _bottom(self):
return self.size ** 2 + 3
def _create_graph(self):
self.edges = [[] for _ in range(self.size ** 2 + 4)]
for i in range(self.size):
for j in range(self.size):
if i < self.size - 1:
self.edges[self._2d_2_1d(i, j)].append(
self._2d_2_1d(i + 1, j))
self.edges[self._2d_2_1d(i + 1, j)].append(
self._2d_2_1d(i, j))
if j > 0:
self.edges[self._2d_2_1d(i, j)].append(
self._2d_2_1d(i + 1, j - 1))
self.edges[self._2d_2_1d(i + 1, j - 1)].append(
self._2d_2_1d(i, j))
if j < self.size - 1:
self.edges[self._2d_2_1d(i, j)].append(
self._2d_2_1d(i, j + 1))
self.edges[self._2d_2_1d(i, j + 1)].append(
self._2d_2_1d(i, j))
for i in range(self.size):
self.edges[self._left()].append(self._2d_2_1d(i, 0))
self.edges[self._2d_2_1d(i, self.size - 1)].append(self._right())
for j in range(self.size):
self.edges[self._top()].append(self._2d_2_1d(0, j))
self.edges[self._2d_2_1d(self.size - 1, j)].append(self._bottom())
def play(self, i, j):
"""
Plays a move: puts a piece of the current player
into the specified cell.
Arguments:
- row
- column
Exceptions:
- InvalidMoveException if the current cell is not empty.
"""
if self.grid[i][j] != EMPTY:
raise InvalidMoveException(
"Cell ({}, {}) is not empty!".format(i, j))
self.grid[i][j] = self.current
if self._check_winner():
self.winner = self.current
self.current = BLUE if self.current == RED else RED
return self.winner
def _check_winner(self):
if self.current == BLUE:
return self._is_connected(self._left(), self._right(), BLUE)
return self._is_connected(self._top(), self._bottom(), RED)
def _is_connected(self, i, j, player, marked=None):
if not marked:
marked = set()
if i == j:
return True
marked.add(i)
for k in self.edges[i]:
if k not in marked:
if k in (self._left(), self._right(),
self._top(), self._bottom()):
if self._is_connected(k, j, player, marked):
return True
else:
i_2, j_2 = self._1d_2_2d(k)
if self.grid[i_2][j_2] == player:
if self._is_connected(k, j, player, marked):
return True
return False
def serialize(self):
"""Returns a string representing the board"""
return "{}/{}".format(
self.winner if self.winner else "",
"#".join("-".join(str(i) for i in r) for r in self.grid))