# Tic Tac Toe

Written by Mehran Sahami

This program runs a game of tic tac toe, and can even determine who wins!

Mehran made a video walking through the example. Good times:

## Solution

"""
File: tictactoe.py
-----------------
This program allows to players to play tic-tac-toe.  The board in
the game is SIZE x SIZE, set using the constant SIZE.  Each element
on the board will contain either 'X', 'O', or None to indicate that
either a particular player occupies a particular space in the grid
or the space is empty (contains None).
"""

SIZE = 3    # The board used will be SIZE x SIZE

def create_empty_board(n):
"""
Creates and returns a grid (list of lists), that has n
rows and each row has n columns.  All the elements in
the grid are set to None.
>>> create_empty_board(2)
[[None, None], [None, None]]
"""
grid = []                   # Create empty grid
for y in range(n):          # Create rows one at a time
row = []
for x in range(n):      # Build up each row by appending to a list
row.append(None)
grid.append(row)        # Append the row (list) onto grid
return grid

def player_turn(board, symbol):
"""
Asks player (symbol) to make a move and records that move on
the board.  Players are prompted if they make an invalid move.
"""
valid_move = False
while not valid_move:
print(symbol + "'s move")
row = int(input("Row: "))
col = int(input("Col: "))
# Make sure the move is in empty space and is on the board.
if row < 0 or row >= SIZE \
or col < 0 or col >= SIZE \
or board[row][col]:
print("Invalid move.  Try again.")
else:
board[row][col] = symbol    # Record a valid move
valid_move = True

def check_row(board, row):
"""
Checks to see if one of the players got all the spaces in a row.  If so,
it returns the symbol for that player.  If not, it returns None.
"""
symbol = board[row][0]
for col in range(1, SIZE):
if board[row][col] != symbol:   # If we find non-matching symbol then
return None                 # their isn't a winner on this row.
return symbol   # Will only get here if all symbols in row match

def check_column(board, col):
"""
Checks to see if one of the players got all the spaces in a column.  If so,
it returns the symbol for that player.  If not, it returns None.
"""
symbol = board[0][col]
for row in range(1, SIZE):
if board[row][col] != symbol:
return None
return symbol   # Will only get here is all symbols in column match

def check_down_diagonal(board):
"""
Checks to see if one of the players got all the spaces in the diagonal
going from top-left corner to bottom-right corner.  If so,
it returns the symbol for that player.  If not, it returns None.
"""
symbol = board[0][0]
for row in range(1, SIZE):
if board[row][row] != symbol:
return None
return symbol   # Will only get here is all symbols in diagonal match

def check_up_diagonal(board):
"""
Checks to see if one of the players got all the spaces in the diagonal
going from bottom-left corner to top-right corner.  If so,
it returns the symbol for that player.  If not, it returns None.
"""
symbol = board[0][SIZE - 1]
for row in range(1, SIZE):
if board[row][SIZE - 1 - row] != symbol:
return None
return symbol   # Will only get here is all symbols in diagonal match

def check_winner(board):
"""
Checks to see if one of the players got three in a row by checking
all the rows, columns, and two diagonals on the board.  If a player
got three in a row, return the symbol for that player.  If not,
return None.
"""
# Check rows
for row in range(SIZE):
winner = check_row(board, row)
if winner:
return winner

# Check columns
for col in range(SIZE):
winner = check_column(board, col)
if winner:
return winner

# Check diagonals
winner = check_down_diagonal(board)
if winner:
return winner
winner = check_up_diagonal(board)

return winner   # This could be None if last check didn't find a winner

def flip_turn(symbol):
"""
Flips the player (symbol) whose turn it is.
"""
if symbol == 'X':
return 'O'
else:
return 'X'

def print_row_separator(columns):
"""
Prints a row separator line for the board.
For example, if columns is 3, it will print:
--+---+--
"""
print("--+", end="")
for i in range(1, columns - 1):
print("---+", end="")
print("--")

def print_board(board):
"""
Prints out tic-tac-board board
"""
rows = len(board)                   # Could have used SIZE, but wanted
cols = len(board[0])                # to show a different way to do this.
for y in range(rows):
for x in range(cols):
symbol = board[y][x]
if not symbol:              # Print a space if symbol is None
symbol = " "
print(symbol, end="")
if x < SIZE - 1:            # Print column separator if not at end of line
print(" | ", end="")
else:
print("")               # Print "new line" at end of line
if y < SIZE - 1:                # Print row separator if not at end of grid
print_row_separator(cols)

def main():
"""
Play a two-player game of tic-tac-toe
"""
winner = None
board = create_empty_board(SIZE)
player = 'X'                    # Player X goes first
num_moves = 0
while winner == None:           # Take turns until we have winner
print_board(board)
player_turn(board, player)
num_moves += 1              # Keep track of total moves made
winner = check_winner(board)
if not winner:
if num_moves == SIZE ** 2:  # If all spaces on board are filled
winner = "No one"       # then there is no winner.
player = flip_turn(player)

print_board(board)
print(winner + " won!")

if __name__ == '__main__':
main()