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