imthe666st

PMTOK Solver

Jul 19th, 2020 (edited)
1,896
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.42 KB | None | 0 0
  1. '''
  2. Puzzle solver for Paper Mario: The Origami King
  3. '''
  4.  
  5. from queue import PriorityQueue
  6.  
  7. '''
  8. Define possible movements
  9. '''
  10. def rotate(board, row, amount):
  11.     # amount is capped at 12!
  12.     #if amount >= 12 or row >= 4:
  13.     #    raise Exception(f'Invalid move: R({row},{amount})')
  14.    
  15.     # make a new board, we want to keep the old one (and discard manually later)
  16.     new_board = [[x for x in brow] for brow in board]
  17.     new_board[row] = new_board[row][amount:] + new_board[row][:amount]
  18.     return new_board
  19.  
  20. # it's ugly but works..
  21. def slide(board, column, amount):
  22.     # 0 <= column < 6
  23.     # 0 <= amount < 8
  24.    
  25.     # make a new board, we want to keep the old one (and discard manually later)
  26.     new_board = [[x for x in brow] for brow in board]
  27.     # get column
  28.     ncolumn = [row[column] for row in new_board] + list(reversed([row[(column + 6) % 12] for row in new_board]))
  29.     # move the column
  30.     ncolumn = ncolumn[amount:] + ncolumn[:amount]
  31.     for i, item in enumerate(ncolumn[:4] + list(reversed(ncolumn[4:]))):
  32.         if i < 4:
  33.             new_board[i][column] = item
  34.         else:
  35.             new_board[i-4][(column + 6) % 12] = item
  36.     return new_board
  37.  
  38. '''
  39. define a heuristic for the priority queue.
  40. '''
  41. def unbound_enemies(board):
  42.     # copy the board
  43.     nboard = [[x for x in row] for row in board]
  44.  
  45.     # slide over all columns to see if the column is JUMP solved
  46.     for column in range(12):
  47.         # if they're all enemies, set them to 0, we don't care about them anymore
  48.         if sum([nboard[row][column] for row in range(4)]) == 4:
  49.             # set all to 0
  50.             for row in range(4):
  51.                 nboard[row][column] = 0
  52.  
  53.     # slide over the nearest two rows to see if they're HAMMER solved
  54.     for column in range(12):
  55.         if nboard[0][column] + nboard[1][column] + nboard[0][(column+1)%12] + nboard[1][(column+1)%12] == 4:
  56.             nboard[0][column] = 0
  57.             nboard[1][column] = 0
  58.             nboard[0][(column+1)%12] = 0
  59.             nboard[1][(column+1)%12] = 0
  60.  
  61.     return sum([enemy for row in nboard for enemy in row])
  62.  
  63. def lonely_enemies(board):
  64.     total_loneliness = 0
  65.     for row in range(4):
  66.         for column in range(12):
  67.             if board[row][column] == 0:
  68.                 continue
  69.  
  70.             if row > 0:
  71.                 total_loneliness += board[row-1][column]
  72.             if row < 3:
  73.                 total_loneliness += board[row+1][column]
  74.  
  75.             total_loneliness += board[row][(column+1)%12]
  76.             total_loneliness += board[row][(column-1)%12]
  77.  
  78.     return -total_loneliness
  79.  
  80. def heuristic(board):
  81.     return unbound_enemies(board) + lonely_enemies(board)
  82.  
  83. def solve(board, moves=3, progress=True):
  84.     pq = PriorityQueue()
  85.     pq.put((-1, 0, [], board))
  86.    
  87.     tested_fields = 0
  88.    
  89.     while not pq.empty():
  90.         heu, used_moves, path, cboard = pq.get()
  91.         tested_fields += 1
  92.        
  93.         # comment this out if needed, just shows some progress
  94.         if progress and not tested_fields % 25000:
  95.             print(f'Tested {tested_fields} fields...')
  96.         # check if the board is the solution
  97.         # todo!
  98.         if unbound_enemies(cboard) == 0:
  99.             yield (used_moves, path, tested_fields, cboard)
  100.        
  101.         if used_moves >= moves:
  102.             continue
  103.         # otherwise add all possible moves
  104.         # rotating
  105.         for row in range(4):
  106.             for amount in range(1,12):
  107.                 nboard = rotate(cboard, row, amount)
  108.                 # evaluate and put..
  109.                 npath = path + [('rotate', row, amount)]
  110.                 pq.put((heuristic(nboard), used_moves+1, npath, nboard))
  111.        
  112.         # sliding
  113.         for column in range(6):
  114.             for amount in range(1,8):
  115.                
  116.                 nboard = slide(cboard, column, amount)
  117.                 # evaluate and put..
  118.                 npath = path + [('slide', row, amount)]
  119.                 pq.put((heuristic(nboard), used_moves+1, npath, nboard))
  120.  
  121. '''
  122. Represents the following battle:
  123. https://cdn.discordapp.com/attachments/619995837828694016/734457814939992114/unknown.png
  124.  
  125. You only need to modify these 3 fields!
  126. '''
  127. battle_field = [
  128.     [0,1,1,0,0,0,0,1,1,0,0,0],
  129.     [1,0,1,0,0,0,1,0,1,0,0,0],
  130.     [0,0,0,0,0,0,0,0,0,0,0,0],
  131.     [0,0,0,0,0,0,0,0,0,0,0,0]
  132. ]
  133.  
  134. moves = 3
  135.  
  136. for (moves, path, tested, board) in solve(battle_field, moves=moves, progress=False):
  137.     print(*path)
  138.  
  139.  
Advertisement
Add Comment
Please, Sign In to add comment