I thought I'd set myself a challenge as I've been programming in Python a little while now (nothing too heavy, mind you!) and make a 4-in-a-row game which I could play against the computer or another person, on a grid which i specify how big. I though I had the grid sorted, but now I'm coming to put in the gameplay and it's not working. :(

``````#!/usr/bin/env python
import sys

def main():
quit = False
global x
x = raw_input("How wide would you like your game? (4 - 8) ")
if x.isdigit():
x  = int(x)
else:
print "Please enter a non-negative integer!"
main()
# Any smaller than 4 and the game is not very playable, any larger than 8 and it could go on \$
global y
y = raw_input("And how tall? (4 - 8) ")
if y.isdigit():
y = int(y)
else:
print "Please enter a non-negative integer between 4 and 8!"
main()
inputPlayerParameters()

def inputPlayerParameters():
z = raw_input("Enter '1' to play the computer or enter '2' to play another human: ")
if z.isdigit():
z = int(z)
if z == 1:
print "You will play the computer"
gameBoard = initialiseGrid(x, y)
while True:
drawGrid(gameBoard)
player1Go(gameBoard)
computerGo(gameBoard)
elif z == 2:
print "You will play another person"
gameBoard = initialiseGrid(x,y)
while True:
drawGrid(gameBoard)
player1Go(gameBoard)
player2Go(gameBoard)
else:
print "You did not enter 1 or 2."
inputPlayerParameters()
else:
print "ERROR: DID NOT ENTER '1' OR '2'"
inputPlayerParameters()

def initialiseGrid(x,y):
rows = [0] * x
i = 0
grid = [0] * y
while i < y:
grid[i] = rows
i=i+1
return grid

def drawGrid(grid):
for element in grid:
print element
for i in range(x):
print "",i+1,

def player1Go(grid):
print "Enter the column number you wish to go in: "
userInput = raw_input(">>> ")
if userInput == "quit":
confirm = raw_input("Are you sure you want to quit? y/n ")
if confirm == "y":
sys.exit(0)
elif userInput == "save":
confirm = raw_input("Save the game? y/n ")
if confirm == "y":
filename = open("connect4game.txt", "wb")
filename.dump(grid)
filename.close()
elif userInput.isdigit():
userInput = int(userInput)
print "You went in column", userInput
if (userInput > x):
print "Please enter a valid column number!"
player1Go(grid)
else:
grid[userInput-1][0] = "o"
checkForWin()
else:
print "Input not recognised! Try again."
player1Go()

def player2Go(grid):
print "Enter the column number you wish to go in: "
userInput = raw_input(">>> ")
if userInput == "quit":
confirm = raw_input("Are you sure you want to quit? y/n ")
if confirm == "y":
sys.exit(0)
else:
checkForWin()

def computerGo(grid):
print "Computer thinking.."
checkForWin()

def quitGame(message, draw = True):
if draw == True:
setGrid()
print message
exit()

def checkForWin():
print "Checking state..."
#       if win = True:
#               print "GAME OVER!"

main()``````

For example, if I set the grid to 4x4 and play against myself: when I specify that I want to go in column 1, the entire column fills with "o" instead of just the bottom square.

I would very much appreciate any help! :)

The problem is here
rows = [0] * x
run the following code and see what you get

``````rows = [0] * x
for row in rows:
print id(row)
##
##   you should see that all of the memory addresses are the same
##   meaning that you have the same object in the list several times,
##   so when you change one, they all change because they are
##   the same object.  Instead, try the following
##
rows = [[0] for x in range(0, 3)]
for row in rows:
print id(row)``````

Also, you don't allow for errors, as in these lines for example.
z = raw_input("Enter '1' to play the computer or enter '2' to play another human: ")
if z.isdigit():

``````z = 0
while z not in ["1", "2"]:
z = raw_input("Enter '1' to play the computer or enter '2' to play another human: ")
z = int(z)
if z == 1:
etc.``````

And your input for x and y can be done the same way, but consider using one function and passing the string literal "How wide would you like your game? (4 - 8) ", and the check list ["4", "5", "6", "7", "8"] and letting it return the choice, instead of the redundant code.

The final thing that I see, and I didn't take a close look at your code, is

``````def drawGrid(grid):
for element in grid:
print element
for i in range(x):
print "",i+1,``````

You do no pass "x" to the function, so it is getting the value from somewhere else (global variables, etc), which is never a good idea. You want to control which variables are used where (which brings up classes. This would be much better in a class structure, but I am guessing that you haven't gotten that far yet).

Thankyou very much for your help! :) It's really appreciated. The first tip has saved me quite a few lines of code and definitely makes the program more robust.

I have changed the way the grid is intialised, as I realised I was just creating the same element multiple times. Silly!

I currently have x and y set to be global variables. Is this not good syntax?

Anyway, this is the new version:

``````def initialiseGrid(x,y):
grid = []
for i in range(x):
grid.append([0])
for i in range(x):
for j in range(y-1):
grid[i].extend([0])
return grid``````

Notice the duplicate line
for i in range(x):
You should be able to use

``````def initialiseGrid(x,y):
grid = []
for h in range(x):
grid.append([0])
for j in range(y-1):
grid[h].extend([0])
return grid``````

Generally, it is considered bad practice to use "i", "l", or "o" as single digit variable names as they can look like numbers.