LAB Manual
LAB Manual
LAB Manual
LAB Manual
___________________
Lab Instructor Signature
Index
2
LAB # 01
INTRODUCTION TO PYTHON
The purpose of this lab is to get you familiar with Python and its IDE
___________________
Lab Instructor Signature
4
Experiment
INTRODUCTION
01
OBJECTIVE
Python Installation
To get started working with Python 3, you’ll need to have access to the Python interpreter.
There are several common ways to accomplish this:
• Python can be obtained from the Python Software Foundation website at python.org.
Typically, that involves downloading the appropriate installer for your operating
system and running it on your machine.
• Some operating systems, notably Linux, provide a package manager that can be run to
install Python.
• On macOS, the best way to install Python 3 involves installing a package manager
called Homebrew. You’ll see how to do this in the relevant section in the tutorial.
• On mobile operating systems like Android and iOS, you can install apps that provide a
Python programming environment. This can be a great way to practice your coding
skills on the go.
Alternatively, there are several websites that allow you to access a Python interpreter online
without installing anything on your computer at all.
It is highly unlikely that your Windows system shipped with Python already installed.
Windows systems typically do not. Fortunately, installing does not involve much more than
downloading the Python installer from the python.org website and running it. Let’s take a look
at how to install Python 3 on Windows:
1. Open a browser window and navigate to the Download page for Windows at
python.org.
5
2. Underneath the heading at the top that says Python Releases for Windows, click on
the link for the Latest Python 3 Release - Python 3.x.x. (As of this writing, the latest
is Python 3.6.5.)
3. Scroll to the bottom and select either Windows x86-64 executable installer for 64-bit
or Windows x86 executable installer for 32-bit. (See below.)
• If your system has a 32-bit processor, then you should choose the 32-bit installer.
• On a 64-bit system, either installer will actually work for most purposes. The 32-bit
version will generally use less memory, but the 64-bit version performs better for
applications with intensive computation.
• If you’re unsure which version to pick, go with the 64-bit version.
Note: Remember that if you get this choice “wrong” and would like to switch to another
version of Python, you can just uninstall Python and then re-install it by downloading another
installer from python.org.
Important: You want to be sure to check the box that says Add Python 3.x to PATH as
shown to ensure that the interpreter will be placed in your execution path.
6
Then just click Install Now. That should be all there is to it. A few minutes later you should
have a working Python 3 installation on your system.
PyCharm is available in three editions: Professional, Community, and Educational (Edu). The
Community and Edu editions are open-source projects and they are free, but they has less
features. PyCharm Edu provides courses and helps you learn programming with Python. The
Professional edition is commercial, and provides an outstanding set of tools and features. For
details, see the editions comparison matrix.
To install PyCharm
THEORY
Operators
The Python interpreter can be used to evaluate expressions, for example simple arithmetic
expressions. If you enter such expressions at the prompt (>>>) they will be evaluated and the
result will be returned on the next line.
>>> 1 + 1
2
>>> 2 * 3
6
Boolean operators also exist in Python to manipulate the primitive True and False values.
>>> 1==0
False
>>> not (1==0)
True
>>> (2==2) and (2==3)
False >>> (2==2)
or (2==3)
7
True
Strings
Like Java, Python has a built in string type. The + operator is overloaded to do string
concatenation on string values.
There are many built-in methods which allow you to manipulate strings.
>>> 'artificial'.upper()
'ARTIFICIAL'
>>>
'HELP'.lower()
'help' >>>
len('Help')
4
Notice that we can use either single quotes ' ' or double quotes " " to surround string. This
allows for easy nesting of strings.
In Python, you do not have declare variables before you assign to them.
In Python, input and output operations (OI Operations) are performed using two built-in
functions. Following are the two built-in functions to perform output operations and input
operations.
When we use the print( ) function to display a message, the string can be enclosed
either in the single quotation or double quotation.
print(num1)
#Output : 10
In the above example, we have created a variable num1 with a value 10. The value of
the variable num1 is displayed using the print( ) function.
In the above example code, we have used a formatted string. In Python, we can use the
formatted string that is prefixed with character "f". In the formatted string, the variable
values are included using curly braces ({ }).
Always the return value of print( ) function is None.
When input( ) function is used without a message, it simply prompts for the input value.
When the user enters the input value it reads the entered value as a string and assigns
it to the left-hand-side variable.
Example
number_1 = input()
print(f'The number you have entered is {number_1}')
10
When we run the above piece of code, in the output screen simply it waits for the user
input without displaying anything. Observe the following figure.
Here, the major problem is that the user does not have any information regarding what
is the next step the user has to do? To solve this problem, we use the input( ) function
with a message which tells the user that what the user has to do?
When input( ) function is used with a message, it prompts with a given message for the
input value. When the user enters the input value it reads the entered value as a string
and assigns it to the left-hand-side variable.
Example
When we run the above piece of code, in the output screen is displayed with a given
message and waits for the user input. Observe the following figure.
11
When we run the above piece of code, the input( ) function reads complete data
entered by the user but assigns an only first character to the variable ch. Look at the
following figure.
12
In the above example, the user has entered 'abc' as input value but the variable ch is
assigned with value 'a' only. Similarly, we can extract any character form the input data
using the index value.
13
LAB TASKS:
Exercise: Dir and Help
Learn about the methods Python provides for strings. To see what methods Python provides for
a datatype, use the dir and help commands:
>>> s = 'abc'
>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__',
'__ge__', '__getattribute__', '__getitem__', '__getnewargs__',
'__getslice__', '__gt__', '__hash__', '__init__','__le__', '__len__',
'__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__','__repr__', '__rmod__', '__rmul__', '__setattr__', '__str__',
'capitalize', 'center', 'count', 'decode', 'encode', 'endswith',
'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower',
'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip',
'replace', 'rfind','rindex', 'rjust', 'rsplit', 'rstrip', 'split',
'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate',
'upper', 'zfill']
>>> help(s.find)
Help on built-in function find:
Return -1 on failure.
>> s.find('b')
1 Try out some of the string functions listed in dir (ignore those with underscores '_' around
the method name).
Sample input:
Before swapping
a=2,b=56,c=78,d=9
After Swapping
a=,9,b=78,c=56,d=2
Input:
# taking values from the user
w = input()
x = input()
y = input()
z = input()
print ('The value of w is {}'.format(w))
print('The value of x is {}'.format(x))
print('The value of y is {}'.format(y))
print('The value of z is {}'.format(z))
# swapping the values
temp_var = x
x = y
y = z
z = temp_var
temp_var = w
w = x
x = temp_var
print('The value of w after swapping is{}'.format(w))
print('The value of x after swapping is {}'.format(x))
print('The value of y after swapping is {}'.format(y))
print('The value of z after swapping is {}'.format(z))
15
Output
Input
celsius = float(input("Enter temperature in celsius: "))
fahrenheit = (celsius * 9/5) + 32
print('%.2f Celsius is: %0.2f Fahrenheit' %(celsius, fahrenheit))
Output:
16
LAB # 02
BUILT-IN DATA STRUCTURES TO PYTHON
___________________
Lab Instructor Signature
17
Experiment
INTRODUCTION
02
OBJECTIVE
THEORY
Python comes equipped with some useful built-in data structures, broadly similar to Java's
collections package.
Lists
Python also allows negative-indexing from the back of the list. For instance, fruits[-1] will
access the last element 'banana':
>>> fruits[-2]
'pear'
>>> fruits.pop()
'banana'
>>> fruits
['apple', 'orange', 'pear']
18
>>> fruits.append('grapefruit')
>>> fruits
['apple', 'orange', 'pear', 'grapefruit']
>>> fruits[-1] = 'pineapple'
>>> fruits
['apple', 'orange', 'pear', 'pineapple']
We can also index multiple adjacent elements using the slice operator. For instance,
fruits[1:3], returns a list containing the elements at position 1 and 2. In general
fruits[start:stop] will get the elements in start, start+1, ..., stop-1. We can also
do fruits[start:] which returns all elements starting from the start index. Also
fruits[:end] will return all elements before the element at position end:
>>> fruits[0:2]
['apple', 'orange']
>>> fruits[:3]
['apple', 'orange', 'pear']
>>> fruits[2:]
['pear', 'pineapple']
>>> len(fruits)
4
The items stored in lists can be any Python data type. So for instance we can have lists of lists:
Tuples
A data structure similar to the list is the tuple, which is like a list except that it is immutable
once it is created (i.e. you cannot change its content once created). Note that tuples are
surrounded with parentheses while lists have square brackets.
>>> pair[1] = 6
TypeError: object does not support item assignment
The attempt to modify an immutable structure raised an exception. Exceptions indicate errors:
index out of bounds errors, type errors, and so on will all report exceptions in this way.
Sets
A set is another data structure that serves as an unordered list with no duplicate items. Below,
we show how to create a set:
>>> shapes = ['circle','square','triangle','circle'] >>>
setOfShapes = set(shapes)
Next, we show how to add things to the set, test if an item is in the set, and perform common
set operations (difference, intersection, union):
>>> setOfShapes
set(['circle','square','triangle'])
>>> setOfShapes.add('polygon')
>>> setOfShapes
set(['circle','square','triangle','polygon'])
>>> 'circle' in setOfShapes
True
>>> 'rhombus' in setOfShapes
False
>>> favoriteShapes = ['circle','triangle','hexagon']
>>> setOfFavoriteShapes = set(favoriteShapes) >>>
setOfShapes - setOfFavoriteShapes
set(['square','polyon'])
>>> setOfShapes & setOfFavoriteShapes set(['circle','triangle'])
>>> setOfShapes | setOfFavoriteShapes
set(['circle','square','triangle','polygon','hexagon'
Note that the objects in the set are unordered; you cannot assume that their traversal or
print order will be the same across machines!
Dictionaries
The last built-in data structure is the dictionary which stores a map from one type of object (the
key) to another (the value). The key must be an immutable type (string, number, or tuple). The
value can be any Python data type.
20
Note: In the example below, the printed order of the keys returned by Python could be different
than shown below. The reason is that unlike lists which have a fixed ordering, a dictionary is
simply a hash table for which there is no fixed ordering of the keys (like HashMaps in Java).
The order of the keys depends on how exactly the hashing algorithm maps keys to buckets, and
will usually seem arbitrary. Your code should not rely on key ordering, and you should not be
surprised if even a small modification to how your code uses a dictionary results in a new key
ordering.
>>> studentIds = {'knuth': 42.0, 'turing': 56.0, 'nash': 92.0 } >>>
studentIds['turing']
56.0
>>> studentIds['nash'] = 'ninety-two'
>>> studentIds
{'knuth': 42.0, 'turing': 56.0, 'nash': 'ninety-two'}
>>> del studentIds['knuth']
>>> studentIds
{'turing': 56.0, 'nash': 'ninety-two'}
>>> studentIds['knuth'] = [42.0,'forty-two']
>>> studentIds
{'knuth': [42.0, 'forty-two'], 'turing': 56.0, 'nash': 'ninety-two'}
>>> studentIds.keys()
['knuth', 'turing', 'nash']
>>> studentIds.values()
[[42.0, 'forty-two'], 56.0, 'ninety-two']
>>> studentIds.items()
[('knuth',[42.0, 'forty-two']), ('turing',56.0), ('nash','ninety-two')]
>>> len(studentIds) 3
Writing Scripts
Now that you've got a handle on using Python interactively, let's write a simple Python script
that demonstrates Python's for loop. Open the file called foreach.py, which should contain the
following code:
At the command line, use the following command in the directory containing foreach.py:
[cs188-ta@nova ~/tutorial]$ python foreach.py
apples for sale oranges for sale pears for
sale bananas for sale apples are too
expensive! oranges cost 1.500000 a pound
pears cost 1.750000 a pound
Remember that the print statements listing the costs may be in a different order on your screen
than in this tutorial; that's due to the fact that we're looping over dictionary keys, which are
unordered. To learn more about control structures (e.g., if and else) in Python, check out the
official Python tutorial section on this topic.
If you like functional programming you might also like map and filter:
>>> list(map(lambda x: x * x, [1,2,3]))
[1, 4, 9]
>>> list(filter(lambda x: x > 3, [1,2,3,4,5,4,3,2,1])) [4,
5, 4]
LAB TASKS:
Exercise: Lists
(i)Play with some of the list functions. You can find the methods you can call on an object via
the dir and get information about them via the help command:
>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
'__delslice__', '__doc__', '__eq__', '__ge__', '__getattribute__',
'__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__',
'__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__',
'__rmul__', '__setattr__', '__setitem__', '__setslice__', '__str__',
'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse',
'sort']
>>> help(list.reverse)
Help on built-in function reverse:
reverse(...)
L.reverse() -- reverse *IN PLACE*
Note: Ignore functions with underscores "_" around the names; these are private helper
methods. Press 'q' to back out of a help screen
(ii)Write a Python program to count the number of strings where the string length is 2 or more
and the first and last character are same from a given list of strings.
Sample List : ['abc', 'xyz', 'aba', '1221']
Expected Result : 2.
Exercise: Dictionaries
(i)Use dir and help to learn about the functions you can call on dictionaries and implement it.
23
24
Sample Dictionary :
dic1={1:10, 2:20}
dic2={3:30, 4:40}
dic3={5:50,6:60}
Expected Result : {1: 10, 2: 20, 3: 30, 4: 40, 5: 50, 6: 60}
Input:
dic1={1:10, 2:20}
dic2={3:30, 4:40}
dic3={5:50,6:60}
dic4 = {}
for d in (dic1, dic2, dic3): dic4.update(d)
print(dic4)
25
Output:
(i)Write a list comprehension which, from a list, generates a lowercased version of each string
that has length greater than five.
(ii)Write a Python program to print a specified list after removing the 0th, 4th and 5th elements
Exercise: Operators:
Play with some Operators in Python (assignment, bitwise, logical, arithmetic, identity,
membership)
Membership operator:
list1=[1,2,3,4,5] Output:
list2=[6,7,8,9]
26
LAB # 03
DECISION MAKING IN PYTHON
___________________
Lab Instructor Signature
28
Experiment
INTRODUCTION
03
OBJECTIVE
THEORY
Decision making is anticipation of conditions occurring while execution of the program and
specifying actions taken according to the conditions.
Decision structures evaluate multiple expressions which produce TRUE or FALSE as outcome.
You need to determine which action to take and which statements to execute if outcome is
TRUE or FALSE otherwise.
Following is the general form of a typical decision making structure found in most of the
programming languages −
1 if statements
2 if...else statements
3 nested if statements
var = 100
if ( var == 100 ) : print "Value of expression is 100"
print "Good bye!"
Python IF Statement
It is similar to that of other languages. The if statement contains a logical expression
using which data is compared and a decision is made based on the result of the
comparison.
Syntax
if expression:
statement(s)
If the boolean expression evaluates to TRUE, then the block of statement(s) inside the
if statement is executed. If boolean expression evaluates to FALSE, then the first set of
code after the end of the if statement(s) is executed.
Flow Diagram
Example
#!/usr/bin/python
var1 = 100
if var1:
print "1 - Got a true expression value"
print var1
var2 = 0
if var2:
31
Flow Diagram
32
Example
#!/usr/bin/python
var1 = 100
if var1:
print "1 - Got a true expression value"
print var1
else:
print "1 - Got a false expression value"
print var1
var2 = 0
if var2:
print "2 - Got a true expression value"
print var2
else:
print "2 - Got a false expression value"
print var2
The elif Statement
The elif statement allows you to check multiple expressions for TRUE and execute a
block of code as soon as one of the conditions evaluates to TRUE. Similar to the else,
the elif statement is optional. However, unlike else, for which there can be at most one
statement, there can be an arbitrary number of elif statements following an if.
syntax
if expression1:
statement(s)
elif expression2:
statement(s)
elif expression3:
statement(s)
else:
statement(s)
33
Core Python does not provide switch or case statements as in other languages, but we
can use if..elif...statements to simulate switch case as follows −
Example
#!/usr/bin/python
var = 100
if var == 200:
print "1 - Got a true expression value"
print var
elif var == 150:
print "2 - Got a true expression value"
print var
elif var == 100:
print "3 - Got a true expression value"
print var
else:
print "4 - Got a false expression value"
print var
Syntax
The syntax of the nested if...elif...else construct may be −
if expression1:
statement(s)
if expression2:
statement(s)
elif expression3:
statement(s)
elif expression4:
statement(s)
34
else:
statement(s)
else:
statement(s)
Example
#!/usr/bin/python
var = 100
if var < 200:
print "Expression value is less than 200"
if var == 150:
print "Which is 150"
elif var == 100:
print "Which is 100"
elif var == 50:
print "Which is 50"
elif var < 50:
print "Expression value is less than 50"
else:
print "Could not find true expression"
LAB TASKS:
1. Write a Program to print positive number entered by the user. If the user enters a negative
number, it is skipped
2. Write a Program to check whether an integer is positive or negative. This program considers
0 as a positive number
35
EXPERIMENT # 04
ARTIFICIAL INTELLIGENCE LAB
The purpose of this lab is to understand how agents and environments interact
Experime
INTRODUCTION nt
04
37
OBJECTIVE
The objective of this lab is to show the loop of interaction between the agent and the
environment
THEORY
It would be like a home thermostat recognizing that if the temperature increases to 75 degrees in
the house, the thermostat is prompted to kick on. It doesn't need to know what happened with the
temperature yesterday or what might happen tomorrow. Instead, it operates based on the idea
that if _____ happens, _____ is the response.
Simple reflex agents are just that - simple. They cannot compute complex equations or solve
complicated problems. They work only in environments that are fully-observable in the current
percept, ignoring any percept history. If you have a smart light bulb, for example, set to turn on
at 6 p.m. every night, the light bulb will not recognize how the days are longer in summer and
the lamp is not needed until much later. It will continue to turn the lamp on at 6 p.m. because
that is the rule it follows. Simple reflex agents are built on the condition-action rule.
These agents simply decide actions based on their current percept. By identifying that certain
actions are warranted in certain conditions,
38
39
40
@abstractmethod
41
defexecuteStep(self,n=1):
for _ in range(0,n):
self.displayPerception()
self.agent.sense(self)
res = self.agent.act()
self.action = res
if res == 'clean':
self.currentRoom.status = 'clean'
elif res == 'right':
self.currentRoom =
self.r2 else:
self.currentRoom = self.r1
self.displayAction()
self.step += 1
def executeAll(self): raise
NotImplementedError('action must be defined!') def
displayPerception(self): print("Perception at step
%d is [%s,%s]"
%(self.step,self.currentRoom.status,self.currentRoom.location))
42
defdelay(self,n=100):
self.delay = n
Room class
@author: dr.aarij
''' class
Room:
def __init__(self,location,status="dirty"):
self.location = location
self.status = status
Abstract agent
@author: dr.aarij
'''
from abc import abstractmethod
class
Agent(object):
'''
classdocs
'''
@abstractmethod
def __init__(self):
pass
@abstractmethod def
sense(self,environment):
pass
@abstractmethod
def act(self):
pass
def
__init__(self):
'''
Constructor
''' pass
def
sense(self,env):
self.environment = env
def act(self): if
self.environment.currentRoom.status == 'dirty':
return 'clean' if
self.environment.currentRoom.location == 'A':
return 'right' return 'left'
Test program
if __name__ == '__main__':
vcagent = VaccumAgent.VaccumAgent()
env = TwoRoomVaccumCleanerEnvironment(vcagent)
env.executeStep(50)
Exercises
1. Run the two room vacuum cleaner agent program and understand it. Convert the program
to a Three room environment.
2. Convert the environment to a ‘n’ room environment where n >= 2
3. Does the agent ever stop? If no, can you make it stop? Is your program rational?
4. Score your agent, -1 points for moving from a room, +25 points to clean a room that is
dirty, and -10 points if a room is dirty. The scoring will take place after every 1 second
5. Convert the agent to a reflex based agent with a model. Afterwards take the sensors away
from the agents i.e. now the agent cannot perceive anything. Does your agent still work?
if so then why?
Solution:
class Environment(object):
def __init__(self):
self.locationcondition = {'A': random.randint(0, 1), 'B': random.r
andint(0, 1)}
# RANDOM CONDITION
class Sreflexagent(Environment):
44
def __init__(self, environment):
print("Current Environment condition")
print(environment.locationcondition)
# place vacuum at random location
vacuumlocation = 1
score = 0
# if vacuum is at location A, print its placed at A, then if its d
irty, clean it, else move to B
if vacuumlocation == 0:
print("1: vacuum is randomly placed at Location A")
# covers {[1] [1]} and {[1] [0]}
if environment.locationcondition['A'] == 1:
print("2. Location A is dirty")
environment.locationcondition['A'] == 0
score = score + 1
print("3. Location A has been cleaned")
print("4. moving to location B")
if environment.locationcondition['B'] == 1:
print("5. Location B is dirty")
environment.locationcondition['B'] == 0
score = score + 1
print("6. Location B has been cleaned")
print("score:", score)
else:
print("5. Location B is clean")
print("score:", score)
# covers {[0] [0]} and {[0] [1]}
else:
print("2. Location A is clean, moving to location B")
# if B is dirty clean, else say its clean
if environment.locationcondition['B'] == 1:
print("3. Location B is dirty")
environment.locationcondition['B'] == 0
score = score + 1
print("4. Location B has been cleaned")
print("score:", score)
else:
print("3. Location B is clean")
print("score:", score)
# if location starts with B...
elif vacuumlocation == 1:
class Sreflexagent(Environment):
def __init__(self, environment):
45
print("Current Environment condition")
print(environment.locationcondition)
# place vacuum at random location
vacuumlocation = 1
score = 0
# if vacuum is at location A, print its placed at A, then if its d
irty, clean it, else move to B
if vacuumlocation == 0:
print("1: vacuum is randomly placed at Location A")
# covers {[1] [1]} and {[1] [0]}
if environment.locationcondition['A'] == 1:
print("2. Location A is dirty")
environment.locationcondition['A'] == 0
score = score + 1
print("3. Location A has been cleaned")
print("4. moving to location B")
if environment.locationcondition['B'] == 1:
print("5. Location B is dirty")
environment.locationcondition['B'] == 0
score = score + 1
print("6. Location B has been cleaned")
print("score:", score)
else:
print("5. Location B is clean")
print("score:", score)
# covers {[0] [0]} and {[0] [1]}
else:
print("2. Location A is clean, moving to location B")
# if B is dirty clean, else say its clean
if environment.locationcondition['B'] == 1:
print("3. Location B is dirty")
environment.locationcondition['B'] == 0
score = score + 1
print("4. Location B has been cleaned")
print("score:", score)
else:
print("3. Location B is clean")
print("score:", score)
# if location starts with B...
elif vacuumlocation == 1:
print("1. vacuum is randomly placed at Location B")
# covers {[1] [1]} and {[0] [1]}
if environment.locationcondition['B'] == 1:
46
print("2. Location B is dirty")
environment.locationcondition['B'] == 0
score = score + 1
print("3. Location B has been cleaned")
print("4. moving to location A")
if environment.locationcondition['A'] == 1:
print("5. Location A is dirty")
environment.locationcondition['A'] == 0
score = score + 1
print("6. Location A has been cleaned")
print("score:", score)
else:
print("5. Location A is clean")
print("score:", score)
# covers {[0] [0]} and {[1] [0]}
else:
# move to A
print("2. location B is clean, moving to Location A")
# if A is dirty
if environment.locationcondition['A'] == 1:
print("3. Location A is dirty")
environment.locationcondition['A'] == 0
score = score + 1
print("4. Location A has been cleaned")
print("score:", score)
else:
print("3. Location A is clean")
print("score:", score)
# print(Environment.locationcondition)
theEnvironment = Environment()
thevacuum = Sreflexagent(theEnvironment)
47
48
Lab-05
Objectives:
Apparatus
● Hardware Requirement
Personal computer.
● Software Requirement
Anaconda
Background:
The eight queens puzzle is the problem of placing eight chess queens on an 8×8 chessboard so that no two
queens threaten each other; thus, a solution requires that no two queens share the same row, column, or
diagonal. The eight queens puzzle is an example of the more general n queens problem of placing n non-
attacking queens on an n×n chessboard, for which solutions exist for all natural numbers n with the exception
of n = 2 and n = 3
The problem of finding all solutions to the 8-queens problem can be quite computationally
expensive, as there are 4,426,165,368 (i.e., 64C8) possible arrangements of eight queens on an 8×8
board, but only 92 solutions. It is possible to use shortcuts that reduce computational requirements
or rules of thumb that avoids brute-force computational techniques. For example, by applying a
simple rule that constrains each queen to a single column (or row), though still considered brute
force, it is possible to reduce the number of possibilities to 16,777,216 (that is, 88) possible
combinations. Generating permutations further reduces the possibilities to just 40,320 (that is, 8!),
Solutions
The eight queens puzzle has 92 distinct solutions. If solutions that differ only by the symmetry
operations of rotation and reflection of the board are counted as one, the puzzle has 12 solutions.
These are called fundamental solutions; representatives of each are shown below.
A fundamental solution usually has eight variants (including its original form) obtained by rotating
90, 180, or 270° and then reflecting each of the four rotational variants in a mirror in a fixed
position. However, should a solution be equivalent to its own 90° rotation (as happens to one
solution with five queens on a 5×5 board), that fundamental solution will have only two variants
(itself and its reflection). Should a solution be equivalent to its own 180° rotation (but not to its 90°
rotation), it will have four variants (itself and its reflection, its 90° rotation and the reflection of
that). If n > 1, it is not possible for a solution to be equivalent to its own reflection because that
would require two queens to be facing each other. Of the 12 fundamental solutions to the problem
with eight queens on an 8×8 board, exactly one (solution 12 below) is equal to its own 180° rotation,
and none is equal to its 90° rotation; thus, the number of distinct solutions is 11×8 + 1×4 = 92.
Algorithm:
Lab Exercise:
global N
N = 4
def printSolution(board):
for i in range(N):
for j in range(N):
print()
for i in range(col):
if board[row][i] == 1:
return False
if board[i][j] == 1:
return False
if board[i][j] == 1:
return False
return True
return True
53
for i in range(N):
board[i][col] = 1
return True
board[i][col] = 0
return False
def solveNQ():
54
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
return False
printSolution(board)
return True
# Driver Code
solveNQ()
55
56
Lab-06
Breadth First search and Depth First Search
Objectives:
To familiarize students with basic concepts of un-informed search technique, i.e. BreadthFirst
Search and Depth First Search
Theory:
BFS stands for Breadth First Search is a vertex based technique for finding a shortest path in
graph. It uses a Queue data structure which follows first in first out. In BFS, one vertex is
selected at a time when it is visited and marked then its adjacent are visited and stored in the
queue. It is slower than DFS.
Example-
A
/\
B C
/ /\
D E F
Output is:
A, B, C, D, E, F
DFS stands for Depth First Search is a edge based technique. It uses the Stack data structure ,
performs two stages, first visited vertices are pushed into stack and second if there is no
57
A
/\
B C
/ /\
D E F
Output is:
A, B, D, C, E, F
BFS vs DFS
S.N
O BFS DFS
1. BFS stands for Breadth First Search. DFS stands for Depth First Search.
BFS(Breadth First Search) uses Queue data DFS(Depth First Search) uses Stack data
2. structure for finding the shortest path. structure.
BFS is more suitable for searching vertices DFS is more suitable when there are
3. which are closer to the given source. solutions away from source.
5. The Time complexity of BFS is O(V + E) The Time complexity of DFS is also O(V +
when Adjacency List is used and O(V^2) E) when Adjacency List is used and O(V^2)
58
when Adjacency Matrix is used, where V when Adjacency Matrix is used, where V
stands for vertices and E stands for edges. stands for vertices and E stands for edges.
6. Here, siblings are visited before the children Here, children are visited before the siblings
LAB TASKS:
BFS:
global N
N = 4
def printSolution(board):
for i in range(N):
for j in range(N):
print (board[i][j], end = " ")
print()
def isSafe(board, row, col):
# Check this row on left side
for i in range(col):
if board[row][i] == 1:
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] == 1:
return False
# Check lower diagonal on left side
61
for i, j in zip(range(row, N, 1),
range(col, -1, -1)):
if board[i][j] == 1:
return False
return True
def solveNQUtil(board, col):
# base case: If all queens are placed
# then return true
if col >= N:
return True
# Consider this column and try placing
# this queen in all rows one by one
for i in range(N):
if isSafe(board, i, col):
# Place this queen in board[i][col]
board[i][col] = 1
# recur to place rest of the queens
62
if solveNQUtil(board, col + 1) == True:
return True
# If placing queen in board[i][col
# doesn't lead to a solution, then
# queen from board[i][col]
board[i][col] = 0
# if the queen can not be placed in any row in
# this colum col then return false
return False
def solveNQ():
board = [ [0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
if solveNQUtil(board, 0) == False:
print ("Solution does not exist")
return False
printSolution(board)
return True
63
# Driver Code
solveNQ()
DFS:
graph = {
'5' : ['3','7'],
'3' : ['2', '4'],
'7' : ['8'],
'2' : [],
'4' : ['8'],
'8' : []
visited = set() # Set to keep track of visited nodes of graph.
def dfs(visited, graph, node): #function for dfs
if node not in visited:
64
print (node)
visited.add(node)
for neighbour in graph[node]:
dfs(visited, graph, neighbour)
# Driver Code
print("Following is the Depth-First Search")
dfs(visited, graph, '5')
65
Lab-07
Tower of Hanoi
Objectives:
Theory:
Tower of Hanoi
Tower of Hanoi is a mathematical puzzle where we have three rods and n disks. The
objective of the puzzle is to move the entire stack to another rod, obeying the following
simple rules:
Rules
2. Each move consists of taking the upper disk from one of the stacks and
placing it on top of another stack i.e. a disk can only be moved if it is
the uppermost disk on a stack.
Approach:
Take an example for 2 disks:
Let rod 1 = 'A', rod 2 = 'B', rod 3 = 'C'.
Examples:
Input : 2
Output : Disk 1 moved from A to B
Disk 2 moved from A to C
Disk 1 moved from B to C
Input : 3
Output : Disk 1 moved from A to C
Disk 2 moved from A to B
67
LAB TASKS:
def TowerOfHanoii(n, source, destination, aux):
if n==1:
print ("Move Disk 1 from Source",source,"to Destination",destination)
return
TowerOfHanoii(n-1,source, aux, destination)
print ("Move Disk",n,"from source",source,"to Destination",destination)
TowerOfHanoii(n-1,aux,destination,source)
n = 5
TowerOfHanoii(n,'A','B','C')
68
Output:
69
Lab-08
Informed Search (Greedy Search)
Objectives:
Theory:
Informed Search: Informed Search algorithms have information on the goal state which helps
in more efficient searching. This information is obtained by a function that estimates how close
a state is to the goal state.
Examples:
Greedy Search
A* Search
It provides the direction regarding the No suggestion is given regarding the solution in
solution. it.
Greedy Search, A* Search, Graph Search Depth First Search, Breadth First Search
Search Heuristics:
In an informed search, a heuristic is a function that estimates how close a state is to the goal
state. For example – Manhattan distance, Euclidean distance, etc. (Lesser the distance, closer the
goal.) Different heuristics are used in different informed algorithms discussed below.
Greedy Search:
In greedy search, we expand the node closest to the goal node. The “closeness” is estimated by a
heuristic h(x).
Lower the value of h(x), closer is the node from the goal.
Strategy: Expand the node closest to the goal state, i.e. expand the node with a lower h value.
Example:
Question. Find the path from S to G using greedy search. The heuristic values h of each node
below the name of the node.
Advantage: Works well with informed search problems, with fewer steps to reach a goal.
Disadvantage: Can turn into unguided DFS in the worst case.
LAB TASKS:
activities = [["A1", 0, 6],
73
["A2", 3, 4],
["A3", 1, 2],
["A4", 5, 8],
["A5", 5, 7],
["A6", 8, 9]
]
#Function to solve the Activity Selection Problem
def printMaxActivities(activities):
activities.sort(key=lambda x: x[2])
i = 0
firstA = activities[i][0]
print(firstA)
for j in range(len(activities)):
if activities[j][1] > activities[i][2]:
print(activities[j][0])
i = j
printMaxActivities(activities)
74
Conclusion:
A greedy algorithm is a method of solving a problem that chooses the best solution
available at the time. It is not concerned about whether the current best outcome will lead to the
overall best result.
A greedy algorithm doesn’t reverse its earlier decisions. It operates in a top-down approach. This
algorithm may not produce the best result for all the problems
The Activity Selection Problem makes use of the Greedy Algorithm in the following
manner:
For all the remaining activities, check whether the start time of the activity is greater or
equal to the finish time ofthe previously selevted activity. If so, select this activity and
print it.
76
Lab-09
Objectives:
Theory:
Informed Search: Informed Search algorithms have information on the goal state which helps
in more efficient searching. This information is obtained by a function that estimates how close
a state is to the goal state.
Examples:
Greedy Search
A* Search
It provides the direction regarding the No suggestion is given regarding the solution in
solution. it.
Greedy Search, A* Search, Graph Search Depth First Search, Breadth First Search
Search Heuristics:
In an informed search, a heuristic is a function that estimates how close a state is to the goal
state. For example – Manhattan distance, Euclidean distance, etc. (Lesser the distance, closer the
goal.) Different heuristics are used in different informed algorithms discussed below.
Greedy Search:
In greedy search, we expand the node closest to the goal node. The “closeness” is estimated by a
heuristic h(x).
Lower the value of h(x), closer is the node from the goal.
78
Strategy: Expand the node closest to the goal state, i.e. expand the node with a lower h value.
Example:
Question. Find the path from S to G using greedy search. The heuristic values h of each node
below the name of the node.
Advantage: Works well with informed search problems, with fewer steps to reach a goal.
Disadvantage: Can turn into unguided DFS in the worst case.
A* Tree Search:
A* Tree Search, or simply known as A* Search, combines the strengths of uniform-cost search
and greedy search. In this search, the heuristic is the summation of the cost in UCS, denoted by
g(x), and the cost in the greedy search, denoted by h(x). The summed cost is denoted by f(x).
Heuristic: The following points should be noted wrt heuristics in A* search
Here, h(x) is called the forward cost and is an estimate of the distance of the current
node from the goal node.
And, g(x) is called the backward cost and is the cumulative cost of a node from the
root node.
80
A* search is optimal only when for all nodes, the forward cost for a node h(x)
underestimates the actual cost h*(x) to reach the goal. This property of A* heuristic is
called admissibility.
Admissibility:
Solution. Starting from S, the algorithm computes g(x) + h(x) for all nodes in the fringe at
each step, choosing the node with the lowest sum. The entire work is shown in the table
below.
Note that in the fourth set of iteration, we get two paths with equal summed cost f(x), so we
expand them both in the next set. The path with a lower cost on further expansion is the chosen
path.
Path h(x)g(x) f(x)
S 7 0 7
S -> A 9 3 12
S -> D 5 2 7
S -> D -> B 4 2 + 1 = 37
S -> D -> E 3 2 + 4 = 69
LAB TASKS:
Code:
def aStarAlgo(start_node, stop_node):
open_set = set(start_node)
closed_set = set()
g = {} #store distance from starting node
parents = {} # parents contains an adjacency map of all nodes
g[start_node] = 0 #start_node is root node i.e it has no parent nodes #s
o start_node is set to its own parent node
parents[start_node] = start_node
while len (open_set) > 0:
n = None
#node with lowest fo is found
for v in open_set:
if n == None or g[v] + heuristic(v) < g[n] + heuristic(n):
n = v
if n == stop_node or Graph_nodes[n] == None:
pass
else:
83
for (m, weight) in get_neighbors (n):
if m not in open_set and m not in closed_set:
open_set.add(m)
parents[m] = n
g[m] = g[n] + weight
#for each node m, compare its distance from start i.e g(m) to the
#from start through n node
else:
if g[m] > g[n] + weight:
#update g(m)
g[m] - g[n] + weight
#change parent of m to n
parents[m] = n
#if m in closedset, remove and add to open
if m in closed_set:
closed_set.remove(m)
open_set.add(m)
if n == None:
print('Path does not exist!')
return None
# if the current node is the stop_node # then we begin reconstr
uctin the path from it to the start_node
84
if n == stop_node:
path = []
while parents[n] != n:
path.append(n)
n = parents[n]
path.append(start_node)
path.reverse()
print('Path found: {}'.format(path))
return path
open_set.remove(n)
closed_set.add(n)
print('Path does not exist!')
return None
#define fuction to return neighbor and its distance #from the passed node
def get_neighbors(v):
if v in Graph_nodes:
return Graph_nodes[v]
else:
return None
def heuristic(n):
H_dist = {
85
'A': 11,
'B': 6,
'C': 99,
'D': 1,
'E': 7,
'G': 0,
}
return H_dist[n]
#Describe your graph here
Graph_nodes = {
'A': [('B', 2), ('E', 3)],
'B': [('C', 1), ('G', 9)],
'C': None,
'E': [('D', 6)],
'D': [('G', 1)],
aStarAlgo('A', 'G')
Output:
86
87
Lab-10
Adversarial Search
Objectives:
To familiarize students to implement informed search algorithms using Zero sum games concept
Theory:
So far, we have seen single agent environments. However, there are environments
where there is more than one agent. These are called multi agent environments. Here
we are focusing on the environments that are adversarial i.e. where your agent is up
against an agent that is trying to minimize your utility. The more the adversarial agent
minimize your agent’s utility the more the adversarial agent will maximize its utility.
Board games like chess, checkers, ludo, tic-tac-toe, etc. are examples of this kind of
adversarial environments. These games are turn taking games. Unlike, search, we
cannot plan for victory because after your agent has made a move, the next move
belongs to the adversary.
In these environments, we must devise a strategy that make an optimal decision given a
state. We can model these games as a search problem as follows:
88
We can have an abstract representation of the above formulation as follows:
from abc import abstractmethod
class
Game(object)
:
''
'
cl
as
sd
oc
s
'''
@abstractmethod
def __init__(self,
params): pass
@abstractmethod
def
getInitialState(self)
:pass
@abstractmethod
def
getPlayer(self,state)
:pass
@abstractmethod
def
getActions(self,state):
pass
89
@abstractmethod
def getResult(self, state,
action): pass
@abstractmethod
def
terminalTest(self,state):
pass
@abstractmethod
def
utility(self,state,player):
pass
@abstractmethod
def getAgentCount(self):
pass
Similarly, an abstract class for a game’s state could be designed as follows:
class
State(objec
t):
'''
classdocs
'''
def __init__(self, params):pass
def
isMax(self):
pass
def
isNextAgentMax(self):
pass
def
getAction(self):pass
For now, we will consider that the environment is deterministic i.e. we know the
outcomes of an action taken by any agent at any particular state. To make an optimal
decision our agent has to simulate the actions taken by the adversarial agent if the
agent chooses any particular agent. In order to simulate the agent, we need to simulate
what the adversarial agent will be thinking about our agent, and what our agent will be
thinking about the adversarial agent and so on. This is graphically depicted in the
following diagram.
90
So for all the possible actions, available to our agent, at any given state, our agent will
take the action that returns the maximum value. Whereas, the adversarial agent will
choose an action that returns the minimum value, as the lower the value the better it is
for our adversarial agent.
One algorithm that calculates these values is called the minimax algorithm. There
pseudo code is given below:
91
The value of an action is recursively calculated at the terminal nodes, as shown in the
figure below:
We will simulate this behavior in this experiment.
To simulate a search tree, we will create a tree data structure as follows:
from com.ai.adversarial.elements.state import State
92
class
AdversarialNode(State):
'''
classdocs
'''
def __init__(self, value, name, isMax, children =
[]): '''
Constructor '''
self._name = name
self._utility = value
self._isMax = isMax
self._children = children
self._move = 1
self._action = None
def isLeaf(self):
return len(self._children) == 0
def isMax(self):
return self._isMax
def
isNextAgentMax(self):
return not self._isMax
def addChild(self,child):
self._children.append(child);
def getAction(self):
return self._action
def __str__(self):
s = "Name is %s, value is %d" %(self._name,self._utility)
s += "\n children are "
for ch in range(len(self._children)):
s+= str(self._children[ch])
return s
To model a tree such as shown in the figure above we could make a game for it:
from com.ai.adversarial.elements.game import Game
from com.ai.adversarial.sample.treegame.adversarialNode import
AdversarialNo
de import
sys
93
LAB TASKS:
95
Code:
import numpy as np
import random
from time import sleep
# Creates an empty board
def create_board():
return(np.array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]))
# Check for empty places on board
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)
# Select a random place for the player
96
def random_place(board, player):
selection = possibilities(board)
current_loc = random.choice(selection)
board[current_loc] = player
return(board)
# Checks whether the player has three
# of their marks in a horizontal row
def row_win(board, player):
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)
# Checks whether the player has three
# of their marks in a vertical row
def col_win(board, player):
97
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)
# Checks whether the player has three
# of their marks in a diagonal row
def diag_win(board, player):
win = True
y = 0
for x in range(len(board)):
if board[x, x] != player:
win = False
if win:
return win
win = True
if win:
98
for x in range(len(board)):
y = len(board) - 1 - x
if board[x, y] != player:
win = False
return win
# Evaluates whether there is
# a winner or a tie
def evaluate(board):
winner = 0
for player in [1, 2]:
if (row_win(board, player) or
col_win(board,player) or
diag_win(board,player)):
winner = player
if np.all(board != 0) and winner == 0:
winner = -1
return winner
# Main function to start the game
def play_game():
99
board, winner, counter = create_board(), 0, 1
print(board)
sleep(2)
while winner == 0:
for player in [1, 2]:
board = random_place(board, player)
print("Board after " + str(counter) + " move")
print(board)
sleep(2)
counter += 1
winner = evaluate(board)
if winner != 0:
break
return(winner)
# Driver Code
print("Winner is: " + str(play_game()))
100
Output: