# Game of tic tac toe. The nine locations on
# the board are numbered 0-8
status = None
game_over = False
# Dictionary with keys containing the board locations (0-8)
# and values are the squares at those locations
squares_by_position = {}
x_positions = []
o_positions = []
# The free positions on the board listed in order they should
# be taken. For example, the center position (4) is listed first
free_positions = [4, 0, 2, 6, 8, 1, 3, 5, 7]
center_position = 4
opposite_corners = [
[0, 8],
[2, 6]
]
side_positions = [1, 3, 5, 7]
# The positions of the 8 ways to win: three rows, three columns, and
# two diagonals
tic_tac_toes = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]]
# Look for a case where I have two positions in a row, column, or diagonal
# and the third position is free. I should select this position to win.
def get_position_to_win():
for tic_tac_toe in tic_tac_toes:
o_count = 0
free_count = 0
position_to_win = None
for position in tic_tac_toe:
if position in o_positions:
o_count += 1
elif not is_taken(position):
free_count += 1
position_to_win = position
if o_count == 2 and free_count == 1:
return position_to_win
return None
# Look for a case where my opponent has two positions in a row, column,
# or diagonal and the third position is free. I should select this position
# to block my opponent from winning.
def get_position_to_block():
for tic_tac_toe in tic_tac_toes:
x_count = 0
free_count = 0
position_to_block = None
for position in tic_tac_toe:
if position in x_positions:
x_count += 1
elif not is_taken(position):
free_count += 1
position_to_block = position
if x_count == 2 and free_count == 1:
return position_to_block
# Special case: if the player has two opposite corners
# and I have the center, select a side position rather
# than a third corner
if len(free_positions) == 6 and are_opposite_corners(x_positions) and 4 in o_positions:
for side_position in side_positions:
if side_position in free_positions:
return side_position
return None
# Return true if the two positions are in opposite corners and false otherwise
def are_opposite_corners(positions):
if len(positions) != 2:
return false;
for corner_positions in opposite_corners:
if positions[0] in corner_positions and positions[1] in corner_positions:
return True
return False
# See if all the positions in a row, column, or diagonal are taken
def is_tic_tac_toe(positions, tic_tac_toe):
for position in tic_tac_toe:
if not position in positions:
return False
return True
# See if there is a winner based on the positions I or my opponent has
def check_for_winner(positions):
if len(positions) < 3:
return False
for tic_tac_toe in tic_tac_toes:
if is_tic_tac_toe(positions, tic_tac_toe):
# Draw a red line from the starting to ending position of the tic tac toe
square0 = squares_by_position[tic_tac_toe[0]]
square2 = squares_by_position[tic_tac_toe[2]]
sprite = codesters.Line(square0.get_x(), square0.get_y(), square2.get_x(), square2.get_y(), "red")
return True
return False
# Return true if a position is take and false otherwise
def is_taken(position):
return not position in free_positions
# Briefly display a message above the board to the player
def set_status(text, wait_time=2, color="blue"):
global status
if not status is None:
status.hide()
status = codesters.Text(text, 0, 200, color)
stage.wait(wait_time)
status.hide()
# Process player clicks
def click(sprite):
global your_turn, game_over
# Check if the game is already over
if game_over:
set_status("Hey, the game is already over!", color="red")
return
position = sprite.position
# See if the position is already taken
if is_taken(position):
set_status("Hey, that position is already taken!", color="red")
return
# Put an X in the postion the player clicked on
x_positions.append(position)
free_positions.remove(position)
squares_by_position[position].text.set_text("X")
# Check to see if the player won
if check_for_winner(x_positions):
game_over = True
set_status("Congratulations, you won!")
return
# See if all the positions have been taken
if len(free_positions) == 0:
game_over = True
set_status("Cat's Game")
return
# Nobody has won yet. See of there's a move for me to win
my_move = get_position_to_win()
# If there's no move to win, see if there's a move to block the player from winning
if my_move is None:
my_move = get_position_to_block()
# Otherwise, choose to best remaining free space on the board
if my_move is None:
my_move = free_positions[0]
# Make my move
o_positions.append(my_move)
free_positions.remove(my_move)
square = squares_by_position[my_move]
square.text.set_text("O")
# Check to see if I won
if check_for_winner(o_positions):
game_over = True;
set_status("Hey, what do you know, I won!")
# Draw the board
board = codesters.Square(0, 0, 500, "white")
# Draw a pair of vertical lines and a pair of horizontal lines
for coordinate in [-50, 50]:
codesters.Line(-150, coordinate, 150, coordinate, "blue")
codesters.Line(coordinate, 150, coordinate, -150, "blue")
# Create 9 squares in which to write the Xs and Os
x_coordinates = [-100, 0, 100]
y_coordinates = [100, 0, -100]
for row in range(3):
for column in range(3):
square = codesters.Square(x_coordinates[column], y_coordinates[row], 97, "white")
square.position = (row * 3) + column
text = " "
text = str(square.position)
square.text = codesters.Text(" ", x_coordinates[column], y_coordinates[row], "blue")
squares_by_position[square.position] = square
square.event_click(click)
set_status("It's your turn")