I'm making a Tic-Tac-Toe game using PyGame on Python where the user plays against an AI. Now I tried making a MiniMax function to recursively find the most optimal move, and by also using Alpha-Beta Pruning. I have (successfully?) made the rest of the game, but I am unable to get this part done. It has incredibly hard and messy. I tried searching everywhere, but none of them made the game like I did. So I'm here. Can someone please help me out?
(The complete code, if needed)
X = 'X'
O = 'O'
EMPTY = None
X_count = 0
O_count = 0
def initial_state():
"""Returns starting state of the board
"""
return [
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY]
]
def display(board, autoprint=False):
"""Displays the board nested list as
a 3x3 matrix for board visualization
"""
vis_board = ''
for row in board:
for playr in row:
if playr is None:
playr = ' '
vis_board += playr + ' '
vis_board += '\n'
if autoprint:
print(vis_board)
return None
return vis_board
def player(board):
"""Returns player who has the next turn on a board
"""
global X, O, X_count, O_count
for i, row in enumerate(board):
for j, move in enumerate(row):
if move == X:
X_count += 1
elif move == O:
O_count += 1
# `X` always starts first
if O_count < X_count:
return O
return X
def actions(board):
"""Returns set of all possible actions
(i, j) available on the board
"""
global EMPTY
action_set = set()
for i, row in enumerate(board):
for j, move in enumerate(row):
if move is EMPTY:
action_set.add((i, j))
return action_set
def result(board, action):
"""Returns the board that results from
making move (i, j) on the board.
"""
global EMPTY
if type(action) is not tuple or len(action) != 2:
raise Exception('invalid action taken')
# Duplicating *board* by slicing the entire list
# into another variable with a different `id`
dup_board = board[:]
# Unpack the coordinates as `I` and `J`
I, J = action
# Check if place has not already been used
if dup_board[I][J] is EMPTY:
dup_board[I][J] = player(dup_board)
#else:
#raise Exception('invalid action taken')
return dup_board
def is_full(board):
"""Returns True if all places have been occupied, else returns False
"""
global EMPTY
for row in board:
for playr in row:
if playr is EMPTY:
return False
return True
def winner(board):
"""Returns the winner of the game, if there is one.
"""
winr = None # Initial declaration to avoid errors if no winner found
# Check diagonally
if (board[1][1] == board[0][0] and board[0][0] == board[2][2])\
or (board[1][1] == board[0][2] and board[0][2] == board[2][0]):
winr = board[1][1]
return winr
for i in range(3):
# Check each row for three-in-a-row
if board[i][0] == board[i][1] and board[i][1] == board[i][2]:
winr = board[i][1]
break
# Check each column for three-in-a-column
elif board[0][i] == board[1][i] and board[1][i] == board[2][i]:
winr = board[1][i]
break
return winr
def terminal(board):
"""Returns True if game is over, False otherwise.
"""
if winner(board) is None and not is_full(board):
return False
return True
def utility(board):
"""Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
"""
global X, O
if terminal(board):
winr = winner(board)
if winr == X:
util = 1
elif winr == O:
util = -1
else:
util = 0
return util
return None
def minimax(board):
"""Returns the optimal action for the current player on the board.
"""
#### THIS FUNCTION WAS JUST MADE TO RUN THE PROGRAM
#### AND IS WHAT I DON'T UNDERSTAND SO PLEASE DO NOT
#### CONSIDER THIS FUNCTION WHILE ANSWERING THE QUESTION,
#### RATHER, PLEASE TELL ME THE ACTUAL METHODOLOGY
#### AND/OR SOME CODE FOR MINIMAX (PROBABLY WITH
#### ALPHA-BETA PRUNING). THANK YOU!
if terminal(board):
return None
max_ = -float('inf')
best_action = None
# Recursively choose the best action in `actions` of *board*
for action in actions(board):
rslt = result(board, action)
if utility(rslt) == 1:
best_action = action
break
return best_action
Also, if there are any other bugs/errors, please tell me so I can correct them.
Thank you in advance!