I've seen a few TTT related posts recently, and thought I might learn something or be reminded of certain techniques from attempting my own rendition. Here is that attempt! Hopefully any new coders can benefit from this as well. Any improvement suggestions are welcome, and numpy, scipy & bitwise operations are open to aid optimization.


import numpy as np
from random import randint

def print_board(board):
c | {}   {}   {} |
  +   +   +   +
b | {}   {}   {} |
  +   +   +   +
a | {}   {}   {} |
    1   2   3
""".format(*list(x if x != '0' else ' ' for x in board)))

def check_for_win(board):
    def check_diagonal(dia):
        return '0' not in dia and len(set(dia)) == 1

    a = np.array(board).reshape(3, 3)

    # check lr diagonal
    if check_diagonal(a.diagonal()):
        return True

    # check rl diagonal
    if check_diagonal(np.fliplr(a).diagonal()):
        return True

    # check rows & cols
    for matr in [a, np.transpose(a)]:
        for row in matr:
            if '0' not in row and len(set(row)) == 1:
                return True

    return False

if __name__ == "__main__":
    board = ['0'] * 9
    codes = ('c1', 'c2', 'c3', 'b1', 'b2', 'b3', 'a1', 'a2', 'a3')
    turn, user = 1, 'X' if bool(randint(0, 1)) else 'O'
    print('Welcome! First to play is %s!' % user)

    while True:
        print('Player %s: ' % user)
        code = input().strip().lower()

        if code in codes:
            idx = codes.index(code)
            board[idx] = user

            if turn >= 5:
                if '0' not in board:
                    print("Aw, it's a draw!")
                elif check_for_win(board):
                    print('%s won in %d turns! Congratulations!' % (user, turn))

            user = 'X' if user == 'O' else 'O'
            turn = turn + 1
            print("Hmm, that's not a valid input.")


Input Validation

You aren’t completely validating input

  • X: a1
  • O: b2
  • X: a2
  • O: a3 (block)
  • X: a3 For the Win!?

Invalid Win/Draw Detection Logic:

X  O  X
O     O
X  O  X

Last move in centre: “Aw, it’s a draw!”???


This is a bad function to use. It exits the Python interpreter. Unconditionally. Period.

When you want to change your code to play repeated games, run unit tests, etc., you won’t be able to. Nothing will execute after exit().

Better would be to move the code into a function, and simply return to force an exit of the current local context, instead of an exit of the entire interpreter.


You are using {} and .format() in print_board, and using %s and %d in "..." % (...) expressions. Usually, you should try to avoid mixing formatting methods. Both are awkward to use, as the value and the place the value goes are far apart in the code. Avoid using the % style.

Additionally, instead of:

print('%s won in %d turns! Congratulations!' % (user, turn))

using an f-string moves the variables inside the format string:

print(f'{user} won in {turn} turns! Congratulations!')

(I’d leave the print_board function alone, but in other print formatting, f-strings are a win. )

  • \$\begingroup\$ The mention of f-strings is certainly a good reminder! I've fixed the input validation & win detection bugs now; ty for isolating the edgiest of cases. B/c this is a loop in the main program and I need some variables to persist, I've opted to go for break over exit(), which should be more clean. \$\endgroup\$ – T145 Nov 2 '19 at 20:47
  • \$\begingroup\$ I appreciate the green checkmark, but Code Review is not a high volume site. You should leave your question open for more than 19 hours, in order to accumulate additional reviews. I’m sure there are more things to comment on in the code, but accepting an answer will typically discourage additional reviews. \$\endgroup\$ – AJNeufeld Nov 3 '19 at 1:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.