Program 3

Intermediate Scripts

ICS-31: Introduction to Programming


Introduction This programming assignment is designed to ensure that you know how to write scripts that combine the standard control structures in Python: blocks, ifs, for loops, while loops, break, and try/except statements. You will also continue gaining experience with the more basic Python features: assignment and import statements and expressions (that use arithmetic, relational, logical, and textual operators -including lots of logical/bool expressions). Finally, you will practice writing, testing, and debugging scripts using iterative enhancement: a divide-and-conquer technique for building and testing scripts a bit at a time.

Please follow the instructions below for each script: finish each enhancement before continuing to the next one (including printing whatever messages it displays in the console, copied exactly). Import whatever modules/functions you think appropriate, using whatever form of import you think appropriate (try to use both forms appropriately). Use short (but not overly short) well-chosen names. For indefinite loops, please write while True: loops with if/break statements in their blocks; when you are finished, you may transform the while test if the result is simpler.

You will create each modules in a new project folder, and submit each module separately in Checkmate. Only one student should submit all parts of the the assignment, but both student's names should appear in the comments at the top of each submitted .py file. It should look something like


# Romeo Montague, Lab 1
# Juliet Capulet, Lab 1
# We certify that we worked cooperatively on this programming
#   assignment, according to the rules for pair programming
Please turn in each script as you finish it, so that I can accurately assess the progress of the class as a whole during this assignment.

Print this document and carefully read it, marking any parts that contain important detailed information that you find (for review before you turn in the files). Reread the section on Time Management from Programming Assignment 0 before starting this assignment.

Finally, for your information, I am listing below the number of lines in my solution scripts. These scripts are formated in the standard way. I am counting only lines with code; but I am not counting blank lines nor lines that are comments. My darts script is 10 lines (although with blank lines and comments it is 19 lines); my compressmodule script is 19 lines (although with blank lines and comments it was 26 lines); my montyhall script is 34 lines (although with blank lines and comments it is 42 lines); Your scripts might be smaller, and they might be larger; but if your script starts exceeding 2-3 times the size of mine, you might want to rethink your approach (or come get some help). Speaking of lines, I found it useful to put \ on one line to continue it on the next line (so the line wouldn't be too long).


Calculating π with Randomly Thrown Darts Write a script that performs the following tasks.
  • Prompt the user for the number of darts to throw (accept only numbers greater than 0)
  • Simulate throwing that many darts at a 2x2 square whose center is the origin, by using a random number generator, constrained by the square's boundary, to generate x and y coordinates for each dart.
  • Approximate the value of π (pi) by computing the ratio of the count of those darts that land inside a circle inscribed in the square divided by the count of darts that landed inside the square (which is the the total number of darts thrown, since all darts are constrained to land in the square).
That calculation approximates the ratio of the circle's area to the square's, illsutrated below

To simulate throwing darts, we will call the uniform method imported from the random module. Its header is random.uniform(min : float, max : float) -> float: When called, it returns random float between the values of min and max inclusive (e.g., in the range [min,max]) such that each value is equally likely (that is what uniform means; their are other distributions like normal that the random module can produce). From the picture above, the x and y coordinates we compute for each dart should be between -1 and +1.

Use a bool expression to determine whether or not the coordinates of the dart are inside or outside the circle that is inscribed in the square (remember some geometry: it is inside if the distance from the origin to the dart's coordinate is less than or equal to the radius, which is 1).

Design, code, test, and debug this script using iterative-enhancement, as 6 mini-scripts. Test each script to ensure that it is correct before proceeding to the next enhancement; simplify your code as much as possible before proceeding. This is the same methodology that we will use for larger scripts; so, it is a good idea to practice this technique here, where the script is small, even if you can write the entire script all at once. Later, we will discuss this technique in greater depth.

  1. Write a kernel script that prompts the user for the number of darts to throw, and then loops that many times, printing the literal 'Dart Thrown' for each loop iteration. Ensure the user is prevented from entering zero or a negative number. Ensure the number of times Dart Thrown is printed is the same as the requested number of darts (it is easy to be off by 1). Run the script a few times, entering a few different small values to test it.

  2. Enhance the script so that after it prints Dart Thrown, it simulates throwing a dart and prints the random x- and y-coordinates of each dart (store values into these names and print them). Ensure that these values change, and are all between -1. and +1. and seem random.

  3. Enhance the script so that after it prints the x- and y-coordinates of each dart, it prints either "Inside Circle" or "Outside Circle" for that dart's coordinates. Using a calculator, hand check that the inside/outside calculation is computing its result correctly for a few darts, ensuring it is printing each message correctly.

  4. Enhance the script to keep track of (count) the number of darts that land inside the circle, and display this value during each iteration of the loop. Ensure that for each dart that lands inside the circle, this count increments by one. IMPORTANT REQUIREMENT: All counters in Python should be of type int; later you might use these int values to compute float values: recall dividing two int values results in a float value.

  5. Enhance the script to approximate π (pi) after the loop termintes by computing the ratio of darts that land in the circle to the total number of darts thrown, and multiplying this value by 4 (the area of the circle) according to the formula above. Display this result before terminating the script.

  6. Remove all intermediate output statements. Actually, a better idea is to put each itermediate print into a comment, so we could uncomment the line to get the print back into the scritp. Only the prompt and the final answer should appear on the console.
Here is an example of my script running (before commenting the extra prints). If there is anything wrong with the script, we should be able to detect some problem by carefully examining this output. Of course because we are using random numbers, your answer may be different.
  Enter number of darts to throw: 4
  Dart thrown
    x = -0.381578344458126 / y = 0.9986559633141296
    Outside Circle
    Inside count = 0
  Dart thrown
    x = 0.6230088145199804 / y = -0.6090162025597516
    Inside Circle
    Inside count = 1
  Dart thrown
    x = -0.6099645490335772 / y = 0.40343888285810103
    Inside Circle
    Inside count = 2
  Dart thrown
    x = 0.02227869342945632 / y = -0.6092767196745654
    Inside Circle
    Inside count = 3
  pi is approximately = 3.0

Here is an example of my script running (which took about 3 seconds). Of course because we are using random numbers, your answer may be different.

  Enter number of darts to throw: 1000000
  pi is approximately = 3.142088

Hand in ONLY THE FINAL ENHANCEMENT of the script: the one meeting the complete specifications, with no intermediate output produced. Test it by throwing 10; 100; 1,000; 10,000; 100,000, 1 million, and 10 million darts (try a few billion, if you have a fast computer or have a lot of time on your hands). Also, try the same number of darts more than once and observe that the computed value changes, because the darts are thrown randomly each time. Typically, the more darts thrown, the better the answer approximates the true value of π (pi): 3.141592653589793....


Compressing a Python Module Write a script that performs the following tasks.
  • Prompt the user for the name of an input file (a string, representing the name of a module file, to which you will append .py). Eclipse expects to find a file with this name in your project folder.
  • Open an output file with the same name as the input file, but with compressed- prepended.
  • Echo lines from the input to output files, but remove all blank lines and the comment parts of lines.
Here is a kernel script that does something similar but simpler: it echoes information from the input file to the console and an output file, adding a line number at the front of each line. Notice that if the second argument to open is 'w' it opens a file for writing; if the second argument to open is absent, its default value is 'r' for reading files. Finally the print method has another parameter we have not used (but will now use) named file: if it is absent its dfault value specifies the console; if it is present, it must be some file object open for writing. Semantically, it prints the information specified, but into a file (not the console).
import prompt

in_file_name  = prompt.for_string('Enter file name')
in_file       = open(in_file_name)
out_file_name = 'out-'+in_file_name
out_file      = open(out_file_name,'w')

line_no = 0
for l in in_file:
    line_no += 1
    ls = l.rstrip()
    print(line_no, ls))                 # print to console
    print(line_no, ls, file=out_file)   # print to file

When you run this code the first time, you might have to refresh the project to see the newly create file appear in the PDev Package Explorer window.

Design, code, test, and debug this script using iterative-enhancement, as 5 mini-scripts. Test each script to ensure that it is correct before proceeding to the next enhancement; simplify your code as much as possible before proceeding. This is the same methodology that we will use for larger scripts; so, it is a good idea to practice this technique here, where the script is small. Soon, we will discuss this technique in greater depth.

  1. Create a module file for this script in the project folder for this assignment and copy this as the kernel script there. Remove the code for processing line numbers. Update the prompting and input/output (read/write) file names to conform to the specification.

  2. Enhance the script to not echo (to the console or the output file) comments: recall that comments extend from the first # to the end of the line (we will assume no # characters appear in string literals, which would cause a problem). Hint: use the .find method discussed/used in class.

  3. Enhance the script to not echo (to the console or the output file) any blank lines: note that blank lines contain no characters at all, or contain only spaces. Hint: use the .replace method to determine if a string is blank. Semantically, the assignment r = s.replace('x','y') binds r to a new string that has the same characters as s, except every x is replaced by a y. Practice using remove in the Python interpreter to better understand it.

  4. Enhance the script so that if the user enters the name of an input file that Python cannot find (in which case Python raises the FileNotFoundError) exception, the user is repeatedly prompted to enter a file name until Python can successfully find the file the user enters (thus no exception is raised). Hint: try/except.

  5. Enhance the script to not echo (to the console) any lines from the input file; lines are sent to the output file only.
Here is an example of my script running; note that I have copied the craps.py module into the project folder to use as data.
  Enter file name: crops
    Cannot find file: crops.py
  Enter file name: craps
  Compressed file contents written in file: compressed-craps.py
The actual contents of compressed-craps.py is as follows. Compare it to the crapslpy contents. It is interesting that we can use some programs as data to other programs.
  from goody     import irange
  from dice      import Dice
  from stopwatch import Stopwatch
  import prompt
  import predicate
  win_count     = 0                            
  lose_count    = 0
  dice          = Dice([6,6])
  game_timer    = Stopwatch()
  games_to_play = prompt.for_int('Enter 
  game_timer.start()
  for game in irange(1, games_to_play):        
      first_roll = dice.roll().pip_sum()       
      if first_roll == 7 or first_roll == 11:
          win_count += 1                       
      elif first_roll == 2 or first_roll == 3 or first_roll == 12:
          lose_count += 1                      
      else:                                    
          point = first_roll                   
          while(True):                         
              roll = dice.roll().pip_sum()
              if roll == point:                
                  win_count += 1               
                  break
              elif roll == 7:                  
                  lose_count+= 1               
                  break
  game_timer.stop();
  print('  Raw Wins/Lose =', '{:,}'.format(win_count), '/', '{:,}'.format(lose_count))
  print('  % Wins/Lose   =', 100.0*win_count/(win_count+lose_count), '/', 100.0*lose_count/(win_count+lose_count))
  print()
  print('  Dice Thrown   =', '{:,}'.format(dice.rolls()))
  print('  Avg Dice/game =', dice.rolls()/games_to_play)
  print()
  print('  Elapsed Time  =' , game_timer.read(), 'seconds')
Hand in ONLY THE FINAL ENHANCEMENT of the script: the one meeting the complete specifications, with no intermediate output produced.

Monty Hall
Let's Make a Deal
In the 1970s, a popular TV game show was "Let's Make A Deal", whose host was Monty Hall. A typical contestant would be shown three doors: behind one door was a valuable prize; behind the other two doors were "goats" (worthless prizes). The contestant would first pick a door. Then, Monty would often show them a goat prize behind one of the other doors; no matter what door the contestant picked, there was always one other door with a goat prize behind it for Monty to show them, and Monty knows what is behind every door. Next, Monty would often ask the contestant whether or not he/she wanted to keep the door they originally chose, or switch to the other door (the one not shown by Monty).

Is there an advantage to staying with your door? Is there an advantage to switching doors? Are both options equally good (in terms of how likely the contestant will win the valuable prize)? You could watch many shows and keep statistics on what strategy the contestant used and whether he/she won or lost with that strategy. Or, we can write a script that can simulate very many shows, to determine if there is a best strategy, and what it is. Although such a computer simulation will tell you which strategy is best, it won't tell you why that strategy is best, but knowing which might help you figure out why.

You may also want to use your reasoning powers to deduce the correct answer before running your script, but this problem is notoriously difficult to solve correctly. Marilyn vos Savant (she writes the column, "Ask Marilyn", in Parade Magazine; she supposedly has the highest IQ of anyone in the US) discussed this problem in her column in the early 1990s and it generated a huge volumne of mail, many from PhDs in mathematics who (incorrectly) disagreed with her analysis. If you do a web search, you'll find lots written about the Monty Hall problem. But before you do, try to come up with your own solution to this problem. In fact, it appears in the following clip from the movie 21; the student supplies the answer but does not justify the solution. The previouis descrioption is a simplification of the real game show. You can watch various episodes of Let's Make a Deal on You Tube.

What is interesting here is that different people can each effectively argue that their solution is correct (because the problem is subtle, and many wrong solutions sound right). Once someone has formulated a solution, it is difficult to convince them that they are wrong. Yet, we can write a simple computer program to determine which, if any, strategy is best: even given such evidence, people have a hard time "giving up" on an incorrect solution.

So, you will write a script that prompts the user for the strategy to use (switch to the remaining door or stay with the door you chose originally), the number of games to play (accept only numbers greater than 0), and whether to print a detailed trace of the script; then it simulates playing that many games, keeping track of how often playing the chosen strategy wins. The script prints the statistics it collects (the number of times the player won and lost) at the end: from these numbers, you can deduce which strategy is best (if there really is a significant difference).

To simulate choosing doors, we will use the randint function in the random module. Its header is random.randint(min : int, max : int) -> int: When called, it returns random int between the values of min and max inclusive (e.g., in the range [min,max]) such that each value is equally likely. Before playing, a random door is chosen as the "good prize" door,and a random door is chosen as the "contestant's door", and then Monty chooses the door to expose: it can't be either the good prize or chosen door.

When you need to choose a random door, call this function and bind the results to the appropriate name (e.g., prize_door, chosen_door, exposed_door) for use in subsequent statements in the script.

Design, code, test, and debug this script using iterative-enhancement, as 6 mini-scripts. Test each script to ensure that it is correct before proceeding to the next enhancement; simplify your code as much as possible before proceeding. This is the same methodology that we will use for larger scripts; so, it is a good idea to practice this technique here, where the script is small. Soon, we will discuss this technique in greater depth.

  1. Write a kernel script that prompts the user for the number of games to play, and then loops that many times, printing the literal "Game Played" for each loop iteration. Ensure the number of times Game Played is printed is the same as the requested number of games (enter a few different small values to test it).

  2. Enhance the script so that each time a game is played, the script first randomly chooses which door has the prize behind it and which door the contestant chooses; print these doors for each game on one line (e.g., Prize behind door 1 / Contestant chooses door 2 -of course, these doors may have the same number). After printing these choices, the script should prompt the user (in this enhancement, the user plays Monty Hall) for which door number to expose to the contestant; only allow doors 1-3. Print this door number too (e.g., Monty exposes door 3); assume Monty chooses a door that is neither the prize nor the contestant's door.

  3. Enhance the script to first prompt for whether or not to use the switch strategy: whether or not for the simulated player to switch doors once Monty exposes his door. Bind this bool value to a name and use it to determine when the player wins/loses Then, after each game is played, print whether or not the player won or lost with that strategy (based on the strategy and all the chosen and prize doors). Solving this part requires writing a boolean expression using all the known information collected here and in the previous enhancement. Ensure this expression computes the correct value, first by oval diagrams for different random values, then by running the script.

  4. Enhance the script to keep track of the number of times that the player won. When printing whether the player won or lost, print the total number of times that they have won. After the loop terminates, print the total number of games played and the total number of times they have lost or won.

  5. Enhance the sciprt to randomly choose which door to expose (so Python can run this simulation at a high speed, without bothering the user to enter this value). Do this by repeatedly (in a while loop) choosing any random door, but terminating the loop (allowing that choice) only when the choice is not the door chosen by the contestant, nor the door containing the prize . Display each attempt (indented more) and the final choice for which door to expose after terminating (written in an enhancement above). Your sciprt should be printing information identically to mine, when mine is run with a detailed trace.

  6. Enhance the script to prompt for whether or not to provde a detailed trace. Bind this bool value to a name. Execute each of the the intermediate/tracing output statements only if this tracing variable is bound to True (put every tracing-print in an if statement controlled by this name). If this tracing name is bound to False, only the answers in the final output should appear in the console (preceeded by a blank line): a line printing the number of games played, and the number of times a player using the selected strategy won and the number of times the player lost.
Here is an example of my script running (with tracing on). With tracing on, if there is anything wrong with the script, we should be able to detect some problem by carefully examining this output. Of course because we are using random numbers, your answer may be different.
  Switch when asked?: True
  Trace game?: True
  Enter number of games to play: 4
  Game Played
    Prize behind door 1 / Contestant chooses door 2
      trying to expose door 3
    Monty exposes door 3
    Player won; has won 1 times
  Game Played
    Prize behind door 3 / Contestant chooses door 1
      trying to expose door 3
      trying to expose door 2
    Monty exposes door 2
    Player won; has won 2 times
  Game Played
    Prize behind door 2 / Contestant chooses door 3
      trying to expose door 3
      trying to expose door 3
      trying to expose door 3
      trying to expose door 2
      trying to expose door 3
      trying to expose door 1
    Monty exposes door 1
    Player won; has won 3 times
  Game Played
    Prize behind door 1 / Contestant chooses door 1
      trying to expose door 2
    Monty exposes door 2
    Player lost; has won 3 times
  
  4 games played
    3 games won
    1 games lost

Here is an example of my script running (which took about 15 seconds). Of course because we are using random numbers, your answer may be different.

  Switch when asked?: True
  Trace game?: False
  Enter number of games to play: 1000000

  1000000 games played
    666861 games won
    333139 games lost 

Hand in ONLY THIS FINAL ENHANCEMENT of the script: the one meeting the complete specifications and printing just six lines of output, including the three prompts. Test it by playing 10; 100; 1,000; 10,000; 100,000, and 1 million games. Also, try the same number of games more than once and observe that the computed values change, because the doors are chosen randomly each time. Typically, the more games played, the more accurate the answer is.