0% found this document useful (0 votes)
10 views14 pages

AI Lab Programs

The document outlines various AI lab programs, including implementations of algorithms such as Depth First Search for the Water Jug Problem, Best First Search for the Missionaries-Cannibals Problem, A* Search, AO* Search, and solutions for the 8-Queens Problem and TSP using heuristic approaches. It also covers problem-solving strategies using Forward and Backward Chaining, and the resolution principle in First Order Predicate Logic. Each section includes code snippets and explanations for the algorithms and problems addressed.
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)
10 views14 pages

AI Lab Programs

The document outlines various AI lab programs, including implementations of algorithms such as Depth First Search for the Water Jug Problem, Best First Search for the Missionaries-Cannibals Problem, A* Search, AO* Search, and solutions for the 8-Queens Problem and TSP using heuristic approaches. It also covers problem-solving strategies using Forward and Backward Chaining, and the resolution principle in First Order Predicate Logic. Each section includes code snippets and explanations for the algorithms and problems addressed.
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/ 14

AI LAB PROGRAMS

1. Implement and Demonstrate Depth First Search Algorithm on Water


Jug Problem.
def dfs(x, y, action, target, visited, path):
# If already visited this state, skip it
if (x, y) in visited:
return False

# If we reach the target in either jug


if x == target or y == target:
path.append((x, y, action))
return True

# Mark as visited and add current state to path


visited.add((x, y))
path.append((x, y, action))

# Fill Jug 1
if dfs(5, y, "Fill Jug 1", target, visited, path):
return True

# Fill Jug 2
if dfs(x, 3, "Fill Jug 2", target, visited, path):
return True

# Pour Jug 1 to Jug 2


move = min(x, 3 - y)
if dfs(x - move, y + move, "Pour Jug 1 to Jug 2", target, visited, path):
return True

# Pour Jug 2 to Jug 1


move = min(5 - x, y)
if dfs(x + move, y - move, "Pour Jug 2 to Jug 1", target, visited, path):
return True

# Empty Jug 1
if dfs(0, y, "Empty Jug 1", target, visited, path):
return True

# Empty Jug 2
if dfs(x, 0, "Empty Jug 2", target, visited, path):
return True

# Backtrack
path.pop()
return False

def solve(target):
visited = set()
path = []
if dfs(0, 0, "Start", target, visited, path):
print("\nSolution path:")
for a, b, act in path:
print(f"Jug1: {a}, Jug2: {b} -> {act}")
else:
print("No solution found.")

# Example run
if __name__ == "__main__":
try:
target = int(input("Enter the target amount of water: "))
if 0 < target <= 5:
solve(target)
else:
print("Target must be between 1 and 5 (inclusive) for the 5L-3L jug
problem.")
except ValueError:
print("Please enter a valid integer.")
2. Implement and Demonstrate Best First Search Algorithm on
Missionaries-Cannibals Problems using Python.
from collections import deque

def is_valid(state):
ml, cl, boat, mr, cr, _ = state

# Ensure no group of missionaries is outnumbered by cannibals on either bank


if ml < 0 or cl < 0 or mr < 0 or cr < 0:
return False
if (ml > 0 and ml < cl) or (mr > 0 and mr < cr):
return False
return True

def next_states(state):
ml, cl, boat, mr, cr, _ = state
moves = [
(0, 2, "2C"),
(0, 1, "1C"),
(1, 0, "1M"),
(2, 0, "2M"),
(1, 1, "1M and 1C"),
]

next_states = []
for m, c, action in moves:
if boat == 1: # Boat on right bank, move to left
new_state = (ml + m, cl + c, 0, mr - m, cr - c, f"Move {action} to left")
else: # Boat on left bank, move to right
new_state = (ml - m, cl - c, 1, mr + m, cr + c, f"Move {action} to right")

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

return next_states

def solve():
start_state = (0, 0, 1, 3, 3, "Start: 3M and 3C on right bank")
goal_state = (3, 3, 0, 0, 0) # All on left bank, boat on left

queue = deque([(start_state, [])])


visited = set()

while queue:
current_state, path = queue.popleft()
ml, cl, boat, mr, cr, _ = current_state

if (ml, cl, boat, mr, cr) == goal_state:


return path + [current_state]

if (ml, cl, boat, mr, cr) not in visited:


visited.add((ml, cl, boat, mr, cr))
for next_state in next_states(current_state):
queue.append((next_state, path + [current_state]))

return None

# Run the solver


solution = solve()
if solution:
print("Solution found:\n")
for state in solution:
ml, cl, boat, mr, cr, action = state
side = "left" if boat == 0 else "right"
print(f"LHS: M={ml}, C={cl} | RHS: M={mr}, C={cr} | Boat on {side} ->
{action}")
else:
print("No solution found.")
3. Implement A* Search & AO* Search algorithms.
A* Search algorithm:
import heapq # for priority queue

def a_star(graph, heuristic, start, goal):


# Priority queue: (estimated total cost, current node)
open_list = [(0, start)]

# Track the best path to each node


came_from = {start: None}

# Actual cost from start to current node


g_cost = {start: 0}

while open_list:
_, current = heapq.heappop(open_list)

# Goal reached — reconstruct the path


if current == goal:
path = []
while current is not None:
path.append(current)
current = came_from[current]
path.reverse()
return path, g_cost[goal]

# Explore neighbors
for neighbor, cost in enumerate(graph[current]):
if cost == 0:
continue # Skip if not connected

new_cost = g_cost[current] + cost

# Update path if this path is cheaper


if neighbor not in g_cost or new_cost < g_cost[neighbor]:
g_cost[neighbor] = new_cost
priority = new_cost + heuristic.get(neighbor, float('inf'))
heapq.heappush(open_list, (priority, neighbor))
came_from[neighbor] = current

# No path found
return None, float('inf')

# --- Example Usage ---

graph = [
[0, 1, 4, 0, 0, 0],
[1, 0, 2, 5, 0, 0],
[4, 2, 0, 3, 6, 0],
[0, 5, 3, 0, 2, 7],
[0, 0, 6, 2, 0, 4],
[0, 0, 0, 7, 4, 0]
]

heuristic = {
0: 10,
1: 8,
2: 6,
3: 4,
4: 2,
5: 0
}

start_node = 0
goal_node = 5

path, total_cost = a_star(graph, heuristic, start_node, goal_node)

if path:
print("Path:", path)
print("Total Cost:", total_cost)
else:
print("No path found.")

AO* Search Algorithm:


class Node:
def __init__(self, name, heuristic):
self.name = name
self.heuristic = heuristic
self.children = []

def add_child(self, nodes, costs, and_node=False):


if and_node:
# AND node: group of children must all be followed
self.children.append(list(zip(nodes, costs)))
else:
# OR nodes: each child is an independent option
self.children.extend(zip(nodes, costs))

def ao_star(start):
solved = set()
frontier = [(start, start.heuristic)]
total_cost = 0

while frontier:
node, _ = frontier.pop(0)

if not node.children:
solved.add(node)
continue

# Evaluate all children to find the minimum cost path


best_group = None
min_cost = float('inf')

for group in node.children:


group = group if isinstance(group, list) else [group]
cost = sum(c + n.heuristic for n, c in group)
if cost < min_cost:
min_cost = cost
best_group = group

total_cost = min_cost

# Add unexplored children from the best group to frontier


for child, _ in best_group:
if child not in solved:
frontier.append((child, child.heuristic))

solved.add(node)
return solved, total_cost

# ---------- Example Setup ----------

A = Node('A', 10)
B = Node('B', 8)
C = Node('C', 6)
D = Node('D', 4)
E = Node('E', 2)
F = Node('F', 1)
G = Node('G', 0)

A.add_child([B, C], [2, 3], and_node=True)


B.add_child([D, E], [4, 5])
C.add_child([D, F], [1, 2])
D.add_child([G], [3])
E.add_child([G], [1])
F.add_child([G], [2])

solved_nodes, path_cost = ao_star(A)

print("Solved Nodes:", [n.name for n in solved_nodes])


print("Total Path Cost:", path_cost)
4. Solve 8-Queens Problem with suitable assumptions.
def is_safe(position, row, col):
"""
Check if placing a queen at (row, col) is safe.
"""
for r in range(row):
c = position[r]
# Check same column or same diagonal
if c == col or abs(c - col) == abs(r - row):
return False
return True

def solve_queens(row=0, position=None, solutions=None):


"""
Recursive backtracking to find all solutions to the 8-Queens problem.
- row: current row to place a queen
- position: list storing column positions of queens
- solutions: list to store all valid solutions
"""
if position is None:
position = []
if solutions is None:
solutions = []

if row == 8:
solutions.append(position[:]) # Found a valid solution
return

for col in range(8):


if is_safe(position, row, col):
position.append(col) # Place queen
solve_queens(row + 1, position, solutions)
position.pop() # Backtrack

return solutions

# Find and display all solutions


all_solutions = solve_queens()

# Print the first 3 solutions in board format


def print_board(position):
for row in position:
line = ['.'] * 8
line[row] = 'Q'
print(' '.join(line))
print()

print("Total solutions found:", len(all_solutions))


for i in range(min(3, len(all_solutions))):
print(f"Solution {i+1}:")
print_board(all_solutions[i])
5. Implementation of TSP using heuristic approach.
def travel(graph, start):
"""
Greedy TSP-like traversal: from the starting city, always go to the nearest
unvisited city.
"""
num_cities = len(graph)
unvisited = list(range(num_cities))
unvisited.remove(start) # Remove the starting city from the unvisited list

path = [start]
current = start

while unvisited:
# Choose the closest unvisited city
next_city = min(unvisited, key=lambda city: graph[current][city])
path.append(next_city)
unvisited.remove(next_city)
current = next_city

return path

# Distance matrix: graph[i][j] is the distance from city i to city j


graph = [
[0, 10, 15, 20],
[10, 0, 35, 25],
[15, 35, 0, 30],
[20, 25, 30, 0]
]

start = 0
path = travel(graph, start)

# Calculate total distance by summing each step in the path


total_distance = sum(graph[path[i]][path[i + 1]] for i in range(len(path) - 1))

print("Travel path:", path)


print("Total distance:", total_distance)
6. Implementation of the problem-solving strategies: either using
Forward Chaining or Backward Chaining.
# Forward‐Chaining
print('Forward Chaining:')
facts = [
['croaks', 'frog'],
['eats flies', 'frog'],
['frog', 'green'],
['chirps', 'canary'],
['sings', 'canary'],
['canary', 'yellow']
]

starting_fact = ['croaks', 'frog']

def forward_chain(rules, initial_facts):


"""
Perform forward chaining on a set of if-then rules.
- rules: list of [antecedent, consequent] pairs
- initial_facts: list of facts known at the start
Returns the set of all facts that can be derived.
"""
# Use a set for quick membership tests and to avoid duplicates
known = set(initial_facts)

# Keep looping until no new fact can be added


added_new_fact = True
while added_new_fact:
added_new_fact = False

# Check each rule: if the antecedent is already known, we can add the
consequent
for antecedent, consequent in rules:
if antecedent in known and consequent not in known:
known.add(consequent)
added_new_fact = True
print(f"Inferred '{consequent}' because '{antecedent}' is known.")

return known

# Run forward chaining starting from the given facts


derived_facts = forward_chain(facts, starting_fact)

print("All known/derived facts:", derived_facts)


# All known/derived facts: {'croaks', 'frog', 'green'}

# Backward -Chaining
# Rules are expressed as: if antecedent then consequent
print('\nBackward Chaining:')
facts = [
['croaks', 'frog'],
['eats flies', 'frog'],
['frog', 'green'],
['chirps', 'canary'],
['sings', 'canary'],
['canary', 'yellow']
]

# Initial facts we know


known_facts = ['croaks']

def backward_chain(rules, goal, known):


"""
Try to prove the goal using backward chaining.
- rules: list of [antecedent, consequent] pairs
- goal: the fact we want to prove
- known: list of known facts
Returns True if the goal can be proven; otherwise False.
"""
if goal in known:
return True # Already known

# Search for rules where goal is the consequent


for antecedent, consequent in rules:
if consequent == goal:
# Try to prove the antecedent recursively
if backward_chain(rules, antecedent, known):
known.append(goal) # Add to known facts
return True

return False # Goal cannot be proven

# Test a few goals


goals = ['frog', 'green', 'canary', 'yellow']

for g in goals:
result = backward_chain(facts, g, known_facts.copy())
print(f"Can we prove '{g}'? ->", "Yes" if result else "No")
# Output:
# Can we prove 'frog'? → Yes
# Can we prove 'green'? → Yes
# Can we prove 'canary'? → No
# Can we prove 'yellow'? → No
7. Implement resolution principle on FOPL related problems.
import re

# Check if a string is a simple literal like 'A' or '~A'


def is_literal(expr):
return re.fullmatch(r'~?[A-Z]', expr.strip()) is not None

# Convert to Disjunctive Normal Form (DNF): OR of ANDs


def to_dnf(expr):
expr = expr.strip()

if is_literal(expr): # Base case: already a literal


return expr

# Choose operator based on presence


op = 'or' if 'or' in expr else 'and'
parts = re.split(r'\s+or\s+|\s+and\s+', expr)

# Recursively apply DNF


return f' {op} '.join(to_dnf(p) for p in parts)

# Convert to Conjunctive Normal Form (CNF): AND of ORs


def to_cnf(expr):
expr = expr.strip()

if is_literal(expr): # Base case


return expr

op = 'and' if 'and' in expr else 'or'


parts = re.split(r'\s+and\s+|\s+or\s+', expr)

# Recursively apply CNF


return f' {op} '.join(to_cnf(p) for p in parts)

# Sample formula
formula = "A and B or C or D"

# Run transformations
print("Original:", formula)
print("CNF:", to_cnf(formula))
print("DNF:", to_dnf(formula))
8. Implement any Game and demonstrate the Game playing strategies.
import random

class TicTacToe:
def __init__(self):
self.board = [' '] * 9 # 3x3 board flattened

def display(self):
# Print board in 3 rows
for i in range(0, 9, 3):
print(f" {self.board[i]} | {self.board[i+1]} | {self.board[i+2]} ")
if i < 6: print("---+---+---")

def make_move(self, pos, player):


# Place player mark if empty
if self.board[pos] == ' ':
self.board[pos] = player
return True
return False

def available_moves(self):
# List empty positions
return [i for i, val in enumerate(self.board) if val == ' ']

def is_winner(self, player):


# All winning triplets (rows, cols, diagonals)
wins = [(0,1,2),(3,4,5),(6,7,8),
(0,3,6),(1,4,7),(2,5,8),
(0,4,8),(2,4,6)]
return any(all(self.board[i] == player for i in triplet) for triplet in wins)

def is_full(self):
# Board full if no empty spots
return ' ' not in self.board

def game_over(self):
# Check if either player won or board full
return self.is_winner('X') or self.is_winner('O') or self.is_full()

def minimax(game, player):


"""
Minimax returns dict with best move index and its score.
'X' tries to maximize score, 'O' tries to minimize.
Scores: +1 (X wins), -1 (O wins), 0 (draw).
"""
opponent = 'O' if player == 'X' else 'X'

# Base cases
if game.is_winner('X'): return {'score': 1}
if game.is_winner('O'): return {'score': -1}
if game.is_full(): return {'score': 0}

moves = []
for move in game.available_moves():
new_game = TicTacToe()
new_game.board = game.board[:] # copy board
new_game.make_move(move, player)

# Recursively evaluate opponent's response


result = minimax(new_game, opponent)
moves.append({'index': move, 'score': result['score']})
# Choose best move depending on player
if player == 'X':
return max(moves, key=lambda m: m['score'])
else:
return min(moves, key=lambda m: m['score'])

def play_game():
game = TicTacToe()
current = 'X' # X uses minimax, O moves randomly

print("Starting Tic-Tac-Toe:")
game.display()

while not game.game_over():


if current == 'X':
print("\n[X] Minimax is thinking...")
move = minimax(game, 'X')['index']
else:
print("\n[O] Random move...")
move = random.choice(game.available_moves())

game.make_move(move, current)
game.display()

if game.is_winner(current):
print(f"\n{current} wins!")
return
elif game.is_full():
print("\nIt's a draw!")
return

# Switch player
current = 'O' if current == 'X' else 'X'

if __name__ == "__main__":
play_game()

You might also like