Search code examples
pythonalgorithmtuples

Connect 4 Python Code, my diagonal check is not working, what am I doing wrong here?


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

Problem image

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


Solution

  • 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];
    

    Remark

    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))
        )