0% found this document useful (0 votes)
3 views31 pages

PDF_GO_AI Lab program

The document contains various Python implementations of classic artificial intelligence problems, including the 8-puzzle, 8-Queens, cryptarithmetic puzzles, A* search algorithms, Alpha-Beta pruning, and constraint satisfaction problems. Each section provides code examples and explanations for solving these problems using different algorithms and techniques. It serves as a comprehensive lab manual for students studying artificial intelligence.

Uploaded by

rathinavelan777
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)
3 views31 pages

PDF_GO_AI Lab program

The document contains various Python implementations of classic artificial intelligence problems, including the 8-puzzle, 8-Queens, cryptarithmetic puzzles, A* search algorithms, Alpha-Beta pruning, and constraint satisfaction problems. Each section provides code examples and explanations for solving these problems using different algorithms and techniques. It serves as a comprehensive lab manual for students studying artificial intelligence.

Uploaded by

rathinavelan777
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

AAI301 ARTIFICIAL INTELLIGENCE LAB MANUAL

YEAR/SEM: II/III REGULATION: 2024

1.a

Python program that implements the classic 8-puzzle (sliding puzzle) problem.
This version includes:

 A representation of the puzzle as a 3x3 grid


 A function to shuffle the puzzle
 A function to make a move (up, down, left, right)
 A simple command-line loop to play the puzzle

import random

class EightPuzzle:

def __init__(self):

self.goal_state = [[1, 2, 3],

[4, 5, 6],

[7, 8, 0]] # 0 represents the empty space

[Link] = [row[:] for row in self.goal_state]

[Link]()

def shuffle(self):

# Perform random moves to shuffle the puzzle


moves = ['up', 'down', 'left', 'right']

for _ in range(100):

move = [Link](moves)

self.make_move(move)

def find_empty(self):

for i in range(3):

for j in range(3):

if [Link][i][j] == 0:

return i, j

def make_move(self, direction):

i, j = self.find_empty()

if direction == 'up' and i < 2:

[Link][i][j], [Link][i+1][j] = [Link][i+1][j], [Link][i][j]

elif direction == 'down' and i > 0:

[Link][i][j], [Link][i-1][j] = [Link][i-1][j], [Link][i][j]

elif direction == 'left' and j < 2:

[Link][i][j], [Link][i][j+1] = [Link][i][j+1], [Link][i][j]

elif direction == 'right' and j > 0:

[Link][i][j], [Link][i][j-1] = [Link][i][j-1], [Link][i][j]

else:

print("Invalid move!")

def is_solved(self):

return [Link] == self.goal_state


def display(self):

for row in [Link]:

print(' '.join(str(n) if n != 0 else ' ' for n in row))

print()

def main():

puzzle = EightPuzzle()

print("Welcome to the 8-Puzzle Game!")

print("Goal state:")

for row in puzzle.goal_state:

print(row)

print("\nCurrent state:")

[Link]()

while not puzzle.is_solved():

move = input("Enter your move (up/down/left/right): ").lower()

puzzle.make_move(move)

[Link]()

if puzzle.is_solved():

print("Congratulations! You solved the puzzle.")

if __name__ == "__main__":

main()

1.b

8-Queens problem is a classic chess puzzle:


Place 8 queens on a chessboard so that no two queens attack each other.

Here’s a simple Python program that solves the 8-Queens problem using backtracking:
N=8

def print_solution(board):

for row in board:

line = ""

for col in row:

line += "Q " if col else ". "

print(line)

print("\n")

def is_safe(board, row, col):

# Check left side of the row

for i in range(col):

if board[row][i]:

return False

# Check upper diagonal on left side

for i, j in zip(range(row, -1, -1), range(col, -1, -1)):

if board[i][j]:

return False

# Check lower diagonal on left side

for i, j in zip(range(row, N, 1), range(col, -1, -1)):

if board[i][j]:

return False
return True

def solve_nq_util(board, col):

# If all queens are placed, print the solution

if col >= N:

print_solution(board)

return True

res = False

for i in range(N):

if is_safe(board, i, col):

board[i][col] = 1

res = solve_nq_util(board, col + 1) or res

board[i][col] = 0 # BACKTRACK

return res

def solve_nq():

board = [[0 for _ in range(N)] for _ in range(N)]

if not solve_nq_util(board, 0):

print("No solution exists.")

return False

return True
if __name__ == "__main__":

solve_nq()

1.c

cryptarithmetic puzzle solver in Python.

import itertools

def solve():

# Unique letters in the puzzle

letters = 'SENDMORY'

# Possible digits (0-9)

digits = range(10)

# The first letters of SEND, MORE, MONEY must not be zero

for perm in [Link](digits, len(letters)):

s, e, n, d, m, o, r, y = perm

if s == 0 or m == 0:

continue # SEND and MORE must not start with zero

send = s * 1000 + e * 100 + n * 10 + d

more = m * 1000 + o * 100 + r * 10 + e

money = m * 10000 + o * 1000 + n * 100 + e * 10 + y

if send + more == money:

print(f"SOLUTION FOUND:")
print(f" {send} + {more} = {money}")

print(f"Mapping: S={s}, E={e}, N={n}, D={d}, M={m}, O={o}, R={r}, Y={y}")

return

print("No solution found.")

if __name__ == "__main__":

solve()

2.a

Python implementation of the A* search algorithm

from queue import PriorityQueue

class Graph:

def __init__(self):

[Link] = {}

self.h = {} # Heuristic values

def add_edge(self, from_node, to_node, cost):

if from_node in [Link]:

[Link][from_node].append((to_node, cost))

else:

[Link][from_node] = [(to_node, cost)]

def set_heuristic(self, node, h_value):

self.h[node] = h_value
def a_star_search(self, start, goal):

open_set = PriorityQueue()

open_set.put((0, start))

came_from = {}

g_score = {start: 0}

while not open_set.empty():

_, current = open_set.get()

if current == goal:

return self.reconstruct_path(came_from, current)

for neighbor, cost in [Link](current, []):

tentative_g_score = g_score[current] + cost

if neighbor not in g_score or tentative_g_score < g_score[neighbor]:

came_from[neighbor] = current

g_score[neighbor] = tentative_g_score

f_score = tentative_g_score + [Link](neighbor, float('inf'))

open_set.put((f_score, neighbor))

return None

def reconstruct_path(self, came_from, current):

path = [current]

while current in came_from:

current = came_from[current]

[Link](current)
[Link]()

return path

def main():

graph = Graph()

# Example graph edges

graph.add_edge('A', 'B', 1)

graph.add_edge('A', 'C', 3)

graph.add_edge('B', 'D', 1)

graph.add_edge('C', 'D', 1)

graph.add_edge('B', 'E', 6)

graph.add_edge('D', 'E', 1)

# Example heuristic values (straight-line estimates)

graph.set_heuristic('A', 4)

graph.set_heuristic('B', 2)

graph.set_heuristic('C', 2)

graph.set_heuristic('D', 1)

graph.set_heuristic('E', 0) # Goal

start = 'A'

goal = 'E'

path = graph.a_star_search(start, goal)

if path:

print(f"Path from {start} to {goal}: {' -> '.join(path)}")


else:

print("No path found.")

if __name__ == "__main__":

main()

2.b

Python implementation of memory bounded A* search algorithm

a Memory Bounded A* algorithm is an enhancement of standard A* to limit memory usage.

One classic version is Iterative Deepening A* (IDA*), which combines the space efficiency
of DFS with the heuristic of A*. Another is Simplified Memory-Bounded A* (SMA*).

👉 IDA* is simpler to implement and commonly used in puzzles like the 8-puzzle or
pathfinding.
👉 SMA* is more advanced, but the core idea is that when memory is full, you discard the
worst nodes.

import copy

goal_state = [

[1, 2, 3],

[4, 5, 6],

[7, 8, 0] # 0 is the blank

MOVES = [(-1,0),(1,0),(0,-1),(0,1)] # Up, Down, Left, Right

def manhattan(state):

distance = 0

for i in range(3):

for j in range(3):
val = state[i][j]

if val != 0:

goal_x = (val - 1) // 3

goal_y = (val - 1) % 3

distance += abs(i - goal_x) + abs(j - goal_y)

return distance

def find_blank(state):

for i in range(3):

for j in range(3):

if state[i][j] == 0:

return i, j

def neighbors(state):

x, y = find_blank(state)

results = []

for dx, dy in MOVES:

nx, ny = x + dx, y + dy

if 0 <= nx < 3 and 0 <= ny < 3:

new_state = [Link](state)

new_state[x][y], new_state[nx][ny] = new_state[nx][ny], new_state[x][y]

[Link](new_state)

return results

def ida_star(start):

threshold = manhattan(start)

path = [start]
while True:

temp = search(path, 0, threshold)

if temp == True:

return path

if temp == float('inf'):

return None

threshold = temp

def search(path, g, threshold):

node = path[-1]

f = g + manhattan(node)

if f > threshold:

return f

if node == goal_state:

return True

minimum = float('inf')

for succ in neighbors(node):

if succ not in path: # avoid cycles

[Link](succ)

temp = search(path, g + 1, threshold)

if temp == True:

return True

if temp < minimum:

minimum = temp

[Link]()

return minimum
def print_state(state):

for row in state:

print(" ".join(str(val) if val != 0 else " " for val in row))

print()

def main():

# Example scrambled state (you can change this!)

start_state = [

[2, 8, 3],

[1, 6, 4],

[7, 0, 5]

print("Start state:")

print_state(start_state)

print("Solving with IDA*...")

solution = ida_star(start_state)

if solution:

print(f"Solution found in {len(solution) - 1} moves:")

for state in solution:

print_state(state)

else:

print("No solution found.")

if __name__ == "__main__":
main()

3. Alpha – Beta pruning

Alpha-Beta Pruning is a classic improvement over the Minimax algorithm used in game tree
searches (e.g., for Tic-Tac-Toe, Chess, etc.).
It prunes branches that don’t need to be evaluated, saving time without affecting the result.

Here’s a simple Python program that implements Minimax with Alpha-Beta Pruning for a
simple game tree.
I’ll show it with a small example (like a hypothetical game tree), so you can understand the
core logic.

# Simple example of Minimax with Alpha-Beta Pruning

def minimax(depth, node_index, maximizing_player, values, alpha, beta):

# Base case: leaf node

if depth == 3:

return values[node_index]

if maximizing_player:

best = float('-inf')

# Left child

val = minimax(depth + 1, node_index * 2, False, values, alpha, beta)

best = max(best, val)

alpha = max(alpha, best)

# Right child

val = minimax(depth + 1, node_index * 2 + 1, False, values, alpha, beta)

best = max(best, val)


alpha = max(alpha, best)

if beta <= alpha:

# Prune

return best

return best

else:

best = float('inf')

# Left child

val = minimax(depth + 1, node_index * 2, True, values, alpha, beta)

best = min(best, val)

beta = min(beta, best)

# Right child

val = minimax(depth + 1, node_index * 2 + 1, True, values, alpha, beta)

best = min(best, val)

beta = min(beta, best)

if beta <= alpha:

# Prune

return best

return best
def main():

# Example game tree: leaf nodes with static scores

values = [3, 5, 6, 9, 1, 2, 0, -1]

print("Optimal value:", minimax(0, 0, True, values, float('-inf'), float('inf')))

if __name__ == "__main__":

main()

4. Constraint Satisfaction Problem

Constraint Satisfaction Problem (CSP) with Python.


CSPs are a core part of AI — classic examples include map coloring, Sudoku, N-Queens,
scheduling, etc.

Below is a simple Python CSP solver for the classic Map Coloring Problem:

 The goal: Color regions of a map so that no adjacent regions have the same color.

def is_consistent(assignment, var, value, constraints):

for neighbor in constraints[var]:

if neighbor in assignment and assignment[neighbor] == value:

return False

return True

def backtrack(assignment, variables, domains, constraints):

# If assignment is complete, return it

if len(assignment) == len(variables):

return assignment

# Select an unassigned variable


unassigned = [v for v in variables if v not in assignment]

var = unassigned[0]

for value in domains[var]:

if is_consistent(assignment, var, value, constraints):

assignment[var] = value

result = backtrack(assignment, variables, domains, constraints)

if result is not None:

return result

del assignment[var] # Backtrack

return None # Failure

def main():

# Variables: Regions of Australia

variables = ['WA', 'NT', 'SA', 'Q', 'NSW', 'V', 'T']

# Domains: Possible colors

domains = {}

colors = ['Red', 'Green', 'Blue']

for var in variables:

domains[var] = colors

# Constraints: Adjacency

constraints = {

'WA': ['NT', 'SA'],

'NT': ['WA', 'SA', 'Q'],


'SA': ['WA', 'NT', 'Q', 'NSW', 'V'],

'Q': ['NT', 'SA', 'NSW'],

'NSW': ['Q', 'SA', 'V'],

'V': ['SA', 'NSW'],

'T': [] # Tasmania is isolated

solution = backtrack({}, variables, domains, constraints)

if solution:

print("Solution found:")

for region in variables:

print(f"{region}: {solution[region]}")

else:

print("No solution found.")

if __name__ == "__main__":

main()

5. Implement propositional checking algorithm

Let’s write a simple propositional logic satisfiability checker in Python.


This is basically a SAT solver — a classic propositional checking algorithm.

A minimal implementation of the DPLL algorithm (Davis–Putnam–Logemann–Loveland),


which is the foundation for many SAT solvers.

Python Program: Basic DPLL SAT Solver


This works for propositional formulas in CNF (Conjunctive Normal Form).

def dpll(clauses, assignment):

# If no clauses, it's satisfied


if not clauses:

return assignment

# If any clause is empty, it's unsatisfiable

if [] in clauses:

return None

# Unit clause propagation

for clause in clauses:

if len(clause) == 1:

literal = clause[0]

return dpll(assign(clauses, literal), assignment + [literal])

# Pure literal elimination

literals = [literal for clause in clauses for literal in clause]

for literal in set(literals):

if -literal not in literals:

return dpll(assign(clauses, literal), assignment + [literal])

# Choose a literal (first from first clause)

literal = clauses[0][0]

result = dpll(assign(clauses, literal), assignment + [literal])

if result is not None:

return result

return dpll(assign(clauses, -literal), assignment + [-literal])

def assign(clauses, literal):


new_clauses = []

for clause in clauses:

if literal in clause:

continue # Clause is satisfied

new_clause = [l for l in clause if l != -literal]

new_clauses.append(new_clause)

return new_clauses

def main():

# Example CNF: (A ∨ ¬B) ∧ (B ∨ C) ∧ (¬A ∨ ¬C)

# We'll represent literals as integers:

# A = 1, B = 2, C = 3

# Negative for negation: ¬A = -1

clauses = [

[1, -2], # A ∨ ¬B

[2, 3], # B ∨ C

[-1, -3] # ¬A ∨ ¬C

result = dpll(clauses, [])

if result is None:

print("Unsatisfiable")

else:

print("Satisfiable assignment found:")

for literal in result:


var = abs(literal)

value = literal > 0

print(f"Variable {var} = {value}")

if __name__ == "__main__":

main()

6. a. Implement forward chaining

Python program for Forward Chaining, which is a simple reasoning algorithm used in propositional
logic and rule-based expert systems.

✅ What is Forward Chaining?

Forward Chaining:

 Starts with known facts


 Applies inference rules to derive new facts
 Continues until the goal is reached or no new facts can be inferred

🎯 Example Scenario

Let’s use a classic:

Rules:

1. If it is raining, the ground is wet.


2. If the sprinkler is on, the ground is wet.
3. If the ground is wet, the grass is slippery.

Facts:

 It is raining.
 The sprinkler is on.

Goal:

 Is the grass slippery?

def forward_chaining(facts, rules, goal):


inferred = set(facts)

new_inferred = True

while new_inferred:

new_inferred = False

for rule in rules:

if rule['if'].issubset(inferred) and rule['then'] not in inferred:

[Link](rule['then'])

print(f"Inferred: {rule['then']}")

new_inferred = True

if rule['then'] == goal:

print(f"Goal '{goal}' reached!")

return True

print(f"Goal '{goal}' NOT reached.")

return False

def main():

# Initial facts

facts = {"raining", "sprinkler_on"}

# Rules: If 'if' conditions are true, then 'then' is inferred

rules = [

{"if": {"raining"}, "then": "ground_wet"},

{"if": {"sprinkler_on"}, "then": "ground_wet"},

{"if": {"ground_wet"}, "then": "grass_slippery"}


]

goal = "grass_slippery"

print(f"Initial facts: {facts}")

print(f"Trying to infer: {goal}")

forward_chaining(facts, rules, goal)

if __name__ == "__main__":

main()

6.b Implement backward chaining

Backward chaining is the reverse of forward chaining:

 You start with a goal,


 Try to prove it by finding rules that lead to it,
 Then recursively prove the conditions (premises) of those rules.

It’s commonly used in expert systems (e.g., MYCIN, Prolog).

def backward_chaining(goal, facts, rules, proved=None):

if proved is None:

proved = set()

if goal in facts:

print(f"Fact '{goal}' is known.")

return True

for rule in rules:

if rule['then'] == goal:
print(f"Trying to prove '{goal}' using rule: IF {rule['if']} THEN {goal}")

all_premises_proved = True

for premise in rule['if']:

if premise not in proved:

if backward_chaining(premise, facts, rules, proved):

[Link](premise)

else:

all_premises_proved = False

break

if all_premises_proved:

print(f"Proved: '{goal}'")

return True

print(f"Cannot prove: '{goal}'")

return False

def main():

# Facts

facts = {"raining", "sprinkler_on"}

# Rules: premises -> conclusion

rules = [

{"if": {"raining"}, "then": "ground_wet"},

{"if": {"sprinkler_on"}, "then": "ground_wet"},

{"if": {"ground_wet"}, "then": "grass_slippery"}


]

goal = "grass_slippery"

print(f"Known facts: {facts}")

print(f"Trying to prove: {goal} using backward chaining...")

result = backward_chaining(goal, facts, rules)

if result:

print(f"\nConclusion: '{goal}' IS TRUE.")

else:

print(f"\nConclusion: '{goal}' cannot be proved with current facts and rules.")

if __name__ == "__main__":

main()

6.c Implement resolution strategies

Resolution — a fundamental inference rule for Propositional Logic (and first-order logic).

Resolution is the basis of SAT solvers, theorem provers, and automatic reasoning.
It shows that if you want to prove PPP, you add ¬P\neg P¬P to your knowledge base (KB)
and use resolution:

 If you derive a contradiction (empty clause), then PPP must be true (proof by
refutation).

✅ Python Program: Simple Resolution Refutation


Below is a clear Python program that:
 Uses propositional clauses in CNF.
 Uses binary resolution.
 Shows step-by-step derived clauses.
 Stops when the empty clause is derived (contradiction).

def pl_resolution(KB, alpha):

clauses = KB + [[-x for x in alpha]] # Add negation of alpha

new = set()

print("Initial clauses:")

for c in clauses:

print(c)

while True:

n = len(clauses)

pairs = [(clauses[i], clauses[j]) for i in range(n) for j in range(i + 1, n)]

for (ci, cj) in pairs:

resolvents = resolve(ci, cj)

if [] in resolvents:

print("\nDerived empty clause by resolving:")

print(f"{ci} and {cj}")

return True

[Link](tuple(sorted(r)) for r in resolvents)

new_clauses = [list(c) for c in new if list(c) not in clauses]

if not new_clauses:

return False

for c in new_clauses:

[Link](c)
print(f"Derived new clause: {c}")

def resolve(ci, cj):

resolvents = []

for di in ci:

for dj in cj:

if di == -dj:

# Resolvent is (ci ∪ cj) without di and dj

new_clause = list(set(ci + cj))

new_clause.remove(di)

new_clause.remove(dj)

if not contains_complementary_literals(new_clause):

[Link](new_clause)

return resolvents

def contains_complementary_literals(clause):

return any(-l in clause for l in clause)

def main():

# Example KB:

# 1. A ∨ B -> [1, 2]

# 2. ¬A -> [-1]

# Goal: Prove B -> alpha = [2]

KB = [

[1, 2], # A ∨ B

[-1] # ¬A
]

alpha = [2] # B

result = pl_resolution(KB, alpha)

print("\nResult:")

if result:

print("Alpha is entailed by the KB. (Contradiction found)")

else:

print("Alpha is NOT entailed by the KB. (No contradiction)")

if __name__ == "__main__":

main()

7. Program for building naïve bayes models

Python program that demonstrates how to implement Naive Bayes Classification using the
scikit-learn library. I’ll also include a basic implementation from scratch if you want to see how
it works internally.

# Naive Bayes using scikit-learn

from [Link] import load_iris

from sklearn.model_selection import train_test_split

from sklearn.naive_bayes import GaussianNB

from [Link] import accuracy_score, classification_report

# Load example dataset

iris = load_iris()

X = [Link]
y = [Link]

# Split into train and test sets

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# Create a Gaussian Naive Bayes classifier

gnb = GaussianNB()

# Train the model

[Link](X_train, y_train)

# Make predictions

y_pred = [Link](X_test)

# Evaluate the model

print("Accuracy:", accuracy_score(y_test, y_pred))

print("\nClassification Report:\n", classification_report(y_test, y_pred))

8. Implement Bayesian networks and perform inferences

Python program that shows how to implement a simple Bayesian Network and perform
inference on it.
We’ll use the pgmpy library — it’s a good library for probabilistic graphical models in
Python.

✅ Step 1: Install pgmpy


If you haven’t installed pgmpy, run this:

bash
CopyEdit
pip install pgmpy
✅ Step 2: Example Program — Bayesian Network &
Inference
Below is a complete example that builds a Bayesian Network for the classic Rain-
Sprinkler-Grass Wet problem.

Rain-Sprinkler-Grass Wet

 Nodes: Cloudy, Sprinkler, Rain, WetGrass


 Cloudy influences Sprinkler and Rain
 Sprinkler and Rain influence WetGrass

python
CopyEdit
from [Link] import BayesianNetwork
from [Link] import TabularCPD
from [Link] import VariableElimination

⃣ Define the structure


# 1️
model = BayesianNetwork([
('Cloudy', 'Sprinkler'),
('Cloudy', 'Rain'),
('Sprinkler', 'WetGrass'),
('Rain', 'WetGrass')
])

⃣ Define CPDs (Conditional Probability Distributions)


# 2️
cpd_cloudy = TabularCPD(variable='Cloudy', variable_card=2, values=[[0.5],
[0.5]])

cpd_sprinkler = TabularCPD(
variable='Sprinkler',
variable_card=2,
values=[[0.5, 0.9], [0.5, 0.1]],
evidence=['Cloudy'],
evidence_card=[2]
)

cpd_rain = TabularCPD(
variable='Rain',
variable_card=2,
values=[[0.8, 0.2], [0.2, 0.8]],
evidence=['Cloudy'],
evidence_card=[2]
)

cpd_wetgrass = TabularCPD(
variable='WetGrass',
variable_card=2,
values=[
[1.0, 0.1, 0.1, 0.01],
[0.0, 0.9, 0.9, 0.99]
],
evidence=['Sprinkler', 'Rain'],
evidence_card=[2, 2]
)

⃣ Add CPDs to the model


# 3️
model.add_cpds(cpd_cloudy, cpd_sprinkler, cpd_rain, cpd_wetgrass)

⃣ Validate the model


# 4️
assert model.check_model()

⃣ Perform inference
# 5️
infer = VariableElimination(model)

# Example Query: What is P(Rain | WetGrass = True)


query = [Link](variables=['Rain'], evidence={'WetGrass': 1})
print(query)

# Another query: What is P(Sprinkler | WetGrass = True, Rain = False)


query2 = [Link](variables=['Sprinkler'], evidence={'WetGrass': 1,
'Rain': 0})
print(query2)

✅ What this does


1. Builds the network structure
2. Defines CPDs for each node
3. Checks model consistency
4. Runs inference using Variable Elimination

📌 Output
This will print:

 The probability distribution for Rain given WetGrass = True


 The probability distribution for Sprinkler given WetGrass = True and Rain =
False

⚡ Key points
 pgmpy is very flexible. You can also learn structure from data, estimate CPDs, do
MAP estimation, and more.
 For large networks, exact inference can be slow — then you can use approximate
methods like sampling.

You might also like