diff --git a/AI Game/Tic-Tac-Toe-AI/README.md b/AI Game/Tic-Tac-Toe-AI/README.md new file mode 100644 index 00000000000..5e8034404c4 --- /dev/null +++ b/AI Game/Tic-Tac-Toe-AI/README.md @@ -0,0 +1,39 @@ +# TIC TAC WITH AI + +## Overview + +TIC TAC WITH AI is a Python-based Tic Tac Toe game using the Pygame library. This project includes a basic implementation of the game with an AI opponent.The AI uses the minimax algorithm to make optimal moves, providing a challenging opponent for players. + +## Features + +- Single-player mode against an AI +- Minimax algorithm for AI decision-making +- Interactive graphical user interface using Pygame +- Restart functionality to play multiple games in one session +- Visual indicators for game outcome: win, lose, or draw + + + +### Game Controls + +- **Mouse Click:** Place your mark (X) on the board. +- **R Key:** Restart the game. + +### Game Rules + +- The game is played on a 3x3 grid. +- You are X, and the AI is O. +- Players take turns placing their marks in empty squares. +- The first player to get three of their marks in a row (horizontally, vertically, or diagonally) wins. +- If all nine squares are filled and neither player has three in a row, the game ends in a draw. + +### Sample Interface + +![image](https://github.com/BhanuSaketh/Python/assets/118091571/41a2823f-b901-485d-b53e-6ddd774dfd96) + + +## How It Works + +- The game board is represented by a 3x3 numpy array. +- The game loop listens for user input and updates the game state accordingly. +- The AI uses the minimax algorithm to evaluate possible moves and choose the best one. diff --git a/AI Game/Tic-Tac-Toe-AI/tictactoe.py b/AI Game/Tic-Tac-Toe-AI/tictactoe.py index 6157ff6efb0..9a6e35d5d2e 100644 --- a/AI Game/Tic-Tac-Toe-AI/tictactoe.py +++ b/AI Game/Tic-Tac-Toe-AI/tictactoe.py @@ -1,104 +1,170 @@ -import tkinter as tk #provides a library of basic elements of GUI widgets -from tkinter import messagebox #provides a different set of dialogues that are used to display message boxes -import random - -def check_winner(board, player): - # Check rows, columns, and diagonals for a win - for i in range(3): - if all(board[i][j] == player for j in range(3)) or all(board[j][i] == player for j in range(3)): +import sys +import numpy as np +import pygame + +pygame.init() +WHITE = (255, 255, 255) +GRAY = (180, 180, 180) +RED = (255, 0, 0) +GREEN = (0, 255, 0) +BLACK = (0, 0, 0) + +WIDTH = 300 +HEIGHT = 300 +BOARD_ROWS = 3 +BOARD_COLS = 3 +SQUARE_SIZE = WIDTH // BOARD_COLS +CIRCLE_RADIUS = SQUARE_SIZE // 3 +CIRCLE_WIDTH = 15 +CROSS_WIDTH = 25 + +screen = pygame.display.set_mode((WIDTH, HEIGHT)) +pygame.display.set_caption("TIC TAC WITH AI") +screen.fill(BLACK) +board = np.zeros((BOARD_ROWS, BOARD_COLS)) + +def draw_lines(color=WHITE): + for i in range(1, BOARD_ROWS): + pygame.draw.line(screen, color, start_pos=(0, SQUARE_SIZE * i), end_pos=(WIDTH, SQUARE_SIZE * i)) + pygame.draw.line(screen, color, start_pos=(SQUARE_SIZE * i, 0), end_pos=(SQUARE_SIZE * i, WIDTH)) + +def draw_figure(color=WHITE): + for row in range(BOARD_ROWS): + for col in range(BOARD_COLS): + if board[row][col] == 1: + pygame.draw.circle(screen, 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, color, (col * SQUARE_SIZE + SQUARE_SIZE // 4, row * SQUARE_SIZE + SQUARE_SIZE // 4), (col * SQUARE_SIZE + 3 * SQUARE_SIZE // 4, row * SQUARE_SIZE + 3 * SQUARE_SIZE // 4), CROSS_WIDTH) + pygame.draw.line(screen, color, (col * SQUARE_SIZE + SQUARE_SIZE // 4, row * SQUARE_SIZE + 3 * SQUARE_SIZE // 4), (col * SQUARE_SIZE + 3 * SQUARE_SIZE // 4, row * SQUARE_SIZE + SQUARE_SIZE // 4), 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(check_board=board): + for row in range(BOARD_ROWS): + for col in range(BOARD_COLS): + if check_board[row][col] == 0: + return False + return True + +def check_win(player, check_board=board): + for col in range(BOARD_COLS): + if check_board[0][col] == player and check_board[1][col] == player and check_board[2][col] == player: return True - if all(board[i][i] == player for i in range(3)) or all(board[i][2 - i] == player for i in range(3)): + for row in range(BOARD_ROWS): + if check_board[row][0] == player and check_board[row][1] == player and check_board[row][2] == player: + return True + if check_board[0][0] == player and check_board[1][1] == player and check_board[2][2] == player: + return True + if check_board[0][2] == player and check_board[1][1] == player and check_board[2][0] == player: return True return False -def is_board_full(board): - return all(all(cell != ' ' for cell in row) for row in board) - -def minimax(board, depth, is_maximizing): - if check_winner(board, 'X'): - return -1 - if check_winner(board, 'O'): - return 1 - if is_board_full(board): #if game is full, terminate +def minimax(minimax_board, depth, is_maximizing): + if check_win(2, minimax_board): + return 1000 - depth + elif check_win(1, minimax_board): + return depth - 1000 + elif is_board_full(minimax_board): return 0 - if is_maximizing: #recursive approach that fills board with Os - max_eval = float('-inf') - for i in range(3): - for j in range(3): - if board[i][j] == ' ': - board[i][j] = 'O' - eval = minimax(board, depth + 1, False) #recursion - board[i][j] = ' ' - max_eval = max(max_eval, eval) - return max_eval - else: #recursive approach that fills board with Xs - min_eval = float('inf') - for i in range(3): - for j in range(3): - if board[i][j] == ' ': - board[i][j] = 'X' - eval = minimax(board, depth + 1, True) #recursion - board[i][j] = ' ' - min_eval = min(min_eval, eval) - return min_eval - -#determines the best move for the current player and returns a tuple representing the position -def best_move(board): - best_val = float('-inf') - best_move = None - - for i in range(3): - for j in range(3): - if board[i][j] == ' ': - board[i][j] = 'O' - move_val = minimax(board, 0, False) - board[i][j] = ' ' - if move_val > best_val: - best_val = move_val - best_move = (i, j) - - return best_move - -def make_move(row, col): - if board[row][col] == ' ': - board[row][col] = 'X' - buttons[row][col].config(text='X') - if check_winner(board, 'X'): - messagebox.showinfo("Tic-Tac-Toe", "You win!") - root.quit() - elif is_board_full(board): - messagebox.showinfo("Tic-Tac-Toe", "It's a draw!") - root.quit() - else: - ai_move() + if is_maximizing: + best_score = float('-inf') + for row in range(BOARD_ROWS): + for col in range(BOARD_COLS): + if minimax_board[row][col] == 0: + minimax_board[row][col] = 2 + score = minimax(minimax_board, depth + 1, False) + minimax_board[row][col] = 0 + best_score = max(score, best_score) + return best_score else: - messagebox.showerror("Error", "Invalid move") - -#AI's turn to play -def ai_move(): - row, col = best_move(board) - board[row][col] = 'O' - buttons[row][col].config(text='O') - if check_winner(board, 'O'): - messagebox.showinfo("Tic-Tac-Toe", "AI wins!") - root.quit() - elif is_board_full(board): - messagebox.showinfo("Tic-Tac-Toe", "It's a draw!") - root.quit() - -root = tk.Tk() -root.title("Tic-Tac-Toe") - -board = [[' ' for _ in range(3)] for _ in range(3)] -buttons = [] - -for i in range(3): - row_buttons = [] - for j in range(3): - button = tk.Button(root, text=' ', font=('normal', 30), width=5, height=2, command=lambda row=i, col=j: make_move(row, col)) - button.grid(row=i, column=j) - row_buttons.append(button) - buttons.append(row_buttons) - -root.mainloop() + best_score = float('inf') + for row in range(BOARD_ROWS): + for col in range(BOARD_COLS): + if minimax_board[row][col] == 0: + minimax_board[row][col] = 1 + score = minimax(minimax_board, depth + 1, True) + # print(score) + minimax_board[row][col] = 0 + best_score = min(score, best_score) + return best_score + +def best_move(): + best_score = float('-inf') + move = (-1, -1) + for row in range(BOARD_ROWS): + for col in range(BOARD_COLS): + if board[row][col] == 0: + board[row][col] = 2 + score = minimax(board, 0, False) + board[row][col] = 0 + if score > best_score: + best_score = score + move = (row, col) + if move != (-1, -1): + mark_square(move[0], move[1], 2) + return True + return False + +def restart_game(): + screen.fill(BLACK) + draw_lines() + for row in range(BOARD_ROWS): + for col in range(BOARD_COLS): + board[row][col] = 0 + +draw_lines() +player = 1 +game_over = False + +while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + if event.type == pygame.MOUSEBUTTONDOWN and not game_over: + mouseX = event.pos[0] // SQUARE_SIZE + mouseY = event.pos[1] // SQUARE_SIZE + + if available_square(mouseY, mouseX): + mark_square(mouseY, mouseX, player) + if check_win(player): + game_over = True + player = player % 2 + 1 + + if not game_over: + if best_move(): + if check_win(2): + game_over = True + player = player % 2 + 1 + if not game_over: + if is_board_full(): + game_over = True + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_r: + restart_game() + game_over = False + player = 1 + + draw_figure() + pygame.display.update() + + if game_over: + if check_win(1): + draw_figure(GREEN) + draw_lines(GREEN) + elif check_win(2): + draw_figure(RED) + draw_lines(RED) + else: + draw_figure(GRAY) + draw_lines(GRAY) + pygame.display.update() + pygame.time.wait(3000) + restart_game() + game_over = False + player = 1