-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgrid.py
205 lines (136 loc) · 4.21 KB
/
grid.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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
from collections import Counter
from debug import debug
def _translate(cells, numrows, numcols):
return ((i+numrows, j+numcols) for i, j in cells)
def translate(cells, numrows, numcols):
return frozenset(_translate(cells, numrows, numcols))
def normalize ( cells ) :
"""
Return a set of cells in normal form (min x,y is zero).
Assumes cells is a collection (list, set, frozenset, ...).
"""
if not cells: return frozenset()
rows, cols = zip(*cells)
imin, jmin = min(rows), min(cols)
return translate(cells, -imin, -jmin)
def neighbors(cell):
"""
Get neighbors of this cell in no specific order.
>>> sorted(neighbors((7,5)))
[(6, 5), (7, 4), (7, 6), (8, 5)]
"""
return frozenset(_neighbors(cell))
def _neighbors(cell):
"""
Get upper, right, lower, left neighbors of this cell, in that order.
>>> list(_neighbors((7,5)))
[(7, 6), (8, 5), (7, 4), (6, 5)]
"""
i, j = cell
yield (i, j+1)
yield (i+1, j)
yield (i, j-1)
yield (i-1, j)
def _neighbors_from_direction(neighbor, cell):
"""
Get the neighbors you see if you look left, forward, right, then backward
after entering the cell from a neighboring one.
(clockwise).
"""
x, y = neighbor
i, j = cell
dx, dy = x-i, y-j
if dx == 0:
if dy == 1:
yield (i+1, j)
yield (i, j-1)
yield (i-1, j)
yield (i, j+1)
else:
yield (i-1, j)
yield (i, j+1)
yield (i+1, j)
yield (i, j-1)
elif dx == 1:
yield (i, j-1)
yield (i-1, j)
yield (i, j+1)
yield (i+1, j)
else:
yield (i, j+1)
yield (i+1, j)
yield (i, j-1)
yield (i-1, j)
def is_connected ( todo ) :
"""
todo is a set (also works with lists but with worse complexity)
"""
try:
queue = [todo.pop()]
except KeyError:
return True
while queue:
current = queue.pop()
for neighbor in _neighbors(current):
if neighbor in todo:
todo.remove(neighbor)
queue.append(neighbor)
return not todo
def boundary_cells ( cells ):
"""
Returns cells of the boundary of the input polyomino.
"""
kill_count = Counter(chain(*[_neighbors(cell) for cell in cells]))
debug("kill_count", kill_count)
killed = map(lambda t: t[0], filter(lambda t: t[1] == 4, kill_count.items()))
return cells.difference(killed)
def boundary ( cells, origin ):
"""
Returns boundary of the input polyomino (clockwise).
"""
n = len(cells)
if n == 0: return []
if n == 1: return [(0,0), (0,1), (1,1), (1,0)]
# xxxxxx
# ^ x
# ! xxxx
# xxx
# xx
first = origin
debug("first", first)
# those are coordinates of the lattice
previous = first
if (first[0],first[1]+1) in cells:
boundary = [(first[0],first[1]+1)]
second = (first[0],first[1]+1)
else:
boundary = [(first[0]+1,first[1]+1)]
second = (first[0]+1,first[1])
current = second
while True:
assert len(boundary) <= 2 * (n+1)
debug('boundary', boundary)
debug('previous', previous)
debug('current', current)
for case, neighbor in enumerate(_neighbors_from_direction(previous, current)):
debug('neighbor', case, 'is', neighbor)
if case >= 1:
x = boundary[-1][0] + neighbor[0] - current[0]
y = boundary[-1][1] + neighbor[1] - current[1]
boundary.append((x,y))
if neighbor in cells:
previous = current
current = neighbor
break
if current == second and previous == first:
boundary.pop()
return boundary
def _corners ( b ) :
for p, q, r in zip(b[-1:] + b[:-1], b, b[1:] + b[:1]):
if (not p[0] == q[0] == r[0]) and (not p[1] == q[1] == r[1]):
yield q
def corners ( b ) :
return list(_corners(b))
def _boundary_lengths ( c ) :
for p, q in zip(c, c[1:] + c[:1]):
yield max(abs(p[0]-q[0]), abs(p[1]-q[1]))