Edits / Updates:
  • Mastermind problem: Error checking occurs in get_guess_feedback function
  • Two typos in hw1.c:
    • add or correct declaration of pattern: unsigned int pattern; in main
    • spell iresult correctly in main

Goals for this homework

  • Practice the C development cycle
  • Write a small C program to get used to the syntax.
  • Write programs that exercise functions and printing.

You are expected to complete this assignment individually. If you need help, you are invited to come to office hours and/or ask questions on piazza. Clarification questions about the assignments may be asked publicly. Once you have specific bugs related to your code, make the posts private.

Another note about Piazza. Piazza is your community resource - please use those as discussions amongst yourselves. We are not monitoring it all day, rather we each have check-in times once a day. Therefore, you need to start early enough to wait for feedback and/or build a vibrant, supportive community that helps each other while we are completing other necessary tasks (research, developing assignments, preparing for lecture, performing advising tasks, etc.).

Make sure you filled out the Student Information Form

This homework has several exercises. We are also providing you with some resources on printf and error handling for this assignment.

Homework 1, which includes warmup 1, will be collected from your subversion repository at 11:59pm prior to your next lab session.

You should submit two files for this assignment (hw1.c and Makefile), in addition to ones from your warm-up, in your subversion repository as directed below. Your subversion repository is named CNET-cs152-win-19, and you can check it out with the following command:

  $ svn checkout https://phoenixforge.cs.uchicago.edu/svn/CNET-cs152-win-19 
(where CNET is your cnetid). Do not commit any executable, a.out or any other, to your repository; we will (in fact, must) recompile your code on our own machines on our end.

printf

The printf() function takes a string as its first argument. The contents of this string are printed to the screen as-is, but with the same kinds of escape conventions as we saw in Racket (e.g., \n is the newline character). The function can be used with a string and no other parameters:

  printf("Hello, world!\n");

If you want to include a representation of the value of one or more expression in the string printed by printf, you need to include one or more format codes in printf's string argument, and you need to pass correspondingly many expressions as additional arguments to printf, separated by commas. Some examples follow.

Format codes begin with the character % and the next character(s) specify the type of the value to be printed. For now, it suffices to know that d specifies an int, and lf specifies a double. To be clear, put together, we write %d as a placeholder when we want to write an int, %ld for a long int, and %lf for a double.

For example,

  printf("The number is %d.\n", 42);
prints this:
  The number is 42.

If we want more than one expression included in the same string, we use multiple format codes and pass arguments in lockstep with those codes:

  int ndigits = 6;
  double approx_pi = 3.14159;
  printf("Pi to %d digits is %lf, and pi squared is %lf\n",
         ndigits, approx_pi, approx_pi * approx_pi);

For this assignment, you'll also need to control the spacing of your printing. Here is a website that provides a reference in how to format with printf.

Error Handling

Error handling capabilities vary by language, and what you want to do in an error varies by the situation. When an error occurs in a function, the question becomes, what should you do, and how do you notify the caller that an error occurred?

In C, there is no good way to distinguish error conditions from normal execution. This is unfortunate because if you write a function, and it's used in a variety of different circumstances, it is bad programming practice to determine within the function what will be done. For example, one program might want to exit, whereas another might want to notify the user that there was bad input and to try again.

In this course, we will print the error message in a special way (see below). If there is an opportunity, we will designate a specific return value for an error condition. If there is no available return value, then we will exit from within the function.

When we print error messages, it is helpful and appropriate for them to be routed through a special error mechanism. This gives them priority, increases their likelihood of being printed in the case of a program crash, and allows them to be separated from other output. To accomplish this, we use a variant of printf called fprintf. This function works the same as printf, but takes in an extra argument at the beginning: the "file" to which to write the message. Passing in stderr as that argument will send the output to the screen, but using the special error mechanism:

  fprintf(stderr, "error: too many widgets for the number of grommets\n");
  fprintf(stderr, "error: need ten boondoggles, but only have %d\n", num_bds);

Sometimes, these lines will be followed by exit(1); This immediately exits the program and returns a code. If you were writing a large program, you might assign a different code to each type of error that would result in an exit.

Types

The homework exercises follow. We use the type int for integers (except, in certain cases made explicit below, unsigned int), double for floating-point numbers (i.e., non-integers), and int for Booleans (noting that C has no Boolean type).

Makefile

Because you are adding a second file to your directory, you need to add a second target to your Makefile. In your makefile, add another two lines (with a space between these and the ones already there).
hw1: hw1.c 
	clang -Wall -o hw1 hw1.c 
To compile, type:
$ make hw1 

To run, type:
$ ./hw1 

Set Up - Skeleton project

The first thing to do is to create a skeleton project that will minimally execute. This week, I provided it for you. In the future, you will be expected to create it for yourself. Let's imagine that this homework only required a single function, surface_area_cylinder. Then your skeleton would be the following:
  • Step 1: Create hw1.c. For each function, it must do a few things.
    1. Have the right signature - return type, name, inputs
    2. Print to stderr the fact that it is not yet implemented
    3. Return somethingwith the right type.

    For example:
    double surface_area_cylinder(double height, double radius)
    {
    	fprintf(stderr,"surface_area_cylinder not yet implemented\n");
    	return 0.0;
    }
    
  • Step 2: Create a main function. For each function, put in a single function call. For example:
    int main()
    {
    	double dval;
    
    	dval = surface_area_cylinder(2,1.5);
    }
    

First get this compiling and running. It won't print out anything, but this will mean that your code will compile and execute with our infrastructure. This must work in order to get any points in this course. Do this first, not last.

Exercise 1: Geometric Shapes

This exercise pertains to geometric shapes. You are going to write two functions, in increasing difficulty (from a programming perspective).

double surface_area_cube(double edge_length); 

Write a function that calculates the surface area of a cube. Each side has length edge_length. Return the result - do not print it out.

Once you have written the calculation in code, make sure you also identify what values of edge_length would not be valid. For any invalid input, print an appropriate error message and return -1.

double sides_to_area(unsigned int num_sides, double side_length);

Write a function that calculates the area (not surface area, but internal area) of a 2-d polygon with all equal-length sides. Support a triangle, square, pentagon, hexagon, octagon, and nonagon.

Consider any input outside of those to be invalid input. For invalid input, print an appropriate error message and return -1.

For both functions, if the user enters an invalid input, print out an error with the format "error (function_name): [description of error]". Remember, as described above, to use fprintf rather than printf and send the output to stderr. Return -1.0 to indicate that an invalid input was received. Do not exit.

Don't forget to complete this portion, test it, and commit it before moving on! This means you need to write a test function and develop a suite of test cases!

Exercise 2: Printing

This exercise pertains to printing. You are going to print out a number grid, starting with the 10's of the input number and going on for 100 numbers. It will be formatted such that every column lines up, and the numbers within the column are right justified. Click on this example for clarification.

void print_number_grid(int start_num);

Some details:
  • Each row begins with a number divisible by 10
  • Input number is always in the first row
  • Numbers are right-justified
  • Numbers may be up to 6 digits. If they are less than 6 digits, then there will be additional spaces.
  • There is one additional space between each number
  • There is a carriage return immediately following the last number in each row
  • The line "number grid: -15" is for illustration purposes - do not print that out in your function.
  • No loops are necessary for this, but if you already know them, you may use them. We will not provide help with loops (since it will be covered after the assignment is due).

If a number larger than 999999 or smaller than -999999 would need to be printed out for a particular input value, then it is an invalid input. Print out an appropriate error message and return early (by doing return with no input arguments).


Don't forget to complete this portion, test it, and commit it before moving on!

Exercise 3 : Mastermind

This exercise pertains to operators and functions. You are going to implement a key function in the game of Mastermind. In mastermind, the goal is to match the pattern of pegs that the master has set. There are 6 different colors of guessing pegs (which Wikipedia calls code pegs). Each guess, the master provides limited feedback using feedback pegs (which Wikipedia calls key pegs). There are up to 12 rounds of guessing / feedback to try to guess the pattern.

There are two feedback colors - black and white. You receive a black feedback peg for each guessing peg that is the right color and location. You receive a white feedback peg for each guessing peg that is the right color and incorrect location. You receive no feedback pegs for any other guessin pegs. The feedback pegs are given to you in random order - you do not know which feedback peg goes with which guessing peg within a single round - you need to look at past guessing / feedback rounds to deduce more information.

You will write the function that, given a guess and a solution, provides the feedback. The guessing pegs and solution pegs are encoded as 4-digit integers, each digit holding a color between 1 and 6. The feedback is a little different - it will be two digits. The 10's place will be the number of black pegs (exact matches). The 1's place will be the number of white pegs (matches that are the right color but not the right position).

For this exercise, we will write a series of helper functions to break down the problem.

unsigned int extract_digit(unsigned int pattern, unsigned int digit);
What value is in specified digit of pattern? Digit 0 is the right-most digit, digit 1 is to the left of it, etc. You only need to support extraction of digits for this project, which is a 4-digit number.
unsigned int num_of_color(unsigned int pattern, unsigned int color);
How many instances of color are there in pattern? The pattern is a 4-digit, base-10 number. Color is also a number.
unsigned int count_exact_matches(unsigned int guess, unsigned int solution);
Count how many digits in guess match both in color and position as compared to the solution. There are four digits in each of guess and solution.
unsigned int count_color_matches(unsigned int guess, unsigned int solution);
Count how many digits in guess match in color, regardless of position, as compared to the solution. There are four digits in the guess and solution.

These are all used to create the final feedback. The final feedback is a two-digit number. The tens digit contains the number of items that match in both placement and color. The ones place tells which ones are the correct color but incorrect placement. In addition, all error checking of the user's guess will occur in this function to make sure that both the guess and solution adhere to the 4-digit, values 1-6 format. If there is an invalid guess, print out an error message and return the value
unsigned int get_guess_feedback(unsigned int guess, unsigned int solution);



For guess: 3413, solution: 2315,
  • extract_digit(3413, 2) = 4
  • num_of_color(3413, 1) = 1
  • num_of_color(3413, 2) = 0
  • num_of_color(3413, 3) = 2
  • count_exact_matches(3413, 2315) = 1 (the 1 in digit 1)
  • count_color_matches(3413, 2315) = 2 (1 each of 1 and 3)
  • get_guess_feedback(3413, 2315) = 11 (1 exact match and 1 additional color match)

Submit

At this point, for hw1, you should have done the following:
  • Created one new file and filled in the proper information: hw1.c.
  • Added the new executable hw1 to your Makefile.
  • $ svn add hw1.c 
    
  • Implemented all of your functions.
  • Compiled your executable manually and with the Makefile
  • Implemented your test cases
  • Executed your code
  • Debugged your code
  • Submitted your code:
    $ svn commit -m "hw1 complete"