National Textile University
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 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'
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.
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.
5. computer_move(self):
• Determines the best move for the computer based on the current board state.
• Recursively explores possible moves and assigns scores to evaluate each move.
7. check_winner(self):
• 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():
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
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 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
return None
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
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)
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")
root.mainloop()
Explanation:
1. Puzzle15 Class:
• 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):
• 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.
• __init__(self, master, puzzle): Initializes the PuzzleGUI with a tkinter master window and
a Puzzle15 object.
11. create_board(self):
• Creates the puzzle board GUI using tkinter buttons arranged based on the initial state of
the puzzle.
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.
• Advantages:
• Optimality: BFS finds the shortest path to the solution in terms of the
number of moves.
• Disadvantages:
• 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:
• Space Complexity: DFS can have lower space complexity if the solution is
located deep in the search tree.
• Disadvantages:
• Optimality: DFS does not guarantee finding the optimal solution; it may find
a solution that is not the shortest path.
• Advantages:
• Disadvantages:
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.