/*
* Programmer: San Yeung
* File: hw6.cpp
* Purpose: this file contains the main function for the program
* that simulate Hans random walk search for his pet
* penguin.
* Date: 10/3/2017
*/
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;
// Global constants
const int SIM_NUM = 500; // The number of simulation (walk)
const int MAX_NUM_TURN = 500; // The maximum turns allowed in a walk
const int SEED = 2; // The random seed
const int HOME_X = 0;
const int HOME_Y = 0;
const int HANS_INIT_DIR = 90;
const int HANS_STEP_MIN = 50;
const int HANS_STEP_MAX = 100;
const int PENG_INIT_X = 200;
const int PENG_INIT_Y = 200;
const int PENG_INIT_DIR = 180;
const int PENG_STEP_MIN = 20;
const int PENG_STEP_MAX = 50;
const int COP_POST_X = 75;
const int COP_POST_Y = 0;
const int DONUT_SHOP_X = 0;
const int DONUT_SHOP_Y = 75;
const int COP_MOVES_PATTERN = 10;
const int PENG_CAPTURE_RANGE = 175; // Steps
const int COP_NOTICE_RANGE = 5; // Steps
const int RETURN_HOME_RANGE = 10; // Steps
const int Y_TO_HOME_LOWER_PROB = 0;
const int Y_TO_HOME_UPPER_PROB = 39; // 40% (0-39) for the biased y-direction
const int X_TO_HOME_LOWER_PROB = 40;
const int X_TO_HOME_UPPER_PROB = 79; // 40% (40-79) for the biased x-direction
const int Y_AWAY_HOME_LOWER_PROB = 80;
const int Y_AWAY_HOME_UPPER_PROB = 89; // 10% for the y-direction away from home
const int X_AWAY_HOME_LOWER_PROB = 90;
const int X_AWAY_HOME_UPPER_PROB = 99; // 10% for the x-direction away from home
// Function prototypes
// Desc: Begin the simulation of Hans's search for his penguin
// Pre: None
// Post: A complete simulation run will output the stat results for each case
void runSimulation();
// Desc: Hans movement
// Pre: Direction (dir) value should be in the range of 0-360
// Post: Hans's position and direction will be updated
void moveHans(int & x, int & y, int & dir);
// Desc: Penguin movement
// Pre: Direction (dir) value should be in the range of 0-360
// Post: The penguin's position and direction will be updated
void movePenguin(int & x, int & y, int & dir);
// Desc: The cop only moves to the donut shop for every ten moves of the other
// characters, and moves back to his post on the next turn.
// Pre: None
// Post: The cop's position will be updated if numMoves match the desired
// moving pattern.
void moveCop(int & x, int & y, const int numMoves);
// Desc: Hans heads home with preferred probability on the directions that are
// closer to home.
// Pre: Direction (dir) value should be in the range of 0-360
// Post: Hans's position and direction will be updated
void moveHansHome(int & x, int & y, int & dir);
// Desc: Check if the distance between two objects is within a threshold
// Pre: None
// Post: Return true if the distance between two objects is within the distance
// threshold, else false.
bool checkDistance(const int d1_x, const int d1_y,
const int d2_x, const int d2_y, const int dist_thres);
// Desc: Update the x and y positions of a character in the simulation
// Pre: The number of movement (steps) should be positive and the direction
// range (dir) should be within 0-360
// Post: The position of a character will be updated.
void updatePos(int & x, int & y, const int dir, const int steps);
// Desc: Update the direction randomly (either left or right)
// Pre: The current direction (dir) should be within 0-360
// Post: dir will be updated to the random new direction
void updateDir(int & dir);
// Main function
int main() {
runSimulation();
return 0;
}
// Function definitions
void runSimulation()
{
// Variable declarations
// Character position and direction
int hans_x, hans_y, hans_dir;
int peng_x, peng_y, peng_dir;
int cop_x, cop_y;
// Outcome status for each scenario
bool hansMetPeng = false, hansMetCop = false, copMetPeng = false;
bool hansHomeWithPeng = false;
bool hansInJail = false;
bool terminateCurrentWalk = false;
// Outcome stat counters
int hansReturnHomeCount = 0;
int hansExhaustionCount = 0;
int hansGotFreeRideCount = 0;
int hansGotFreeRideFromCopAndPengCount = 0;
int hansInJailCount = 0;
srand(SEED);
for(int i = 0; i < SIM_NUM; i++)
{
// Variable initialization
hans_x = HOME_X;
hans_y = HOME_Y;
hans_dir = HANS_INIT_DIR;
peng_x = PENG_INIT_X;
peng_y = PENG_INIT_Y;
peng_dir = PENG_INIT_DIR;
cop_x = COP_POST_X;
cop_y = COP_POST_Y;
hansMetPeng = false;
hansMetCop = false;
copMetPeng = false;
hansHomeWithPeng = false;
hansInJail = false;
terminateCurrentWalk = false;
int j = 1;
do
{
if(!hansMetPeng && !hansMetCop && !copMetPeng){
// Everyone is walking peacefully
moveHans(hans_x, hans_y, hans_dir);
movePenguin(peng_x, peng_y, peng_dir);
moveCop(cop_x, cop_y, j);
// Check character distances among each other
hansMetPeng = checkDistance(hans_x, hans_y, peng_x, peng_y,
PENG_CAPTURE_RANGE);
hansMetCop = checkDistance(hans_x, hans_y, cop_x, cop_y,
COP_NOTICE_RANGE);
copMetPeng = checkDistance(cop_x, cop_y, peng_x, peng_y,
PENG_CAPTURE_RANGE);
}
else if(hansMetPeng)
{
// Hans head home with his penguin
moveHansHome(hans_x, hans_y, hans_dir);
moveCop(cop_x, cop_y, j);
// Check character distances among each other
hansHomeWithPeng = checkDistance(hans_x, hans_y, HOME_X, HOME_Y,
RETURN_HOME_RANGE);
hansMetCop = checkDistance(hans_x, hans_y, cop_x, cop_y,
COP_NOTICE_RANGE);
if(hansHomeWithPeng)
{
terminateCurrentWalk = true;
hansReturnHomeCount++;
}
else if(hansMetCop)
{
hansGotFreeRideCount++;
hansHomeWithPeng = true;
terminateCurrentWalk = true;
}
}
else if(hansMetCop)
{
// Hans goes to jail and dies
terminateCurrentWalk = true;
hansInJail = true;
hansInJailCount++;
}
else if(copMetPeng)
{
// The cop and the penguin hangout together, only Hans moves
moveHans(hans_x, hans_y, hans_dir);
// Check character distances among each other
hansMetPeng = checkDistance(hans_x, hans_y, peng_x, peng_y,
PENG_CAPTURE_RANGE);
if(hansMetPeng)
{
hansGotFreeRideFromCopAndPengCount++;
terminateCurrentWalk = true;
hansHomeWithPeng = true;
}
}
j++;
}while(!terminateCurrentWalk && (j <= MAX_NUM_TURN));
if(!hansHomeWithPeng && !hansInJail)
hansExhaustionCount++;
}
cout<<"% Hans returns home with his penguin: "
<<hansReturnHomeCount*100.0/SIM_NUM<<endl;
cout<<"% Hans and his penguin got a free ride from the cop: "
<<hansGotFreeRideCount*100.0/SIM_NUM<<endl;
cout<<"# Hans got a free ride while the cop hangs with the penguin: "
<<hansGotFreeRideFromCopAndPengCount*100.0/SIM_NUM<<endl;
cout<<"% Hans thrown in jail and dies: "
<<hansInJailCount*100.0/SIM_NUM<<endl;
cout<<"% Hans exhausted and dies: "
<<hansExhaustionCount*100.0/SIM_NUM<<endl;
return;
}
void moveHans(int & x, int & y, int & dir)
{
// Variable declarations
static bool firstMove = true;
int randSteps;
// Get random steps
randSteps = (rand()%(HANS_STEP_MAX-HANS_STEP_MIN+1))+HANS_STEP_MIN;
if(firstMove){
// First movement, no need to update direction
updatePos(x, y, dir, randSteps);
firstMove = false;
}
else{
// Update direction
updateDir(dir);
updatePos(x, y, dir, randSteps);
}
return;
}
void movePenguin(int & x, int & y, int & dir)
{
// Variable declarations
static bool firstMove = true;
int randSteps;
// Get random steps
randSteps = (rand()%(PENG_STEP_MAX-PENG_STEP_MIN+1))+PENG_STEP_MIN;
if(firstMove){
// First movement, no need to update direction
updatePos(x, y, dir, randSteps);
firstMove = false;
}
else{
// Update direction
updateDir(dir);
updatePos(x, y, dir, randSteps);
}
return;
}
void moveCop(int & x, int & y, const int numMoves)
{
static bool backToPost = false;
if(!backToPost && (numMoves % COP_MOVES_PATTERN == 0))
{
x = DONUT_SHOP_X;
y = DONUT_SHOP_Y;
backToPost = true; // Cop should move back to post the next turn
}
else if(backToPost)
{
x = COP_POST_X;
y = COP_POST_Y;
backToPost = false;
}
return;
}
void moveHansHome(int & x, int & y, int & dir)
{
// Variable declarations
int randSteps;
int randDirProb; // random direction with probability
// Get random steps
randSteps = (rand()%(HANS_STEP_MAX-HANS_STEP_MIN+1))+HANS_STEP_MIN;
// Update direction
randDirProb = rand() % 100;
// Update the direction
if(randDirProb >= Y_TO_HOME_LOWER_PROB &&
randDirProb <= Y_TO_HOME_UPPER_PROB){ // Move towards home
if(y > 0) // 1st and 2nd quadrant
dir = 270; // go south
else if (y < 0) // 3rd and 4th quadrant
dir = 90; // go north
}
else if (randDirProb >= X_TO_HOME_LOWER_PROB &&
randDirProb <= X_TO_HOME_UPPER_PROB)
{
if(x > 0) // 1st and 4th quadrant
dir = 180; // go west
else if (x < 0) // 2nd and 3rd quadrant
dir = 0; // go east
}
else if (randDirProb >= Y_AWAY_HOME_LOWER_PROB &&
randDirProb <= Y_AWAY_HOME_UPPER_PROB)
{
if(y > 0) // 1st and 2nd quadrant
dir = 90; // go north
else if (y < 0) // 3rd and 4th quadrant
dir = 270; // go south
}
else if(randDirProb >= X_AWAY_HOME_LOWER_PROB &&
randDirProb <= X_AWAY_HOME_UPPER_PROB)
{
if(x > 0) // 1st and 4th quadrant
dir = 0; // go east
else if (x < 0) // 2nd and 3rd quadrant
dir = 180; // go west
}
else // Erroneous probability obtained
{
cout<<"Erroneous probability obtained when Hans is going home!"<<endl;
exit(1);
}
// Update position
updatePos(x, y, dir, randSteps);
return;
}
bool checkDistance(const int d1_x, const int d1_y,
const int d2_x, const int d2_y, const int dist_thres)
{
double distance = sqrt( ((d2_x-d1_x)*(d2_x-d1_x)) +
((d2_y-d1_y)*(d2_y-d1_y)) );
return (distance <= dist_thres? true: false);
}
void updatePos(int& x, int& y, const int dir, const int steps){
switch(dir)
{
case 0: // Positive x
x += steps;
break;
case 90: // Positive y
y += steps;
break;
case 180: // Negative x
x -= steps;
break;
case 270: // Negative y
y -= steps;
break;
default:
cout<<"Invalid direction encountered!"<<endl;
exit(1);
break;
}
return;
}
void updateDir(int & dir)
{
// Variable Declaration
int randDir;
// Choose the random direction
randDir = (rand()%2 == 1? 1: 0); // 1 for left and 0 for right
// Update the direction
if(randDir){
dir += 90;
if(dir>=360)
dir = dir%360;
}
else{
dir -= 90;
if(dir<0)
dir = dir+360;
}
return;
}