aboutsummaryrefslogtreecommitdiffstats
path: root/python/board.py
blob: 36cfe8c19395f5619990ab689f7d45bd5fcf7571 (plain) (blame)
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)