0% found this document useful (0 votes)
4 views24 pages

Teena Ai File

The document outlines a series of experiments focused on solving classic AI problems, including the Water Jug Problem, the 8-Puzzle Problem, and the implementation of search algorithms like BFS, DFS, and A*. Each experiment includes a theoretical background, programming implementations, and learning outcomes that emphasize the importance of search algorithms in problem-solving and optimization. The document concludes with insights on the effectiveness and limitations of various algorithms used in artificial intelligence.

Uploaded by

prarit.work
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views24 pages

Teena Ai File

The document outlines a series of experiments focused on solving classic AI problems, including the Water Jug Problem, the 8-Puzzle Problem, and the implementation of search algorithms like BFS, DFS, and A*. Each experiment includes a theoretical background, programming implementations, and learning outcomes that emphasize the importance of search algorithms in problem-solving and optimization. The document concludes with insights on the effectiveness and limitations of various algorithms used in artificial intelligence.

Uploaded by

prarit.work
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 24

Experiment – 1

Aim : Water Jug Problem

Theory:
The two-jug problem is a classic AI search problem where we must measure a
specific amount of water using two jugs of different capacities, following a set of
constraints. In this experiment, we have a 3-liter jug and a 5-liter jug, and our goal
is to measure exactly 4 liters using systematic steps. The problem can be solved
using state-space search algorithms like BFS or DFS, where each state represents
the water levels in both jugs. The solution involves filling, transferring, and
emptying water until we reach the desired amount.

Program :
#include <iostream>
#include <vector>
using namespace std;

vector<int> jugs = {0, 0}; // Jug A and Jug B

void empty(int index) {


jugs[index] = 0;
}

void fill(int index) {


if (index == 0)
jugs[0] = 5; // Jug A capacity
else if (index == 1)
jugs[1] = 3; // Jug B capacity
}

void swap(int to) {


if (to == 0 && jugs[0] + jugs[1] <= 5) {
jugs[0] = jugs[0] + jugs[1];
empty(1);
} else if (to == 0) {
if (jugs[0] + jugs[1] > 5) {
jugs[1] = jugs[0] + jugs[1] - 5;
jugs[0] = 5;
}
} else if (to == 1 && jugs[0] + jugs[1] <= 3) {
jugs[1] = jugs[0] + jugs[1];
empty(0);
} else if (to == 1) {
if (jugs[0] + jugs[1] > 3) {
jugs[0] = jugs[0] + jugs[1] - 3;
jugs[1] = 3;
}
}
}

int main() {
vector<int> goal = {4, 0};

vector<string> options = {
"1. Add Jug A water to Jug B",
"2. Add Jug B water to Jug A",
"3. Empty Jug A",
"4. Empty Jug B",
"5. Fill Jug A",
"6. Fill Jug B"
};

while (true) {
cout << "Current State: A=" << jugs[0] << ", B=" << jugs[1] <<
endl;
if (jugs == goal) {
cout << "Success!" << endl;
break;
}

for (string opt : options)


cout << opt << endl;
int choice;
cout << "Choose your option: ";

cin >> choice;

switch (choice) {
case 1: swap(1); break;
case 2: swap(0); break;
case 3: empty(0); break;
case 4: empty(1); break;
case 5: fill(0); break;
case 6: fill(1); break;
default: cout << "Invalid choice" << endl;
}
}

return 0;
}
Output:

LEARNING :
Through the program, we learned how search algorithms can be applied towards
solving real-world problems. We were able to solve the two jugs water measuring
problem by exhaustively searching through all its possible states and following a simple
decision process to reach a desired result. This practical exercise illustrates the
importance of state space search in AI by showing that even with only simple loops and
conditionals, one can put together an algorithm that convincingly solves a problem.

CONCLUSION
The generalized program clearly shows how search algorithms can be used to
measure a specific amount of water with any two jugs of different capacities. It
also strengthens our knowledge in the area of search algorithms while
underlining the importance of developing flexible solutions that could work with
a whole range of inputs.
Experiment – 2

Aim:
8 Puzzle Problem
Theory:
One of the most famous puzzles is the 8-puzzle, which features a 3 × 3 grid with
one space empty and nine tiles numbered from 1 to 8. The latter can be slid into
the empty space, allowing them to be rearranged. The problem will consist of
changing the puzzle from the given configuration to a target configuration with a
series of valid movements. Thus, one is to find the sequence of moves that would
bring the puzzle into its goal configuration. So, the solution implies an
examination of various move sequences, usually represented by a tree whose
branches would correspond to the possible move from some state. Some other
efficient methods of solution are algorithms of Breadth- First Search and А*,
which search through prospective solutions in an orderly fashion

Program:
#include <iostream>
using namespace std;

const int S = 3;

void swap(int &a, int &b) {


int temp = a;
a = b;
b = temp;
}

void printM(int matrix[S][S], int x, int y) {


for (int i = 0; i < S; i++) {
for (int j = 0; j < S; j++) {
if (i == x && j == y)
cout << "* ";
else
cout << matrix[i][j] << " ";
}
cout << endl;
}
}

int main() {
int matrix[S][S];
cout << "Enter numbers from 1 to 8 in any order (use 0 for the blank
space):\n";

for (int i = 0; i < S; i++) {


for (int j = 0; j < S; j++) {
cin >> matrix[i][j];
}
}

// Find position of the blank (0)


int x = -1, y = -1;
for (int i = 0; i < S; i++) {
for (int j = 0; j < S; j++) {
if (matrix[i][j] == 0) {
x = i;
y = j;
}
}
}

if (x == -1 || y == -1) {
cout << "Error: No blank space (0) found in matrix.\n";
return 1;
}

char move;
while (true) {
printM(matrix, x, y);
cout << "Enter move (U/D/L/R): ";
cin >> move;
int x1 = x, y1 = y;

switch (move) {
case 'U':
case 'u':
if (x > 0) x1--;
else cout << "Invalid move!\n";
break;
case 'D':
case 'd':
if (x < S - 1) x1++;
else cout << "Invalid move!\n";
break;
case 'L':
case 'l':
if (y > 0) y1--;
else cout << "Invalid move!\n";
break;
case 'R':
case 'r':
if (y < S - 1) y1++;
else cout << "Invalid move!\n";
break;
default:
cout << "Invalid input! Use U, D, L, or R.\n";
continue;
}

// Swap only if the new position is different


if (x != x1 || y != y1) {
swap(matrix[x][y], matrix[x1][y1]);
x = x1;
y = y1;
}
}

return 0;
}
OUTPUT:

LEARNING:
State-Space Search: The concept it introduces is a consideration of all feasible
states in finding an optimum solution, wherein each configuration takes the form
of a particular state.It provides an algorithmic thinking process that, in a very
simple way, the puzzle shows how search algorithms like BFS or A* could be
employed in scientific and engineering applications to systematically explore
possibilities for the solution of complex problems. Optimization: The need is
stressed that finding a solution is not all; instead, one needs to find the most
efficient solution, which gives an optimum number of moves needed to achieve
this.
CONCLUSION
The 8-puzzle problem is a very straightforward and effective technique through
which to learn the rudiments in problem-solving and designing strategies for
related algorithms. Solving this puzzle actually deepens the basic understanding
of state-space explorations, search algorithms, and optimization techniques. Still,
the methods applied for 8-puzzle solving are relevant to far more serious
problems within the computer science and artificial intelligence field.
Experiment:3

Aim: 8 puzzle problem using hill climbing


Theory:
Hill Climbing is an optimization algorithm that continuously moves towards a
better solution by selecting the neighboring state with the best heuristic value. It
is a local search algorithm that does not backtrack or explore all possible paths,
making it efficient but prone to getting stuck in local optima. In the case of the
8-Puzzle Problem, the goal is to rearrange a 3×3 grid of numbered tiles into the
correct order by sliding tiles into the empty space. The heuristic function
commonly used includes:
• Misplaced Tiles: Counting the number of tiles that are not in their goal position.
• Manhattan Distance: Summing the horizontal and vertical distances of each
tile from its correct position. Hill Climbing iteratively moves towards a state with
a lower heuristic value (fewer misplaced tiles or a shorter total distance), aiming
to reach the goal state efficiently.

Program:
l = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
m = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

def find_blank(tile):
for i in range(3):
for j in range(3):
if tile[i][j] == 0:
return i, j
return -1, -1

def down(tile):
a, b = find_blank(tile)
if a == 2:
print("Move down not possible")
else:
tile[a][b], tile[a + 1][b] = tile[a + 1][b], tile[a][b]
def up(tile):
a, b = find_blank(tile)
if a == 0:
print("Move up not possible")
else:
tile[a][b], tile[a - 1][b] = tile[a - 1][b], tile[a][b]

def right(tile):
a, b = find_blank(tile)
if b == 2:
print("Move right not possible")
else:
tile[a][b], tile[a][b + 1] = tile[a][b + 1], tile[a][b]

def left(tile):
a, b = find_blank(tile)
if b == 0:
print("Move left not possible")
else:
tile[a][b], tile[a][b - 1] = tile[a][b - 1], tile[a][b]

# Input initial matrix


for i in range(3):
for j in range(3):
l[i][j] = int(input(f"Enter value at position {i},{j} (0 for
blank): "))

print("\nInitial 2D List (Matrix):")


for row in l:
print(row)

# Input goal matrix


for i in range(3):
for j in range(3):
m[i][j] = int(input(f"Enter goal value at position {i},{j} (0 for
blank): "))

print("\nFinal 2D List (Matrix):")


for row in m:
print(row)
def heuristic(tile):
count = 0
for i in range(3):
for j in range(3):
if tile[i][j] != m[i][j]:
count += 1
return count

while l != m:
print("\nCurrent State:")
for row in l:
print(row)
current_heuristic = heuristic(l)
print("Heuristic Value:", current_heuristic)

choice = int(input("Enter choice:\n1) Up\n2) Down\n3) Right\n4) Left\


n"))

previous_state = [row[:] for row in l] # Create a deep copy of the


current state

if choice == 1:
up(l)
elif choice == 2:
down(l)
elif choice == 3:
right(l)
elif choice == 4:
left(l)
else:
print("Invalid choice")
continue

new_heuristic = heuristic(l)

if new_heuristic > current_heuristic:


print("increased value ")
l = previous_state
print("Goal state reached!")

Output :

LEARNING
Through this program, we learned how heuristic-based search can efficiently
optimize problem-solving in constraint-based environments. Unlike exhaustive
search methods, Hill Climbing intelligently selects moves that improve the
solution. The experiment highlighted both the strengths (fast convergence) and
limitations (getting stuck in local optima) of local search techniques. Additionally,
we explored strategies like random restarts and simulated annealing, which help
overcome local minima and improve solution quality.
CONCLUSION
The Hill Climbing algorithm provides an effective way to solve the 8 puzzle
problem by iteratively improving tile arrangements based on heuristic evaluation.
This experiment deepened our understanding of heuristic search techniques and
demonstrated how local search methods can be applied to optimization and
constraint satisfaction problems. However, due to its tendency to get trapped in
local optima, better alternatives like A search* or Simulated Annealing may be
preferred for more complex scenarios.
EXPERIMENT 4
Aim: Implementing BFS and DFS
Theory:
Breadth-First Search (BFS) and Depth-First Search (DFS) are
fundamental
algorithms used for traversing graphs.
● BFS:
○ Explores all neighbors at the present depth
before moving to the next level.
○ Uses a queue (FIFO) to track nodes.
○ Guarantees the shortest path in an unweighted graph.
● DFS:
○ Explores a path as deeply as possible
before backtracking.
○ Uses a stack (LIFO) or recursion for traversal.
○ May not always find the shortest path but is
useful for exploring deeper structures.
Program: BFS
#include <iostream>
#include <queue>
using namespace std;

int main() {
int visited[4] = {0}; // Array to track visited nodes
int i = 1; // Starting node

// Adjacency matrix (4x4)


int a[4][4] = {
{0, 1, 1, 0},
{1, 1, 0, 0},
{1, 1, 1, 1},
{0, 1, 1, 0}
};
queue<int> q;
cout << i << " ";
visited[i] = 1;
q.push(i);

while (!q.empty()) {
int node = q.front();
q.pop();

for (int j = 0; j < 4; j++) {


if (a[node][j] == 1 && visited[j] == 0) {
cout << j << " ";
visited[j] = 1;
q.push(j);
}
}
}

return 0;
}

OUTPUT:

DFS:
#include <iostream>
#include <vector>
using namespace std;

void dfs (int node,vector <vector<int>> &adj , vector<bool>&visited )

{ cout<< node <<" ";


visited[node]=true;
for (int neighbour : adj[node]){
if (!visited[neighbour]){
dfs(neighbour,adj,visited);
}
}
}
int main(){
int n =5;
vector<vector<int>>adj(n);
adj[0]={1,2};
adj[1]={0,2};
adj[2]={0,1,3,4};
adj[3]={2};
adj[4]={2};

vector<bool> visited(n,false);

cout <<"dfs traversal ";


dfs(2,adj,visited);
return 0;
}

Output:

LEARNING :
Through this program, we learned how BFS and DFS operate in graph
traversal. BFS explores layer by layer, ensuring the shortest path in
unweighted graphs, while DFS explores deeply first, making it useful for problems
like topological sorting and backtracking.
CONCLUSION : Both BFS and DFS effectively traversed the graph. BFS provided
a level-wise traversal, whereas DFS explored paths deeply before
backtracking. This experiment reinforced our understanding of
fundamental search algorithms and their use in solving graph-related problems.
Experiment -5

Aim :Write a program to implement A* Algorithm.


Theory :
The core of the A* algorithm is based on cost functions and heuristics. It uses two
main parameters:
g(n): The actual cost from the starting node to any node n.
h(n): The heuristic estimated cost from node n to the goal. This is where A*
integrates knowledge beyond the graph to guide the search.
The sum,
f(n)=g(n)+h(n)
f(n)=g(n)+h(n), represents the total estimated cost of the cheapest solution
through nnn. The A* algorithm functions by maintaining a priority queue (or open
set) of all possible paths along the graph, prioritizing them based on their fff
values. The steps of the algorithm are as follows:

Initialization: Start by adding the initial node to the open set with its f(n).
Loop: While the open set is not empty, the node with the lowest f(n) value is
removed from the queue.
Goal Check: If this node is the goal, the algorithm terminates and returns the
discovered path.
Node Expansion: Otherwise, expand the node (find all its neighbors),
calculating g, h, and f values for each neighbor. Add each neighbor to the open
set if it's not already present, or if a better path to this neighbor is found.
Repeat: The loop repeats until the goal is reached or if there are no more nodes
in the open set, indicating no available path.
Program :
OUTPUT :

Learning
The A* (A-star) algorithm is a powerful and widely used pathfinding technique
that teaches several core principles in artificial intelligence and algorithm design.
It combines two key components: the actual cost from the start node to the
current node (g(n)) and a heuristic estimate of the cost from the current node to
the goal (h(n)). This combination allows A* to efficiently find the shortest path
between two points on a graph or grid.
Optimization : A* is highly optimized compared to uninformed search algorithms
Speed vs. Accuracy: A* smartly trades off between speed and accuracy using f(n)
= g(n) + h(n).
Heuristic Quality: A good heuristic drastically reduces time and memory by
exploring fewer nodes.

Conclusion :
The A* algorithm stands out as a powerful and intelligent approach to
pathfinding and graph traversal.While A* is powerful, it’s not without its
limitations. The memory consumption can be significant, as it needs to maintain
all explored and unexplored nodes in memory. Furthermore, the choice of
heuristic heavily influences the algorithm's performance; a poor heuristic can
lead to inefficient exploration and increased computation.
Experiment -6

Aim :Write a program to implement AO*


Algorithm. Theory :
The AO algorithm* (short for “And-Or Star”) is a powerful best-first search
method used to solve problems that can be represented as a directed acyclic
graph (DAG). Node Types
AND Nodes: Represent states where all child nodes must be satisfied to achieve a
goal. If a task requires multiple conditions to be met, it would be represented as
an AND node.
OR Nodes: Represent states where at least one child node must be satisfied to
achieve a goal. This type is useful in scenarios where multiple paths can lead to a
solution.
Search Process :
The search begins at the initial node and explores its child nodes based on the
type (AND or OR). The costs associated with nodes are calculated using the
following principles:
For OR Nodes: The algorithm considers the lowest cost among the child nodes.
The cost for an OR node can be expressed as:
C(n)=min{C(c1),C(c2),…,C(ck)}
C(n)=min{C(c 1 ),C(c 2),…,C(c k )}where C(n)C(n) is the cost of node nand c1,c2,
…,ck c1,c 2 ,…,c k are the child nodes of n
For AND Nodes: The algorithm computes the cost of all child nodes and selects
the maximum cost, as all conditions must be met. The cost for an AND node can
be expressed as:
C(n)=max{C(c1),C(c2),…,C(ck)}C(n)=max{C(c 1 ),C(c 2 ),…,C(c k )}where
C(n)C(n) is the cost of node nn, and c1,c2,…,ckc 1 ,c 2 ,…,c k are the child nodes
of n.
Total Estimated Cost
The total estimated cost
f(n)f(n) at any node n is given by:f(n)=C(n)+h(n)f(n)=C(n)+h(n)where:
C(n)C(n) is the actual cost to reach node n
n from the start node.h(n).h(n) is the estimated cost from node n to the goal.

Program:
Output :

Learning :
The AO* algorithm teaches how to solve problems using AND-OR graphs, where
solutions may require fulfilling multiple conditions (AND) or choosing among
alternatives (OR). Unlike traditional pathfinding, AO* works on goal trees .it
efficiently navigates such graphs using heuristics to guide the search toward the
most promising solution, avoiding exhaustive exploration. By recursively
evaluating subproblems and selectively expanding only the most relevant paths,
AO* emphasizes optimal decision-making in structured problem spaces. It
teaches the importance of heuristic design, optimal substructure, and intelligent
pruning, offering insights into building goal-driven, cost-effective, and scalable AI
systems.

Optimization : The AO* algorithm is considered optimal because it guarantees


the least-cost solution in an AND-OR graph, provided that the heuristic used is
admissible (i.e., it never overestimates the actual cost to reach the goal).

Conclusion :
In conclusion, the AO* algorithm stands out as an efficient and intelligent
approach for solving problems in AND-OR graphs . Its ability to handle AND and
OR nodes allows for flexibility and efficiency in various applications, from gaming
to robotics and natural language processing.

You might also like