-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBockState.py
113 lines (96 loc) · 4.6 KB
/
BockState.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
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from copy import deepcopy
plt.ioff() ## Turn off immediate display of plots
COLORS = ["lightgray", "red", "blue", "green", "yellow",
"orange", "purple", "pink", "brown"]
class BlockState:
# Specify mapping from directions to grid coordinate offsets:
neighbour_offset = {"left": (0,-1), "right": (0,+1), "down":(+1,0), "up":(-1,0)}
def __init__( self, blockstate, colors=COLORS ):
self.blockstate = blockstate
self.nrows = len(blockstate)
self.ncols = len(blockstate[0])
self.blocknums = set().union(*[set(row) for row in blockstate])
self.blocknums = self.blocknums - {0}
self.blocknumlist = list(self.blocknums)
self.colors = colors
def __repr__(self):
return( str( self.blockstate ))
# Find the cells occupied by a given number
def blockcells( self, blocknum ):
blockcells = []
for row in range(self.nrows):
for col in range(self.ncols):
if self.blockstate[row][col] == blocknum:
blockcells.append((row,col))
return blockcells
# Test if a cell is free (unblocked) in a given direction
# Free if not blocked by edge of grid or by a cell of different colour
def free_cell( self, direction, cell ):
row, col = cell
offrow, offcol = BlockState.neighbour_offset[direction]
neighrow, neighcol = (row + offrow, col + offcol)
if not (0 <= neighrow < self.nrows): return False #at top or bottom
if not (0 <= neighcol < self.ncols): return False #at left or right
neighval = self.blockstate[neighrow][neighcol]
# Neighboring cell must be empty or part of the same coloured block
return (neighval==0 or neighval==self.blockstate[row][col])
def free_block( self, direction, blockn ):
blockcells = self.blockcells(blockn)
for cell in blockcells:
if not self.free_cell(direction, cell):
return False
return True
def possible_moves(self):
moves = []
for blocknum in self.blocknumlist:
for direction in ["left", "right", "down", "up"]:
if self.free_block(direction, blocknum):
moves.append((blocknum, direction))
return moves
def next_state(self, move):
next_blockstate = deepcopy(self.blockstate)
blockno, direction = move
cells = self.blockcells(blockno)
## first clear all cells of the block (set to 0)
for cell in cells:
row, col = cell
next_blockstate[row][col] = 0
rowoff, coloff = BlockState.neighbour_offset[direction]
## now set all neighbour cells (in move direction) to be
## cells with the blocknumber
for cell in cells:
row, col = cell
next_blockstate[row+rowoff][col+coloff] = blockno
return BlockState(next_blockstate)
def color_key(self):
return {b:self.colors[b] for b in self.blocknumlist}
def figure(self, scale=0.5):
nrows = self.nrows
ncols = self.ncols
fig, ax = plt.subplots(figsize=(ncols*scale+0.1,nrows*scale+0.1))
plt.close(fig)
ax.set_axis_off() # Don't show border lines and coordinate values
frame = patches.Rectangle((0,0),1,1, linewidth=5, edgecolor='k', facecolor='w')
ax.add_patch(frame)
for row in range(nrows):
for col in range(ncols):
greyrect = patches.Rectangle( (((col*0.9)/ncols)+0.05,
(((nrows-row-1)*0.9)/nrows)+0.05 ),
0.9/ncols, 0.9/nrows,
linewidth=1, edgecolor="gray", facecolor="lightgray")
ax.add_patch(greyrect)
for row in range(nrows):
for col in range(ncols):
cellval = self.blockstate[row][col]
if cellval > 0:
cellcol = COLORS[cellval]
rect = patches.Rectangle( (((col*0.9)/ncols)+0.05,
(((nrows-row-1)*0.9)/nrows)+0.05 ),
0.9/ncols, 0.9/nrows,
linewidth=0, edgecolor=cellcol, facecolor=cellcol)
ax.add_patch(rect)
return fig
def display(self):
display(self.figure())