Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor robot-simulator exercise #461

Merged
merged 3 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions exercises/practice/robot-simulator/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# ignore

## Properties and fields in Groovy

In this exercise, you might have noticed that the stub defines getters, like `getX()`, but the tests access the value directly as `.x`. This is due to how Groovy defines properties. Let’s break it down:

- A **field** is a member of a class, interface, or trait used to **store data**.
- A **property** is an **externally visible** feature of a class.

Key Points about Groovy Properties:
- Groovy automatically generates getters and setters for non-private fields (or only a getter if the field is marked as `final`).
- Properties can be accessed by name (as in this exercise's test) and will call the getter or setter transparently.
- By convention, Groovy will recognize properties even if there is no backing field, provided there are getters or setters.

In this exercise, we’ve only specified the required getters (and hence properties as per above) to enforce proper implementation. You're free to decide which fields (and their types) are most suitable for your solution.

If you want to explore the topic of fields and properties further, please refer to [Chapter 3.3. Fields and Properties](https://www.groovy-lang.org/objectorientation.html#_fields_and_properties) in the Groovy documentation.
Original file line number Diff line number Diff line change
@@ -1,48 +1,69 @@
class RobotSimulator {
int pos_x
int pos_y
String direction
private int pos_x
private int pos_y
private String direction

static allowed_directions = ['north', 'east', 'south', 'west']

/**
* Returns the current position of the robot relative to the X axis
*/
int getX() {
return pos_x
}

/**
* Returns the current position of the robot relative to the Y axis
*/
int getY() {
return pos_y
}

/**
* Returns the direction that the robot is currently facing
*/
String getDirection() {
return direction
}

RobotSimulator(int pos_x, int pos_y, String direction) {
this.pos_x = pos_x;
this.pos_y = pos_y;
this.direction = direction;
this.pos_x = pos_x
this.pos_y = pos_y
this.direction = direction
}

def move(String commands) {
commands.each {
switch(it) {
switch (it) {
case "L":
def i = allowed_directions.indexOf(direction)
direction = allowed_directions[i - 1]
break;
break
case "R":
def i = allowed_directions.indexOf(direction)
i = (i + 1) % 4
direction = allowed_directions[i]
break;
break
case "A":
switch(direction) {
switch (direction) {
case 'north':
pos_y += 1
break;
break
case 'east':
pos_x += 1
break;
break
case 'south':
pos_y -= 1
break;
break
case 'west':
pos_x -= 1
break;
break
default:
break;
break
}
break;
break
default:
break;
break
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,27 @@ class RobotSimulator {
throw new UnsupportedOperationException('Constructor implementation is missing')
}

/**
* Returns the current position of the robot relative to the X axis
*/
int getX() {
throw new UnsupportedOperationException('getX implementation is missing')
}

/**
* Returns the current position of the robot relative to the Y axis
*/
int getY() {
throw new UnsupportedOperationException('getY implementation is missing')
}

/**
* Returns the direction that the robot is currently facing
*/
String getDirection() {
throw new UnsupportedOperationException('getDirection implementation is missing')
}

def move(String commands) {
throw new UnsupportedOperationException('Move implementation is missing')
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,85 +1,110 @@
import spock.lang.*
import spock.lang.Ignore
import spock.lang.Specification
import spock.lang.Unroll

class RobotSimulatorSpec extends Specification {

@Unroll
def "Create robot at origin #label"() {
expect:
RobotSimulator results = new RobotSimulator(x, y, direction)
RobotSimulator expected = new RobotSimulator(x, y, direction)
results.properties == expected.properties
when:
def results = new RobotSimulator(x, y, direction)

then:
results.x == x
results.y == y
results.direction == direction

where:
label || x || y || direction
"at origin facing north" || 0 || 0 || "north"
"at negative position facing south" || -1 || -1 || "south"
label || x | y | direction
"at origin facing north" || 0 | 0 | "north"
"at negative position facing south" || -1 | -1 | "south"
}

@Ignore
@Unroll
def "Rotating clockwise #label"() {
expect:
RobotSimulator results = new RobotSimulator(x1, y1, direction1)
given:
def results = new RobotSimulator(x1, y1, direction1)

when:
results.move("R")
RobotSimulator expected = new RobotSimulator(x2, x2, direction2)
results.properties == expected.properties

then:
results.x == x2
results.y == y2
results.direction == direction2

where:
label || x1 || y1 || direction1 || x2 || y2 || direction2
"changes north to east" || 0 || 0 || "north" || 0 || 0 || "east"
"changes east to south" || 0 || 0 || "east" || 0 || 0 || "south"
"changes south to west" || 0 || 0 || "south" || 0 || 0 || "west"
"changes west to north" || 0 || 0 || "west" || 0 || 0 || "north"
label || x1 | y1 | direction1 || x2 | y2 | direction2
"changes north to east" || 0 | 0 | "north" || 0 | 0 | "east"
"changes east to south" || 0 | 0 | "east" || 0 | 0 | "south"
"changes south to west" || 0 | 0 | "south" || 0 | 0 | "west"
"changes west to north" || 0 | 0 | "west" || 0 | 0 | "north"
}

@Ignore
@Unroll
def "Rotating counter-clockwise #label"() {
expect:
RobotSimulator results = new RobotSimulator(x1, y1, direction1)
given:
def results = new RobotSimulator(x1, y1, direction1)

when:
results.move("L")
RobotSimulator expected = new RobotSimulator(x2, x2, direction2)
results.properties == expected.properties

then:
results.x == x2
results.y == y2
results.direction == direction2

where:
label || x1 || y1 || direction1 || x2 || y2 || direction2
"changes north to west" || 0 || 0 || "north" || 0 || 0 || "west"
"changes west to south" || 0 || 0 || "west" || 0 || 0 || "south"
"changes south to east" || 0 || 0 || "south" || 0 || 0 || "east"
"changes east to north" || 0 || 0 || "east" || 0 || 0 || "north"
label || x1 | y1 | direction1 || x2 | y2 | direction2
"changes north to west" || 0 | 0 | "north" || 0 | 0 | "west"
"changes west to south" || 0 | 0 | "west" || 0 | 0 | "south"
"changes south to east" || 0 | 0 | "south" || 0 | 0 | "east"
"changes east to north" || 0 | 0 | "east" || 0 | 0 | "north"
}

@Unroll
@Ignore
def "Moving forward one #label"() {
expect:
RobotSimulator results = new RobotSimulator(x1, y1, direction)
given:
def results = new RobotSimulator(x1, y1, direction)

when:
results.move("A")
RobotSimulator expected = new RobotSimulator(x2, y2, direction)
results.properties == expected.properties

then:
results.x == x2
results.y == y2
results.direction == direction

where:
label || x1 || y1 || direction || x2 || y2
"facing north increments Y" || 0 || 0 || "north" || 0 || 1
"facing south decrements Y" || 0 || 0 || "south" || 0 || -1
"facing east increments X" || 0 || 0 || "east" || 1 || 0
"facing west decrements X" || 0 || 0 || "west" || -1 || 0
label || x1 | y1 | direction || x2 | y2
"facing north increments Y" || 0 | 0 | "north" || 0 | 1
"facing south decrements Y" || 0 | 0 | "south" || 0 | -1
"facing east increments X" || 0 | 0 | "east" || 1 | 0
"facing west decrements X" || 0 | 0 | "west" || -1 | 0
}

@Ignore
@Unroll
def "Follow series of instructions #label"() {
expect:
RobotSimulator results = new RobotSimulator(x1, y1, direction1)
given:
def results = new RobotSimulator(x1, y1, direction1)

when:
results.move(commands)
RobotSimulator expected = new RobotSimulator(x2, y2, direction2)
results.properties == expected.properties

then:
results.x == x2
results.y == y2
results.direction == direction2

where:
label || x1 || y1 || direction1 || x2 || y2 || direction2 || commands
"moving east and north from README" || 7 || 3 || "north" || 9 || 4 || "west" || "RAALAL"
"moving west and north" || 0 || 0 || "north" || -4 || 1 || "west" || "LAAARALA"
"moving west and south" || 2 || -7 || "east" || -3 || -8 || "south" || "RRAAAAALA"
"moving east and north" || 8 || 4 || "south" || 11 || 5 || "north" || "LAAARRRALLLL"
label || x1 | y1 | direction1 || x2 | y2 | direction2 || commands
"moving east and north from README" || 7 | 3 | "north" || 9 | 4 | "west" || "RAALAL"
"moving west and north" || 0 | 0 | "north" || -4 | 1 | "west" || "LAAARALA"
"moving west and south" || 2 | -7 | "east" || -3 | -8 | "south" || "RRAAAAALA"
"moving east and north" || 8 | 4 | "south" || 11 | 5 | "north" || "LAAARRRALLLL"
}
}
Loading