T.Y.BSc.
IT Artificial Intelligence and Application I22044
PRACTICAL NO. 1
a) Implement depth first search algorithm.
Input:
graph1 = {
'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F'],
'D': [],
'E': ['F'],
'F':[]
}
visited=set()
def dfs(graph, node, visited):
if node not in visited:
visited.append(node)
for n in graph[node]:
dfs(graph,n, visited)
return visited
visited = dfs(graph1,'A', [ ])
print(visited)
Output:
b) Implement breadth first search algorithm.
Input:
graph = {
'5' : ['3','7'],
'3' : ['2', '4'],
'7' : ['8'],
'2' : [],
'4' : ['8'],
'8' : []
}
visited = [] # List for visited nodes.
queue = [] #Initialize a queue
def bfs(visited, graph, node):
visited.append(node)
queue.append(node)
while queue:
m = queue.pop(0)
for neighbour in graph[m]:
T.Y.BSc.IT Artificial Intelligence and Application I22044
if neighbour not in visited:
visited.append(neighbour)
queue.append(neighbour)
bfs(visited, graph, '5')
print(visited)
Output:
T.Y.BSc.IT Artificial Intelligence and Application I22044
PRACTICAL NO.2
a) Solve tower of hanoi problem.
Input:
def TowerOfHanoi(n , src, dest, aux):
if n==1:
print ("Move disk 1 from source",src,"to destination",dest)
return
TowerOfHanoi(n-1, src, aux, dest)
print ("Move disk",n,"from source",src,"to destination",dest)
TowerOfHanoi(n-1, aux, dest, src)
disks = int(input('Enter the number of disks: '))
TowerOfHanoi(disks, 'A', 'B', 'C') # Calling the function
Output:
b) Simulate 4-Queen / N-Queen problem.
Input:
#Number of queens
print ("Enter the number of queens")
N = int(input())
#chessboard
#NxN matrix with all elements 0
board = [[0]*N for _ in range(N)]
def is_attack(i, j):
#checking if there is a queen in row or column
for k in range(0,N):
if board[i][k]==1 or board[k][j]==1:
return True
#checking diagonals
for k in range(0,N):
for l in range(0,N):
if (k+l==i+j) or (k-l==i-j):
if board[k][l]==1:
return True
return False
def N_queen(n):
if n==0:
return True
for i in range(0,N):
T.Y.BSc.IT Artificial Intelligence and Application I22044
for j in range(0,N):
'''checking if we can place a queen here or not
queen will not be placed if the place is being attacked
or already occupied'''
if (not(is_attack(i,j))) and (board[i][j]!=1):
board[i][j] = 1
#recursion
#wether we can put the next queen with this arrangment or not
if N_queen(n-1)==True:
return True
board[i][j] = 0
return False
N_queen(N)
for i in board:
print (i)
Output:
T.Y.BSc.IT Artificial Intelligence and Application I22044
PRACTICAL NO.5
a) Shuffle deck of cards.
Input:
import itertools, random
deck = list(itertools.product(range(1,14),['Spade','Heart','Diamond','Club']))
random.shuffle(deck)
# draw five cards
print("You got:")
for i in range(5):
print(deck[i][0], "of", deck[i][1])
Output:
T.Y.BSc.IT Artificial Intelligence and Application I22044
PRACTICAL NO.3
a) Implement alpha beta search.
Input:
import math
# Function to perform alpha-beta pruning
def alpha_beta_search(depth, node_index, maximizing_player, values, alpha, beta):
# Base case: if we have reached a leaf node
if depth == 3:
return values[node_index]
if maximizing_player:
max_eval = -math.inf
# Evaluate the children of the current node
for i in range(2): # assuming binary tree
eval = alpha_beta_search(depth + 1, node_index * 2 + i, False, values, alpha, beta)
max_eval = max(max_eval, eval)
alpha = max(alpha, eval)
if beta <= alpha:
break # Beta cut-off
return max_eval
else:
min_eval = math.inf
# Evaluate the children of the current node
for i in range(2): # assuming binary tree
eval = alpha_beta_search(depth + 1, node_index * 2 + i, True, values, alpha, beta)
min_eval = min(min_eval, eval)
beta = min(beta, eval)
if beta <= alpha:
break # Alpha cut-off
return min_eval
# Example values at the leaf nodes of the tree (depth = 3)
values = [3, 5, 6, 9, 1, 2, 0, -1]
# Initial values for alpha and beta
alpha = -math.inf
beta = math.inf
# Start alpha-beta pruning
result = alpha_beta_search(0, 0, True, values, alpha, beta)
print("The optimal value is:", result)
Output:
T.Y.BSc.IT Artificial Intelligence and Application I22044
b) Implement hill climbing problem.
Input:
import random
def objective_function(solution):
return sum(solution)
def generate_neighbor(current_solution):
neighbor = current_solution[:]
index = random.randint(0, len(neighbor) - 1)
neighbor[index] = 1 - neighbor[index] # Flip the value at the selected index
return neighbor
def hill_climbing():
# Initialization
current_solution = [random.randint(0, 1) for _ in range(10)] # Generate an initial solution
current_fitness = objective_function(current_solution)
# Iterative process
while True:
# Neighbor generation
neighbor = generate_neighbor(current_solution)
neighbor_fitness = objective_function(neighbor)
# Comparison
if neighbor_fitness >= current_fitness:
current_solution = neighbor
current_fitness = neighbor_fitness
else:
break # Terminate if no better solution is found
return current_solution, current_fitness
# Usage example
best_solution, best_fitness = hill_climbing()
print("Best Solution:", best_solution)
print("Best Fitness:", best_fitness)
Output:
T.Y.BSc.IT Artificial Intelligence and Application I22044
PRACTICAL NO.4
a) Implement A* algorithm.
Input:
import heapq
# Class for representing nodes in the search space
class Node:
def init (self, position, parent=None):
self.position = position
self.parent = parent
self.g = 0
self.h = 0
self.f = 0
def eq (self, other):
return self.position == other.position
def lt (self, other):
return self.f < other.f
# A* algorithm
def astar(start, end, grid):
# Initialize the start and end nodes
start_node = Node(start)
end_node = Node(end)
# Open list (priority queue) and closed list
open_list = []
closed_list = set()
# Add the start node to the open list
heapq.heappush(open_list, start_node)
# Loop until we find the goal or exhaust the search space
while open_list:
# Get the current node with the lowest f score
current_node = heapq.heappop(open_list)
closed_list.add(current_node.position)
# If we reach the goal, reconstruct the path
if current_node == end_node:
return reconstruct_path(current_node)
# Generate neighbors (up, down, left, right)
neighbors = get_neighbors(current_node, grid)
for neighbor_position in neighbors:
# Skip if the neighbor is in the closed list (already visited)
if neighbor_position in closed_list:
continue
T.Y.BSc.IT Artificial Intelligence and Application I22044
# Create a new node for the neighbor
neighbor_node = Node(neighbor_position, current_node)
# Calculate the g, h, and f scores for the neighbor
neighbor_node.g = current_node.g + 1 # Assuming uniform cost of 1 for grid
neighbor_node.h = heuristic(neighbor_node.position, end_node.position)
neighbor_node.f = neighbor_node.g + neighbor_node.h
# If the neighbor is already in the open list with a lower f score, skip it
if add_to_open_list(open_list, neighbor_node):
heapq.heappush(open_list, neighbor_node)
return None # No path found
# Heuristic function (Manhattan distance for grid)
def heuristic(position, goal):
return abs(position[0] - goal[0]) + abs(position[1] - goal[1])
# Get neighbors for a node (up, down, left, right movements)
def get_neighbors(node, grid):
neighbors = []
row, col = node.position
# Define movement directions: up, down, left, right
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
for direction in directions:
new_row = row + direction[0]
new_col = col + direction[1]
# Ensure the neighbor is within bounds and not an obstacle
if 0 <= new_row < len(grid) and 0 <= new_col < len(grid[0]) and grid[new_row][new_col] ==
0:
neighbors.append((new_row, new_col))
return neighbors
# Helper function to check if a node should be added to the open list
def add_to_open_list(open_list, neighbor_node):
for node in open_list:
if neighbor_node == node and neighbor_node.f >= node.f:
return False
return True
# Reconstruct the path by tracing back from the goal node
def reconstruct_path(node):
path = []
while node:
path.append(node.position)
node = node.parent
return path[::-1] # Return the path in reverse order (from start to goal)
# Test case: A simple 5x5 grid (0 = free, 1 = obstacle)
grid = [
T.Y.BSc.IT Artificial Intelligence and Application I22044
[0, 1, 0, 0, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 1, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 0, 0]
]
start = (0, 0) # Starting position (top-left)
end = (4, 4) # Goal position (bottom-right)
# Run the A* algorithm
path = astar(start, end, grid)
# Print the result
if path:
print(f"Path found: {path}")
else:
print("No path found.")
Output:
T.Y.BSc.IT Artificial Intelligence and Application I22044
b) Solve water jug problem.
Input:
from collections import defaultdict
jug1, jug2, aim = 5, 4, 3
visited = defaultdict(lambda: False)
def waterJugSolver(amt1, amt2):
if (amt1 == aim and amt2 == 0) or (amt2 == aim and amt1 == 0):
print(amt1, amt2)
return True
if visited[(amt1, amt2)] == False:
print(amt1, amt2)
visited[(amt1, amt2)] = True
return (waterJugSolver(0, amt2) or
waterJugSolver(amt1, 0) or
waterJugSolver(jug1, amt2) or
waterJugSolver(amt1, jug2) or
waterJugSolver(amt1 + min(amt2,(jug1-amt1)),
amt2=min(amt2,(jug1-amt1))) or
waterJugSolver(amt1 - min(amt1, (jug2-amt2)),amt2+min(amt1,(jug2-amt2))))
else:
return False
print("Steps: ")
waterJugSolver(0, 0)
Output:
T.Y.BSc.IT Artificial Intelligence and Application I22044
PRACTICAL NO.6
a) Derive the expressions based on Associative Law.
Input:
# Function to demonstrate associative law for addition
def associative_addition(a, b, c):
return (a + b) + c == a + (b + c)
# Function to demonstrate associative law for multiplication
def associative_multiplication(a, b, c):
return (a * b) * c == a * (b * c)
# Test cases
a, b, c = 5, 10, 3
print(f"Associative law for addition holds: {associative_addition(a, b, c)}")
print(f"Associative law for multiplication holds: {associative_multiplication(a, b, c)}")
Output:
b) Derive the expressions based on Distributive Law.
Input:
# Function to demonstrate the distributive law
def distributive_law(a, b, c):
left_side = a * (b + c)
right_side = (a * b) + (a * c)
return left_side == right_side
# Test case
a, b, c = 5, 10, 3
print(f"Distributive law holds: {distributive_law(a, b, c)}")
Output:
T.Y.BSc.IT Artificial Intelligence and Application I22044
PRACTICAL NO.7
a) Derive the predicate. (for e.g.: Sachin is batsman, batsman is cricketer)
- > Sachin is Cricketer
Input:
class Entity:
def init (self, name):
self.name = name
self.relations = {}
def add_relation(self, relation, entity):
if relation not in self.relations:
self.relations[relation] = []
self.relations[relation].append(entity)
def is_relation(self, relation, entity):
return entity in self.relations.get(relation, [])
# Define entities
sachin = Entity("Sachin")
batsman = Entity("Batsman")
cricketer = Entity("Cricketer")
# Define relationships
sachin.add_relation("is_a", batsman)
batsman.add_relation("is_a", cricketer)
# Function to derive predicates
def derive(entity, relation):
if entity.is_relation(relation, batsman):
if batsman.is_relation("is_a", cricketer):
return f"{entity.name} is a {cricketer.name}."
return f"No derivation found for {entity.name}."
# Derive predicate
result = derive(sachin, "is_a")
print(result) # Output: Sachin is a Cricketer.
Output:
T.Y.BSc.IT Artificial Intelligence and Application I22044
PRACTICAL NO.8
a) Write a program which contains three predicates: male, female, parent. Make rules for
following family relations: father, mother, grandfather, grandmother, brother, sister, uncle,
aunt, nephew and niece, cousin. Question: i. Draw Family Tree. ii. Define: Clauses, Facts,
Predicates and Rules with conjunction and disjunction.
Input:
class Person:
def init (self, name, gender):
self.name = name
self.gender = gender
self.parents = []
self.children = []
def add_parent(self, parent):
self.parents.append(parent)
parent.children.append(self)
# Predicate functions
def male(person):
return person.gender == 'male'
def female(person):
return person.gender == 'female'
def parent(person):
return person.children
# Family relationship functions
def father(child):
for parent in child.parents:
if male(parent):
return parent
return None
def mother(child):
for parent in child.parents:
if female(parent):
return parent
return None
def grandfather(grandchild):
for parent in grandchild.parents:
father_of_parent = father(parent)
if father_of_parent:
return father_of_parent
return None
def grandmother(grandchild):
for parent in grandchild.parents:
mother_of_parent = mother(parent)
T.Y.BSc.IT Artificial Intelligence and Application I22044
if mother_of_parent:
return mother_of_parent
return None
def brother(sibling1, sibling2):
return male(sibling1) and sibling1 != sibling2 and any(parent in sibling1.parents for parent in
sibling2.parents)
def sister(sibling1, sibling2):
return female(sibling1) and sibling1 != sibling2 and any(parent in sibling1.parents for parent in
sibling2.parents)
def uncle(niece_or_nephew):
for parent in niece_or_nephew.parents:
for sibling in parent.children:
if sibling != parent and male(sibling):
return sibling
return None
def aunt(niece_or_nephew):
for parent in niece_or_nephew.parents:
for sibling in parent.children:
if sibling != parent and female(sibling):
return sibling
return None
def nephew(niece_or_nephew):
for parent in niece_or_nephew.parents:
for sibling in parent.children:
if sibling != parent and male(sibling):
return sibling
return None
def niece(niece_or_nephew):
for parent in niece_or_nephew.parents:
for sibling in parent.children:
if sibling != parent and female(sibling):
return sibling
return None
def cousin(cousin1, cousin2):
return any(parent in cousin1.parents for parent in cousin2.parents) and cousin1 != cousin2
# Example family setup
john = Person("John", "male")
susan = Person("Susan", "female")
mike = Person("Mike", "male")
lisa = Person("Lisa", "female")
james = Person("James", "male")
mary = Person("Mary", "female")
# Define parent relationships
T.Y.BSc.IT Artificial Intelligence and Application I22044
john.add_parent(mike)
susan.add_parent(mike)
john.add_parent(lisa)
susan.add_parent(lisa)
mike.add_parent(james)
mike.add_parent(mary)
# Example queries
print("Father of John:", father(john).name)
print("Mother of John:", mother(john).name)
print("Grandfather of John:", grandfather(john).name)
print("Grandmother of John:", grandmother(john).name)
print("Is Mike John's brother?", brother(mike, john))
print("Is Lisa John's sister?", sister(lisa, john))
print("Uncle of John:", uncle(john).name)
print("Aunt of John:", aunt(john).name)
Output :
T.Y.BSc.IT Artificial Intelligence and Application I22044
PRACTICAL NO.9
a) Design an application to simulate number puzzle problem.
Input:
import random
class Puzzle:
def init (self):
self.size = 4
self.board = self.initialize_board()
self.empty_tile = (3, 3) # Coordinates of the empty tile (row, col)
def initialize_board(self):
"""Initialize the board with numbers 1-15 and an empty tile (0)"""
numbers = list(range(1, 16)) + [0] # 0 represents the empty tile
random.shuffle(numbers)
board = [numbers[i * self.size:(i + 1) * self.size] for i in range(self.size)]
return board
def display(self):
"""Display the current state of the board"""
for row in self.board:
print(" ".join(f"{num:2d}" if num != 0 else " . " for num in row))
print()
def find_empty_tile(self):
"""Find the current position of the empty tile"""
for i in range(self.size):
for j in range(self.size):
if self.board[i][j] == 0:
return (i, j)
def is_solvable(self):
"""Check if the current configuration is solvable"""
flat_list = [num for row in self.board for num in row if num != 0]
inversions = sum(
1 for i in range(len(flat_list)) for j in range(i + 1, len(flat_list)) if flat_list[i] > flat_list[j]
)
return inversions % 2 == 0
def slide(self, direction):
"""Slide a tile in the specified direction (up, down, left, right)"""
row, col = self.empty_tile
if direction == "up" and row < self.size - 1:
self.board[row][col], self.board[row + 1][col] = self.board[row + 1][col],
self.board[row][col]
self.empty_tile = (row + 1, col)
elif direction == "down" and row > 0:
self.board[row][col], self.board[row - 1][col] = self.board[row - 1][col],
self.board[row][col]
self.empty_tile = (row - 1, col)
elif direction == "left" and col < self.size - 1:
T.Y.BSc.IT Artificial Intelligence and Application I22044
self.board[row][col], self.board[row][col + 1] = self.board[row][col + 1],
self.board[row][col]
self.empty_tile = (row, col + 1)
elif direction == "right" and col > 0:
self.board[row][col], self.board[row][col - 1] = self.board[row][col - 1],
self.board[row][col]
self.empty_tile = (row, col - 1)
def is_solved(self):
"""Check if the board is in a solved state"""
count = 1
for row in range(self.size):
for col in range(self.size):
if (row, col) == (self.size - 1, self.size - 1):
return self.board[row][col] == 0 # Last tile should be 0
if self.board[row][col] != count:
return False
count += 1
return True
def play_game():
puzzle = Puzzle()
# Ensure the puzzle is solvable
while not puzzle.is_solvable():
puzzle = Puzzle()
print("Welcome to the 15-Puzzle Game!")
puzzle.display()
while not puzzle.is_solved():
move = input("Enter your move (up, down, left, right): ").lower()
if move in ["up", "down", "left", "right"]:
puzzle.slide(move)
puzzle.display()
else:
print("Invalid move! Please enter 'up', 'down', 'left', or 'right'.")
print("Congratulations! You've solved the puzzle!")
if name == " main ":
play_game()
Output:
T.Y.BSc.IT Artificial Intelligence and Application I22044
PRACTICAL NO.10
a) Solve constraint satisfaction problem.
Input:
def is_safe(board, row, col, n):
"""Check if it's safe to place a queen at board[row][col]."""
# Check this column on upper side
for i in range(row):
if board[i][col] == 'Q':
return False
# Check upper diagonal on left side
for i, j in zip(range(row, -1, -1), range(col, -1, -1)):
if j < 0:
break
if board[i][j] == 'Q':
return False
# Check upper diagonal on right side
for i, j in zip(range(row, -1, -1), range(col, n)):
if j >= n:
break
if board[i][j] == 'Q':
return False
return True
def solve_n_queens_util(board, row, n, solutions):
"""Utilize backtracking to solve the N-Queens problem."""
if row >= n:
# Found a solution, add it to the solutions list
solutions.append([''.join(row) for row in board])
return
for col in range(n):
if is_safe(board, row, col, n):
board[row][col] = 'Q' # Place queen
solve_n_queens_util(board, row + 1, n, solutions) # Recur to place rest of the queens
board[row][col] = '.' # Backtrack
def solve_n_queens(n):
"""Solve the N-Queens problem and return all solutions."""
board = [['.' for _ in range(n)] for _ in range(n)] # Initialize the board
solutions = []
solve_n_queens_util(board, 0, n, solutions)
return solutions
def print_solutions(solutions):
"""Print all the solutions in a human-readable format."""
for idx, solution in enumerate(solutions):
T.Y.BSc.IT Artificial Intelligence and Application I22044
print(f"Solution {idx + 1}:")
for row in solution:
print(row)
print()
if name == " main ":
n = 4 # Change this value to solve for a different N
solutions = solve_n_queens(n)
print_solutions(solutions)
Output: