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 create a program to play the game Lights Out.
Quite often, data that we need for a program will be stored in a data file so we don't have to enter the data one by one using the keyboard. Recall the program we used to find the maximum of a set of 5 numbers:
def main():
A = [4, 7, 3, 9, 8]
i = 0
max = A[0]
while i < len(A)-1:
i = i + 1
if A[i] > max:
max = A[i]
print "The maximum positive integer is", max
main()
Let's assume that there is a text file that contains the 5 data values shown above, one per line. We can modify the program to initialize the array with 5 data values from a file as follows:
def main():
A = [] # start with an empty array
filename = raw_input("Input filename containing data: ")
infile = open(filename, "r")
for j in range(5):
number = eval(infile.readline())
A.append(number)
i = 0
max = A[0]
while i < len(A)-1:
i = i + 1
if A[i] > max:
max = A[i]
print "The maximum positive integer is", max
main()
Assume there is a text file (created using pico) named nums.txt in the same folder as our program, and it contains the following data:
4 7 3 9 8
The program starts with an empty array A. It then asks the user for the name of the data file using the raw_input function. For example, the user might enter nums.txt. We then open the file for reading using the open function. The "r" means we want to read from the file specified by the filename. The function returns a link to the file which we store in the variable infile. Then whenever we want to go to the file for data, we use the variable infile.
The program now loops 5 times and reads each line of the file in one at a time, converting the string read in to a value (number) using eval(infile.readline()), and then appending this value to the array. The expression infile.readline() reads the next line from the text file, and the function eval converts it to a number since it is read in as a string.
The Lights Out game is a popular game that consists of a 5 X 5 grid of lights. When the game starts, some of the lights are on and some are off, as shown below.
The object of the game is to turn all of the lights off. When you click on a light, it toggles (from off to on, or from on to off) and so does its four neighbors (above, below, to the left and to the right). Not all lights have 4 neighbors, however. For example, the corner lights only have 2 neighbors.
The initial state of the lights will be given by a data file, where 0 represents off and 1 represents on. The file will have 25 numbers, one for each light, one per line, in order row by row, left to right. Click HERE to see how the game above would be stored in the file. (Right click it to save it to your desktop.)
The game follows the following algorithm:
1. Draw the lines for the board. 2. Initialize the game board lights. 3. Draw all of the lights on the board. 4. Set GameOver to False. 5. While GameOver is False do the following: a. Get the row and column of the light button that is clicked. b. Toggle the chosen light and its neighbors. c. Draw all of the lights on the board. d. If all of the lights on the board are off, set GameOver to True. 6. Output "YOU WIN!"
Each of the major steps of the algorithm will be represented by Python functions. Here is the main function and the other functions, empty for now:
# lightsout.py
# Program that simulates a lights-out game
from graphics import *
def main():
filename = raw_input("Input name of puzzle file: ")
window = GraphWin("Lights Out", 250, 250)
board = initialize_board(filename)
draw_lines(window)
display_lights(window,board)
game_over = False
while game_over == False:
p = window.getMouse()
print p.getX()," ",p.getY()
col = p.getX()/50 # compute column where player clicked
row = p.getY()/50 # compute row where player clicked
update_board(board, row, col)
display_lights(window,board)
game_over = check_for_winner(board)
print "GAME OVER"
raw_input("Press <ENTER> to quit.")
window.close()
You can download a copy of this to get started by right-clicking HERE to save the file.
The main function starts by asking the user for the name of the file containing the data for the lights. It opens a 250 X 250 window for the game and initializes the board array with the data in the file using another function named initialize_board. It then draws the horizontal and vertical lines in the window to divide it into 25 equal squares and then draws the circular lights, one per square, using the draw_lines and display_lights functions. We will write these functions today.
The variable board represents a two-dimensional array (an array of arrays). To create it, we have to create a 5 X 5 array, we have to create an empty array and then append 5 empty arrays to it:
[ [], [], [], [], [] ]
The following code will read in the 25 light values from our data file and append them to this array:
def initialize_board(filename):
infile = open(filename, "r")
board = []
for i in range(5):
board.append([])
for row in range(5):
for col in range(5):
columnvalue = eval(infile.readline())
board[row].append(columnvalue)
return board
The first four lines create the empty array of arrays. We then have a nested loop that reads in the 25 values for the array of arrays.
Drawing the 8 lines needed to divide up the window is straight-forward:
def draw_lines(window):
line1 = Line(Point(0,50),Point(250,50))
line1.draw(window)
line2 = Line(Point(0,100),Point(250,100))
line2.draw(window)
line3 = Line(Point(0,150),Point(250,150))
line3.draw(window)
line4 = Line(Point(0,200),Point(250,200))
line4.draw(window)
line5 = Line(Point(50,0),Point(50,250))
line5.draw(window)
line6 = Line(Point(100,0),Point(100,250))
line6.draw(window)
line7 = Line(Point(150,0),Point(150,250))
line7.draw(window)
line8 = Line(Point(200,0),Point(200,250))
line8.draw(window)
To draw the lights, we need to draw 25 circles, filled in with white (off) or yellow (on). Here are the rows and columns of the game, along with the location of the centers of the circles in the window:
row column center for circle in window 0 0 25, 25 0 1 75, 25 0 2 125,25 0 3 175,25 0 4 225,25 1 0 25, 75 1 1 75, 75 1 2 125,75 1 3 175,75 1 4 225,75 2 0 25, 125 2 1 75, 125 2 2 125,125 2 3 175,125 2 4 225,125 etc.
Do you see the pattern here? For a given row and column, the circle has a center at (column*50+25, row*50+25). All circles have a radius of 25. Here is a function that displays the lights of the board in the window:
def display_lights(window,board):
for row in range(5):
for column in range(5):
center = Point(column*50+25,row*50+25)
circ = Circle(center,25)
if (board[row][column] == 1):
circ.setFill("yellow")
else:
circ.setFill("white")
circ.draw(window)
Run this program and see if you see the game board set up as expected.