COE428 Lab 2: Recursion

Program development and debugging

This is a one week lab. This lab must be submitted at least 48 hours before the beginning of your next lab period.

 

Prelab preparation

Before coming to the lab you should:

·         Read the lab. Try to prepare any questions you may have about the lab.

·         Refer to Lab Guide.

·         Create your lab directory for lab2. (i.e. use mkdir lab2 within your coe428 directory.)    

·         Change to your coe428/lab2 and unzip the lab2.zip file with the command:

                      unzip     /home/courses/coe428/lab2/lab2.zip

 

Introduction

This lab reviews even more basic C programming with an emphasis on recursive algorithms. You will:

·         Use programs to aid your understanding of recursion.

Tutorial I: Recursion in C

You have learned in your lectures how to describe and code (in C) several algorithms that are recursive. We re-examine the classic Towers of Hanoi algorithm here.

Specifications for executable

The specs are:

·         Towers are identified with a single integer: the left, middle and right towers are named as '1', '2' and '3' respectively.

·         The program should print (to stdout) the moves required to solve the problem as a sequence of lines in the format:

 
              FROM_ID SPACE DEST_ID
            

For example, to move 3 disks from Tower 1 to Tower 2, the stdout output should be:

 
1 2
 
1 3
 
2 3
 
1 2
 
3 1
 
3 2
 
1 2
 
              

·         NOTHING ELSE should be written to stdout.

The algorithm can be expressed in C as:

#include <stdio.h>
 
#include "towers.h"
 
void towers(unsigned int n, unsigned int from, unsigned int dest)
 
{
 
    unsigned int spare = 6 - from - dest;
 
    if (n != 0) {
 
        towers(n-1, from, spare);
 
        printf("%d %d\n", from, dest);
 
        towers(n-1, spare, dest);
 
    }
 
}
 
        
 
 
 
 

Displaying debugging information

In order to see how the algorithm runs, it is useful to display additional information as the program runs. For example, more insight might be gained if the output was something like:

 
towers(3, 1, 2)         Initial invocation
 
..towers(2, 1, 3)       Recurse (indentation indicates depth)
 
....towers(1, 1, 2)     Recurse
 
......towers(0, 1, 3)   Recurse (base case, returns immediately)
 
......Move #1: From Tower 1 to Tower 2  First move (by towers(1, 1, 2) 
 
......towers(0, 3, 2)
 
....Move #2: From Tower 1 to Tower 3
 
....towers(1, 2, 3)
 
......towers(0, 2, 1)
 
......Move #3: From Tower 2 to Tower 3
 
......towers(0, 1, 3)
 
..Move #4: From Tower 1 to Tower 2
 
..towers(2, 3, 2)
 
....towers(1, 3, 1)
 
......towers(0, 3, 2)
 
......Move #5: From Tower 3 to Tower 1
 
......towers(0, 2, 1)
 
....Move #6: From Tower 3 to Tower 2
 
....towers(1, 1, 2)
 
......towers(0, 1, 3)
 
......Move #7: From Tower 1 to Tower 2
 
......towers(0, 3, 2)
 
     
 
   

How to do this?

The specs require a precise format for the stdout output. Thus we are not allowed to print this additional information to stdout. We opt to use stderr.

The C code

The C source code to do this is shown here:

/* Author: kclowes */
 
/* Description: Solves "Towers of Hanoi" problem.
 
 *              Prints sequence of moves to stdout.
 
 *              Prints other information tracing the
 
 *              algorithm's progress to stderr.
 
 */
 
#include <stdio.h>
 
#include "towers.h"
 
 
 
static void showRecursionDepth(void);
 
static unsigned int depth = 0;
 
static unsigned int moveNumber = 0;
 
 
 
void towers(unsigned int n, unsigned int from, unsigned int dest)
 
{
 
    unsigned int spare = 6 - from - dest;
 
    showRecursionDepth();
 
    fprintf(stderr, "towers(%d, %d, %d)\n", n, from, dest);
 
    depth++;
 
    if (n != 0) {
 
        towers(n-1, from, spare);
 
        showRecursionDepth();
 
        fprintf(stderr, "Move #%d: From Tower %d to Tower %d\n",
 
                        ++moveNumber, from, dest);
 
        printf("%d %d\n", from, dest);
 
        towers(n-1, spare, dest);
 
    }
 
    depth--;
 
}
 
 
 
static void showRecursionDepth()
 
{
 
    int i;
 
    for(i = 0; i < depth; i++)
 
        fprintf(stderr, "..");
 
}
 
          

Compile and run towers

You do not need to type in any of the C source code files; you got copies of the necessary files when you copied the "needed" files for the lab.

Create the executable towers with the command:

 
gcc -o towers towers.c towersMain.c
 

You can now run the program with the command:

 
towers
 

Lots of lines of information will be displayed on the command line window. The next section explains how you can view what is of interest.

Separating stdout and stderr

We have seen previously that the stdout stream using the > symbol. Try it with the towers command as follows:

 
towers > junk1
 

You will still see the quite verbose output written to stderr but the output written to stdout will no longer appear on the screen (and mixed up with stderr); instead, it is put into the file junk1 (where you can view it at your leisure).

It is also possible to redirect stderr with the two-character symbol 2>. For example, we can do the opposite of the previous example (seeing stdout on the screen and re-directing stderr to a file) with the command:

 
            
 
 
 
              towers 2> junk2
 
            
 
          
 

You can also redirect both stdout and stderr at the same time; in this case, each will be written to a separate file and nothing will appear on the screen. Try the following command

 
            
 
towers 2> details > pureStdout
 
          
 

Requirement 1

Answer these questions in your README file.

Question:

Suppose that towers(5, 2, 3) is invoked.

1.      How will the first recursive call to towers() be invoked? Answer this question in the form: towers(x, y, z) where you give the actual values to the three parameters.

2.      How many recursive calls to towers() will be made before this first recursive call actually returns to the initial invocation?

3.      Once towers(5, 2, 3) has invoked its first recursive call to towers() and this invocation has returned, what will be printed to stdout? (i.e. What actual move will be made by towers(5, 2, 3) ?)

4.      How will the second recursive call to towers() be invoked? Answer this question in the form: towers(x, y, z) where you give the actual values to the three parameters.

Question:

Suppose that towers(8, 1, 2) is invoked. How many lines will be printed to stdout?

Note

·         You should note (or try to convince yourself) that the number of lines printed to stdout is precisely equal to the number of moves required to solve the problem.

·         You can use the theoretical analysis of the problem to determine the number of moves.

·         The towers command behaves somewhat differently than what has been described so far. In particular, if it is invoked with an argument, it will move the specified number of disks from Tower 1 to Tower 2. For example,

 
                     
 
 
 
                        towers 10
 
                      
 
                    
 

will output the actions to move 10 disks.

·         Of course, you can redirect the moves to a file and then count the number of lines in the file, allowing you to use the software to verify your theoretical answer.

·         Instead of counting the lines manually, you can use the Unix command wc -l someFile to do the work for you. 

Tutorial II: The main() function

Specifications for the towers command

NAME

towerstowers of Hanoi solver

SYNOPSIS

towers numberDisks fromTower destTower

DESCRIPTION

Solves the Towers of Hanoi problem and prints the required moves to stdout. One line of text in the format FromTowerID ToTowerID is written for each move. The TowerIDs are '1' (for left tower), '2' (for middle tower) or '3' (for right Tower).

The program behaves as follows depending on the arguments given on the command line:

No arguments

If no arguments are given, the problem is solved for moving 3 disks from Tower 1 to Tower 2.

One argument (numberDisks)

If only one argument is given, the problem is solved for moving numberDisks disks from Tower 1 to Tower 2.

Three arguments (numberDisks fromID toID)

If all three arguments are given, the problem is solved for moving numberDisks disks from Tower fromID to Tower toID. The tower IDs must be either '1', '2' or '3' and the two IDs must be different.

EXIT CODE

If the command is invoked correctly, the moves are output and the exit status is 0 (zero).

Otherwise, an incorrect invocation produces no output. A message is displayed on stderr and the exit status is non-zero.

BUGS

The program does not yet behave as specified. The command line args to identify the two towers are not recognized. Furthermore, incorrect invocation is not detected.

Initial version of main()

You are provided with an initial version of the main() function in the file towersMain.c and the listing is shown below. However, the bugs identified in the previous section can be fixed in the main() function.

#include <stdlib.h>
 
#include "towers.h"
 
 
 
int main(int argc, char **argv)
 
{
 
    int n = 3;
 
    int from = 1;
 
    int dest = 2;
 
    if (argc > 1) {
 
        n = atoi(argv[1]);
 
    }
 
    towers(n, from, dest);
 
    exit(0);
 
}

Requirement 2

You have to modify the main() function so that the bugs identified are fixed. The file containing the main function must still be calledtowersMain.c; i.e. you edit the existing file to fix the bugs.

Lab Submission

README file

Your README file should contain:

1.      A brief description of what you did (and did not) achieve in the Lab.

2.      Your answers to the Questions in Requirement 1.

Submit your lab

1. Go to your coe428 directory

2. Zip your lab2 directory by using the following command:

      zip   -r   lab2.zip   lab2/

3. Submit the lab2.zip file using the following command:

       submit   coe428   lab2   lab2.zip

 

by Ken Clowes, revised by Olivia Das