exercism / go

Exercism exercises in Go.
https://exercism.org/tracks/go
MIT License
969 stars 649 forks source link

robot-simulator: code && instructions is not matching #1159

Open exklamationmark opened 6 years ago

exklamationmark commented 6 years ago

Hi,

I'm was trying to do the robot-simulator exercise. However, I found what was written in the README && what's in the test are very different.

While I appreciate a more complex & closer-to-life simulation, which was presented in the test, it was not very clear what to do from the instructions alone.

What do you think about updating the exercise, such that:

P.S: I also don't fully agree with some of the types && test defined, so I made some changes

hilary commented 6 years ago

I agree we should fix the problem of the description not matching well with the tests, but I'd like to think this through a bit before we launch into it.

How about if we make this problem a multi-step problem, changing the robot types as we go? I think it's fine to have them solve the easier problem first, then move on to the more difficult.

However... a fundamental tenant of exercism is that we try to avoid imposing any specific solution on the learner. I was concerned that the original version of the problem imposed too much structure. Your version imposes even more!

Can you come up with a way (try thinking in interfaces rather than in data structures) to test without imposing a solution? I'll take a thwack at it too 😈

exklamationmark commented 6 years ago

hi @hilary, thanks for taking a stab at this.

I think it'd be interesting to make this a multi-step problem. Below is my take on a more proper exercise. Feel free to add your critiques.

High-level ideas

Implement a simulation involving a bunch of named robots, a central controller and a room with obstacles (think Amazon warehouse). The controller operates in an environment with obstacles (some static, some dynamic, plus the robots are obstacle to other robots). It receives a command (e.g: robot A: advance, robot B: turn left, robot B: advance, etc) and try to execute them.

We design the controller as a deterministic process: given an initial state and a sequence of commands, there is only one outcome. Our test will verify this.

Levels of difficulty is added by adding complexity into the situation.

Steps in the exercise

Assuming the environment can be modelled as a grid and each robot occupy one single cell. We can progress like this:

  1. Implement the system with the controller + a single robot (the original problem). We should be able to initialize this robot with a position & direction, then issue advance / turn left / turn right commands. Start by assuming there is no environmental boundary and no obstacles.
  2. Still with that single robot, add environmental boundary && static obstacles. Advance commands can fail now.
  3. Add multiple robots. Robots are also dynamic obstacles that move around.

Code and interfaces

This part is tricky to define as an exercism exercise. If I do this IRL, I would probably define the types used to model the system (e.g: Position, Direction, Command, etc), plus what interfaces are exposed to an external user.

Here, I made a decision not to allow individual robots to be controlled and to force all commands through a central controller. It'd be nice to see more suggestions on how to make this more flexible to students.

What I would define

type Position {
    X, Y int
}

type Direction int
const (
    North Direction = iota
    East
    South
    East
)

type RobotCommand int
const (
    Advance RobotCommand = iota
    TurnLeft
    TurnRight
)

type Command struct {
    RobotName string
    Cmd RobotCommand
}

// RobotManager handles one or more robots
type RobotManager interface {
    // AddRobot add a single named robot. Adding might fail if the position is occupied.
    AddRobot(name string, pos Position, direction Direction) error
    // QueryLocation returns the location & direction of a named robot.
    QueryLocation(name string) (Position, Direction)
    // Execute try to perform an single command. Asking a robot to move out-of-bound or run into an obstacle should fail.
    Execute(cmd Command) error 
}

// RoomManager controls robots in a room with obstacle(s). 
type RoomManager interface {
    RobotManager

    SetBoundary(bottomLeft, topRight Position)
    AddStaticObstacle(pos Position) error // can't add on top of another obstacle
}

func NewRobotManager() RobotManager {
    // TBD
}

func NewRoomManager() RoomManager {
   // TBD
}

tests will probably be like this

func TestStage1(t *testing.T) {
    mng := NewRobotManager()

    // mng.AddRobot( ... )

    // mng.Execute( ... )
    // mng.Execute( ... )

    // pos, direction := QueryLocation ( ... )
    // assert
}

func TestStage2(t *testing.T) {
    mng := NewRoomManager()

    // mng.AddRobot( ... )

    // mng.SetBoundary ( ... )
    // mng.AddStaticObstacle ( ... )

    // mng.Execute( ... )
    // mng.Execute( ... )

    // pos, direction := QueryLocation ( ... )
    // assert
}

func TestStage3(t *testing.T) {
    mng := NewRoomManager()

    // mng.AddRobot( "alice" )
    // mng.AddRobot( "bob" )
    // mng.AddRobot( "eve" )

    // mng.SetBoundary ( ... )
    // mng.AddStaticObstacle ( ... )

    // mng.Execute( "alice", ... )
    // mng.Execute( "bob", ... )
    // mng.Execute( "alice", ... )
    // mng.Execute( "eve", ... )

    // pos, direction := QueryLocation ( "alice", ... )
    // assert
    // pos, direction := QueryLocation ( "bob", ... )
    // assert
    // pos, direction := QueryLocation ( "eve", ... )
    // assert
}

It's worth nothing that adding concurrency to this problem will be pretty hard, as the final state depends on some kind of execution order. Thus I left it out (compared to the current test).

wdyt? 😛

junedev commented 2 years ago

Short note: This issue still exists and should be addressed.