I've made a working version of Connect Four in Python. I'm pretty new to Python so it's decently primitive. Please take a look and let me know what you think!
import random
board = [["." for x in range(7)] for x in range(6)]
symbol = ""
winCondition = ""
fullCols = []
def boardLogic(pick, user):
if user == "user":
symbol = "0"
else:
symbol = "X"
if board[0][pick] != ".":
if board[1][pick] != ".":
if board[2][pick] != ".":
if board[3][pick] != ".":
if board[4][pick] != ".":
if board[5][pick] != ".":
print("That column is full.")
if pick not in fullCols:
fullCols.append(pick)
fullCols.sort()
print fullCols
else:
board[5][pick] = symbol
else:
board[4][pick] = symbol
else:
board[3][pick] = symbol
else:
board[2][pick] = symbol
else:
board[1][pick] = symbol
else:
board[0][pick] = symbol
def printBoard():
for row in board:
for val in row:
print '{:4}'.format(val),
print
def does_square_contain_win(i, j):
#right_diag: [[i,j], [i-1,j+1], [i-2,j+2], [i-3,j+3]]
if i-3 in range(0, 6) and j+3 in range(0,5):
if board[i][j] == "0" and board[i-1][j+1] == "0" and board[i-2][j+2] == "0" and board[i-3][j+3] == "0":
print("******************************\nPlayer Wins\n******************************")
quit()
if i-3 in range(0, 6) and j+3 in range(0,5):
if board[i][j] == "X" and board[i-1][j+1] == "X" and board[i-2][j+2] == "X" and board[i-3][j+3] == "X":
print("******************************\nComputer Wins\n******************************")
quit()
#left_diag: [[i,j], [i-1,j-1], [i-2,j-2], [i-3,j-3]]
if i-3 in range(0, 6) and j-3 in range(0,5):
if board[i][j] == "0" and board[i-1][j-1] == "0" and board[i-2][j-2] == "0" and board[i-3][j-3] == "0":
print("******************************\nPlayer Wins\n******************************")
quit()
if i-3 in range(0, 6) and j-3 in range(0,5):
if board[i][j] == "X" and board[i-1][j-1] == "X" and board[i-2][j-2] == "X" and board[i-3][j-3] == "X":
print("******************************\nComputer Wins\n******************************")
quit()
#right: [[i,j], [i,j+1], [i,j+2], [i,j+3]]
if j+3 in range(0, 5):
if board[i][j] == "0" and board[i][j+1] == "0" and board[i][j+2] == "0" and board[i][j+3] == "0":
print("******************************\nPlayer Wins\n******************************")
quit()
if j+3 in range(0, 5):
if board[i][j] == "X" and board[i][j+1] == "X" and board[i][j+2] == "X" and board[i][j+3] == "X":
print("******************************\nComputer Wins\n******************************")
quit()
#down: [[i,j], [i-1,j], [i-2,j], [i-3,j]]
if i-3 in range(0, 6):
if board[i][j] == "0" and board[i-1][j] == "0" and board[i-2][j] == "0" and board[i-3][j] == "0":
print("******************************\nPlayer Wins\n******************************")
quit()
if i-3 in range(0, 6):
if board[i][j] == "X" and board[i-1][j] == "X" and board[i-2][j] == "X" and board[i-3][j] == "X":
print("******************************\nComputer Wins\n******************************")
quit()
while True:
printBoard()
#pick a column
while True:
try:
yourPick = int(raw_input('Pick a column 0 - 6: '))
except ValueError:
print 'That\'s not a number!'
else:
if 0 <= yourPick < 6 and yourPick not in fullCols:
break
else:
print 'Out of range or column full. Try again.'
print(yourPick)
#column logic
boardLogic(yourPick, "user")
#opponent randomly picks a column
while True:
try:
theirPick = random.randint(0, 6)
except ValueError:
print 'That\'s not a number!'
else:
if theirPick not in fullCols:
break
else:
print 'Column full!'
print(theirPick)
#column logic
boardLogic(theirPick, "notUser")
#check win conditions
for i in range(0, 6):
for j in range(0, 5):
does_square_contain_win(i, j)
-
\$\begingroup\$ the nested if-else statements was already covered, but also you check the condition, if i-3 in range(0, 6) and j-3 in range(0,5): , multiple times - that definitely can be simplified. \$\endgroup\$mugabits– mugabits2016年02月19日 20:41:08 +00:00Commented Feb 19, 2016 at 20:41
2 Answers 2
Raise the abstraction
if board[0][pick] != ".":
if board[1][pick] != ".":
if board[2][pick] != ".":
if board[3][pick] != ".":
if board[4][pick] != ".":
if board[5][pick] != ".":
print("That column is full.")
if pick not in fullCols:
fullCols.append(pick)
fullCols.sort()
print fullCols
else:
board[5][pick] = symbol
else:
board[4][pick] = symbol
else:
board[3][pick] = symbol
else:
board[2][pick] = symbol
else:
board[1][pick] = symbol
else:
board[0][pick] = symbol
So if the first is empty you assign the first, if the second is empty you assign the second ...
In other words you assign the first empty, let me write some pseudocode:
def boardLogic(pick, user):
# ... Logic before (this should maybe be moved elsewhere)
try:
first_empty = first(lambda i: board[i][pick] == EMPTY, range(5+1))
board[first_empty][pick] = symbol
except StopIteration:
print("That column is full.")
# ... No empty space
Where
EMPTY = '.'
And first
can very easily be found on StackOverflow
In high level languages you should try to write code that resembles the high level description of the problem, and avoid a lot of chained conditionals.
Caridorc made a very good point which I think is the key thing to look at, but there's other stuff here I wanna note.
There's no need to instantiate symbol
or winCondition
. For one, it doesn't seem like you use winCondition
. But also with symbol
it's perfectly fine to just define it inside a function rather than in the global namespace. You only need it in one place? Just use it there.
Still on symbol
. There's no real need to pass user
to boardLogic
. You could save yourself time by passing symbol
values directly, since user
just gets passed as a literal anyway:
def boardLogic(pick, symbol):
boardLogic(yourPick, "0")
There's tons of repetition in does_square_contain_win
. You should really only have two print lines, one for computer win and one for player win. Your if statements should be then used to narrow down to one of those 2 conditions. Even an overly long if
statement is better than many many little if blocks.
When getting user input, instead of using else
on a try except
, you should continue
after the ValueError
:
while True:
try:
yourPick = int(raw_input('Pick a column 0 - 6: '))
except ValueError:
print 'That\'s not a number!'
continue
if 0 <= yourPick < 6 and yourPick not in fullCols:
break
else:
print 'Out of range or column full. Try again.'
It's easier to read... and I had forgotten about else
blocks being on except
s. Also why are you treating the computer's choices like human input? They can't possibly raise that ValueError
, and the player doesn't need to know that the computer has tried to pick fullCols
. Leave out prints here, just loop until theirPick
isn't in fullCols
.
Now, if you wanted a better method you could get a list of numbers from 0 to 6 that are not in fullCols
and randomly pick from there. That way you know a move is valid straight away:
theirPick = random.choice([x for x in range(7) if x not in fullCols])
random.choice
will randomly choose one value from a collection. The list here is a list comprehension of all values from 0 to 6 if they're not in fullCols
. This means you're always going to get a valid number.
Now that I'm at the end, the way you're testing win conditions is very odd. Why are you looping over every single square to check for a win? Surely you only need to check the user and computers' picks? It's definitely very inefficient, and hard to read. You should either just have a function that reads the whole board at once (which I think would be more readable) or specifically just run tests on the most recently added positions. Doing every position is just going to confuse people.