feat: added live coding features - obstacles and half-directions.

This commit is contained in:
Chris Davoren 2023-11-15 12:32:08 +10:00
parent aff508d835
commit 9abe516ec7
4 changed files with 87 additions and 14 deletions

11
example_e.txt Normal file
View File

@ -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

6
example_f.txt Normal file
View File

@ -0,0 +1,6 @@
BLOCK 0,4,ghjghjhg,hjghjgjhdts
PLACE 1,2,NORTHWEST
MOVE
RIGHT
MOVE
REPORT

View File

@ -4,7 +4,7 @@ class Robot:
of their position, direction, and movement limits (i.e. "board" size). of their position, direction, and movement limits (i.e. "board" size).
Note: Direction is specified via compass points (north, west, etc). As per 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. SOUTHWEST corner of the board.
Attributes: Attributes:
@ -26,12 +26,17 @@ class Robot:
# Directions ordered such that adding 1 corresponds to RIGHT turn # Directions ordered such that adding 1 corresponds to RIGHT turn
DIRECTIONS = { DIRECTIONS = {
"NORTH": 0, "NORTH": 0,
"EAST": 1, "NORTHEAST": 1,
"SOUTH": 2, "EAST": 2,
"WEST": 3, "SOUTHEAST": 3,
"SOUTH": 4,
"SOUTHWEST": 5,
"WEST": 6,
"NORTHWEST": 7
} }
VALID_COMMANDS = [ VALID_COMMANDS = [
"BLOCK",
"PLACE", "PLACE",
"MOVE", "MOVE",
"LEFT", "LEFT",
@ -45,9 +50,13 @@ class Robot:
# positive y is EAST) # positive y is EAST)
_MOVEMENT_VECTORS = { _MOVEMENT_VECTORS = {
0: {"x": 0, "y": 1}, 0: {"x": 0, "y": 1},
1: {"x": 1, "y": 0}, 1: {"x": 1, "y": 1},
2: {"x": 0, "y": -1}, 2: {"x": 1, "y": 0},
3: {"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): 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._position_y = None
self._direction = None self._direction = None
self._blocks = []
def valid_position(self, x: int, y: int) -> bool: def valid_position(self, x: int, y: int) -> bool:
""" """
Calculates whether the given coordinates are valid for the limits set 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. 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): def is_initialized(self):
""" """
@ -153,14 +175,16 @@ class Robot:
position_y (int): Vertical coordinate for placement. position_y (int): Vertical coordinate for placement.
direction_name (str): Direction of placement; must be one of the direction_name (str): Direction of placement; must be one of the
string keys in `Robot.DIRECTIONS`. Must be specified on first 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. 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 # have been validated
# print('Checking position...')
if not self.valid_position(position_x, position_y): if not self.valid_position(position_x, position_y):
return return
# print('Position check passed.')
if direction_name is not None: if direction_name is not None:
direction_name = direction_name.upper() direction_name = direction_name.upper()
@ -220,7 +244,7 @@ class Robot:
if not self.is_initialized(): if not self.is_initialized():
return return
self._direction = (self._direction - 1) % 4 self._direction = (self._direction - 1) % len(Robot.DIRECTIONS)
def rotate_right(self): def rotate_right(self):
""" """
@ -234,7 +258,7 @@ class Robot:
if not self.is_initialized(): if not self.is_initialized():
return return
self._direction = (self._direction + 1) % 4 self._direction = (self._direction + 1) % len(Robot.DIRECTIONS)
def interpret_command(self, command: str): def interpret_command(self, command: str):
""" """
@ -252,11 +276,32 @@ class Robot:
command = command.upper() command = command.upper()
command_tokens = [x.strip() for x in command.split(' ') if len(x) > 0] 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 \ if len(command_tokens) == 0 or not command_tokens[0] in \
Robot.VALID_COMMANDS: Robot.VALID_COMMANDS:
return return
match command_tokens[0]: 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": case "PLACE":
try: try:
# Must have parameters # Must have parameters
@ -276,6 +321,7 @@ class Robot:
# Direction parameter is optional on second and subsequent # Direction parameter is optional on second and subsequent
# placements. The place() method accounts for an absent # placements. The place() method accounts for an absent
# direction on first call and fails silently. # direction on first call and fails silently.
# print("PLACE command parsed: {}, {}".format(place_x, place_y))
if len(parameter_tokens) > 2: if len(parameter_tokens) > 2:
place_direction = parameter_tokens[2] place_direction = parameter_tokens[2]
self.place(place_x, place_y, place_direction) self.place(place_x, place_y, place_direction)
@ -305,8 +351,9 @@ class Robot:
if not self.is_initialized(): if not self.is_initialized():
return "Uninitialized" return "Uninitialized"
return "{},{},{}".format( return "{},{},{} | BLOCKS : {}".format(
self._position_x, self._position_x,
self._position_y, self._position_y,
self.get_direction() self.get_direction(),
":".join([str(x) for x in self._blocks])
) )

View File

@ -2,6 +2,7 @@
import toyrobot import toyrobot
def feed_file(filename: str, robot: toyrobot.Robot): def feed_file(filename: str, robot: toyrobot.Robot):
with open(filename) as f: with open(filename) as f:
for line in f: for line in f:
@ -9,6 +10,7 @@ def feed_file(filename: str, robot: toyrobot.Robot):
print(line) print(line)
robot.interpret_command(line) robot.interpret_command(line)
def main(): def main():
print('a)') print('a)')
feed_file('example_a.txt', toyrobot.Robot()) feed_file('example_a.txt', toyrobot.Robot())
@ -26,5 +28,12 @@ def main():
feed_file('example_d.txt', toyrobot.Robot()) feed_file('example_d.txt', toyrobot.Robot())
print() print()
print('e)')
feed_file('example_e.txt', toyrobot.Robot())
print()
print('f)')
feed_file('example_f.txt', toyrobot.Robot())
print()
if __name__ == "__main__": if __name__ == "__main__":
main() main()