diff --git a/example_e.txt b/example_e.txt new file mode 100644 index 0000000..b681ff9 --- /dev/null +++ b/example_e.txt @@ -0,0 +1,11 @@ +BLOCK 2,2 +BLOCK 3,2 +PLACE 1,2,EAST +BLOCK 1,2 +MOVE +LEFT +MOVE +REPORT +PLACE 3,1 +MOVE +REPORT \ No newline at end of file diff --git a/example_f.txt b/example_f.txt new file mode 100644 index 0000000..a362fd9 --- /dev/null +++ b/example_f.txt @@ -0,0 +1,6 @@ +BLOCK 0,4,ghjghjhg,hjghjgjhdts +PLACE 1,2,NORTHWEST +MOVE +RIGHT +MOVE +REPORT \ No newline at end of file diff --git a/toyrobot/robot.py b/toyrobot/robot.py index d71e7da..be2d5cb 100644 --- a/toyrobot/robot.py +++ b/toyrobot/robot.py @@ -4,7 +4,7 @@ class Robot: of their position, direction, and movement limits (i.e. "board" size). Note: Direction is specified via compass points (north, west, etc). As per - the original problem description, the coordinates (0, 0) represent the + the original problem description, the coordinates (0, 0) represent the SOUTHWEST corner of the board. Attributes: @@ -26,12 +26,17 @@ class Robot: # Directions ordered such that adding 1 corresponds to RIGHT turn DIRECTIONS = { "NORTH": 0, - "EAST": 1, - "SOUTH": 2, - "WEST": 3, + "NORTHEAST": 1, + "EAST": 2, + "SOUTHEAST": 3, + "SOUTH": 4, + "SOUTHWEST": 5, + "WEST": 6, + "NORTHWEST": 7 } VALID_COMMANDS = [ + "BLOCK", "PLACE", "MOVE", "LEFT", @@ -45,9 +50,13 @@ class Robot: # positive y is EAST) _MOVEMENT_VECTORS = { 0: {"x": 0, "y": 1}, - 1: {"x": 1, "y": 0}, - 2: {"x": 0, "y": -1}, - 3: {"x": -1, "y": 0}, + 1: {"x": 1, "y": 1}, + 2: {"x": 1, "y": 0}, + 3: {"x": 1, "y": -1}, + 4: {"x": 0, "y": -1}, + 5: {"x": -1, "y": -1}, + 6: {"x": -1, "y": 0}, + 7: {"x": -1, "y": 1}, } def __init__(self, max_x: int = DEFAULT_MAX_X, max_y: int = DEFAULT_MAX_Y): @@ -79,6 +88,8 @@ class Robot: self._position_y = None self._direction = None + self._blocks = [] + def valid_position(self, x: int, y: int) -> bool: """ Calculates whether the given coordinates are valid for the limits set @@ -95,7 +106,18 @@ class Robot: bool: True if given coordinates are within limits, otherwise False. """ - return x >= 0 and y >= 0 and x <= self._max_x and y <= self._max_y + within_limits = x >= 0 and y >= 0 and x <= self._max_x and y <= self._max_y + on_block = (x, y) in self._blocks + # print(self._blocks) + # print(within_limits) + # print(on_block) + return within_limits and not on_block + + def add_block(self, x: int, y: int): + if x == self._position_x and y == self._position_y: + return + + self._blocks.append((x, y)) def is_initialized(self): """ @@ -153,14 +175,16 @@ class Robot: position_y (int): Vertical coordinate for placement. direction_name (str): Direction of placement; must be one of the string keys in `Robot.DIRECTIONS`. Must be specified on first - placement (or this call will fail silently), but is optional + placement (or this call will fail silently), but is optional thereafter. """ - # Must be careful not to make any state changes until all inputs + # Must be careful not to make any state changes until all inputs # have been validated + # print('Checking position...') if not self.valid_position(position_x, position_y): return + # print('Position check passed.') if direction_name is not None: direction_name = direction_name.upper() @@ -220,7 +244,7 @@ class Robot: if not self.is_initialized(): return - self._direction = (self._direction - 1) % 4 + self._direction = (self._direction - 1) % len(Robot.DIRECTIONS) def rotate_right(self): """ @@ -234,7 +258,7 @@ class Robot: if not self.is_initialized(): return - self._direction = (self._direction + 1) % 4 + self._direction = (self._direction + 1) % len(Robot.DIRECTIONS) def interpret_command(self, command: str): """ @@ -252,11 +276,32 @@ class Robot: command = command.upper() command_tokens = [x.strip() for x in command.split(' ') if len(x) > 0] + # This is not strictly necessary as case if len(command_tokens) == 0 or not command_tokens[0] in \ Robot.VALID_COMMANDS: return match command_tokens[0]: + case "BLOCK": + # print('Found block...') + try: + if len(command_tokens) < 2: + # print('Insufficient tokens...') + return + + parameter_tokens = [x.strip() for x in command_tokens[1].split(',')] + + if len(parameter_tokens) < 2: + # print('Insufficient parameters...') + return + block_x = int(parameter_tokens[0]) + block_y = int(parameter_tokens[1]) + + # print("Adding block: {} {}".format(block_x, block_y)) + self.add_block(block_x, block_y) + except ValueError as ve: + # print('Integer parsing error...') + return case "PLACE": try: # Must have parameters @@ -276,6 +321,7 @@ class Robot: # Direction parameter is optional on second and subsequent # placements. The place() method accounts for an absent # direction on first call and fails silently. + # print("PLACE command parsed: {}, {}".format(place_x, place_y)) if len(parameter_tokens) > 2: place_direction = parameter_tokens[2] self.place(place_x, place_y, place_direction) @@ -305,8 +351,9 @@ class Robot: if not self.is_initialized(): return "Uninitialized" - return "{},{},{}".format( + return "{},{},{} | BLOCKS : {}".format( self._position_x, self._position_y, - self.get_direction() + self.get_direction(), + ":".join([str(x) for x in self._blocks]) ) diff --git a/trexamples.py b/trexamples.py index b3196b5..b004bc1 100644 --- a/trexamples.py +++ b/trexamples.py @@ -2,6 +2,7 @@ import toyrobot + def feed_file(filename: str, robot: toyrobot.Robot): with open(filename) as f: for line in f: @@ -9,6 +10,7 @@ def feed_file(filename: str, robot: toyrobot.Robot): print(line) robot.interpret_command(line) + def main(): print('a)') feed_file('example_a.txt', toyrobot.Robot()) @@ -26,5 +28,12 @@ def main(): feed_file('example_d.txt', toyrobot.Robot()) print() + print('e)') + feed_file('example_e.txt', toyrobot.Robot()) + print() + + print('f)') + feed_file('example_f.txt', toyrobot.Robot()) + print() if __name__ == "__main__": main()