Amity University, Noida Aset (Cse) Batch: 2020-2024: Course Code: CSE401 Course: Artificial Intelligence
Amity University, Noida Aset (Cse) Batch: 2020-2024: Course Code: CSE401 Course: Artificial Intelligence
Amity University, Noida Aset (Cse) Batch: 2020-2024: Course Code: CSE401 Course: Artificial Intelligence
ASET (CSE)
Batch : 2020-2024
Lab File
fi
EXPERIMENT-1
Theory: Tic-tac-toe who take turns marking the spaces in a 3×3 grid. The player who succeeds in
placing three of their marks in a horizontal, vertical, or diagonal row wins the game. Players soon
discover that the best play from both parties leads to a draw. Hence, tic-tac-toe is most often played
by young children, who often have not yet discovered the optimal strategy. Because of the
simplicity of tic-tac-toe, it is often used as a pedagogical tool for teaching the concepts of good
sportsmanship and the branch of artificial intelligence that deals with the searching of game trees. It
is straightforward to write a computer program to play tic-tac-toe perfectly or to enumerate the 765
essentially different positions (the state space complexity) or the 26,830 possible games up to
rotations and reflections (the game tree complexity) on this space. The game can be generalized to
an m,n,k-game in which two players alternate placing stones of their own colour on an m×n board,
with the goal of getting k of their own colour in a row. Tic-tac-toe is the (3,3,3)-game. Tic-tac-toe is
the game where n equals 3 and d equals 2. If played properly, the game will end in a draw, making
tic-tac-toe a futile game.
Code:
import numpy as np
import random
def create_board():
return(np.array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]))
def possibilities(board):
l = []
for i in range(len(board)):
for j in range(len(board)):
if board[i][j] == 0:
l.append((i, j))
return(l)
selection = possibilities(board)
current_loc = random.choice(selection)
board[current_loc] = player
return(board)
for x in range(len(board)):
win = True
for y in range(len(board)):
if board[x, y] != player:
win = False
continue
if win == True:
return(win)
return(win)
for x in range(len(board)):
win = True
for y in range(len(board)):
if board[y][x] != player:
win = False
continue
if win == True:
return(win)
return(win)
win = True
for x in range(len(board)):
if board[x, x] != player:
win = False
return(win)
# a winner or a tie
def evaluate(board):
winner = 0
if (row_win(board, player) or
col_win(board,player) or
diag_win(board,player)):
winner = player
winner = -1
return winner
def play_game():
print(board)
while winner == 0:
print(board)
counter += 1
winner = evaluate(board)
if winner != 0:
break
if winner == -1:
return("DRAW")
return(winner)
# Driver Code
OUTPUT
EXPERIMENT-2
Objective: Write a program to implement a Single Player Game
Theory:
N-Puzzle or sliding puzzle is a popular puzzle that consists of N tiles where N can be 8, 15, 24 and
so on. In our example N = 8. The puzzle is divided into sqrt(N+1) rows and sqrt(N+1) columns. E.g.
15-Puzzle will have 4 rows and 4 columns and an 8-Puzzle will have 3 rows and 3 columns. The
puzzle consists of N tiles and one empty space where the tiles can be moved. Start and Goal
configurations (also called state) of the puzzle are provided. The puzzle can be solved by moving
the tiles one by one in the single empty space and thus achieving the Goal configuration.
Code:
class Node:
def __init__(self,data,level,fval):
self.data = data
self.level = level
self.fval = fval
def generate_child(self):
x,y = self.find(self.data,'_')
val_list = [[x,y-1],[x,y+1],[x-1,y],[x+1,y]]
children = []
for i in val_list:
child = self.shuffle(self.data,x,y,i[0],i[1])
child_node = Node(child,self.level+1,0)
children.append(child_node)
return children
def shuffle(self,puz,x1,y1,x2,y2):
temp_puz = []
temp_puz = self.copy(puz)
temp = temp_puz[x2][y2]
temp_puz[x2][y2] = temp_puz[x1][y1]
temp_puz[x1][y1] = temp
return temp_puz
else:
return None
def copy(self,root):
temp = []
for i in root:
t = []
for j in i:
t.append(j)
temp.append(t)
return temp
def find(self,puz,x):
for i in range(0,len(self.data)):
for j in range(0,len(self.data)):
if puz[i][j] == x:
return i,j
class Puzzle:
def __init__(self,size):
self.n = size
self.open = []
self.closed = []
def accept(self):
puz = []
for i in range(0,self.n):
puz.append(temp)
return puz
def f(self,start,goal):
return self.h(start.data,goal)+start.level
def h(self,start,goal):
temp = 0
for i in range(0,self.n):
for j in range(0,self.n):
temp += 1
return temp
def process(self):
start = self.accept()
goal = self.accept()
start = Node(start,0,0)
start.fval = self.f(start,goal)
self.open.append(start)
print("\n\n")
while True:
cur = self.open[0]
print("")
print(" | ")
print(" | ")
for i in cur.data:
for j in i:
print(j,end=" ")
print("")
if(self.h(cur.data,goal) == 0):
break
for i in cur.generate_child():
i.fval = self.f(i,goal)
self.open.append(i)
self.closed.append(cur)
del self.open[0]
puz = Puzzle(3)
puz.process()
OUTPUT:
EXPERIMENT-3
Objective: Write a program to implement BFS for water jug problem using Python.
Theory:
Breadth-First Search for a graph is similar to Breadth-First Traversal of a tree. The only catch here
is, unlike trees, graphs may contain cycles, so we may come to the same node again. To avoid
processing a node more than once, we use a Boolean visited array. For simplicity, it is assumed that
all vertices are reachable from the starting vertex. For example, in the following graph, we start
traversal from vertex 2. When we come to vertex 0, we look for all adjacent vertices of it. 2 is also
an adjacent vertex of 0. If we don’t mark visited vertices, then 2 will be processed again and it will
become a non-terminating process. A Breadth-First Traversal of the following graph is 2, 0, 3, 1.
Code:
capacity = (12,8,5)
x = capacity[0]
y = capacity[1]
z = capacity[2]
memory = {}
ans = []
def get_all_states(state):
a = state[0]
b = state[1]
c = state[2]
ans.append(state)
return True
if((a,b,c) in memory):
return False
memory[(a,b,c)] = 1
#empty jug a
if(a>0):
#empty a into b
if(a+b<=y):
if( get_all_states((0,a+b,c)) ):
ans.append(state)
return True
else:
ans.append(state)
return True
#empty a into c
if(a+c<=z):
if( get_all_states((0,b,a+c)) ):
ans.append(state)
return True
else:
ans.append(state)
return True
#empty jug b
if(b>0):
#empty b into a
if(a+b<=x):
ans.append(state)
return True
else:
ans.append(state)
return True
#empty b into c
if(b+c<=z):
ans.append(state)
return True
else:
ans.append(state)
return True
#empty jug c
if(c>0):
#empty c into a
if(a+c<=x):
ans.append(state)
return True
else:
ans.append(state)
return True
#empty c into b
if(b+c<=y):
ans.append(state)
return True
else:
ans.append(state)
return True
return False
initial_state = (12,0,0)
print("Starting work...\n")
get_all_states(initial_state)
ans.reverse()
for i in ans:
print(i)
OUTPUT:
EXPERIMENT-3
Theory: A* Search algorithm is one of the best and popular techniques used in path-finding and
graph traversals. Consider a square grid having many obstacles and we are given a starting cell and
a target cell. We want to reach the target cell (if possible) from the starting cell as quickly as
possible. Here A* Search Algorithm comes to the rescue. What A* Search Algorithm does is that at
each step it picks the node according to a value ‘f’ which is a parameter equal to the sum of two
other parameters – ‘g’ and ‘h’. At each step it picks the node/cell having the lowest ‘f’, and process
that node/cell. We define ‘g’ and ‘h’ as simply as possible below:
g = the movement cost to move from the starting point to a given square on the grid, following the
path generated to get there.
h = the estimated movement cost to move from that given square on the grid to the final destination.
This is often referred to as the heuristic, which is nothing but a kind of smart guess. We really don’t
know the actual distance until we find the path, because all sorts of things can be in the way (walls,
water, etc.). There can be many ways to calculate this ‘h’.
Code:
class Node():
self.parent = parent
self.position = position
self.g = 0
self.h = 0
self.f = 0
"""Returns a list of tuples as a path from the given start to the given end in the given maze"""
open_list = []
closed_list = []
open_list.append(start_node)
current_node = open_list[0]
current_index = 0
current_node = item
current_index = index
open_list.pop(current_index)
closed_list.append(current_node)
if current_node == end_node:
path = []
current = current_node
path.append(current.position)
current = current.parent
# Generate children
children = []
for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]: # Adjacent
squares
continue
if maze[node_position[0]][node_position[1]] != 0:
continue
# Append
children.append(new_node)
if child == closed_child:
continue
child.g = current_node.g + 1
continue
open_list.append(child)
def main():
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
start = (0, 0)
end = (7, 6)
print(path)
if __name__ == '__main__':
main()
OUTPUT
EXPERIMENT-5
Objective: Implement a Brute Force solution to the knapsack problem in python
Code:
class Bounty:
best = Bounty( 0, 0, 0)
current = Bounty( 0, 0, 0)
best_amounts = (0, 0, 0)
print ("This is achieved by carrying (one solution) %d panacea, %d ichor and %d gold" % \
print ("The weight to carry is %4.1f and the volume used is %5.3f" % (best.weight, best.volume))
EXPERIMENT-6
Objective: Implement Graph colouring problem using python
Theory: In graph theory, graph colouring is a special case of graph labelling; it is an assignment of
labels traditionally called "colours" to elements of a graph subject to certain constraints. In its
simplest form, it is a way of colouring the vertices of a graph such that no two adjacent vertices are
of the same colour; this is called a vertex colouring. Similarly, an edge colouring assigns a colour to
each edge so that no two adjacent edges are of the same colour, and a face colouring of a planar
graph assigns a colour to each face or region so that no two faces that share a boundary have the
same colour. Vertex colouring is the starting point of graph colouring. Other colouring problems can
be transformed into a vertex version. For example, an edge colouring of a graph is just a vertex
colouring of its line graph, and a face colouring of a plane graph is just a vertex colouring of its
dual.
Code:
neighbors = {}
colors_of_states = {}
color_of_neighbor = colors_of_states.get(neighbor)
if color_of_neighbor == color:
return False
return True
def get_color_for_state(state):
if promising(state, color):
return color
def main():
colors_of_states[state] = get_color_for_state(state)
print (colors_of_states)
main()
OUTPUT
EXPERIMENT-7
Objective: Write a program to implement DFS using python
Theory: -Depth First Traversal (or Search) for a graph is like Depth First Traversal of a tree. The
only catch here is, unlike trees, graphs may contain cycles, so we may come to the same node again.
In the following graph, we start traversal from vertex 2. When we come to vertex 0, we look for all
adjacent vertices of it. 2 is also an adjacent vertex of 0. If we don’t mark visited vertices, then 2 will
be processed again and it will become a non-terminating process. A Depth First Traversal of the
following graph is 2, 0, 1, 3.
Code: -
from collections import defaultdict
class Graph:
def __init__(self):
self.graph = defaultdict(list)
def addEdge(self,u,v):
self.graph[u].append(v)
def DFSUtil(self,v,visited):
visited[v]= True
print (v)
for i in self.graph[v]:
if visited[i] == False:
self.DFSUtil(i, visited)
def DFS(self,v):
visited = [False]*(len(self.graph))
self.DFSUtil(v,visited)
g = Graph()
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 2)
g.addEdge(2, 0)
g.addEdge(2, 3)
g.addEdge(3, 3)
g.DFS(2)
OUTPUT
EXPERIMENT-8
Objective: Tokenisation of word and sentences with the help of Natural Language ToolKit (NLTK)
Package
Theory: - NLTK is a leading platform for building Python programs to work with human language
data. It provides easy-to-use interfaces to over 50 corpora and lexical resources such as WordNet,
along with a suite of text processing libraries for classification, tokenization, stemming, tagging,
parsing, and semantic reasoning, wrappers for industrial-strength NLP libraries, and an
active discussion forum.
NLTK has been called “a wonderful tool for teaching, and working in, computational linguistics
using Python,” and “an amazing library to play with natural language.”
Natural Language Processing with Python provides a practical introduction to programming for
language processing. Written by the creators of NLTK, it guides the reader through the
fundamentals of writing Python programs, working with corpora, categorizing text, analysing
linguistic structure, and more.
Code:-
import nltk
from nltk.tokenize import word_tokenize
nltk.download('punkt')
text = "God is Great! I won a lottery."
print(word_tokenize(text))
from nltk.tokenize import sent_tokenize
text = "God is Great! I won a lottery."
print(sent_tokenize(text))
EXPERIMENT-9
Objective: -Design an XOR truth table using Python
Theory: - A Logic gate is an elementary building block of any digital circuits. It takes one or two
inputs and produces output based on those inputs. Outputs may be high (1) or low (0). Logic gates
are implemented using diodes or transistors. The XOR gate gives an output of 1 if either both inputs
are different, it gives 0 if they are same.
Code: -
if a != b:
return 1
else:
return 0
# Driver code
if __name__=='__main__':
print(XOR(5, 5))
print("------------------------------------------")
OUTPUT:
EXPERIMENT-10
Theory: -
Scikit-Fuzzy is a collection of fuzzy logic algorithms intended for use in the SciPy Stack, written in
the Python computing language. This SciKit is developed by the SciPy community. Scikit-Fuzzy is
a Python package based on SciPy that allows implementing all the most important fuzzy logic
algorithms (including fuzzy C-means). In this example, we continue using the MNIST dataset, but
with a major focus on fuzzy partitioning.
To perform the clustering, Scikit-Fuzzy implements the cmeans method (in the skfuzzy. cluster
package) which requires a few mandatory parameters: data, which must be an array D ∈ ℜN × M
(N is the number of features; therefore, the array used with Scikit-Learn must be transposed); c, the
number of clusters; the coefficient m, error, which is the maximum tolerance; and maxiter, which is
the maximum number of iterations.