Initial commit of files.
This commit is contained in:
commit
e41e360ed8
|
@ -0,0 +1 @@
|
||||||
|
__pycache__
|
|
@ -0,0 +1,3 @@
|
||||||
|
# __init__.py
|
||||||
|
|
||||||
|
from .robot import Robot
|
|
@ -0,0 +1,243 @@
|
||||||
|
class Robot:
|
||||||
|
"""
|
||||||
|
Class representing a single "Toy Robot". Robots instances have knowledge of their
|
||||||
|
position, direction, and movement limits.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
DEFAULT_MAX_X (int): The default maximum allowable horizontal coordinate
|
||||||
|
(inclusive).
|
||||||
|
DEFAULT_MAX_Y (int): The default maximum allowable vertical coordinate
|
||||||
|
(inclusive).
|
||||||
|
DIRECTIONS (dict): A dictionary (str: int) of direction names (e.g. NORTH, EAST
|
||||||
|
etc) and their numerical encoding. Only the keys are intended for use externally as a list of valid directions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEFAULT_MAX_X = 5
|
||||||
|
DEFAULT_MAX_Y = 5
|
||||||
|
|
||||||
|
# Directions ordered such that adding 1 corresponds to RIGHT turn
|
||||||
|
DIRECTIONS = {
|
||||||
|
"NORTH": 0,
|
||||||
|
"EAST": 1,
|
||||||
|
"SOUTH": 2,
|
||||||
|
"WEST": 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Private internals
|
||||||
|
# Key corresponds to numerical direction defined in Robot.directions
|
||||||
|
# Value is an offset vector in with x and y keys (positive x is NORTH, 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},
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, max_x: int = DEFAULT_MAX_X, max_y: int = DEFAULT_MAX_Y):
|
||||||
|
"""
|
||||||
|
Creates a new Robot instance with the specified position limits (optional).
|
||||||
|
Implicitly, the minimum limit on coordinates is 0 both horizontally and
|
||||||
|
vertically.
|
||||||
|
|
||||||
|
See the class attributes `DEFAULT_MAX_X` and `DEFAULT_MAX_Y` for the respective
|
||||||
|
default numerical values.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
max_x (int): The maximum allowable horizontal coordinate (inclusive,
|
||||||
|
positive is EAST).
|
||||||
|
max_y (int): The maximum allowable vertical coordinate (inclusive,
|
||||||
|
positive is NORTH).
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If max_x or max_y are less than 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if max_x < 0 or max_y < 0:
|
||||||
|
raise ValueError("Cannot specify negative limits")
|
||||||
|
|
||||||
|
self._max_x = max_x
|
||||||
|
self._max_y = max_y
|
||||||
|
|
||||||
|
self._position_x = None
|
||||||
|
self._position_y = None
|
||||||
|
self._direction = None
|
||||||
|
|
||||||
|
def set_limits(self, max_x: int, max_y: int):
|
||||||
|
"""
|
||||||
|
Sets the positional limits for this Robot. See __init__() for details.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
max_x (int): New maximum allowable horizontal coordinate.
|
||||||
|
max_y (int): New maximum allowable vertical coordinate.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If max_x or max_y are less than 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if max_x < 0 or max_y < 0:
|
||||||
|
raise ValueError("Cannot specify negative limits")
|
||||||
|
|
||||||
|
self._max_x = max_x
|
||||||
|
self._max_y = max_y
|
||||||
|
|
||||||
|
def get_limits(self) -> (int, int):
|
||||||
|
"""
|
||||||
|
Returns the current maximum coordinates valid for this instance.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(int, int): A tuple of the maximum coordinates in the order (maximum_x,
|
||||||
|
maximum y).
|
||||||
|
"""
|
||||||
|
return (self._max_x, self._max_y)
|
||||||
|
|
||||||
|
def valid_position(self, x: int, y: int) -> bool:
|
||||||
|
"""
|
||||||
|
Calculates whether the given coordinates are valid for the limits set on this
|
||||||
|
Robot instance.
|
||||||
|
|
||||||
|
This function is used by the `move()` function to verify that a move action
|
||||||
|
would be successful.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x: Proposed horizontal coordinate.
|
||||||
|
y: Proposed vertical coordinate.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
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
|
||||||
|
|
||||||
|
def is_initialized(self):
|
||||||
|
"""
|
||||||
|
Returns whether this Robot instance has been initialized (i.e. whether a valid
|
||||||
|
`place()` command has been executed).
|
||||||
|
|
||||||
|
This function is used by the `move()` function to verify that the instance has
|
||||||
|
been correctly placed before movement.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if correctly initialized, otherwise false.
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
self._position_x is not None
|
||||||
|
and self._position_y is not None
|
||||||
|
and self._direction is not None
|
||||||
|
)
|
||||||
|
|
||||||
|
def move(self):
|
||||||
|
"""
|
||||||
|
Moves the robot one space in the direction it is currently facing, provided that
|
||||||
|
said movement would be within limits.
|
||||||
|
|
||||||
|
Will do nothing if this Robot instance has not been initialized successfully
|
||||||
|
with `place()` or the specified movement would be out of bounds.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.is_initialized():
|
||||||
|
return
|
||||||
|
|
||||||
|
vector = Robot._MOVEMENT_VECTORS[self._direction]
|
||||||
|
new_position_x = self._position_x + vector["x"]
|
||||||
|
new_position_y = self._position_y + vector["y"]
|
||||||
|
|
||||||
|
if not Robot.valid_position(new_position_x, new_position_y):
|
||||||
|
return
|
||||||
|
|
||||||
|
self._position_x = new_position_x
|
||||||
|
self._position_y = new_position_y
|
||||||
|
|
||||||
|
def place(self, position_x: int, position_y: int, direction_name: str):
|
||||||
|
"""
|
||||||
|
Places the Robot instance at the specified coordinates with the specified
|
||||||
|
direction.
|
||||||
|
|
||||||
|
Will do nothing if the given coordinates are out of bounds, or the direction is
|
||||||
|
not one of the keys in `Robot.DIRECTIONS`.
|
||||||
|
|
||||||
|
See `__init__()` for detailed coordinate information.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
position_x (int): Horizontal coordinate for placement.
|
||||||
|
position_y (int): Vertical coordinate for placement.
|
||||||
|
direction_name (str): Direction of placement; must be one of the string keys
|
||||||
|
in `Robot.DIRECTIONS`.
|
||||||
|
"""
|
||||||
|
direction_name = direction_name.upper()
|
||||||
|
if direction_name not in Robot.DIRECTIONS.keys() or not self.valid_position(
|
||||||
|
position_x, position_y
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
self._position_x = position_x
|
||||||
|
self._position_y = position_y
|
||||||
|
self._direction = Robot.DIRECTIONS[direction_name]
|
||||||
|
|
||||||
|
def get_position(self) -> (int, int):
|
||||||
|
"""
|
||||||
|
Returns the current position of this Robot instance.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(int, int): A tuple with this instance's current coordinates in the order
|
||||||
|
(position_x, position_y), or (None, None) if this instance has not yet
|
||||||
|
been initialized.
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
(self._position_x, self._position_y)
|
||||||
|
if self.is_initialized()
|
||||||
|
else (None, None)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_direction(self) -> str:
|
||||||
|
"""
|
||||||
|
Returns the direction in which this instance is currently facing in string form.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The current direction of this instance; will be one of the keys of
|
||||||
|
`Robot.DIRECTION`. Will return None if this instance has not yet been
|
||||||
|
initialized.
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
{v: k for k, v in Robot.DIRECTIONS.items()}[self._direction]
|
||||||
|
if self.is_initialized()
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
|
def rotate_left(self):
|
||||||
|
"""
|
||||||
|
Rotates this Robot instance's direction to the LEFT. For example, if currently
|
||||||
|
facing NORTH, the new direction will be WEST.
|
||||||
|
|
||||||
|
Does nothing if this instance has not yet been correctly placed (initialized).
|
||||||
|
"""
|
||||||
|
if not self.is_initialized():
|
||||||
|
return
|
||||||
|
self._direction = (self._direction - 1) % 4
|
||||||
|
|
||||||
|
def rotate_right(self):
|
||||||
|
"""
|
||||||
|
Rotates this Robot instance's direction to the RIGHT. For example, if currently
|
||||||
|
facing NORTH, the new direction will be EAST.
|
||||||
|
|
||||||
|
Does nothing if this instance has not yet been correctly placed (initialized).
|
||||||
|
"""
|
||||||
|
if self.is_initialized():
|
||||||
|
return
|
||||||
|
self._direction = (self._direction + 1) % 4
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""
|
||||||
|
Returns a string representation of the instance including position and
|
||||||
|
direction.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: This instance's string representation.
|
||||||
|
"""
|
||||||
|
if not self.is_initialized():
|
||||||
|
return "Uninitialized"
|
||||||
|
return "X: {}, Y: {}, direction: {}".format(
|
||||||
|
self._position_x,
|
||||||
|
self._position_y,
|
||||||
|
{v: k for k, v in Robot.DIRECTIONS.items()}[self._direction],
|
||||||
|
)
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# import sys
|
||||||
|
# import argparse
|
||||||
|
|
||||||
|
import toyrobot
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
current_bot = toyrobot.Robot(10, 10)
|
||||||
|
print(current_bot)
|
||||||
|
current_bot.place(6, 6, "NORTH")
|
||||||
|
print(current_bot)
|
||||||
|
current_bot.place(2, 2, "NORTH")
|
||||||
|
print(current_bot)
|
||||||
|
current_bot.place(6, 6, "NORTH")
|
||||||
|
print(current_bot)
|
||||||
|
print("Not yet implemented")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in New Issue