I've been teaching myself Python3 for about a week now and I decided to put my skills to the test by writing a simple Tic-Tac-Toe game. Here is what I came up with:#!/usr/bin/env python3import randomboard = [0]*9def resetBoard():    for index, i in enumerate(board):        board[index] = 0def printBoard():    counter = 0    s =     print(|-----|-----|-----|)    for index, i in enumerate(board):        s += |          if(board[index] == 0):            s += str(index)        if(board[index] == 1):            s+= x        if(board[index] == 2):            s += o        s +=           counter += 1        if(counter == 3):            s += |            print(s)            print(|-----|-----|-----|)            s =             counter = 0def getPlayers():    return input(1 or 2 Players?)def moveSquare(xOro, ai):    square = 0    flag = 0    while(board[square] != 0 or flag == 0):        if(ai == 0):            if(xOro == 1):                square = eval(input(Choose a square to place X: ))            if(xOro == 2):                square = eval(input(Choose a square to place O: ))        if(ai == 1):            square = random.randint(0, 8)        flag = 1    board[square] = xOro    print(\n\n)def checkForWin():    if(board[0] == board[1] and board[1] == board[2] and board[2] != 0):        return 1    if(board[3] == board[4] and board[4] == board[5] and board[5] != 0):        return 1    if(board[6] == board[7] and board[7] == board[8] and board[8] != 0):        return 1    if(board[0] == board[3] and board[3] == board[6] and board[6] != 0):        return 1    if(board[1] == board[4] and board[4] == board[7] and board[7] != 0):        return 1    if(board[2] == board[5] and board[5] == board[8] and board[8] != 0):        return 1    if(board[0] == board[4] and board[4] == board[8] and board[8] != 0):        return 1    if(board[2] == board[4] and board[4] == board[6] and board[6] != 0):        return 1    for index, i in enumerate(board):        if(i  == 0):            break        if(index == 8):            return 2    return 0def gameOver():    return eval(input(\nPress 0 and then ENTER to play again!))# Mainrandom.seed()quit = 0while(quit == 0):    players = 0    win = 0    while(players != 1 and players != 2):        players = eval(getPlayers())    resetBoard()    game = 0 #game = 1 when game is finished    while(game == 0): #game loop        printBoard()        moveSquare(1, 0)        game = checkForWin()        if(game > 0):            win = game            break        printBoard()        if(players == 1):            moveSquare(2, 1)        if(players == 2):            moveSquare(2, 0)        game = checkForWin()        if(game > 0):            win = game + 1            break    if(win == 1):        print(\nX Wins!)    if(win == 2):        print(\nO Wins!)    if(win == 3):        print(\n The game is a draw!)    quit = gameOver()exit()I am interested in the following points about my code:How can I make my code smaller and more optimized?The game displays O Wins instead of The game is a draw! and I'm not sure why.How can I sanitize input to prevent crashes if a string is entered instead of an integer (for example when choosing a square on the board)?Any additional points to improve my code or general tips that will help me become a better Python programmer.
Improving and optimizing Tic-Tac-Toe game written in Python 3
python;optimization;game;python 3.x
#!/usr/bin/env python3import randomboard = [0]*9Having global variables that change is considered a bad idea. You'd be better off putting this variable and the methods inside a class.def resetBoard():    for index, i in enumerate(board):        board[index] = 0You could also do board[:] = [0] * 9def printBoard():    counter = 0In python you rarely need to keep track of counters like this    s = In python, you should avoid building strings by adding different pieces together    print(|-----|-----|-----|)    for index, i in enumerate(board):        s += |          if(board[index] == 0):The point of enumerate is that i is the value in board. Use it rather then refetching it here.            s += str(index)        if(board[index] == 1):            s+= x        if(board[index] == 2):            s += o        s +=   By building the string this way its hard to follow exactly what the string is.        counter += 1        if(counter == 3):Instead of making the counter use `if index % 3 == 2:' Also you don't need the parens            s += |            print(s)            print(|-----|-----|-----|)            s =             counter = 0Your whole algorithm here is rendered more complicated because you are iterating over cells rather the rows. Your algorithm would be much more natural with rowsdef getPlayers():    return input(1 or 2 Players?)Shouldn't this return an int, not a string?def moveSquare(xOro, ai):variable names are recommended to follow x_or_o as a naming convetion    square = 0    flag = 0If this is a boolean flag, use False not 0    while(board[square] != 0 or flag == 0):No need for the parens around the while expression        if(ai == 0):Again use True/False for boolean flags. No need for parens            if(xOro == 1):                square = eval(input(Choose a square to place X: ))            if(xOro == 2):                square = eval(input(Choose a square to place O: ))Avoid magic numbers like 1 and 2 to denote X and O. In this case, I'd probably pass 'x' or 'o' strings.        if(ai == 1):Use an else:            square = random.randint(0, 8)        flag = 1Flag is really terrible variable name because I don't know what it means. Boolean flags are usually an ugly solution    board[square] = xOro    print(\n\n)def checkForWin():    if(board[0] == board[1] and board[1] == board[2] and board[2] != 0):        return 1Don't use magic numbers like that. Instead create some global constants to return or something    if(board[3] == board[4] and board[4] == board[5] and board[5] != 0):        return 1    if(board[6] == board[7] and board[7] == board[8] and board[8] != 0):        return 1    if(board[0] == board[3] and board[3] == board[6] and board[6] != 0):        return 1    if(board[1] == board[4] and board[4] == board[7] and board[7] != 0):        return 1    if(board[2] == board[5] and board[5] == board[8] and board[8] != 0):        return 1    if(board[0] == board[4] and board[4] == board[8] and board[8] != 0):        return 1    if(board[2] == board[4] and board[4] == board[6] and board[6] != 0):        return 1A lot duplication here.     for index, i in enumerate(board):        if(i  == 0):            break        if(index == 8):            return 2    return 0def gameOver():    return eval(input(\nPress 0 and then ENTER to play again!))Eval is a bit dangerous because the user can put in anything they want.# MainYou should your main logic in a functionrandom.seed()quit = 0while(quit == 0):Make quite a boolean variable    players = 0    win = 0    while(players != 1 and players != 2):        players = eval(getPlayers())    resetBoard()    game = 0 #game = 1 when game is finishedGame becoming 1 when the game is finished seems backwards    while(game == 0): #game loop        printBoard()        moveSquare(1, 0)        game = checkForWin()        if(game > 0):            win = game            breakNot much point checking game == 0 above if you are just going to break anyways        printBoard()        if(players == 1):            moveSquare(2, 1)        if(players == 2):            moveSquare(2, 0)        game = checkForWin()        if(game > 0):            win = game + 1            breakSee how the two parts of the loop are very similar? Suggests that you should get rid of the duplication.    if(win == 1):        print(\nX Wins!)    if(win == 2):        print(\nO Wins!)    if(win == 3):        print(\n The game is a draw!)    quit = gameOver()Having gameOver() return whether or not you should quit seems confusing. The name doesn't suggest thatexit()Not much point in calling quit, the program is about to terminate anywaysAnd just to demonstrate how I'd do this:#!/usr/bin/env python3import randomWINNING_LINES = [    [0, 1, 2],    [3, 4, 5],    [5, 6, 7],    [0, 3, 5],    [1, 4, 6],    [2, 5, 7],    [0, 4, 7],    [2, 4, 5]]STALEMATE = '!'class TicTacToe(object):    def __init__(self):        self.board = [' '] * 9    def print(self):        # make a copy of the board        cells = self.board[:]         # put a digit in every blank cell        for index, cell in enumerate(cells):            if cell == ' ':                cells[index] = str(index)        # put spaces around the cells        cells = [' %s ' % cell for cell in cells]        # print the actual board        print('|---|---|---|')        for start in range(0, 9, 3):            row = cells[start:start+3]            print('|', '|'.join(row), '|', sep = '')            print('|---|---|---|')    def move_square(self, player, ai):        square = -1        while square < 0 or square >= 9 or self.board[square] != ' ':            if ai:                square = random.randrange(9)            else:                question = Choose a square to place {}:.format(player.upper())                square_text = input(question)                print(square_text)                try:                    square = int(square_text)                except ValueError:                    # user didn't enter a number                    square = -1        self.board[square] = player        print(\n\n)    def status(self):        # check for a win        for a, b, c in WINNING_LINES:            if self.board[a] == self.board[b] and self.board[b] == self.board[c] and self.board[a] != ' ':                return self.board[a]        if not any(cell == ' ' for cell in self.board):            return STALEMATE        return ' 'def get_players():    while True:        text = input(1 or 2 players)        try:            value = int(text)        except ValueError:            pass        else:            if 0 < value < 3:                return valuedef play_again():    return input(\nPress 0 and then ENTER to play again!) == '0'GAME_OVER_MESSAGES = {    'X' : 'X wins!',    'O' : 'O wins!',    '!' : 'The game is a draw!'}def main():    while True:        # create a list to denote whether        # a player is AI        ai_players = [False, False]        if get_players() == 1:            ai_players[1] = True        tic_tac_toe = TicTacToe()        player_turn = 0        player_codes = 'XY'        while tic_tac_toe.status() == ' ':            tic_tac_toe.print()            tic_tac_toe.move_square(player_codes[player_turn], ai_players[player_turn])            player_turn = (player_turn + 1) % 2        print(GAME_OVER_MESSAGES[tic_tac_toe.status()])        if not play_again():            breakif __name__ == '__main__':    main()