CSC 161 Grinnell College Fall, 2013
 
Imperative Problem Solving and Data Structures
 
 

Laboratory Exercise: Conditionals

Goals

The purpose of this lab is to introduce the different types of conditional statements in C and practice simple control flow.

Introduction

Programs that don't make decisions are of limited usefulness. Sophisticated programs are able to make decisions based on the results of various tests and, act according to the input data or current state of execution. To perform these tests and manage the flow of execution, C provides conditionals.

In regards to the Scribbler robots, conditionals are how the robot can be programmed to make decisions based on data gathered from its sensors. You will use the robot to explore the use of conditionals.

This lab will cover the use of if, if else, else if, and switch control structures.

The if Statement

The syntax of a standard if statement is as follows:

  if (TEST)
     result();
         

The result will only execute if the TEST evaluates to true. In C, true means anything that is not 0. Thus, a statement:

  if ( 1 )
     result();
         

will always execute.

* Note: An if will only determine the execution of the statement directly following it. To group multiple statements together into one, curly brackets: { and } are used. For example, in the code segment

  if (TEST)
     {
        result1();
        result2();
     }
         

both result1() and result2() will be executed if TEST is true, and neither of these statements will be executed if TEST is false. In contrast, in the following code segment

  if (TEST)
     result1();
     result2();
         

only result1() is considered as being part of the if (indenting notwithstanding). Thus, if TEST is true, then result1() and result2() are executed. However, if TEST is false, then only result1() is skipped. Since result2() is outside the if statement, result2() will be executed whether or not TEST is true.

Review the sample program: conditional-battery.c. This program illustrates simple uses of the if statement.

  1. Copy the program conditionals.c to a working directory. This can be accomplished with cp (copy) command in Linux. In your working directory, paste the following line into your terminal window and run it:

    cp ~walker/public_html/courses/161.sp12/modules/cond-loops-testing/conditionals.c .

    This program already contains the code needed to connect and disconnect from the robot.

  2. Using a conditional, test to see whether there is an obstacle in front of one of the robot's IR sensors.

    This can be done using the rGetIRTxt or rGetIRNum functions. Each function should return a one (true), or zero (false) depending on whether there is an obstacle close to the scribbler's IR sensors.

  3. Next, make the robot beep depending on the result so you know whether there is an obstacle or not. If there are not obstacles in front of any of the sensors, it should move forward for 1 second. This way you can understand whether there is an obstacle that your robot is detecting.

    After you are finished, compile and run your program with the robot in different locations. Try it with and without obstacles in front of the robot.

  4. Verify whether or not your program works.

    If all went well, your test should look something like this:

    if (rGetIRTxt("left") == 1)
      rBeep(1, 880);

    * You may have curly brackets too, either way is fine.

The if ... else ... Statement

In the previous exercise, nothing happens if there isn't an obstacle in front of the left IR sensor.

  1. Add an else to the if statement so that the robot beeps twice in the case that no obstacle is detected. You will need to put curly brackets around what you want the robot to do for the else case since you want it to do more than 1 actions:
    else
    {   rBeep(1, 880);
      rBeep(1, 800);
    }
  2. Compile and run your program. What happens?
  3. Now change the program so that there are no curly brackets around the rBeep commands. What did the robot do now?

    Remember, only the first statement after an if or else is included in the conditional.

else if ...

Testing one sensor is great, but the robot has two IR sensors. It would be more interesting if we tested both the sensors and acted according to the resulting values. To do this you can employ an else if test.

Avoiding Obstacles

Rather than beeping, consider telling the robot to turn if it senses an obstacle in front of one of its sensors. If there is an obstacle in front of the left sensor, turn right, and if there is an obstacle in front of the right sensor, turn left. This is a simple, but effective way to avoid an obstacle.

  1. Change your code so the robot behaves as described.

    Your code should roughly resemble the following:

    if ( left sensor )
      turn right;
    else if ( right sensor )
      turn left;
  2. Compile, run, and verify your code works before moving on.
  3. Test what happens in the case where there is an obstacle in front of both sensors. Then move on to the next part to figure out what to do about this situation.

Testing Two Things at Once

Consider the case where there is an obstacle in front of both sensors. Using the previous code, first it would check if there is an obstacle in front of the left sensor. Since there is one, it would turn right. Then it would go on to whatever comes next, which could be going forward. However, the left sensor could still have the same obstacle in front of it if the robot didn't turn enough.

How could we make this code work better?

if ( left sensor && !right sensor)
  turn right;
else if ( !left sensor && right sensor)
  turn left;
else if ( left sensor && right sensor )
  turn 180 degrees;
else go forward;

This checks for any situation that might occur and tells the robot what to do in each one.

The problem becomes even more prevalent if the data from the sensors was gathered beforehand (as it should be), and then the tests were executed:

int left = rGetIRTxt ("left");
int right = rGetIRTxt ("right");
if ( left )
  turn right;
else if ( right )
  turn left;

In this case, if both were true, the robot would just turn right, then turn left, and end back where it started.

It is very important that you consider when exactly you are asking for sensor data from the robot. The above example illustrates the generally correct method of gathering data before examining it. To illustrate the importance, consider the following code:

if ( rGetIRTxt("left") && other_condition1 )
  result1();
else if ( rGetIRTxt("left") && other_condition2 )
  result2();
else if ( rGetIRTxt("left") && other_condition3 )
  result3();
else if ( rGetIRTxt("left") && other_condition4 )
  result4();

By placing the call to the function rGetIRTxt in the if statement, the you are actually gathering the data four [different] times. Calling the function many times is less efficient than calling it once, giving the value to a variable, and just using that variable. Suppose the robot is moving beforehand. When the program gets to the first test, rGetIRTxt fails and so execution moves on to the first else. But, by now the robot has moved in range of an obstacle. The value for rGetIRTxt would then be different when the robot is queried again -- causing the first else if to pass based on the altered value of the IR sensor. If you examine the logic in the case that all other_conditions are true, the program never should have gotten to the first else if if the value of rGetIRTxt was initially false .

To avoid the situation altogether, store the value from one call to rGetIRTxt first. Then perform all the tests on that variable.

The && and || Operators

To combine two tests, use the && (AND), and || (OR) operators. If you want certain code to execute only if both tests are true, use the AND operator. If your want to do something if either test is true use the OR operator.

  1. Copy the program combine-tests.c to your working directory and open it in an editor. This program resembles what you should have if you completed exercise two correctly plus the calls to rGetIRTxt have been removed from the if statements and are now done beforehand with the results being stored for use later.

  2. Rather than an else if test, move both tests to the first if statement.
  3. Next, tell the robot to move backwards if both sensors are blocked. You code should have the following structure:
    if ( left && right )
      move backward;

    If the above is false, it is still possible that there is an obstacle in front of one sensor and not the other.

  4. After an else statement, add the code you wrote in the first exercise that tells the robot to turn right or left if either the left or right sensor (respectively) is blocked.

    A variation of this algorithm might tell the robot to gather sensor data and always turn one direction if either of the sensors is blocked. An OR operator would be appropriate in this case.

  5. Try this code and see what happens when you put an obstacle in front of any of the sensors:

    if ( left || right )
     rBeep(1, 550);

Switch Case

Sometimes you need to test a condition and perform one of many results based on the value of a variable. Rather than stringing a bunch of else ifs together, there is a switch statement in C.

switch operates a little differently than if. Instead of evaluating a boolean expression, switch operates on an integer value as follows:

switch (val) {
case 17: do_this_if_val=17();
  break;
case 21: do_that_if_val=21();
  break;
case 43: do_something_else_if_val=43();
  break;
default: no_case_action();
  break;
}

do_that(); happens if val == 21. The default: is executed if no case is met. * Note: although a break after the default: is not necessary, it is good practice to add it. Further, execution continues until a break; statement is reached. Leaving the break; out of a case causes the default to be executed every time that case is also executed.

Program second-count.c provides an example of a switch statement within a program.

  1. Using a switch statement, write a program that prints out how much battery has left and beeps if there are less than 6V battery. (Remember the rGetBattery() function.)

Switch to avoid obstacles

Values returned from the rGetObstacleNum (or Txt) function range from 0 to 6000.

  1. Query the robot for the value of its obstacle sensors (these are the IR sensors located on the fluke dongle) and,
  2. convert the result to a range from 0 to 5 (or 1 to 6).
  3. Then, write a switch statement that instructs the robot to perform one of 6 actions based on the range of values from rGetObstacle.

Some ideas might include moving forward for longer amounts of time the farther away the robot is from an obstacle, or beeping longer or multiply times if the robot is closer to an object.

* Hint: Remember integer division truncates any decimal in C.

Feedback Welcome

Development of laboratory exercises is an interative process. Prof. Walker welcomes your feedback! Feel free to talk to him during class or stop by his office.