Tuesday, April 9, 2013

State Design Pattern - Mars Rover Example


The below is the popular problem for state design pattern. I got this problem as part of interview process for a company and i solved the problem and find the code in this post.


Expectation 

 

1. For the solution, we would want you to use either C++ or Java.

2. We are interested in the DESIGN ASPECT of your solution and would like to evaluate your OBJECT ORIENTED PROGRAMMING SKILLS.

3. You may use external libraries or tools for building or testing purposes.

4. Optionally, you may also include a brief explanation of your design and assumptions along with your code.

5. Kindly note that we are NOT expecting a web-based application or a comprehensive UI. Rather, we are expecting a simple, console based application and interested in your source code.

==========

INTRODUCTION TO THE PROBLEM

 

The problem below require some kind of input. You are free to implement

any mechanism for feeding input into your solution (for example, using hard

coded data within a unit test).  You should provide sufficient evidence

that your solution is complete by, as a minimum, indicating that it works

correctly against the supplied test data.

 

 

MARS ROVERS

 

A squad of robotic rovers are to be landed by NASA on a plateau on Mars. This plateau, which is curiously rectangular, must be navigated by the rovers so that their on-board cameras can get a complete view of the surrounding terrain to send back to Earth.

 

A rover's position and location is represented by a combination of x and y co-ordinates and a letter representing one of the four cardinal compass points. The plateau is divided up into a grid to simplify navigation. An example position might be 0, 0, N, which means the rover is in the bottom left corner and facing North.

 

In order to control a rover, NASA sends a simple string of letters. The

possible letters are 'L', 'R' and 'M'. 'L' and 'R' makes the rover spin 90

degrees left or right respectively, without moving from its current spot.

'M' means move forward one grid point, and maintain the same heading.

 

Assume that the square directly North from (x, y) is (x, y+1).

 

INPUT:

The first line of input is the upper-right coordinates of the plateau, the

lower-left coordinates are assumed to be 0,0.

 

The rest of the input is information pertaining to the rovers that have

been deployed. Each rover has two lines of input. The first line gives the rover's position, and the second line is a series of instructions telling the rover how to explore the plateau.

 

The position is made up of two integers and a letter separated by spaces, corresponding to the x and y co-ordinates and the rover's orientation.

 

Each rover will be finished sequentially, which means that the second rover won't start to move until the first one has finished moving.

 

 

OUTPUT

The output for each rover should be its final co-ordinates and heading.

 

INPUT AND OUTPUT

 

Test Input:

5 5

1 2 N

LMLMLMLMM

3 3 E

MMRMMRMRRM

 

Expected Output:

1 3 N

5 1 E



















//
//  RoverHeadingInterface.h
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#ifndef MarsRoverApp_RoverHeadingInterface_h
#define MarsRoverApp_RoverHeadingInterface_h

class RoverHeading
{
    public:
        RoverHeading() {}
        virtual void turnLeft() = 0;
        virtual void turnRight() = 0;
};

#endif





//
//  MoveRoverInterface.h
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//


#ifndef MarsRoverApp_MoveRoverInterface_h
#define MarsRoverApp_MoveRoverInterface_h

#include "RoverHeadingInterface.h"
#include "Position.h"
class Rover;

class MoveRover:public RoverHeading
{
    
protected:
    char heading;
    Rover *rover;
        
public:
    MoveRover(Rover* rover, char heading);
    
    virtual void move() = 0;
    
    char currentHeading();
};

#endif





//
//  MoveRoverInterface.cpp
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#include "MoveRoverInterface.h"


MoveRover::MoveRover(Rover* rover, char heading)
{
    this->rover = rover;
    this->heading = heading;
}


char MoveRover::currentHeading()
{
    return heading;
}

//
//  PlaneBounds.h
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#ifndef __MarsRoverApp__PlaneBounds__
#define __MarsRoverApp__PlaneBounds__

#include <iostream>

class PlaneBounds
{
private:
    int xTerritory, yTerritory;
    
public:
    PlaneBounds(int x, int y);
    bool isSafeToMove(int value);
};

#endif /* defined(__MarsRoverApp__PlaneBounds__) */

//
//  PlaneBounds.cpp
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#include "PlaneBounds.h"

PlaneBounds::PlaneBounds(int x, int y)
{
    xTerritory = x;
    yTerritory = y;
}


bool PlaneBounds::isSafeToMove(int value)
{
    if (0 <= value && value <= xTerritory && value <= yTerritory)
    {
        return true;
    }
    else
    {
        return false;
    }
}





//
//  Position.h
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#ifndef __MarsRoverApp__Position__
#define __MarsRoverApp__Position__

#include <iostream>
#include "PlaneBounds.h"

class Position
{
private:
    int x, y;
    PlaneBounds *territory;
    
public:
    Position(int x, int y, PlaneBounds *territory);
    int getX();
    int getY();
    void incrementY();
    void incrementX();
    void decrementY();
    void decrementX();
};

#endif /* defined(__MarsRoverApp__Position__) */





//
//  Position.cpp
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#include "Position.h"

Position::Position(int x, int y, PlaneBounds *territory)
{
    this->x = x;
    this->y = y;
    this->territory = territory;
}

int Position::getX()
{
    return x;
}

int Position::getY()
{
    return y;
}


void Position::incrementY()
{
    if (territory->isSafeToMove(y+1))
    {
          y++;
    }
    else
    {
        std::cout<<"I cannot move towards NORTH, I reached territory!";
        exit(0);
    }
  
}

void Position::incrementX()
{
    if (territory->isSafeToMove(x+1))
    {
        x++;
    }
    else
    {
        std::cout<<"I cannot move towards EAST, I reached territory!";
        exit(0);
    }
}

void Position::decrementX()
{
    if (territory->isSafeToMove(x-1))
    {
        x--;
    }
    else
    {
        std::cout<<"I cannot move towards WEST, I reached territory!";
        exit(0);
    }
}

void Position::decrementY()
{
    if (territory->isSafeToMove(y-1))
    {
        y--;
    }
    else
    {
        std::cout<<"I cannot move SOUTH, I reached territory!";
        exit(0);
    }
}





//
//  HeadWest.h
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//


#ifndef __MarsRoverApp__HeadWest__
#define __MarsRoverApp__HeadWest__

#include "MoveRoverInterface.h"

#include <iostream>

class HeadWest: public MoveRover
{
public:
    HeadWest(Rover* rover);
    void turnLeft();
    void turnRight();
    void move();
    
};

#endif /* defined(__MarsRoverApp__HeadWest__) */





//
//  HeadWest.cpp
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#include "HeadWest.h"
#include "Rover.h"
#include "HeadSouth.h"
#include "HeadNorth.h"


HeadWest::HeadWest(Rover* rover):MoveRover(rover, 'W')
{
    
}

void HeadWest::turnLeft()
{
    rover->operateRover = new HeadSouth(rover);
}

void HeadWest::turnRight()
{
    rover->operateRover = new HeadNorth(rover);
}

void HeadWest::move()
{
    rover->position->decrementX();
}





//
//  HeadEast.h
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//


#ifndef __MarsRoverApp__HeadEast__
#define __MarsRoverApp__HeadEast__

#include "MoveRoverInterface.h"
#include <iostream>

class HeadEast: public MoveRover
{
    public:
        HeadEast(Rover* rover);
        void turnLeft();
        void turnRight();
        void move();

};


#endif /* defined(__MarsRoverApp__HeadEast__) */





//
//  HeadEast.cpp
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#include "HeadEast.h"
#include "Rover.h"
#include "HeadNorth.h"
#include "HeadSouth.h"

HeadEast::HeadEast(Rover* rover):MoveRover(rover, 'E')
{
    
}

 void HeadEast::turnLeft()
{
    rover->operateRover = new HeadNorth(rover);
}

void HeadEast::turnRight()
{
    rover->operateRover = new HeadSouth(rover);
}

void HeadEast::move()
{
    rover->position->incrementX();
}





//
//  HeadSouth.h
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//


#ifndef __MarsRoverApp__HeadSouth__
#define __MarsRoverApp__HeadSouth__
#include "MoveRoverInterface.h"

#include <iostream>

class HeadSouth: public MoveRover
{
    public:
        HeadSouth(Rover* rover);
        void turnLeft();
        void turnRight();
        void move();
};

#endif /* defined(__MarsRoverApp__HeadSouth__) */





//
//  HeadSouth.cpp
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#include "HeadSouth.h"
#include "Position.h"
#include "HeadEast.h"
#include "HeadWest.h"
#include "Rover.h"


HeadSouth::HeadSouth(Rover* rover):MoveRover(rover, 'S')
{
    
}

void HeadSouth::turnLeft()
{
    rover->operateRover = new HeadEast(rover);
}

void HeadSouth::turnRight()
{
    rover->operateRover = new HeadWest(rover);
}

void HeadSouth::move()
{
    rover->position->decrementY();
}





//
//  HeadNorth.h
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//
#include "MoveRoverInterface.h"

#ifndef __MarsRoverApp__HeadNorth__
#define __MarsRoverApp__HeadNorth__

#include <iostream>

class HeadNorth: public MoveRover
{
    public:
        HeadNorth(Rover* rover);
        void turnLeft();
        void turnRight();
        void move();
    
};

#endif /* defined(__MarsRoverApp__HeadNorth__) */





//
//  HeadNorth.cpp
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#include "HeadNorth.h"
#include "Rover.h"
#include "HeadWest.h"
#include "HeadEast.h"

HeadNorth::HeadNorth(Rover* rover):MoveRover(rover, 'N')
{
    
}

void HeadNorth::turnLeft()
{
    rover->operateRover = new HeadWest(rover);
}

void HeadNorth::turnRight()
{
    rover->operateRover = new HeadEast(rover);
}

void HeadNorth::move()
{
    rover->position->incrementY();
}





//
//  Rover.h
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//


#ifndef __MarsRoverApp__Rover__
#define __MarsRoverApp__Rover__

#include <string.h>
#include <iostream>
#include "MoveRoverInterface.h"

class Rover
{
    
    public: Position *position;
    public: MoveRover *operateRover;

    public:

        Rover(int x, int y, char heading, PlaneBounds *territory);
        void sendCommand(const char *commandString);
    
        void currentPosition();
};

#endif /* defined(__MarsRoverApp__Rover__) */





//
//  Rover.cpp
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#include "Rover.h"
#include "HeadEast.h"
#include "HeadNorth.h"
#include "HeadSouth.h"
#include "HeadWest.h"

Rover::Rover(int x, int y, char heading, PlaneBounds *territory)
{
    position = new Position(x, y, territory);
    
    switch (heading) {
        case 'W':
            operateRover = new HeadWest(this);
            break;
            
        case 'S':
            operateRover = new HeadSouth(this);
            break;

        case 'N':
            operateRover = new HeadNorth(this);
            break;

        case 'E':
            operateRover = new HeadEast(this);
            break;
    }
    
}

void Rover::currentPosition()
{
    std::cout<<position->getX()<<" "<<position->getY()<<" "<<operateRover->currentHeading()<<"\n";
}

void Rover::sendCommand(const char *commandString)
{
    size_t commandLength = strlen(commandString);
    
    for (int index = 0; index < commandLength; index++) {
        
        switch (commandString[index]) {
            case 'M':
                operateRover->move();
                break;
                
            case 'L':
                operateRover->turnLeft();
                break;
                
            case 'R':
                operateRover->turnRight();
                break;

                
            default:
                std::cout<<commandString[index]<<" "<<"is an unknown command!";
                break;
        }
    }
}





//
//  main.cpp
//  MarsRoverApp
//
//  Created by Stalin S on 4/2/13.
//  Copyright (c) 2013 Stalin S. All rights reserved.
//

#include <iostream>
#include "Rover.h"

int main(int argc, const char * argv[])
{
    PlaneBounds *territory = new PlaneBounds(5, 5);
    Rover *rover = new Rover(1, 2, 'N', territory);
    rover->currentPosition();
    rover->sendCommand("LMLMLMLMM");
    rover->currentPosition();
    std::cout<<"\n\n\n";
    
    Rover *rover1 = new Rover(3, 3, 'E', territory);
    rover1->currentPosition();
    rover1->sendCommand("MMRMMRMRRM");
    rover1->currentPosition();
    std::cout<<"\n\n\n";

    
    Rover *rover2 = new Rover(1, 2, 'N', territory);
    rover2->currentPosition();
    rover2->sendCommand("LMLMLMLMMMMM");
    rover2->currentPosition();
    std::cout<<"\n\n\n";

    

}






Output:





1 2 N
1 3 N



3 3 E
5 1 E



1 2 N
I cannot move towards NORTH, I reached territory!

No comments: