-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathgo.py
176 lines (141 loc) · 5.14 KB
/
go.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/usr/bin/env python
# coding: utf-8
"""Go library made with pure Python.
This library offers a variety of Go related classes and methods.
There is a companion module called 'goban' which serves as a front-end
for this library, forming a fully working go board together.
"""
__author__ = "Aku Kotkavuo <aku@hibana.net>"
__version__ = "0.1"
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
class Stone(object):
def __init__(self, board, point, color):
"""Create and initialize a stone.
Arguments:
board -- the board which the stone resides on
point -- location of the stone as a tuple, e.g. (3, 3)
represents the upper left hoshi
color -- color of the stone
"""
self.board = board
self.point = point
self.color = color
self.group = self.find_group()
def remove(self):
"""Remove the stone from board."""
self.group.stones.remove(self)
del self
@property
def neighbors(self):
"""Return a list of neighboring points."""
neighboring = [(self.point[0] - 1, self.point[1]),
(self.point[0] + 1, self.point[1]),
(self.point[0], self.point[1] - 1),
(self.point[0], self.point[1] + 1)]
for point in neighboring:
if not 0 < point[0] < 20 or not 0 < point[1] < 20:
neighboring.remove(point)
return neighboring
@property
def liberties(self):
"""Find and return the liberties of the stone."""
liberties = self.neighbors
stones = self.board.search(points=self.neighbors)
for stone in stones:
liberties.remove(stone.point)
return liberties
def find_group(self):
"""Find or create a group for the stone."""
groups = []
stones = self.board.search(points=self.neighbors)
for stone in stones:
if stone.color == self.color and stone.group not in groups:
groups.append(stone.group)
if not groups:
group = Group(self.board, self)
return group
else:
if len(groups) > 1:
for group in groups[1:]:
groups[0].merge(group)
groups[0].stones.append(self)
return groups[0]
def __str__(self):
"""Return the location of the stone, e.g. 'D17'."""
return 'ABCDEFGHJKLMNOPQRST'[self.point[0]-1] + str(20-(self.point[1]))
class Group(object):
def __init__(self, board, stone):
"""Create and initialize a new group.
Arguments:
board -- the board which this group resides in
stone -- the initial stone in the group
"""
self.board = board
self.board.groups.append(self)
self.stones = [stone]
self.liberties = None
def merge(self, group):
"""Merge two groups.
This method merges the argument group with this one by adding
all its stones into this one. After that it removes the group
from the board.
Arguments:
group -- the group to be merged with this one
"""
for stone in group.stones:
stone.group = self
self.stones.append(stone)
self.board.groups.remove(group)
del group
def remove(self):
"""Remove the entire group."""
while self.stones:
self.stones[0].remove()
self.board.groups.remove(self)
del self
def update_liberties(self):
"""Update the group's liberties.
As this method will remove the entire group if no liberties can
be found, it should only be called once per turn.
"""
liberties = []
for stone in self.stones:
for liberty in stone.liberties:
liberties.append(liberty)
self.liberties = set(liberties)
if len(self.liberties) == 0:
self.remove()
def __str__(self):
"""Return a list of the group's stones as a string."""
return str([str(stone) for stone in self.stones])
class Board(object):
def __init__(self):
"""Create and initialize an empty board."""
self.groups = []
self.next = BLACK
def search(self, point=None, points=[]):
"""Search the board for a stone.
The board is searched in a linear fashion, looking for either a
stone in a single point (which the method will immediately
return if found) or all stones within a group of points.
Arguments:
point -- a single point (tuple) to look for
points -- a list of points to be searched
"""
stones = []
for group in self.groups:
for stone in group.stones:
if stone.point == point and not points:
return stone
if stone.point in points:
stones.append(stone)
return stones
def turn(self):
"""Keep track of the turn by flipping between BLACK and WHITE."""
if self.next == BLACK:
self.next = WHITE
return BLACK
else:
self.next = BLACK
return WHITE