AI Lab Programs
AI Lab Programs
LIST OF EXPERIMENTS
SL. NO Experiments
1 Implement and Demonstrate Depth First Search Algorithm on Water Jug Problem
10 Build a bot which provides all the information related to text in search box
Algorithm:
1. Start with the initial state where both jugs are empty.
2. Perform depth-first search to explore all possible states starting from the initial state.
3. At each state, generate all possible next states by performing the following operations:
Emptying Jug 1
Emptying Jug 2
Filling Jug 1 to its capacity
Filling Jug 2 to its capacity
Pouring water from Jug 1 to Jug 2 until Jug 1 is empty or Jug 2 is full
Pouring water from Jug 2 to Jug 1 until Jug 2 is empty or Jug 1 is full
4. Keep track of visited states to avoid revisiting the same state.
5. If a target state is reached, return the path from the initial state to the target state.
6. If no solution is found, return None.
class State:
def init (self, jug1,
jug2): self.jug1 = jug1
self.jug2 = jug2
visited.add(current)
return None
# Empty Jug 1
next_states.append(State(0, current.jug2))
# Empty Jug 2
next_states.append(State(current.jug1, 0))
# Fill Jug 1
next_states.append(State(jug1_capacity, current.jug2))
# Fill Jug 2
next_states.append(State(current.jug1, jug2_capacity))
# Pour Jug 1 to Jug 2
pour_amount = min(current.jug1, jug2_capacity - current.jug2)
next_states.append(State(current.jug1 - pour_amount, current.jug2 + pour_amount))
# Pour Jug 2 to Jug 1
pour_amount = min(current.jug2, jug1_capacity - current.jug1)
next_states.append(State(current.jug1 + pour_amount, current.jug2 - pour_amount))
return [state for state in next_states if 0 <= state.jug1 <= jug1_capacity and 0 <= state.jug2 <=
jug2_capacity]
def main():
jug1_capacity =
4
jug2_capacity = 3
target_capacity = 2
initial_state = State(0, 0)
target_state = State(target_capacity, 0)
if path:
print("Solution Found:")
for idx, state in enumerate(path):
print(f"Step {idx}: {state}")
else:
print("No solution exists.")
Output:
Solution Found:
Step 0: (0, 0)
Step 1: (4, 0)
Step 2: (4, 3)
Step 3: (0, 3)
Step 4: (3, 0)
Step 5: (3, 3)
Step 6: (4, 2)
Step 7: (0, 2)
Step 8: (2, 0)
The DFS algorithm explores all possible states recursively and finds a valid sequence of steps
leading to the target amount.
To transfer three missionaries and three cannibals across a river using a boat, ensuring that
at no point are the missionaries outnumbered by the cannibals on either side.
Algorithm
1. Initialization : Start with the initial state and define the goal state.
2. Priority Queue : Use a priority queue to manage states, prioritizing those with lower
costs.
3. Cost and Predecessor Tracking: Keep track of the cost (number of steps) to reach each
state and their predecessors.
4. Main Loop : Iterate until the priority queue is empty:
Dequeue the state with the lowest cost.
Check if it's the goal state. If so, end the search.
Generate successors, update costs, and enqueue new
states. Update predecessors for successors.
5. Path Reconstruction : Once the goal state is found, trace back through predecessors to
reconstruct the solution path.
6. Print Solution : Display the solution path.
Program:
from queue import PriorityQueue
class State:
def init (self, lm, lc, boat, rm, rc):
self.lm, self.lc, self.boat, self.rm, self.rc = lm, lc, boat, rm, rc
def is_valid(self):
return all(x >= 0 for x in (self.lm, self.lc, self.rm, self.rc)) and \
(self.lm == 0 or self.lm >= self.lc) and \
(self.rm == 0 or self.rm >= self.rc)
def successors(state):
return [State(state.lm - m * state.boat + m * (1 - state.boat),
state.lc - c * state.boat + c * (1 - state.boat),
1 - state.boat,
state.rm + m * state.boat - m * (1 - state.boat),
state.rc + c * state.boat - c * (1 - state.boat))
for m, c in [(i, j) for i in range(3) for j in range(3) if 1 <= i + j <= 2]]
def best_first_search():
start, goal = State(3, 3, 1, 0, 0), State(0, 0, 0, 3, 3)
frontier, came_from, cost_so_far = PriorityQueue(), {start: None}, {start: 0}
frontier.put((0, start))
def print_solution(path):
if not path:
print("No solution
found.") else:
for i, state in enumerate(path):
print("Step {}: Left {}M {}C | Boat {} | Right {}M {}C".format(
i, state.lm, state.lc, 'left' if state.boat else 'right', state.rm, state.rc))
Output:
Step 0: Left 3M 3C | Boat left | Right 0M 0C
Step 1: Left 3M 1C | Boat right | Right 0M 2C
Step 2: Left 4M 1C | Boat left | Right -1M 2C
Step 3: Left 2M 1C | Boat right | Right 1M 2C
Step 4: Left 2M 2C | Boat left | Right 1M 1C
Step 5: Left 1M 1C | Boat right | Right 2M 2C
Step 6: Left 1M 2C | Boat left | Right 2M 1C
Step 7: Left 1M 0C | Boat right | Right 2M 3C
Step 8: Left 1M 1C | Boat left | Right 2M 2C
Step 9: Left 0M 0C | Boat right | Right 3M 3C
Inference / Results:
The Best-First Search algorithm successfully finds a valid sequence of moves to transport the
missionaries and cannibals across the river without violating any constraints.
1. Initialization:
o The graph is represented using an adjacency list.
o The Graph class is initialized with this adjacency list.
o The get_neighbors method returns the neighbors of a given node.
o The h method defines a heuristic function that estimates the cost from a given node
to the goal. In this case, it's a simple heuristic function that assigns a cost of 1 to each
node.
2. A* Algorithm:
o The A* algorithm starts by initializing:
open_lst: a set to keep track of nodes to be evaluated, starting with the start
node.
closed_lst: a set to keep track of nodes that have been evaluated.
dist: a dictionary to store the cost of getting to a node from the start node.
prenode: a dictionary to store the previous node in the optimal path.
o The main loop continues until there are nodes to be evaluated (open_lst is
not empty).
o Within each iteration of the loop:
The node with the lowest total cost (path cost + heuristic) is selected from
open_lst.
If no such node is found, it means there's no path to the goal, and the
algorithm terminates.
If the selected node is the goal node, the algorithm reconstructs and prints the
path by tracing back from the goal to the start using the prenode dictionary.
Otherwise, the neighbors of the selected node are explored:
If a neighbor is not in open_lst or closed_lst, it's added to open_lst, and its
path cost and previous node are updated.
If a neighbor is in open_lst or closed_lst but the new path to it is shorter
than the previous path, its path cost and previous node are updated accordingly.
The selected node is moved from open_lst to closed_lst.
3. Output
o If the goal is reached, the algorithm prints the shortest path found from the start
node to the stop node.
o If no path is found, it prints "Path does not exist!".
Program:
from collections import deque
class Graph:
def init (self, adjac_lis):
# Initialize the graph with an adjacency list
self.adjac_lis = adjac_lis
# Dictionary to store the cost of getting to a node from the start node
dist = {start: 0}
# Dictionary to store the previous node in the optimal path
prenode = {start: start}
# Main loop
while len(open_lst) > 0:
n = None
# Find the node with the lowest cost + heuristic value
for v in open_lst:
if n is None or dist[v] + self.h(v) < dist[n] + self.h(n):
n=v
# If no path is found
if n is None:
print('Path does not exist!')
return None
Output:
Path found: ['A', 'B', 'D']