3
\$\begingroup\$

This is a basic Tic-Tac-Toe game with a minimal interface. The only concessions to ease of use are input validation (7 lines) and 1-indexed rows and columns (a few bytes). I've been trying to tighten it up while still maintaining readability and PEP-8 style.

I'm pretty happy with most of it, except for printing the board: I'd like to do it at the beginning, between each move, and at the end, but I don't see how to manage that without either repeating the formatted print() call or turning it into a function and calling it twice. As you can see, my current compromise omits the empty starting board in favor of the more interesting ending board.

This printing issue is my focus at the moment, but I'm also interested in any other suggestions for improvement.

board = [['*' for i in range(3)] for j in range(3)]

def askmove(piece):
    while True:
        try:
            x,y = map(int, input('\n> '))
            if board[x-1][y-1] == '*':
                board[x-1][y-1] = piece
            else:
                continue
        except (ValueError, IndexError):
            pass
        else:
            break
    print('', *(' '.join(row) for row in board), sep='\n')
    if ([piece] * 3 in board or
        (piece,) * 3 in zip(*board) or
        all(board[i][i] == piece for i in range(3)) or
        all(board[i][2-i] == piece for i in range(3))):
        print(piece, 'wins.')
        return True
    elif all(p!='*' for row in board for p in row):
        print('Tie.')
        return True

while True:
    if askmove('X') or askmove('O'):
        break
\$\endgroup\$
1
\$\begingroup\$

Input validation

Input validation is not really related to Tic-Tac-Toe, but is shared among all console programs.

The code to get the correct coordinates should be moved into a function in its own file to reuse it in the future and make the tic-tac-toe script even shorter.

Names

askmove does a complete turn, so I would call it: make_turn

Refactor

The check for the winning position is very much isolated from the rest of the program. I think the program would gain a lot of readibility if you wrote a is_won function.

\$\endgroup\$
1
\$\begingroup\$

I stared at it some more, and found a one-line DRY improvement:

if ([piece] * 3 in board or
    (piece,) * 3 in zip(*board) or
    all(board[i][i] == piece for i in range(3)) or
    all(board[i][2-i] == piece for i in range(3))):
    print(piece, 'wins.') # return removed
elif all(p!='*' for row in board for p in row):
    print('Tie.') # return removed
else:
    return True # invert condition; game now continues on True

Which led to a one-line LoC improvement:

while askmove('X') and askmove('O'):
    pass

I had been trying for something like this:

    win condition:
        return piece + ' wins'
    tie condition:
        return 'Tie.'

while print_wrapper(askmove('X') or askmove('O') or '') and sys.exit():
    pass

However, I couldn't figure out how to do it without spending a few lines on a wrapper function to give print() an exit code, and then import sys would need two lines too, so the above was the best I could manage.

\$\endgroup\$

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.