 4d47's solution

to Robot Simulator in the PHP Track

Published at Jul 13 2018 · 0 comments
Instructions
Test suite
Solution

Note:

This solution was written on an old version of Exercism. The tests below might not correspond to the solution code, and the exercise may have changed since this code was written.

Write a robot simulator.

A robot factory's test facility needs a program to verify robot movements.

The robots have three possible movements:

• turn right
• turn left

Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of {x,y} coordinates, e.g., {3,8}, with coordinates increasing to the north and east.

The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing.

• The letter-string "RAALAL" means:
• Turn right
• Turn left
• Turn left yet again
• Say a robot starts at {7, 3} facing north. Then running this stream of instructions should leave it at {9, 4} facing west.

Running the tests

1. Go to the root of your PHP exercise directory, which is `<EXERCISM_WORKSPACE>/php`. To find the Exercism workspace run

`````` % exercism debug | grep Workspace
``````
2. Get PHPUnit if you don't have it already.

`````` % wget --no-check-certificate https://phar.phpunit.de/phpunit.phar
% chmod +x phpunit.phar
``````
3. Execute the tests:

`````` % ./phpunit.phar robot-simulator/robot-simulator_test.php
``````

Source

Inspired by an interview question at a famous company.

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

robot-simulator_test.php

``````<?php

include_once 'robot-simulator.php';

class RobotSimulatorTest extends PHPUnit\Framework\TestCase
{

/**
* A robot is created with a position and a direction
*/
public function testCreate()
{
// Robots are created with a position and direction
\$robot = new Robot([0, 0], Robot::DIRECTION_NORTH);
\$this->assertEquals([0, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_NORTH, \$robot->direction);

// Negative positions are allowed
\$robot = new Robot([-1, -1], Robot::DIRECTION_SOUTH);
\$this->assertEquals([-1, -1], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_SOUTH, \$robot->direction);
}

/**
* Rotate the robot's direction 90 degrees clockwise
*/
public function testTurnRight()
{
\$robot = new Robot([0, 0], Robot::DIRECTION_NORTH);

// Change the direction from north to east
\$robot->turnRight();
\$this->assertEquals([0, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_EAST, \$robot->direction);

// Change the direction from east to south
\$robot->turnRight();
\$this->assertEquals([0, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_SOUTH, \$robot->direction);

// Change the direction from south to west
\$robot->turnRight();
\$this->assertEquals([0, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_WEST, \$robot->direction);

// Change the direction from west to north
\$robot->turnRight();
\$this->assertEquals([0, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_NORTH, \$robot->direction);
}

/**
* Rotate the robot's direction 90 degrees counterclockwise
*/
public function testTurnLeft()
{
\$robot = new Robot([0, 0], Robot::DIRECTION_NORTH);

// Change the direction from north to west
\$robot->turnLeft();
\$this->assertEquals([0, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_WEST, \$robot->direction);

// Change the direction from west to south
\$robot->turnLeft();
\$this->assertEquals([0, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_SOUTH, \$robot->direction);

// Change the direction from south to east
\$robot->turnLeft();
\$this->assertEquals([0, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_EAST, \$robot->direction);

// Change the direction from east to north
\$robot->turnLeft();
\$this->assertEquals([0, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_NORTH, \$robot->direction);
}

/**
* Move the robot forward 1 space in the direction it is pointing
*/
{
// Increases the y coordinate by one when facing north
\$robot = new Robot([0, 0], Robot::DIRECTION_NORTH);
\$this->assertEquals([0, 1], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_NORTH, \$robot->direction);

// Decreases the y coordinate by one when facing south
\$robot = new Robot([0, 0], Robot::DIRECTION_SOUTH);
\$this->assertEquals([0, -1], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_SOUTH, \$robot->direction);

// Increases the x coordinate by one when facing east
\$robot = new Robot([0, 0], Robot::DIRECTION_EAST);
\$this->assertEquals([1, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_EAST, \$robot->direction);

// Decreases the x coordinate by one when facing west
\$robot = new Robot([0, 0], Robot::DIRECTION_WEST);
\$this->assertEquals([-1, 0], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_WEST, \$robot->direction);
}

/**
* Where R = Turn Right, L = Turn Left and A = Advance,
* the robot can follow a series of instructions
* and end up with the correct position and direction
*/
public function testInstructions()
{
// Instructions to move west and north
\$robot = new Robot([0, 0], Robot::DIRECTION_NORTH);
\$robot->instructions('LAAARALA');
\$this->assertEquals([-4, 1], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_WEST, \$robot->direction);

// Instructions to move west and south
\$robot = new Robot([2, -7], Robot::DIRECTION_EAST);
\$robot->instructions('RRAAAAALA');
\$this->assertEquals([-3, -8], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_SOUTH, \$robot->direction);

// Instructions to move east and north
\$robot = new Robot([8, 4], Robot::DIRECTION_SOUTH);
\$robot->instructions('LAAARRRALLLL');
\$this->assertEquals([11, 5], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_NORTH, \$robot->direction);
}

/**
* @expectedException InvalidArgumentException
*/
public function testMalformedInstructions()
{
\$robot = new Robot([0, 0], Robot::DIRECTION_NORTH);
\$robot->instructions('LARX');
}

/**
* Optional instructions chaining
*/
public function testInstructionsChaining()
{
\$robot = new Robot([0, 0], Robot::DIRECTION_NORTH);
\$robot->turnLeft()
->turnRight()
->turnLeft()
\$this->assertEquals([-4, 1], \$robot->position);
\$this->assertEquals(Robot::DIRECTION_WEST, \$robot->direction);
}
}``````
``````<?php

class Robot {
const
DIRECTION_NORTH = [  0,  1 ],
DIRECTION_EAST  = [  1,  0 ],
DIRECTION_SOUTH = [  0, -1 ],
DIRECTION_WEST  = [ -1,  0 ];

public
\$position,
\$direction;

public function __construct(array \$position, array \$direction) {
assert(count(\$position) == 2);
\$this->position = \$position;
\$this->direction = \$direction;
}

public function turnRight(): Robot {
[\$x, \$y] = \$this->direction;
\$this->direction = [\$y, -\$x];
return \$this;
}

public function turnLeft(): Robot {
[\$x, \$y] = \$this->direction;
\$this->direction = [-\$y, \$x];
return \$this;
}

\$this->position += \$this->direction;
\$this->position += \$this->direction;
return \$this;
}

public function instructions(string \$steps): Robot {
if (!preg_match('/^[LRA]*\$/', \$steps)) {
throw new InvalidArgumentException('Instruction set must only contains L, R or A.');
}
for (\$i = 0; \$i < strlen(\$steps); \$i++) {
switch (\$steps[\$i]) {
case 'L': \$this->turnLeft(); break;
case 'R': \$this->turnRight(); break;
}
}
return \$this;
}
}``````