forked from james-willis/flow-free-ai
-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.py
145 lines (112 loc) · 5.56 KB
/
game.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
"""Represent a game of Flow Free.
This module is designed to represent an instance of the game Flow Free, Flow Free is a mobile game
created by Big Duck Games, and is available on iOS and Android devices. This modules attempts to
replicate the behavior of the game as closely as possible, and is designed with human and AI
players in mind.
"""
class GameInstance(object):
"""Represent a game of Flow Free and the behaviors required to play the game.
Attributes:
dim (int): the dimension of the gameboard.
board (:obj:`list` of :obj:`tile`): A two dimensional (dim x dim) list of the tiles that
comprise the gameboard.
dots (:obj:`list` of :obj:`dots`): A list of all of the dots on the gameboard.
"""
def __init__(self, dim, dots):
self.board = [[self.Tile()] * dim for _ in range(dim)]
self.dots = dots
self.dim = dim
for dot in dots:
self.board[dot.x][dot.y] = self.Tile(True, dot.color)
class Tile(object):
"""A tile to represent a single position on a Flow Free board.
Attributes:
is_dot (boolean): Indicates if the tile contains a dot.
color (str): Indicates the color of the tile.
next (:obj:`tile`): The next tile in line. Is None if no next item in line.
"""
def __init__(self, is_dot=False, color=None):
self.is_dot = is_dot
self.color = color
self.next = None
class Dot(object):
"""Represent a dot on the gameboard.
Attributes:
x (int): the x coordinate of the location of the dot on the gameboard.
y (int): the y coordinate of the location of the dot on the gameboard.
color (str): the color of the dot, there will be exactly two dots of each color on a
given board.
"""
def __init__(self, x, y, color):
self.x = x
self.y = y
self.color = color
def color_tile(self, previous, current):
"""Updates the gameboard to add a tile to a path.
Args:
previous (:obj:`tuple` of :obj:`int`): a tuple of the location of the end of the line
you'd like to expand. The first element is the x coordinate and the second element
is the y coordinate.
current (:obj:`tuple` of :obj:`int`): a tuple of the location of tile to add to the
line. The first element is the x coordinate and the second element is the y
coordinate.
Raises:
IndexError: If either of the args is outside the bounds of the gameboard.
ValueError: If the tile to be colored is not at the end of a line or is a dot of a
different color from the line.
"""
previous_tile = self.board[previous[0]][previous[1]]
current_tile = self.board[current[0]][current[1]]
for dim in previous + current:
if not 0 <= dim < self.dim:
raise IndexError("Previous and current tiles must be within dimensions of\
gameboard")
if current_tile.is_dot and previous_tile.color != current_tile.color:
raise ValueError("Cannot draw on dot")
if previous_tile.next:
raise ValueError("Previous tile must be the end of line")
if not previous_tile.color:
raise ValueError("Previous tile must be part of a line")
if abs(current[0] - previous[0]) + abs(current[1] - previous[1]) != 1:
raise ValueError("New tile must be adjacent to previous tile")
# If starting a new line, delete line from other dot of same color.
if previous_tile.is_dot:
other_dot = None
for dot in self.dots:
# If its the other dot of the same color.
if dot.x != previous[0] and dot.y != previous[1] and dot.color == previous.color:
other_dot = dot
self.remove_line((other_dot.x, other_dot.y))
# If theres already a line here, delete the existing line from current tile.
if current_tile.color:
self.remove_line(current)
previous_tile.next = current_tile
current_tile.color = previous_tile.color
def remove_line(self, origin):
"""Remove a line drawn from the gameboard.
The path is removed from the provided origin argument and delete the line following the
linked list of `next ` values until a dot is encountered or the line ends.
Args:
origin (:obj:`tuple` of :obj:`int`): a tuple of the location of the tile to begin line
removal from. The first element is the x coordinate and the second element is the y
coordinate.
"""
current_tile = self.board[origin[0]][origin[1]]
if current_tile.is_dot:
temp = current_tile.next
current_tile.next = None
current_tile = temp
# Remove color of all non dot tiles in line.
while current_tile and current_tile.color and not current_tile.is_dot:
temp = current_tile.next
current_tile.color = None
current_tile.next = None
current_tile = temp
def game_won(self):
"""Determine if the current board is a winning configuration.
A winning configuration means that all dots are connected via lines to their pair (other dot
of the same color) and all tiles have a color.
Return:
True if the board is a winning configuration, False otherwise.
"""
raise NotImplementedError