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

Noughts and crosses command line game in Python 3.5

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

Problem

Based on this Code Review answer, I have created a noughts and crosses game in Python, which asks for an index (1 to 9) and updates the grid, and displays it on the screen. I have done some very untidy programming in places, and would please like some help on how to make it more readable, and avoid repeating statements, such as print_grid(). Also I would like to make the 'Change player' part more simple. Any other constructive criticism or improvements also gratefully recieved.

winning_lines = [(0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6),
             (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)]

def get_turn(player):
    return int(input("Player %d: " % player).strip()) - 1

def format_player(num):
    return ' OX'[num]

def print_grid(board):
    """Formats and prints the grid."""

    print('\n'
          '{}│{}│{}\n'
          '─┼─┼─\n'
          '{}│{}│{}\n'
          '─┼─┼─\n'
          '{}│{}│{}\n'
          '\n'
          .format(*board))

def check_for_win(board, player):
    """Returns True for win, anything else, False"""
    for winning_line in winning_lines:  # Take all possible winning lines one by one
        for pos in winning_line: # Test each index of the winning line
            if board[pos] != format_player(player):
                break
        else: return True
    return False

def play_game():
    player = 1
    board = [" "] * 9

    while True:
        print_grid(board)
        place = get_turn(player)
        if place in range(9) and board[place] == " ":
            board[place] = format_player(player)

            if check_for_win(board, player):
                print_grid(board)
                return player

            # Change player

            if player == 1:
                player = 2
            else:
                player = 1

winner = play_game()

print("Player %d wins!" % winner)

Solution

-
The functions get_turn, format_player and play_game have no docstrings. What do they do? What do they return?

-
In check_for_win, the value of format_player(player) does not change and so you could remember it in a local variable to avoid recomputing it on each time around the loop.

-
check_for_win could be simplified using the built-in function all, like this:

def check_for_win(board, player):
    """Returns True if player has won, otherwise False"""
    player = format_player(player)
    for winning_line in winning_lines:
        if all(board[pos] == player for pos in winning_line):
            return True
    return False


and then that could be futher simplified using the built-in function any, like this:

def check_for_win(board, player):
    """Returns True if player has won, otherwise False"""
    player = format_player(player)
    return any(all(board[pos] == player for pos in winning_line)
               for winning_line in winning_lines)


-
If the player's input is incorrect, there is no message explaining what the problem was.

-
Repeatedly switching between player 1 and player 2 can be done using itertools.cycle, like this:

from itertools import cycle
for player in cycle((1, 2)):
    # etc.


-
I think that printing the winner should be the responsibility of the play_game function, not of the top-level code.

-
There is no check to see if the game is tied. If the board is full but there is no winner then the game continues to prompt player 2 for a move.

Code Snippets

def check_for_win(board, player):
    """Returns True if player has won, otherwise False"""
    player = format_player(player)
    for winning_line in winning_lines:
        if all(board[pos] == player for pos in winning_line):
            return True
    return False
def check_for_win(board, player):
    """Returns True if player has won, otherwise False"""
    player = format_player(player)
    return any(all(board[pos] == player for pos in winning_line)
               for winning_line in winning_lines)
from itertools import cycle
for player in cycle((1, 2)):
    # etc.

Context

StackExchange Code Review Q#152629, answer score: 3

Revisions (0)

No revisions yet.