0% found this document useful (0 votes)
18 views12 pages

National Textile University

This code implements a 15-puzzle game using BFS and A* search algorithms. It allows shuffling the puzzle tiles and solving it either through BFS or A* with different heuristics. The GUI displays the puzzle board and updates based on solved moves.

Uploaded by

maryammunawar110
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views12 pages

National Textile University

This code implements a 15-puzzle game using BFS and A* search algorithms. It allows shuffling the puzzle tiles and solving it either through BFS or A* with different heuristics. The GUI displays the puzzle board and updates based on solved moves.

Uploaded by

maryammunawar110
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

National Textile University

Lab Report
INTRODUCTION TO ARTIFICAL INTELLIGENCE
Name: Maryam Munawar, Fatima Ahmad
Reg#:22-NTU-CS-1344,22-NTU-CS-1341
Smestor:4TH
Degree: BSAI
Submitted To: Prof Waqar Ahmad
Task 1
Tick Tack Toe
Code:
import tkinter as tk
from tkinter import messagebox
import random

class TicTacToe:
def __init__(self, master):
self.master = master
self.master.title("Tic Tac Toe")
self.board = [' ' for _ in range(9)]
self.current_player = 'X'
self.buttons = []
self.create_board()

def create_board(self):
for i in range(3):
for j in range(3):
button = tk.Button(self.master, text='', font=('Arial', 20),
width=6, height=2,
command=lambda row=i, col=j:
self.on_click(row, col))
button.grid(row=i, column=j, padx=5, pady=5)
self.buttons.append(button)

def on_click(self, row, col):


index = 3 * row + col
if self.board[index] == ' ':
self.board[index] = self.current_player
self.buttons[index].config(text=self.current_player)
if self.check_winner() or ' ' not in self.board:
self.game_over()
else:
self.current_player = 'O' if self.current_player == 'X' else 'X'
if self.current_player == 'O':
self.computer_move()

def computer_move(self):
available_moves = [i for i, val in enumerate(self.board) if val == ' ']
index = self.minimax(self.board, True)['index']
self.board[index] = self.current_player
self.buttons[index].config(text=self.current_player)
if self.check_winner() or ' ' not in self.board:
self.game_over()
else:
self.current_player = 'O' if self.current_player == 'X' else 'X'

def minimax(self, board, is_maximizing):


if self.check_winner() == 'X':
return {'score': -1}
elif self.check_winner() == 'O':
return {'score': 1}
elif ' ' not in board:
return {'score': 0}

if is_maximizing:
best_score = {'score': float('-inf')}
for i in range(len(board)):
if board[i] == ' ':
board[i] = 'O'
score = self.minimax(board, False)['score']
board[i] = ' '
if score > best_score['score']:
best_score['score'] = score
best_score['index'] = i
return best_score
else:
best_score = {'score': float('inf')}
for i in range(len(board)):
if board[i] == ' ':
board[i] = 'X'
score = self.minimax(board, True)['score']
board[i] = ' '
if score < best_score['score']:
best_score['score'] = score
best_score['index'] = i
return best_score

def check_winner(self):
winning_combinations = [(0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1,
4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)]
for combo in winning_combinations:
if self.board[combo[0]] == self.board[combo[1]] ==
self.board[combo[2]] != ' ':
return self.board[combo[0]]
return None

def game_over(self):
winner = self.check_winner()
if winner:
messagebox.showinfo("Game Over", f"Player {winner} wins!")
else:
messagebox.showinfo("Game Over", "It's a tie!")
self.master.destroy()

def main():
root = tk.Tk()
game = TicTacToe(root)
root.mainloop()

if __name__ == "__main__":
main()

Explanation:
1. Importing Libraries:

• import tkinter as tk: This imports the tkinter library and aliases it as tk for easier use.

• from tkinter import messagebox: This imports the messagebox module from tkinter
for displaying messages.

2. TicTacToe Class:

• __init__(self, master): Initializes the TicTacToe game with a tkinter master window.

• self.board: Represents the Tic Tac Toe board as a list of 9 empty spaces initially.

• self.current_player: Tracks the current player ('X' or 'O').

• self.buttons: Stores references to the tkinter buttons representing the board.

3. create_board(self):

• Creates the Tic Tac Toe board using tkinter buttons in a 3x3 grid layout.
• Each button is assigned a command that calls self.on_click(row, col) when clicked.

4. on_click(self, row, col):

• Handles the player's move when a button is clicked.

• Updates the board, checks for a winner, and switches players.

5. computer_move(self):

• Simulates the computer's move using the minimax algorithm.

• Determines the best move for the computer based on the current board state.

6. minimax(self, board, is_maximizing):

• Implements the minimax algorithm for the computer's move decision.

• Recursively explores possible moves and assigns scores to evaluate each move.

7. check_winner(self):

• Checks if there is a winner on the current board.

• Returns the winning player ('X' or 'O') or None if there is no winner yet.

8. game_over(self):

• Displays a message box with the game result (winner or tie) and closes the tkinter
window.

9. main():

• Creates a tkinter root window and initializes the TicTacToe game.

• Starts the main event loop using root.mainloop().

This code implements a simple Tic Tac Toe game using tkinter for the GUI and
the minimax algorithm for the computer's move. It allows a player to play
against the computer and handles game logic such as checking for a winner
and ending the game.
Task2
15-Puzzle
Code:
import random
import heapq
import tkinter as tk
from collections import deque

class Puzzle15:
def __init__(self, initial_state):
self.initial_state = initial_state
self.goal_state = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0]
self.moves = [(0, -1), (0, 1), (-1, 0), (1, 0)] # Up, Down, Left, Right

def get_empty_tile_position(self, state):


return state.index(0)

def is_valid_move(self, x, y):


return 0 <= x < 4 and 0 <= y < 4

def apply_move(self, state, move):


empty_tile_position = self.get_empty_tile_position(state)
empty_tile_row = empty_tile_position // 4
empty_tile_col = empty_tile_position % 4
new_row = empty_tile_row + move[0]
new_col = empty_tile_col + move[1]

if self.is_valid_move(new_row, new_col):
new_position = new_row * 4 + new_col
new_state = state[:]
new_state[empty_tile_position], new_state[new_position] =
new_state[new_position], new_state[empty_tile_position]
return new_state
else:
return None

def is_goal_state(self, state):


return state == self.goal_state

def shuffle_puzzle(self, steps=100):


state = self.initial_state[:]
for _ in range(steps):
empty_tile_position = self.get_empty_tile_position(state)
possible_moves = [move for move in self.moves if
self.is_valid_move(empty_tile_position // 4 + move[0], empty_tile_position % 4 +
move[1])]
move = random.choice(possible_moves)
state = self.apply_move(state, move)
return state

def bfs(self):
visited = set()
queue = deque([(self.initial_state, [])])

while queue:
state, path = queue.popleft()
if self.is_goal_state(state):
return path

visited.add(tuple(state))
empty_tile_position = self.get_empty_tile_position(state)
empty_tile_row = empty_tile_position // 4
empty_tile_col = empty_tile_position % 4

for move in self.moves:


new_state = self.apply_move(state, move)
if new_state is not None and tuple(new_state) not in visited:
new_path = path + [move]
queue.append((new_state, new_path))

return None

def h1(self, state):


return sum([1 if state[i] != self.goal_state[i] else 0 for i in
range(len(state))])

def h2(self, state):


return sum([abs(state.index(i) % 4 - self.goal_state.index(i) % 4) +
abs(state.index(i) // 4 - self.goal_state.index(i) // 4) for i in range(1, 16)])

def astar(self, heuristic):


visited = set()
queue = []
heapq.heappush(queue, (heuristic(self.initial_state), self.initial_state,
[]))
while queue:
_, state, path = heapq.heappop(queue)
if self.is_goal_state(state):
return path

visited.add(tuple(state))
empty_tile_position = self.get_empty_tile_position(state)
empty_tile_row = empty_tile_position // 4
empty_tile_col = empty_tile_position % 4

for move in self.moves:


new_state = self.apply_move(state, move)
if new_state is not None and tuple(new_state) not in visited:
new_path = path + [move]
heapq.heappush(queue, (heuristic(new_state) + len(new_path),
new_state, new_path))

return None

class PuzzleGUI:
def __init__(self, master, puzzle):
self.master = master
self.puzzle = puzzle
self.frame = tk.Frame(self.master)
self.buttons = []
self.create_board()
self.frame.pack()

def create_board(self):
for i in range(4):
for j in range(4):
index = i * 4 + j
tile_value = self.puzzle.initial_state[index]
if tile_value != 0:
button = tk.Button(self.frame, text=str(tile_value), width=5,
height=2, command=lambda idx=index: self.move_tile(idx))
else:
button = tk.Button(self.frame, text=" ", width=5, height=2)
button.grid(row=i, column=j)
self.buttons.append(button)

def move_tile(self, index):


empty_tile_index =
self.puzzle.get_empty_tile_position(self.puzzle.initial_state)
empty_tile_row = empty_tile_index // 4
empty_tile_col = empty_tile_index % 4
tile_row = index // 4
tile_col = index % 4
if (abs(empty_tile_row - tile_row) == 1 and empty_tile_col == tile_col)
or \
(abs(empty_tile_col - tile_col) == 1 and empty_tile_row ==
tile_row):
move = (empty_tile_row - tile_row, empty_tile_col - tile_col)
new_state = self.puzzle.apply_move(self.puzzle.initial_state, move)
self.update_board(new_state)

def update_board(self, state):


for button, value in zip(self.buttons, state):
if value != 0:
button.config(text=str(value))
else:
button.config(text=" ")
self.puzzle.initial_state = state

if __name__ == "__main__":
initial_state = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0]
puzzle = Puzzle15(initial_state)
shuffled_state = puzzle.shuffle_puzzle(steps=1000)

root = tk.Tk()
root.title("15 Puzzle Game")

gui = PuzzleGUI(root, puzzle)

root.mainloop()

Explanation:
1. Puzzle15 Class:

• __init__(self, initial_state): Initializes the Puzzle15 game with an initial state.

• self.initial_state: Represents the initial state of the puzzle, which is a list of numbers
from 1 to 15 and 0 representing the empty space.

• self.goal_state: Represents the goal state where the numbers are arranged in ascending
order and the empty space is at the end.
• self.moves: Defines the possible moves in the puzzle, including Up, Down, Left, and
Right.

2. get_empty_tile_position(self, state):

• Returns the index of the empty tile (0) in the given state.

3. is_valid_move(self, x, y):

• Checks if a move is valid based on the current position.

4. apply_move(self, state, move):

• Applies a move to the given state, swapping the empty tile with the adjacent tile in the
specified direction.

5. shuffle_puzzle(self, steps=100):

• Shuffles the puzzle by applying random valid moves for the specified number of steps.

6. bfs(self):

• Performs a Breadth-First Search (BFS) algorithm to find the solution path from the initial
state to the goal state.

7. h1(self, state):

• Heuristic function h1 calculates the number of misplaced tiles between the current
state and the goal state.

8. h2(self, state):

• Heuristic function h2 calculates the Manhattan distance between tiles in the current
state and the goal state.

9. astar(self, heuristic):

• Performs the A* search algorithm using the specified heuristic function (h1 or h2) to find
the optimal solution path.

10. PuzzleGUI Class:

• __init__(self, master, puzzle): Initializes the PuzzleGUI with a tkinter master window and
a Puzzle15 object.

• self.master: Represents the tkinter master window.

• self.puzzle: Represents the Puzzle15 object.

• self.frame: Creates a tkinter frame to hold the puzzle GUI components.

• self.buttons: Stores references to tkinter buttons representing the puzzle tiles.

11. create_board(self):
• Creates the puzzle board GUI using tkinter buttons arranged based on the initial state of
the puzzle.

• Each button is assigned a command to call self.move_tile(index) when clicked.

12. move_tile(self, index):

• Handles the tile movement when a button representing a tile is clicked.

• Updates the puzzle state and GUI accordingly.

13. update_board(self, state):

• Updates the puzzle board GUI based on the given state.

14. main():

• Initializes the initial state of the puzzle, creates a tkinter root window, and sets up the
PuzzleGUI.

• Starts the main event loop using root.mainloop() to handle user interactions and puzzle
updates.

This code implements a 15 Puzzle game using tkinter for the graphical
interface, allowing players to interact with the puzzle by moving tiles to solve
it. The Puzzle15 class provides the logic for puzzle manipulation and solving
using search algorithms, while the PuzzleGUI class handles the GUI
components and user interactions.

Comparison Of Algorithms Using In 15 Puzzle:


1. Breadth-First Search (BFS):

• Advantages:

• Completeness: BFS is guaranteed to find a solution if one exists, as long as


the puzzle is finite.

• Optimality: BFS finds the shortest path to the solution in terms of the
number of moves.

• Disadvantages:

• Memory Intensive: BFS can be memory-intensive, especially for large state


spaces, as it stores all explored states in memory.

• Time Complexity: BFS may take longer to find a solution compared to other
algorithms, especially if the branching factor is high.
2. Depth-First Search (DFS):

• Advantages:

• Memory Efficiency: DFS requires less memory compared to BFS as it


explores one branch of the search tree at a time.

• Space Complexity: DFS can have lower space complexity if the solution is
located deep in the search tree.

• Disadvantages:

• Completeness: DFS is not guaranteed to find a solution if the search space


is infinite or if it enters a cycle.

• Optimality: DFS does not guarantee finding the optimal solution; it may find
a solution that is not the shortest path.

3. A Search with Heuristics (A)**:

• Advantages:

• Optimality: A* with admissible heuristics guarantees finding the optimal


solution, provided the heuristic is admissible (never overestimates).

• Efficiency: A* can be more efficient than uninformed search algorithms like


BFS and DFS, especially in large state spaces.

• Disadvantages:

• Heuristic Quality: The effectiveness of A* depends on the quality of the


heuristic function used. Inaccurate heuristics can lead to suboptimal
solutions.

• Heuristic Selection: Choosing an appropriate heuristic can be challenging


and may require domain knowledge.

In summary, BFS is suitable for finding optimal solutions in small state spaces
but may be impractical for large puzzles due to memory requirements. DFS is
memory-efficient but may not always find optimal solutions. A* with
admissible heuristics combines efficiency and optimality, making it a
preferred choice for large state spaces with accurate heuristics.

You might also like