I have been given a 2D tuples that should be seen as Connect4 game. Mt code should detect that there are 4 X's in the first tuple diagonally and 4 O's horizontally in the second tuple. I have been able to find the horizontal and vertical 4 pairs, but my code has not been able to detect the diagonal ones. I need to know what I'm doing wrong
Here is my code:
def check_winner(tuples):
result = None
#check for row win
for row in range(0,6):
for col in range(0,4):
if tuples[row][col] == tuples[row][col+1] == tuples[row][col+2] == tuples[row][col+3]=="O":
result = tuples[row][col];
elif tuples[row][col] == tuples[row][col+1] == tuples[row][col+2] == tuples[row][col+3]=="X":
result = tuples[row][col];
#check for column win
for col in range(0,7):
for row in range(0,3):
if tuples[row][col] == tuples[row+1][col] == tuples[row+2][col] == tuples[row+3][col]=="O":
result = tuples[row][col];
elif tuples[row][col] == tuples[row+1][col] == tuples[row+2][col] == tuples[row+3][col]=="X":
result = tuples[row][col];
#checks for diagonal win
for x in range(0,3):
for y in range(0,4):
if tuples[x][y] == tuples[x+1][y+1] == tuples[x+2][y+2] == tuples[x+3][y+3]=="O":
result = tuples[x][y];
if tuples[x][y] == tuples[x+1][y+1] == tuples[x+2][y+2] == tuples[x+3][y+3]=="X":
result = tuples[x][y];
return result
Your code checks for wins in three different directions: horizontal, vertical, and descending diagonals (\
). But there are also the ascending diagonals to check (/
). The second input in the example has a win on an ascending diagonal, and so your function does not detect it.
So add this block to your function:
# ascending diagonal check
for x in range(0,3):
for y in range(3,7): # allow for subtracting from y
if tuples[x][y] == tuples[x+1][y-1] == tuples[x+2][y-2] == tuples[x+3][y-3]=="O":
result = tuples[x][y];
if tuples[x][y] == tuples[x+1][y-1] == tuples[x+2][y-2] == tuples[x+3][y-3]=="X":
result = tuples[x][y];
Your function could exit the function as soon as it assigns to result
. There is no gain in continuing the search for a win when you already have found one.
There is a lot of repetition in your code. For instance, the distinction between =="O"
and =="X"
can be avoided. Just get tuples[x][y]
when you find 4 of the same values, no matter whether it is "O", "X" or None
. If it is None
continue. If not, return it.
But you can even parameterise the direction of the search and determine the range of starting cell's coordinates for a given direction.
For example:
def first_true(iterable, default=False, pred=None): # a recipe from the docs on itertools
return next(filter(pred, iterable), default)
def who_wins_at(tuples, row, col, dy, dx):
if (tuples[row][col] == tuples[row+dy][col+dx]
== tuples[row+dy*2][col+dx*2]
== tuples[row+dy*3][col+dx*3]):
return tuples[row][col]
def who_wins_direction(tuples, dy, dx):
return first_true(
who_wins_at(tuples, row, col, dy, dx)
for row in range([3, 0, 0][dy + 1], [6, 6, 3][dy + 1])
for col in range([3, 0, 0][dx + 1], [7, 7, 4][dx + 1])
)
def check_winner(tuples):
return first_true(
who_wins_direction(tuples, dy, dx)
for (dy, dx) in ((0, 1), (1, 0), (1, 1), (1, -1))
)