HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

TicTacToe in Python3 w/ Simple AI

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
tictactoepython3simple

Problem

Here is my version of TicTacToe on python 3.x

I'm learning python for a several weeks and I will be very appretiated if you take a look on my script and review it!

P.S. I've used NumPad to input move to make game more user-friendly. So 5 means center, 7 means right-upper corner, 6 means right side etc.

```
from random import randint, choice
from os import system as bash
from time import time

def intInput(StringToDisplay):
# Simply checks that input is valid integer
while True:
try:
x = int(input(StringToDisplay))
return x
break
except ValueError:
print('Input integer number, please')
except Exception:
print('Unexpected error or keyboard interrupt')

def drawBoard():
print('\
╔═══╦═══╦═══╗\n\
║ {0} ║ {1} ║ {2} ║\n\
╠═══╬═══╬═══╣\n\
║ {3} ║ {4} ║ {5} ║\n\
╠═══╬═══╬═══╣\n\
║ {6} ║ {7} ║ {8} ║\n\
╚═══╩═══╩═══╝ '.format(
board_status[7], board_status[8], board_status[9],
board_status[4], board_status[5], board_status[6],
board_status[1], board_status[2], board_status[3]))

def askPlayerLetter():
# Function that asks which letter player wants to use
print('Do you want to be X or O?')
Letter = input().upper()
while Letter != 'X' and Letter != 'O':
print('Please type appropriate symbol')
Letter = input('Prompt: ').upper()
if Letter == 'X': # then X will be used by player; O by computer
return ['X', 'O']
else:
return ['O', 'X']

def whoGoesFirst():
# Timer used to count 0.75 seconds while displaying who goes first
if randint(0, 1) == 0:
CurrentTime, Timer = time(), time() + 0.75
print('You go first')
while Timer > CurrentTime:
CurrentTime = time()
return 'player'
else:
CurrentTime, Timer = time(), time() + 0.75
print('Computer goes first')
while Timer > CurrentTime:
Current

Solution

-
First of all, nice ingenuity on displaying an almost graphical board, that's good for a beginner.

-
The intInput function should follow PEP8 and be something like 'get_int_input'. Also, since you want the number to be between 1 and 9, you can add optional parameters to make the function more generic and decouple the check. StringToDisplay should also be more like displayed_prompt. The same goes for all the variable/function names.

-
Also in intInput you have a break after the return which is useless, since you're going to return anyway. You also don't need a variable for the input, since the exception is going to be caught anyway.

Like this:

def get_int_input(displayed_prompt='', min_value=1, max_value=9):
    while True:
        try:
            input_value = int(input(displayed_prompt))
            if (input_value >= min_value and input_value <= max_value):
                return input_value
            else:
                raise ValueError
        except ValueError:
            print('Input an integer number between {0} and {1}, please'.format(min_value, max_value))
        except Exception:
            print('Unexpected error or keyboard interrupt')


  • In askPlayerLetter you don't need to print and then input, you can input with a prompt. Also, you can use an array or tuple for the allowed values.



Like this:

def ask_player_letter():
    allowed_letters = ['X', 'O']
    letter = input('Do you want to be X or O?').upper()
    while letter not in allowed_letters:
        letter = input('Please choose between {} and {}: '.format(
            *allowed_letters)).upper()
    if letter == 'X':
        return allowed_letters
    else:
        return allowed_letters[::-1]


-
You have a lot of numbers, like 0.75 that should be defined somewhere in a variable, so you can fine-tune what you need in one place instead of hunting around the code for places where you use those values.

-
You use a copy of your board to make the move and then check the winning condition. That's all good and fine because this is a tiny program, but you should keep scalability in mind. A copy is an expensive operation, so if some day you want to optimize you can keep two "boards" in memory and doing/undoing the operation in the "off-screen" board and putting it in the "on-screen" board if that's the move you want. That way you'll only set/unset one element at a time instead of copying back everything every cycle of the loop.

-
In randomMoveFromList you can use a list comprehension.

Like this:

def random_move_from_list(moves_list):
    PossibleMoves = [move for move in moves_list if isSpaceFree(board_status, move)]
    if len(PossibleMoves) != 0:
        return choice(PossibleMoves)

    return None


-
You could consider using a generator, so that you could have a function like get_next_move and yield a value, but in this specific case that would probably be a bit of overkill.

-
In isBoardFull you go through the board just to count how many cells are occupied. Since your code is the only thing occupying the cells, you can keep a counter every time a cell is occupied and use that, avoiding the loop.

-
To check victory you could think about some tricks. For example, you could still show the X and the O, but internally you could save them as -1 and 1, so if the sum of one of the rows or columns is -3 or 3, someone has won. Keep in mind that you can do something like if (sum(brd[4:6]) in [-3, 3]).

Code Snippets

def get_int_input(displayed_prompt='', min_value=1, max_value=9):
    while True:
        try:
            input_value = int(input(displayed_prompt))
            if (input_value >= min_value and input_value <= max_value):
                return input_value
            else:
                raise ValueError
        except ValueError:
            print('Input an integer number between {0} and {1}, please'.format(min_value, max_value))
        except Exception:
            print('Unexpected error or keyboard interrupt')
def ask_player_letter():
    allowed_letters = ['X', 'O']
    letter = input('Do you want to be X or O?').upper()
    while letter not in allowed_letters:
        letter = input('Please choose between {} and {}: '.format(
            *allowed_letters)).upper()
    if letter == 'X':
        return allowed_letters
    else:
        return allowed_letters[::-1]
def random_move_from_list(moves_list):
    PossibleMoves = [move for move in moves_list if isSpaceFree(board_status, move)]
    if len(PossibleMoves) != 0:
        return choice(PossibleMoves)

    return None

Context

StackExchange Code Review Q#151871, answer score: 4

Revisions (0)

No revisions yet.