A kata is an exercise. In martial art is form. In software development is a way to practicing. For example in TDD.
Mars rover kata aims to solve some problem in the context of the mars rover. Here you can find a list of kata. kata-log.rocks/mars-rover-kata Kata are exercises you can use to practicing in tdd, in pair or alone.
Mars rover kata rules are:
- You are given the initial starting point (x,y) of a rover and the direction (N,S,E,W) it is facing.
- The rover receives a character array of commands.
- Implement commands that move the rover forward/backward (f,b).
- Implement commands that turn the rover left/right (l,r).
- Implement wrapping at edges. But be careful, planets are spheres. - Connect the x edge to the other x edge, so (1,1) for x-1 to (5,1), but connect vertical edges towards themselves in inverted coordinates, so (1,1) for y-1 connects to (5,1).
- Implement obstacle detection before each move to a new square. If a given sequence of commands encounters an obstacle, the rover moves up to the last possible point, aborts the sequence and reports the obstacle.
I am playing with mars rover kata and this is the complete result:
package main
import (
"fmt"
)
type Point struct {
x int
y int
}
type Sphere struct {
firstCorner Point
lastCorner Point
obstacles []Point
}
type Rover struct {
position Point
direction string
world Sphere
}
func (r *Rover) moves (moves []string) {
for _, m := range moves {
r.move(m)
}
}
func (r *Rover) move (command string) {
if command == "l" {
if r.direction == "e" { r.direction = "n"; return }
if r.direction == "s" { r.direction = "e"; return }
if r.direction == "w" { r.direction = "s"; return }
if r.direction == "n" { r.direction = "w"; return }
}
if command == "r" {
if r.direction == "n" { r.direction = "e"; return }
if r.direction == "e" { r.direction = "s"; return }
if r.direction == "s" { r.direction = "w"; return }
if r.direction == "w" { r.direction = "n"; return }
}
if command == "f" {
if r.direction == "n" { r.update(r.position.x,r.position.y+1) }
if r.direction == "s" { r.update(r.position.x,r.position.y-1) }
if r.direction == "w" { r.update(r.position.x-1,r.position.y) }
if r.direction == "e" { r.update(r.position.x+1,r.position.y) }
}
if command == "b" {
if r.direction == "n" { r.update(r.position.x,r.position.y-1) }
if r.direction == "s" { r.update(r.position.x,r.position.y+1) }
if r.direction == "w" { r.update(r.position.x+1,r.position.y) }
if r.direction == "e" { r.update(r.position.x-1,r.position.y) }
}
}
func (r *Rover) update (x int, y int) {
if y > r.world.lastCorner.y {
x,y = r.world.lastCorner.y,x
}
if y < r.world.firstCorner.y {
x,y = r.world.lastCorner.y,r.world.lastCorner.x
}
if x < r.world.firstCorner.x {
x,y = r.world.lastCorner.x, r.world.firstCorner.y
}
if x > r.world.lastCorner.x {
x,y = r.world.firstCorner.x, r.world.firstCorner.y
}
isFree := true
destination := Point{x,y}
for _, obstacle := range r.world.obstacles {
if obstacle.x == destination.x && obstacle.y == destination.y {
isFree = false
}
}
if isFree == true {
r.position = destination
}
}
func main() {
fmt.Println("Mars Rover Kata!")
}
I guess there is a way to simplify this code:
func (r *Rover) move (command string) {
if command == "l" {
if r.direction == "e" { r.direction = "n"; return }
if r.direction == "s" { r.direction = "e"; return }
if r.direction == "w" { r.direction = "s"; return }
if r.direction == "n" { r.direction = "w"; return }
}
if command == "r" {
if r.direction == "n" { r.direction = "e"; return }
if r.direction == "e" { r.direction = "s"; return }
if r.direction == "s" { r.direction = "w"; return }
if r.direction == "w" { r.direction = "n"; return }
}
if command == "f" {
if r.direction == "n" { r.update(r.position.x,r.position.y+1) }
if r.direction == "s" { r.update(r.position.x,r.position.y-1) }
if r.direction == "w" { r.update(r.position.x-1,r.position.y) }
if r.direction == "e" { r.update(r.position.x+1,r.position.y) }
}
if command == "b" {
if r.direction == "n" { r.update(r.position.x,r.position.y-1) }
if r.direction == "s" { r.update(r.position.x,r.position.y+1) }
if r.direction == "w" { r.update(r.position.x+1,r.position.y) }
if r.direction == "e" { r.update(r.position.x-1,r.position.y) }
}
}
But I am also afraid to move into unnecessary simplification. I mean: code could be better for computers but worst for humans. And since I am on the side of humans I think this code is good enough. But I also know that go have some idiomatic stuff that can make this code better. Any suggestion?
x
wraps around on a sphere,y
should stay put. The topology defined by the code looks more like a projective plane to me. Correct me if I am wrong. \$\endgroup\$