0% found this document useful (0 votes)
372 views100 pages

LAB Manual

Download as docx, pdf, or txt
Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1/ 100

1

LAB Manual

Name Muhammad Fawad


Registration No 8783
Department BSSE
Course Artificial Intelliegence Lab
Faculty Ma’am Darakhshan Syed

___________________
Lab Instructor Signature

Index
2

S. no Name of the Page Awarded Signature


Experiment No Marks
1 Introduction to python 4
2 Built-in data structures 16
to python
3 Decision making in 27
python
4 Artificial intelligence 36
5 To Implement a program 48
to solve the N-Queen
problem
6 Breadth First search and 56
Depth First Search
7 Tower of Hanoi 66
8 Informed Search 70
(Greedy Search)
9 Informed Search (A* 77
Search)
10 Adversarial Search 88
3

LAB # 01
INTRODUCTION TO PYTHON
The purpose of this lab is to get you familiar with Python and its IDE

Name Muhammad Fawad


Registration No 8783
Department BSSE
Course Artificial Intelliegence Lab
Faculty Ma’am Darakhshan Syed

___________________
Lab Instructor Signature
4

Experiment
INTRODUCTION

01
OBJECTIVE

Introduction to python, Basic concepts

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:

Step 1: Download the Python 3 Installer

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.)

Sidebar: 32‐bit or 64‐bit Python?


For Windows, you can choose either the 32-bit or 64-bit installer. Here’s what the difference
between the two comes down to:

• 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.

Step 2: Run the Installer


Once you have chosen and downloaded an installer, simply run it by double-clicking on the
downloaded file. A dialog should appear that looks something like this:

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

1. Download PyCharm for your operating system.


2. Do the following depending on your operating system: o
Windows installation:
1. Run the PyCharm-*.exe file you've downloaded.
2. Follow the instructions in the installation wizard.

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.

>>> 'artificial' + "intelligence" 'artificialintelligence'

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.

We can also store expressions into variables.


>>> s = 'hello
world' >>>
print(s) hello
world >>>
s.upper() 'HELLO
WORLD'
>>> len(s.upper())
11
>>> num = 8.0
>>> num += 2.5
>>> print(num)
10.5

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.

 print( ) - Used for output operation.


 input( ) - Used for input operations.
8

Output Operations using print() function


The built-in function print( ) is used to output the given data to the standard output
device (Screen).

Displaying a message using print( ) function


In Python, using the print( ) function we can display a text message. The print( ) takes a
string as an argument and displays the same.
Example

print('This message is displayed on the screen')

print("This message is also displayed on the screen")

When we use the print( ) function to display a message, the string can be enclosed
either in the single quotation or double quotation.

Displaying a variable value using print( ) function


In Python, the built-in function print( ) function is also used to display variables value.
The following example shows that how to display variable value using print( ) function.
Example

num1 = 10 #variale num1 with value 10

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.

Formatted print( ) function to display a combination of message and


value
The built-in function print( ) is also used to display the combination of message and
variable value. Let's look at the following example.
Example
9

num1 = 10 #variale num1 with value 10

print('The value of variable num1 is ' + num1)

#Output : The value of variable num1 is 10

We can also write the same print( ) function as follows.


Example

num1 = 10 #variale num1 with value 10

print(f"The value of variable num1 is {num1}")

#Output : The value of variable num1 is 10

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.

Input Operations using input() function


The Python provides a built-in function input( ) to perform input operations. The input( )
function can be used either with some message or without a message.

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

number_1 = input('Enter any number: ')


print(f'The number you have entered is {number_1}')

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

Always the input( ) function reads input value as string value only.


To read the value of any other data type, there is no input function in Python. Explicitly
we need to convert to the required data type using type casing.

Reading a single character in Python


The Python does not provide any direct function to read a single character as input. But
we can use the index value to extract a single character from the user entered input
value. Let's look at the following Python code.
Example

ch = input('Enter any data: ')[0]


print(f'The character entered is {ch}')

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:

find(...) method of builtins.str instance


S.find(sub[, start[, end]]) -> int

Return the lowest index in S where substring sub is found,


such that sub is contained within S[start:end]. Optional
arguments start and end are interpreted as in slice notation.

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).

Exercise Python input /output Basic operations

(i)Write a Python program to swap 4 variables values (input four values.


14

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

(ii) Write a Python program to convert temperatures to and from celsius,


Fahrenheit.
Formula : c/5 = f-32/9
Expected Output :
Enter temp in Celsius: 60°C
Temperature in Fahrenheit is

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

Name Muhammad Fawad


Registration No 8783
Department BSSE
Course Artificial Intelliegence Lab
Faculty Ma’am Darakhshan Syed

___________________
Lab Instructor Signature
17

Experiment
INTRODUCTION

02
OBJECTIVE

Introduction to the built-in data types in python

THEORY

Built-in Data Structures

Python comes equipped with some useful built-in data structures, broadly similar to Java's
collections package.

Lists

Lists store a sequence of mutable items:

>>> fruits = ['apple','orange','pear','banana']


>>> fruits[0] 'apple'

We can use the + operator to do list concatenation:


>>> otherFruits = ['kiwi','strawberry']
>>> fruits + otherFruits
>>> ['apple', 'orange', 'pear', 'banana', 'kiwi', 'strawberry']

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:

>>> lstOfLsts = [['a','b','c'],[1,2,3],['one','two','three']]


>>> lstOfLsts[1][2]
3
>>> lstOfLsts[0].pop()
'c'
>>> lstOfLsts
[['a', 'b'],[1, 2, 3],['one', 'two', 'three']]

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 = (3,5)


>>> pair[0]
3
>>> x,y = pair
>>> x
3
>>> y
5
19

>>> 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)

Another way of creating a set is shown below:

>>> setOfShapes = {‘circle’, ‘square’, ‘triangle’, ‘circle’}

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

As with nested lists, you can also create dictionaries of dictionaries.

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:

# This is what a comment looks like


fruits = ['apples', 'oranges', 'pears', 'bananas']
for fruit in fruits: print(fruit + ' for
sale')

fruitPrices = {'apples': 2.00, 'oranges': 1.50, 'pears': 1.75}


for fruit, price in fruitPrices.items(): if price < 2.00:
print('%s cost %f a pound' % (fruit, price)) else:
print(fruit + ' are too expensive!')
21

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]

The next snippet of code demonstrates Python's list comprehension construction:


nums = [1,2,3,4,5,6] plusOneNums = [x+1
for x in nums] oddNums = [x for x in nums
if x % 2 == 1] print(oddNums)
oddNumsPlusOne = [x+1 for x in nums if x % 2 ==1] print(oddNumsPlusOne)

This code is in a file called listcomp.py, which you can run:


[cs188-ta@nova ~]$ python listcomp.py
[1,3,5]
[2,4,6]
22

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*

>>> lst = ['a','b','c']


>>> lst.reverse()
>>> ['c','b','a']

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

(ii)Write a Python script to concatenate following dictionaries to create a new one. 

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:

Exercise: List Comprehensions

(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

Sample List : ['Red', 'Green', 'White', 'Black', 'Pink', 'Yellow',’Teapink’]


Expected Output : ['Green', 'White', 'Black']

Exercise: Operators:
Play with some Operators in Python (assignment, bitwise, logical, arithmetic, identity,
membership)

(i) What will be the output of the given program


Identity Operators in Python Output:
x=6
if (type(x) is int): true
print ("true")
else:
print ("false")
x = 7.2 Output:
if (type(x) x = 7.2
if (type(x) is not int): invalid syntax
print ("true")
else:
print ("false")

Membership operator:
list1=[1,2,3,4,5] Output:
list2=[6,7,8,9]
26

for item in list1: not overlapping


    if item in list2:
        print("overlapping")    
else:
    print("not overlapping")

Floor division and  Exponent and Assign Output:


a//=3
invalid character in identifier
a**=5
print(“floor divide=”,a)
print(“exponent=”,a)
Bitwise Operaotors: Output
a = 60 /* 60 = 0011 1100 */
line 1 a = 60 /* 60 = 0011 1100 */
b = 13 /* 13 = 0000 1101 */
^ SyntaxError: invalid syntax
int c = 0

c=a&b /* 12 = 0000 1100 */


print("Line 1", c )

c=a|b /* 61 = 0011 1101 */


print("Line 2 ", c )

c=a^b /* 49 = 0011 0001 */


print("Line 3 ", c )

c = ~a /*-61 = 1100 0011 */


print("Line 4", c )

c = a << 2 /* 240 = 1111 0000 */


printf("Line 5 ", c );

c = a >> 2 /* 15 = 0000 1111 */


printf("Line 6 -", c );
27

LAB # 03
DECISION MAKING IN PYTHON

Name Muhammad Fawad


Registration No 8783
Department BSSE
Course Artificial Intelliegence Lab
Faculty Ma’am Darakhshan Syed

___________________
Lab Instructor Signature
28

Experiment
INTRODUCTION

03
OBJECTIVE

Understanding the concept of if-else in python

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 −

Python programming language assumes any non-zero and non-null values as TRUE,


and if it is either zero or null, then it is assumed as FALSE value.
Python programming language provides following types of decision making statements.
Click the following links to check their detail.
29

Sr.No Statement & Description


.

1 if statements

An if statement consists of a boolean expression followed by one or more


statements.

2 if...else statements

An if statement can be followed by an optional else statement, which executes


when the boolean expression is FALSE.

3 nested if statements

You can use one if or else if statement inside another if or else if statement(s).

Let us go through each decision making briefly −

Single Statement Suites


If the suite of an if clause consists only of a single line, it may go on the same line as
the header statement.
Here is an example of a one-line if clause −
#!/usr/bin/python

var = 100
if ( var == 100 ) : print "Value of expression is 100"
print "Good bye!"

When the above code is executed, it produces the following result −


Value of expression is 100
Good bye!
30

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

print "2 - Got a true expression value"


print var2
print "Good bye!"

When the above code is executed, it produces the following result −


1 - Got a true expression value
100
Good bye!

Python IF...ELIF...ELSE Statements


An else statement can be combined with an if statement. An else statement contains
the block of code that executes if the conditional expression in the if statement resolves
to 0 or a FALSE value.
The else statement is an optional statement and there could be at most only
one else statement following if.
Syntax
The syntax of the if...else statement is −
if expression:
statement(s)
else:
statement(s)

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

print "Good bye!"

When the above code is executed, it produces the following result −


1 - Got a true expression value
100
2 - Got a false expression value
0
Good bye!

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

print "Good bye!"

When the above code is executed, it produces the following result −


3 - Got a true expression value
100
Good bye!

Python nested IF statements


There may be a situation when you want to check for another condition after a
condition resolves to true. In such a situation, you can use the nested if construct.
In a nested if construct, you can have an if...elif...else construct inside
another if...elif...else construct.

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"

print "Good bye!"

When the above code is executed, it produces following result −


Expression value is less than 200
Which is 100
Good bye!

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

3. Write a Program to check whether an integer is positive, negative or zero


36

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

Agents and environments


A simple reflex agent is the most basic of the intelligent agents out there. It performs actions
based on a current situation. When something happens in the environment of a simple reflex
agent, the agent quickly scans its knowledge base for how to respond to the situation at-hand
based on pre-determined rules.

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

Python code for the abstract Environment

from abc import abstractmethod


class
Environment(object):
'''
classdocs
'''

@abstractmethod
41

def __init__(self, n):


self.n = n
def executeStep(self,n=1): raise
NotImplementedError('action must be defined!')
def executeAll(self): raise
NotImplementedError('action must be defined!')
def delay(self,
n=100): self.delay
= n

For the Two Room Vacuum Cleaner Environment

from com.environment import


Environment from com.environment
import Room from com.agent import
VaccumAgent
class
TwoRoomVaccumCleanerEnvironment(Environment.Environment):
''' classdocs '''
def __init__(self, agent):
'''
Constructor
'''
self.r1 =
Room.Room('A','dirty') self.r2
= Room.Room('B','dirty')
self.agent = agent
self.currentRoom = self.r1
self.delay = 1000 self.step = 1
self.action = ""

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

def displayAction(self): print("------- Action taken at


step %d is [%s]" %(self.step,self.action))

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

Vaccum cleaner Agent

from com.agent import Agent


class
VaccumAgent(Agent.Agent):
'''
classdocs
'''
43

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

To Implement a program to solve the N-Queen problem.

Objectives:

To compute correlation of discrete time signals and study their properties.

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

Constructing and counting solutions:

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!),

which are then checked for diagonal attacks.


49

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.

All fundamental solutions are presented below:


50

Algorithm:

1) Start in the leftmost column

2) If all queens are placed return true

3) Try all rows in the current column.

Do following for every tried row.

a) If the queen can be placed safely in this row

Then mark this [row, column] as part of the

Solution and recursively check if placing

Queen here leads to a solution.

b) If placing the queen in [row, column] leads to

a solution then return true.


51

c) If placing queen doesn't lead to a solution then

Unmark this [row, column] (Backtrack) and go to

step(a) to try other rows.

3) If all rows have been tried and nothing worked,

return false to trigger backtracking.

Lab Exercise:

1. Implement the algorithm in python language

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


52

    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

    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
53

  

    # 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

            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():
54

    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

  

# 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:

Breadth First Search

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

Depth First Search

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

vertices then visited vertices are popped. 


Example- 
 

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 can be used to find single source


shortest path in an unweighted graph,
because in BFS, we reach a vertex with In DFS, we might traverse through more
minimum number of edges from a source edges to reach a destination vertex from a
3. vertex. source.

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.

DFS is more suitable for game or puzzle


BFS considers all neighbors first and problems. We make a decision, then explore
therefore not suitable for decision making all paths through this decision. And if this
4. trees used in games or puzzles. decision leads to win situation, we stop.

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

Pseudo code of Breadth First search

Pseudo code of Depth First search


59

LAB TASKS:

1. Write a python code to implement BFS


2. Write a python code to implement DFS
60

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:

To familiarize students to solve a problem through recursion

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

1. Only one disk can be moved at a time.

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.

3. No disk may be placed on top of a smaller disk.

Approach:
Take an example for 2 disks:
Let rod 1 = 'A', rod 2 = 'B', rod 3 = 'C'.

Step 1: Shift first disk from 'A' to 'B'.


Step 2: Shift second disk from 'A' to 'C'.
Step 3: Shift first disk from 'B' to 'C'.

The pattern here is:


66

Shift 'n-1' disks from 'A' to 'B'.


Shift last disk from 'A' to 'C'.
Shift 'n-1' disks from 'B' to 'C'.

Image illustration for 3 disks:

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

Disk 1 moved from C to B


Disk 3 moved from A to C
Disk 1 moved from B to A
Disk 2 moved from B to C
Disk 1 moved from A to C

LAB TASKS:

1. Write a python code to implement Tower of Hanoi for disks=5


Code:

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:

To familiarize students to implement informed search algorithms using Greedy search

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

Informed Search vs. Uninformed Search:  

Informed Search Uninformed Search

It uses knowledge for the searching


process. It doesn’t use knowledge for searching process.

It finds solution slow as compared to informed


It finds solution more quickly. search.
70

Informed Search Uninformed Search

It may or may not be complete. It is always complete.

Cost is low. Cost is high.

It consumes less time. It consumes moderate time.

It provides the direction regarding the No suggestion is given regarding the solution in
solution. it.

It is less lengthy while implementation. It is more lengthy while implementation.

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).

Heuristic: A heuristic h is defined as-

h(x) = Estimate of distance of node x from the goal node.


71

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.

Solution. Starting from S, we can traverse to A(h=9) or D(h=5). We choose D, as it has the


lower heuristic cost. Now from D, we can move to B(h=4) or E(h=3). We choose E with a
lower heuristic cost. Finally, from E, we go to G(h=0). This entire traversal is shown in the
search tree below, in blue.
72

Path:   S -> D -> E -> G 

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:

Write a python code to implement Greedy Search

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

Implementation of Activity Selection Problem

The Activity Selection Problem makes use of the Greedy Algorithm in the following

manner:

 First, sort the activities based on their finish time.


 Select the first activity from the sorted list and print it.
75

 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

Informed Search (A*Search)

Objectives:

To familiarize students to implement informed search algorithms using A* Search

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

Informed Search vs. Uninformed Search:  

Informed Search Uninformed Search

It uses knowledge for the searching


process. It doesn’t use knowledge for searching process.

It finds solution slow as compared to informed


It finds solution more quickly. search.

It may or may not be complete. It is always complete.


77

Informed Search Uninformed Search

Cost is low. Cost is high.

It consumes less time. It consumes moderate time.

It provides the direction regarding the No suggestion is given regarding the solution in
solution. it.

It is less lengthy while implementation. It is more lengthy while implementation.

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).

Heuristic: A heuristic h is defined as-

h(x) = Estimate of distance of node x from the goal node.

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.

Solution. Starting from S, we can traverse to A(h=9) or D(h=5). We choose D, as it has the


lower heuristic cost. Now from D, we can move to B(h=4) or E(h=3). We choose E with a
lower heuristic cost. Finally, from E, we go to G(h=0). This entire traversal is shown in the
search tree below, in blue.
79

Path:   S -> D -> E -> G 

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:   

Strategy: Choose the node with the lowest f(x) value. 


Example: 
Question. Find the path to reach from S to G using A* search. 
81

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

       

S -> D -> B -> C    2 3 + 2 = 57

S -> D -> B -> E    3 3 + 1 = 47

       

S -> D -> B -> C -> G 0 5 + 4 = 99

S -> D -> B -> E -> G     0 4 + 3 = 77

Path:   S -> D -> B -> E -> G 


Cost:   7 
82

LAB TASKS:

Write a python code to implement A* Search

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

''' 
 
@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

from com.ai.adversarial.search.simpleMinimax import SimpleMinimax 


 
 
class
MinimaxTreeGame(Game): 
    ''' 
    classdocs 
    ''' 
 
 
    def __init__(self): 
        '''         Constructor 
         
                                    A 
                    B                C         
D                 E    F    G    H    I    J   
K    L    M                 3    12   8    2   
4    6    14    5    2         ''' 
         
        bottom1 = [AdversarialNode(3,"E",True,[]), 
                   AdversarialNode(12,"F",True,[]), 
                   AdversarialNode(8,"G",T
rue,[])]          
        bottom2 = [AdversarialNode(2,"H",True,[]),                   
AdversarialNode(4,"I",True,[]), 
                   AdversarialNode(6,"J",True,[])] 
 
        bottom3 = [AdversarialNode(14,"K",True,[]), 
                   AdversarialNode(5,"L",True,[]), 
                   AdversarialNode(2,"M",True,[])] 
         
b. = AdversarialNode(‐sys.maxsize ‐ 1,"B",False,bottom1) 
b. = AdversarialNode(‐sys.maxsize ‐ 1,"C",False,bottom2)  
d = AdversarialNode(‐sys.maxsize ‐ 1,"D",False,bottom3) 
         
        a = AdversarialNode(‐sys.maxsize ‐ 1,"A",True,[b,c,d]) 
         
        self._root = a 
         
    def
getInitialState(self):    
return self._root 
     
    def
getPlayer(self,state):      
return state.isMax() 
     
    def
getActions(self,state): 
94

        return [x for x  in range(len(state._children))] 


          
    def getResult(self, state, action): 
        return state._children[action] 
      
    def
terminalTest(self,state):      
return state.isLeaf() 
      
    def utility(self,state,player): 
        return state._value 
             
    def
getAgentCount(self):  
        return 2 
     
    def
printState(self,state): 
        toPrintNodes = [] 
        toPrintNodes.append(state) 
        while len(toPrintNodes) > 0: 
            node = toPrintNodes[0] 
            del toPrintNodes[0] 
            print("Name = %s, value = %d"%
(node._name,node._utility)) 
            toPrintNodes += node._children 
         
     
     
 
 
An implementation of vanilla minimax is shown below. The code shown below is a bit
different from the pseudo code shown in the AIMA book. However, the code is more
generic and could be used to simulate environments where there could be more than
one adversarial agent. 

LAB TASKS:
95

Write a python code to implement Tic Tac Toe game.

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:

You might also like