Sliding Tile Puzzle
This classic puzzle relies on a 4 × 4 board with 15 numbered tiles and one free space. The objective is to slide the tiles until the numbers are in the correct order, going left to right and top to bottom. Tiles can only slide; you’re not allowed to directly pick them up and rearrange them. Some versions of this puzzle toy feature scrambled images that form a complete picture once solved.
More information about sliding tile puzzles can be found at https://en.wikipedia.org/wiki/Sliding_puzzle.
sliding_tile_puzzle.py
1"""Sliding Tile Puzzle, by Al Sweigart al@inventwithpython.com
2Slide the numbered tiles into the correct order.
3This code is available at https://nostarch.com/big-book-small-python-programming
4Tags: large, game, puzzle"""
5
6import random, sys
7
8BLANK = ' ' # Note: This string is two spaces, not one.
9
10
11def main():
12 print('''Sliding Tile Puzzle, by Al Sweigart al@inventwithpython.com
13
14 Use the WASD keys to move the tiles
15 back into their original order:
16 1 2 3 4
17 5 6 7 8
18 9 10 11 12
19 13 14 15 ''')
20 input('Press Enter to begin...')
21
22 gameBoard = getNewPuzzle()
23
24 while True:
25 displayBoard(gameBoard)
26 playerMove = askForPlayerMove(gameBoard)
27 makeMove(gameBoard, playerMove)
28
29 if gameBoard == getNewBoard():
30 print('You won!')
31 sys.exit()
32
33
34def getNewBoard():
35 """Return a list of lists that represents a new tile puzzle."""
36 return [['1 ', '5 ', '9 ', '13'], ['2 ', '6 ', '10', '14'],
37 ['3 ', '7 ', '11', '15'], ['4 ', '8 ', '12', BLANK]]
38
39
40def displayBoard(board):
41 """Display the given board on the screen."""
42 labels = [board[0][0], board[1][0], board[2][0], board[3][0],
43 board[0][1], board[1][1], board[2][1], board[3][1],
44 board[0][2], board[1][2], board[2][2], board[3][2],
45 board[0][3], board[1][3], board[2][3], board[3][3]]
46 boardToDraw = """
47+------+------+------+------+
48| | | | |
49| {} | {} | {} | {} |
50| | | | |
51+------+------+------+------+
52| | | | |
53| {} | {} | {} | {} |
54| | | | |
55+------+------+------+------+
56| | | | |
57| {} | {} | {} | {} |
58| | | | |
59+------+------+------+------+
60| | | | |
61| {} | {} | {} | {} |
62| | | | |
63+------+------+------+------+
64""".format(*labels)
65 print(boardToDraw)
66
67
68def findBlankSpace(board):
69 """Return an (x, y) tuple of the blank space's location."""
70 for x in range(4):
71 for y in range(4):
72 if board[x][y] == ' ':
73 return (x, y)
74
75
76def askForPlayerMove(board):
77 """Let the player select a tile to slide."""
78 blankx, blanky = findBlankSpace(board)
79
80 w = 'W' if blanky != 3 else ' '
81 a = 'A' if blankx != 3 else ' '
82 s = 'S' if blanky != 0 else ' '
83 d = 'D' if blankx != 0 else ' '
84
85 while True:
86 print(' ({})'.format(w))
87 print('Enter WASD (or QUIT): ({}) ({}) ({})'.format(a, s, d))
88
89 response = input('> ').upper()
90 if response == 'QUIT':
91 sys.exit()
92 if response in (w + a + s + d).replace(' ', ''):
93 return response
94
95
96def makeMove(board, move):
97 """Carry out the given move on the given board."""
98 # Note: This function assumes that the move is valid.
99 bx, by = findBlankSpace(board)
100
101 if move == 'W':
102 board[bx][by], board[bx][by+1] = board[bx][by+1], board[bx][by]
103 elif move == 'A':
104 board[bx][by], board[bx+1][by] = board[bx+1][by], board[bx][by]
105 elif move == 'S':
106 board[bx][by], board[bx][by-1] = board[bx][by-1], board[bx][by]
107 elif move == 'D':
108 board[bx][by], board[bx-1][by] = board[bx-1][by], board[bx][by]
109
110
111def makeRandomMove(board):
112 """Perform a slide in a random direction."""
113 blankx, blanky = findBlankSpace(board)
114 validMoves = []
115 if blanky != 3:
116 validMoves.append('W')
117 if blankx != 3:
118 validMoves.append('A')
119 if blanky != 0:
120 validMoves.append('S')
121 if blankx != 0:
122 validMoves.append('D')
123
124 makeMove(board, random.choice(validMoves))
125
126
127def getNewPuzzle(moves=200):
128 """Get a new puzzle by making random slides from a solved state."""
129 board = getNewBoard()
130
131 for i in range(moves):
132 makeRandomMove(board)
133 return board
134
135
136# If this program was run (instead of imported), run the game:
137if __name__ == '__main__':
138 main()