BSc.
CSIT Artificial Intelligence Sanam Timilsina
LAB:2
1. WAP to implement BFS for a graph.
Source Code:
From collections import deque
class Graph:
def __init__(self):
self.graph = {}
def add_edge(self, node, neighbors):
self.graph[node] = neighbors
def bfs(self, start_node):
visited = set()
queue = deque([start_node])
while queue:
current_node = queue.popleft()
if current_node not in visited:
print(current_node, end=' ')
visited.add(current_node)
neighbors = self.graph[current_node]
for neighbor in neighbors:
if neighbor not in visited:
queue.append(neighbor)
# Example usage:
# Create a graph object
graph = Graph()
# Add edges to the graph
graph.add_edge('A', ['B', 'C'])
graph.add_edge('B', ['D', 'E'])
graph.add_edge('C', ['F'])
graph.add_edge('D', [])
graph.add_edge('E', [])
graph.add_edge('F', [])
# Perform BFS starting from node 'A'
start_node = 'A'
print("BFS traversal starting from node", start_node)
graph.bfs(start_node)
1 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
Output:
Graph:
2. WAP to implement Uniform-cost search.
Source Code:
import heapq
class Graph:
def __init__(self, graph_dict):
self.graph_dict = graph_dict
def uniform_cost_search(self, start_node, goal_node):
priority_queue = [(0, start_node)]
visited = set()
while priority_queue:
cost, current_node = heapq.heappop(priority_queue)
if current_node == goal_node:
return cost
visited.add(current_node)
if current_node not in self.graph_dict:
continue
for neighbor, edge_cost in
self.graph_dict[current_node].items():
if neighbor not in visited:
heapq.heappush(priority_queue, (cost + edge_cost,
neighbor))
2 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
return -1 # Goal node not found
# Example usage:
# Define the graph as a dictionary of dictionaries
graph_dict = {
'A': {'B': 5, 'C': 3},
'B': {'D': 2, 'E': 4},
'C': {'F': 6},
'D': {'G': 1},
'E': {'G': 3},
'F': {'H': 5},
'G': {'H': 2},
'H': {}
}
# Create the graph object
graph = Graph(graph_dict)
# Perform Uniform-Cost Search from node 'A' to node 'H'
start_node = 'A'
goal_node = 'H'
cost = graph.uniform_cost_search(start_node, goal_node)
if cost != -1:
print(f"The cost of the path from {start_node} to {goal_node} is:
{cost}")
else:
print(f"No path found from {start_node} to {goal_node}")
Output:
Graph:
3 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
3. WAP to implement DFS for a graph.
Source Code:
class Graph:
def __init__(self, graph_dict):
self.graph_dict = graph_dict
def DFS(self, start_node):
visited = set()
self._recursive_dfs(start_node, visited)
def _recursive_dfs(self, current_node, visited):
print(current_node) # Print the current node
visited.add(current_node)
if current_node not in self.graph_dict:
return
for neighbor in self.graph_dict[current_node]:
if neighbor not in visited:
self._recursive_dfs(neighbor, visited)
# Example usage:
# Define the graph as a dictionary
graph_dict = {
'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F'],
'D': [],
'E': [],
'F': ['G'],
'G': ['H'],
'H': []
}
# Create the graph object
graph = Graph(graph_dict)
# Perform Depth-First Search starting from node 'A'
graph.DFS('A')
4 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
Output:
Graph:
4. WAP to implement Depth limited search for a graph.
Source Code
class Graph:
def __init__(self, graph_dict):
self.graph_dict = graph_dict
def DLS(self, start_node, goal_node, depth_limit):
return self._recursive_dls(start_node, goal_node, depth_limit)
def _recursive_dls(self, current_node, goal_node, depth_limit):
if current_node == goal_node:
return True
if depth_limit <= 0:
return False
if current_node not in self.graph_dict:
return False
for neighbor in self.graph_dict[current_node]:
5 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
if self._recursive_dls(neighbor, goal_node, depth_limit -
1):
return True
return False
# Example usage:
# Define the graph as a dictionary
graph_dict = {
'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F'],
'D': [],
'E': [],
'F': ['G'],
'G': ['H'],
'H': []
}
# Create the graph object
graph = Graph(graph_dict)
# Define the start node and the goal node
start_node = 'A'
goal_node = 'H'
# Define the depth limit
depth_limit = 3
# Perform Depth-Limited Search
result = graph.DLS(start_node, goal_node, depth_limit)
# Print the result
if result:
print(f"Goal node '{goal_node}' is reachable from start node
'{start_node}' within depth limit {depth_limit}")
else:
print(f"Goal node '{goal_node}' is not reachable from start node
'{start_node}' within depth limit {depth_limit}")
Output:
6 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
Graph:
5. WAP to implement greedy best first search.
Source Code:
import heapq
class Node:
def __init__(self, state, parent=None, heuristic=0):
self.state = state
self.parent = parent
self.heuristic = heuristic
def __lt__(self, other):
return self.heuristic < other.heuristic
def greedy_best_first_search(initial_state, goal_state, heuristic,
successors):
open_list = []
closed_set = set()
start_node = Node(initial_state, None, heuristic(initial_state))
heapq.heappush(open_list, start_node)
while open_list:
current_node = heapq.heappop(open_list)
if current_node.state == goal_state:
return get_path(current_node)
closed_set.add(current_node.state)
for successor in successors(current_node.state):
if successor in closed_set:
continue
7 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
successor_node = Node(successor, current_node,
heuristic(successor))
if is_node_in_open_list(successor_node, open_list):
continue
heapq.heappush(open_list, successor_node)
return None
def get_path(node):
path = []
current = node
while current:
path.append(current.state)
current = current.parent
path.reverse()
return path
def is_node_in_open_list(node, open_list):
for open_node in open_list:
if open_node.state == node.state:
return True
return False
# Example usage:
# Define the initial state, goal state, and the heuristic function
initial_state = 'A'
goal_state = 'H'
def heuristic(state):
# Define a heuristic function that estimates the cost from the
current state to the goal state
# In this example, we use a simple dictionary to store the heuristic
values for each state
heuristic_values = {
'A': 8,
'B': 6,
'C': 16,
'D': 1,
'E': 2,
'F': 3,
'G': 1,
'H': 0,
}
8 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
return heuristic_values[state]
# Define the successors function
def successors(state):
# Define a successors function that returns the possible successors
of the given state
# In this example, we use a simple dictionary to store the
successors for each state
successors = {
'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F'],
'D': ['G'],
'E': ['G'],
'F': ['H'],
'G': ['H'],
'H': [],
}
return successors[state]
# Solve the problem using Greedy Best-First Search
path = greedy_best_first_search(initial_state, goal_state, heuristic,
successors)
# Print the path
if path:
print("Path found:", path)
else:
print("No path found")
Output:
Graph:
9 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
6. WAP to implement A* search.
Source Code:
import heapq
class Node:
def __init__(self, state, parent=None, g=0, h=0):
self.state = state
self.parent = parent
self.g = g # cost from start node to current node
self.h = h # heuristic cost from current node to goal node
self.f = g + h # total cost (f = g + h)
def __lt__(self, other):
return self.f < other.f
def astar_search(initial_state, goal_state, heuristic, successors):
open_list = []
closed_set = set()
start_node = Node(initial_state)
heapq.heappush(open_list, start_node)
while open_list:
current_node = heapq.heappop(open_list)
if current_node.state == goal_state:
return get_path(current_node)
closed_set.add(current_node.state)
for successor, cost in successors(current_node.state):
if successor in closed_set:
continue
g = current_node.g + cost
h = heuristic(successor, goal_state)
f = g + h
successor_node = Node(successor, current_node, g, h)
if is_node_in_open_list(successor_node, open_list):
continue
heapq.heappush(open_list, successor_node)
10 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
return None
def get_path(node):
path = []
current = node
while current:
path.append(current.state)
current = current.parent
path.reverse()
return path
def is_node_in_open_list(node, open_list):
for open_node in open_list:
if open_node.state == node.state and open_node.f <= node.f:
return True
return False
# Example usage:
# Define the initial state, goal state, and the heuristic function
initial_state = 'A'
goal_state = 'H'
def heuristic(state, goal_state):
# Define a heuristic function that estimates the cost from the
current state to the goal state
# In this example, we use a simple dictionary to store the heuristic
values for each state
heuristic_values = {
'A': 8,
'B': 6,
'C': 4,
'D': 3,
'E': 2,
'F': 3,
'G': 1,
'H': 0,
}
return heuristic_values[state]
# Define the successors function
def successors(state):
# Define a successors function that returns the possible successors
of the given state and their costs
# In this example, we use a simple dictionary to store the
successors for each state along with their costs
successors = {
11 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
'A': [('B', 4), ('C', 2)],
'B': [('D', 5), ('E', 10)],
'C': [('F', 3)],
'D': [('G', 6)],
'E': [('G', 8)],
'F': [('H', 3)],
'G': [('H', 2)],
'H': [],
}
return successors[state]
# Solve the problem using A* search
path = astar_search(initial_state, goal_state, heuristic, successors)
# Print the path
if path:
print("Path found:", path)
else:
print("No path found")
Output:
Graph:
12 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
7. WAP to implement Hill Climbing (Steepest Ascent) Search.
Source Code:
def hill_climbing(problem, heuristic):
current_state = problem.get_initial_state()
while True:
neighbors = problem.get_neighbors(current_state)
best_neighbor = None
best_neighbor_cost = float('-inf')
for neighbor in neighbors:
neighbor_cost = heuristic(neighbor)
if neighbor_cost > best_neighbor_cost:
best_neighbor = neighbor
best_neighbor_cost = neighbor_cost
if best_neighbor_cost <= heuristic(current_state):
break
current_state = best_neighbor
return current_state
# Example usage:
# Define the problem class
class Problem:
def __init__(self):
self.initial_state = 3
def get_initial_state(self):
return self.initial_state
def get_neighbors(self, state):
return [state + 1, state - 1]
# Define the heuristic function
def heuristic(state):
return -abs(state - 5) # Negative distance from the goal state
# Create an instance of the problem
problem = Problem()
# Solve the problem using Hill Climbing (Steepest Ascent)
13 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
solution = hill_climbing(problem, heuristic)
# Print the solution
print("Solution:", solution)
Output:
Graph:
8. WAP to solve any one Cryptarithmetic Problem (like TWO +TWO = FOUR or SEND
+MORE = MONEY ). (Recommended Language: Python)
Source Code:
from itertools import permutations
def solve_cryptarithmetic(puzzle):
unique_letters = set(char for word in puzzle for char in word if
char.isalpha())
letters = list(unique_letters)
num_letters = len(letters)
digits = range(10)
14 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
for perm in permutations(digits, num_letters):
mapping = dict(zip(letters, perm))
if all(mapping[word[0]] != 0 for word in puzzle):
values = [int(''.join(str(mapping[char]) for char in
word)) for word in puzzle]
if values[0] + values[1] == values[2]:
return mapping
return None
# Example usage:
puzzle = ["TWO", "TWO", "FOUR"]
solution = solve_cryptarithmetic(puzzle)
if solution is not None:
print("Solution found:")
for word in puzzle:
print(''.join(str(solution[char]) for char in word), end=' ')
else:
print("No solution found.")
Output:
9. Implementing Frame (Recommended Language: C++ / use object pointers)
"Ram is a person living in Nepal. He was born on 15th December of year 1990. He is 6
inch tall and has 75 kg weight. He has a job. He works at 'ABC company' as AI
Researcher and earns 1.5 lakhs per month. The company is situated at Kathmandu."
Represent above information in frames diagrammatically and also implement it using the
concepts of class in C+.
Source Code:
#include <iostream>
#include <string>
class Person {
15 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
private:
std::string name;
std::string country;
std::string dateOfBirth;
int height;
int weight;
public:
Person(std::string n, std::string c, std::string dob, int h, int
w)
: name(n), country(c), dateOfBirth(dob), height(h), weight(w)
{}
void displayInformation() {
std::cout << "+------------------------------------------+"
<< std::endl;
std::cout << "| Person |"
<< std::endl;
std::cout << "|------------------------------------------|"
<< std::endl;
std::cout << "| Name: " << name << std::endl;
std::cout << "| Country: " << country << std::endl;
std::cout << "| Date of Birth: " << dateOfBirth << std::endl;
std::cout << "| Height: " << height << " inch" <<
std::endl;
std::cout << "| Weight: " << weight << " kg" <<
std::endl;
std::cout << "+------------------------------------------+"
<< std::endl;
}
};
class Employee {
private:
std::string occupation;
double salary;
std::string company;
std::string location;
public:
Employee(std::string occ, double sal, std::string comp,
std::string loc)
: occupation(occ), salary(sal), company(comp), location(loc)
{}
void displayInformation() {
16 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
std::cout << "+------------------------------------------+"
<< std::endl;
std::cout << "| Employee |"
<< std::endl;
std::cout << "|------------------------------------------|"
<< std::endl;
std::cout << "| Occupation: " << occupation << std::endl;
std::cout << "| Salary: " << salary << " lakhs per
month" << std::endl;
std::cout << "| Company: " << company << std::endl;
std::cout << "| Location: " << location << std::endl;
std::cout << "+------------------------------------------+"
<< std::endl;
}
};
int main() {
Person* ram = new Person("Ram", "Nepal", "15th December 1990", 6,
75);
Employee* ramJob = new Employee("AI Researcher", 1.5, "ABC
company", "Kathmandu");
ram->displayInformation();
ramJob->displayInformation();
delete ram;
delete ramJob;
return 0;
}
Output:
17 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
10. Implementing Frame (Recommended Language: C++ / use object pointers)
"Ram is a person living in Nepal. He was born on 15th December of year 1990. He is 6
inch tall and has 75 kg weight. He has a job. He works at 'ABC company' as AI
Researcher and earns 1.5 lakhs per month. The company is situated at Kathmandu."
Represent above information in frames diagrammatically and also implement it using the
concepts of class in C++.
Source Code:
#include <iostream>
#include <string>
using namespace std;
class Person {
private:
string name;
string country;
string dateOfBirth;
int height;
int weight;
18 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
public:
Person(string n, string c, string dob, int h, int w)
: name(n), country(c), dateOfBirth(dob), height(h), weight(w)
{}
void displayInformation() {
cout << "+------------------------------------------+" <<
endl;
cout << "| Person |" <<
endl;
cout << "|------------------------------------------|" <<
endl;
cout << "| Name: " << name << endl;
cout << "| Country: " << country << endl;
cout << "| Date of Birth: " << dateOfBirth << endl;
cout << "| Height: " << height << " inch" << endl;
cout << "| Weight: " << weight << " kg" << endl;
cout << "+------------------------------------------+" <<
endl;
}
};
class Employee {
private:
string occupation;
double salary;
string company;
string location;
public:
Employee(string occ, double sal, string comp, string loc)
: occupation(occ), salary(sal), company(comp), location(loc)
{}
void displayInformation() {
cout << "+------------------------------------------+" <<
endl;
cout << "| Employee |" <<
endl;
cout << "|------------------------------------------|" <<
endl;
cout << "| Occupation: " << occupation << endl;
cout << "| Salary: " << salary << " lakhs per month"
<< endl;
cout << "| Company: " << company << endl;
cout << "| Location: " << location << endl;
19 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
cout << "+------------------------------------------+" <<
endl;
}
};
int main() {
Person ram("Ram", "Nepal", "15th December 1990", 6, 75);
Employee ramJob("AI Researcher", 1.5, "ABC company",
"Kathmandu");
ram.displayInformation();
ramJob.displayInformation();
return 0;
}
Output:
11. WAP to develop a sample medical expert system capable of diagnosing disease based on
the provided symptoms.
20 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
Source Code:
class Disease:
def __init__(self, name, symptoms):
self.name = name
self.symptoms = symptoms
class MedicalExpertSystem:
def __init__(self):
self.diseases = []
def add_disease(self, name, symptoms):
disease = Disease(name, symptoms)
self.diseases.append(disease)
def diagnose(self, input_symptoms):
matched_diseases = []
for disease in self.diseases:
if set(disease.symptoms).issubset(input_symptoms):
matched_diseases.append(disease)
if len(matched_diseases) == 0:
print("No matching disease found.")
else:
print("Possible diseases:")
for disease in matched_diseases:
print("- ", disease.name)
# Create a medical expert system
expert_system = MedicalExpertSystem()
# Add diseases and their symptoms
expert_system.add_disease("Common Cold", ["sore throat", "runny
nose", "cough"])
expert_system.add_disease("Flu", ["fever", "headache", "muscle
pain"])
expert_system.add_disease("Allergies", ["sneezing", "itchy eyes",
"rash"])
# Get user input for symptoms
user_input = input("Enter the symptoms (comma-separated): ")
input_symptoms = user_input.split(", ")
# Diagnose the disease based on symptoms
expert_system.diagnose(input_symptoms)
21 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
12. Realization of AND, OR and NOT gates using Artificial Neurons.
Source Code:
import numpy as np
# Activation function (step function)
def step_function(x):
return 1 if x >= 0 else 0
# AND gate
def AND_gate(x1, x2):
# Weight and bias values for the AND gate
weights = np.array([0.5, 0.5])
bias = -0.7
# Calculate the weighted sum
weighted_sum = np.dot([x1, x2], weights) + bias
# Apply the activation function
output = step_function(weighted_sum)
return output
# OR gate
def OR_gate(x1, x2):
# Weight and bias values for the OR gate
weights = np.array([0.5, 0.5])
bias = -0.2
# Calculate the weighted sum
weighted_sum = np.dot([x1, x2], weights) + bias
# Apply the activation function
output = step_function(weighted_sum)
return output
# NOT gate
def NOT_gate(x):
# Weight and bias values for the NOT gate
weight = -0.5
bias = 0.2
# Calculate the weighted sum
weighted_sum = x * weight + bias
# Apply the activation function
output = step_function(weighted_sum)
22 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
return output
# Test the gates
print("AND gate:")
print("0 AND 0 =", AND_gate(0, 0))
print("0 AND 1 =", AND_gate(0, 1))
print("1 AND 0 =", AND_gate(1, 0))
print("1 AND 1 =", AND_gate(1, 1))
print("\nOR gate:")
print("0 OR 0 =", OR_gate(0, 0))
print("0 OR 1 =", OR_gate(0, 1))
print("1 OR 0 =", OR_gate(1, 0))
print("1 OR 1 =", OR_gate(1, 1))
print("\nNOT gate:")
print("NOT 0 =", NOT_gate(0))
print("NOT 1 =", NOT_gate(1))
Output:
23 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
13. A Simple Example of Back Propagation Learning.
Source Code:
import numpy as np
# Define the sigmoid activation function
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# Define the derivative of the sigmoid function
def sigmoid_derivative(x):
return x * (1 - x)
# Define the neural network class
class NeuralNetwork:
def __init__(self, input_size, hidden_size, output_size):
self.input_size = input_size
self.hidden_size = hidden_size
self.output_size = output_size
# Initialize the weights with random values
self.weights1 = np.random.randn(self.input_size,
self.hidden_size)
self.weights2 = np.random.randn(self.hidden_size,
self.output_size)
def forward(self, X):
# Perform forward propagation
self.hidden_layer = sigmoid(np.dot(X, self.weights1))
self.output_layer = sigmoid(np.dot(self.hidden_layer,
self.weights2))
return self.output_layer
def backward(self, X, y, learning_rate):
# Perform backward propagation and update the weights
output_error = y - self.output_layer
output_delta = output_error *
sigmoid_derivative(self.output_layer)
hidden_error = np.dot(output_delta, self.weights2.T)
hidden_delta = hidden_error *
sigmoid_derivative(self.hidden_layer)
self.weights2 += learning_rate * np.dot(self.hidden_layer.T,
output_delta)
self.weights1 += learning_rate * np.dot(X.T, hidden_delta)
24 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
def train(self, X, y, epochs, learning_rate):
for epoch in range(epochs):
output = self.forward(X)
self.backward(X, y, learning_rate)
# Example usage:
# Define the training dataset
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])
# Create a neural network with 2 input neurons, 2 hidden neurons, and
1 output neuron
network = NeuralNetwork(2, 2, 1)
# Train the network for 10000 epochs with a learning rate of 0.1
network.train(X, y, epochs=10000, learning_rate=0.1)
# Test the trained network
print("Predictions:")
for i in range(len(X)):
prediction = network.forward(X[i])
print(f"Input: {X[i]}, Output: {prediction}")
Output:
14. WAP to solve the N Queen Problem. (Problem: To find an arrangement of N queens on a
chess board of size N×N, such that no queen can attack any other queens on the board)
Source Code:
def is_safe(board, row, col, N):
# Check if there is a queen in the same column
for i in range(row):
if board[i][col] == 1:
25 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
return False
# Check if there is a queen in the upper left diagonal
i, j = row, col
while i >= 0 and j >= 0:
if board[i][j] == 1:
return False
i -= 1
j -= 1
# Check if there is a queen in the upper right diagonal
i, j = row, col
while i >= 0 and j < N:
if board[i][j] == 1:
return False
i -= 1
j += 1
return True
def solve_n_queen(board, row, N):
if row == N:
# Print the solution
for i in range(N):
for j in range(N):
print(board[i][j], end=" ")
print()
print()
return
for col in range(N):
if is_safe(board, row, col, N):
board[row][col] = 1
solve_n_queen(board, row + 1, N)
board[row][col] = 0
def n_queen(N):
board = [[0 for _ in range(N)] for _ in range(N)]
solve_n_queen(board, 0, N)
# Test the program
n_queen(4) # Solve 4-queen problem
Output:
26 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
15. WAP to solve Water Jug Problem.(Problem statement: Given two jugs, a 4-gallon and 3-
gallon having no measuring markers on them. There is a pump that can be used to fill the
jugs with water and the water can be poured on the ground. How can you get exactly 2
gallons of water into 4-gallon jug?
Source Code:
from collections import deque
def water_jug_problem(target):
queue = deque([(0, 0)])
visited = set()
while queue:
jug_state = queue.popleft()
jug1 = jug_state[0]
jug2 = jug_state[1]
if jug1 == target:
return jug_state
if jug_state in visited:
continue
visited.add(jug_state)
# Fill jug1
queue.append((4, jug2))
# Fill jug2
queue.append((jug1, 3))
# Empty jug1
queue.append((0, jug2))
# Empty jug2
27 93/077
BSc.CSIT Artificial Intelligence Sanam Timilsina
queue.append((jug1, 0))
# Pour jug1 to jug2
amount = min(jug1, 3 - jug2)
queue.append((jug1 - amount, jug2 + amount))
# Pour jug2 to jug1
amount = min(jug2, 4 - jug1)
queue.append((jug1 + amount, jug2 - amount))
return None
# Solve the Water Jug Problem to get exactly 2 gallons in the 4-
gallon jug
target_amount = 2
result = water_jug_problem(target_amount)
if result:
print(f"Solution found: {result[0]} gallons in the 4-gallon jug
and {result[1]} gallons in the 3-gallon jug.")
else:
print("Solution not found.")
Output:
28 93/077