Advertisement
DXPower

Pentominos Solver

Jun 22nd, 2018
664
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.56 KB | None | 0 0
  1. import os
  2. from colorama import init, deinit, Fore, Back, Style
  3. clear = lambda: os.system('cls')
  4. clear()
  5.  
  6. init()
  7.  
  8. colors = [Fore.BLACK, Fore.WHITE, Fore.WHITE + Style.BRIGHT, Fore.RED + Style.DIM, Style.BRIGHT + Fore.GREEN, Fore.YELLOW, Fore.BLUE, Fore.MAGENTA, Fore.CYAN, Fore.RED + Style.BRIGHT, Fore.BLUE + Style.BRIGHT, Fore.MAGENTA + Style.BRIGHT, Fore.YELLOW + Style.BRIGHT]
  9.  
  10. # Shapes
  11. shapes = [    
  12.     { "variations":
  13.         ("OOOO\n" # L
  14.          "O..."),
  15.       "used": False },
  16.    
  17.     { "variations":
  18.         ("..OO\n" # N
  19.          "OOO."),
  20.       "used": False },
  21.    
  22.     { "variations":
  23.         ("OOO\n" # P
  24.          ".OO"),
  25.       "used": False },
  26.    
  27.     { "variations":
  28.         ("OOO\n" # T
  29.      ".O.\n"
  30.      ".O."),
  31.       "used": False },
  32.    
  33.     { "variations":
  34.         ("O.O\n" # U
  35.          "OOO"),
  36.       "used": False },
  37.    
  38.     { "variations":
  39.         ("O..\n" # V
  40.      "O..\n"
  41.      "OOO"),
  42.       "used": False },
  43.    
  44.     { "variations":
  45.         ("..O\n" # W
  46.          ".OO\n"
  47.          "OO."),
  48.       "used": False },
  49.    
  50.     { "variations":
  51.         (".O.\n" # X
  52.          "OOO\n"
  53.          ".O."),
  54.       "used": False },
  55.    
  56.     { "variations":
  57.         ("OOOO\n" # Y
  58.         ".O.."),
  59.       "used": False },
  60.    
  61.     { "variations":
  62.         ("OO.\n" # Z
  63.          ".O.\n"
  64.          ".OO"),
  65.       "used": False },
  66.      
  67.     { "variations":
  68.         ("OOOOO"),
  69.       "used": False
  70.     }, # I
  71.    
  72.     { "variations":
  73.         (".OO\n" # F
  74.          "OO.\n"
  75.          ".O."),
  76.       "used": False }
  77. ]
  78.  
  79. # Interpret shape strings above
  80.  
  81. for i in range(0, len(shapes)):
  82.     variations = []
  83.  
  84.     shape = shapes[i]["variations"].split("\n")
  85.  
  86.     # Turn the list of strings of the shape into list of lists of 0's and 1's representing shape
  87.     for j in range(0, len(shape)):
  88.         shape[j] = list(shape[j])
  89.  
  90.         shape[j] = [0 if c == "." else 1 for c in shape[j]]
  91.  
  92.     mirror = [list(reversed(row)) for row in shape]
  93.  
  94.     for _ in range(0, 4):
  95.         shape = [list(elem) for elem in zip(*shape[::-1])] # Rotate clockwise
  96.         mirror = [list(elem) for elem in zip(*mirror[::-1])]
  97.  
  98.         if not shape in variations:
  99.             variations.append(shape)
  100.        
  101.         if not mirror in variations:
  102.             variations.append(mirror)
  103.  
  104.     shapes[i]["variations"] = variations
  105.  
  106. input_string = input("Enter width and height: \n").split(" ")
  107. width = int(input_string[0])
  108. height = int(input_string[1])
  109. pieces_needed = (width * height) / 5
  110. board = [[0 for i in range(0, width)] for j in range(0, height)]
  111. num_steps = 0
  112.  
  113. def print_board():
  114.     global num_steps
  115.  
  116.     for r in board:
  117.         s = ""
  118.         for c in r:
  119.             s += colors[c] + "██" + Style.RESET_ALL
  120.        
  121.         print(s + Style.RESET_ALL + "|")
  122.  
  123.     print("----------------------------")
  124.     print(num_steps)
  125.  
  126. def try_place_shape(shape, x, y, id, adj):
  127.     if y + len(shape) > height or x + len(shape[0]) > width:
  128.         return False
  129.  
  130.     foundAdj = False
  131.  
  132.     for j, r in enumerate(shape):
  133.         by = y + j
  134.  
  135.         for i, c in enumerate(r):
  136.             bx = x + i
  137.  
  138.             if c == 1:
  139.                 if board[by][bx] > 0:
  140.                     return False
  141.                 elif adj and not foundAdj: # Some shapes may not touch another when placed in their spot. It's useless to place those
  142.                     if bx > 0 and board[by][bx - 1] > 0:
  143.                         foundAdj = True
  144.                     elif bx + 1 < width and board[by][bx + 1] > 0:
  145.                         foundAdj = True
  146.                     elif by > 0 and board[by - 1][bx] > 0:
  147.                         foundAdj = True
  148.                     elif by + 1 < height and board[by + 1][bx] > 0:
  149.                         foundAdj = True
  150.                     else:
  151.                         foundAdj = False
  152.  
  153.     if adj and not foundAdj:
  154.         return False
  155.  
  156.     for j, r in enumerate(shape):
  157.         by = y + j
  158.         for i, c in enumerate(r):
  159.             bx = x + i
  160.  
  161.             if c == 1:
  162.                 board[by][bx] = id
  163.  
  164.     # Check for impossible areas. Done in one scan by combining areas it sees above with area's it's created by traveling from left to right
  165.     areas = {}
  166.     combines = {}
  167.     ai = -1
  168.  
  169.     for j, r in enumerate(board):
  170.         for i, c in enumerate(r):
  171.             if c == 0:
  172.                 left = 1
  173.                 up = 1
  174.  
  175.                 if i > 0:
  176.                     left = board[j][i - 1]
  177.                 if j > 0:
  178.                     up = board[j - 1][i]
  179.                 elif i == 0 and j == 0:
  180.                     areas[ai] = 1
  181.                     board[j][i] = ai
  182.                     ai -= 1
  183.                     continue
  184.                
  185.                 if left < 0 and up < 0:
  186.                     if left == up:
  187.                         areas[left] += 1
  188.                     else:
  189.                         areas[left] += 1
  190.                         combines[left] = up
  191.                    
  192.                     board[j][i] = left
  193.                 elif left < 0:
  194.                     areas[left] += 1
  195.                     board[j][i] = left
  196.                 elif up < 0:
  197.                     areas[up] += 1
  198.                     board[j][i] = up
  199.                 else:
  200.                     ai -= 1
  201.                     areas[ai] = 1
  202.                     board[j][i] = ai
  203.    
  204.     # Reset our math
  205.     for j, r in enumerate(board):
  206.         for i, c in enumerate(r):
  207.             if c < 0:
  208.                 board[j][i] = 0
  209.  
  210.     # Combine all areas      
  211.     for key, value in combines.items():
  212.         v = value # Sometimes chains of areas may exist
  213.         while v in combines:
  214.             v = combines[v]
  215.  
  216.         areas[v] += areas[key]
  217.         del areas[key]
  218.  
  219.     # Check if any areas are "cutoff". This is a slight optimization that sometimes prevents the contuation of a search that has an obvious "hole" that can only be filled by
  220.     # an already used piece
  221.     if len(areas) > 1:
  222.         remove_shape(shape, x, y)
  223.         return False
  224.  
  225.     # If the area left is not a multiple of 5, then it is unsolvable.
  226.     for key, value in areas.items():
  227.         if value < 5 or value % 5 != 0:
  228.             remove_shape(shape, x, y)
  229.             return False
  230.    
  231.     return True
  232.  
  233. def remove_shape(shape, x, y):
  234.     for j, r in enumerate(shape):
  235.         for i, c in enumerate(r):
  236.             if c == 1:
  237.                 board[y + j][x + i] = 0
  238.  
  239. solution_stack = []
  240.  
  241. # It's safe to use a recursive search because the max depth we can go is 12.
  242. def recursive_search():
  243.     global num_steps
  244.    
  245.     for i, shape in enumerate(shapes):
  246.         if shape["used"]:
  247.             continue
  248.  
  249.         for j, variation in enumerate(shape["variations"]):
  250.             for x in range(0, width):
  251.                 for y in range(0, height):
  252.                     if try_place_shape(variation, x, y, i + 1, len(solution_stack) != 0):
  253.                         shape["used"] = True
  254.                         solution_stack.append({ "shape": variation, "x": x, "y": y})
  255.                         num_steps += 1
  256.  
  257.                         print_board()
  258.                         clear()
  259.  
  260.                         if len(solution_stack) == pieces_needed:
  261.                             return True
  262.                         elif recursive_search():
  263.                             return True
  264.                         else:
  265.                             shape["used"] = False
  266.                             solution_stack.pop()
  267.                             remove_shape(variation, x, y)
  268.  
  269. recursive_search()
  270. print_board()
  271.  
  272. input()
  273. deinit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement