As the title suggests, I'm trying to create a Tic-Tac-Toe game using the Minimax algorithm.
My problem occurs when the computer is supposed to counter my movements. Instead of doing so, it plays in what seems to be random yet repetitive patterns.
This is my first time using the Minimax algorithm, so any help or advice would be welcomed.
Thanks in advance!
Here is my code:
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
import numpy as np
import random
class Board(GridLayout):
def __init__(self):
GridLayout.__init__(self)
self.cols = 3
self.level = 8
self.listBoard = []
array = ([0, 0, 0],
[0, 0, 0],
[0, 0, 0])
self.array = np.array(array)
for i in range(self.cols):
listLine = []
for j in range(self.cols):
cell = Button(font_size=50, on_press=self.click)
cell.i = i
cell.j = j
self.add_widget(cell)
listLine.append(cell)
self.listBoard.append(listLine)
self.check = False
start = random.randint(0, 1)
self.player = ''
self.computer = ''
# player is 1, computer is 10
if start == 1:
self.player = 'X'
self.computer = 'O'
else:
self.computer = 'X'
self.player = 'O'
move = self.minimax(self.array.copy(), self.level)
self.listBoard[move[0]][move[1]].text = self.computer
self.array[move[0]][move[1]] = 10
print(self.array)
def click(self, button):
if button.text == '' and not self.check:
button.text = self.player
self.array[button.i][button.j] = 1
print(self.array)
if self.check_victory(self.array) == 'Player':
print('Player Wins!')
self.check = True
moves = self.find_moves(self.array)
if moves.any() and not self.check:
move = self.minimax(self.array.copy(), self.level)
self.listBoard[move[0]][move[1]].text = self.computer
self.array[move[0]][move[1]] = 10
print(self.array)
elif self.check_victory(self.array) == 'None':
print('Tie!')
self.check = True
if self.check_victory(self.array) == 'Computer' and not self.check:
print('Computer Wins!')
self.check = True
moves = self.find_moves(self.array)
if not moves.any() and self.check_victory(self.array) == 'None' and not self.check:
print('Tie!')
def find_moves(self, array):
moves = np.argwhere(array == 0)
return moves
def check_victory(self, array):
temp1 = array
temp2 = np.rot90(array)
column1 = np.sum(temp1, axis=0)
column2 = np.sum(temp2, axis=0)
diagonal1 = np.diag(temp1).sum()
diagonal2 = np.diag(temp2).sum()
for i in range(3):
if column1[i] % 10 == 3 or column2[i] % 10 == 3:
return 'Player'
if column1[i] / 10 == 3 or column2[i] / 10 == 3:
return 'Computer'
if diagonal1 % 10 == 3 or diagonal2 % 10 == 3:
return 'Player'
if diagonal1 / 10 == 3 or diagonal2 / 10 == 3:
return 'Computer'
return 'None'
def minimax(self, game_state, level):
moves = self.find_moves(game_state)
best_move = moves[0]
best_score = float('-inf')
for move in moves:
clone = self.next_state(game_state.copy(), move[0], move[1], 'Computer')
score = self.min_play(clone, level - 1)
if score > best_score:
best_move = move
best_score = score
return best_move
def min_play(self, game_state, level):
if self.check_victory(game_state) == 'Player' or level == 0:
return self.evaluate(game_state)
moves = self.find_moves(game_state)
best_score = float('inf')
for move in moves:
clone = self.next_state(game_state.copy(), move[0], move[1], 'Player')
score = self.max_play(clone, level - 1)
if score < best_score:
# best_move = move
best_score = score
return best_score
def max_play(self, game_state, level):
if self.check_victory(game_state) == 'Computer' or level == 0:
return self.evaluate(game_state)
moves = self.find_moves(game_state)
best_score = float('-inf')
for move in moves:
clone = self.next_state(game_state, move[0], move[1], 'Computer')
score = self.min_play(clone, level - 1)
if score > best_score:
# best_move = move
best_score = score
return best_score
def next_state(self, array, i, j, who):
if who == 'Player':
array[i][j] = 1
else:
array[i][j] = 10
return array
def evaluate(self, array):
if self.check_victory(array) == 'Player':
return -1
if self.check_victory(array) == 'Computer':
return 1
return 0
class TestApp(App):
def build(self):
self.title = 'based graphics'
return Board()
TestApp().run()