Converting If statement to a loop
<p>I am working on a practice problem where we are to input a list into a function argument, that will represent a tic tac toe board, and return the outcome of the board. That is, X wins, O wins, Draw, or None (null string).</p>

<p>I have it solved, but I was wondering if there is a way I could manipulate my algorithm into a loop, as it was recommended to use a loop to compare each element of the main diagonal with all the
elements of its intersecting row and column, and then check the two diagonals. I'm new to python, so my solution might be a bit longer then it needs to be. How could a loop be implemented to check the outcome of the tic tac toe board?   </p>

<pre><code>def gameState (List):
    xcounter=0
    ocounter=0
    if List[0][0]==List[0][1]   and List[0][0]==List[0][2]:
        return List[0][0]
    elif List[0][0]==List[1][0] and List[0][0]==List[2][0]:
        return List[0][0]
    elif List[0][0]==List[1][1] and List[0][0]==List[2][2]:
        return List[0][0]
    elif List[1][1]==List[1][2] and List[1][1]==List[1][0] :
        return List[1][1]
    elif List[1][1]==List[0][1] and List[1][1]==List[2][1]:
        return List[1][1]
    elif List[1][1]==List[0][0] and List[1][1]==List[2][2]:
        return List[1][1]
    elif List[2][2]==List[2][0] and List[2][2]==List[2][1]:
        return List[2][2]
    elif List[2][2]==List[1][2] and List[2][2]==List[0][2]:
        return List[2][2]
    elif List[2][2]==List[1][1] and List[2][2]==List[0][0]:
        return List[2][2]
    for listt in List:
        for elm in listt:
            if elm=="X" or elm=="x":
                xcounter+=1
            elif elm=="O" or elm=="o":
                ocounter+=1
    if xcounter==5 or ocounter==5:
        return "D"
    else:
        return ''
</code></pre>

<p>First up, there are only <em>eight</em> ways to win at TicTacToe. You have nine compare-and-return statements so one is superfluous. In fact, on further examination, you check <code>00, 11, 22</code> <em>three</em> times (cases 3, 6 and 9) and totally <em>miss</em> the <code>02, 11, 20</code> case.</p>

<p>In terms of checking with a loop, you can split out the row/column checks from the diagonals as follows:</p>

<pre><code># Check all three rows and columns.

for idx in range(3):
    if List[0][idx] != ' ':
        if List[0][idx] == List[1][idx] and List[0][idx] == List[2][idx]:
            return List[0][idx]
    if List[idx][0] != ' ':
        if List[idx][0] == List[idx][1] and List[idx][0] == List[idx][2]:
            return List[idx][0]

# Check two diagonals.

if List[1][1] != ' ':
    if List[1][1] == List[0][0] and List[1][1] == List[2][2]:
        return List[1][1]
    if List[1][1] == List[0][2] and List[1][1] == List[2][0]:
        return List[1][1]

# No winner yet.

return ' '
</code></pre>

<p>Note that this ensures a row of empty cells isn't immediately picked up as a win by nobody. You need to check only for wins by a "real" player. By that, I mean you don't want to detect three empty cells in the first row and return an indication based on that if the second row has an <em>actual</em> winner.</p>

<hr>

<p>Of course, there are numerous ways to refactor such code to make it more easily read and understood. One way is to separate out the logic for checking a <em>single</em> line and then call that for each line:</p>

<pre><code># Detect a winning line. First cell must be filled in
#   and other cells must be equal to first.

def isWinLine(brd, x1, y1, x2, y2, x3, y3):
    if brd[x1][y1] == ' ': return False
    return brd[x1][y1] == brd[x2][y2] and brd[x1][y1] == brd[x3][y3]

# Get winner of game by checking each possible line for a winner,
#   return contents of one of the cells if so. Otherwise return
#   empty value.

def getWinner(brd):
    # Rows and columns first.

    for idx in range(3):
        if isWinLine(brd, idx, 0, idx, 1, idx, 2): return brd[idx][0]
        if isWinLine(brd, 0, idx, 1, idx, 2, idx): return brd[0][idx]

    # Then diagonals.

    if isWinLine(brd, 0, 0, 1, 1, 2, 2): return brd[1][1]
    if isWinLine(brd, 2, 0, 1, 1, 0, 2): return brd[1][1]

    # No winner yet.

    return ' '
</code></pre>

<p>Then you can just use:</p>

<pre><code>winner = getWinner(List)
</code></pre>

<p>in your code and you'll either get back the winner or an empty indication if there isn't one.</p>

python loops for-loop while-loop