CSC 161 Grinnell College Fall, 2011
 
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.

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

Exercise 1

Copy the program conditionals.c to a working directory. This can be accomplished with the copy url program in linux. In your working directory, paste the following line into your terminal window and run it:

curl http://www.cs.grinnell.edu/~walker/courses/161.fa11/modules/module-cond-loops-testing/conditionals.c

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

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

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.

  1. Next, make the robot beep depending on the result so you know whether there is an obstacle or not. If you are adverse to beeping, consider using printf instead.

After you are finished, compile and run your program with the robot in different locations.

  1. 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:
else
  rBeep(1, 880);
  rBeep(1, 800);
  1. Compile and run your program now. What happens?

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

  1. Fix the program so it operates as expected.

else if ...

Exercise Two

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 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;
  1. Compile, run, and verify your code works before moving on.
  2. What happens if there is an obstacle in front of both sensors?

Testing Two Things at Once

There is a problem with the code we just wrote. Consider the case where there is an obstacle in front of both sensors. We could just have the robot choose a direction to move using the previous method, but it might be more effective if we have a separate case for that condition altogether. To clarify, in the current method, the robot checks the left sensor and moves, then checks the right sensor. The right sensor won't be looking at the same location after the robot moves, and the robot might hit the obstacle on its right because it waited to check that side. This 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. Besides being a waste of clock cycles (a function call is much more expensive than examining a variable), this method is inaccurate. 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.

Exercise 3

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.

  1. Rather than an else if test, move both tests to the first if statement.
  2. 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.

  1. 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.

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();
  break;
case 21: do_that();
  break;
case 43: do_something_else();
  break;
default: no_case();
  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.

Exercise 4

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.

Reminder: Complete Evaluation Form

When you have finished this lab, be sure to fill out its evaluation form in the "Lab Evaluation" section for CSC 161 on Pioneer Web