Further Explanation 0: big picture design

The way this game is set up, there are three basic tasks
  1. carrying out "chosen" direction changes (random or user-commanded)
  2. changing location (stepping forward, possibly with a change in direction dictated by bouncing off a wall), and
  3. determining whether the player dies.
These can be done as three separate tasks. Do it that way!
  1. Have a chunk of code that goes through all the objects and checks for & carries out chosen changes of direction.
  2. Then have a chunk of code that goes through all the objects and steps them forward (which may involve bouncing).
  3. Then have a chunk of code that goes through all the object and determines whether there has been a collision that kills the player.
Keep these tasks separate!

Further Explanation 1: debugging in ncurses

Debugging a program that uses ncurses can be a bit bothersome. For example, one of the most basic debugging techniques is to temporarily add cout (or better, cerr) lines to output messages as the program runs. With ncurses, however, that output interferes with the ncurses output, making this technique pretty much unusable. Using "valgrind" (for those who've seen it) to help diagnose segfaults has the same problem. However, in Lab 10 we showed you an easy fix for these problems, which we'll recapitulate here. We strongly recommend that you try this before you move on from Part 1, even if your Part 1 works perfectly and there's nothing to debug.

Note: the easycurses inputChar() function (in "no-delay mode", as you'll be using it) is "non-blocking". That means that it doesn't wait for the user to to press a key. Instead, it returns the appropriate key if a key has just been pressed, and returns the constant ERR otherwise. So, for example, adding the line shown in red below to your Part 1 code would print a message whenever the user pressed a key.

char ch = inputChar();
if( ch != ERR ) {    // if there was no error 
  // use cerr instead of cout to print out the pressed key
  cerr << "Read a '" << ch << "'!" << endl;
}
if( ch == 'y' )
  break; // game exits with a 'y'

If you want to take advantage of this, however, you need messages written to cerr to print somewhere else other than the terminal window that's showing the ncurses output.
  1. We made a rundebug script for you to manage all this. It opens a 2nd terminal and shows you everything that your program prints with cerr. Just run it with your program like this:
    ./rundebug a.out

You should understand what is happening in that script, so here are the steps if you were to do this manually yourself:

  1. Create a file err in the same directory as your project 3 work using the command:
    ~/$ echo > err 

    Note: you only need to do this once!
  2. Open a separate terminal window, cd to the project 3 directory containing "err", and give the command
    ~/$  tail -f err
    Go back to your original terminal and type something like this:
    ~/$ echo "whoooooooo" > err 
    You should see the whoooooo pop up in the other terminal. Spooky, eh?
  3. Now run project3's part 1 in the original terminal like this:
    ~/$ ./a.out 2> err
    ... and watch as your ncurses window is pure and unblighted by debugging messages, but the message does pop up at the appropriate time in the second window.
Important: You'll want to use this technique throughout this project!

Note: For those of you who've seen it, you can also use "valgrind" this way, which is useful if you have a segfault, though it requires compiling with the -g option to insert debugging information. You would run your program like this:

~/$ ~/$ valgrind ./a.out 2> err
... and watch as the valgrind messages display in the second terminal window.

Further Explanation 2: bouncing off walls

All moving objects in this project bounce off walls in the same way. Normally, if moving one step in the current direction would put you inside a wall, you turn 180 degrees and step in the new direction (away from the wall), all in one turn. Like this:

The only "tricky" situation is when there is a wall both in your current direction and in the opposite direction. In that case, the object stays in the same place and in the same direction, like this:

Further Explanation 3: collisions and death

There are two (and only two!) ways that objects in this game can collide. Either after one "step" they end up occupying the same square, like this:

... which is of, of course, the obvious case. Less obviously, two objects can also collide by swapping positions, like this:

Identifying this is a bit tricky, because you have to know not only what square the object is in at time t, but also what square the object was in prior to that move, i.e. at time t-1.