Information about the fifteen puzzle can be found at http://en.wikipedia.org/wiki/15_puzzle
Although it is possible to create all fifteen tiles manually, it is a lot easier to create them with a short Python script. With scripting, creating a 40 by 40 grid is the same effort as creating a four by four grid.
>>> rows = cols = 4 >>> length = 3 >>> for row in range(rows): for col in range(cols): if row == rows - 1 and col == cols - 1: break else: rect = rectangle(x=col*length, y=row*length, width=length, height=length) rect.label = str(row * cols + col + 1)
When the user clicks on a tile we will slide the tile to the empty space if the clicked tile is next to the empty space. To accomplish this we will keep track of the position of the tiles and empty space in terms of the row and column they occupy. For example, in Figure 1 the empty space is located at the (3, 3) position (remember, Python uses zero based indexing).
If the clicked tile is in the same row as the empty tile and located next to the empty tile then change the clicked tile’s x (horizontal) position. If the tile is located on the right hand side of the empty tile then decrease the clicked tile’s x value by length (remember that we set the tile width to length). If the tile is located on the left hand side of the empty tile then increase the clicked tile’s x value by length. Then swap the empty tile’s col value (empty_col) with that of the clicked one.
If the clicked tile is in the same column as the empty tile and located next to the empty tile then change the clicked tile’s y (vertical) position. If the tile is located above the empty tile then decrease the clicked tile’s y value by length (remember that we set the tile height to length). If the tile is located below the empty tile then increase the clicked tile’s y value by length. Then swap the empty tile’s row value (empty_row) with that of the clicked one.
>>> rows = cols = 4
>>> length = 3
>>> for row in range(rows):
for col in range(cols):
if row == rows - 1 and col == cols - 1:
break
else:
rect = rectangle(x=col*length, y=row*length, width=length, height=length)
rect.label = str(row * cols + col + 1)
rect.row = row
rect.col = col
>>> empty_row = rows - 1
>>> empty_col = cols - 1
>>> def slide(tile):
global empty_row, empty_col
if empty_row == tile.row and abs(empty_col - tile.col) == 1:
tile.x += (empty_col - tile.col) * length
empty_col, tile.col = tile.col, empty_col
elif empty_col == tile.col and abs(empty_row - tile.row) == 1:
tile.y += (empty_row - tile.row) * length
empty_row, tile.row = tile.row, empty_row
Now we can test the slide function from the Python interpreter.
>>> slide(rectangle15) >>> slide(rectangle11) >>> slide(rectangle10) >>> slide(rectangle6) >>> slide(rectangle1)
Although we have not tested all possible combinations, our function seems to be working.
When the user clicks on a tile next to the empty space we would like to slide the tile into the empty space. We will bind the slide function to the LEFTDOWNON event for each tile we create. Functions need to be defined before they can be bound to an event.
rows = cols = 4 length = 3 empty_row = rows - 1 empty_col = cols - 1 def slide(event, source): global empty_row, empty_col if empty_row == source.row and abs(empty_col - source.col) == 1: source.x += (empty_col - source.col) * length empty_col, source.col = source.col, empty_col elif empty_col == source.col and abs(empty_row - source.row) == 1: source.y += (empty_row - source.row) * length empty_row, source.row = source.row, empty_row for row in range(rows): for col in range(cols): if row == rows - 1 and col == cols - 1: break # leave an empty space else: rect = rectangle(x=col*length, y=row*length, width=length, height=length) rect.label = str(row * cols + col + 1) rect.draggable = False rect.row = row rect.col = col rect.bind(LEFTDOWNON, slide)
def shuffle(): choices = range(rows*cols-1) for i in range(500): slide(None, system.rectangles[random.choice(choices)])
def shuffle(event, source): choices = range(rows*cols-1) for i in range(500): slide(None, system.rectangles[random.choice(choices)]) shuffleButton = rectangle(x=0, y=(cols+3)*length, width=2*length, height=length, fillColor=(150, 150, 150, 150)) shuffleButton.bind(LEFTDOWNON, shuffle)
rows = cols = 4
length = 3
empty_row = rows - 1
empty_col = cols - 1
def slide(event, source):
global empty_row, empty_col
if empty_row == source.row and abs(empty_col - source.col) == 1:
source.x += (empty_col - source.col) * length
empty_col, source.col = source.col, empty_col
elif empty_col == source.col and abs(empty_row - source.row) == 1:
source.y += (empty_row - source.row) * length
empty_row, source.row = source.row, empty_row
for row in range(rows):
for col in range(cols):
if row == rows - 1 and col == cols - 1:
break # leave an empty space
else:
rect = rectangle(x=col*length, y=row*length, width=length, height=length)
rect.label = str(row * cols + col + 1)
rect.draggable = False
rect.row = row
rect.col = col
rect.bind(LEFTDOWNON, slide)
def shuffle(event, source):
choices = range(rows * cols - 1)
for i in range(500):
slide(None, system.rectangles[random.choice(choices)])
shuffleButton = rectangle(x=(cols+3)*length, y=0, width=2*length,
height=length, fillColor=(150, 150, 150, 150))
shuffleButton.label = 'Shuffle'
shuffleButton.bind(LEFTDOWNON, shuffle)
system.zeroGravity = True
system.box.active = False