Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid multiple `elif` statements?

So, I was trying to make a basic Tic-Tac-Toe game with python, And I created one which works perfectly fine, but my code is not so good, as it has a lot of code for checking the list indexes(Winner Of The Game), which kinda bothers me. So, How I avoid using the left index for checking the winner of the game?

My Code:

board = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']


def show_board():# for showing the tic-tac-toe board
    print(' | ' + str(board[0]) + ' | ' +
          str(board[1]) + ' | ' + str(board[2]) + ' | ')
    print(' | ' + str(board[3]) + ' | ' +
          str(board[4]) + ' | ' + str(board[5]) + ' | ')
    print(' | ' + str(board[6]) + ' | ' +
          str(board[7]) + ' | ' + str(board[8]) + ' | ')


def main():
    one = 1
    flag = 1

    show_board()
    while one == 1:
        if flag == 1:
            x_o = 'X'
        if flag == 2:
            x_o = 'O'
        pos = int(input('Player "' + x_o + '" Turn: '))

        if x_o == 'o':
            x_o = 'O'
        if x_o == 'x':
            x_o = 'X'
        if board[pos - 1] == 'O' or board[pos - 1] == 'O':
            print('That Place Is Already Filled By Player "0"')

        if board[pos - 1] == 'X' or board[pos - 1] == 'X':
            print('That Place Is Already Filled By Player "X"')

        else:
            try:
                board[pos - 1] = x_o
            except IndexError:
                print('Type Numbers Between Only 1 And 9')

            if flag == 1:
                flag = 2
            elif flag == 2:
                flag = 1

            show_board()

            # Checking The Winner Of The Game

            # for horizontal X
            if board[0] == board[1] == board[2] == 'X':
                one = 2
                print('The Winner Is Player "X"!')

            elif board[3] == board[4] == board[5] == 'X':
                one = 2
                print('The Winner Is Player "X"!')

            elif board[6] == board[7] == board[8] == 'X':
                one = 2
                print('The Winner Is Player "X"!')

            # for Daigonal X
            elif board[0] == board[4] == board[8] == 'X':
                one = 2
                print('The Winner Is Player "X"!')

            elif board[2] == board[4] == board[6] == 'X':
                one = 2
                print('The Winner Is Player "X"!')

            # for Vertical X
            elif board[1] == board[4] == board[7] == 'X':
                one = 2
                print('The Winner Is Player "X"!')

            elif board[2] == board[5] == board[8] == 'X':
                one = 2
                print('The Winner Is Player "X"!')

            elif board[0] == board[3] == board[6] == 'X':
                one = 2
                print('The Winner Is Player "X"!')

            # for horizontal O
            elif board[0] == board[1] == board[2] == 'O':
                one = 2
                print('The Winner Is Player "O"!')

            elif board[3] == board[4] == board[5] == 'O':
                one = 2
                print('The Winner Is Player "O"!')

            elif board[6] == board[7] == board[8] == 'O':
                one = 2
                print('The Winner Is Player "O"!')

            # for Diagonal O
            elif board[0] == board[4] == board[8] == 'O':
                one = 2
                print('The Winner Is Player "O"!')

            elif board[2] == board[4] == board[6] == 'O':
                one = 2
                print('The Winner Is Player "O"!')

            # for Vertical 0
            elif board[1] == board[4] == board[7] == 'O':
                one = 2
                print('The Winner Is Player "O"!')

            elif board[2] == board[5] == board[8] == 'O':
                one = 2
                print('The Winner Is Player "O"!')

            elif board[0] == board[3] == board[6] == 'O':
                one = 2
                print('The Winner Is Player "O"!')

            elif board[0] != ' ' and board[1] != ' ' and board[2] != ' ' and board[3] != ' ' and board[4] != ' ' and board[5] != ' ' and board[6] != ' ' and board[7] != ' ' and board[8] != ' ':
                print('The Match Is A Tie!')
                one = 2


main()

So, as you can see I'm using a lot of if statements for checking the winner of the game. How can I avoid that and do it in fewer lines.

like image 850
Heisenberg Avatar asked Sep 14 '19 06:09

Heisenberg


2 Answers

You can replace them with some loops like:

board = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']


def show_board():# for showing the tic-tac-toe board
    print(' | ' + str(board[0]) + ' | ' +
          str(board[1]) + ' | ' + str(board[2]) + ' | ')
    print(' | ' + str(board[3]) + ' | ' +
          str(board[4]) + ' | ' + str(board[5]) + ' | ')
    print(' | ' + str(board[6]) + ' | ' +
          str(board[7]) + ' | ' + str(board[8]) + ' | ')


def main():
    one = 1
    flag = 1

    show_board()
    while one == 1:
        if flag == 1:
            x_o = 'X'
        if flag == 2:
            x_o = 'O'
        pos = int(input('Player "' + x_o + '" Turn: '))

        if x_o == 'o':
            x_o = 'O'
        if x_o == 'x':
            x_o = 'X'
        if board[pos - 1] == 'O' or board[pos - 1] == 'O':
            print('That Place Is Already Filled By Player "0"')

        if board[pos - 1] == 'X' or board[pos - 1] == 'X':
            print('That Place Is Already Filled By Player "X"')

        else:
            try:
                board[pos - 1] = x_o
            except IndexError:
                print('Type Numbers Between Only 1 And 9')

            if flag == 1:
                flag = 2
            elif flag == 2:
                flag = 1

            show_board()

            # Checking The Winner Of The Game
            won = False
            for turn in ('X', 'O'):
                # horizontal
                if not won:
                    for i in (0, 3, 6):
                        if all(board[i + k] == turn for k in range(3)):
                            won = True
                            break
                # vertical
                if not won:       
                    for i in range(3):
                        if all(board[i + k] == turn for k in (0, 3, 6)):
                            won = True
                            break
                # diagonal
                if not won:
                    if all(board[k] == turn for k in (0, 4, 8)) or \
                            all(board[k] == turn for k in (2, 4, 6)):
                        won = True
                # handle winning
                if won:
                    one = 2
                    print(f'The Winner Is Player "{turn}"!')
                    break
            # handle a tie
            if not won and all(square != ' ' for square in board):
                one = 2
                print('The Match Is A Tie!')


main()

(You can probably also polish/simplify the rest of the code quite a bit)


EDIT

Here is some similar code simplified / polished / expanded to:

  • handle boards of arbitrary sizes
  • correctly handle non-numerical input
  • use more functions
  • get rid of redundant variables
NUM_ROWS = 3
NUM_COLS = 3
NUM_WIN = 3
BOARD_SIZE = NUM_ROWS * NUM_COLS

EMPTY = ' '
BOARD = [EMPTY] * BOARD_SIZE
TURNS = 'X', 'O'


def show_board(board):
    """Show the tic-tac-toe board."""
    for i in range(0, BOARD_SIZE, NUM_COLS):
        print(' | ' + ' | '.join(board[i:i + NUM_COLS]) + ' | ')


def ij(i, j):
    """Convert (row, col) to board index."""
    return i + NUM_COLS * j


def check_winner(board, turn):
    """Check if there is a winner."""
    # horizontal
    for i in range(NUM_ROWS):
        for j in range(NUM_COLS - NUM_WIN + 1):
            if all(board[ij(i, j + k)] == turn for k in range(NUM_WIN)):
                return True
    # vertical
    for i in range(NUM_ROWS - NUM_WIN + 1):
        for j in range(NUM_COLS):
            if all(board[ij(i + k, j)] == turn for k in range(NUM_WIN)):
                return True
    # diagonal
    for i in range(NUM_ROWS - NUM_WIN + 1):
        for j in range(NUM_COLS - NUM_WIN + 1):
            K = NUM_WIN
            if all(board[ij(i + k, j + k)] == turn for k in range(NUM_WIN)):
                return True
            if all(board[ij(i + NUM_WIN - k - 1, j + k)] == turn
                   for k in range(NUM_WIN)):
                return True


def check_tie(board):
    """Check if tie."""
    return all(square != EMPTY for square in board)


def next_turn(turn):
    """Advance to next turn."""
    return TURNS[(TURNS.index(turn) + 1) % 2]


def main():
    """Tic-tac-toe game."""
    turn = TURNS[0]
    show_board(BOARD)
    while True:
        valid_input = False
        while not valid_input:
            try:
                choice = int(input(f'Player `{turn}` turn: '))
                valid_input = (1 <= choice <= BOARD_SIZE)
                if not valid_input:
                    raise ValueError
            except ValueError:
                print(f'Type numbers between 1 and {BOARD_SIZE} only.')
            else:
                idx = choice - 1
                if BOARD[idx] != EMPTY:
                    print(f'Position `{idx}` already taken by `{BOARD[idx]}`')
                else:
                    BOARD[idx] = turn

        show_board(BOARD)
        won = check_winner(BOARD, turn)
        if won:
            print(f'The winner is player `{turn}`!')
            break
        # handle a tie
        if not won and check_tie(BOARD):
            print('The match is a tie!')
            break
        turn = next_turn(turn)


if __name__ == '__main__':
    main()

Please check mnk-game's source code for a similar implementation using NumPy and classes (especially Board.py). DISCLAIMER: I am the author of the package.


EDIT 2

Note that for tic-tac-toe only, check_winner() could be shortend using an approach similar to what @rusu_ro1 proposed :

WIN_CASES = [
    [0, 1, 2], [3, 4, 5], [6, 7, 8],  # horizontal
    [0, 3, 6], [1, 4, 7], [2, 5, 8],  # vertical
    [0, 4, 8], [2, 4, 6],  # diagonal
]


def check_winner(board, turn, win_cases=WIN_CASES):
    """Check if there is a winner."""
    for win_case in win_cases:
        if all(board[i] == turn for i in win_case):
            return True
like image 148
norok2 Avatar answered Oct 20 '22 09:10

norok2


import random as rnd
size = 3

## random board to test
board = [[rnd.choice(["X", "O"]) for _ in range(size)] for _ in range(size)]
print(board)

def check_winner(board):
    for mark in ["X", "O"]:
        ## count mark in all rows
        count_in_rows = [row.count(mark) for row in board]
        ## count mark in all columns
        count_in_columns = [ [board[row_n][col_n] for row_n in range(size)].count(mark) for col_n in range(size)]
        ## count mark in fwd diagonal
        count_in_diag = [[board[i][i] for i in range(size)].count(mark)]
        ## count in backward diagonal
        count_in_diag.append([board[i][size-i-1] for i in range(size)].count(mark))

        print((count_in_rows + count_in_columns + count_in_diag))

        ## if there is 3 count of mark in any rows, columns or diagonal
        if 3 in (count_in_rows + count_in_columns + [count_in_diag]):
            print(f"winner is {mark}")
            return mark

        return

check_winner(board)  
like image 21
Dev Khadka Avatar answered Oct 20 '22 11:10

Dev Khadka