Advertisement
Guest User

Untitled

a guest
Mar 29th, 2018
425
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.75 KB | None | 0 0
  1. from heapq import heappush,heappop
  2. import sys
  3. import random
  4.  
  5. # Как это работает:
  6. # - доска кодируется массивом из 16 чисел 1..16, где 16 означает пустое
  7. # место (глупо, надо переделать на 0). Индексы массива 0..15.
  8. # - состояние доски включает в себя также "замороженные" индексы, которые мы
  9. # не хотим больше двигать.
  10. # главная функция board.moveto(val, to) находит способ передвинуть костяшку
  11. # номер val с ее текущего места на индекс номер to. Чтобы найти такой пусть,
  12. # мы рассматриваем доску как граф, в котором замороженные вершины-индексы удаляем.
  13. # Среди оставшихся находим самый быстрый путь алгоритмом Дайкстры. Теперь, когда
  14. # мы его нашли, мы хотим собственно двигать костяшку по этому пути, и для каждого
  15. # ее шага сначала находим пусть для 16 (пустого места) встать на следующий шаг в пути
  16. # (это еще один Дайкстра, только ведомая костяшка тоже заморожена) и потом обменяться
  17. # с ведомой костяшкой. Потом опять позиционируем пустышку, и так далее.
  18. # Все это будет работать кроме тех случаев, когда вложенный Дайкстра для пустышки не
  19. # может найти путь, потому что граф рассоединился. Это то, что происходит в знакомой
  20. # ситуации, когда 1,2,3 уже стоят на месте, а 4 придвинулся к своему, но не может встать,
  21. # не потревожив 1,2,3. Для таких случаев у нас есть две hardcoded последовательности,
  22. # для угловой по горизонтали или по вертикали.
  23. # Мы расставляем костяшки в порядке 1-8, потом 9,13 (в этом порядке), 10, 14, 11 и
  24. # пустышку в угол, теперь либо все в порядке, либо 12/15 поменяны местами.
  25.  
  26. # Returns the set of the 2-4 neighbours of board cell n
  27. def neighbors(n):
  28.   s = set(i for i in [n+4,n-4] if i >= 0 and i < 16)
  29.   next = set([n+1]) if n % 4 != 3 else set()
  30.   prev = set([n-1]) if n % 4 != 0 else set()
  31.   return s | next | prev
  32.  
  33. # Finds the shortest distance path. Assumes
  34. # vertices are 0..15 except those in the set 'frozen'.
  35. def dijkstra(fr, to, frozen):
  36.   h = []
  37.   heappush(h, (0,fr))
  38.   final_edge = {}
  39.   visited = set()
  40.   while h:
  41.     (d, v) = heappop(h)
  42.     if v in visited:
  43.       continue
  44.     visited.add(v)
  45.     if v == to:
  46.       break
  47.     for n in neighbors(v) - frozen - visited:
  48.       heappush(h, (d+1, n))
  49.       if n not in final_edge or final_edge[n][1] > d:
  50.         final_edge[n] = v, d
  51.         # print "final_edge from: ", v, " to: ", n, " for distance: ", d
  52.   if to not in final_edge:
  53.     return []
  54.   moves = [to]
  55.   v = to
  56.   while v != fr:
  57.     (v, d) = final_edge[v]
  58.     moves.append(v)
  59.   return list(reversed(moves))
  60.  
  61. class board:
  62.   def __init__(self, randomize=False):
  63.     self.arr = [i+1 for i in range(0,16)]
  64.     self.inv = [i-1 for i in range(0,17)]
  65.     self.moves = []
  66.     self.frozen = set()
  67.     if randomize:
  68.       random.shuffle(self.arr)
  69.     for (k,v) in enumerate(self.arr):
  70.       self.inv[v] = k
  71.   def at(self, i): return self.arr[i]
  72.   def where(self, n): return self.inv[n]
  73.   def show(self):
  74.     for i in range(0,16):
  75.       sys.stdout.write('%2d ' % self.arr[i])
  76.       if i % 4 == 3:
  77.         print ""
  78.  
  79.   # Makes a puzzle move. n must be a cell adjacent to the blank.
  80.   def move(self, n):
  81.     blanks = [i for i in neighbors(n) if self.arr[i]==16]
  82.     if len(blanks) != 1:
  83.       raise Exception('Invalid move at: ' + str(n))
  84.     val = self.arr[n]
  85.     self.arr[n], self.arr[blanks[0]] = 16, val
  86.     self.inv[val], self.inv[16] = blanks[0], n
  87.     self.moves.append(n)
  88.  
  89.   # Execute given moves and add them to the list.
  90.   def execute(self, new_moves):
  91.     for n in new_moves:
  92.       self.move(n)
  93.     self.moves.extend(new_moves)
  94.  
  95.   # Position the blank in a wanted location. Returns true/false if
  96.   # successful/impossible.
  97.   def moveblank(self, to, frozen=None):
  98.     if frozen is None:
  99.       frozen = self.frozen
  100.     if self.where(16) != to:
  101.       path = dijkstra(self.where(16), to, frozen)
  102.       if not path:
  103.         return False
  104.       self.execute(path[1:])
  105.     return True
  106.  
  107.   # Move value val to cell to, freeze cell to.
  108.   def moveto(self, val, to):
  109.     # print "---moving val %d from %d to %d" % (val, self.where(val), to)
  110.     # print self.frozen
  111.     fr = self.where(val)
  112.     if fr == to:
  113.       self.frozen.add(to)
  114.       return
  115.     # first find a path through non-frozen cells
  116.     path = dijkstra(fr, to, self.frozen)
  117.     if not path:
  118.       raise Exception('Path not found')
  119.     # For each path step except last, position the blank there and make the move
  120.     cur = fr  # current location of the moving piece
  121.     for p in path[1:-1]:
  122.       self.moveblank(p, self.frozen | set([cur]))
  123.       self.execute([cur])
  124.       cur = p
  125.     # We're at the final step. Can we move the blank there?
  126.     if self.moveblank(path[-1], self.frozen | set([cur])):
  127.       self.execute([cur])
  128.     else:
  129.       # We must be at a right or bottom edge.
  130.       if cur == path[-1]+4:  # right edge
  131.         self.moveblank(cur+4, self.frozen | set([cur]))
  132.         self.execute([cur, cur-4, cur-5, cur-1, cur, cur+4, cur+3, cur-1, cur-5, cur-4, cur])
  133.       elif cur == path[-1]+1:  # bottom edge
  134.         self.moveblank(cur+1, self.frozen | set([cur]))
  135.         self.execute([cur, cur-1, cur-5, cur-4, cur, cur+1, cur-3, cur-4, cur-5, cur-1, cur])
  136.       else:
  137.         raise Exception('Unable to perform final step')
  138.     self.frozen.add(to)
  139.     # print "---done moving val %d" % val
  140.  
  141. def solve_board(board):
  142.   for piece in [1,2,3,4,5,6,7,8,9,13,10,14]:
  143.     board.moveto(piece, piece-1)
  144.   board.moveto(11, 10)
  145.   board.moveblank(15)
  146.   # board.show()
  147.   return
  148.  
  149. solvable = 0
  150. unsolvable = 0
  151. for i in range(0,1000):
  152.   b = board(True)
  153.   solve_board(b)
  154.   for i in [0,1,2,3,4,5,6,7,8,9,10,12,13]:
  155.     if b.at(i) != i+1:
  156.       raise Exception('Unsolved')
  157.   if b.at(11) == 12:
  158.     solvable += 1
  159.   else:
  160.     unsolvable += 1
  161. print "solvable: %d unsolvable: %d" % (solvable, unsolvable)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement