├── visualizer.py └── solver.py /visualizer.py: -------------------------------------------------------------------------------- 1 | import time 2 | import pygame 3 | 4 | 5 | class Visualizer: 6 | BLACK = (0, 0, 0) 7 | GREEN = (0, 255, 0) 8 | SCALE = 16 9 | 10 | def __init__(self): 11 | pygame.init() 12 | self.gameDisplay = pygame.display.set_mode((56 * Visualizer.SCALE, 56 * Visualizer.SCALE)) 13 | 14 | 15 | def visualize(self, positions): 16 | self.gameDisplay.fill(Visualizer.BLACK) 17 | 18 | for tup in positions: 19 | piece, position = tup 20 | piece = (Visualizer.SCALE * piece[0], Visualizer.SCALE * piece[1]) 21 | position = (Visualizer.SCALE * position[0], Visualizer.SCALE * position[1]) 22 | 23 | border = 5 24 | pygame.draw.polygon(self.gameDisplay, Visualizer.GREEN, (position, (position[0] + piece[0], position[1]), (position[0] + piece[0], position[1] + piece[1]), (position[0], position[1] + piece[1]))) 25 | pygame.draw.polygon(self.gameDisplay, Visualizer.BLACK, ((position[0] + border, position[1] + border), (position[0] + piece[0] - border, position[1] + border), (position[0] + piece[0] - border, position[1] + piece[1] - border), (position[0] + border, position[1] + piece[1] - border))) 26 | 27 | pygame.display.update() 28 | time.sleep(.01) 29 | -------------------------------------------------------------------------------- /solver.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | import visualizer 4 | import time 5 | 6 | 7 | class Board: 8 | SIZE = 56 9 | 10 | def __init__(self): 11 | self.grid = [[True for _ in range(Board.SIZE)] for _ in range(Board.SIZE)] # grid[y][x] = True if unoccupied 12 | self.next_free = (0, 0) # (x, y), the top leftmost free cell 13 | self.space = Board.SIZE * Board.SIZE # free space on this board 14 | 15 | 16 | ''' Returns True if this piece fits at the top leftmost free space ''' 17 | def does_fit(self, piece): 18 | # Ensure this piece fits within the bounds of the board 19 | if self.next_free[0] + piece[0] > Board.SIZE or self.next_free[1] + piece[1] > Board.SIZE: 20 | return False 21 | 22 | # Ensure each cell is free 23 | for w in range(piece[0]): 24 | for h in range(piece[1]): 25 | if not self.grid[self.next_free[1] + h][self.next_free[0] + w]: 26 | return False 27 | 28 | return True 29 | 30 | 31 | ''' Inserts this piece at the top leftmost free position 32 | Assumes this piece fits 33 | Returns the top left of this inserted piece ''' 34 | def insert(self, piece): 35 | position = copy.copy(self.next_free) 36 | 37 | # Fill in the cells occupied by this piece 38 | for x in range(piece[0]): 39 | for y in range(piece[1]): 40 | self.grid[self.next_free[1] + y][self.next_free[0] + x] = False 41 | 42 | # Update top-leftmost free position 43 | updated = False 44 | for y in range(Board.SIZE): 45 | for x in range(Board.SIZE): 46 | if not updated and self.grid[y][x]: 47 | self.next_free = (x, y) 48 | updated = True 49 | 50 | 51 | # Update remaining space 52 | self.space -= piece[0] * piece[1] 53 | 54 | return position 55 | 56 | 57 | ''' Return a copy of this board ''' 58 | def copy(self): 59 | copied_board = Board() 60 | copied_board.grid = copy.deepcopy(self.grid) 61 | copied_board.next_free = copy.copy(self.next_free) 62 | copied_board.space = self.space 63 | return copied_board 64 | 65 | 66 | 67 | def get_solution(board, remaining, positions): 68 | visualizer.visualize(positions) 69 | 70 | if board.space == 0: 71 | return positions 72 | 73 | for piece in remaining: 74 | for isRotated in (False, True): 75 | rotated_piece = piece if not isRotated else (piece[1], piece[0]) 76 | if board.does_fit(rotated_piece): 77 | # insert piece into board, remove piece from remaining, append position 78 | new_board = board.copy() 79 | new_remaining = copy.deepcopy(remaining) 80 | position = new_board.insert(rotated_piece) 81 | new_remaining.remove(piece) 82 | positions.append((rotated_piece, position)) 83 | 84 | # Get recursive solution 85 | solution = get_solution(new_board, new_remaining, positions) 86 | if solution: 87 | return solution 88 | 89 | # Revert unsuccessful position addition 90 | positions.pop() 91 | 92 | 93 | 94 | ''' ======================= Run the Code ======================= ''' 95 | visualizer = visualizer.Visualizer() 96 | 97 | all_pieces = [(28, 14), (21, 18), (21, 18), (21, 14), (21, 14), (32, 11), (32, 10), (28, 7), (28, 6), (17, 14), (14, 4), (10, 7)] 98 | board = Board() 99 | print(get_solution(board, all_pieces, [])) 100 | 101 | time.sleep(25) --------------------------------------------------------------------------------