Program 4

Programming with Classes

Introduction to Computer Science I
ICS-21


Introduction Please print a copy of this assignment, read it carefully, and highlight material you think will be useful to you while you are working on the program or submitting it.

This programming assignment is designed to ensure that you know how to write programs that use constructors, methods, and fields (although rarely) in other classes. Of course, you will continue gaining experience with the standard control structures in Java (blocks, ifs, fors, breaks, try/catch) as well as the more basic Java features (declarations and expression statements using arithmetic, state-change, relational, and logical operators).

You will write four programs in this assignment. As always, you can check the behavior of your programs against mine by downloading, unzipping, and then running the file Program #4 Executables, to help you understand the specification of the problem and observe the programmer/user interaction that you are to implement. See Program #1 for details on how to run these executables on both PCs and in Eclipse (PCs and Macs). Remember, you can run these programs, but not examine their source (Java) code. Copy the input/output form of the executable programs in the programs that you write: use exactly the same prompts and messages.

For your information, I am listing below the number of lines in my solution programs. These programs are formated in the standard way. I am counting only lines with code (even if the only code on the line is a brace that closes a block); but I am not counting blank lines norlines filled with comments. My "big collatz" program is 24 lines; my "dice ewar" program is 72 lines; my "phone database" program is 37 lines; my "heart monitor" program is 67 lines. Your programs might be smaller, and they might be larger; but if your program starts going over 2-3 times the size of mine, you might want to rethink it (or come get some help).

Please follow the instructions below for each program: finish each enhancement before continuing to the next one (including printing whatever messages it displays in the console, copied exactly).

The Class Examples program illustrates the use of many classes, including the ones that you will need to use in this assignment. The last two programs require writing nested loops: one loop inside another. I used multiple break statements inside some of these loops.

To work on this assignment, create one Java project (call it Program4) and create four new Java classes in it. Each class will contain a program that you will write to solve one problem; name the classes BigCollatz, DiceWar, PhoneDatabase, and ICD. Write, run, and debug each class/program as you did in Program #3. When you finish each part, submit its .java file.

Only one programmer of the pair should dropoff the programs: the same one for each part. It doesn't matter which of the pair submits, but that person should submit all the parts. Of course, each program should contain both student names (in the comment: the same one you cut, pasted, ane filled in at the top of each program in Program #1).


Collatz with BigInteger Don't start this project with the new project folder. Instead, download the Program4 project folder, unzip it, and start a new project in Eclipse using this project folder (which contains a Collatz.java program that works for int values). Create the DiceWar and PhoneDatabase classes here.

When compiled and run, the Collatz.java class prompts the user for an int value and performs the Collatz process (read the comments for all the details) on that number until it is "reduced" to 1. We have seen this program before, when working with the Eclipse Debugger.

This program requires you to use objects constructed from the BigInteger class; remember to import it.

For this programming assignment, enhance this program to use variables that refer to BigInteger objects rather than store int primitives. In this way, there is almost no limit on the size of number that we can apply the Collatz process to, checking whether all values are ultimately reduced to 1 (which happens for all examples I've tried, and happens very quickly).

Fundamentally what you are doing in this program is translating from the use of primitive types and values to the use of reference types and objects. This is a good starting point for switching to object-oriented programming. It will involve importing the BigInteger class (see Javadoc for its full name, including its package). In addition, you should...

  • Use constructors for BigInteger objects to initialize variables/constants, instead of using int literals -there are no BigInteger literals! Also note that the BigInteger class declares some public final constants which you should find useful.
  • Use methods like .divide, .multiply, and .add to perform arithmetic on BigInteger values (instead of using arithmetic operators -they do not work for BigInteger values). Note that there are no state-change operators for BigInteger objects (e.g., no ++): like String, it is an immutable class.
  • Use methods like .equals and/or .compareTo to compare BigInteger values (instead of using relational operators: remember that == probably doesn't do what you want done!)
Check the Prompt class for a useful input method. You should also make good use of cascaded method calls in your solution (taking the place of subexpressions and operator precendence).

Submit your final program (test it on small numbers, and big numbers: numbers with tens or hundreds of digits). Finally, remember to update the Description: comment to describe the changes added to the program.

PS: With a perfect understanding of BigInteger converting this program would take about 5 minutes: I guess you will take longer, because you will learn a lot about the BigInteger class, specifically, and understanding how to use objects, generally.


Dice War This program requires you to use objects constructed from the DiceEnsemble and Timer classes; remember to import them. Write a program that simulates playing games of dice war; the program should also collect certain statistics while it is playing these games and report them after the required number of games have been played. In a game of dice war, each player starts out with his/her own dice ensemble, consisting of (2,6-sided) dice, and some number (entered by the user) of chips. Each player roles his/her dice. If one player's pip sum is higher, that player gets a chip from the other player. Whenever one player has no chips left, the game is over (and that player has lost; and the other player has won). So there are many plays (throws of the dice) in each game.

Your program must prompt the user for the number of games to play, and the number of chips with which the players start each game. It also prompts the user to determine if the program's behavior is to be traced (use tracing as a debugging technique, only when playing few games). The program then simulates that many games of dice war, using objects constructed from the DiceEnsemble class: one pair of dice for each player. It keeps track of the number of times each player wins, the length (number of dice rolls) of the shortest and longest games, and the total number of dice rolls (over all the games). It also uses a Timer to keep track of how long (in clock-time) it takes to play all the games. Try to use the DiceEnsemble objects themselves to keep track of some of the required information, so you do not have to declare extra variables.

Ultimately the program prints

  • how often each player won (it is a fair game, so these numbers should be about equal)
  • the length of the shortest and longest games
  • the total number of rolls (over all games)
  • the average number of rolls per game
  • the amount of time it took to simulate all the games
  • the simulation speed (number of games per second)
Finally, the program should also be capable of tracing its events. Such a facility is not used for long simulations, but instead it is very useful for short debugging runs. By tracing every important event in the simulation, we can display information useful for spotting bugs. When you build your program, you should trace the following events (if the user requests a trace)
  • starting a new game
  • playing one roll: indicate what each player rolled and how many chips each player has left after the roll (and redistribution of the chips).
  • winning a game
Run my executable with tracing (for a small number of games), with each person starting out with 3-5 chips) and without tracing (for a large number of games, with increasing number of starting chips) to observe all its behavior.

For this assignment, try to work out your own enhancements. A starting point could be writing a program to play one game (with the user supplying the number of starting chips); then add tracing; then add playing multiple games (with the user supplying the number); then add code for keeping all the statistics; then add timing the game. Explore the Javadoc of the DiceEnsemble and Timer classes, and use them effectively to minimize the variables and code you must write. I found Integer.MAX_VALUE (check out this static final field in the Integer wrapper class in Javadoc) useful as an initialization for computing the smallest-length game, although there are many other ways to compute this value correctly.


Phone Database This program requires you to use objects constructed from the TypedBufferReader and StringTokenizer classes (as well as use of the EOFException class); remember to import them.

Write a program that reads a database of names and their associated phone numbers (from a file, catenating them into one huge String). As a debugging check (leave it in your program; it is in my executable too), ask the user if he/she wants to print the database (to see if it has been read correctly); if so, print the String. Then, it repeatedly prompts the user for a name (again, a String) and look up and print its associated phone number (or print that the name cannot be found) in the database. If the user enter the "name" QUIT, whether upper-, lower-, or mixed-case, the program should instead terminate.

Your program must prompt the user for an input file (see the file phoneinput.txt provided with the executable) and then read all the names and phone numbers from that file (each is read as a String) from that file, catenating them into one large database string (with a space between every name and phone number). Then your program should repeatedly prompt the user for a name, and terminate if the user enters QUIT; if the user enters any other name, use a loop to process a StringTokenizer object (initialized to the database): it should process tokens until the name is found (use a case-insensitive equality comparison; then the next token is the phone number) or there are no more tokens (then the name was not in the database).

Note that the Class Examples program illustrates how to read all the values from a file, and how to process all the tokens in a String; study this code and adapt it to this program. This resulting program is pretty small; it requires file reading (see above), and ensures that you understand how to use the StringTokenizer class (which has a constructor and a few useful methods that you must learn to use).


Implantable Cardiac Defibrillators Write a program that simulates the working of an Implantable Cardiac Defibrillator (ICD). An ICD is a small electronic device placed in the chest cavity of a patient who is suffering from arrhythmias (heartbeat irregularities). This device constantly monitors the electrical output of a beating heart: if it detects a bradycardia (heart beating too slowly) it acts a pacemaker; more importantly, if it detects a tachycardia (heart beating too fast to pump blood effectively: in extreme cases this results in ventricular fibrillation) at which point it acts as a defibrillator by supplying a large shock to the heart in an attempt to restore a normal rhythm. This shock is described by patients as feeling like a kick in the chest -although many patients are unconscious by the time action is taken on the detected arrhythmia. If you are interested, you can read more detailed information on ICDs.

The basic algorithm inside an ICD computes the zero-crossing count (ZCC) of the electical signals it samples while it is monitoring a heart. Each signal should be between the value of -100 and +100 inclusive (if not, the ICD is receiving faulty signals, and it should shut itself off). During an interval (typically lasting a few seconds), whenever the signal value goes from positive to negative or negative to positive, the ICD increments its ZCC. For the purposes of this assignment, we will treat 0 as a positive number. At the end of an interval, the ICD checks to see whether its ZCC is within a normal range: in a bradycardia the ZCC is too low (equals or falls below some threshold); in a tachycardia the ZCC is to high (equals or exceeds some threshold). If the ICD detects either of these conditions it takes the necessary action; then it resets the ZCC and samples the heart signals for another interval. Of course, real ICDs have evolved to exhibit much more sophisticated behaviors, but this simple model is good enough for this programming assignment.

For example, if the ICD is using an interval length of 10, the following table labels each sample, shows the signal value for that sample, and the current ZCC.

Sample# 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...
Signal  0  5 10  5 -5  5 10  5 -5 -5  5 10  5 -5  5 -5 10  5 -5  5  5 10 ...
ZCC     0  0  0  0  1  2  2  2  3  3  1  1  1  2  3  4  5  5  6  7  0  0 ... 
ZCC reset to 0----------------------^-----------------------------^----- ...
For example, between sample #4 and #5, the signal goes from -5 to 5 so the ZCC is incremented. The ZCC at sample #10 (the end of the first interval) is 3 (after which it is reset to 0 but immediately incremented to 1 because between sample #10 and #11 the signal goes from -5 to 5); at the end of the second interval it has risen to 7.

This program will simulate the simple ICD algorithmg, allowing us to test it on various data files that represent samples taken of the actual electrical signals of a monitored heart. It should operate as follows

  • Prompt the user for the name of the ICD's configuration data file (an input file), and the name of the simulated heart data file to monitor (an input file).
    The configuration file specifies three int values: an example would look like 100 15 25:
    • The interval length: how many signals to read from the data file while computing each ZCC; after these many signals, the ICD decides if the heart is arrythmic and resets the ZCC. It continues reading more signals, deciding whether or not the heart is arrythmic every interval length.
    • The bradycardia threshold: if the ZCCs in a an interval equals or falls below this threshold, the heart is beating too slowly.
    • The tachycardia threshold: if the ZCCs in an interval equlas or exceeds this threshold, the heart is beating too quickly.
    The simulated heart file contains a sequence of electical signals that the ICD should process.
  • Display on the console the information extracted from the configuration file; if these three values cannot be read correctly, (see the simplebad1-config.txt and simplebad2-config.txt files) the program should terminate.
  • Simulate the action of the ICD: read signals from the simulated heart file and compute the ZCC for each interval; display the ZCC for that interval, along with any action to take if the ZCC indicates an abnormally beating heart.
  • Continue this process until
    • There is no more data in the simulated heart file
    • A bad signal value is read: smaller than -100 or greater than +100
Before starting to write your program, run my exectable on all the different data files that I've supplied (see the executable download), to familiarize youself with its operations, output messages, etc. Then, follow the iterative enhancement approach when writing this program. It is an excellent idea to add comments as you are writing the code, to help you understand it while you are enhancing it.

Initially, test your enhancements on the simple-config.txt and simple-heart.txt data files (they use the data illustrated above) Eventually test your enhancements using regular-config.txt and each of the regular- input files.

  1. Write a kernel program that prompts the user for the name of the configuration file, reads all the data that it contains, and then displays it on the console. Place all this code in to one large try-catch statement: if you cannot successfully read all the required values, catch any exception, then print an error message, and terminate the program. Test it on a good file (simple-config.txt) and two bad ones (simplebad1-config.txt and simplebad2-config.txt).

  2. Enhance the program so that it also prompts the user for the name of the heart data file too, reads and displays every value in this file (prefaced by the sample number: 1 for the first signal, 2 for the next, etc.), terminating the program when it unsuccessfully tries to read a data value from the file (e.g., it tries to read a non-integer value or it tries to read another value when there is none). Hint: this is the only loop that your program needs, and it should include another try-catch statement that handles any thrown exception by printing a message and terminating the loop/program. Test it using simple-config.txt and one a good file (simple-heart.txt) and a bad one (simplebad1-heart.txt).

  3. Enhance the program so that it also terminates the loop if the signal value is not between -100 and 100 inclusive, displaying the appropriate message. Test it using simple-config.txt and two bad files (simplebad2-heart.txt and simplebad3-heart.txt).

  4. Enhance the program so that it displays on the console the message Make Decision after reading enough sample signals to fill the interval. For example, if the interval is 10 samples, the ICD should read and display the first 10 signals (numbered 1 through 10) and then display Make Decision; then it should read and display the second 10 signals (numbered 11 through 20) and then display Make Decision again, etc. Test it using simple-config.txt and simple-heart.txt.

  5. Enhance the program so that it computes and displays the ZCC after it reads each value from the heart data file. This change will require two variables: one storing the value of the previous signal and one storing the value of the current signal; the current value becomes the previous one at the end of each loop iteration. Initially (before the loop even starts), set the previous value to 0. Each iteration of the loop reads a new value for current from the heart input file.

    Also change the Make Decision message to display the current ZCC (whose value it will actually use to check for bradycardia or tachycardia). Important: after each decision is made, the ICD should reset the ZCC to 0, as it begins computing the ZCC for the next interval of signals.

    Test it using simple-config.txt and simple-heart.txt and compare the results to the table of values above. When you have verified that the right ZCC values appear in these messages, remove (or comment out) the code that displays each signal, and the ZCC for each new signal read.

  6. Enhance the program to display Bradycardia Detected if the ZCC equals or falls below the threshold read from the configuration file, and display Tachycardia Detected if the ZCC equals or exceeds the threshold read from the configuration file.
Test the final program using regular-config.txt and each of the regular- input files.

Extra Credit The dice war problem has 1 point of extra credit. To earn it, at the bottom of the main comment in the DiceWar class, include a section labelled Extra Credit. In this section, estimate the average number of dice rolls for a game where each player starts out with 1,000,000 chips. Explain the details of how you arrived at your estimate.

Do not discuss your estimate with anyone but the student you are pairing with.