csc-665-homework/hw1/tictactoe/tictactoe.py

188 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import math
# Use these constants to fill in the game board
X = "X"
O = "O"
EMPTY = None
def start_state():
"""
Returns starting state of the board.
"""
return [[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY]]
def player(board):
"""
Returns which player (either X or O) who has the next turn on a board.
In the initial game state, X gets the first move. Subsequently, the player alternates with each additional move.
Any return value is acceptable if a terminal board is provided as input (i.e., the game is already over).
"""
count_X = sum(row.count(X) for row in board)
count_O = sum(row.count(O) for row in board)
if count_X > count_O:
return O
else:
return X
def actions(board):
"""
Returns the set of all possible actions (i, j) available on the board.
The actions function should return a set of all the possible actions that can be taken on a given board.
Each action should be represented as a tuple (i, j) where i corresponds to the row of the move (0, 1, or 2)
and j corresponds to the column of the move (also 0, 1, or 2).
Possible moves are any cells on the board that do not already have an X or an O in them.
Any return value is acceptable if a terminal board is provided as input.
"""
actions = set()
for i in range(3):
for j in range(3):
if board[i][j] == EMPTY:
actions.add((i, j))
return actions
def succ(board, action):
"""
Returns the board that results from making move (i, j) on the board, without modifying the original board.
If `action` is not a valid action for the board, you should raise an exception.
The returned board state should be the board that would result from taking the original input board, and letting
the player whose turn it is make their move at the cell indicated by the input action.
Importantly, the original board should be left unmodified. This means that simply updating a cell in `board` itself
is not a correct implementation of this function. Youll likely want to make a deep copy of the board first before
making any changes.
"""
if action not in actions(board):
raise ValueError("Invalid action")
new_board = [row[:] for row in board]
new_board[action[0]][action[1]] = player(board)
return new_board
def winner(board):
"""
Returns the winner of the game, if there is one.
- If the X player has won the game, the function should return X.
- If the O player has won the game, the function should return O.
- If there is no winner of the game (either because the game is in progress, or because it ended in a tie), the
function should return None.
You may assume that there will be at most one winner (that is, no board will ever have both players with
three-in-a-row, since that would be an invalid board state).
"""
# Check rows
for row in board:
if row[0] == row[1] == row[2] and row[0] is not None:
return row[0]
# Check columns
for col in range(3):
if board[0][col] == board[1][col] == board[2][col] and board[0][col] is not None:
return board[0][col]
# Check diagonals
if board[0][0] == board[1][1] == board[2][2] and board[0][0] is not None:
return board[0][0]
if board[0][2] == board[1][1] == board[2][0] and board[0][2] is not None:
return board[0][2]
return None
def terminal(board):
"""
Returns True if game is over, False otherwise.
If the game is over, either because someone has won the game or because all cells have been filled without anyone
winning, the function should return True.
Otherwise, the function should return False if the game is still in progress.
"""
if winner(board) is not None or all(all(cell is not EMPTY for cell in row) for row in board):
return True
else:
return False
def utility(board):
"""
Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
You may assume utility will only be called on a board if terminal(board) is True.
"""
if winner(board) == X:
return 1
elif winner(board) == O:
return -1
else:
return 0
def minimax(board):
"""
Returns the optimal action for the current player on the board.
The move returned should be the optimal action (i, j) that is one of the allowable actions on the board.
If multiple moves are equally optimal, any of those moves is acceptable.
If the board is a terminal board, the minimax function should return None.
"""
if terminal(board):
return None
if player(board) == X:
best_score = float('-inf')
best_action = None
for action in actions(board):
score = min_value(succ(board, action))
if score > best_score:
best_score = score
best_action = action
return best_action
else:
best_score = float('inf')
best_action = None
for action in actions(board):
score = max_value(succ(board, action))
if score < best_score:
best_score = score
best_action = action
return best_action
"""
Helper function lol
"""
def max_value(board):
if terminal(board):
return utility(board)
v = float('-inf')
for action in actions(board):
v = max(v, min_value(succ(board, action)))
return v
"""
Another helper function lol
"""
def min_value(board):
if terminal(board):
return utility(board)
v = float('inf')
for action in actions(board):
v = min(v, max_value(succ(board, action)))
return v