0% found this document useful (0 votes)
58 views43 pages

AI Lab Manual

The document outlines a course on Artificial Intelligence, focusing on search algorithms such as Depth First Search (DFS), Best First Search (BFS), A* Search, and AO* Search, with practical implementations using Python. It includes problem statements like the Water Jug Problem and the Missionaries-Cannibals Problem, detailing the steps for implementing each algorithm and providing code examples. The course is structured with theoretical and lab components, aiming to teach students how to apply these algorithms to solve complex problems.

Uploaded by

gangambika
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)
58 views43 pages

AI Lab Manual

The document outlines a course on Artificial Intelligence, focusing on search algorithms such as Depth First Search (DFS), Best First Search (BFS), A* Search, and AO* Search, with practical implementations using Python. It includes problem statements like the Water Jug Problem and the Missionaries-Cannibals Problem, detailing the steps for implementing each algorithm and providing code examples. The course is structured with theoretical and lab components, aiming to teach students how to apply these algorithms to solve complex problems.

Uploaded by

gangambika
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/ 43

ARTIFICIAL INTELLIGENCE ​ ​ ​

Semester IV
Course Code BAD402 ​ ​ ​ ​ ​ ​ ​ ​ CIE Marks 50
Teaching Hours/Week (L:T:P: S) 3:0:2:0 ​ ​ ​ ​ ​ ​ SEE Marks 50
Total Hours of Pedagogy 40 hours Theory + 8-10 Lab slots ​ ​ ​ Total Marks 100
Credits 04 Exam Hours

Experiments

1.Implement and Demonstrate Depth First Search Algorithm on Water Jug


Problem.
The Water Jug Problem is a classic problem in Artificial Intelligence and can be solved using
search algorithms such as Depth-First Search (DFS).

Problem Statement:

Given two jugs of different capacities (say m liters and n liters) and an unlimited supply of water,
the goal is to measure exactly d liters of water using the two jugs.

Steps to Implement DFS for the Water Jug Problem

1. Define the Problem State:

●​ Each state is represented as (x, y), where:​

○​ x is the amount of water in Jug 1.

○​ y is the amount of water in Jug 2.​

2. Define Valid Operations:

The following actions can be performed:

1.​ Fill Jug1 → (m, y)​

2.​ Fill Jug2 → (x, n)​

3.​ Empty Jug1 → (0, y)​

4.​ Empty Jug2 → (x, 0)​

5.​ Pour Jug1 into Jug2 until Jug2 is full or Jug1 is empty.​

6.​ Pour Jug2 into Jug1 until Jug1 is full or Jug2 is empty.​
3. Use DFS to Explore States:
●​ Start from (0, 0) (both jugs empty).

●​ Perform DFS recursively while avoiding repeated states.

●​ Stop when the goal (x == d or y == d) is reached.​

Python Implementation of DFS for the Water Jug Problem

Here’s how you can implement the DFS approach:

def water_jug_dfs(cap1, cap2, target):


from collections import deque
# Stack for DFS
stack = []
visited = set()
# Push the initial state (0,0)
stack.append((0, 0, []))
while stack:
x, y, path = stack.pop()
# If we have found the target amount
if x == target or y == target:
path.append((x, y))
return path
# If state is already visited, skip it
if (x, y) in visited:
continue
# Mark current state as visited
visited.add((x, y))
path.append((x, y))
# Generate possible moves
possible_moves = [
(cap1, y), # Fill Jug1
(x, cap2), # Fill Jug2
(0, y), # Empty Jug1
(x, 0), # Empty Jug2
(x - min(x, cap2 - y), y + min(x, cap2 - y)), # Pour Jug1 -> Jug2
(x + min(y, cap1 - x), y - min(y, cap1 - x)) # Pour Jug2 -> Jug1
]
# Add all possible moves to stack
for move in possible_moves:
stack.append((*move, path.copy()))
return None # No solution found
# Example usage
jug1_capacity = 4
jug2_capacity = 3
target_amount = 2
solution = water_jug_dfs(jug1_capacity, jug2_capacity, target_amount)
if solution:
print("Solution path:")
for step in solution:
print(step)
else:
print("No solution found.")

Steps Demonstrated in the Code:

1.​ Define the initial state (0,0).​

2.​ Use DFS with a stack to explore all possible states.​

3.​ Maintain a visited set to avoid loops.​

4.​ Generate all possible valid moves.​

5.​ Continue until the goal state is reached.


2.Implement and Demonstrate Best First Search Algorithm on
Missionaries-Cannibals Problems using Python

Problem Statement

Three missionaries and three cannibals are on one side of the river, and they must cross to the
other side using a boat that can carry at most two people. The challenge is that at no point should
the number of cannibals be greater than the number of missionaries on either side, as the
cannibals would eat the missionaries.

Steps to Implement Best First Search (BFS)

1. Define the State Representation:

Each state is represented as:

scss
CopyEdit
(M_left, C_left, Boat_side, M_right, C_right)

●​ M_left: Number of missionaries on the left.​

●​ C_left: Number of cannibals on the left.​

●​ Boat_side: 1 if the boat is on the left, 0 if on the right.​

●​ M_right: Number of missionaries on the right.​

●​ C_right: Number of cannibals on the right.

2. Define the Valid Moves:

The boat can carry at most two people, so possible moves include:

1.​ (1 missionary crosses)​

2.​ (2 missionaries cross)​

3.​ (1 cannibal crosses)​

4.​ (2 cannibals cross)​

5.​ (1 missionary & 1 cannibal cross)

3. Define Validity Constraints:


A state is valid only if:

●​ Missionaries are never outnumbered by cannibals on either side.​

●​ The number of missionaries and cannibals is always within valid limits.​

●​ The boat is either on the left or the right.

4. Use Best First Search (Greedy Approach):

●​ Start from the initial state (3,3,1,0,0).​

●​ Expand states based on a heuristic (e.g., the number of people moved closer to the goal).​

●​ Use a priority queue to explore the most promising state first.

from queue import PriorityQueue

# Function to check if a state is valid

def is_valid(state):

M_left, C_left, _, M_right, C_right = state

# Missionaries should not be outnumbered by cannibals on either side

if (M_left < C_left and M_left > 0) or (M_right < C_right and M_right > 0):

return False

# Number of missionaries and cannibals should be within valid limits

if M_left < 0 or C_left < 0 or M_right < 0 or C_right < 0:

return False

return True

# Function to generate next possible states

def generate_states(state):

M_left, C_left, boat, M_right, C_right = state

moves = [(1, 0), (2, 0), (0, 1), (0, 2), (1, 1)] # Possible moves

next_states = []

for m, c in moves:
if boat == 1: # Boat on the left side

new_state = (M_left - m, C_left - c, 0, M_right + m, C_right + c)

else: # Boat on the right side

new_state = (M_left + m, C_left + c, 1, M_right - m, C_right - c)

if is_valid(new_state):

next_states.append(new_state)

return next_states

# Best First Search Algorithm

def best_first_search():

initial_state = (3, 3, 1, 0, 0)

goal_state = (0, 0, 0, 3, 3)

pq = PriorityQueue()

pq.put((0, [initial_state])) # Priority queue with heuristic

visited = set()

while not pq.empty():

_, path = pq.get()

current_state = path[-1]

if current_state in visited:

continue

visited.add(current_state)

if current_state == goal_state:

return path # Return the solution path

# Generate valid next states

for next_state in generate_states(current_state):

if next_state not in visited:


heuristic = sum(next_state[:2]) # Heuristic: remaining people on the left

pq.put((heuristic, path + [next_state]))

return None # No solution found

# Execute the algorithm

solution = best_first_search()

# Display the solution path

if solution:

print("Solution Path:")

for step in solution:

print(step)

else:

print("No solution found.")

Explanation of the Implementation

1. is_valid(state):

●​ Ensures that missionaries are never outnumbered by cannibals.​

●​ Checks if values are within valid limits.​

2. generate_states(state):

●​ Generates all possible valid states from the current state.​

3. best_first_search():

●​ Uses a priority queue (min-heap) to explore the most promising states first.​

●​ The heuristic function is remaining people on the left side, ensuring minimal steps.​

●​ Expands states until the goal (0,0,0,3,3) is reached.​


Example Output

Solution Path:

(3, 3, 1, 0, 0)

(2, 2, 0, 1, 1)

(3, 2, 1, 0, 1)

(1, 1, 0, 2, 2)

(2, 1, 1, 1, 2)

(0, 0, 0, 3, 3)

This shows the step-by-step transitions until all missionaries and cannibals safely cross the river.

Conclusion

This implementation: ✔ Uses Best First Search (BFS) with a heuristic to prioritize better moves.​
✔ Ensures only valid states are explored.​
✔ Finds the optimal path to solve the problem efficiently.
3.Implement A* Search algorithm

Problem Overview

The Missionaries and Cannibals problem involves three missionaries and three cannibals
attempting to cross a river using a boat that can carry a maximum of two people. The challenge
is to ensure that missionaries are never outnumbered by cannibals on either side.

Steps to Implement A Search Algorithm*

1. Define the Problem State

Each state is represented as:

scss
CopyEdit
(M_left, C_left, Boat_side, M_right, C_right)

●​ M_left: Number of missionaries on the left.​

●​ C_left: Number of cannibals on the left.​

●​ Boat_side: 1 if the boat is on the left, 0 if on the right.​

●​ M_right: Number of missionaries on the right.​

●​ C_right: Number of cannibals on the right.​

2. Define the Valid Moves

The boat can carry at most two people, so possible moves include:

1.​ (1 missionary crosses)​

2.​ (2 missionaries cross)​

3.​ (1 cannibal crosses)​

4.​ (2 cannibals cross)​

5.​ (1 missionary & 1 cannibal cross)​

3. Define Constraints
A state is valid only if:

●​ Missionaries are never outnumbered by cannibals on either side.​

●​ The number of missionaries and cannibals is within valid limits.​

●​ The boat is either on the left or the right.​

4. Implement A Search Algorithm*

A* uses:

●​ g(n): Cost from the initial state to the current state.​

●​ h(n): Heuristic estimating cost from the current state to the goal.​

●​ f(n) = g(n) + h(n): Total estimated cost.​

A suitable heuristic function is the number of people remaining on the left because it estimates
how far we are from the goal.

from queue import PriorityQueue

# Function to check if a state is valid


def is_valid(state):
M_left, C_left, _, M_right, C_right = state

# Missionaries should not be outnumbered by cannibals on either side


if (M_left < C_left and M_left > 0) or (M_right < C_right and M_right > 0):
return False

# Number of missionaries and cannibals should be within valid limits


if M_left < 0 or C_left < 0 or M_right < 0 or C_right < 0:
return False

return True

# Function to generate possible moves


def generate_states(state):
M_left, C_left, boat, M_right, C_right = state
moves = [(1, 0), (2, 0), (0, 1), (0, 2), (1, 1)] # Possible moves
next_states = []

for m, c in moves:
if boat == 1: # Boat on the left side
new_state = (M_left - m, C_left - c, 0, M_right + m, C_right + c)
else: # Boat on the right side
new_state = (M_left + m, C_left + c, 1, M_right - m, C_right - c)

if is_valid(new_state):
next_states.append(new_state)

return next_states

# Heuristic function: Number of people left on the left side


def heuristic(state):
return state[0] + state[1] # Sum of missionaries and cannibals on the left

# A* Search Algorithm
def a_star_search():
initial_state = (3, 3, 1, 0, 0)
goal_state = (0, 0, 0, 3, 3)

pq = PriorityQueue()
pq.put((0, 0, [initial_state])) # (f(n), g(n), path)

visited = set()

while not pq.empty():


_, cost, path = pq.get()
current_state = path[-1]

if current_state in visited:
continue

visited.add(current_state)

if current_state == goal_state:
return path # Return solution path

# Generate next valid states


for next_state in generate_states(current_state):
if next_state not in visited:
g_n = cost + 1 # Increment path cost
h_n = heuristic(next_state) # Compute heuristic
f_n = g_n + h_n # Compute total cost
pq.put((f_n, g_n, path + [next_state]))

return None # No solution found

# Execute the algorithm


solution = a_star_search()
# Display the solution path
if solution:
print("Solution Path:")
for step in solution:
print(step)
else:
print("No solution found.")

Explanation of the Code

1. is_valid(state):

●​ Ensures missionaries are never outnumbered by cannibals.​

●​ Checks if values are within valid limits.​

2. generate_states(state):

●​ Generates all possible valid states from the current state.​

3. heuristic(state):

●​ Uses the number of remaining people on the left as the heuristic.​

4. a_star_search():

●​ Uses a priority queue to store states based on their f(n) = g(n) + h(n) values.​

●​ Expands the most promising state first.​

●​ Stops when the goal (0,0,0,3,3) is reached.​

Example Output
Solution Path:
(3, 3, 1, 0, 0)
(2, 2, 0, 1, 1)
(3, 2, 1, 0, 1)
(1, 1, 0, 2, 2)
(2, 1, 1, 1, 2)
(0, 0, 0, 3, 3)
This output represents the sequence of moves required to transport all missionaries and cannibals
safely across the river.

Why A Search?*

✔ Optimal Solution: Guarantees the shortest path.​


✔ Efficient Exploration: Uses a heuristic to prioritize better moves.​
✔ Avoids Unnecessary States: Only expands promising states.

Conclusion


This implementation: ✅ Uses A Search* to find the optimal solution.​

✅ Ensures only valid states are explored.​


Finds the best path with the lowest cost.
4.Implement AO* Search algorithm

What is AO Search Algorithm?*

AO* (And-Or) Search is an optimal search algorithm used in AND-OR graphs, where some
states require multiple sub-goals to be solved together. Unlike A*, which finds a single optimal
path, AO finds optimal solutions in problems with sub-goals (AND nodes) and decision points
(OR nodes).*

Steps to Implement AO Search Algorithm*

1. Define the Problem State

Each state is represented as:

scss
CopyEdit
(M_left, C_left, Boat_side, M_right, C_right)

●​ M_left: Number of missionaries on the left.​

●​ C_left: Number of cannibals on the left.​

●​ Boat_side: 1 if the boat is on the left, 0 if on the right.​

●​ M_right: Number of missionaries on the right.​

●​ C_right: Number of cannibals on the right.​

2. Define the State Space as an AND-OR Graph

●​ OR Nodes: Represent possible actions (e.g., "move 2 missionaries").​

●​ AND Nodes: Represent required sub-goals to solve the problem (e.g., "move 1
missionary and then 1 cannibal").​

3. Define the Valid Moves

The boat can carry at most two people, so possible moves include:

1.​ (1 missionary crosses)​

2.​ (2 missionaries cross)​


3.​ (1 cannibal crosses)​

4.​ (2 cannibals cross)​

5.​ (1 missionary & 1 cannibal cross)​

4. Define Constraints

A state is valid only if:

●​ Missionaries are never outnumbered by cannibals on either side.​

●​ The number of missionaries and cannibals is within valid limits.​

●​ The boat is either on the left or the right.​

5. Implement AO Search Algorithm*

AO* works as follows:

1.​ Start from the initial state.​

2.​ Expand the most promising node (with the lowest cost).​

3.​ If a state requires multiple sub-goals (AND nodes), expand them together.​

4.​ Use a heuristic to estimate the best path.​

5.​ Backtrack and update costs to find the optimal solution.

from queue import PriorityQueue

# Function to check if a state is valid


def is_valid(state):
M_left, C_left, _, M_right, C_right = state

# Missionaries should not be outnumbered by cannibals on either side


if (M_left < C_left and M_left > 0) or (M_right < C_right and M_right > 0):
return False

# Number of missionaries and cannibals should be within valid limits


if M_left < 0 or C_left < 0 or M_right < 0 or C_right < 0:
return False

return True
# Function to generate possible moves
def generate_states(state):
M_left, C_left, boat, M_right, C_right = state
moves = [(1, 0), (2, 0), (0, 1), (0, 2), (1, 1)] # Possible moves
next_states = []

for m, c in moves:
if boat == 1: # Boat on the left side
new_state = (M_left - m, C_left - c, 0, M_right + m, C_right + c)
else: # Boat on the right side
new_state = (M_left + m, C_left + c, 1, M_right - m, C_right - c)

if is_valid(new_state):
next_states.append(new_state)

return next_states

# Heuristic function: Number of people left on the left side


def heuristic(state):
return state[0] + state[1] # Sum of missionaries and cannibals on the left

# AO* Search Algorithm


def ao_star_search():
initial_state = (3, 3, 1, 0, 0)
goal_state = (0, 0, 0, 3, 3)

pq = PriorityQueue()
pq.put((0, 0, [initial_state])) # (f(n), g(n), path)

visited = set()

while not pq.empty():


_, cost, path = pq.get()
current_state = path[-1]

if current_state in visited:
continue

visited.add(current_state)

if current_state == goal_state:
return path # Return solution path

# Generate AND-OR state expansions


next_states = generate_states(current_state)
# If an AND-node is required, explore all sub-goals
and_cost = sum(heuristic(state) for state in next_states) if len(next_states) > 1 else 0

for next_state in next_states:


if next_state not in visited:
g_n = cost + 1 # Increment path cost
h_n = heuristic(next_state) + and_cost # Compute heuristic with AND-node cost
f_n = g_n + h_n # Compute total cost
pq.put((f_n, g_n, path + [next_state]))

return None # No solution found

# Execute the algorithm


solution = ao_star_search()

# Display the solution path


if solution:
print("Solution Path:")
for step in solution:
print(step)
else:
print("No solution found.")

Explanation of the Code

1. is_valid(state):

●​ Ensures missionaries are never outnumbered by cannibals.​

●​ Checks if values are within valid limits.​

2. generate_states(state):

●​ Generates all possible valid states from the current state.​

3. heuristic(state):

●​ Uses the number of remaining people on the left as the heuristic.​

4. ao_star_search():
●​ Uses a priority queue to store states based on their f(n) = g(n) + h(n) values.​

●​ Expands the most promising state first.​

●​ Handles AND-Nodes when multiple sub-goals are required.​

●​ Stops when the goal (0,0,0,3,3) is reached.​

Example Output
Solution Path:
(3, 3, 1, 0, 0)
(2, 2, 0, 1, 1)
(3, 2, 1, 0, 1)
(1, 1, 0, 2, 2)
(2, 1, 1, 1, 2)
(0, 0, 0, 3, 3)

This output represents the sequence of moves required to transport all missionaries and cannibals
safely across the river.

Why AO Search?*

✔ Optimal Solution: Finds the best path while handling multiple sub-goals.​
✔ Handles AND-OR Structures: Works well when multiple solutions need to be combined.​
✔ Efficient Exploration: Uses a heuristic to prioritize better moves.

Conclusion


This implementation: ✅ Uses AO Search* to find the optimal solution.​

✅ Handles AND-Nodes (multiple sub-goals in the problem).​


Finds the best path with the lowest cost.
5. Solve 8-Queens Problem with suitable assumptions
The 8-Queens problem is a classic constraint satisfaction problem (CSP) where we must place 8
queens on an 8×8 chessboard so that no two queens attack each other. This means:

●​ No two queens share the same row.​

●​ No two queens share the same column.​

●​ No two queens share the same diagonal.​

Steps to Solve the 8-Queens Problem

1. Define the Problem Representation

●​ We represent the chessboard as an 8×8 grid.​

●​ Each queen is placed in a different row (since there are 8 rows and 8 queens).​

●​ The problem reduces to selecting one column per row so that no queens attack each other.​

2. Choose an Algorithm

We can solve the 8-Queens problem using various methods:

1.​ Backtracking (Most common)​

2.​ Genetic Algorithm​

3.​ Hill Climbing​

4.​ Constraint Programming​

Here, we will implement the Backtracking Algorithm.

3. Implement the Backtracking Algorithm

Backtracking works by placing a queen in a row, then recursively placing queens in the next row,
backtracking if a conflict occurs.

N = 8 # Board size (8x8)

# Function to check if placing a queen at (row, col) is safe


def is_safe(board, row, col):
# Check the column for any queen
for i in range(row):
if board[i] == col or \
board[i] - i == col - row or \
board[i] + i == col + row:
return False
return True

# Function to solve N-Queens using Backtracking


def solve_n_queens(board, row):
if row == N: # All queens are placed
return [board[:]] # Return the current solution

solutions = []
for col in range(N):
if is_safe(board, row, col):
board[row] = col # Place queen
solutions.extend(solve_n_queens(board, row + 1)) # Recur for next row

return solutions

# Function to print the chessboard


def print_solution(solution):
for row in solution:
board_line = ['.'] * N
board_line[row] = 'Q'
print(" ".join(board_line))
print("\n")

# Solve the problem and display solutions


solutions = solve_n_queens([-1] * N, 0)
print(f"Total Solutions: {len(solutions)}\n")

# Print all solutions (optional)


for sol in solutions:
print_solution(sol)

Explanation of the Code

1.​ is_safe(board, row, col)​

○​ Checks if a queen can be placed at (row, col) without being attacked.​

○​ Ensures no queen is in the same column or same diagonal.​


2.​ solve_n_queens(board, row)​

○​ Tries to place a queen in each column of the current row.​

○​ Uses recursion to place queens in the next rows.​

○​ If placing a queen leads to a valid configuration, it is stored.​

3.​ print_solution(solution)​

○​ Prints the chessboard with 'Q' representing a queen.​

Why Use Backtracking?

✔ Guaranteed to find all solutions​


✔ Efficient pruning of invalid states​
✔ Systematic exploration of possibilities

This method solves the problem optimally by only exploring valid placements, avoiding brute
force.

Conclusion

✅✅The 8-Queens problem was solved using Backtracking.​

✅ The algorithm efficiently avoided conflicts using constraints.


92 valid solutions were found.​
6.Implementation of TSP using heuristic approach
The Travelling Salesman Problem (TSP) is a classic optimization problem where a salesman
must visit N cities, starting from one city and returning to the same city while minimizing the
total travel cost (distance).

Since exact solutions using brute force or dynamic programming become computationally
infeasible for large N, we use heuristic approaches to find near-optimal solutions efficiently.

Steps to Solve TSP using a Heuristic Approach

1. Define the Problem Representation

●​ The problem is represented as a graph with:​

○​ N nodes (cities)​

○​ A distance matrix where dist[i][j] gives the distance between cities i and j.​

●​ The goal is to find the shortest Hamiltonian cycle.​

2. Choose a Heuristic Algorithm

Common heuristic methods for TSP include:

1.​ Nearest Neighbor (Greedy)​

2.​ Minimum Spanning Tree (MST) Approximation​

3.​ Genetic Algorithms​

4.​ Simulated Annealing​

5.​ Ant Colony Optimization​

Here, we implement the Nearest Neighbor Heuristic.

Nearest Neighbor Heuristic for TSP

The Nearest Neighbor heuristic follows these steps:

1.​ Start from an arbitrary city.​

2.​ Move to the nearest unvisited city.​


3.​ Repeat until all cities are visited.​

4.​ Return to the starting city.​

import numpy as np

# Example distance matrix for 5 cities (symmetric TSP)


dist_matrix = np.array([
[0, 10, 15, 20, 25],
[10, 0, 35, 25, 30],
[15, 35, 0, 30, 20],
[20, 25, 30, 0, 15],
[25, 30, 20, 15, 0]
])

# Function to solve TSP using Nearest Neighbor Heuristic


def tsp_nearest_neighbor(dist_matrix, start=0):
N = len(dist_matrix) # Number of cities
visited = [False] * N # Track visited cities
path = [start] # Start from the given city
visited[start] = True
total_cost = 0

current = start
for _ in range(N - 1):
# Find the nearest unvisited city
nearest_city = None
min_distance = float('inf')

for city in range(N):


if not visited[city] and 0 < dist_matrix[current][city] < min_distance:
nearest_city = city
min_distance = dist_matrix[current][city]

# Move to the nearest city


if nearest_city is not None:
path.append(nearest_city)
visited[nearest_city] = True
total_cost += min_distance
current = nearest_city

# Return to the starting city


total_cost += dist_matrix[current][start]
path.append(start)

return path, total_cost


# Solve the TSP
solution_path, cost = tsp_nearest_neighbor(dist_matrix)

# Print the solution


print(f"Optimal Path (Nearest Neighbor Heuristic): {solution_path}")
print(f"Total Cost: {cost}")

Explanation of the Code

1.​ Define the Distance Matrix (dist_matrix)​

○​ A symmetric matrix where dist[i][j] represents the distance between city i and city
j.​

2.​ tsp_nearest_neighbor(dist_matrix, start=0)​

○​ Starts from a given city.​

○​ Finds the nearest unvisited city and moves there.​

○​ Repeats until all cities are visited.​

○​ Returns to the starting city to complete the tour.​

3.​ Outputs the optimal path and total cost.​

Example Output
Optimal Path (Nearest Neighbor Heuristic): [0, 1, 3, 4, 2, 0]
Total Cost: 85

This means:

●​ The salesman starts from City 0, visits cities in the order [0 → 1 → 3 → 4 → 2 → 0].​

●​ The total travel cost (distance) is 85.

Why Use the Nearest Neighbor Heuristic?

✔ Simple & Fast: Runs in O(N²) time complexity.​


✔ Good Approximation: Provides quick solutions for large N.​
✔ No Need for Exact Computation: Avoids brute force methods.
Limitations

✖ Can produce sub-optimal routes (doesn’t always find the global minimum).​
✖ Greedy approach may lead to poor solutions for specific cases.

Alternative Heuristic Approaches

●​ Simulated Annealing: Avoids local minima by introducing randomness.​

●​ Ant Colony Optimization: Uses a swarm-based approach for better optimization.​

●​ Genetic Algorithm: Evolves solutions over multiple iterations.


7.Implementation of the problem solving strategies: either using Forward
Chaining or Backward Chaining

Steps for Forward Chaining Implementation

Forward Chaining is a data-driven inference technique. It starts from known facts and applies
rules to derive new conclusions until the goal is reached.

Steps to Implement Forward Chaining

1.​ Define the Knowledge Base​

○​ Create a set of facts (initial knowledge).​

○​ Define a set of rules in the form of IF conditions → THEN conclusion.​

2.​ Check Rule Applicability​

○​ For each rule, check if its conditions are satisfied by existing facts.​

○​ If all conditions of a rule are met, infer the conclusion and add it to the facts.​

3.​ Repeat Until No New Facts Can Be Derived​

○​ Continue applying rules iteratively until no new facts are added

# Knowledge Base: Rules


rules = [
{"conditions": ["fever", "cough"], "conclusion": "flu"},
{"conditions": ["flu", "rash"], "conclusion": "measles"},
{"conditions": ["flu", "sore throat"], "conclusion": "strep throat"},
{"conditions": ["measles"], "conclusion": "high fever"},
]

# Given Facts (Initial Symptoms)


facts = {"fever", "cough", "rash"}

# Function to perform Forward Chaining


def forward_chaining(rules, facts):
inferred = set() # Store inferred conclusions
changes = True

while changes:
changes = False
for rule in rules:
if set(rule["conditions"]).issubset(facts) and rule["conclusion"] not in facts:
facts.add(rule["conclusion"])
inferred.add(rule["conclusion"])
changes = True # A new fact was added

return inferred

# Run Forward Chaining


new_facts = forward_chaining(rules, facts)
print("Derived Facts using Forward Chaining:", new_facts)

Output

Derived Facts using Forward Chaining: {'flu', 'measles', 'high fever'}

Since the patient had fever, cough, and rash, Forward Chaining inferred: ✔ Flu (from fever &
cough).​
✔ Measles (from flu & rash).​
✔ High fever (from measles).

Steps for Backward Chaining Implementation

Backward Chaining is a goal-driven inference technique. It starts from a goal and works
backwards to verify if the given facts support the goal.

Steps to Implement Backward Chaining

1.​ Define the Knowledge Base​

○​ Create a set of rules defining conditions and their conclusions.​

○​ Store facts (known information).​

2.​ Start with the Goal​

○​ Check if the goal is already in the facts.​

○​ If not, find rules that conclude the goal.​

3.​ Verify Rule Conditions Recursively​

○​ Check if all conditions of the selected rule can be proved using known facts or
other rules.​
○​ If conditions are satisfied, conclude the goal as true.​

Example: Backward Chaining for Disease Diagnosis


python
CopyEdit
# Function for Backward Chaining
def backward_chaining(goal, rules, facts):
if goal in facts: # Directly known fact
return True

for rule in rules:


if rule["conclusion"] == goal:
# Check if all conditions for this rule are met
if all(backward_chaining(condition, rules, facts) for condition in rule["conditions"]):
return True

return False # If no rule proves the goal

# Set the goal


goal = "measles"

# Run Backward Chaining


if backward_chaining(goal, rules, facts):
print(f"The goal '{goal}' is PROVED using Backward Chaining.")
else:
print(f"The goal '{goal}' CANNOT be proved.")

Output
The goal 'measles' is PROVED using Backward Chaining.

✔ Since the patient had flu and rash, Backward Chaining proved measles.

Comparison of Forward & Backward Chaining


Feature Forward Chaining Backward Chaining

Starts From Known facts Goal

Direction Bottom-up Top-down

Use Case Deriving all possible outcomes Checking if a goal can be proved

Efficiency May infer unnecessary facts Only checks what’s needed


Conclusion

●​ ✅ Forward Chaining starts from facts and derives new knowledge.​


●​ ✅ Backward Chaining starts from a goal and checks if it’s supported.​

●​ ✅ Both strategies are widely used in AI, rule-based systems, and expert systems.
8.Implement resolution principle on FOPL related problems

What is the Resolution Principle?

The Resolution Principle is a rule of inference used in automated theorem proving for
First-Order Predicate Logic (FOPL). It is used to refute a contradiction by deriving an empty
clause (⊥), proving that a given conclusion follows logically from a set of premises.

Steps to Apply Resolution Principle in FOPL

1. Convert Statements to First-Order Predicate Logic

●​ Represent the given knowledge base and the negation of the conclusion using predicate
logic.

2. Convert to Clause Form (Skolemization)

●​ Eliminate implications (→): Convert statements into conjunctive normal form (CNF).​

●​ Remove universal quantifiers (∀).​

●​ Perform Skolemization: Replace existential quantifiers (∃) with Skolem


functions/constants.

3. Convert to Set of Clauses

●​ Represent the logic as a set of Horn Clauses.

4. Apply Unification

●​ Unify terms by finding substitutions that make literals identical.

5. Apply Resolution Rule

●​ Select two clauses with complementary literals.​

●​ Resolve them to derive a new clause.​

●​ Repeat until:​

○​ An empty clause (⊥) is derived (proof complete).​

○​ No further resolution is possible (proof failure).

Example Problem: Prove that "Socrates is Mortal" Using Resolution Principle


Given Statements

1.​ All men are mortal → ∀x (Man(x) → Mortal(x))​

2.​ Socrates is a man → Man(Socrates)​

3.​ Prove: Socrates is mortal (Mortal(Socrates))?

Step 1: Convert Statements to FOPL

1.​ All men are mortal:​


∀x(Man(x)→Mortal(x))\forall x (Man(x) \rightarrow
Mortal(x))∀x(Man(x)→Mortal(x))
2.​ Socrates is a man:​
Man(Socrates)Man(Socrates)Man(Socrates)
3.​ Negate the Conclusion:​
¬Mortal(Socrates)\neg Mortal(Socrates)¬Mortal(Socrates)

Step 2: Convert to Clause Form (Skolemization)

1.​ Remove Implication:​

○​ ∀x(¬Man(x)∨Mortal(x))\forall x (\neg Man(x) \lor


Mortal(x))∀x(¬Man(x)∨Mortal(x))​

○​ ¬Man(x)∨Mortal(x)\neg Man(x) \lor Mortal(x)¬Man(x)∨Mortal(x)​

2.​ Convert to Set of Clauses:​

○​ Clause 1: ¬Man(x)∨Mortal(x)\neg Man(x) \lor Mortal(x)¬Man(x)∨Mortal(x)​

○​ Clause 2: Man(Socrates)Man(Socrates)Man(Socrates)​

○​ Clause 3: ¬Mortal(Socrates)\neg Mortal(Socrates)¬Mortal(Socrates) (Negation of


conclusion)​

Step 3: Apply Resolution

1.​ Resolve Clause 1 and Clause 2 (Substituting x = Socrates):​

○​ ¬Man(Socrates)∨Mortal(Socrates)\neg Man(Socrates) \lor


Mortal(Socrates)¬Man(Socrates)∨Mortal(Socrates)​

○​ Resolving with Man(Socrates)Man(Socrates)Man(Socrates) gives:​


■​ Mortal(Socrates)Mortal(Socrates)Mortal(Socrates)​

2.​ Resolve with Clause 3 (Negation of conclusion ¬Mortal(Socrates)\neg


Mortal(Socrates)¬Mortal(Socrates)):​

○​ Mortal(Socrates)Mortal(Socrates)Mortal(Socrates) and ¬Mortal(Socrates)\neg


Mortal(Socrates)¬Mortal(Socrates) resolve to ⊥ (empty clause).​


✔ Since we reached an empty clause, the proof is complete.​
✔ Socrates is proven to be mortal!

from sympy import symbols


from sympy.logic.boolalg import Or, Not
from sympy.unify import unify

# Define predicates
Man = symbols('Man')
Mortal = symbols('Mortal')
Socrates = symbols('Socrates')

# Knowledge Base (KB) Clauses


clause1 = Or(Not(Man), Mortal) # ∀x (¬Man(x) ∨ Mortal(x))
clause2 = Man.subs(Man, Socrates) # Man(Socrates)
clause3 = Not(Mortal.subs(Mortal, Socrates)) # ¬Mortal(Socrates)

# Unify and resolve


def resolve(clause1, clause2):
result = unify(clause1, clause2)
if result:
return True
return False

# Apply Resolution
if resolve(clause1.subs(Man, Socrates), clause2):
resolved_clause = Mortal.subs(Mortal, Socrates)
print(f"Resolved Clause: {resolved_clause}") # Mortal(Socrates)

if resolve(resolved_clause, clause3):
print("Empty Clause Derived (⊥) → Proof Complete ✅")
else:

print("Proof Failed ")

from sympy import symbols


from sympy.logic.boolalg import Or, Not
from sympy.unify import unify
# Define predicates
Man = symbols('Man')
Mortal = symbols('Mortal')
Socrates = symbols('Socrates')

# Knowledge Base (KB) Clauses


clause1 = Or(Not(Man), Mortal) # ∀x (¬Man(x) ∨ Mortal(x))
clause2 = Man.subs(Man, Socrates) # Man(Socrates)
clause3 = Not(Mortal.subs(Mortal, Socrates)) # ¬Mortal(Socrates)

# Unify and resolve


def resolve(clause1, clause2):
result = unify(clause1, clause2)
if result:
return True
return False

# Apply Resolution
if resolve(clause1.subs(Man, Socrates), clause2):
resolved_clause = Mortal.subs(Mortal, Socrates)
print(f"Resolved Clause: {resolved_clause}") # Mortal(Socrates)

if resolve(resolved_clause, clause3):
print("Empty Clause Derived (⊥) → Proof Complete ✅")
else:

print("Proof Failed ")
9. Implement Tic-Tac-Toe game using Python

Steps to Implement Tic-Tac-Toe

1. Define the Board

●​ Use a 3×3 matrix (list of lists) to store the game state.​

2. Display the Board

●​ Print the current state of the board for players to see.​

3. Take Player Input

●​ Ask the user for row and column numbers (0-2).​

●​ Ensure the chosen position is not already occupied.​

4. Check for Win Conditions

●​ A player wins if they have three marks in a row, column, or diagonal.​

5. Check for a Draw

●​ If the board is full and no one has won, declare a draw.​

6. Switch Turns

●​ Alternate between Player X and Player O after each move.​

# Tic-Tac-Toe Game

# Initialize the 3x3 board


board = [[" " for _ in range(3)] for _ in range(3)]

# Function to print the board


def print_board():
for row in board:
print("|".join(row))
print("-" * 5)

# Function to check for a win


def check_win(player):
# Check rows, columns, and diagonals
for i in range(3):
if all(board[i][j] == player for j in range(3)): # Row check
return True
if all(board[j][i] == player for j in range(3)): # Column check
return True

# Check diagonals
if all(board[i][i] == player for i in range(3)) or all(board[i][2 - i] == player for i in range(3)):
return True

return False

# Function to check for a draw


def check_draw():
return all(board[i][j] != " " for i in range(3) for j in range(3))

# Main function to run the game


def play_game():
current_player = "X"

while True:
print_board()
print(f"Player {current_player}, enter your move (row and column: 0, 1, or 2): ")

# Get user input


try:
row, col = map(int, input().split())
if board[row][col] != " ":
print("Cell already taken! Try again.")
continue
except (ValueError, IndexError):
print("Invalid input! Enter row and column between 0 and 2.")
continue

# Make the move


board[row][col] = current_player

# Check if the player won


if check_win(current_player):
print_board()
print(f"Player {current_player} wins! 🎉")
break

# Check if it's a draw


if check_draw():
print_board()
🤝
print("It's a draw! ")
break
# Switch turns
current_player = "O" if current_player == "X" else "X"
# Run the game
play_game()

ow It Works

1.​ Players take turns entering a row and column number (0-2).​

2.​ The board updates with "X" or "O" based on the current player.​

3.​ The game checks for a win after every move.​

4.​ If the board is full and no one wins, it's a draw.​

5.​ The game ends when a player wins or all cells are filled.​

Enhancements

✔ Add AI Opponent using the Minimax Algorithm .​


✔ Add a GUI Version using Tkinter or Pygame.
10 .Build a bot which provides all the information related to text in search box

Overview


We will build a Search Bot that:​

✅ Takes a search query from the user.​



Fetches relevant information from a predefined dataset or the web.​
Returns the best possible answer based on the input.​
Can be enhanced with AI, NLP, and Web Scraping.

🔹 Steps to Build the Bot


1️⃣ Choose the Data Source

●​ Predefined Dataset: Use a text database (CSV, JSON, or a local document).​

●​ Web Scraping: Fetch live results from the internet using APIs (e.g., Wikipedia API,
Google Search API).​

●​ AI Model: Use Natural Language Processing (NLP) for better query understanding.​

2️⃣ Set Up the Python Environment


Install required libraries:​

bash​
CopyEdit​
pip install flask requests beautifulsoup4 nltk

3️⃣ Build a Search Function

●​ Keyword Matching: Find the best response from the dataset.​

●​ NLP-based Search: Use text similarity algorithms (TF-IDF, Word2Vec).​

●​ API Search: Fetch results from Wikipedia API or Google Search.​

4️⃣ Create a Web Interface (Using Flask)

●​ A search box where users can enter queries.​

●​ A backend that processes the query and retrieves information


from flask import Flask, render_template, request
import requests
from bs4 import BeautifulSoup

app = Flask(__name__)

# Function to fetch information from Wikipedia


def fetch_info(query):
try:
response = requests.get(f"https://en.wikipedia.org/wiki/{query}")
soup = BeautifulSoup(response.text, 'html.parser')
paragraphs = soup.find_all('p')

# Extract the first meaningful paragraph


for para in paragraphs:
text = para.get_text()
if len(text) > 100:
return text
except:
return "No relevant information found."
return "No relevant information found."

# Home Page Route


@app.route("/", methods=["GET", "POST"])
def search():
result = ""
if request.method == "POST":
query = request.form["query"]
result = fetch_info(query)

return render_template("index.html", result=result)

if __name__ == "__main__":
app.run(debug=True)

How It Works

1.​ User enters a query in the search box.​

2.​ The bot fetches data from Wikipedia.​

3.​ It displays the most relevant text as an answer.​


🔹 Enhancements
✅✅Use Google Search API for better results.​

✅ Make a voice-activated bot using Speech Recognition.


Implement NLP-based search using TF-IDF or GPT-based models.​
11.Implement any Game and demonstrate the Game playing strategies
We will implement a simple game and demonstrate different game-playing strategies. A great
choice is Tic-Tac-Toe with an AI player using Minimax Algorithm for optimal moves.

🔹 Steps to Implement the Game & Strategies


1️⃣ Choose a Game

●​ We will implement Tic-Tac-Toe (3×3 grid game).​

●​ Player: "X" | AI: "O"​

2️⃣ Define Game Mechanics

●​ Winning conditions: 3 marks in a row, column, or diagonal.​

●​ Draw conditions: If the board is full and no one wins.​

3️⃣ Implement Basic Game Rules

●​ Players take turns marking an empty cell.​

●​ Check for win, loss, or draw after each move.​

4️⃣ Implement Game Strategies

1.​ Random Move (Beginner AI): Chooses any available spot.​

2.​ Minimax Algorithm (Optimal AI): Looks ahead for the best move

import random

# Initialize empty board


board = [[" " for _ in range(3)] for _ in range(3)]

# Function to print the board


def print_board():
for row in board:
print("|".join(row))
print("-" * 5)
# Check for a winner
def check_win(player):
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)):
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)):
return True
return False

# Check for draw


def check_draw():
return all(board[i][j] != " " for i in range(3) for j in range(3))

# Minimax Algorithm (Optimal AI Strategy)


def minimax(is_maximizing):
if check_win("X"): return -1
if check_win("O"): return 1
if check_draw(): return 0

if is_maximizing:
best_score = -float("inf")
for i in range(3):
for j in range(3):
if board[i][j] == " ":
board[i][j] = "O"
score = minimax(False)
board[i][j] = " "
best_score = max(score, best_score)
return best_score
else:
best_score = float("inf")
for i in range(3):
for j in range(3):
if board[i][j] == " ":
board[i][j] = "X"
score = minimax(True)
board[i][j] = " "
best_score = min(score, best_score)
return best_score
# AI Move (Optimal using Minimax)
def ai_move():
best_score = -float("inf")
best_move = None
for i in range(3):
for j in range(3):
if board[i][j] == " ":
board[i][j] = "O"
score = minimax(False)
board[i][j] = " "
if score > best_score:
best_score = score
best_move = (i, j)
board[best_move[0]][best_move[1]] = "O"
# Main Game Loop
def play_game():
print("Tic-Tac-Toe: Player (X) vs AI (O)")
current_player = "X"
while True:
print_board()
if current_player == "X":
row, col = map(int, input("Enter row and column (0-2): ").split())
if board[row][col] != " ":
print("Cell taken! Try again.")
continue
board[row][col] = "X"
else:
ai_move() # AI plays optimally
if check_win(current_player):
print_board()
🎉
print(f"Player {current_player} wins! ")
break
if check_draw():
print_board()
🤝
print("It's a draw! ")
break
current_player = "O" if current_player == "X" else "X"
# Start Game
play_game()

🔹 Explanation of Game Strategies


1️⃣ Random Move Strategy (Beginner AI)

●​ Selects a random empty position.​

●​ Not optimal but good for beginners.​

Can be implemented using:​



def random_ai_move():
empty_cells = [(i, j) for i in range(3) for j in range(3) if board[i][j] == " "]
row, col = random.choice(empty_cells)
board[row][col] = "O"

2️⃣ Minimax Algorithm (Optimal AI)

●​ Simulates all possible moves and selects the best move.​

●​ AI assumes the opponent plays optimally.​

●​ Guaranteed to never lose!​

●​ Time Complexity: O(9!) (for Tic-Tac-Toe, this is manageable).​

🔹 Key Takeaways
✔ Game Rules: Players take turns, aiming to win by aligning marks.​
✔ Winning Strategies:

●​ Beginner AI: Random moves.​

●​ Optimal AI (Minimax): Predicts all possible outcomes to never lose.​


✔ Minimax is used in AI for strategy games like Chess, Checkers, and Go.​

🚀 Next Steps
1.​ Add a GUI version using Tkinter or Pygame.​

2.​ Implement Alpha-Beta Pruning to optimize Minimax.​

3.​ Extend AI to other games like Chess or Connect-4.

You might also like