1. Due Date

Complete lab: Due by 11:59 pm, Monday, Feb. 15, 2021

Unlike the other lab assignments in this class, you will complete this lab on your own without a lab partner.

2. Lab Overview and Goals

This lab implements a program that is designed to give you practice with some C programming basics, including some program design and problem solving.

Make sure that you use only the C features that we specify in this lab and that you are not using features of C that you may know from other courses. Look at Section 5 (Details) and Section 6 (Requirements) to ensure you are solving the lab the way in which we intend. In Section 7 (Tips) are some implementation tips to help you.

You are also required to use the vim editor to implement this program.

2.1. Lab Goals

  • Practice with C programming basics: declaring variables, types, constants, loops, conditionals, functions

  • Practice writing and using C functions, function prototypes, pass-by-value

  • Practice with nested for loops

  • Using some C library functions (random nubmers and output)

  • Using a Makefile

  • Using git to clone a repository and to submit your lab solution

3. Starting Point Code

3.1. Getting Your Lab Repo

First, clone your Lab 1 repo into your cs31/labs subdirectory:

  1. get your Lab 1 ssh-URL from the CS31 git org. The repository to clone is named Lab1-yourUserID with your user name.

  2. cd into your cs31/labs subdirectory:

    $ cd ~/cs31/labs
    $ pwd
  3. clone your repo

    git clone [the ssh url to your your repo]
    cd Lab1-yourUserID

There are more detailed instructions about getting your lab repo from the "Getting Lab Starting Point Code" section of the Using Git for CS31 Labs page.

3.2. Starting Point files

Cloning the repository will give you the following starting point code files (note: the exact number of floatsX.txt files in your repo may differ from this example listing):

$ ls
 Makefile       README.md     prog.c

These files are:

  1. Makefile: A Makefile simplifies the process of compiling your program. We’ll look at these in more detail later in the course. You’re welcome to look over this one, but you shouldn’t need to edit it for this lab. If you are interested, take a look at the reference in Section 9 for more information about make and makefiles.

  2. prog.c: the file into which you will add your C solution, using good C style and complete comments. With the starting point code, is one helper function already implemented for you:

    1. read_int: takes a prompt string and returns and int value entered by the user. You can look at the function code, but it uses some C language syntax that we will not cover until later. It is not important now that you know how it is implemented, just how to call it from code you write. There is an example call to it from 'main'.

  3. README.adoc: some instructions for compiling and running the program.

4. Compiling and Running

To compile your program run make, which will use the Makefile to compile the binary executable, prog, from prog.c. Running make lists the gcc compiler output, which you should examine for errors and warnings.

$ make

To run the prog executable binary:

$ ./prog

4.1. Sample Output

The following shows a few example runs of a working program, including different control flows and error handling. Your program’s output should be identical to this when given the same input, with the exception of the specific average result since the exact set of random values will differ from run to run. However, you should evaluate the result, and ensure that it makes sense as an average for the range you give.

5. Lab Details

You will implement a program that does the following:

  1. Computes the average of a set of randomly generated values, whose range and cardinality are entered by the user.

  2. Repeatedly draws a pictures of a mountain, until the user chooses to quit. Each mountain’s size is determined by the user.

The main parts of the lab involve writing code using C functions, loops, if statements, and nested loops. It includes some error handling for input values out of a specified range, but you are not required to handle bad input (such as the user entering an invalid int value like hello when prompted to enter an int).

You may want to start by looking at the sample output from several program runs in Section 4.1 to get a sense of what this program does.

5.1. Starting point code details

The starting point code includes the start of a main function, and includes all C libraries your need in this program (#include at the top of the file).

It also includes a function that we have already implemented for you. The function named read_int takes a prompt string and returns an int value read in from the user. This function uses some features of C that we have not yet discussed, so it not important that you know how it is implemented right now, but you should know how to call it. Here are some example calls that show how to use this function:

int num, age;

num = read_int("How many values do you want: ");
age = read_int("Enter your age: ");

Also, in the main fuction we have included a call to the srand function, which is used to seed C’s random number generator:

srand(time(0));

5.2. Part 1: Computing Average of Set of Random values

Your main program should start by printing out a short message about what the program does, and then compute the average of a set of randomly generate int values, in the following way:

  1. Prompt the user to enter a number of values to average over, and read in the value entered by the user (call read_int to do this). Check that the value returned by read_int (the value entered by the user) is positive, and if not print out an error message and repeat this step.

  2. Prompt the user to enter the high end of the range of values, and read in the value entered by the user (call read_int to do this). Check that the user enters a positive value for the number, and if not, print out an error message and repeat this step.

  3. Call your compute_average function that returns the average of a number of randomly generated int values in a specified range.

  4. Print out the results (print out in similar way as shown in the sample output in Section 4.1).

For this part, you will need to implement a compute_average function. The function should take as input:

  1. num: the number of values to average over

  2. high: the high value in the random range [0, high]

And should return the average (as a float) of num randomly generated int values in the range [0, high]. Its function prototype should look like:

float compute_average(int num, int high);

5.2.1. rand function

The rand function, from the C stdlib, returns a random int value between 0 and RAND_MAX (some large int value). Here is an example call:

int val;

val = rand(); // get next pseudo-random int value in range [0,RAND_MAX]

5.3. Part 2: Drawing a Pretty Picture(s)

After your program computes average, it should draw a pretty picture of some size specified by the user, and possibly draw more pictures for the user based on thier input. The main control flow is:

  1. Get the size of the picture to draw between 3 and MAX (constant value in the program) from the user (by calling your get_in_range function).

  2. Call your draw_picture function to draw a picture of the user entered size

  3. Ask the user if they want to see another picture, and get their response of 0 or 1 (0 for no, 1 for yes).

    • if they enter 1 (for yes), repeat these steps

    • if they enter 0 (for no) print out a good bye message

For this part, you will implement two functions that you should call from main (within code that implements the control flow for this part listed above). These two functions are:

int get_in_range(int lo, int hi);

void draw_picture(int size);  /* void return type: function does not return a value */

5.3.1. get_in_range

This function:

  1. Prints message to user to enter a value between lo and hi

  2. Prompts user to enter a value and reads it in (by calling read_int)

  3. Checks to see if value entered is in the range between lo and hi inclusive

    • if so, return it

    • if not, print out an error message and repeat from step 2

5.3.2. draw_picture

This function, given a passed size value, draws a picture of a mountain (a pyramid of stars). For example, it would draw the following for a size value of 8:

        *
       ***
      *****
     *******
    *********
   ***********
  *************
 ***************

and this for a size value of 5:

     *
    ***
   *****
  *******
 *********

See Section 4.1 for more examples.

Your code will print out the pattern by making calls to only the following three printf statements:

printf(" ");  // print a single space  (a single space string: type one space between "": " ")
printf("*");  // print a single star
printf("\n"); //  print a new line

The draw_picture function requires a solution that uses a doubly nested for loop (you may have more than one inner loop, but don’t have a nesting depth deeper than doubly nested). Look at the example output and see how the pattern of what is drawn changes based on the passed size value. For example, consider:

  • How many total lines are printed?

  • How many spaces are printed on each line, and how does it change with each subsequent line printed?

  • How many stars are printed each line, and how does the number change with each subsequent line printed?

See the Tips section (Section 7) for some recommendations for how to solve this.

6. Lab Requirements

For full credit, your solution should meet the following requirements:

  • When run, your program’s output should look like the example output shown in Section 4.1.

  • You should implement at least the three required functions, including adding their function prototypes to the top of the prog.c file and their implementations at the bottom (like read_int).

  • You should only use parts of the C programming language that we have covered in class to solve this lab. Specifically, if you have taken CS35 (or have prior experience in C or C++), and if you are familiar with C string, array, or pointer variables, you may NOT use any of these in your solution.

  • Your picture drawing must use nested for loops, and must be done by making calls to print a single space, a single star, or a single new line:

    printf(" ");
    printf("*");
    printf("\n");
  • Your code must be well-commented, modular, robust, and use meaningful variable and function names. Each function should include a brief description of its behavior, and a description of each parameter and what it returns. Also, do not wrap lines and follow C-code conventions with local variable declarations and function prototypes.

    Look at the C code style guide for examples of complete comments, tips on how not to wrap lines, good indenting styles and suggestions for meaningful variable and function names in your program. Also, declare all local variables at the top of each function body before the set of statements in the function body.

  • Your code should compile cleanly with no errors or warnings and should not crash unexpectedly during execution.

  • Code you submit should have my "TODO" comments removed (these are my notes to you, as you implement them remove the "TODO").

  • Any debugging output should be removed (or commented out) from your submission for grading; again, your program’s output should match Section 4.1

  • You should use vim editor for editing your prog.c file.

7. Tips and Hints

  • Start early on this. We strongly recommend not using any of your late days on this lab assignment.

  • Type CNTL-C in the terminal to kill a program stuck in an infinite loop. read_int is not super robust to bad user input, such as the user entering hello there instead of a valid int value. As a result, it could trigger an infinite loop, as could any loop code you add.

  • Start by implementing and testing the compute_ave function, and test for several input values to ensure correctness. To verify that your function is correct, think about what you expect the average of a set of random values in the range you pass to be. Your result should be close to this. Since the values are randomly generated, and since the program calls srand to seed the random number generator each run, two runs of your program using the exact same user input may produce slightly different average results.

  • The C mod operator (%) can be applied to two int operands and evaluates to the remainder part of int division. For example, num % 5 returns a result in the set [0,4] depending on the remainder of the value of num divided by 5 (10 % 5 evaluates to 0, and 102 % 5 evaluates to 2).

  • Think carefully about type in C, and the effects on how operators are evaluated. For example, if both operands to / are int, / does int division. If at least one is a float (or double), / does float (or double) division.

  • printf takes a format string that can include placeholders for argument values. %g is a placeholder for a float or double, and %d is a placeholder for an int value. Make sure that the type of the placeholder you use matches the type of the argument you pass for it. If not, your program’s output may be incorrect only because of this mismatch (i.e. you are computing the correct result, you are just printing it out incorrectly).

  • int get_in_range(int lo, int hi); can be used for both questions related to the picture drawing part of your program. After printing out a message with the range of value to input, it should call read_int("value: ") to get the input value entered by the user. It should not return until the user enters a valid input value (one in the specified range). Look at the example runs in Section 4.1 to get a sense of what this function does and prints out when the user enters a value not in range and it repeats reading in a value until the user does enter one in range.

  • For the draw_picture function, you can have more than one inner loop in the outer loop, but you do not need more than double nesting. It is worth thinking about the pattern on paper a bit before coding. Consider that the outer loop controls how many lines are printed, and the inner loop(s) what is printed each line. If what is printed each line changes (and it does), come up with a pattern for how many stars (and how many spaces) are printed based on which line is being printed (which i value for the outer loop variable).

  • As a first step in determining how to implement the draw_picture, you can start by solving an easier drawing problem, and then modify it bit by bit to draw more complicated patterns until you draw the mountain pattern. For example, you could start first by drawing a box of stars based on size:

    value: 3
    ***
    ***
    ***
    
    value: 8
    ********
    ********
    ********
    ********
    ********
    ********
    ********
    ********

    Next, try drawing a triangle of stars, the number of stars and number of lines printed depend on the value of size:

    value: 3
    *
    **
    ***
    
    value: 8
    *
    **
    ***
    ****
    *****
    ******
    *******
    ********

    Once you get that, then think about creating this picture next, by figuring out the pattern for the number of spaces that are printed before the stars on each line:

    value: 3
       *
      **
     ***
    
    value: 8
            *
           **
          ***
         ****
        *****
       ******
      *******
     ********

    From here, you can add in another part to draw "the other side of the mountain" to look like the examples in Section 4.1.

  • Start on this early, and post on Piazza, attend ninja sessions, and/or come to office hours if you have questions!

8. Submitting your Lab

Please remove any debugging output prior to submitting.

To submit your code, commit your changes locally using git add and git commit. Then run git push while in your lab directory. Only one partner needs to run the final git push, but make sure both partners have pulled and merged each others changes.

Also, it is good practice to run make clean before doing a git add and commit; you do not want to add to the repo any files that are built by gcc (e.g. executable files). Included in your lab git repo is a .gitignore file telling git to ignore these files, so you likely won’t add these types of files by accident. However, if you have other gcc compiled binary files in your repo, please be careful about this.

Here are the commands to submit your solution in the sorter.c file (from one of you or your partner’s ~/cs31/labs/Lab1-yourUserID subdirectory):

$ make clean
$ git add prog.c
$ git commit -m "correct and well commented Lab1 solution"
$ git push

Verify that the results appear (e.g., by viewing the the repository on CS31-S21). You will receive deductions for submitting code that does not run or repos with merge conflicts. Also note that the time stamp of your final submission is used to verify you submitted by the due date or by the number of late days that you used on this lab, so please do not update your repo after you submit your final version for grading.

If you have difficulty pushing your changes, see the "Troubleshooting" section and "can’t push" sections at the end of the Using Git for CS31 Labs page. And for more information and help with using git, see the git help page.

After you submit your solution for grading, you should fill out and submit the required Lab 1 Questionnaire.

9. Handy References