My solutions to Harvard's online course CS50AI, An Introduction to Machine Learning
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

180 lines
4.1 KiB

"""
Tic Tac Toe Player
"""
import math
import copy
X = "X"
O = "O"
EMPTY = None
def initial_state():
"""
Returns starting state of the board.
"""
return [[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY]]
def player(board):
X_count = 0
O_count = 0
for i in range(3): # Count the number of Xs and Os
for j in range(3):
if board[i][j] == X:
X_count += 1
elif board[i][j] == O:
O_count += 1
if X_count == O_count: # If X and Os are equal, then it is X's turn
return X
return O
def actions(board):
action_set = set()
for i in range(3):
for j in range(3):
if not board[i][j]:
action_set.add((i, j))
return action_set
def result(board, action):
if board[action[0]][action[1]]:
raise Exception("You cannot make that action!")
board_copy = copy.deepcopy(board)
board_copy[action[0]][action[1]] = player(board)
return board_copy
def winner(board):
for i in range(3):
column = [board[x][i] for x in range(3)]
if EMPTY not in board[i]: # Skip row if there is an empty square
if all([x == board[i][0] for x in board[i]]):
return board[i][0]
if EMPTY not in column: # Skip row if there is an empty square
if all([x == column[0] for x in column]):
return column[0]
# Check for diagonal matches
for i in [0, 2]:
diagonal = [board[abs(x)][abs(i-x)] for x in range(3)]
if EMPTY not in diagonal: # Skip row if there is an empty square
if all([x == diagonal[0] for x in diagonal]):
return diagonal[0]
return None
def terminal(board):
# Check for horizontal matches
for i in range(3):
column = [board[x][i] for x in range(3)]
if EMPTY not in board[i]: # Skip row if there is an empty square
if all([x == board[i][0] for x in board[i]]):
return True
if EMPTY not in column: # Skip row if there is an empty square
if all([x == column[0] for x in column]):
return True
# Check for diagonal matches
for i in [0, 2]:
diagonal = [board[abs(x)][abs(i-x)] for x in range(3)]
if EMPTY in diagonal: # Skip row if there is an empty square
continue
if all([x == diagonal[0] for x in diagonal]):
return True
# Check whether the board is full
for i in range(3):
for j in range(3):
if not board[i][j]:
return False
return True
def utility(board):
# Check for horizontal matches
for i in range(3):
column = [board[x][i] for x in range(3)]
if EMPTY not in board[i]: # Skip row if there is an empty square
if all([x == board[i][0] for x in board[i]]):
if board[i][0] == X:
return 1
return -1
if EMPTY not in column: # Skip row if there is an empty square
if all([x == column[0] for x in column]):
if column[0] == X:
return 1
return -1
# Check for diagonal matches
for i in [0, 2]:
diagonal = [board[abs(x)][abs(i-x)] for x in range(3)]
if EMPTY in diagonal: # Skip row if there is an empty square
continue
if all([x == diagonal[0] for x in diagonal]):
if diagonal[0] == X:
return 1
return -1
# Check whether the board is full
for i in range(3):
for j in range(3):
if not board[i][j]:
return False
return 0
def minimax(board):
if terminal(board):
return None
optimum_actions = set()
neutral_actions = set()
for action in actions(board):
if player(board) == X:
min_result = min_value(result(board,action))
if min_result == 1:
optimum_actions.add(action)
elif min_result == 0:
neutral_actions.add(action)
else:
max_result = max_value(result(board,action))
if max_result == -1:
optimum_actions.add(action)
elif max_result == 0:
neutral_actions.add(action)
if not len(optimum_actions):
return neutral_actions.pop()
return optimum_actions.pop()
def min_value(board):
if terminal(board):
return utility(board)
val = 2
for action in actions(board):
val = min(val, max_value(result(board, action)))
if val == -1: # Do alpha beta pruning
return val
return val
def max_value(board):
if terminal(board):
return utility(board)
val = -2
for action in actions(board):
val = max(val, min_value(result(board, action)))
if val == 1: # Do alpha beta pruning
return val
return val