-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #43 from Naumantamboli/main
Tic-Tac-Toe game with AI (using Minmax)
- Loading branch information
Showing
3 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Tic-Tac-Toe Game with AI | ||
|
||
This project is a Tic-Tac-Toe game implemented in Python using the Pygame library. The game presents a classic, engaging experience where players can compete against either an AI opponent or another human player. The AI leverages the Minimax algorithm, a decision-making algorithm used in game theory, to evaluate potential moves and choose the best possible action at each turn. This guarantees that the AI will always make optimal moves, creating a challenging and competitive environment for players of all skill levels. | ||
|
||
### Key Features: | ||
- **Two Players**: Play against an AI or another human player. | ||
- **Minimax Algorithm**: The AI makes optimal moves based on the Minimax strategy. | ||
- **Graphical User Interface**: A simple and intuitive GUI using Pygame. | ||
- **Reset Option**: Restart the game easily at any time. | ||
|
||
# Prerequisites | ||
The following libraries are required to run the script: | ||
```bash | ||
pip install pygame numpy | ||
``` | ||
|
||
# Installing Instructions | ||
1. **Clone the Repository**: | ||
Clone this repository to your local machine using: | ||
```bash | ||
git clone <repository-url> | ||
``` | ||
|
||
2. **Install Dependencies**: | ||
Navigate to the project directory and install the required packages: | ||
```bash | ||
pip install pygame numpy | ||
``` | ||
|
||
3. **Run the Script**: | ||
After cloning and installing dependencies, you can run the script directly: | ||
```bash | ||
python tic_tac_toe.py | ||
``` | ||
|
||
4. **Customize the Script**: | ||
You can modify game settings, such as board size and player options, directly in the script. | ||
|
||
# Gameplay Instructions | ||
- **Player vs AI:**: Click on the grid to make your move. The AI will respond automatically. | ||
- **AI Difficulty Levels**: You can adjust the difficulty of the AI in the game settings to suit your skill level. Higher levels make the AI more challenging. | ||
- **Restart Game**: Press the R key to restart the game at any time. | ||
|
||
# Usage | ||
**Player vs AI**: Click on the grid to make your move. The AI will respond automatically. | ||
|
||
**Restart Game**: Press the R key to restart the game at any time. | ||
|
||
# Example | ||
After starting the game, you can play by clicking on the grid cells. The AI will make its move following your turn, and the game will continue until one player wins or the game ends in a draw. | ||
![Game Screen](screenshots/screenshot1.png) | ||
![Winning State](screenshots/screenshot2.png) | ||
|
||
|
||
# Author | ||
**[Naumantamboli](https://github.com/Naumantamboli)** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pygame==2.1.3 | ||
numpy==1.23.5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
import pygame | ||
import sys | ||
import numpy as np | ||
|
||
pygame.init() | ||
|
||
WIDTH, HEIGHT = 600, 600 | ||
LINE_WIDTH = 15 | ||
BOARD_ROWS = 3 | ||
BOARD_COLS = 3 | ||
SQUARE_SIZE = WIDTH // BOARD_COLS | ||
CIRCLE_RADIUS = SQUARE_SIZE // 3 | ||
CIRCLE_WIDTH = 15 | ||
CROSS_WIDTH = 25 | ||
SPACE = SQUARE_SIZE // 4 | ||
|
||
BG_COLOR = (28, 170, 156) | ||
LINE_COLOR = (23, 145, 135) | ||
CIRCLE_COLOR = (239, 231, 200) | ||
CROSS_COLOR = (66, 66, 66) | ||
TEXT_COLOR = (255, 255, 255) | ||
|
||
screen = pygame.display.set_mode((WIDTH, HEIGHT)) | ||
pygame.display.set_caption('Tic Tac Toe') | ||
screen.fill(BG_COLOR) | ||
|
||
font = pygame.font.Font(None, 40) # Font for displaying messages | ||
board = np.zeros((BOARD_ROWS, BOARD_COLS)) | ||
|
||
difficulty = "medium" # AI difficulty level ("easy", "medium", "hard") | ||
|
||
def draw_lines(): | ||
for row in range(1, BOARD_ROWS): | ||
pygame.draw.line(screen, LINE_COLOR, (0, row * SQUARE_SIZE), (WIDTH, row * SQUARE_SIZE), LINE_WIDTH) | ||
for col in range(1, BOARD_COLS): | ||
pygame.draw.line(screen, LINE_COLOR, (col * SQUARE_SIZE, 0), (col * SQUARE_SIZE, HEIGHT), LINE_WIDTH) | ||
|
||
# Draw figures (O and X) | ||
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(): | ||
return np.all(board != 0) | ||
|
||
def check_win(player): | ||
for row in range(BOARD_ROWS): | ||
if np.all(board[row, :] == player): | ||
return True | ||
for col in range(BOARD_COLS): | ||
if np.all(board[:, col] == player): | ||
return True | ||
if board[0, 0] == player and board[1, 1] == player and board[2, 2] == player: | ||
return True | ||
if board[0, 2] == player and board[1, 1] == player and board[2, 0] == player: | ||
return True | ||
return False | ||
|
||
def display_message(message): | ||
text = font.render(message, True, TEXT_COLOR) | ||
text_rect = text.get_rect(center=(WIDTH // 2, HEIGHT // 2)) | ||
screen.blit(text, text_rect) | ||
pygame.display.update() | ||
pygame.time.wait(2000) # Wait for 2 seconds to display the message | ||
|
||
# Minimax algorithm with difficulty levels | ||
def minimax(board, depth, is_maximizing): | ||
if check_win(2): # AI win | ||
return 1 | ||
elif check_win(1): # Player win | ||
return -1 | ||
elif is_board_full(): | ||
return 0 | ||
|
||
if is_maximizing: | ||
best_score = -np.inf | ||
for row in range(BOARD_ROWS): | ||
for col in range(BOARD_COLS): | ||
if available_square(row, col): | ||
board[row][col] = 2 | ||
score = minimax(board, depth + 1, False) | ||
board[row][col] = 0 | ||
best_score = max(score, best_score) | ||
return best_score | ||
else: | ||
best_score = np.inf | ||
for row in range(BOARD_ROWS): | ||
for col in range(BOARD_COLS): | ||
if available_square(row, col): | ||
board[row][col] = 1 | ||
score = minimax(board, depth + 1, True) | ||
board[row][col] = 0 | ||
best_score = min(score, best_score) | ||
return best_score | ||
|
||
# AI Move based on difficulty level | ||
def ai_move(): | ||
best_score = -np.inf | ||
move = None | ||
for row in range(BOARD_ROWS): | ||
for col in range(BOARD_COLS): | ||
if available_square(row, col): | ||
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: | ||
mark_square(move[0], move[1], 2) | ||
|
||
# Easy AI move: choose a random available square | ||
def easy_ai_move(): | ||
available_moves = [(row, col) for row in range(BOARD_ROWS) for col in range(BOARD_COLS) if available_square(row, col)] | ||
if available_moves: | ||
move = available_moves[np.random.randint(len(available_moves))] | ||
mark_square(move[0], move[1], 2) | ||
|
||
def restart(): | ||
screen.fill(BG_COLOR) | ||
draw_lines() | ||
global board | ||
board = np.zeros((BOARD_ROWS, BOARD_COLS)) | ||
|
||
player = 1 # Player 1 is human | ||
game_over = False | ||
|
||
draw_lines() | ||
|
||
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] # X coordinate | ||
mouseY = event.pos[1] # Y coordinate | ||
|
||
clicked_row = mouseY // SQUARE_SIZE | ||
clicked_col = mouseX // SQUARE_SIZE | ||
|
||
if available_square(clicked_row, clicked_col): | ||
mark_square(clicked_row, clicked_col, player) | ||
if check_win(player): | ||
display_message("Player Wins!") | ||
game_over = True | ||
player = 2 | ||
|
||
if player == 2 and not game_over: | ||
if difficulty == "easy": | ||
easy_ai_move() | ||
else: | ||
ai_move() | ||
|
||
if check_win(2): | ||
display_message("AI Wins!") | ||
game_over = True | ||
player = 1 | ||
|
||
if event.type == pygame.KEYDOWN: | ||
if event.key == pygame.K_r: | ||
restart() | ||
game_over = False | ||
player = 1 | ||
|
||
if game_over and is_board_full(): | ||
display_message("It's a Draw!") | ||
game_over = True | ||
|
||
draw_figures() | ||
pygame.display.update() |