CS302 -- Lab 3 - Golf Handicaps



In this lab, you will write a non-trivial application in which you will use STL maps and vectors.

In the game of golf, players can rank themselves with a handicap. This is a number that basically tells you how good you are at the game. The lower your handicap, the better you are.

The US Golf Association has a very lengthy description of how you calculate a handicap. It may be found at http://www.usga.org/handicap/, if you're interested. I'm going to simplify it a bit. Here is how you calculate a handicap for a golfer:

For example, suppose I have played 20 rounds of golf from the white tees at Three Ridges golf course, whose rating is 69.3 and whose slope is 119. On ten of the rounds, I shot 88 and on the other ten, I shot 85. Then I would compute my handicap as follows:

Your Job

Your job is to write the program handicap. It should take two command line arguments:
handicap score-file course-file
The score-file is a file that contains scores. Each line of this file is in the following format:
Month Day Year Name Score Course
Month is a number between 1 and 12. Day is a number between 1 and 31. You do not have to error check for legal month/day combinations (i.e. don't worry about 2/30). Year is the year (i.e. 1999, 2000 -- no Y2K bugs on this program). Name is a one-word name of a golfer, and Score is an integer score. Course is the name of a course, and may contain any number of words separated by white space. The scores can be in any order, and there can be any number of golfers in the score file.

There are example score files in /home/bvz/courses/302/labs/lab3:

The course-file is a file that contains golf courses plus their ratings and slopes. This file has a looser structure than the score file. It contains three kinds of lines:
  1. Course Lines: These start with the word ``Course'' and then contain the name of the golf course. Again, the name is words separated by white space.
  2. Rating/Slope Lines: These have the word ``Rating'' then the rating (which is a floating point number), then the word ``Slope'' and then the slope (which you should also treat as a floating point number). When you encounter this line, it is the rating and slope for the course named on the most recently encountered ``Course'' line.
  3. All Other Lines: All other lines should be ignored.
An example (good to use for testing) is in courses. Look at the first four lines:

Course Three Ridges -- White Tees
Rating 69.3 Slope 119
Par     72

This says that there is a course that's called ``Three Ridges -- White Tees'' with a rating of 69.3 and a slope of 119. You ignore the ``Par'' line, and the blank line after the ``Par'' line.

In both files (scores and courses) you should create a string for a course that is composed of each word separated by a space. For example, the following course specifications should be equivalent:

Course Three Ridges -- White Tees

Course      Three          Ridges          -- White            Tees

Now, your program must read in both of these files, and exit if it sees any errors. It should however error check all lines before exiting. If both files are ok, and all golfers have at least 20 scores, then it should compute the handicap for each golfer, and then print out each golfer and his or her handicap ordered by handicap (lowest first). Print out the handicap first (padded to 5 characters and two decimal places), and then the golfer's name.

For example:

UNIX> handicap score1 courses
14.31 Jim
UNIX> handicap score2 courses
 3.70 Phil
14.31 Jim
UNIX> handicap score3 courses
 3.70 Phil
14.31 Jim
UNIX> handicap bigscore courses
 2.58 Tiger
 8.26 Phil
 8.31 Sergio
 9.63 David
 9.77 Anika
10.12 Jose
18.12 Ernie
18.21 Colin
18.50 John
18.88 Se-Ri
39.55 Karrie
UNIX> handicap scorebad1 courses
Jim: Less than 20 scores (only 4)
UNIX> handicap scorebad2 courses
scorebad2 Line 1: Course `` Three Ridges -- Ebony Tees'' not found in course file
UNIX> handicap scorebad3 courses
scorebad3 Line 1: Bad Month
UNIX> handicap scorebad4 courses
scorebad4 Line 1: Bad Score
UNIX>                  
A working executable is available at ~bvz/cs302/lab3/handicap. If you set the environment variable PRINTDIFFS to be "yes", then the program will also print out each golfer's differential and date number (defined in step 4 below) or each score. You can use this to test yourself in case your computations do not seem to match those here: (note, you do not have to implement this feature. It is just included it so that you can help test your own code).
UNIX> setenv PRINTDIFFS yes
UNIX> handicap score1 courses
Jim
  Dnum: 743660   Differential: 17.76
  Dnum: 743661   Differential: 17.76
  Dnum: 743691   Differential: 14.91
  Dnum: 743693   Differential: 14.91
  Dnum: 743722   Differential: 17.76
  Dnum: 743725   Differential: 17.76
  Dnum: 743753   Differential: 14.91
  Dnum: 743757   Differential: 14.91
  Dnum: 743784   Differential: 17.76
  Dnum: 743789   Differential: 17.76
  Dnum: 743815   Differential: 14.91
  Dnum: 743821   Differential: 14.91
  Dnum: 743846   Differential: 17.76
  Dnum: 743853   Differential: 17.76
  Dnum: 743877   Differential: 14.91
  Dnum: 743885   Differential: 14.91
  Dnum: 743908   Differential: 17.76
  Dnum: 743939   Differential: 14.91
  Dnum: 743970   Differential: 17.76
  Dnum: 744001   Differential: 14.91

14.31 Jim
UNIX> setenv PRINTDIFFS no
UNIX> handicap score1 courses
14.31 Jim
UNIX> 


Fields, printf, and converting strings to numbers

You may use a C++ version of the Fields library for this assignment and you may use printf rather than cout if you wish. Of course you are free to use stream I/O if you prefer. In order to use the Fields library in your program you will need the following statement:

#include "Fields.h" You will need to add a -I/home/bvz/courses/302/include flag to your compilation command. You will also need to add /home/bvz/courses/302/objs/lib302.a to your linker command. For example:
g++ -o handicap handicap.o /home/bvz/courses/302/objs/lib302.a
You can find documentation for the C++ Fields library here. You can also find source code for the C++ Fields libary that can be compiled on either a mac or windows machine at /home/bvz/courses/302/src/macwin.

If you use the C++ Fields library then you will need to convert the strings it creates to numbers. hints provides three ways to do it.


What To Submit

You should submit the following files:

  1. handicap.h: Even though you are using only one .cpp file for this assignment you should still place your classes in a .h file. Doing so will give you good practice for future labs.
  2. handicap.cpp
  3. makefile
Remember, you submit labs using the special script discussed on the TA web site.

Suggested steps for writing this program:

Below are outlined suggestions for how to go about writing this program, which you should find helpful.
  1. Write a basic program that tests for the right number of command line arguments, and print out the right error message if that was wrong. Get your makefile working.
  2. Write a procedure called read_course_file() that takes the course file name and an STL map as an argument and fills the STL map with course classes, which you should define. This function tests for all varieties of errors in the course file. When it returns, the main() routine prints out all of the courses in alphabetical order. Test this a bit, especially checking the error handling code. The error conditions you should detect are:
    1. Duplicate course names
    2. Duplicate rating/slopes for the same course
    3. Invalid numbers for the rating and/or slope
    4. Negative numbers for the rating and/or slope
    5. Wrong number of fields for a rating/slope line
    6. Wrong keywords for the rating/slope line--if field 0 is "rating" then field 2 must be "slope"
    7. No rating/slope for a course
    8. Wrong number of fields for a course line
    9. A rating line precedes any of the courses in the file
    The file coursesbad1 contains a file that tests all of the error conditions.
  3. Write a procedure called read_scores_file() that takes the score file name and the course STL map as input, and fills a STL vector with score classes, which you define. However, for this step, all the function does is read the score file and check for errors. These errors are: bad month/day/year/score, bad line (too few words), and course not found in the course file (which you do check by searching in the course map). Test this against all the input files listed above. You can read about maps at http://www.cppreference.com, under C++ Maps.
  4. Now, traverse the vector of scores, calculating the differential for each score, storing it as part of the score class. Test this by again traversing the vector of scores, this time printing out all of the differentials. This is what you get when you set PRINTDIFFS in the provided executable above.
  5. Now, test to make sure that all the golfers had at least 20 scores, and print an error if that was not the case. Create a STL map of golfer classes, which store a golfer's name, vector of scores, and handicap. Then, traversing the vector of scores, for each score find the appropriate golfer in the golfer map and add the score to the golfer's vector of scores. If the golfer does not yet exist in the map, then create an object for that golfer and insert the golfer into the map. Now, traverse your map of golfers, printing an error message if any golfer has less than 20 scores. Test this.

  6. Now, for each golfer in your map, sort its vector of scores. To do this, write a boolean function named compare_scores_by_date to compare two score objects by date. The function should return true if the date of the first score is strictly less than the date of the second score. If the two dates are equal you should return false. A trick for comparing two dates is to compute a date number for each date and then compare the date numbers. You can compute a date number like this:

    number = day + (month*31) + (year * 12 * 31)

    Compute this, then print it out for each score and check to make sure that it is working.

    To sort a STL vector, you need to use the STL generic sort() function and pass it your compare_scores_by_date function. You can read about it at http://www.cppreference.com, under C++ Algorithms.

  7. Now, compute the handicap for each golfer. To do this, for each golfer in the golfer vector, use the golfer's last 20 scores. Step 6 should have already sorted each golfer's scores into ascending order by date so you should be able to erase the inital scores until there are only twenty scores left in the vector. Sort this updated vector by differential. You will need to write a new boolean function, named compare_scores_by_differential, that takes two differentials as parameters and returns true if the first differential is strictly less than the second differential. Compute the average of the first ten differentials, multiply by 0.96, and store the result as this golfer's handicap.

  8. Finally, iterate through your golfer map and create a new golfer vector with the golfers. Sort your golfer vector by handicap. This is simply a matter of creating yet another boolean function, named compare_handicaps, that takes two golfers and returns true if the first golfer's handicap is strictly less than the second golfer's handicap. Print out the golfer vector in order.

  9. More testing, commenting, etc.