X Tutup
import random, time #random.seed(42) # Select which puzzle to solve. SIZE = 4 # Set this to either 3 or 4. BLANK = 0 # Constants for directions: UP = 'up' DOWN = 'down' LEFT = 'left' RIGHT = 'right' def displayBoard(board): """Display `board` on the screen.""" for y in range(SIZE): # Iterate over each row. for x in range(SIZE): # Iterate over each column. if board[y * SIZE + x] == BLANK: print('__ ', end='') else: print(str(board[y * SIZE + x]).rjust(2) + ' ', end='') print() # Print a newline at the end of the row. def getNewBoard(): """Return a list of lists that represents a new tile puzzle.""" # NOTE: All the single digit numbers have a space in front of them. return list(range(1, SIZE * SIZE)) + [BLANK] def findBlankSpace(board): """Return an [x, y] list of the blank space's location.""" for x in range(SIZE): for y in range(SIZE): if board[y * SIZE + x] == BLANK: return [x, y] def makeMove(board, move): """Carry out the given move on the given board in `board`.""" bx, by = findBlankSpace(board) blankIndex = by * SIZE + bx if move == UP: tileIndex = (by + 1) * SIZE + bx elif move == LEFT: tileIndex = by * SIZE + (bx + 1) elif move == DOWN: tileIndex = (by - 1) * SIZE + bx elif move == RIGHT: tileIndex = by * SIZE + (bx - 1) board[blankIndex], board[tileIndex] = board[tileIndex], board[blankIndex] def undoMove(board, move): """Do the opposite move of `move` to undo it on `board`.""" if move == UP: makeMove(board, DOWN) elif move == DOWN: makeMove(board, UP) elif move == LEFT: makeMove(board, RIGHT) elif move == RIGHT: makeMove(board, LEFT) def getValidMoves(board): """Returns a list of the valid moves to make on this board.""" blankx, blanky = findBlankSpace(board) validMoves = [] if blanky != SIZE - 1: # Blank space is not on the bottom row. validMoves.append(UP) if blankx != SIZE - 1: # Blank space is not on the right column. validMoves.append(LEFT) if blanky != 0: # Blank space is not on the top row. validMoves.append(DOWN) if blankx != 0: # Blank space is not on the left column. validMoves.append(RIGHT) return validMoves def getNewPuzzle(): """Get a new puzzle by making random slides from a solved state.""" board = getNewBoard() for i in range(50): validMoves = getValidMoves(board) makeMove(board, random.choice(validMoves)) return board def solve(board, maxMoves): """Attempt to solve the puzzle in `board` in at most `maxMoves` moves. Returns True if solved, otherwise False.""" print('Attempting to solve in at most', maxMoves, 'moves...') solutionMoves = [] # A list of UP, DOWN, LEFT, RIGHT values. cache = set() hitsMisses = {'hits': 0, 'misses': 0} st = time.time() solved = attemptMove(board, solutionMoves, cache, hitsMisses, maxMoves, 0) duration = round(time.time() - st, 2) if solved: displayBoard(board) for move in solutionMoves: print('Move', move) makeMove(board, move) print() # Print a newline. displayBoard(board) print() # Print a newline. print('Solved in', len(solutionMoves), 'moves:') print(', '.join(solutionMoves)) #_log(maxMoves, duration, hitsMisses) #input('debug: check memory') return True # Puzzle was solved. else: #_log(maxMoves, duration, hitsMisses) #input('debug: check memory') return False # Unable to solve in maxMoves moves. def _log(maxMoves, duration, hitsMisses): with open('_log.csv', 'a') as fo: fo.write(str(maxMoves) + '\t' + str(round(duration, 1)) + '\t' + str(hitsMisses['hits']) + '\t' + str(hitsMisses['misses']) + '\t' + str(round(hitsMisses['hits'] / (hitsMisses['hits'] + hitsMisses['misses']) * 100, 2)) + '%\n') print(str(maxMoves) + '\t' + str(round(duration, 1)) + '\t' + str(hitsMisses['hits']) + '\t' + str(hitsMisses['misses']) + '\t' + str(round(hitsMisses['hits'] / (hitsMisses['hits'] + hitsMisses['misses']) * 100, 2)) + '%') def attemptMove(board, movesMade, stateCache, cacheHitsMisses, maxMoves, depth): """A recursive function that attempts all possible moves on `board` until a solution is found or we've reached the `maxMoves` limit. Returns True if a solution was found, in which case, movesMade contains the series of moves to solve the puzzle. Returns False if no solution was found in `maxMoves` moves.""" if depth > maxMoves: return False # BASE CASE if board == SOLVED_BOARD: # BASE CASE - Solved the puzzle: return True boardAsBytes = bytes(board) if boardAsBytes in stateCache: cacheHitsMisses['hits'] += 1 return False else: cacheHitsMisses['misses'] += 1 stateCache.add(boardAsBytes) # Attempt each of the valid moves: for move in getValidMoves(board): # Make the move: makeMove(board, move) movesMade.append(move) # RECURSIVE CASE - Attempt another move: if attemptMove(board, movesMade, stateCache, cacheHitsMisses, maxMoves, depth + 1): # If the puzzle is solved, return True: undoMove(board, move) return True # Undo this last move to set up for the next move: undoMove(board, move) movesMade.pop() # Remove the last move since it was undone. return False # BASE CASE - Unable to find a solution. # Start the program: SOLVED_BOARD = getNewBoard() gameBoard = getNewPuzzle() displayBoard(gameBoard) startTime = time.time() maxMoves = 10 while True: if solve(gameBoard, maxMoves): break # Break out of the loop when a solution is found. maxMoves += 1 print('Run in', round(time.time() - startTime, 1), 'seconds.')
X Tutup