diff --git a/exercises/practice/robot-simulator/.docs/instructions.append.md b/exercises/practice/robot-simulator/.docs/instructions.append.md new file mode 100644 index 00000000..f2618d3a --- /dev/null +++ b/exercises/practice/robot-simulator/.docs/instructions.append.md @@ -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. \ No newline at end of file diff --git a/exercises/practice/robot-simulator/.meta/src/reference/groovy/RobotSimulator.groovy b/exercises/practice/robot-simulator/.meta/src/reference/groovy/RobotSimulator.groovy index 6919a522..9f79de52 100644 --- a/exercises/practice/robot-simulator/.meta/src/reference/groovy/RobotSimulator.groovy +++ b/exercises/practice/robot-simulator/.meta/src/reference/groovy/RobotSimulator.groovy @@ -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 } } } diff --git a/exercises/practice/robot-simulator/src/main/groovy/RobotSimulator.groovy b/exercises/practice/robot-simulator/src/main/groovy/RobotSimulator.groovy index 2bdb7687..bf5664ae 100644 --- a/exercises/practice/robot-simulator/src/main/groovy/RobotSimulator.groovy +++ b/exercises/practice/robot-simulator/src/main/groovy/RobotSimulator.groovy @@ -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') } diff --git a/exercises/practice/robot-simulator/src/test/groovy/RobotSimulatorSpec.groovy b/exercises/practice/robot-simulator/src/test/groovy/RobotSimulatorSpec.groovy index 93fa17d4..7b6e68a1 100644 --- a/exercises/practice/robot-simulator/src/test/groovy/RobotSimulatorSpec.groovy +++ b/exercises/practice/robot-simulator/src/test/groovy/RobotSimulatorSpec.groovy @@ -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" } }