Functions Case Study: Wandering Grue
Let's say you'd like to have a wandering Grue in your Mysterious House that pops up in a room (or rooms) randomly.
One way to do this is to make a function that decides whether the Grue is in the room or not:
Test Frameowrk
Later in our complete code, we will see code similar to:
eaten=grue()
if eaten:
    print "Sadly, you were torn limb-from-limb by the Grue and suffered a slow, painful death."
else:
    print "Congratulations, you have not been eaten by the Grue! May you have a long happy life."
Here we introduce a new python data type:  boolean.  Python booleans can take two values True and False.  These code fragments are  
equivalent:
   if x > 3:
       print "yep!"
   
   if x > 3 is True:
       print "yep!"
   
   if bool(x>3) is True:
       print "yep!"
The "if" syntax implies if predicate is True.  In Python, most things 
evaluate to True, except:  None, False, 0 (0.0, etc.), 
empty strings, zero-length lists, dicts, and few other oddities [1].  
Variation 1:  not very random 
def grue_always():
    ''' this grue always appears.  returns true'''
    return True
Our not very random grue always appears.
Exercise:  Make the reverse -- a grue that never appears.  The signature
of the function should be grue_never() -> False
Variation 2:  the 50/50 Grue 
import random 
## random is a Python module, as mentioned above. 
## We need to import it to access the random() function.
## now to begin the function definition
## Everything inside the function definition is indented! Remember, white space matters!
def random_grue(): 
    ''' boolean.  a grue that appears 50% of the time '''
    ## we want something that will return True 50% of the time.
    ## one method:  get a random float between (0,1), and return True if it's over .5
    ## now we need a random number. random() will give us one between 0 and 1
    n=random.random() ## the random before the dot tells Python what module to look in for the function, which is the one we imported above
    if n > 0.5:
        grue=1 ## 1 == True
    else:
        grue=0 ## 0 == False
    return grue ## returning allows us to capture the value
So what does the random_grue() function do? Let's try it. In the Python interpreter:
    >>> import random
    >>> def random_grue():
            n=random.random()
            if n>0/5:
                    grue = 1
            else:
                    grue = 0
            return grue
  
    >>> random_grue()
    1
The first command is to import the random module, the second is to define the function and the third is to actually call the function. The 1 is the return of the function. You may get a 1 or a 0 depending on the number random() generated. (Hint: try running it several times)
> A digression on pseudorandom numbers, and tips for getting the same ones every time!
Variation 2: the Moody Grue
import random 
def grue_moody(cutoff=.5): 
    ''' boolean.  a grue that appears (100*cutoff)% of the time '''
    n=random.random() 
    above_cutoff = n < cutoff
    return above_cutoff
def grue_moody2(cutoff=.5): 
    ''' boolean.  a grue that appears (100*cutoff)% of the time '''
    return random.random() < cutoff
Note that we simplified down the function quite a bit by returning the boolean
value directly (especially in 'grue_moody2'), rather than doing any conditional logic 
to get a 1 or 0.  Also notices that we specified a default value for 
the argument cutoff.  
Exercises
- Predict the behaviour of these functions calls.  Then try them.  
- grue_moody()
- grue_moody(-1)
- grue_moody(1)
- grue_moody("a")
- grue_moody([1,2,3])
 
- Fix the code so that it prints an angry message and returns Noneif n is outside the interval (0,1).
- try help(grue_moody). What do you see?
- what are the types of random,random.random,random.random()
Variation 3:  the location, location, location Grue 
In our final variation, we want a grue that:
- has different percentages of appearing based on which page
- should have zero chance of appearing in the main room "foyer"
- should have a default chance of appearing of 5% in rooms that aren't otherwise described
import random 
grue_fractions = { 'foyer':0, 'thedark': 1.0, 'nocake': .2 }
def location_grue(room=None, base=.05, cutoffs=dict()): 
    ''' (boolean), does a grue appear in the room?
    room : str room name
    base : 'cutoff', float between (0,1), for base (room not found)
    cutoffs: dict of room_name: cutoff (float between 0,1)
    '''
    cutoff = cutoffs[room]
    return random.random() < cutoff
Exercises
- as written, location_gruehas some bugs, and doesn't meet spec. Identify and fix them.- try: location_grue('foyer', cutoffs=grue_fractions)
- what happens if 'room' isn't in 'cutoffs'?
- research the 'get' method of dictionarys... help({}.get). Use this method to fix the code.