Chapter 5
Contents
- 1 Making Decisions
Making Decisions
So you gotta let me know Should I stay or should I go? -: From the song, Should I stay or should I go, Mick Jones (The Clash), 1982. |
Robots are designed so that their sensors can be used to enable them to make decisions about their behaviors. Sensing is useful in decision making because that is what makes the robot aware of its environment. You have already seen how sensing time as a way of proprioception can help in writing some behaviors. In this chapter, we will use the information obtained from sensors to specify decisions a robot can make. Python provides commands or statements that help in decision making. First, let us review what we know about sensors and how to obtain sensor values that can then be used in decision making.
Obtaining Sensory Information
Recall from the last chapter that besides time, your Scribbler has four kinds of sensors: light, proximity, line, and stall. By using the joystick operation you also saw how the sensor values vary as the robot moves about its environment. The Myro library provides simple functions to obtain these sensor values. These are listed below:
- Light Sensors
- getLight(<POSITION>) Returns the current value in the <POSITION> light sensor. Your Scribbler has three light sensors (see picture above). <POSITION> can either be one of 'left', 'center', 'right' or one of the numbers 0, 1, 2. The positions 0, 1, and 2 correspond to the left, center, and right sensors. Examples:
>>> getLight('left') 221 >>> getLight(0) 221 >>> getLight('center') 302 >>> getLight(1) 301 >>> getLight('right') 200 >>> getLight(2) 200
- Proximity Sensors
- getIR(<POSITION>) returns the values of the IR sensors. <POSITION> can be 0 or 'left' and 1 or 'right'.
>>> getIR('left') 0 >>> getIR('right') 0 >>> getIR(0) 0 >>> getIR(1) 0
- Stall Sensor
- getStall() returns the values of the internal stall sensors. Its value can be 0 (the robots is not stalled) or 1 (it is stalled and cannot continue to move). This is also a proprioceptor. That is, an internal sensor. It gets set whenever the motors of the robot are trying to move but the robot is no longer moving.
>>> getStall() 0
- Line Sensors
- getLine(<POSITION>) returns the values of the line sensors. <POSITION> can be 0 or 'left' and 1 or 'right'.
>>> getLine(0) 1 >>> getLine('left') 1 >>> getLine('right) 1 >>> getLine(1) 0
Now that you know how to find out what the robot is currently sensing, we can learn how this information can be used to make decisions. Here is an exercise:
Problem: Move forward and stop when you run into something.
Simple Decision Making
In order to write a control program for the above behavior, all you have to do is make the robot go forward until it stalls. When it senses that it has stalled (i.e. it can no longer go forward), it stops. This can be expressed in the following manner:
while I am not stalled: go forward
That is repeatedly issue the command to go forward as long as it is not stalled. You have already seen how to express repetitions in Chapter 3. We can use the while-loop to express this, except this time, instead of using True, we will use the condition that it is not stalled. In Python, you can express this as follows:
while getStall()==0: forward(1) stop()
the first line above uses what is called a conditional expression in Python: getStall()==0. You should read it as if it were asking the question (or testing the condition):
Is the value returned by getStall() equal to 0?
Such questions are called conditional expressions and their answers are either a yes or a no. In Python, we translate a yes to the value True and a no to the value False. The operator == is a way of asking if the thing on its left (in this case, the value returned by getStall()) is equal to the value on its right (in this case 0). == is a very useful operation and can be used to see if any two entities are equal. For example:
>>> 5 == 5 True >>> 5 == 7 False >>> "robot" == "robot" True >>> "robot" == "Robot" False >>> 3.45 == 3.45 True >>> 3.45 == 3.451 False >>> a, b, c = 10, 20, 10 >>> a == b False >>> a == c True >>> a == a True
Getting back to the problem at hand (we will return to conditional expressions in more detail below), we could also use a new command in Python: the if statement to make this decision:
while True: forward(1) if getStall() == 1: stop() break
There are two new commands here. First the easy one: the command break can be used inside any loop to tell the execution to break out of the loop (or terminate the repetitions specified by the loop). The if statement has the following structure:
if <CONDITION>: <do something> <do something> ...
That is, if the condition specified by <CONDITION> is True then whatever is specified in the body of the if statement is carried out. In case the condition is False, all the statements under the if command are skipped over. Thus in the while-loop shown above, the robot is being asked to go forward, repeatedly forever. If it stalls (i.e. getStall() returns 1), stop and break out of the loop.
Exercise 0: Write a complete robot brain program to implement the behavior described above. Try out both ways of specifying the loop.
Exercise 1: For the above situation, suppose we write the following commands:
forward(1) while True: if getStall() == 1: stop() break
Does this result in the same behavior or not? Try it on your robot to confirm your response.
In Python, the values True and False are defined to be synonymous with 1 and 0, respectively. That is:
>>> 0 == False True >>> 1 == False False >>> 1 == True True >>> 0 == True False
Also, the operation not can be used to negate any conditional expression or a value. That is:
>>> not True False >>> not False True >>> not 1 False >>> not 0 True
Thus, you could write the above examples of robot behaviors as follows:
while not getStall(): forward(1) stop()
while True: forward(1) if getStall(): stop() break
Conditional Expressions
Conditional expressions are used to specify conditions in the while-loops and if-statements. These expressions always return a True or a False values (or, alternately a 1 or a 0). Above, we saw the use of == operator to compare if two things are equal. Other relational operations that can be used for comparisons are: less than (<), less than or equal to(<=), greater than (>), greater than or equal to (>=), and not equal to (!=), Below, we show how they can be used in Python conditional expressions:
>>> 5 < 7 True >>> 5 <= 3 False >>> 5 != 5 False >>> 5 != 6 True >>> 15 > 9 True >>> (5 + 9) >= (7-3) True >>> "something" != "something else" True
Besides relational operations you can build more complex conditional expressions using the logical operations (also called Boolean operations): and, or, and not. Here are some examples:
>>> (5 < 7) and (8 > 3) True >>> not ( (5 < 7) and (8 > 3)) False >>> (6 > 7) or (3 > 4) False >>> (6 > 7) or (3 > 2) True
We can define the meaning of logical operators as follows:
- <expression-1> and <expression-2>: Such an expression will result in a value True only if both <expression-1> and <expression-2> are True. In all other cases (i.e. if either one or both of <expression-1> and <expression-2> are False) it results in a False.
- <expression-1> or <expression-2>: Such an expression will result in a value True if either <expression-1> or <expression-2> are True or if both are True. In all other cases (i.e. if both of <expression-1> and <expression-2> are False) it results in a False.
- not <expression>: Such an expression will result in a value True if <expression> is False or False if <expression> is True). I.e., it flips or complements the value of expression.
These operators can be combined with relational expressions to form arbitrarily complex conditional expressions. In fact, any decision making in your programs boils down to forming the appropriate conditional expressions. The logical operators were invented by the logician George Boole in the mid 19th century. Boolean algebra, named after Boole, defines some simple, yet important laws that govern the behavior of logical operators. Here are some useful ones:
- (A or True) is always True.
- (not (not A)) is just A
- (A or (B and C)) is the same as ((A or B) and (A or C))
- (A and (B or C)) is the same as ((A and B) or (A and C))
- (not (A or B)) is the same as ((not A) and (not B))
- (not (A and B)) is the same as ((not A) or (not B))
These idenities or properties can help you in simplifying conditonal expressions.
Beyond simple decisions
The if-statement introduced above is a way of making simple decisions (also called one-way decisions). That is, you can conditionally control the execution of a set of commands based on a single condition. The if-statement is Python is quit eversatile and can be used to make two-way or even multi-way decisions. Here is how you would use it to choose among two sets of commands:
if <condition>: <do something> else: <do something else>
That is, if the <condition> is true it will do the commands specified in <do something>. If, however, the <condition> is false, it will <do something else>. Similarly, you can extend the if-statement to help specify multiple options:
if <condition-1>: <command set 1> elif <condition-2>: <command set 2> elif <condition-3>: <command set 3> ... ... else: <last command set>
Notice the use of the word elif (yes, it is spelled that way!) to designate "else if". Thus, depending upon whiever ciondition is true, the corresponding command set will be carried out. if all else fails, the last command set will be carried out.
Sensing Light
Using the three light sensors the robot can detect varying light conditions in its environment. Let us write a robot program that makes it detect and orient towards bright light. Recall from Chapter 3 that light sensors report low values in bright light conditions and high values in low light. To accomplish this task, we only need to look at the values reported by left and right light sensors. The following describes the robot's behavior:
do forever if left light is brigheter than right light turn left else turn right
Thus, by making use of the if-else statement, we can refine the above into the following:
while True: if left light is brighter that right light: turnLeft(1.0) else: turnRight(1.0)
The only thing remaining in the commands above is to write the condition to detect the difference betwee the two light sensors. This can be done using the expression:
getLight('left') < getLight('right')
Write a complete program that implemnts the above behavior and test it on your robot.
You may have noticed that even in uniform lighting conditions sensors tend to report different values. It is generally a good idea to threshold the difference when making the decision above. Say we set the threshold to a difference of at least 50. That is, if the left and right sensors differ by at least 50 then turn towards the brighter sensor. What happens if the difference is less than the threshold? Let us decide that in that case the robot will stay still. This behavior can be captured by the following:
thresh = 50 while True: # get sensor values for left and right light sensors L = getLight('left') R = getLight('right') # decide how to act based on sensors values if (L - R) > thresh: # left is seeing less light than right so turn right turnRight(1.0) elif (L - R) < thresh: # right is seeing less light than left, so turn left turnLeft(1.0) else: # the difference is less than the threshold, stay put stop()
Notice how we have used the variable thresh to represent the threshold value. This is good programming practice. Since the performance of sensors varies under different light conditions, this allows you to adjust the threshold by simply changing that one value. By using the name thresh instead of a fixed value, say 50, you only have to make such changes in one place of your program.
In the statements above, there is a pattern that you will find recurring in many programs that define robot behaviors using simple decisions:
while True: <sense> <decide and then act>
Such behaviors are called reactive behaviors. That is, a robot is reacting to the change in its environment by deciding how to act based on its sensor values. A wide range of robot behaviors can be written using the above structure. The exercises that follow illustrate this idea.
Decision making robots
Now that you know how to issue commands to select among alternatives and also how to formulate conditional expressions, we can try some robot behaviors that involve decision making.
Exercise: Refrigerator Detective: As a child did you ever wonder if that refrigerator light was always on? Or did it shut off when you closed the door? Well, here is a way to find out. Build a refrigerator detective robot that sits inside the fridge and tells you if the light is on or off!
Exercise: Burglar Alarm Robot: Design a robot that watches your dorm door. As soon as the door opens, it sounds an alarm (beeps).
Exercise: Imagine your robot in an environment that has a 1.5 ft walled corridor going around a 2 ft by 2 ft square box. Write a program that will enable the robot to go around this box. One strategy you can use is to have the robot go forward in a straight line until it bumps into a wall. After a bump it will proceed to make a 90 degree turn (you may need to have it go backwards a little to enable turning room) and then continue again in a straight line.
Exercise: Write a robot program that goes straight and then stops when it detects a wall in front. You will be using the IR sensors for this task.
Exercise: A robot measuring device You have caliberated your robot with regards to how far it travel in a given amount of time. You can use that to design a robot that measures space. Write a program that enables a robot to measure the width of a hallway.
Exercise: Write a robot program to exibhit the following behavior: The robot porefers to stay near a wall (in front). If it does not have a wall in front of it, it moves forward until it finds it. Run your program first by placing it is a play pen (like the green table in your computer lab. Ensure that your program behaves as described. Next, place it on a floor and hold a blank piece of paper in front of it (close enough so the robot can detect it). Now, slowly move the paper away from the robot. What happens? (As soon as the paper moves out of robot's range, it should try to go forward to find another wall. It will stop again as soon as it finds one. By moving the paper you will be able to have the robot follow)
A little more about Python functions
Before we move on, it would be good to take a little refresher on writing Python commands/functions. In Chapter 2 we elarned that the basic syntax for defnining new commands/functions is:
def <FUNCTION NAME>(<PARAMETERS>): <SOMETHING> ... <SOMETHING>
The Myro module provides you with several useful functions (forward, turnRight, etc.) that enable easy control of your robot's basic behaviors. Additionally, using the syntax above, you learned to combine these basic behaviors into more complex behaviors (like wiggle, yoyo, etc.). By using parameters you can further customize the behavior of functions by providing different values for the parameters (for example, forward(1.0) will move the robot faster than forward(0.5)</tt)). At this point, you should also note a crucial difference between the movement commands like <tt>forward, <turnLeft, and commands that provide sensory data like getLight or getStall, etc. The sesory commands always return a value whenever they are issued. That is:
>>> getLight('left') 221 >>> getStall() 0
Commands that retrurn a value when they are invoked are called functions since they actually behave much like mathematical functions. None of the movement commands return any value, but they are useful in other ways. For instance, they make the robot do something. In any program you typically need both kinds of functions: those that do something but do not return anything as a result; and those that do something and return a value. You can already see the utility of having thse two kinds of functions from the examples you have seen so far. Functionsa are an integral and critical part of any program and part of learning to be a good programmer isto learn to recognize abstractions that can then be packaged into individual functions (like drawPolygon, or degreeTurn) which can be used over and over again. Functions that are used often can then be organized into groups or library modules. Most programming languages, Python included, provide a healthy selection of libraries of useful functions so you do not have to write them. Such libraries are often termed as an application programming interface or an API. In the previous chapter you were introduced to the random library provided by Python. It contains several useful functions that provide facilities for generating random numbers. Similarly, Python also provides a math library that provides often used mathematical functions. Some of them are listed below:
- Commonly used functions in the math module
- ceil(x) Return the ceiling of x as a float, the smallest integer value greater than or equal to x.
- floor(x) Return the floor of x as a float, the largest integer value less than or equal to x.
- exp(x) Return e**x.
- log(x[, base]) Return the logarithm of x to the given base. If the base is not specified, return the natural logarithm of x (that is, the logarithm to base e).
- log10(x) Return the base-10 logarithm of x.
- pow(x, y) Return x**y.
- sqrt(x) Return the square root of x.
- Trigonometric functions
- acos(x) Return the arc cosine of x, in radians.
- asin(x)Return the arc sine of x, in radians.
- atan(x) Return the arc tangent of x, in radians.
- cos(x) Return the cosine of x radians.
- sin(x) Return the sine of x radians.
- tan(x) Return the tangent of x radians.
- degrees(x) Converts angle x from radians to degrees.
- radians(x) Converts angle x from degrees to radians.
The module also defines two mathematical constants:
- pi The mathematical constant pi.
- e The mathematical constant e.
In order to use any of these all you have to do is import the math module:
import math >>> math.ceil(5.34) 6.0 >>> math.floor(5.34) 5.0 >>> math.exp(3) 20.085536923187668 >>> math.log10(1000) 3.0 >>> math.log(1024, 2) 10.0 >>> math.pow(2, 10) 1024.0 >>> math.sqrt(7.0) 2.6457513110645907 >>> math.sin(90) 0.89399666360055785 >>> math.sin(math.radians(90)) 1.0 >>> math.pi 3.1415926535897931 >>> math.e 2.7182818284590451 >>> math.sin(math.pi/2) 1.0
Alternately, you can import the math module as shown below:
>>> from math import * >>> ceil(5.34) 6.0 >>> floor(5.34) 5.0 >>> exp(3) 20.085536923187668 >>> log10(1000) 3.0 >>> log(1024,2) 10.0 >>> sqrt(7.0) 2.6457513110645907 >>> sin(radians(90)) 1.0 >>> pi 3.1415926535897931 >>> e 2.7182818284590451 >>> sin(pi/2) 1.0
Example: A Loan Calculator
As an example, consider the following problem:
Your current car, an adorable 1992 SAAB 93 was bought used and you have had nothing but trouble keeping the car on the road. Last night the ignition key broke off in the key slot when you were trying to start it and now the broken piece would not come out (this used to happen a lot with older SAAB's). The mechanic has to dismantle the entire ignition assembly to get the broken key out and it could cost you upwards of $500. The car's engine, which has done over 185,000 miles has left you stranded on the road many times. You have decided that this is it, you are going to go out and gte yourself a bradn new reliable car. You have been moonlighting at a restaurant to make extra money and have managed to save $5500.00 for exactly this situation. You are now wondering what kind of new car you can buy. Obviosly, you will have to take a loan from a bank to finance the rest of the cost but you are not sure how big a loan, and therefore what kind of car, you can afford. You can write a small Python program to help you try out various scenarios.
You can either dream (realistically) of the car you would like to buy, or you can go to any of the helpful car buying sites on the web (www.edmunds.com is a good place to start). You spend hours looking at features and options and finally narrow your desires to a couple of choices. Your first choice is going to cost you $22,000.00 and your second choice is priced at $18,995.00. Now you have to decide which of these you can actually afford to purchase.
First, you go talk to a couple of banks and also look at some loan offers on the web. For example, go to bankrate.com and look for current rates for new car loans.
Suppose the loan rates quoted to you are: 6.9% for 36 months, 7.25% for 48 months, and 7.15 for 60 months.
You can see that there is a fair bit of variation in the rates. Given all this information, you are now ready to write a program that can help you figure out which of the two choices you may be able to make. In order to secure the loan, you have to ensure that you have enough money to pay the local sales tax (6% sales tax on a $20,000 car will add up to $1200!). After paying the sales tax you can use the remainder of the money you have svaed up towards the downpayment. The remainder of the money is the amount that you would borrow. Depending on the type of loan you choose, your monthly payments and how long you will make those payments will vary. There is a simple formula that you can use to estimate your monthly payment:
<math>MonthlyPayment = \frac{LoanAmount * MonthlyInterestRate}
{1 - e^{-LoanTerm * \log(1+MonthlyInterestRate)}}
</math>
Whoa! That seems complicated. However, given the formula, you can see that it really requires two mathematical functions: log() and e(), both of which are available in the Python math module. Suddenly, the problem seems not too hard. Let us try and outline the steps needed to write the program:
First, note the cost of the car, the amount of money you have saved, and the sales tax rate Also, note the financials: the interest rate, and the term of the loan The interes rate quoted is generally the annual percentage rate (APR) convert it to monthly rate (by dividing it by 12) Next, compute the sales tax you will pay Use the money left to make a down payment Then determine the amount you will borrow Plug in all of the values in the formula and compute the monthly payment Also, compute the total cost of the car. Output all the results
Next, we can take each of the above steps and start to encode them into a program. Here is a first order refinement:
def main(): # First, note the cost of the car (Cost), # the amount of money you have saved (Cash), # and the sales tax rate (TaxRate) # Also, note the financials: the interest rate (APR), and the term of the loan (Term) # The interest rate quoted is generally the annual percentage rate (APR) # convert it to monthly rate (by dividing it by 12) (MR) # Next, compute the sales tax you will pay (SalesTax) # Use the money left to make a down payment (DownPayment) # Then determine the amount you will borrow (LoanAmount) # Plug in all of the values in the formula and compute the monthly payment (MP) # Also, compute the total cost of the car. (TotalCost) # Output all the results main()
Above, we have taken the steps and converted them into a skeletal Python program. All the steps are converted to Python comments and where needed, we have decided the anmes of variables that will hold the values that will be needed for the calculations. This is useful because this also helps determine how the formula will be encoded and also helps determine what values can be programmed in and which ones you will have to supply as input. Making the program require inputs will easli enable you to enter the different parameters and then based on the outputs you get, you can decide which car to buy. Let us encode all the inputs first:
def main(): # First, note the cost of the car (Cost), Cost = input("Enter the cost of the car: $") # the amount of money you have saved (Cash), Cash = input("Enter the amount of money you have saved: $") # and the sales tax rate (TaxRate) is 6% where you live. SalesTaxRate = 6.0 # Also, note the financials: the interest rate (APR), and the term of the loan (Term) # The interest rate quoted is generally the annual percentage rate (APR) APR = input("Enter the APR for the loan (in %): ") # convert it to monthly rate (by dividing it by 12) (MR) # Next, compute the sales tax you will pay (SalesTax) # Use the money left to make a down payment (DownPayment) # Then determine the amount you will borrow (LoanAmount) # Plug in all of the values in the formula and compute the monthly payment (MP) # Also, compute the total cost of the car. (TotalCost) # Output all the results main()
We have refined the program to include the inputs that will be needed for each run of the program. Notice that we chose not to input the sales tax rate and instead just assigned it to the variable SalesTaxRate). If you wanted, you could also have that be entered as input. What you choose to have as input to your program is your design decision. Sometimes the problem may be framed so it explicitly specifies the inputs, sometimes you have to figure that out. In general, whatever you need to make your program more versatile is what you have to base your decisions on. For instance, fising the sales tax rate to 6.0 will make the program usable in the area you live in. If, for example, you wanted your friend in another part of the country to use the program, you should choose to make that also an input value. Let us go on to the next steps in the program and encode them in Python. These are mainly computations. The first few are simple. perhaps the most complicated computation to encode is the forumla for computing the monthly payment. All of these are shown in the version below.
import math def main(): # First, note the cost of the car (Cost), Cost = input("Enter the cost of the car: $") # the amount of money you have saved (Cash), Cash = input("Enter the amount of money you have saved: $") # and the sales tax rate (TaxRate) is 6% where you live. SalesTaxRate = 6.0 # Also, note the financials: the interest rate (APR), and the term of the loan (Term) # The interest rate quoted is generally the annual percentage rate (APR) APR = input("Enter the APR for the loan (in %): ") # and the term of the loan (Term) term = input("Enter the length of loan term (in months): ") # convert it (APR) to monthly rate (by dividing it by 12) (MR) # also divide it by 100 since the value input is a % (percentage) MR = APR/12.0/100.0 # Next, compute the sales tax you will pay (SalesTax) SalesTax = Cost * SalesTaxRate / 100.0 # Use the money left to make a down payment (DownPayment) DownPayment = Cash - SalesTax # Then determine the amount you will borrow (LoanAmount) LoanAmount = Cost - DownPayment # Plug in all of the values in the formula and compute the monthly payment (MP) MR = (LoanAmount * MR) / (1.0 - math.exp(-term * math.log(1.0 + MR))) # Also, compute the total cost of the car. (TotalCost) TotalCost = SalesTax + DownPayment + MR * term # Output all the results print "Here are the details about your new car..." print "------------------------------------------" print print "Money you have saved $", Cash print "Cost of the car $", Cost print "Sales Tax rate is", SalesTaxRate, "%" print "Sales Tax on the car $", SalesTax print "Your down payment will be $", DownPayment print "The amount of money you will borrow will be $", LoanAmount print "This will be a", term, "month loan at", APR, "% APR" print "Based on this data, your monthly payment will be $", MP print "Your total cost at the end of the loan will be $", TotalCost print main()
When you enter the above program and run it in Python, you can enter the data about your car. here is a sample run:
Enter the cost of the car: $20000.00 Enter the amount of money you have saved: $5500.00 Enter the APR for the loan (in %): 6.9 Enter the length of loan term (in months): 36 Here are the details about your new car... ------------------------------------------ Money you have saved $ 5500.0 Cost of the car $ 20000.0 Sales Tax rate is 6.0 % Sales Tax on the car $ 1200.0 Your down payment will be $ 4300.0 The amount of money you will borrow will be $ 15700.0 This will be a 36 month loan at 6.9 % APR Based on this data, your monthly payment will be $ 484.052914723 Your total cost at the end of the loan will be $ 22925.90493
It appears that at for the 20000.00 car, for a 36 month 6.9% loan you will end up paying $484.05 (or $484.06 depending upon how your loan company round pennies!). In fact, when you need to restrict your output values to specific decimal places (two in the case of dollars and cents, for example), you can use the string formatting features built into Python. For example, in a string, you can specify how to include a floating point value as follows:
"This is the value of PI %5.3f expressed in three places after the decimal" % (math.pi)
The above is a Python expression that has the syntax:
<string> % <expression>
Inside the <string> there is a format specification beginning with a %-sign and ending in an f> What follows the %-sign is a numerical specication of the value to be inserted at that point in the string. The f in the specification referes to the fact it is for a floating point value. Between the %-sign and the f is a number, life 5.3. This specifies that the floating point number to be inserted will take up atleast 5 spaces, 3 of which will be after the decimal. One of the spaces is always occupied by the decimal. Thus, the specification %5.3f specifies a value to be inserted in the following manner:
"This is the value of PI -.--- expressed in three places after the decimal"
You can see the result of executing this in Python below:
>>> "This is the value of PI %5.3f expressed in three places after the decimal" % (math.pi) 'This is the value of PI 3.142 expressed in three places after the decimal' >>> "This is the value of PI %7.4f expressed in three places after the decimal" % (math.pi) 'This is the value of PI 3.1416 expressed in three places after the decimal'
In the second example above, we replaced the specification with %7.4f. Notice that the resulting string allocates seven spaces to print that value. If there are more spaces that needed they get padded by blanks on the leading edge (notice the extra space before 3 in the second example above). If the space specified is less, it will always be expanded to accomodate the number. For example:
>>> "This is the value of PI %1.4f expressed in three places after the decimal" % (math.pi) 'This is the value of PI 3.1416 expressed in three places after the decimal'
We deliberately specified that the value be 1 space wide with 4 sftaer the decimal (i.e. %1.4f). As you can see, the space was expanded to accomodate the value. What is assured is that the value is always printed using the exact number of spaces after the decimal. Here is another example:
>>> "5 is also %1.3f if expressed with three places after the decimal." % 5 '5 is also 5.000 if expressed with three places after the decimal.'
Thus, the value is printed as 5.000 (i.e. the three places after the decimal are always considered relevant in a specification like %1.3f. Similarly, for specifying whole number or integer values you can use the letter-d, and for strings you can use the letter-s:
>>> "Hello %10s, how are you?" % "Arnold" 'Hello Arnold, how are you?'
By default, longer specifications are right-justified. You can use a %- specification to left-justify. For example:
>>> "Hello %-10s, how are you?" % "Arnold" 'Hello Arnold , how are you?'
Having such control over printed values is important when you are trying to output tables of aligned values. Let us modify our program from above to use these formatting features:
import math def main(): # First, note the cost of the car (Cost), Cost = input("Enter the cost of the car: $") # the amount of money you have saved (Cash), Cash = input("Enter the amount of money you have saved: $") # and the sales tax rate (TaxRate) is 6% where you live. SalesTaxRate = 6.0 # Also, note the financials: the interest rate (APR), and the term of the loan (Term) # The interest rate quoted is generally the annual percentage rate (APR) APR = input("Enter the APR for the loan (in %): ") # and the term of the loan (Term) term = input("Enter the length of loan term (in months): ") # convert it (APR) to monthly rate (by dividing it by 12) (MR) # also divide it by 100 since the value input is a % (percentage) MR = APR/12.0/100.0 # Next, compute the sales tax you will pay (SalesTax) SalesTax = Cost * SalesTaxRate / 100.0 # Use the money left to make a down payment (DownPayment) DownPayment = Cash - SalesTax # Then determine the amount you will borrow (LoanAmount) LoanAmount = Cost - DownPayment # Plug in all of the values in the formula and compute the monthly payment (MP) MP = (LoanAmount * MR) / (1.0 - math.exp(-term * math.log(1.0 + MR))) # Also, compute the total cost of the car. (TotalCost) TotalCost = SalesTax + DownPayment + MP * term # Output all the results print "Here are the details about your new car..." print "------------------------------------------" print print "Money you have saved $%1.2f" % Cash print "Cost of the car $%1.2f" % Cost print "Sales Tax rate is %1.2f" % SalesTaxRate print "Sales Tax on the car $", SalesTax, "%" print "Your down payment will be $%1.2f" % DownPayment print "The amount of money you will borrow will be $%1.2f" % LoanAmount print "This will be a %2d month loan at %1.2f APR" % (term, APR) print "Based on this data, your monthly payment will be $%1.2f" % MP print "Your total cost at the end of the loan will be $%1.2f" % TotalCost print main()
When you run it again (say for a slightly different loan term, you get: Enter the cost of the car: $20000.00 Enter the amount of money you have saved: $5500.00 Enter the APR for the loan (in %): 7.25 Enter the length of loan term (in months): 48 Here are the details about your new car...
Money you have saved $5500.00 Cost of the car $20000.00 Sales Tax rate is 6.00 Sales Tax on the car $ 1200.0 % Your down payment will be $4300.00 The amount of money you will borrow will be $15700.00 This will be a 48 month loan at 7.25 APR Based on this data, your monthly payment will be $377.78 Your total cost at the end of the loan will be $23633.43
</pre>
You can see that for the same amount if you borrow it for a longer period you can reduce your monthly payments by over $100 (but you pay about $700 more in the end).
Exercise: Try out this program with the other cars data and for all the loan terms.
Writing functions that return values
Now that you know a little more about how to use functions that return values, it is also time to learn how to write your own functions that return values. Python provides a return-statement that you can use inside a function to return the results of a function. For example:
def triple(x): # Returns x*3 return x * 3
The function above can be used just like the onesyou have been using:
>>> triple(3) 9 >>> triple(5000) 15000
The general form of a return-statement is:
return <expression>
That is, the function in which this statement is encountered will return the value of the <expression>. Thus, in the example above, the return-statement returns the value of the expression 3*x, as shown in the example invocations. By giving different values for the parameter x, the function simply triples it. We can use this new found idea in the car loan example above by writing a function called monthlyPayment that can be used to compute the monthly payment:
def monthlyPayment(A, MR, T): # Given a loan amount, A # the monthly interest rate, MR # and the term (in months), T # compute and return the monthly payment return (A * MR) / (1.0 - math.exp(-T * math.log(1.0 + MR)))
Then, we can rewrite the computation in the main programs as follows:
# Plug in all of the values in the formula and compute the monthly payment (MP) MP = monthlyPayment(LoanAmount, MR, term)
Go ahead and try it. Modify the lateset version o fthe car loan program from above to use the new function and compare results. They should be the same.
Functions can have zero or more return-statements. Some of the functions you have written, like wiggle do not have any. Technically, when a function does not have any return statement that returns a value, the function returns a special value called None. This is already defined in Python.
Functions, as you have seen, can be used to package useful computations and can be used over and over again in many situations. Before we conclude this section, let us give you another example of a function. Recall from above the robot behavior that enables the robot to go forward until it hits a wall. One the the program fragments we used to specify this behavior is shown below:
while not getStall(): forward(1) stop()
In the above example, we are using the value returned by getStall to help us make the decision to continue going forward or stopping. We were fortunate here that the value returned is directly usable in our decision making. Sometimes, you have to do little interpretation of sensor values to figure out what exactly the robot is sensing. You will see that in the case of light sensors. Even though the above statements are easy to read, we can make them even better, by writing a function called stuck() as follows:
def stuck(): # Is the robot stalled? Returns True if it is and False otherwise. return getStall() == 1
The function above is simple enough, since getStall already gives us a usable value. But now if we were to use stuck to write the robot behavior, it would read:
while not stuck(): forward(1) stop()
As you can see, it reads much better. Programming is a lot like writing in this sense. As in writing, there are several ways of expressing an idea in words. Some are better and more readable than others. Some are downright poetic. Similarly, in programming, expressing something in a program can be done in many ways, some better and more readable than others. Programming is not all about functionality, there can be poetry in the way you write a program.
Decision Making in Computer Programs
Decision making is central to all computer programs. In fact there is a famous theorem in computer science that says that if any programmable device is capable of being programmed to do sequential execution, decision making, and repetition, it is capable of expressing any computable algorithm. This is a very powerful idea that is couched in terms of very simple ideas. Given a problem, if you can describe its solution in terms of a combination of the three execution models then you can get any computer to solve that problem. In this section, we will look at decision making in a classic game playing situation.
Computers have been extensively used in game playing: for playing games against people and other computers. The impact of computers on game playing is so tremendous that these days several manufacturers make game playing consoles with computers in them that are completely dedicated to game playing.
Let us design a simple game that you have most likely played while growing up: Paper, Scissors, Rock!
In this game, two players play against each other. At the count of three each player makes a hand gesture indicating that they have selected one of the three items: paper (the hand is held out flat), scissors (two fingers are extended to designate scissors, or rock (indicated by making a fist). The rules of deciding the winner are simple: if both players pick the same object it is a draw; otherwise, paper beats rock, scissors beat paper, and rock beats scissors. Let us write a computer program to pay this game against the computer. Here is an outline for playing this game once:
# Computer and player make a selection # Decide outcome of selections (draw or who won) # Inform player of decision
If we represent the three items in a list, we can have the computer pick one of them at random by using the random number generation facilities provided in Python. If the items are represented as:
items = ["Paper", "Scissors", "Rock"]
Then we can select any of the items above as the computer's choice using the expression:
# Computer makes a selection myChoice = items[<0 or 1 or 2 selected randomly>]
That is items[0] represents the choice "Paper", items[1] represents "Scissors", and items[2] is "Rock". We saw, in Chapter 4, that we can generate a random number in any range using the randrange function from the random library module in Python. Thus we can model the computer making a random selection using the following statement:
from random import * # Computer makes a selection myChoice = items[randrange(0, 3)]
Recall that randrange(n, m) returns a random numbers in the range [n..m-1]. Thus, randrange(0, 3) will return either 0, 1, or 2. We can use the askQuestion command in Pyro to ask the player to indicate their selection (see Chapter 3).
# Player makes a selection yourChoice = askQuestion("Pick an item by clicking on it.", items)
Now that we know how to the computer and player make their selection, we need to think about deciding the outcome. Here is an outline:
if both picked the same item then it is a draw if computer wins then inform the player if player wins then inform the player
Rewriting the above is Python using if-commands we get the following first draft:
if myChoice == yourChoice: print "It is a draw." if <myChoice beats yourChoice>: print "I win." else: print "You win."
All we need to do is figure out how to encode <myChoice beats yourChoice>. The condition has to capture the rules of the game mentioned above. We can encode all the rules in a conditional expression as follows:
if (myChoice == "Paper" and yourChoice == "Rock") or (myChoice == "Scissors" and yourChoice == "Paper") or (myChoice == "Rock" and yourChoice == "Scissors"): print "I win." else: print "You win."
The conditional expression above captures all of the possibilities that should be examined in order to make the decision. Another way of writing the above decision would be to use the following:
if myChoice == "Paper" and yourChoice == "Rock": print "I win." elif myChoice == "Scissors" and yourChoice == "Paper": print "I win." elif myChoice == "Rock" and yourChoice == "Scissors": print "I win." else: print "You win."
That is each condition is examined in turn until one is found that confirms that the computer wins. If none such condition is true, the else-part of the if-statement will be reached to indicate that the player won.
Another alternative to writing the decision above is to encode the decision in a function. Let us assume that there is a function beats that returns True or False depending on the choices. We could then rewrite the above as follows:
if myChoice == yourChoice: print "It is a draw." if beats(myChoice, yourChoice): print "I win." else: print "You win."
Let us take a closer look at how we could define the beats> function. It needs to return <tt>True if myChoice beats yourChoice. So all we need to do is encode the rules of the game described above. Here is a draft of the function:
def beats(me, you): # Does me beat you? If so, return True, False otherwise. if me == "Paper" and you == "Rock": # Paper beats rock return True elif me == "Scissors" and you == "Paper": # Scissors beat paper return True elif me == "Rock" and you == "Scissors": # Rock beats scissors return True else: return False
Once again, we have used the if-statements in Python to encode the rules of the game. Now that we have completely fleshed out all the critical parts of the program, we can put them all together as shown below:
# A program that plays the game of Paper, Scissors, Rock! from myro import * from random import randrange def beats(me, you): # Does me beat you? If so, return True, False otherwise. if me == "Paper" and you == "Rock": # Paper beats rock return True elif me == "Scissors" and you == "Paper": # Scissors beat paper return True elif me == "Rock" and you == "Scissors": # Rock beats scissors return True else: return False def main(): # Play a round of Paper, Scissors, Rock! print "Lets play Paper, Scissors, Rock!" print "In the window that pops up, make your selection>" items = ["Paper", "Scissors", "Rock"] # Computer and Player make their selection... # Computer makes a selection myChoice = items[randrange(0, 3)] # Player makes a selection yourChoice = askQuestion("Pick an item by clicking on it.", items) # inform Player of choices print print "I picked", myChoice print "You picked", yourChoice # Decide if it is a draw or a win for someone if myChoice == yourChoice: print "We both picked the same thing." print "It is a draw." elif beats(myChoice, yourChoice): print "Since", myChoice, "beats", yourChoice, "..." print "I win." else: print "Since", yourChoice, "beats", myChoice, "..." print "You win." print "Thank you for playing. Bye!" main()
A few more print commands were added to make the interaction more natural.
Exercise: Implement the Paper, Scissors, Rock program from above and play it several times to make sure you understand it completely.
Exercise: Modify the program above to play several rounds. Also, incorporate a scoring system that keeps track of the number of times each player won and also the number of draws.
Previous Chapter: Chapter 4, Up: Introduction to Computer Science via Robots, Next Chapter: Chapter 6