Further Explanation 0: big picture design

The way this game is set up, the 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
can be done as three separate tasks. Do it that way! Have a chunk of code that goes through all the objects and checks for & carries out chosen changes of direction. Then have a chunk of code that goes through all the objects and steps them forward (which may involve bouncing). 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" to help diagnose segfaults has the same problem. However, in Lab 11 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 ncurses wgetch(W) 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 c = wgetch(W);
	 if (c != ERR) { cerr << "Read a '" << c << "'!" << endl;
         if (c == '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. Create a fifo in the same directory as your project 3 work using the command
    If you are
    • on a lab machine,
    • ssh'd into a lab machine, or
    • on your VM but not in csunix or any of its subdirectories
    do the following:
    If you are on your VM and working in csunix or one of its subdirectories, do the following:
    Note: this will require typing your password as it is ssh'ing into an academy machine
    Note: please copy and paste this! It's too easy to mess up typing by hand.
    mkfifo err
    perl -e '`pwd`=~/csunix(.*)$/; `ssh mope mkfifo ./$1/err`;'
    Do an ls and you should see "err" show up in the directory listing. The fifo "err" is like a wormhole in the Unix filesystem.
    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. What's going on here is this: the 2> err is telling the shell to redirect standard error (cerr in our program) output to the fifo err, from whence it travels by wormhole to the less -f running in the other window.
    Note: 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.
Important: You'll want to use this technique throughout this project!

Further Explanation 2: bouncing off walls

All moving objects in this project bounce off walls in the same way. Part 2 of the project explains this precisely and completely, but I thought maybe some pictures would be in order. 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.