/**
 * robot3.cpp
 * 
 * created: 4-nov-07/sklar (based on robot2.cpp)
 *
 * a sample simulation environment in which a robot looks for dirt to vacuum.
 * the robots' world contains dirt and one robot. the robot moves around
 * randomly until it finds some dirt and vacuums it.
 *
 * differences from robot2.cpp: demonstrates function overriding by
 * introduction of the "creature" class and different versions for
 * subclasses "robot" and "cat"
 *
 */

#include <stdlib.h>
#include <time.h>
#include <iostream>
using namespace std;

enum direction {north, east, south, west};
const int WORLD_SIZE = 5;
const int MAX_SPOTS  = 3;



/*---------------------------------------------------------------------------*/
/**
 * point class
 *
 * this class should look familiar by now: it has a 2-D (x,y) coordinate
 * and functions that support setting, reporting and printing it.
 *
 */

class point {
private:
  int x, y;
public:
  int  getX() const;
  int  getY() const;
  void set( int x, int y );
  void print() const;
};

int point::getX() const {
  return x;
}

int point::getY() const {
  return y;
}

void point::set( int x, int y ) {
  this->x = x;
  this->y = y;
}

void point::print() const {
  cout << "(" << x << ", " << y << ")  ";
}


/*---------------------------------------------------------------------------*/
/**
 * dirt class
 *
 * dirt is a kind of point that reacts when it is vacuumed
 *
 */
class dirt : public point {
private:
  bool gone;
public:
  dirt() { gone = false; }
  void disappear();
};

void dirt::disappear(){
  cout << "poof!" << endl;
  gone = true;
}


/*---------------------------------------------------------------------------*/
/**
 * creature class
 *
 * a creature is a kind of point that moves around
 *
 */

class creature : public point {
public:
  void move();
  void move( direction d );
};

// pick a random direction to move in, and then move one unit in that direction
void creature::move(){
  direction d;
  d = static_cast<direction>( rand() % 4 );
  move( d );
}

// move in a specific direction, wrapping around if necessary.
// the overloaded function move provides this abilty.
void creature::move( direction d ){
  int x = getX();
  int y = getY();
  // find a new x and y coordinate
  switch( d ) {
  case north:
    y = (y + 1) % WORLD_SIZE;
    break;
  case south:
    y = (y - 1);
    if ( y < 0 ) {
      y = WORLD_SIZE;
    }
    break;
  case east:
    x = (x + 1) % WORLD_SIZE;
    break;
  case west:
    x = (x - 1) % WORLD_SIZE;
    if ( x < 0 ) {
      x = WORLD_SIZE;
    }
    break;
  }
  // set the location of the creature to those coordinates
  set( x, y );
}


/*---------------------------------------------------------------------------*/
/**
 * robot class
 *
 * a robot is a kind of creature that vacuums up dirt spots
 *
 */

class robot : public creature {
private:
  int num_vacuumed;
public:
  robot() { num_vacuumed = 0; }
  void vacuum();
  bool busy( int num_spots );
};

// vacuum up some dirt
void robot::vacuum(){
  cout << "shrooooop...";
  num_vacuumed++;
}

// the robot is busy until all the dirt has been found and vacuumed
bool robot::busy( int num_spots ) {
  if ( num_vacuumed < num_spots ){
    return true;
  }
  else {
    return false;
  }
}


/*---------------------------------------------------------------------------*/
/**
 * cat class
 *
 * a cat is a kind of creature
 *
 */

class cat : public creature {
public:
  void move();
  void move( direction d );
};

void cat::move(){
  direction d;
  d = static_cast<direction>(rand() % 4);
  move(d);
}

// overload the creature's move so cat can pounce (move 2 steps in any direction)
void cat::move( direction d ){
  int x = getX();
  int y = getY();
  // find a new x and y coordinate
  switch( d ) {
  case north:
    y = (y + 2) % WORLD_SIZE;
    break;
  case south:
    y = (y - 2);
    if ( y < 0 ) {
      y = WORLD_SIZE;
    }
    break;
  case east:
    x = (x + 2) % WORLD_SIZE;
    break;
  case west:
    x = (x - 2) % WORLD_SIZE;
    if ( x < 0 ) {
      x = WORLD_SIZE;
    }
    break;
  }
  // set the location of the cat to those coordinates
  set( x, y );
}



/*---------------------------------------------------------------------------*/
/**
 * world class
 *
 */
class world {
private:
  robot rosie;
  dirt  spots[MAX_SPOTS];
  int   num_spots;
  cat   felix;
public:
  world() { num_spots = 0; }
  void  setRobot( int x, int y );
  void  setCat( int x, int y );
  void  setSpot( int x, int y );
  int   findSpot( int x, int y ) const;
  void  robotRoam();
  void  print() const;  
};


void world::setRobot( int x, int y ) {
  rosie.set( x, y );
}

void world::setCat( int x, int y ) {
  felix.set( x, y );
}

void world::setSpot( int x, int y ) {
  if ( num_spots+1 < MAX_SPOTS ) {
    spots[num_spots++].set( x,  y);
  }
}


int world::findSpot( int x, int y ) const{
  bool found = false;
  int  index = 0;
  while (( ! found ) && ( index < num_spots )) {
    if (( x == spots[index].getX() ) && 
        ( y == spots[index].getY() )){
      found = true;
    }
    index++;
  }
  if ( found ) {
    return( index );
  }
  else {
    return( -1 );
  }
} // end of findSpot()


// make the robot roam around. if it finds a spot, it vacuums it up.
// it does this as long as it is "busy", i.e., as long as there are spots
// in the world that need to be cleaned.
void world::robotRoam() {
  int index;
  felix.move();
  setSpot( felix.getX(), felix.getY() );
  while ( rosie.busy( num_spots ) ) {
    rosie.move();
    rosie.print();
    if (( index = findSpot( rosie.getX(), rosie.getY() )) > 0 ) {
      rosie.vacuum();
      spots[index].disappear();
    }
    felix.move();
    setSpot( felix.getX(), felix.getY() );
  }
} // end of robotRoam()


// printing the world is printing the location of the objects in the
// world. we use their print methods to do this.
void world::print() const {
  cout << "\nRosie is at: ";
  rosie.print();
  cout << "\nThe spots are at: " << endl;
  for( int i=0; i<num_spots; i++ ) {
    spots[i].print();
  }
} // end of print()



/*---------------------------------------------------------------------------*/

/**
 * main function
 *
 */
int main() {
  world myworld;
  int x, y;

  // initialize random number generator
  srand( time( NULL ));

  // place the robot and the spots in random locations in the world
  x = rand() % WORLD_SIZE;
  y = rand() % WORLD_SIZE;
  myworld.setRobot( x, y );
  x = rand() % WORLD_SIZE;
  y = rand() % WORLD_SIZE;
  myworld.setCat( x, y );
  myworld.print();

  // let the robot roam around until it finds and cleans all the spots
  cout << "\nRobot roaming..." << endl;
  myworld.robotRoam();
  cout << "\nMy, what a clean world we live in :-)\n\n";


} // end of main()


