Решение на Навигация на Piet от Александър Стоилов

Обратно към всички решения

Към профила на Александър Стоилов

Резултати

  • 10 точки от тестове
  • 1 бонус точка
  • 11 точки общо
  • 8 успешни тест(а)
  • 0 неуспешни тест(а)

Код

from enum import Enum
import unittest
class Color(Enum):
LIGHT_GREEN = "C0FFC0"
DARK_GREEN = "00C000"
LIGHT_YELLOW = "FFFFC0"
DARK_YELLOW = "C0C000"
LIGHT_RED = "FFC0C0"
DARK_RED = "C00000"
LIGHT_BLUE = "C0C0FF"
DARK_BLUE = "0000C0"
WHITE = "FFFFFF"
BLACK = "000000"
class Direction(Enum):
LEFT = 1
RIGHT = 2
UP = 3
DOWN = 4
class Magnitude(Enum):
FORWARD = 1
BACKWARD = 2
class Order:
def __init__(self, direction: Direction, magnitude: Magnitude) -> None:
self.direction = direction
self.magnitude = magnitude
def is_go_up(self):
return (self.direction == Direction.UP and self.magnitude == Magnitude.FORWARD) \
or (self.direction == Direction.DOWN and self.magnitude == Magnitude.BACKWARD)
def is_go_down(self):
return (self.direction == Direction.DOWN and self.magnitude == Magnitude.FORWARD) \
or (self.direction == Direction.UP and self.magnitude == Magnitude.BACKWARD)
def is_go_left(self):
return (self.direction == Direction.LEFT and self.magnitude == Magnitude.FORWARD) \
or (self.direction == Direction.RIGHT and self.magnitude == Magnitude.BACKWARD)
def is_go_right(self):
return (self.direction == Direction.LEFT and self.magnitude == Magnitude.BACKWARD) \
or (self.direction == Direction.RIGHT and self.magnitude == Magnitude.FORWARD)
def validate_arguments_type(*args):
if len(args) != 2:
raise Exception("Only 2 arguments expected - inital point and colors.")
initial_point_coordinates_tuple = args[0]
colors_list = args[1]
if not isinstance(initial_point_coordinates_tuple, tuple):
raise Exception("Point coordinates should be passed in a tuple.")
elif len(initial_point_coordinates_tuple) != 2:
raise Exception("Point coordinates' tuple should contain 2 values.")
elif not isinstance(initial_point_coordinates_tuple[0], int) \
or not isinstance(initial_point_coordinates_tuple[1], int):
raise Exception(
"Point coordinates in the tuple should be passed as integers.")
elif not isinstance(colors_list, list):
raise Exception("Colors should be passed in a list.")
for color in colors_list:
if not isinstance(color, str):
raise Exception("Colors should be passed as a string.")
def get_order_from_color(color_name: str) -> Order:
direction: Direction = None
if "Green".lower() in color_name.lower():
direction = Direction.RIGHT
elif "Yellow".lower() in color_name.lower():
direction = Direction.UP
elif "Red".lower() in color_name.lower():
direction = Direction.LEFT
elif "Blue".lower() in color_name.lower():
direction = Direction.DOWN
magnitude: Magnitude = None
if "Light".lower() in color_name.lower():
magnitude = Magnitude.BACKWARD
elif "Dark".lower() in color_name.lower():
magnitude = Magnitude.FORWARD
return Order(direction, magnitude)
def advance_coordinates(x, y, order: Order) -> tuple[int]:
if order.is_go_left():
x -= 1
elif order.is_go_right():
x += 1
elif order.is_go_up():
y += 1
elif order.is_go_down():
y -= 1
return (x, y)
def calculate_final_vector(*args) -> tuple[int]:
validate_arguments_type(*args)
colors_supported: dict[str, str] = {
color.value: color.name for color in Color
}
start_point = args[0]
point_x = start_point[0]
point_y = start_point[1]
colors_list_passed: list[str] = args[1]
for color in colors_list_passed:
if color.upper() not in colors_supported:
raise Exception("Color not supported.")
color_name: str = colors_supported.get(color.upper())
# if color is black, stop calculation
if "Black".lower() in color_name.lower():
return (point_x, point_y)
# if color is white, do nothing
if "White".lower() in color_name.lower():
continue
# else, get the order (direction and magnitude) based on the color
order: Order = get_order_from_color(color_name)
# advance the coordinates based on the formed order
point_x, point_y = advance_coordinates(point_x, point_y, order)
return (point_x, point_y)
class Test(unittest.TestCase):
test_cases = [
((0, 0), ['00C000', 'C0FFC0', 'C00000', 'FFFFFF', 'C0C000'], (-1, 1)),
((0, 0), ['00c000', 'C0fFc0', 'c00000', 'FfFfff', 'c0c000'], (-1, 1)),
((0, 0), ['00C000', '000000', 'C00000', 'FFFFFF'], (1, 0)),
((0, 0), ['00C000', 'FFFFFF', 'C00000', 'FFFFFF'], (0, 0)),
((0, 0), ['00C000', '00C000', '00C000', '00C000', '00C000'], (5, 0)),
((0, 0), ['FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF'], (0, 0)),
((0, 0), ['00c000', 'c0c000'], (1, 1)),
((0, 0), [], (0, 0)),
]
test_functions = [calculate_final_vector]
def test_all_own_cases(self):
for function_to_test in self.test_functions:
for [(x, y), colors_list, expected] in self.test_cases:
output = function_to_test((x, y), colors_list)
assert output == expected, \
f'(x,y) = {(x, y)}, ' + \
f'colors_list = {colors_list}, ' + \
f'output = {output}, ' + \
f'expected = {expected}'
if __name__ == "__main__":
unittest.main()

Лог от изпълнението

........
----------------------------------------------------------------------
Ran 8 tests in 0.105s

OK

История (1 версия и 3 коментара)

Александър обнови решението на 21.10.2022 02:50 (преди около 2 години)

+from enum import Enum
+import unittest
+
+
+class Color(Enum):
+ LIGHT_GREEN = "C0FFC0"
+ DARK_GREEN = "00C000"
+ LIGHT_YELLOW = "FFFFC0"
+ DARK_YELLOW = "C0C000"
+ LIGHT_RED = "FFC0C0"
+ DARK_RED = "C00000"
+ LIGHT_BLUE = "C0C0FF"
+ DARK_BLUE = "0000C0"
+ WHITE = "FFFFFF"
+ BLACK = "000000"
+
+
+class Direction(Enum):
+ LEFT = 1
+ RIGHT = 2
+ UP = 3
+ DOWN = 4
+
+
+class Magnitude(Enum):
+ FORWARD = 1
+ BACKWARD = 2
+
+
+class Order:
+ def __init__(self, direction: Direction, magnitude: Magnitude) -> None:
+ self.direction = direction
+ self.magnitude = magnitude
+
+ def is_go_up(self):
+ return (self.direction == Direction.UP and self.magnitude == Magnitude.FORWARD) \
+ or (self.direction == Direction.DOWN and self.magnitude == Magnitude.BACKWARD)
+
+ def is_go_down(self):
+ return (self.direction == Direction.DOWN and self.magnitude == Magnitude.FORWARD) \
+ or (self.direction == Direction.UP and self.magnitude == Magnitude.BACKWARD)
+
+ def is_go_left(self):
+ return (self.direction == Direction.LEFT and self.magnitude == Magnitude.FORWARD) \
+ or (self.direction == Direction.RIGHT and self.magnitude == Magnitude.BACKWARD)
+
+ def is_go_right(self):
+ return (self.direction == Direction.LEFT and self.magnitude == Magnitude.BACKWARD) \
+ or (self.direction == Direction.RIGHT and self.magnitude == Magnitude.FORWARD)
+
+
+def validate_arguments_type(*args):
+ if len(args) != 2:
+ raise Exception("Only 2 arguments expected - inital point and colors.")
+
+ initial_point_coordinates_tuple = args[0]
+ colors_list = args[1]
+
+ if not isinstance(initial_point_coordinates_tuple, tuple):
+ raise Exception("Point coordinates should be passed in a tuple.")
+
+ elif len(initial_point_coordinates_tuple) != 2:
+ raise Exception("Point coordinates' tuple should contain 2 values.")
+
+ elif not isinstance(initial_point_coordinates_tuple[0], int) \
+ or not isinstance(initial_point_coordinates_tuple[1], int):
+ raise Exception(
+ "Point coordinates in the tuple should be passed as integers.")
+
+ elif not isinstance(colors_list, list):
+ raise Exception("Colors should be passed in a list.")
+
+ for color in colors_list:
+ if not isinstance(color, str):
+ raise Exception("Colors should be passed as a string.")
+
+
+def get_order_from_color(color_name: str) -> Order:
+ direction: Direction = None
+ if "Green".lower() in color_name.lower():
+ direction = Direction.RIGHT
+ elif "Yellow".lower() in color_name.lower():
+ direction = Direction.UP
+ elif "Red".lower() in color_name.lower():
+ direction = Direction.LEFT
+ elif "Blue".lower() in color_name.lower():
+ direction = Direction.DOWN
+
+ magnitude: Magnitude = None
+ if "Light".lower() in color_name.lower():
+ magnitude = Magnitude.BACKWARD
+ elif "Dark".lower() in color_name.lower():
+ magnitude = Magnitude.FORWARD
+
+ return Order(direction, magnitude)
+
+
+def advance_coordinates(x, y, order: Order) -> tuple[int]:
+ if order.is_go_left():
+ x -= 1
+ elif order.is_go_right():
+ x += 1
+ elif order.is_go_up():
+ y += 1
+ elif order.is_go_down():
+ y -= 1
+ return (x, y)
+
+
+def calculate_final_vector(*args) -> tuple[int]:
+
+ validate_arguments_type(*args)
+
+ colors_supported: dict[str, str] = {
+ color.value: color.name for color in Color
+ }
+
+ start_point = args[0]
+ point_x = start_point[0]
+ point_y = start_point[1]
+
+ colors_list_passed: list[str] = args[1]
+
+ for color in colors_list_passed:
+ if color.upper() not in colors_supported:
+ raise Exception("Color not supported.")
+
+ color_name: str = colors_supported.get(color.upper())
+
+ # if color is black, stop calculation
+ if "Black".lower() in color_name.lower():
+ return (point_x, point_y)
+
+ # if color is white, do nothing
+ if "White".lower() in color_name.lower():
+ continue
+
+ # else, get the order (direction and magnitude) based on the color
+ order: Order = get_order_from_color(color_name)
+
+ # advance the coordinates based on the formed order
+ point_x, point_y = advance_coordinates(point_x, point_y, order)
+
+ return (point_x, point_y)
+
+
+class Test(unittest.TestCase):
+
+ test_cases = [
+ ((0, 0), ['00C000', 'C0FFC0', 'C00000', 'FFFFFF', 'C0C000'], (-1, 1)),
+ ((0, 0), ['00c000', 'C0fFc0', 'c00000', 'FfFfff', 'c0c000'], (-1, 1)),
+ ((0, 0), ['00C000', '000000', 'C00000', 'FFFFFF'], (1, 0)),
+ ((0, 0), ['00C000', 'FFFFFF', 'C00000', 'FFFFFF'], (0, 0)),
+ ((0, 0), ['00C000', '00C000', '00C000', '00C000', '00C000'], (5, 0)),
+ ((0, 0), ['FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF', 'FFFFFF'], (0, 0)),
+ ((0, 0), ['00c000', 'c0c000'], (1, 1)),
+ ((0, 0), [], (0, 0)),
+ ]
+
+ test_functions = [calculate_final_vector]
+
+ def test_all_own_cases(self):
+ for function_to_test in self.test_functions:
+ for [(x, y), colors_list, expected] in self.test_cases:
+ output = function_to_test((x, y), colors_list)
+ assert output == expected, \
+ f'(x,y) = {(x, y)}, ' + \
+ f'colors_list = {colors_list}, ' + \
+ f'output = {output}, ' + \
+ f'expected = {expected}'
+
+
+if __name__ == "__main__":
+ unittest.main()

Решението ти ми напомни на това. Осъзнавам, че осъзнаваш, че е мега overengineered и си го направил така, защото ти е кеф, така че all good. :D
Виж за тестовете ще ти дам малко обратна връзка:

  • Тестовете не е редно да са в production код, но предполагам, това го знаеш.
  • unittest работи със собствени методи за assertion, pytest работи с питонският assert. Амалгамата на двете е нежелателна.
  • Цикличното тестване на много функции в 1 метод е изключително нишово. В общият случай ще е объркващо, ще прави теста труден за четене или ще има множество несвързани assert-и в зависимост от спецификата на всяка функция.

P.S. Ако си публикуваш тестовете в темата (можеш като gist, като линк в github, както прецениш), ще ти дадем една точка там. :)