/**
 * robot.cpp
 * 
 * created: 4-nov-07/sklar (based on parsons' rabbit.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.
 *
 */

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

enum direction {north, east, south, west};
const int WORLD_SIZE = 5;
const int NUM_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
 *
 * a dirt object has a location and reacts when it is vacuumed.
 *
 */
class dirt {
private:
  point location;
  bool  gone;
public:
  dirt() { gone = false; }
  int  getX() const;
  int  getY() const;
  void set(int x, int y);
  void print() const;
  void disappear();
};

int dirt::getX() const{
  return location.getX();
}

int dirt::getY() const{
  return location.getY();
}

void dirt::set(int x, int y){
  location.set(x, y);
}

void dirt::print() const{
  location.print();
}

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



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

/**
 * robot class
 *
 * a robot object that wanders around the world making random moves
 *
 */
class robot {
private:
  point location;
  int   num_vacuumed;
public:
  robot() { num_vacuumed = 0; }
  int  getX() const;
  int  getY() const;
  void set( int x, int y );
  void print() const;
  void move();
  void move( direction d );
  void vacuum();
  bool busy();
};

int robot::getX() const{
  return location.getX();
}

int robot::getY() const{
  return location.getY();
}

void robot::set( int x, int y ){
  location.set( x, y );
}

void robot::print() const{
  location.print();
}

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

// When the robot moves, the world "wraps around", so, for example,
// if the robot is at the east end of the world and moves east, it
// appears at the far west end.
//
// The overloaded function move provides this abilty.
void robot::move( direction d ){
  int x = location.getX();
  int y = location.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 robot to those coordinates
  location.set( x, y );
}


// 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() {
  if ( num_vacuumed < NUM_SPOTS ){
    return true;
  }
  else {
    return false;
  }
}



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

/**
 * world class
 *
 */
class world {
private:
  robot rosie;
  dirt  spots[NUM_SPOTS];
public:
  void  setRobot(int x, int y);
  void  setSpot(int x, int y, int index);
  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::setSpot( int x, int y, int index ) {
  spots[index].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;
  while ( rosie.busy() ) {
    rosie.move();
    rosie.print();
    if (( index = findSpot( rosie.getX(), rosie.getY() )) > 0 ) {
      rosie.vacuum();
      spots[index].disappear();
    }
  }
} // 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 );
  for ( int i=0; i<NUM_SPOTS; i++ ) {
    x = rand() % WORLD_SIZE;
    y = rand() % WORLD_SIZE;
    myworld.setSpot( x, y, i );
  }
  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()
