I'm also working on a game project, and I'm stuck. I guess I'll have to post the specs so you guys can see what I have to do.

Here are the specs:

The valley floor can be viewed as a 10 by 10 grid. The northwest corner (upper left on the screen display) is consider to have coordinates (0,0). The northeast corner (upper right) is (0,9), the southwest is (9,0) and the southeast is (9,9). At any time, each robot is at one of the 100 grid points, facing north, east, south, or west. More than one robot may occupy the same spot. Each time a step elapses in the history of the valley, every robot with any energy remaining may turn and then move one grid unit in the direction it is facing. A robot starts out with 60 units of energy, and each attempt to move costs it one unit of energy. A grid point in the valley may contain an energy source, and if a robot arrives at such a point, its energy level goes back up to 60.

The program skeleton you are to flesh out defines three classes that represent the three kinds of objects this program works with: Valley, Robot, and EnergySource. Details of the interface to these classes are in the program skeleton, but here are the essential responsibilites of each class:
Valley

* When a valley object is created, it has no robots or energy sources.
* When a valley object goes away, any robots or energy sources it contains must be destroyed.
* A robot may be created and added to a valley object.
* An energy source may be created and added to a valley object.
* A valley object may be displayed on the screen, showing the locations of the robots and energy sources, and the energy remaining for each robot in the valley. (If a robot is at an energy source, only the robot appears in the display; if two robots are at the same spot, only one of them appears in the display.)
* Given a robot, a valley object may be asked if there is another robot at the same location as the one given.
* A valley object may be asked if there is an energy source at a given position.
* A valley object may be told to direct each robot in the valley to take a step.

EnergySource

* This simple class merely records and reports an energy source's position.

Robot

* A robot is created with a name, a position, and the direction it is initially facing. A robot starts life with 60 energy units.
* A robot may be told to take a step. A robot with no remaining energy will not move. Otherwise, the robot will attempt to move. In detail, this means: With 1/3 probability, the robot will pick a random direction to face; otherwise, it will stay facing in its current direction. The robot will attempt to move one grid unit in the direction it is facing. (It may not be able to move if its way is blocked by the mountains at the edge of the valley.) The move attempt costs one energy unit. If the robot moves onto an energy source, it is recharged to 60 energy units. If a robot with at least 30 energy units moves onto a spot occupied by a robot with 0 units, it will transfer 10 units to that other robot to give it a chance to get to an energy source.
* Robots have no memory of where they've been or what they've seen there. Robots don't know where other robots are; at best, a robot may find out from the valley object it belongs to what other robot, if any, is where it's currently standing.
* If when a robot takes a step, it arrives at a spot where there are at least two other robots, we'll keep things simple: it finds out about one of them and donates energy if appropriate. Then it's done with its step. It does not try to check for other robots at that spot.

You must not make any deletions, additions, or changes to the public interface of any of the classes -- we're depending on them staying the same so that we can test your programs.

I will post my code below.

Recommended Answers

All 13 Replies

// Portions you are to complete are marked with a TODO: comment.
// The first thing you probably want to do is implement the trivial
// functions.  Then get Valley::addEnergySource and Valley::display going.
// That gives you more flexibility in the order you tackle the rest
// of the functionality.

#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;

///////////////////////////////////////////////////////////////////////////
// Manifest constants
///////////////////////////////////////////////////////////////////////////

const int NROWS = 10;               // number of rows in the valley
const int NCOLS = 10;               // number of columns in the valley
const int MAXROBOTS = 10;           // max number of robots allowed
const int MAXSOURCES = NROWS*NCOLS; // max number of energy sources
const int FULL_ENERGY = 60;         // number of units when fully charged
const int SHARE_THRESHHOLD = 30;    // will share energy if have at least this
const int SHARE_AMOUNT = 10;        // amount of energy to share

///////////////////////////////////////////////////////////////////////////
// Type definitions
///////////////////////////////////////////////////////////////////////////

enum Dir {
    NORTH, EAST, SOUTH, WEST
};

class Valley;  // This is needed to let compiler know that Valley is a type
//   name, since it's mentioned in the Robot declaration.

class Robot
{
public:
    // Constructor
    Robot(string nm, Valley* vp, int r, int c, Dir d);

    // Accessors
    string name() const;
    int    energy() const;
    int    row() const;
    int    col() const;
    Dir    dir() const;

    // Mutators
    bool   step();

private:
    string  m_name;
    int     m_energy;
    int     m_row;
    int     m_col;
    Dir     m_dir;
    Valley* m_valley;
};

class EnergySource
{
public:
    // Constructor
    EnergySource(int r, int c);

    // Accessors
    int row() const;
    int col() const;

private:
    int m_row;
    int m_col;
};

class Valley
{
public:
    // Constructor
    Valley();
    ~Valley();

    // Accessors
    Robot* otherRobotAt(Robot* rp) const;
    bool   energySourceAt(int r, int c) const;
    void   display() const;

    // Mutators
    bool   addRobot(string name, int r, int c, Dir dir);
    bool   addEnergySource(int r, int c);
    bool   step();

private:
    Robot*        m_robots[MAXROBOTS];
    int           m_nrobots;
    EnergySource* m_sources[MAXROBOTS];
    int           m_nsources;
};

///////////////////////////////////////////////////////////////////////////
//  Robot implementation
///////////////////////////////////////////////////////////////////////////

Robot::Robot(string nm, Valley* vp, int r, int c, Dir d)
: m_name(nm), m_energy(FULL_ENERGY), m_row(r), m_col(c), m_dir(d), m_valley(vp)
{
    // Since the first character of the Robot's name shows up in the
    // display, there had better be a first character.
    if (nm.size() == 0)
    {
        cout << "***** A robot must have a non-empty name!" << endl;
        exit(1);
    }
    if (r < 0  ||  r >= NROWS  ||  c < 0  ||  c >= NCOLS)
    {
        cout << "***** Robot created with invalid coordinates (" << r << ","
            << c << ")!" << endl;
        exit(1);
    }
}

// TODO:  Implement the other four accessor functions.  These should be trivial. 
string Robot::name() const
{
    return m_name;
}

int Robot::energy() const
{
    return m_energy;
}

int Robot::row() const
{
    return m_row;
}

int Robot::col() const
{
    return m_col;
}

Dir Robot::dir() const
{
    return m_dir;
}

// TODO:  Implement the step function.  Here's an outline:

bool Robot::step()
{
    // If the robot has no energy left, return false
    // Otherwise,
    //    Randomly change direction with probability 1/3
    if (rand() % 3 == 0)    // 1/3 probability to pick a direction
        m_dir = Dir(rand() % 4);    // pick a random direction
    //    Attempt to move one step in the direction we're currently facing.
    //      (E.g., to move north, decrement the row coordinate.)  If we can't
    //      move in that direction, don't move.
    switch (m_dir)
    {
    case NORTH:
        // TODO: Move one step north, if possible.
        break;
    case SOUTH:
    case WEST:
    case EAST:
        // TODO: Implement the other movements
        break;
    }
    //    The attempt to move consumes one unit of energy.
    //    If as a result of the attempt to move, the robot is at an energy
    //       source, it's recharged to the FULL_ENERGY level.
    //    If at this spot there's another robot whose energy level is 0,
    //       then if we have at least SHARE_THRESHHOLD units of energy,
    //       transfer SHARE_AMOUNT units to that other robot.  (If there
    //       are two or more dead robots here, we donate to only one.)
    //    Return true, indicating the robot attempted to move.
    return true;
}

///////////////////////////////////////////////////////////////////////////
//  EnergySource implementations
///////////////////////////////////////////////////////////////////////////

// TODO:  Implement the constructor
//    EnergySource(int r, int c)
//        The constructed EnergySource's location will be at coordinates (r,c).

EnergySource::EnergySource(int r, int c)
: m_row(r), m_col(c)
{
    if (r < 0  ||  r >= NROWS  ||  c < 0  ||  c >= NCOLS)
    {
        cout << "***** EnergySource created with invalid coordinates (" << r << ","
            << c << ")!" << endl;
        exit(1);
    }
}

// TODO:  Implement the two accessor functions.  These should be trivial.
int EnergySource::row() const
{
    return m_row;
}

int EnergySource::col() const
{
    return m_col;
}

///////////////////////////////////////////////////////////////////////////
//  Valley implementations
///////////////////////////////////////////////////////////////////////////

Valley::Valley()
: m_nrobots(0), m_nsources(0)
{
    //      TODO:  Implement the constructor
    //      Make the valley have no robots or energy sources.
}

Valley::~Valley()
{
    // delete []   ;
    //      TODO:  Implement the destructor
    //      Delete any dynamically allocated objects held by the Valley.
}

// TODO:  Implement the accessors
//
//    Robot* otherRobotAt(Robot* rp) const
//      If there is at least one robot (other than the one rp points to)
//      at the same (r,c) coordinates as the one rp points to, return a
//      pointer to the one of those other robots with the least amount
//      of energy remaining (if there's a tie, any one of the tied robots
//      will do); otherwise, return NULL.
//
//    bool energySourceAt(int r, int c) const
//      If there is an energy source at coordinates (r,c), return true;
//      otherwise, return false.
//
//    Fill in the rest of the display function

void Valley::display() const
{
    char grid[NROWS][NCOLS];
    int r, c;

    // fill the grid with dots
    for (r = 0; r < NROWS; r++)
        for (c = 0; c < NCOLS; c++)
            grid[r][c] = '.';

    // TODO:  mark each energy source with a star
    // for each energy source in the valley,
    //   set the appropriate element of the grid to '*'

    // TODO:  indicate each robot's position
    // for each robot in the valley,
    //   set the appropriate element of the grid to the first character
    //          of the robot's name.

    // Clear the screen
    system("cls");  // Under LINUX, try  system("clear");

    // Draw the grid
    for (r = 0; r < NROWS; r++)
    {
        for (c = 0; c < NCOLS; c++)
            cout << grid[r][c];
        cout << endl;
    }
    cout << endl;

    // TODO:  Write robot energy info
    // for each robot in the valley,
    //   write the robot's name and remaining energy level.
}

// TODO:  Implement the mutators

bool Valley::addRobot(string name, int r, int c, Dir dir)
{
    //      TODO: implement addRobot
    //      If MAXROBOTS have already been added, return false.  Otherwise,
    //      dynamically allocate a new robot whose name is name, at coordinates
    //      (r,c) facing in direction dir.  Save the pointer to the newly
    //      allocated robot and return true.  (Hint:  The Valley class could
    //      contain a private array with MAXROBOTS elements.)
    return true;
}

bool Valley::addEnergySource(int r, int c)
{
    //      TODO: implement addEnergySource
    //      If MAXSOURCES have already been added, return false.  Otherwise,
    //      dynamically allocate a new energy source at coordinates (r,c).
    //      Save the pointer to the newly allocated energy source and return true.
    //      (Hint:  The Valley class could contain a private array with MAXSOURCES
    //      elements.)
    return true;
}

bool Valley::step()
{
    //      TODO: implement step
    //      Have each robot in the valley step.  If any of them attempted to move,
    //      return true.  If none of them did, they're all dead, so return false.
    return true; // This is here for now just so this will compile.
}


///////////////////////////////////////////////////////////////////////////
//  main()
///////////////////////////////////////////////////////////////////////////

// You can use whatever main routine you want.  In fact, try different
// things that will thoroughly test your classes.  This main routine is
// the one that the sample executable uses.

int main()
{
    // Initialize the random number generator
    srand(time(0));

    // Create a valley
    Valley v;

    // Populate it with three robots
    v.addRobot("Abner", 0, 0, SOUTH);
    v.addRobot("Betty", 9, 9, NORTH);
    v.addRobot("Chris", 0, 9, SOUTH);

    // Add energy sources at (2,2), (2,5), (2,8), (5,2), ..., (8,8)
    for (int r = 2; r <= 8; r += 3)
        for (int c = 2; c <= 8; c += 3)
            v.addEnergySource(r, c);

    // Step until all robots are dead, displaying the valley each time
    do
    {
        v.display();
        cout << "Press Enter to continue ";
        cin.ignore(100000, '\n');
    } while(v.step());

    cout << "All robots are dead" << endl;
}

And I will post my question / problem (working on one part of the function) in the next post.

I've done the robot accessors, the EnergySource constructor and accessors, and the Valley constructor.

The Valley accessor looks hard, so I'll skip that for now. I'm trying to tackle the

bool Valley::addEnergySource (int r, int c)

now.

I believe I'm getting ideas confused. I'll show you what I have:

the task:

bool Valley::addEnergySource(int r, int c)
{
    //      TODO: implement addEnergySource
    //      If MAXSOURCES have already been added, return false.  Otherwise,
    //      dynamically allocate a new energy source at coordinates (r,c).
    //      Save the pointer to the newly allocated energy source and return true.
    //      (Hint:  The Valley class could contain a private array with MAXSOURCES
    //      elements.)
    return true;
}

Here's my code:

if (MAXSOURCES)
    return false;
else
//    ?  = new Energysource();

Disregard the previous post, I have this now:

bool Valley::addEnergySource(int r, int c)
{
    //      TODO: implement addEnergySource
    //      If MAXSOURCES have already been added, return false.  Otherwise,
    //      dynamically allocate a new energy source at coordinates (r,c).
    //      Save the pointer to the newly allocated energy source and return true.
    //      (Hint:  The Valley class could contain a private array with MAXSOURCES
    //      elements.)
    if (m_nsources == MAXSOURCES)
        return false;
    else
    {
        EnergySource* es = new EnergySource(r,c);
        return true;
    }
}

Anyways, I'm trying to extend this example to another mutator function:

//      TODO: implement addRobot
    //      If MAXROBOTS have already been added, return false.  Otherwise,
    //      dynamically allocate a new robot whose name is name, at coordinates
    //      (r,c) facing in direction dir.  Save the pointer to the newly
    //      allocated robot and return true.  (Hint:  The Valley class could
    //      contain a private array with MAXROBOTS elements.)
bool Valley::addRobot(string name, int r, int c, Dir dir)
{
    if (m_nrobots == MAXROBOTS)
        return false;
    else
    {
        Robot* rob = new Robot(name, r, vp, c, dir);
        return true;
    }
}

I get one compilation error, that vp is not declared (true). If I take it out, it says I can't overload wtih 4 arguments. What's the wrongdoing here?

I get one compilation error, that vp is not declared (true). If I take it out, it says I can't overload wtih 4 arguments. What's the wrongdoing here?

OK, so if this is Robot's only constructor:

Robot(string nm, Valley* vp, int r, int c, Dir d);

And you try to create a Robot instance like so:

Robot* rob = new Robot(name, r, vp, c, dir);

What makes you think that

a) the compiler will let you use a variable that you've never declared before to use a parameter for a function?

b) removing it will make the compiler omit the 3rd argument in Robot::Robot(), Valley, and just randomly play with the code?

You obviously need to pass some sort of Valley instance to Robot::Robot(), or overload the function so that you can initalize the object without a Valley object. If you choose the former, you'll need to either make one yourself, or pass it in to the function as a parameter.

Hope this helps


You obviously need to pass some sort of Valley instance to Robot::Robot(), or overload the function so that you can initalize the object without a Valley object. If you choose the former, you'll need to either make one yourself, or pass it in to the function as a parameter.

This is the part I don't get. I choose to create a new function, Robot::Robot() to pass the Valley stuff.

How do I do that? Do I just use one parameter (Valley* vp) and have an empty body?

This is the part I don't get. I choose to create a new function, Robot::Robot() to pass the Valley stuff.

How do I do that?

There's nothing wrong with Robot::Robot(), the problem you're having is that you're trying to call this function when you don't have a Valley object to pass to it. I haven't read all of your posts, so I don't know exactly where this is supposed to come from, but you either need to pass it as a parameter/create one, or if you want to fill in vp later, then you will need to create an overloaded constructor that lacks the vp parameter.

I tried something like this:

Robot::Robot(string nm, int r, int c, Dir d)
{
}

and I get a compilation error.

I finally got it to work later, when someone pointed out that I could use the this

bool Valley::addRobot(string name, int r, int c, Dir dir)
{
    if (m_nrobots == MAXROBOTS)
        return false;
    else
    {
        m_robots[m_nrobots] = new Robot(name, this, r, c, dir);
        m_nrobots ++;
        return true;
    }
}

I don't understand how that could work though.

I tried something like this:

Robot::Robot(string nm, int r, int c, Dir d)
{
}

and I get a compilation error.

I finally got it to work later, when someone pointed out that I could use the this

bool Valley::addRobot(string name, int r, int c, Dir dir)
{
    if (m_nrobots == MAXROBOTS)
        return false;
    else
    {
        m_robots[m_nrobots] = new Robot(name, this, r, c, dir);
        m_nrobots ++;
        return true;
    }
}

Silly me! I forgot that this function so happened to be inside of Valley. My apologies to you. Disregard my previous posts.

I don't understand how that could work though.

Simple. You obviously want to pass a pointer of the current instance of Valley to Robot::Robot().

How do you do that? The this pointer points to the current instance of Valley that you're working with, so passing it to the function makes it quick and easy.

(This is what I get for not reading entire posts before answering.)

:) Thanks for clarifying this. I'm on the right track. Just a few more tasks I believe.

Next task I tried to do, but can't think of how, is the step function. (This is inside Robot) I think knowing this would help me think of ideas for other functions:

I'm on bool Robot::step().

EDIT:
I fixed some parts of it.

bool Robot::step()
{
    // If the robot has no energy left, return false
    if (m_energy == 0)
        return false;
    // Otherwise,
    else
    {    
        //    Randomly change direction with probability 1/3
        if (rand() % 3 == 0)    // 1/3 probability to pick a direction
            m_dir = Dir(rand() % 4);    // pick a random direction
        
        //    Attempt to move one step in the direction we're currently facing.
        //      (E.g., to move north, decrement the row coordinate.)  If we can't
        //      move in that direction, don't move.
        switch (m_dir)
        {
        case NORTH:
            // TODO: Move one step north, if possible.
            m_row--;
            break;
            // TODO: Implement the other movements
        case SOUTH:
            m_row++;
            break;
        case WEST:
            m_col--;
            break;
        case EAST:
            m_col++;
            break;
        }        
    }

The second part I'm stuck on.

//    The attempt to move consumes one unit of energy.
      //    If as a result of the attempt to move, the robot is at an energy
      //       source, it's recharged to the FULL_ENERGY level.
    m_energy--;
    if (m_sources[m_nsources] = new EnergySource(r,c))
        m_energy = FULL_ENERGY;
    
    //    If at this spot there's another robot whose energy level is 0,
    //       then if we have at least SHARE_THRESHHOLD units of energy,
    //       transfer SHARE_AMOUNT units to that other robot.  (If there
    //       are two or more dead robots here, we donate to only one.)
    //    Return true, indicating the robot attempted to move.
          else if (// another robot's energy is 0)
        if (m_energy > SHARE_THRESHHOLD)
            // that robot's energy = SHARE_AMOUNT

        return true;

}

I don't know what to stick in the parts I // out.

The stuff I did was in blue. Can you tell me if I'm on the right track?

Seems alright. Just to let you know, when you're moving the robot within the switch statement, you never did any bounds checking to make sure that you're not moving the robot out of its range. You should add this to prevent any "Robot created with invalid coordinates" errors.

Perhaps when it hits a "wall" or whatever you want to call the boundaries, you should make the robot reverse directions (or just pick a random one).

Hope this helps

Seems alright. Just to let you know, when you're moving the robot within the switch statement, you never did any bounds checking to make sure that you're not moving the robot out of its range. You should add this to prevent any "Robot created with invalid coordinates" errors.

This was implemented in my Robot constructor. There's no need to do another one in this function, right?

if (r < 0  ||  r >= NROWS  ||  c < 0  ||  c >= NCOLS)
    {
        cout << "***** Robot created with invalid coordinates (" << r << ","
            << c << ")!" << endl;
        exit(1);
    }

Anyways, I had problems with the second part of my previous post. Any help you can provide for that? It's keeping me from progressing!

Another part I tried was the Valley accessor:

bool energySourceAt(int r, int c) const
//      If there is an energy source at coordinates (r,c), return true;
//      otherwise, return false.
{
    if (EnergySource(r,c))
        return true;
    else
        return false;
}

I got compilation errors. What is wrong?

your switch statement to move (n,e,s,w) doesn't check for boundaries, which will cause you to move off the grid.

The constructor only protects your robot from being created out of bounds not from moving out of bounds.

btw, what project is this for? a college engineering project i assume? if so, which college?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.