Search code examples
pythonfunctionfor-loopnested-liststic-tac-toe

How can i use less for loops in this game and be much effective and dynamic with less lin eof codes?


def tic_tac_toe(board):

    mess = []   
    organize = []     
    winner = []   
    n = len(board)

Can i do this without this much for loops ?

    for i in range(len(board)):
        for j in range(len(board[i])):
            mess.append(board[j][i]) 
     #appending all vertical items


    for i in range(len(board)):
        for j in range(len(board[i])):
            mess.append(board[i][j]) 
    #appending all horizontal items


    for i in range(len(board)):
        for j in range(len(board[i])):
            i = j
            mess.append(board[i][j])
        break

    #appending items that have the same i and j index (i==0 j==0,i==1 j==1,i==2 j ==2) or better say all items in these position (\)


    for i in range(len(board)):
        for j in range(len(board[i])):
            if j - i == 2 or i - j == 2 or (i == 1 and j == 1):
                mess.append(board[i][j])  
    #appending items in these indexes : (i ==0 j ==2 , i == 1 j == 1, i ==2 j == 0) items in these position (/) 



    organize = [mess[k: k + n] for k in range(0, len(mess), n)] # creating nested list of all possible moves


    winner = ["X" if organize[i].count("X") == 3 else "O" if organize[i].count("O") == 3 else "Draw" for i in range(len(organize))] # "X" if 3 "X"s in organize[i]. "O" if 3 "O"s in organize[i] and "Draw" otherwise .

and this determine winner . if one single "X" in winner list The winner is "X" . Same for "O" . but if all elemnts in winner list was "Draw" The game is a Draw .

strong text

    if "X" in winner:
        return "X"
    elif "O" in winner:
        return "O"
    elif winner.count("Draw") == 8 :
        return "Draw"  

   

print(tic_tac_toe([
    ["X", "X", "O"],
    ["O", "O", "X"],
    ["X", "X", "O"]
])) # ➞ "Draw"

Solution

  • If you're happy to do it with numpy, you can simplify it a lot. Numpy allows you to rotate the board, which means you can find the horizontal lines with the same code as the vertical lines (and the same goes for the two diagonals).

    import numpy as np
    
    organize = []
    
    board_rotated = np.rot90(board).tolist() # rotate the board
    
    organize.extend(board) # add horiztonal
    organize.extend(board_rotated) # add vertical
    organize.append(np.diag(board).tolist()) # add top-left to bottom-right diagonal
    organize.append(np.diag(board_rotated).tolist()) # add top-right to bottom-left diagonal
    

    Can be done with no for loops whatsoever, plus you don't need to have the mess list. Everything is added directly to organize.


    What's happening is it rotates the board which allows you to perform the exact same operations on the rotated board as you do on the non-rotated board to get all of the solutions. In the example provided in the question, board_rotated would be:

    [['O', 'X', 'O'],
     ['X', 'O', 'X'],
     ['X', 'O', 'X']]
    

    As expected, this has been rotated 90 degrees.
    The .extend() function calls simply add each row to organise. In the case of board the rows are the horizontal values. The rows are the vertical values when using board_rotated.
    Using np.diag() gives you the diagonal of the matrix. For board this is ['X', 'O', 'O'] and for board_rotated this is ['O', 'O', 'X'].