Александър обнови решението на 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, както прецениш), ще ти дадем една точка там. :)
Здраво се посмях на FizzBuzza :D Благодаря за инфото за тестовете, ще го имам предвид. Пуснах линк с тях в темата.
Получаваш една бонус точка. Не защото решението е практично, а защото очевидно си вложил доста повече усилия от останалите.