If you don't have a copy of the graphics module we used last week, download a copy of this module by clicking HERE. Once this file is downloaded to your desktop, move it to your Python working area (or work on the desktop). This Python module must be in the same folder as any program code that uses it.
In this two part lab, we will complete the program to play a 15 Puzzle game.
Recall the basic game algorithm from last week:
1. Create the game window and read the name of the data file.
2. Initialize the game board with the numbers from the data file.
3. Draw the game board in the window.
4. Until the player has won the game, do the following:
a. Get the row and column on the game board that the user clicks using the mouse.b. Move the number in the row and column to the cell with 0 in it if the chosen row and column is adjacent to the zero cell, horizontally or vertically.
c. Draw the game board in the window.
d. Check to see if the player has won the game.
5. Print GAME OVER and wait for the player to hit enter to close the window.
Let's start with the game as defined from last week (click HERE for a new copy if you need it):
# puzzle15_part2.py
# Program that simulates a 15 Puzzle game
from graphics import *
def main():
filename = raw_input("Input name of puzzle file: ")
window = GraphWin("Puzzle 15", 200, 200)
board = initialize_board(filename)
display_numbers(window,board)
game_over = False
while game_over == False:
p = window.getMouse()
col = p.getX()/50 # compute column where player clicked
row = p.getY()/50 # compute row where player clicked
update_board(board, row, col)
display_numbers(window,board)
game_over = check_for_winner(board)
message = Text(Point(100,100),"GAME OVER")
message.setSize(24)
message.setTextColor("orange")
message.draw(window)
raw_input("Press <ENTER> to quit.")
window.close()
def initialize_board(filename):
infile = open(filename, "r")
board = []
for i in range(4):
board.append([])
for row in range(4):
for col in range(4):
columnvalue = eval(infile.readline())
board[row].append(columnvalue)
return board
def display_numbers(window,board):
for row in range(4):
for col in range(4):
square = Rectangle(Point(col*50,row*50),Point((col+1)*50, (row+1)*50))
square.setFill("white")
square.draw(window)
if board[row][col] != 0:
center = Point(col*50+25,row*50+25)
number = Text(center, board[row][col])
number.setSize(24)
number.setTextColor("purple")
number.draw(window)
def update_board(board, row, col):
return #remove this line when you complete this function
def check_for_winner(board):
return False #remove this line when you complete this function
main()
We will write the remaining 2 functions today.
This function has three parameters that come from the main function: board, row and col. The variable board represents a two-dimensional array (an array of arrays). In this array, we are storing the current locations of each number from 1 to 15. The blank cell is represented by the value 0. The variables row and col represent the row and column where the player clicked in the game window.
What we need to do is determine if the user clicked adjacent to the row and column where the blank cell is (i.e. where the 0 is stored in the array). If so then the number in that cell should swap with the 0.
We must be careful. To see if the player clicked on a cell next to the cell with the 0, we might just choose to look above, below, to the left and the to the right of the clicked cell to see if we find a 0 but the player might have clicked on a cell along the edge of the array. For example, if the player clicks along the top row, then there is no cell above the clicked cell. So we must check two things: is there a cell adjacent to the one clicked and if so, does it have a 0?
Here's how we can achieve this in Python:
def update_board(board, row, col):
if row > 0 and board[row-1][col] == 0:
board[row-1][col] = board[row][col]
board[row][col] = 0
return
if row < 3 and board[row+1][col] == 0:
board[row+1][col] = board[row][col]
board[row][col] = 0
return
if col > 0 and board[row][col-1] == 0:
board[row][col-1] = board[row][col]
board[row][col] = 0
return
if col < 3 and board[row][col+1] == 0:
board[row][col+1] = board[row][col]
board[row][col] = 0
return
In the first if statement, we first check to see that we are not in the first row (i.e. not in row 0), if we are not AND the cell above the current row and col (at board{row-1][col]) contains the 0, then we copy the value in the clicked cell to the cell above and set the clicked cell to 0.
NOTE: In Python, we must check these conditions in the order shown. Normally, you would think that A and B is the same as B and A, but not in program code. When you compute A and B, A is evaluated first, and if it is False, then B is never evaluated. In the first if statement, this saves us if row == 0. The expression row > 0 is False, so we never try to check if board[row-1][col] is 0 which is good since row-1 would be -1 if row is 0 and there is no row numbered -1 in the array.
To determine if the player has won, we need to see if the numbers in the array row by row, left to right, are in numerical order from 1 to 15. So we can use the following algorithm shown in Python.
def check_for_winner(board):
num = 1
row = 0
col = 0
while num <= 15:
if board[row][col] == num:
num = num + 1
col = col + 1
if col > 3:
col = 0
row = row + 1
else:
return False
return True
This function scans the array starting with row 0 and column 0. If it finds a 1 in that cell of the board, it updates num to 2 and moves to the next column. If it finds a 2 in the next cell of the board, it updated num to 3, etc. If it gets to the end of a row (column is greater than 3), it resets column back to 0 and increases row by 1 to go to the next row. At any point, if the next num is not the expected value, we return False immediately, exiting the loop and returning back to the main function. However, if all 15 numbers match, cell by cell, then the loop exits and we return True instead.
Run this program and see if you see the game board set up as expected.