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
|
from GameState import GameState
class Board(GameState):
# Interface methods
def __init__(self, nb_rows, nb_cols, player):
# Basic initialization
self.nb_rows = nb_rows
self.nb_cols = nb_cols
self.player = player # 1 or 2
self.scores = [0, 0, 0]
self.max_score = nb_rows*nb_cols
# Construct edges and nodes matrix
self.edges = [] # Boolean, true means uncut
self.nodes = [] # Represents valence of nodes from strings-and-coins
for x in range(self.nb_cols):
self.edges.append([True]*self.nb_rows)
self.edges.append([True]*(self.nb_rows + 1))
self.nodes.append([4]*self.nb_rows)
self.edges.append([True]*self.nb_rows)
# Construct all possible moves
self.moves_left = set() # Moves are represented as ints
for x in range(len(self.edges)):
for y in range(len(self.edges[x])):
self.moves_left.add(self.coords_to_edge(x, y))
# TODO: chain updating
# Initialize chains
# Chains are represented as lists of nodes
self.closed_chains = [] # Chains which start and end in nodes of valence
@property
def game_result(self):
own_score = self.scores[self.player]
opponent_score = self.scores[self.player % 2 + 1]
if len(self.moves_left) == 0:
diff = own_score - opponent_score
if diff > 0:
return 1
elif diff < 0:
return 0
else:
return 0.5
else:
# Check if one player already has at least half of all points
if own_score > self.max_score//2:
return 1
elif opponent_score > self.max_score//2:
return 0
else:
return None
def get_moves(self):
return self.moves_left
def get_random_move(self):
moves = self.get_moves()
return choice(tuple(moves)) if moves != set() else None
def play_move(self, move):
x, y = self.edge_to_coords(move)
self.edges[x][y] = False
self.moves_left.remove(move)
# Update valence
if x%2 == 0:
# Horizontal edge, decrease valence of left and right nodes
for node_x in x//2 - 1, x//2:
if node_x >= 0 and node_x < nb_cols:
self.nodes[node_x][y] -= 1
else:
# Vertical edge, decrease valence of top and bottom nodes
for node_y in y - 1, y:
if node_y >= 0 and node_y < nb_rows:
self.nodes[x//2][node_y] -= 1
# TODO: chain updating
# Own methods
def undo_move(self, move):
x, y = self.edge_to_coords(move)
self.edges[x][y] = True
self.moves_left.add(move)
# Update valence
if x%2 == 0:
# Horizontal edge, decrease valence of left and right nodes
for node_x in x//2 - 1, x//2:
if node_x >= 0 and node_x < nb_cols:
self.nodes[node_x][y] += 1
else:
# Vertical edge, decrease valence of top and bottom nodes
for node_y in y - 1, y:
if node_y >= 0 and node_y < nb_rows:
self.nodes[x//2][node_y] += 1
# TODO: chain updating
def coords_to_edge(self, x, y):
return x*(self.nb_cols + 1) + y
def edge_to_coords(self, move):
return move//(self.nb_cols + 1), move%(self.nb_cols + 1)
|