Not every permutation is solvable. The check in Board.permute()is found on WolframAlpha's page for the 15 puzzle.The idea is: For each tile, read left-to-right and top-to-bottom, you count the number of tiles smaller than it. Then you add the row the blank is on. If this number is even, we have a solvable permutation.
My last addition allows Multi-Tile Moves (MTM), which means that you can move more than one tile with one click if they are in a straight line.
Here is an example for n = 4. I have found a way to center the numbers in their tiles since Memory.
A brief example of the WolframAlpha method:
- 12 has no numbers before it, so we add 0
- 1 has no numbers before it that are less than 1, so we add 0
- 2 has 1 before it, so we add 1
- 7 has 1 and 2 before it, so we add 2. Our tally is 3
- ...
- 13 has the numbers 1-12 before it, so we add 12 to our tally
- The blank is on the fourth row, so we add 4 to our tally
- The tally must be divisible by 2, because Python chose this as a valid permutation
I solved the above in 58 clicks using a method similar to the one shown on Wikihow. I would like to make the Game Won screen a little less anticlimactic: At the moment it looks like a regular screen. The user is expected to press Restart if they want to play again or the window exit button if they wish to quit.
Here is the code:
# 16-blocks
import pygame, random
pygame.init()
pygame.font.init()
n = 4
pad = 50
toolbar = 100
box_size = 150
side = n*box_size + (n + 1)*pad
window = pygame.display.set_mode( (side, side+toolbar) )
pygame.display.set_caption("15-Blox")
font = pygame.font.SysFont('Verdana', 60)
WHITE = (255,255,255)
BLACK = (0,0,0)
clicks = 0
class Tile:
def __init__(self, i, j):
self.x = (i+1)*pad + i*box_size
self.y = (j+1)*pad + j*box_size
self.id = i + j * n
self.val = self.id
self.color = WHITE
self.surf = pygame.Surface( (box_size, box_size) )
self.rect = pygame.Rect((self.x, self.y),(box_size,box_size))
self.draw()
def draw(self):
if self.val != 0:
pygame.draw.rect(window, WHITE, self.rect)
num_surf = font.render(str(self.val), True, BLACK)
num_rect = num_surf.get_rect()
num_rect.center = self.rect.center
window.blit(num_surf, (num_rect.x, num_rect.y) )
else:
pygame.draw.rect(window, BLACK, self.rect)
def __repr__(self):
return str(self.val)
class Board:
win_state = [i for i in range(1,n*n)] + [0]
def __init__(self):
# tiles in order 0-15
self.board = [Tile(i,j) for j in range(n) for i in range(n)]
self.vals = [i for i in range(n*n)]
self.permute()
def draw(self):
window.fill(BLACK)
for tile in self.board:
tile.draw()
def swap(self, clicked, index):
global clicks
if clicked.val != 0:
way = 0
if self.zero//n == index//n: # same row
way = 1 if self.zero < index else -1
elif self.zero%n == index%n: # same col
way = n if self.zero < index else -n
if way:
for i in range(self.zero, index+way, way):
self.vals[self.zero] = self.vals[i]
self.vals[i] = 0
self.update()
clicks += 1
def update(self):
self.zero = self.vals.index(0)
for i,tile in enumerate(self.board):
tile.val = self.vals[i]
def permute(self):
while True:
random.shuffle(self.vals)
self.update()
e = self.vals.index(0) // n # row of zero
counts = []
for i,tile in enumerate(self.board):
for j in range(i):
if 0 < self.vals[j] < tile.val:
counts.append(1)
if (sum(counts) + e) % 2 == 0:
return
def blox():
global clicks
clicks = 0
board = Board()
while True:
board.draw()
# stats pane
surf = font.render('Clicks: ' + str(clicks), True, WHITE)
rect = surf.get_rect()
rect.bottomleft = (0, side+toolbar)
window.blit(surf, (rect.x, rect.y) )
# restart button
restart_surf = font.render('Restart', True, WHITE)
restart_rect = restart_surf.get_rect()
restart_rect.bottomright = (side, side+toolbar)
window.blit(restart_surf, (restart_rect.x, restart_rect.y) )
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
pygame.font.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if restart_rect.collidepoint(event.pos):
blox()
for index,tile in enumerate(board.board):
if tile.rect.collidepoint(event.pos):
board.swap(tile, index)
if board.vals == Board.win_state:
break
pygame.display.update()
blox()
No comments:
Post a Comment