I have been working on this tic-tac-toe min max project for the past couple days. Right now the program runs but the computer opponent is just choosing the first available space in number order instead of actually optimizing. Would reccomend compiling yourself to better see the issue. If anyone knows what in my script is causing this that would be super helpful.
# MODULES
import pygame, sys
import numpy as np
# initializes pygame
pygame.init()
# ---------
# CONSTANTS
# ---------
WIDTH = 600
HEIGHT = 600
LINE_WIDTH = 15
WIN_LINE_WIDTH = 15
BOARD_ROWS = 3
BOARD_COLS = 3
SQUARE_SIZE = 200
CIRCLE_RADIUS = 60
CIRCLE_WIDTH = 15
CROSS_WIDTH = 25
SPACE = 55
# rgb: red green blue
RED = (255, 0, 0)
BG_COLOR = (28, 170, 156)
LINE_COLOR = (23, 145, 135)
CIRCLE_COLOR = (239, 231, 200)
CROSS_COLOR = (66, 66, 66)
# ------
# SCREEN
# ------
screen = pygame.display.set_mode( (WIDTH, HEIGHT) )
pygame.display.set_caption( 'TIC TAC TOE' )
screen.fill( BG_COLOR )
# -------------
# CONSOLE BOARD
# -------------
board = np.zeros( (BOARD_ROWS, BOARD_COLS) )
# ---------
# FUNCTIONS
# ---------
def draw_lines():
# 1 horizontal
pygame.draw.line( screen, LINE_COLOR, (0, SQUARE_SIZE), (WIDTH, SQUARE_SIZE), LINE_WIDTH )
# 2 horizontal
pygame.draw.line( screen, LINE_COLOR, (0, 2 * SQUARE_SIZE), (WIDTH, 2 * SQUARE_SIZE), LINE_WIDTH )
# 1 vertical
pygame.draw.line( screen, LINE_COLOR, (SQUARE_SIZE, 0), (SQUARE_SIZE, HEIGHT), LINE_WIDTH )
# 2 vertical
pygame.draw.line( screen, LINE_COLOR, (2 * SQUARE_SIZE, 0), (2 * SQUARE_SIZE, HEIGHT), LINE_WIDTH )
def draw_figures():
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
if board[row][col] == 1:
pygame.draw.circle( screen, CIRCLE_COLOR, (int( col * SQUARE_SIZE + SQUARE_SIZE//2 ), int( row * SQUARE_SIZE + SQUARE_SIZE//2 )), CIRCLE_RADIUS, CIRCLE_WIDTH )
elif board[row][col] == 2:
pygame.draw.line( screen, CROSS_COLOR, (col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), (col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SPACE), CROSS_WIDTH )
pygame.draw.line( screen, CROSS_COLOR, (col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SPACE), (col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), CROSS_WIDTH )
def mark_square(row, col, player):
board[row][col] = player
def available_square(row, col):
return board[row][col] == 0
def is_board_full():
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
if board[row][col] == 0:
return False
return True
def is_board_empty(b):
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
if b[row][col] == 0:
return True
return False
def check_win(player, b):
# vertical win check
for col in range(BOARD_COLS):
if b[0][col] == player and b[1][col] == player and b[2][col] == player:
#draw_vertical_winning_line(col, player)
return True
# horizontal win check
for row in range(BOARD_ROWS):
if b[row][0] == player and b[row][1] == player and b[row][2] == player:
#draw_horizontal_winning_line(row, player)
return True
# asc diagonal win check
if b[2][0] == player and b[1][1] == player and b[0][2] == player:
#draw_asc_diagonal(player)
return True
# desc diagonal win chek
if b[0][0] == player and b[1][1] == player and b[2][2] == player:
#draw_desc_diagonal(player)
return True
return False
def restart():
screen.fill( BG_COLOR )
draw_lines()
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
board[row][col] = 0
def minimax(b, max_play, play):
if play == 1 and check_win(play, b):
return -1
if play == 2 and check_win(play, b):
return 1
if is_board_empty(b) == False:
return 0
if max_play:
best = -10000
for row in range(3):
for col in range(3):
if b[row][col] == 0:
b[row][col] = 2
best = max(best, minimax(b, False, play))
b[row][col] = 0
return best
else:
best = 10000
for row in range(3):
for col in range(3):
if b[row][col] == 0:
b[row][col] = 1
best = min(best, minimax(b, True, play))
b[row][col] = 0
return best
def best_move(b, play):
best = -1000
move = (-1, -1)
for row in range(3):
for col in range(3):
if b[row][col] == 0:
b[row][col] = 2
move_val = minimax(b, False, 2)
b[row][col] = 0
if(move_val > best):
move = (row, col)
best = move_val
return move
def cpu_turn(b, play):
choice = best_move(b, play)
b[choice[0]][choice[1]] = 2
draw_lines()
# ---------
# VARIABLES
# ---------
player = 1
game_over = False
# --------
# MAINLOOP
# --------
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN and not game_over and player == 1:
mouseX = event.pos[0] # x
mouseY = event.pos[1] # y
clicked_row = int(mouseY // SQUARE_SIZE)
clicked_col = int(mouseX // SQUARE_SIZE)
if available_square( clicked_row, clicked_col ):
mark_square( clicked_row, clicked_col, player )
if check_win( player, board ):
game_over = True
draw_figures()
player = player % 2 + 1
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
restart()
player = 1
game_over = False
if player == 2 and not game_over:
cpu_turn(board, player)
if check_win( player, board ):
game_over = True
draw_figures()
player = player % 2 + 1
pygame.display.update()