From c8414ae30424a58218e30f7d393532cb0f8ba01b Mon Sep 17 00:00:00 2001 From: "leo.tuaillon" Date: Mon, 24 Apr 2023 23:13:35 +0200 Subject: [PATCH] v1 ai morpion --- TicTacToe.py | 259 ++++++++++++++++++++++++++ __pycache__/constants.cpython-310.pyc | Bin 0 -> 466 bytes constants.py | 25 +++ 3 files changed, 284 insertions(+) create mode 100644 TicTacToe.py create mode 100644 __pycache__/constants.cpython-310.pyc create mode 100644 constants.py diff --git a/TicTacToe.py b/TicTacToe.py new file mode 100644 index 0000000..14f56e2 --- /dev/null +++ b/TicTacToe.py @@ -0,0 +1,259 @@ +import sys +import pygame +import numpy as np +import random +import copy +import time + +from constants import * + +# Initialize pygame +pygame.init() +screen = pygame.display.set_mode((WIDTH, LENGHT)) +pygame.display.set_caption("Tic Tac Toe Game - AI ") +screen.fill( BACKGROUND_COLOR ) + +#Class : Matrix +class Matrix: + def __init__(self): + self.cases = np.zeros((GRID_ROWS, GRID_COLS)) + self.empty_cases = self.cases + self.marked_case = 0 + + + def winer(self): + ''' + Check if there is a winner + return 0 if there is no winner yet + return 1 if player 1 win + return 2 if player 2 win + ''' + + # Check vertical lines + for column in range(GRID_COLS): + if self.cases[0][column] == self.cases[1][column] == self.cases[2][column] != 0: + return self.cases[0][column] + # Check horizontal lines + for row in range(GRID_ROWS): + if self.cases[row][0] == self.cases[row][1] == self.cases[row][2] != 0: + return self.cases[row][0] + + # Check diagonals + if self.cases[0][0] == self.cases[1][1] == self.cases[2][2] != 0: + return self.cases[0][0] + if self.cases[0][2] == self.cases[1][1] == self.cases[2][0] != 0: + return self.cases[0][2] + # Check if there is no winner yet + return 0 + + def empty_case(self, row,column): + return self.cases[row][column] == 0 + + def mark_case(self, row, column, player): + self.cases[row][column] = player + self.marked_case += 1 + + def get_empty_cases(self): + empty_cases = [] + for row in range(GRID_ROWS): + for column in range(GRID_COLS): + if self.empty_case(row, column): + empty_cases.append((row, column)) + return empty_cases + + def isfull(self): + return self.marked_case == 9 + + def isempty(self): + return self.marked_case == 0 + +#Class : AI +class AI: + + def __init__(self, level= 5, player=2): + self.level = level + self.player = player + + def randomM(self,matrix): + empty_cases = matrix.get_empty_cases() + a = random.randrange(0,len(empty_cases)) + + return empty_cases[a] # (row, column) + + def minimax(self, matrix, maximizing): + + #Check if terminal case + case = matrix.winer() + #player 1 win + if case == 1: + return 1, None # eval, move + #player 2 win + if case == 2: + return -1, None # eval, move + #draw + elif matrix.isfull(): + return 0, None # eval, move + + if maximizing: + max_eval = -100 + best_move = None + empty_cases = matrix.get_empty_cases() + + for (row,column) in empty_cases: + temp_matrix = copy.deepcopy(matrix) + temp_matrix.mark_case(row, column, 1) + eval = self.minimax(temp_matrix, False)[0] + if eval > max_eval: + max_eval = eval + best_move = (row, column) + + return max_eval, best_move # eval, move + + elif not maximizing: + min_eval = 100 + best_move = None + empty_cases = matrix.get_empty_cases() + + for (row,column) in empty_cases: + temp_matrix = copy.deepcopy(matrix) + temp_matrix.mark_case(row, column, self.player) + eval = self.minimax(temp_matrix, True)[0] + if eval < min_eval: + min_eval = eval + best_move = (row, column) + + return min_eval, best_move # eval, move + + + def evaluate(self, main_matrix): + if self.level == 0: + #random choice + eval = 'random' + move = self.randomM(main_matrix) + else: + #minimax algo choice + print("AI is thinking...") + time.sleep(1) + eval, move = self.minimax(main_matrix, False) + + print(f"AI has chosen the pos {move} with an eval of {eval}") + + return move # (row, column) + +#Class : Game +class Game: + def __init__(self): + self.matrix = Matrix() + self.ai = AI() + self.player = 1 #1 = X, 2 = O + self.gamemode = 'ai' #1v1 or 1vAI + self.running = True + self.lines() + + def draw_figures(self, row, column): + if self.player == 1: + #draw X + start_desc = (column * CASE_SIZE + CASE_SIZE // 4, row * CASE_SIZE + CASE_SIZE // 4) + end_desc = (column * CASE_SIZE + CASE_SIZE * 3 // 4, row * CASE_SIZE + CASE_SIZE * 3 // 4) + pygame.draw.line(screen,PLAYER1_COLOR, start_desc, end_desc, CROSS_WIDTH) + start_asc = (column * CASE_SIZE + CASE_SIZE // 4, row * CASE_SIZE + CASE_SIZE * 3 // 4) + end_asc = (column * CASE_SIZE + CASE_SIZE * 3 // 4, row * CASE_SIZE + CASE_SIZE // 4) + pygame.draw.line(screen,PLAYER1_COLOR, start_asc, end_asc, CROSS_WIDTH) + elif self.player == 2: + #draw O + center = ( column * CASE_SIZE + CASE_SIZE // 2, row * CASE_SIZE + CASE_SIZE // 2) + pygame.draw.circle(screen, PLAYER2_COLOR,center, CIRCLE_RADIUS, CIRCLE_WIDTH) + + def make_move(self, row, col): + self.matrix.mark_case(row, col, self.player) + self.draw_figures(row, col) + self.player_turn() + + def player_turn(self): + self.player = self.player % 2 + 1 + + def lines(self): + # Horizontal lines + pygame.draw.line(screen, LINE_COLOR, (0, CASE_SIZE), (WIDTH,CASE_SIZE), LINE_WIDTH) + pygame.draw.line(screen, LINE_COLOR, (0, LENGHT - CASE_SIZE), (WIDTH, LENGHT - CASE_SIZE), LINE_WIDTH) + + #Vertical lines + pygame.draw.line(screen, LINE_COLOR, (CASE_SIZE, 0), (CASE_SIZE, LENGHT), LINE_WIDTH) + pygame.draw.line(screen, LINE_COLOR, (WIDTH - CASE_SIZE, 0), (WIDTH - CASE_SIZE, LENGHT), LINE_WIDTH) + + def change_gamemode(self): + if self.gamemode == 'ai': + self.gamemode = '1v1' + else: + self.gamemode = 'ai' + + def isover(self): + return self.matrix.winer() != 0 or self.matrix.isfull() + + def reset(self): + self.__init__() + screen.fill(BACKGROUND_COLOR) + self.lines() + +def main(): + + # Object of Game class + game = Game() + matrix = game.matrix + ai = game.ai + + # Main loop + while True: + + for event in pygame.event.get(): + + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + + if event.type == pygame.KEYDOWN: + + # g-gamemode + if event.key == pygame.K_g: + game.change_gamemode() + + #r-restart + if event.key == pygame.K_r: + game.reset() + matrix = game.matrix + ai = game.ai + + # 0-random ai + if event.key == pygame.K_0: + ai.level = 0 + + # 1-easy ai + if event.key == pygame.K_1: + ai.level = 1 + + #click event + if event.type == pygame.MOUSEBUTTONDOWN: + pos = event.pos + row = pos[1] // CASE_SIZE + column = pos[0] // CASE_SIZE + + if matrix.empty_case(row,column) and game.running: + game.make_move(row, column) + + if game.isover(): + game.running = False + + if game.gamemode == 'ai' and game.player == ai.player and game.running: + # update the screen + pygame.display.update() + + # AI turn + row, column = ai.evaluate(matrix) + game.make_move(row, column) + + if game.isover(): + game.running = False + + pygame.display.update() +main() + diff --git a/__pycache__/constants.cpython-310.pyc b/__pycache__/constants.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..598e2be983ea218f1ee723cf83d79a50ccd6d802 GIT binary patch literal 466 zcmYk3F;9a)7={nHQc6p7wJt6WU7F}pU37V0XnY`gAsOkb6NQi(Lh1tqMH~S}NU-S+GD9Z`UG7x6 z=lV|4JY-ldvpgqZrtQwcYS7kZvv08=4BL~3H>PzrVZqF|6Sy&8?LCbf_n!ISu=94S zt!GVrCB-qmFyiMly@+RJR+RBN%eT8YTBXqABJ_A9Jx=XIJot WNI3*hnMxQ