Answers to Part I exercises
page 8-1 The fish could end up in the starting position, or some even distance away
from the starting point, e.g., +2, +4, +6, or -2, -4, -6. Given the even
number of flips (6), it is impossible for the fish to end up an odd distance away
from the starting position.

page 8-2 Given that a coin flip is a truly random event, the number of heads (move
right) should equal the number of tails (move left), thus canceling each other.
The fish will most likely end up at the starting position, often 2 positions to
the right or left, and very rarely 6 positions to the right or left.

page 9 Stop and help. The output of program sixflips.cpp looks random, although you
should run it a few times to see the “randomness” of the results. Each run will
most likely give a different output than the previous run. If it does not, the
random number generator is being constructed with a seed that causes the
same sequence of numbers to be generated every time you run the program.
Remove the integer parameter passed to the RandGen constructor to obtain
different results with each run.

page 10 Stop and help (top of page). If an integer argument is supplied to the
RandGen constructor, each run will produce the same output.
Stop and help (bottom of page). The final position value will be -2, 0, or +2,
although +2 rarely showed up when the author ran the program. These are
the only possibilities if the number of steps chosen is two.

page 11-1
// diceroll.cpp
#include <iostream.h>
#include "randgen.h"
int main()
{
int k;
RandGen r;
for (k = 0; k < 10; k++)
   cout << r.RandInt(1, 6) << endl;
return 0;
}

page 11-2 The first if statement will evaluate true approximately 33% of the time, so red
will appear about 33% of the time.
The else-if statement will be evaluated the other 66% of the time. The
probability of matching the value 1 is 1/3. Therefore the chances of getting a
“white” value is 2/3 * 1/3, or 22% of the time.
The last else will happen 2/3 of the time the else-if is evaluated. The chances
of getting a “blue” value is 2/3 * 2/3, or 44% of the time.

page 12-3
flip = (int)(r * 2);
r will be a random number 0 =r < 1.0, so 0 =r * 2 < 2.0. When a double is
cast to an int it is truncated, so if 0 =r * 2 < 1.0, then (int)(r * 2) = 0, and
if 1.0 =r * 2 < 2.0, then (int)(r * 2) =1.

page 12-4
// onedwalk.cpp
#include <iostream.h>
#include "randgen.h"
int main()
{
   RandGen randomVals;
   int numSteps, step, flip;
   int position = 0;
   int max = 0;
   int min = 0;
   cout << "Number of steps? ";
   cin >> numSteps;
   for (step = 0; step < numSteps; step++)
   {
      flip = randomVals.RandInt(2);
      if (flip == 0)
         position++;
      else
         position--;
      if (position > max)
         max = position;
      if (position < min)
         min = position;
   }
   cout << "Final position = " << position << endl;
   cout << "Maximum position = " << max << endl;
   cout << "Minimum position = " << min << endl;
   return 0;
}
page 18-1 
The use of a RandGen object is encapsulated inside an AquaFish object. The
#include "randgen.h" call is taken care of at the top of the aquafish.cpp file.

page 18-2
A compiler will compile each file (aquafish.cpp, randgen.cpp, aquamain.cpp) into
object code. The linker links all the object codes together into an executable
program.

page 18-3
AquaFish::AquaFish(int tankSize, int initPosition) : 
   myPosition(initPosition), myTankSize(tankSize),
   myBumpCount(0), myDebugging(true)
{}
page 18-4
AquaFish::AquaFish(int tankSize, int initPosition, bool debug) :
   myPosition(initPosition), myTankSize(tankSize),
   myBumpCount(0), myDebugging(debug)
{}

page 18-5 
When the debugging responsibility is left to the AquaFish class, it makes life
less complicated for the client programmer, i.e., you don’t have to worry about
setting a Boolean value. However, if you wanted to switch the debugging off,
the programmer would have to recode the debugging value inside of the
AquaFish class and recompile.
Providing another constructor that allows the client programmer to set the
Boolean value is very useful. This gives the client programmer a bit more
control of the behavior of an AquaFish object. It also eliminates the need to
recode the AquaFish class if it is necessary to change the debugging value. If
the client programmer does not wish to set the debugging value, it can be
omitted and either of the other two constructors will be invoked. There really
isn’t a downside to this approach.

page 18-6 
// aquafish.h
#ifndef _AQUAFISH_H
#define _AQUAFISH_H
class AquaFish
{
public:
//... already existing member functions
   int MaxPos() const; // Return max position
   int MinPos() const; // Return min position
private:
//... original data members
   int myMaxPos;
   int myMinPos;
};
#endif

// aquafish.cpp
#include <iostream.h>
#include "aquafish.h"
#include "randgen.h"
AquaFish::AquaFish(int tankSize) :
   myPosition(tankSize/2), myTankSize(tankSize), myBumpCount(0), 
   myDebugging(true), myMaxPos(tankSize/2), myMinPos(tankSize/2)
{}

void AquaFish::Swim()
{
   RandGen randomVals;
   int flip;
   if (myPosition == myTankSize - 1) {
      myPosition--;
   }
   else if (myPosition == 0) {
      myPosition++;
   }
   else {
      flip = randomVals.RandInt(2);
      if (flip == 0) {
         myPosition++;
      }
      else {
         myPosition--;
      }
   }
   if (myDebugging) {
      cout << "*** Position = " << myPosition << endl;
   }
   if (myPosition == 0 || myPosition == myTankSize - 1) {
      myBumpCount++;
   }
   if (myPosition > myMaxPos)
      myMaxPos = myPosition;
   if (myPosition < myMinPos)
      myMinPos = myPosition;
}

int AquaFish::MaxPos() const {
   return myMaxPos;
}

int AquaFish::MinPos() const {
   return myMinPos;
}

int AquaFish::BumpCount() const {
   return myBumpCount;
}  
page 18-7 
The bug that was introduced could be in one of three files: aquamain.cpp,
aquafish.h, or aquafish.cpp. A syntax error will be caught by the compiler and
will be easy to spot and correct in the appropriate file. If it’s a logic error, such
as initializing a value with a 1 instead of a 0, that type of error will be more
difficult to catch and fix because it could be in any one of three files.

page 18-8
// aquafish.h
#ifndef _AQUAFISH_H
#define _AQUAFISH_H
#include "apvector.h"
class AquaFish
{
public:
   AquaFish(int tankSize);
   void Swim(); // Swim one foot.
   int BumpCount() const; // Return the bump count.
   int NumVisits(int index) const;
   // Return number of visits at that index
private:
   int myPosition;
   int myBumpCount;
   int myTankSize;
   bool myDebugging;
   apvector<int> myVisits;
};
#endif
___________________
// aquafish.cpp
#include <iostream.h>
#include "aquafish.h"
#include "randgen.h"
AquaFish::AquaFish(int tankSize) :
   myPosition(tankSize/2), myTankSize(tankSize), myBumpCount(0),
   myDebugging(true), myVisits(tankSize,0)
{}

Answers to Part II exercises  
Experimenting with the program: pages 19-28
One of the important concepts in computer science is abstraction — using a 
program or code segment without knowing how it works. Abstraction in the context 
of using a program means that the user of the program needs only to follow the 
rules about using the program. For example, the rules about using a web browser are 
pretty simple: click on the hyperlinks, type in a URL, or type in keywords in a search 
engine. Using a class in C++ involves knowing how to instantiate an object using a 
constructor and how to use all the member functions to access or manipulate the object. 
At any level, abstraction tries to hide the details of implementation from the user.
This section of the case study provides an opportunity to experience the
Marine Biology program as an abstraction. We want to treat the program as a
black box and simply test it. The idea of a black box is that you supply input
to the box, push some buttons on the box (call some routines), and examine
the output from the box without seeing the internals of the box. At this point
in our study of the program, we want to approach it as a black box, test it,
supply different input values, and see how it performs. Learning about the
behavior of the program will provide the overview before you dig into the code
behind the scenes.

You will be asked in the text to experiment with different scenarios.
Different sizes of environments and different quantities of fish are
controlled by the content of the fish.dat file. For example, the following file
would create a 5 x 7 environment with 2 fish:

5 7
0 1
1 5

The first two numbers give the dimensions of the environment. Each
successive pair of numbers gives the location of each fish. You might need to review
the documentation in environ.h that explains the format of the data file, fish.dat. 

page 20-1 simulate.h

page 20-2
The first line in main
          ifstream input("fish.dat");
would be changed by replacing "fish.dat" with a different file name (including
the full path if necessary).
You may also find it helpful to change the code in the main function
in the fishsim.cpp file so that the user is prompted for the fish data file name,
rather than always using "fish.dat". This makes it easier to experiment with
several different fish data files. Simply replace the one line of code
        ifstream input("fish.dat");
with the following four lines of code.

   apstring fishfile;
   cout << "Enter file name for fish: ";
   cin >> fishfile;
  ifstream input(fishfile.c_str());

page 20-3
The modified code is shown below for the for loop in fishsim.cpp

for (step = 0; step < numSteps; step++) {
   sim.Step(env);
   display.Show(env);
   if (step % 5 == 0) {
      cout << " step " << step << " (press return)";
      getline(cin,s);
   }
}
page 20-4
Some common strategies include running the program using different data or
sample input, reading the documentation supplied with the program, and
making minor changes to the code to see what effect it has on the execution of
the program.

page 22 Stop and help.
Possible hypotheses: the fish move one square at a time; fish
movement is random; the possible moves are up, down, left, or right; fish
don’t seem to die off; the fish don’t seem to be reproducing; the fish do not
move outside the boundaries of the environment; fish are not able to share
a location.

page 22 Stop and predict. 
Some possible answers include designing data files to test
different scenarios such as a small environment (3 x 3) with one fish, a small
environment with two fish, checking the life and death behavior of a small
population of fish in a small environment, a small environment (3 x 3) filled
with 8 fish, etc. All of these scenarios will help confirm or challenge different
hypotheses about the program.

page 24 Stop and predict. 
A fish could move to a location that prevents a subsequent
fish from moving. The order of fish movement will have an effect on the
movement of each fish.

page 24 Stop and help (first one). 
The fish seems to move left and right in a random
pattern, much like the fish in a five foot tank from part one. The fish in the
1 x 5 environment might not behave the same depending on its behavior as it
runs into the walls. A fish in the marine biology case study has the potential
of 4 or possibly 8 moves (including diagonals) and we do not yet know its
behavior as it runs into walls.

page 24 Stop and help (second one). 
It seems that the direction selected is random
and limited to one of four possible directions: up, down, left, or right. When it
runs into the boundary of the environment, it moves along or away from the
boundary. The fish does not seem to “escape” outside the environment.

page 28-1
When the program was run multiple times, the fish moved from the starting
configuration on the left to the configuration shown on the right:

Starting     Final  
 A _         B A
 B C         C _

Given the consistency of the output, the pattern of movement is not random.
Two possible patterns of movement could produce this result: row by row, left
to right, or column by column, top to bottom.

page 28-2
Making an assumption that the movement is row by row, left to right, the
resulting configuration should look like this:

A C E
D B H     Running the program confirmed this result.
F G_

page 28-3 
The program took a long time to run, and a single ‘A’ appeared briefly in the
upper left-hand corner. It seems that the program spent time setting up this
large environment and printing out blank characters. In one experiment on a
2000 x 2000 environment the computer had to be reset. The maximum size will
vary depending on the resources (memory and processing speed) of the computer.

page 28-4 
An error message was printed: “error, attempt to create fish at non-empty:
(1,1)”. If a position in the environment already contains a fish, it apparently
is an error to try to create another fish at the same location.
page 28-5 The sequence of fish positions in fish.dat does not matter. The two suggested
fish.dat files produced the same pattern of movement. Different letters
occupied the positions in each trial, but the pattern of movement was
consistent.

page 28-6
This program produces a new data file "newfish.dat" so that the original fish.dat
file is unchanged.

// makeFish.cpp
// This program asks the user for the row size, col size, and
// the number of fish. It then prompts the user for the
// positions of the fish and writes the data values to the
// file newfish.dat in the format specified in environ.h
#include <iostream.h>
#include <fstream.h>
int main() {
   int rowSize, colSize, howMany, count;
   int row, col;
   ofstream outfile ("newfish.dat");
   cout << "Enter row size —> ";
   cin >> rowSize;
   cout << "Enter col size —> ";
   cin >> colSize;
   cout << "Enter number of fish —> ";
   cin >> howMany;
   outfile << rowSize << " " << colSize << endl;
   for (count = 1; count <= howMany; count++) {
      cout << "fish " << count << ", enter row position —> ";
      cin >> row;
      cout << "fish " << count << ", enter col position —> ";
      cin >> col;
      outfile << row << " " << col << endl;
   }
   return 0;
}

Learning how the program stores fish: pages 29-36
This section provides a context to consider alternative strategies of data
storage. After you have studied the apvector and apmatrix classes in your
course, you could use the case study to explore different ways of storing data
as described on page 31 of the case study.

You should skim through all eight header files to start understanding what the
various types of objects can do. We are now moving from experimenting with the 
black box to opening it up and understanding how it works. This section focuses on
how fish are stored in the environment.

page 32 Stop and consider. 
Using an explicit strategy, some of the operations that
would be faster are counting, printing, etc. With the same explicit strategy,
some of the operations that would be less efficient would be inserting a new
value if the list was sorted, searching, and examining the neighbors of a
specific position.

page 35 Stop and help.
Control of the order of processing of fish is currently in the
AllFish member function of the Environment class. One way of changing the
order of processing is to change the AllFish code. A second way is to change the
Step function in the Simulation class so that it sorts the array of fish (fishList),
causing fish to be moved in a different order.

page 36-1
A function would be needed to sort the array of fish into the desired order
based on their position. Once the fish have been sorted, the Step function
would then move each fish.

page 36-2 
Advantage of having the Environment AllFish function manage the order of
movement: The AllFish function already examines each location in the
container class (apmatrix). By simply changing the nesting of the loop (row vs.
column) and direction (ascending vs. descending), the order of processing can
be easily modified.
Disadvantage of having the Environment AllFish function manage the order
of movement: The list of fish returned by AllFish is used for two different
purposes. Simulate::Step uses the list to determine the order for processing
fish; Display::Show uses the list to display all the fish. If AllFish sorts the list
before returning it, then that order might be appropriate for one purpose but
not the other. For example, if AllFish returns the list in bottom-up, right-to-left
order for use in Simulate::Step, then a text-based version of Display::Show will
need to sort the list a second time in order to display the fish.

Learning how fish interact with the environment: pp. 36-42
This section examines the problem of moving fish and keeping track of their
positions in the environment. The program has a fish storing its own position,
and the environment also keeps track of fish positions indirectly by storing
fish objects in the apmatrix. This is the most complicated section to understand
as we learn how three classes (Environment, Fish, and Neighborhood) interact
to move fish. In this section, you will begin making major modifications to the
code in various classes. Make sure that you save an original copy of the
code as they make changes to the classes. Later on in the case study you will
want to compare the output of the original code with modified versions. We
suggest putting the original and modified code in separate directories rather
than renaming them.

page 37 Stop and consider.
Exploring the code down to a detailed level might lead to
an understanding of the interaction of the objects involved in moving a fish.
Conversely, stepping back and thinking about the big picture might lead to
understanding of the issues that led to such a complex answer. Understanding
the issues of fish movement will also help when it comes time to modify the
program to support a larger ocean.

page 40 Stop and help.
A summary of the fish movement process: 1) the fish collects
empty neighboring positions; if there is an empty nearby position, it continues;
2) it stores its current position before moving to a new position; 3) it selects a
random new position from the list of empty neighboring positions; 4) it then
tells the environment to update its new position.

page 42-1 
bool operator != (const Position & lhs, const Position & rhs)
// postcondition: returns true iff lhs != rhs
{
return ! lhs.Equals(rhs);
}
page 42-2 
There is a slight advantage to moving the random neighbor selection process
from the Fish class to the Neighborhood class as described. In either scenario,
the Move function must fill the Neighborhood object (nbrs) with potential
empty positions, test for (nbrs.size > 0), then randomly select a position.
However, moving the random number generation code to the Neighborhood
class better encapsulates the sense of an unordered collection and makes the
code in the Move function easier to read. On the other hand, it would be more
difficult to step through all the positions in a neighborhood if the Select
function returns a randomly chosen position. For example, if a neighborhood
contains four positions, one could not easily step through all the positions
simply by calling Select four times, because two or more of these calls might
return the same position.

page 42-3
Position Neighborhood::Select() const
// precondition: 0 < Size()
// postcondition: returns a random Position in Neighborhood
{
   RandGen randomVals;
   return myList[randomVals.RandInt(0, Size() - 1)];
}

void Fish::Move(Environment & env)
// precondition: Fish stored in env at Location()
// postcondition: Fish has moved to a new location
// in env (if possible)
{
   Neighborhood nbrs = EmptyNeighbors(env, myPos);
   DebugPrint(3, nbrs.ToString());
   if (nbrs.Size() > 0) {
      // there were some empty neighbors, so randomly choose one
      Position oldPos = myPos;
      myPos = nbrs.Select(); // uses new member function
      DebugPrint(1, "Fish at " + oldPos.ToString() + " moves to "
                               + myPos.ToString());
      env.Update(oldPos, *this);
   }
   else {
      DebugPrint(1, "Fish " + ToString() + " can’t move.");
   }
}

page 42-4
Neighborhood Environment::EmptyNeighbors(const Position & pos)
// postcondition: returns a Neighborhood with empty positions
// around pos in N, S, E, W directions
{
   Neighborhood nbrs;
   if (IsEmpty(pos.North()))
      nbrs.Add(pos.North());
   if (IsEmpty(pos.South()))
      nbrs.Add(pos.South());
   if (IsEmpty(pos.East()))
      nbrs.Add(pos.East());
   if (IsEmpty(pos.West()))
      nbrs.Add(pos.West());
   return nbrs;
}

void Fish::Move(Environment & env)
// precondition: Fish stored in env at Location()
// postcondition: Fish has moved to a new location
// in env (if possible)
{
   RandGen randomVals();
   Neighborhood nbrs = env.EmptyNeighbors(myPos);
   DebugPrint(3, nbrs.ToString());
   if (nbrs.Size() > 0) {
      // there were some empty neighbors, so randomly choose one
      Position oldPos = myPos;
      myPos = nbrs.Select(randomVals.RandInt(0, nbrs.Size() - 1));
      DebugPrint(1, "Fish at" + oldPos.ToString() + " moves to "
                              + myPos.ToString());
      env.Update(oldPos, *this);
   }
   else {
      DebugPrint(1, "Fish " + ToString() + " can’t move.");
   }
}

page 42-5
To support this constructor, the AddIfEmpty function was moved from the Fish
to the Neighborhood class.
void Neighborhood::AddIfEmpty(const Environment & env,
const Position & pos)
// postcondition: pos is added to nbrs if pos in env and empty
{
   if (env.IsEmpty(pos)) {
      Add(pos);
   }
}

Here’s the new Neighborhood constructor that queries the environment around
Position pos for empty positions.
Neighborhood::Neighborhood(const Environment & env,
const Position & pos): myList(4), myCount(0)
// postcondition: Size() == # positions in Neighborhood
{
   AddIfEmpty(env, pos.North());
   AddIfEmpty(env, pos.South());
   AddIfEmpty(env, pos.East());
   AddIfEmpty(env, pos.West());
}

And finally, the revised Fish member function, Move.

void Fish::Move(Environment & env)
// precondition: Fish stored in env at Location()
// postcondition: Fish has moved to a new location
// in env (if possible)
{
   RandGen randomVals;
   Neighborhood nbrs(env, myPos);
   DebugPrint(3, nbrs.ToString());
   if (nbrs.Size() > 0) {
      // there were some empty neighbors, so randomly choose one
      Position oldPos = myPos;
      myPos = nbrs.Select(randomVals.RandInt(0, nbrs.Size() - 1));
      DebugPrint(1, "Fish at" + oldPos.ToString() + " moves to "
                              + myPos.ToString());
      env.Update(oldPos, *this);
   }
   else {
      DebugPrint(1, "Fish " + ToString() + " can’t move.");
   }
}

page 42-6
Each change required a moderate amount of rewriting the code. Moving the
random selection process to the Neighborhood class (question 3), or moving the
filling of the Neighborhood object to the Environment class (question 4) simply
shifts code and responsibility without any clear advantage or disadvantage.
Thinking about the entire problem, it seems to make the most sense to have
the environment control the determination of any empty neighbor spot to
move a fish. A Fish object could make that determination by checking locations
in the environment. The Neighborhood class seems to be a helper class to
organize the process. Yet from a code standpoint, there is an advantage to
moving the determination of an empty position into the Neighborhood class.
It consolidates the code about empty positions and potential moves into one
class, so that, if the program needed to support more than four directions
(N,S,E,W), only one class needs to be modified, the Neighborhood class. The
program in its current form, or using either of the first two modifications,
would require changes to two classes to support a different quantity of
directions.

page 42-7
The original design:                Neighborhood selects the random
                                    position, page 42-2
__________________________________  ____________________________________
1. A fish is told to move by the    1. A fish is told to move by the
Simulation object                   Simulation object
2. The fish gets a list of empty    2. The fish gets a list of empty
neighbors from the environment      neighbors from the environment
3. If there is an empty neighbor,   3. If there is an empty neighbor,
the fish gets a random number       the fish asks the Neighborhood
and gets a position from the        object to return a random position
Neighborhood object to move to      stored in Neighborhood
4. The fish informs the Environment 4. The fish informs the Environment
of its new position, which is       of its new position, which is updated
updated

The only difference between the original design and the modification
suggested in question 42-2 is the responsibility for selecting the random
position to move to. In the original design, the fish takes responsibility for
selecting a random number, which in turn is used to get a position from the
Neighborhood object. In the modified version, the Neighborhood object takes
responsibility for selecting a random position within itself and returns that
position to the Fish object.

page 42-8 
Answers may vary, but most people would probably respond with continuing to
explore the code, rather than design the code. Designing code is hard work and
often leads to several cycles of design and coding. One of the advantages of
trying to design code is you will probably end up understanding the problem
to a greater depth than just by reading someone’s else code. It might lead to
an alternative solution because you are starting from scratch with the design.
The drawback is this takes more time. But eventually you will encounter a
situation where original design work is required and prior experience will
be helpful.

Understanding the rest of the program: pages 43-48
The author of the program included debugging statements to help track key
events in the program. Given that fish moves involve a random choice of
a direction, it is difficult to verify if the program is working correctly. By
changing the LEVEL_OF_DEBUG_DETAIL constant value in the utils.cpp file,
different debugging messages are printed out by the program. The chart is
on page 45.

page 44 Stop and help. 
The following configuration would not be printed correctly if
the AllFish function returned fish ordered column-by-column.
_  B  _
A  _  _         fishList would be stored as: A(1,0), B(0,1), C(2,2)
_  _  C
The nested loops in the Show function of the Display class process fish
row-by-row, left to right, while checking the position of the fish in fishList in
sequential order. The ‘A’ fish would be printed first since it’s the first fish in
fishList, but then fish ‘B’ would never be printed because the nested for loops
have already passed over row 0.

page 48-1 
void Display::Show(const Environment & env)
// postcondition: state of env written as text to cout
{
   const int WIDTH = 1; // for each fish
   int rows = env.NumRows();
   int cols = env.NumCols();
   int r, c;
   Fish tempFish;
   for (r = 0; r < rows; r++) {
      for (c = 0; c < cols; c++) {
         tempFish = env.FishAt (Position(r, c));
         if (tempFish.IsUndefined()) {
            cout << setw(WIDTH) << ' ';
         }
         else // this is a position with a fish
         {
            cout << setw(WIDTH) << tempFish.ShowMe();
         }
      } // finished processing all columns in a row
      cout << endl;
   } // finished processing all rows in the grid
}

page 48-2 
Somewhere in the overall program, a function would have to rearrange the
fish into some random order. The function could be in the Environment class,
the Simulation class, or in the utils.cpp file. The function would need to utilize a
random number generator to rearrange the fish in some pseudorandom order.
The Step function would then move each fish in the array, which would be in a
random order based on position. This problem is tricky because the random
sequence would include duplicate positions, and the code would have to avoid
them in arranging the fish positions.
For students interested in exploring this shuffling algorithm, you might assign
a program to
• fill an apvector with integers,
• randomly rearrange the integers in the vector.
Doing this as a separate exercise, outside the complexities of this case study,
allows the student to concentrate on this interesting algorithm.

page 48-3
After making the modifications to support moving fish in random order,
change the LEVEL_OF_DEBUG_DETAIL constant to 1 in the utils.cpp file. This
will cause fish id and positions to be printed out with both the before and after
positions. Several simulations should be run for multiple steps using different
fish.dat files to confirm the movement of fish in random order.

page 48-4 
You could create a Fish data member, myLastPosition, which can be used to
determine the direction the fish has just moved. You need this in order to keep
track of what moving forward means. Use EmptyNeighbors to fill the array as
follows. If the forward position is empty, store that location in the array twice.
Store each available location to the fish’s left or right in the array once. If the
array is not empty, randomly select a position and move. Otherwise, try to
move backward. If that space is empty, the fish does not move.
Another variation, not involving probability, would be to have the fish always
attempt to move forward first, then to one side or the other, and finally
to return to its former position. To do this, first test if the next position in
the same direction is empty. If that position is empty, the fish moves there.
Otherwise it looks for positions to its right or left and selects one. If none of
those is empty, it tries looking behind itself (which is really myLastPosition)
and moves backward. Of course, if all four positions are occupied, the fish does
not move.

page 48-5 
The Simulation class manages the tasks of stepping or running the simulation.
The Step function takes care of filling the array of fish and calling Move for
each fish. The Run member function simply performs multiple calls of Step.
It packages the stepping of a program and keeps the main client program
(fishsim.cpp) nice and clean. It may also allow for a layer of abstraction
to be maintained between the client program and the lower level classes
(Environment and Fish) as the program is modified.

page 48-6 If the main client program starts calling member functions that are rather
detailed and low level such as AllFish and Move, it’s probably a good idea
to create an intermediary class such as Simulation. If the strategies were to
change in the Environment or Fish class and stepping through the environment
required different member function calls, the Simulation class would hide those
changes from the client program.