//## begin module%1.4%.codegen_version preserve=yes
//   Read the documentation to learn more about C++ code generator
//   versioning.
//## end module%1.4%.codegen_version

//## begin module%3D86D3B103DE.cm preserve=no
//	  %X% %Q% %Z% %W%
//## end module%3D86D3B103DE.cm

//## begin module%3D86D3B103DE.cp preserve=no
//## end module%3D86D3B103DE.cp

//## Module: Pusher%3D86D3B103DE; Pseudo Package body
//## Source file: C:\Program Files\Rational\Rose\C++\source\Pusher.cpp

//## begin module%3D86D3B103DE.additionalIncludes preserve=no
//## end module%3D86D3B103DE.additionalIncludes

//## begin module%3D86D3B103DE.includes preserve=yes
//#include <stdlib.h>
#include <math.h>
#include <stdlib.h>
#include "Timer.h"
#include "Commander.h"
//## end module%3D86D3B103DE.includes

// Pusher
#include "Pusher.h"
//## begin module%3D86D3B103DE.additionalDeclarations preserve=yes
//## end module%3D86D3B103DE.additionalDeclarations


// Class Pusher

//## begin Pusher::ARRIVED%3D9ABEE301CC.attr preserve=no  public: static int {UC} 0
const int Pusher::ARRIVED = 0;
//## end Pusher::ARRIVED%3D9ABEE301CC.attr

//## begin Pusher::CUBE_LOST%3D9ABEFC030C.attr preserve=no  public: static int {UC} 1
const int Pusher::CUBE_LOST = 1;
//## end Pusher::CUBE_LOST%3D9ABEFC030C.attr

//## begin Pusher::DRIVE_STALLED%3D9ABEFD00A0.attr preserve=no  public: static int {UC} 2
const int Pusher::DRIVE_STALLED = 2;
//## end Pusher::DRIVE_STALLED%3D9ABEFD00A0.attr

//## begin Pusher::PUSH_CUBE_MET_ROBOT%3D9FC42501D6.attr preserve=no  private: static int {UC} 10
const int Pusher::PUSH_CUBE_MET_ROBOT = 10;
//## end Pusher::PUSH_CUBE_MET_ROBOT%3D9FC42501D6.attr

//## begin Pusher::PUSH_CUBE_CUBE_LOST%3D9FC4450370.attr preserve=no  private: static int {UC} 11
const int Pusher::PUSH_CUBE_CUBE_LOST = 11;
//## end Pusher::PUSH_CUBE_CUBE_LOST%3D9FC4450370.attr

//## begin Pusher::PUSH_CUBE_ARRIVED%3D9FC4460096.attr preserve=no  private: static int {UC} 12
const int Pusher::PUSH_CUBE_ARRIVED = 12;
//## end Pusher::PUSH_CUBE_ARRIVED%3D9FC4460096.attr

Pusher::Pusher()
  //## begin Pusher::Pusher%3D86D3B103DE_const.hasinit preserve=no
      : minimumWaitingPeriod(0),
        maximumWaitingPeriod(1000)
  //## end Pusher::Pusher%3D86D3B103DE_const.hasinit
  //## begin Pusher::Pusher%3D86D3B103DE_const.initialization preserve=yes
  //## end Pusher::Pusher%3D86D3B103DE_const.initialization
{
  //## begin Pusher::Pusher%3D86D3B103DE_const.body preserve=yes
  //## end Pusher::Pusher%3D86D3B103DE_const.body
}



//## Other Operations (implementation)
int Pusher::activate ()
{
  //## begin Pusher::activate%3D9291D102E4.body preserve=yes
  drive->stop(); //deactivate drive commands from other behaviors

  int pushResult;
  bool pushJustAvoided=false;
  do
  {
    int approachResult;
    bool approachJustAvoided=false;
    do
    {
      LCDPrintf("approach\n");
      //KEYWait(KEY3);
      approachResult = approachCube();
      if (approachResult == APPROACH_CUBE_MET_ROBOT)
      {
        LCDPrintf("approach avoid\n");
        //KEYWait(KEY3);
        if (!approachJustAvoided)
        {
          bool robotAvoided = avoidRobot();
          approachJustAvoided = true;
          if (robotAvoided)
          {
            LCDPrintf("a avoid check\n");
            //KEYWait(KEY3);
            bool cubeRetrieved = checkForCube(false); //no need to back up before checking for cube, avoidRobot already backed up
            if (!cubeRetrieved)
              return CUBE_LOST;
          }
          else
            return DRIVE_STALLED;
        }
        else
          return DRIVE_STALLED; //we give up
      }
      else if (approachResult == APPROACH_CUBE_CUBE_LOST)
      {
        LCDPrintf("approach check\n");
        //KEYWait(KEY3);
        bool cubeRetrieved = checkForCube(true); //back up before checking for cube
        if (!cubeRetrieved)
          return CUBE_LOST;
      }
      else if (approachResult == APPROACH_CUBE_NEW_CLUSTER_POINT_CHOSEN)
      {
        LCDPrintf("approach chosen\n");
        //KEYWait(KEY3);
        return ARRIVED;
      }
    }
    while (approachResult != APPROACH_CUBE_ARRIVED);

    LCDPrintf("push\n");
    //KEYWait(KEY3);
    pushResult = pushCube();
    if (pushResult == PUSH_CUBE_MET_ROBOT)
    {
      LCDPrintf("push avoid\n");
      //KEYWait(KEY3);
      if (!pushJustAvoided)
      {
        bool robotAvoided = avoidRobot();
        pushJustAvoided = true;
        if (robotAvoided)
        {
          LCDPrintf("p avoid check\n");
          //KEYWait(KEY3);
          bool cubeRetrieved = checkForCube(false); //no need to back up before checking for cube, avoidRobot already backed up
          if (!cubeRetrieved)
            return CUBE_LOST;
        }
        else
          return DRIVE_STALLED;
      }
      else
        return DRIVE_STALLED; //we give up
    }
    else if (pushResult == PUSH_CUBE_CUBE_LOST)
    {
      LCDPrintf("push check\n");
      //KEYWait(KEY3);
      bool cubeRetrieved = checkForCube(true); //back up before checking for cube
      if (!cubeRetrieved)
        return CUBE_LOST;
    }
  }
  while (pushResult != PUSH_CUBE_ARRIVED);
  LCDPrintf("exit pusher\n");
  //KEYWait(KEY3);

  //back up to re-measure our home cluster density and re-set the robot's home cluster point position
  drive->drive(-0.15);
  if (!drive->commandCompleted())
    return DRIVE_STALLED;

  chooseClusterPoint(true); //true means: we enforce a re-setting of the cluster point position. This is necessary to ensure a re-measurement of our home cluster point density and position

  return ARRIVED;
  //## end Pusher::activate%3D9291D102E4.body
}

int Pusher::approachCube ()
{
  //## begin Pusher::approachCube%3D996568037A.body preserve=yes

  double x, y, phi;
  double cubeX, cubeY;

  myCommander->doSleep(75);
  drive->getPosition(x, y, phi);
  //no need to call sir->cubeDetected() here
  //Explorer.activate and Pusher.activate ensure that there is a valid cube position
  //when entering this method
  sir->getLocalCubePosition(cubeX,cubeY);
  //calculate how much we have to turn to look straight at the cube
  //from our current position x, y, phi
  double rotationAngleToCube = drive->getRotationAngle(0, 0, cubeX, cubeY, 0); //as we use local cube coordinates (i.e. cubeX, cubeY are relative to the current robot position) it would be wrong to pass x, y, phi. We have to pass 0, 0, 0 instead

  drive->turn(rotationAngleToCube);
  if (!drive->commandCompleted())
    return APPROACH_CUBE_MET_ROBOT;

  //give SIR time to re-estimate cube position
  myCommander->doSleep(75);
  //re-read robot position
  drive->getPosition(x, y, phi);
  //re-read the cube position, maybe better data is available now
  if (sir->cubeDetected())
    sir->getLocalCubePosition(cubeX,cubeY);
  else
    return APPROACH_CUBE_CUBE_LOST;
  //re-calculate rotation angle to cube
  rotationAngleToCube = drive->getRotationAngle(0, 0, cubeX, cubeY, 0); //as we use local cube coordinates (i.e. cubeX, cubeY are relative to the current robot position) it would be wrong to pass x, y, phi. We have to pass 0, 0, 0 instead.

  drive->turn(rotationAngleToCube);
  if (!drive->commandCompleted())
    return APPROACH_CUBE_MET_ROBOT;

  //give SIR time to re-estimate cube position
  myCommander->doSleep(75);
  //re-read robot position
  drive->getPosition(x, y, phi);
  //re-read the cube position, maybe better data is available now
  if (sir->cubeDetected())
    sir->getLocalCubePosition(cubeX,cubeY);
  else
    return APPROACH_CUBE_CUBE_LOST;
  //re-calculate rotation angle to cube
  rotationAngleToCube = drive->getRotationAngle(0, 0, cubeX, cubeY, 0); //as we use local cube coordinates (i.e. cubeX, cubeY are relative to the current robot position) it would be wrong to pass x, y, phi. We have to pass 0, 0, 0 instead.


  double distanceToCube = sqrt(cubeX*cubeX+cubeY*cubeY);

  //we get reliable measurements between 0.15 and 0.30 meters
  //so we should have something like:
  //if (distanceToCube >= 0.30 || distanceToCube <= 0.18) //cube is far away or too close, we move to the right distance before starting the real approach
  //but as we have to estimate the local cube density we always go to
  //the same distance
  //we can use the looser limits when using communication and no density
  //estimation is necessary
  if (distanceToCube >= 0.20 || distanceToCube <= 0.18) //cube is far away or too close, we move to the right distance before starting the real approach
  {
    LCDPutString("too far/close\n");
    drive->turn(rotationAngleToCube);
    if (!drive->commandCompleted())
      return APPROACH_CUBE_MET_ROBOT;

    myCommander->doSleep(10); //seems to be necessary??

    drive->drive(distanceToCube - 0.19);
    if (!drive->commandCompleted())
      return APPROACH_CUBE_MET_ROBOT;

    //give SIR time to re-estimate cube position
    myCommander->doSleep(75);
    //re-read robot position
    drive->getPosition(x, y, phi);
    //re-read the cube position, maybe better data is available now
    if (sir->cubeDetected())
      sir->getLocalCubePosition(cubeX,cubeY);
    else
      return APPROACH_CUBE_CUBE_LOST;
    //re-calculate rotation angle to cube
    rotationAngleToCube = drive->getRotationAngle(0, 0, cubeX, cubeY, 0); //as we use local cube coordinates (i.e. cubeX, cubeY are relative to the current robot position) it would be wrong to pass x, y, phi. We have to pass 0, 0, 0 instead.

  }

  if (chooseClusterPoint(false))
  {
    return APPROACH_CUBE_NEW_CLUSTER_POINT_CHOSEN;
  }

  //after the turn our the robot's phi will be
  double phiAfterTurnToCube = phi + rotationAngleToCube;


  //do we need to avoid cluster point when approaching?
  //No, either the cube is between us and cluster point
  //or we are moving away from cluster point

  //start approach
  //if our distance to the ball is smaller than our distance to (0,0)
  //and if the angle between the line (robot-cube) and the line
  //(cube-cluster point) is smaller then 60 degrees
  //then we can start pushing right away - because then the cube lies in front
  //of us and the cluster point is in front of the cube

  double cubeGlobalX;
  double cubeGlobalY;
  drive->local2global(x,y,phi,cubeX,cubeY,cubeGlobalX,cubeGlobalY);
  double angleRobotCubeClusterPoint = drive->getRotationAngle(cubeGlobalX, cubeGlobalY, 0.0, 0.0, phiAfterTurnToCube);

  distanceToCube = sqrt(cubeX*cubeX+cubeY*cubeY); //is that necessary???
  if ((distanceToCube<drive->distanceTo(0.0,0.0)) && (fabs(angleRobotCubeClusterPoint)<60.0))
  {
    LCDPrintf("direct approach\n");
  }
  else //cube is in front of us, but cluster point is behind us. We need to get behind the cube from the other side
  {
      int droveAroundCube = 0;
      double spacingToCube = 0.15; //put elsewhere???

      drive->getPosition(x,y,phi);
      do
      {

        drive->turn(rotationAngleToCube); //turn to cube
        if (!drive->commandCompleted())
          return APPROACH_CUBE_MET_ROBOT;

        myCommander->doSleep(10); //seems to be necessary??

        distanceToCube = sqrt(cubeX*cubeX+cubeY*cubeY);
        drive->drive(distanceToCube - spacingToCube); //drive close to cube
        if (!drive->commandCompleted())
          return APPROACH_CUBE_MET_ROBOT;

        LCDPrintf("near cube\n");

        drive->getPosition(x,y,phi);
        phiAfterTurnToCube = phi;
        //now we are close to the cube
        //what we are planning to do is to drive around the cube in a circle
        //until we have a good angle to push the cube to the cluster point

        bool turnedLeft;
        if (sir->getLeftMargin() >= sir->getRightMargin()) //if more free space to the left we drive around the left side, else we drive around the right side
        {
          drive->turnLeft(90);
          turnedLeft = true;
        }
        else
        {
          drive->turnRight(90);
          turnedLeft = false;
        }
        if (!drive->commandCompleted())
          return APPROACH_CUBE_MET_ROBOT;

        //if we have turned left, we drive around the left side until we have a good angle
        //to push the cube to the cluster point
        //so we drive a circle around the cube with radius spacingToCube
        //how far do we drive on the circle:
        //we drive around the left side. We know the cluster point is in our back.
        //so we calculate at which angle we could push the cube straight to the cluster point
        //this calculated angle is adjusted by the phi we had after we've driven to the cube. The turnRight,
        //turnLeft by 90 degrees that we performed afterwards need to be not considered as we will turn back later after the circle
        //so we drive on the circle until we can push the cube to the cluster point in a straight line
        drive->local2global(x,y,phi,cubeX,cubeY,cubeGlobalX,cubeGlobalY);
        double angleOnCircle = drive->getRotationAngle(cubeGlobalX, cubeGlobalY, 0.0, 0.0, phiAfterTurnToCube);
        if (turnedLeft)
        {
          while (angleOnCircle > 0)
            angleOnCircle -= 360;
          drive->driveCircle(spacingToCube + 0.05, angleOnCircle + 0); //we add 0.05 for the robot width
          while (!drive->done() && !drive->stalled() && !sir->obstacleFront() && !sir->obstacleLeft())
            myCommander->doReschedule();

        }
        else
        {
          while (angleOnCircle < 0)
            angleOnCircle += 360;
          drive->driveCircle(spacingToCube + 0.05, angleOnCircle - 0); //we add 0.05 for the robot width
          while (!drive->done() && !drive->stalled() && !sir->obstacleFront() && !sir->obstacleRight())
            myCommander->doReschedule();
        }
        //1. if the drive is stalled we can't go on and have to avoid the obstacle first
        //2. Otherwise we exited because the drive command has/is completed, so we can go on
        if (drive->stalled())
        {
          drive->stop(); //drive stalled, deactivate any drive command
          return APPROACH_CUBE_MET_ROBOT; //could also be wall, makes no difference, though. Need to avoid anyway
        }

        drive->stop(); //seems to be necessary as the robot drives a curve otherwise
        //Attention: drive->stop may not be called before 'if (drive->stalled())...' as drive->stop
        //resets the stalled information!!!

        myCommander->doSleep(10); //seems to be necessary???

        if (turnedLeft)
        {
          drive->turnRight(85); //turn back only by 85 degrees as we seem to get better results with that
        }
        else
        {
          drive->turnLeft(85); //turn back only by 85 degrees as we seem to get better results with that
        }

        if (!drive->commandCompleted())
          return APPROACH_CUBE_MET_ROBOT;

        //give SIR time to re-estimate cube position
        myCommander->doSleep(75);
        //re-read robot position
        drive->getPosition(x, y, phi);
        //re-read the cube position, maybe better data is available now
        if (sir->cubeDetected())
          sir->getLocalCubePosition(cubeX,cubeY);
        else
          return APPROACH_CUBE_CUBE_LOST;
        //re-calculate rotation angle to cube
        rotationAngleToCube = drive->getRotationAngle(0, 0, cubeX, cubeY, 0); //as we use local cube coordinates (i.e. cubeX, cubeY are relative to the current robot position) it would be wrong to pass x, y, phi. We have to pass 0, 0, 0 instead.


        //if we turned left and now the left margin is below a certain value,
        //probably we have a wall on the left side
        //we don't make the final turn to the cube
        //otherwise we increase the probability of colliding with the wall
        //instead we even turn a bit away from the wall
        //same for the right side
        //store value somewhere else???
        if ((turnedLeft && sir->getLeftMargin()<0.1) || (!turnedLeft && sir->getRightMargin()<0.1))
        {
          if (turnedLeft)
          {
            drive->turnRight(10);
            if (!drive->commandCompleted())
              return APPROACH_CUBE_MET_ROBOT;
          }
          else
          {
            drive->turnLeft(10);
            if (!drive->commandCompleted())
              return APPROACH_CUBE_MET_ROBOT;
          }
        }
        else
        {
          drive->turn(rotationAngleToCube); //final turn to cube
          if (!drive->commandCompleted())
            return APPROACH_CUBE_MET_ROBOT;
        }


        drive->getPosition(x,y,phi); //update position for while loop

        droveAroundCube++;
    }
    while ((droveAroundCube<2) && (fabs(drive->getRotationAngle(x, y, 0.0, 0.0, phi)) > 90.0)); //keep driving around cube if cluster point is not in front of us and we haven't tried the other side yet (if (droveAroundCube==2) we've already tried both sides, so we just start pushing)

    distanceToCube = spacingToCube; //for final approach to cube
  }


  drive->drive(distanceToCube); //drive close to cube
  if (!drive->commandCompleted())
    return APPROACH_CUBE_MET_ROBOT;

  LCDPrintf("arrived\n");

  return APPROACH_CUBE_ARRIVED;
  //## end Pusher::approachCube%3D996568037A.body
}

bool Pusher::avoidRobot ()
{
  //## begin Pusher::avoidRobot%3D99659201D6.body preserve=yes
  double backupDistance = -0.2; //enough??

  drive->drive(backupDistance);
  if (!drive->commandCompleted())
  {
    //return false; //return false if drive stalled? No, maybe other robot can complete avoidance
  }
  
  //int seed = psds->...???

  double r = 0.1;

  /*srand48(seed); //crashes???
  double r = drand48(); //crashes???
  //r is between [0,1]*/

  double x = (r * maximumWaitingPeriod);
  // x is a random double value in the range [0,maximumWaitingPeriod]

	int y = (int) x;
  // y is a random integer in the range [0,maximumWaitingPeriod]

	int z = y + minimumWaitingPeriod;
  // z is a random integer in the range [minimumWaitingPeriod,maximumWaitingPeriod+minimumWaitingPeriod]

  /*double r = ((double)rand() / (double)(RAND_MAX+1)); //better use drand48()
  // r is a random double value in the range [0,1) {including 0, not
  // including 1}. Note we must convert rand() and/or RAND_MAX+1 to floating
  // point values to avoid integer division

  double x = (r * maximumWaitingPeriod);
  // x is a random double value in the range [0,maximumWaitingPeriod) {including 0, not
  // including maximumWaitingPeriod)

	int y = (int) x;
  // y is a random integer in the range [0, floor(maximumWaitingPeriod)]. If
  // maximumWaitingPeriod is an integer then the range is [0,maximumWaitingPeriod-1] {inclusive} //does int really always round down??

	int z = y + minimumWaitingPeriod;
  // z is a random integer in the range [minimumWaitingPeriod,floor(maximumWaitingPeriod)+minimumWaitingPeriod). If
  // maximumWaitingPeriod is an integer then the range is [minimumWaitingPeriod,maximumWaitingPeriod-1+minimumWaitingPeriod] {inclusive}*/

  myCommander->doSleep(z);

	return true;
  //## end Pusher::avoidRobot%3D99659201D6.body
}

bool Pusher::checkForCube (bool backup)
{
  //## begin Pusher::checkForCube%3D99657E01A4.body preserve=yes
  drive->stop();

  if (backup)
  {
    double backupDistance = -0.2;
    drive->drive(backupDistance); //go back a bit, to make cube right in front of us (if there is one) visible on camera
    if (!drive->commandCompleted())
      return false; //can't turn anyway
  }

  myCommander->doSleep(75); //give SIR time to re-detect cube
  if (sir->cubeDetected())
    return true;

  drive->turnLeft(30);
  if (!drive->commandCompleted())
    return false;
  myCommander->doSleep(75); //give SIR time to re-detect cube
  if (sir->cubeDetected())
    return true;

  drive->turnRight(60);
  if (!drive->commandCompleted())
    return false;
  myCommander->doSleep(75); //give SIR time to re-detect cube
  if (sir->cubeDetected())
    return true;

  return false;
  //## end Pusher::checkForCube%3D99657E01A4.body
}

int Pusher::pushCube ()
{
  //## begin Pusher::pushCube%3D996594000A.body preserve=yes
  LCDPrintf("pushing cube\n");

  double x,y,phi;
  drive->getPosition(x,y,phi);

  drive->driveCurveTo(clusterRadius/4*cos(phi),clusterRadius/4*sin(phi)); // cube is in front of us, we push it to (0,0), adjusted by cluster radius
  //keep driving while
  //we have the cube, are not there yet, a drive command is active
  //the drive is not blocking and there is no robot in front of us
  bool cubeLost = false;
  int cubeLostCounter = 0; //maybe it's better to keep track of the time instead of using a cubeLostCounter, that would be more process independent
  while (!cubeLost && !drive->done() && !drive->stalled() && drive->distanceTo(0.0,0.0)>clusterRadius)
  {
    if (!sir->obstacleFront())
      cubeLostCounter++;
    else
      cubeLostCounter = 0;
    if (cubeLostCounter > 25)
      cubeLost = true;
    myCommander->doReschedule();
  }

  //the order of checking has the following reasoning
  //1. if we have arrived, i.e. drive->distanceTo(0,0) <= clusterRadius, we can exit
  //the pushing behavior, no matter what else has happened
  //2. if the drive is stalled we can't go on and have to avoid the obstacle first
  //3. if we have lost the cube, we also can exit
  //4. We exited because the drive command has/is completed, that means we are at (0,0). We should never enter this branch, though, as reason 1 should then be fulfilled, too
  if (drive->distanceTo(0,0) <= clusterRadius)
  {
    LCDPrintf("arrived\n");
    drive->stop(); //close enough, deactivate any drive command
    return PUSH_CUBE_ARRIVED;
  }
  else if (drive->stalled())
  {
    LCDPrintf("obstacle\n");
    drive->stop(); //drive stalled, deactivate any drive command
    return PUSH_CUBE_MET_ROBOT;
  }
  else if (cubeLost)
  {
    LCDPrintf("lost cube\n");
    drive->stop(); //lost cube, deactivate any drive command
    return PUSH_CUBE_CUBE_LOST;
  }
  else
    return PUSH_CUBE_ARRIVED;
  //## end Pusher::pushCube%3D996594000A.body
}

// Additional Declarations
  //## begin Pusher%3D86D3B103DE.declarations preserve=yes
  const int Pusher::APPROACH_CUBE_MET_ROBOT = 20;
  const int Pusher::APPROACH_CUBE_CUBE_LOST = 21;
  const int Pusher::APPROACH_CUBE_ARRIVED = 22;
  const int Pusher::APPROACH_CUBE_NEW_CLUSTER_POINT_CHOSEN = 23;

  bool Pusher::chooseClusterPoint (bool forceChoiceOfClusterPoint)
  {
      double density, clusterPointX, clusterPointY;
      double estimatedTotalDensity = 0.0;
      double bestSingleDensity = 0.0;
      double bestClusterPointX=0.0;
      double bestClusterPointY=0.0;
      double bestClusterPointPhi=0.0;

      sir->getDensity(density, clusterPointX, clusterPointY);
      estimatedTotalDensity += density; //we add up all densities, gives a better estimation of the cluster density
      if (density>bestSingleDensity)
      {
        bestSingleDensity=density;
        bestClusterPointX=clusterPointX;
        bestClusterPointY=clusterPointY;
        bestClusterPointPhi=0.0; //our phi at the end will be like now, so if we get the best results with the current orientation we are already looking into the right direction and have to pass 0.0
      }

      drive->turnLeft(20);
      if (!drive->commandCompleted())
        return false;

      sir->getDensity(density, clusterPointX, clusterPointY);
      estimatedTotalDensity += density; //we add up all densities, gives a better estimation of the cluster density
      if (density>bestSingleDensity)
      {
        bestSingleDensity=density;
        bestClusterPointX=clusterPointX;
        bestClusterPointY=clusterPointY;
        bestClusterPointPhi=20.0; //our phi at the end will be 20 degrees to the right, so if we get the best results with the current orientation we have to pass -20
      }

      drive->turnRight(40);
      if (!drive->commandCompleted())
        return false;

      sir->getDensity(density, clusterPointX, clusterPointY);
      estimatedTotalDensity += density; //we add up all densities, gives a better estimation of the cluster density
      if (density>bestSingleDensity)
      {
        bestSingleDensity=density;
        bestClusterPointX=clusterPointX;
        bestClusterPointY=clusterPointY;
        bestClusterPointPhi=-20.0; //our phi at the end will be 20 degrees to the left, so if we get the best results with the current orientation we have to pass +20
      }

      //turn back to old position, otherwise the calling methods might be confused by the sudden change of robot's position
      drive->turnLeft(20);
      if (!drive->commandCompleted())
        return false;

      //the factor determines by how much the density of the just examined cluster point
      //has to be higher than that of our own cluster point until we switch
      //our cluster points
      //density goes from 0.0 to 3.0
      //if the density of our current cluster point is below 0.16
      //we only switch if the new cluster point has at least double the density
      //if the density of our current cluster point is (e.g.) 0.5 or greater
      //we only switch if the new cluster point has at least a density of
      //(9.5+1/0.5)/10.5 = 1.09.... That is the new cluster point's density has to be at least
      //about 9% higher
      //if the density of our current cluster point is already 3.0
      //then the factor is 3.0, that is we switch the cluster point if the new one
      //has a density of 3.0 as well. Why do we switch, although both cluster points
      //have 3.0? It might be that the new one is indeed larger.
      //Stochastic processes may eventually solve this conflict, but not if every robot sticks
      //to its old cluster point
      double factor = 2.0;
      if (myClusterPointDensity>=0.16)
        factor = (9.5 + 1.0/myClusterPointDensity) / 10.5;
      else
        factor = 2.0;

      if (estimatedTotalDensity != 0.0) //no need to change cluster point if estimatedTotalDensity == 0
      {
        if (estimatedTotalDensity >= factor * myClusterPointDensity || forceChoiceOfClusterPoint)
        {
          LCDPrintf("LARGER!\n");
          LCDPrintf("d old %f\n", myClusterPointDensity);
          LCDPrintf("d new %f\n", estimatedTotalDensity);
          LCDPrintf("x %f\n", -bestClusterPointX);
          LCDPrintf("y %f\n", -bestClusterPointY);
          //KEYWait(KEY3);

          LCDPrintf("y %f\n", (2.0*bestClusterPointY)); //happens only in the beginning?
          //second output is 0.0
          if ((bestClusterPointY == 2.0) || (bestClusterPointY == -2.0)) //to trace a bug
            /*KEYWait(KEY3)*/;
          //another bug: once a robot pushed the first cube to its starting position (two meters away)
          //probably because this method returned false because the drive stalled

          myClusterPointDensity = estimatedTotalDensity;
          drive->setPosition(-bestClusterPointX,-bestClusterPointY,-bestClusterPointPhi);
          return true; //new cluster point chosen
        }
      }

      LCDPrintf("SMALLER!\n");
      LCDPrintf("d %f\n", myClusterPointDensity);
      LCDPrintf("best try %f\n", estimatedTotalDensity);
//      KEYWait(KEY3);

      return false; //no new cluster point chosen
  }


  //## end Pusher%3D86D3B103DE.declarations

//## begin module%3D86D3B103DE.epilog preserve=yes
//## end module%3D86D3B103DE.epilog
