From f45f7e0fda4edb4552bcc14dbfaec3262e9b4cad Mon Sep 17 00:00:00 2001 From: Ruidy Date: Fri, 27 Aug 2021 15:12:18 +0200 Subject: [PATCH] use Coordinates class and type hints --- .coverage | Bin 53248 -> 53248 bytes htmlcov/d_a44f0ac069e85531_test_rover_py.html | 2 +- htmlcov/d_bf1a7b34124815c2_rover_py.html | 139 ++++++++++-------- htmlcov/index.html | 10 +- htmlcov/status.json | 2 +- rover/rover.py | 51 ++++--- 6 files changed, 111 insertions(+), 93 deletions(-) diff --git a/.coverage b/.coverage index 628131557e184cc6d6f49ead1615b8f11d12b701..465fc783a40f16cdcdb987f9a5ca7fdb12c6d31e 100644 GIT binary patch delta 67 zcmV-J0KETzpaX!Q1F&o_2Q@k{IXW;jvvV(#Pz=TZ5BU%959klq55}_*5VQ}I$Bz*a Z4+I1O2_OXK=hx@8d-dM`db8+{7(hEN8$tj8 delta 66 zcmV-I0KNZ!paX!Q1F&o_2QfM{H##&jvvV(#Pz=Wa5BU%959klr5680+5Va4J#*YyY Y4g>@N2_6LU>+{;ZdhdTdv*?c)KpM6g#sB~S diff --git a/htmlcov/d_a44f0ac069e85531_test_rover_py.html b/htmlcov/d_a44f0ac069e85531_test_rover_py.html index d1e8c71..75e72f2 100644 --- a/htmlcov/d_a44f0ac069e85531_test_rover_py.html +++ b/htmlcov/d_a44f0ac069e85531_test_rover_py.html @@ -134,7 +134,7 @@

« index     coverage.py v6.0b1, - created at 2021-08-27 11:47 +0200 + created at 2021-08-27 15:04 +0200

diff --git a/htmlcov/d_bf1a7b34124815c2_rover_py.html b/htmlcov/d_bf1a7b34124815c2_rover_py.html index fd13ad7..4a8495d 100644 --- a/htmlcov/d_bf1a7b34124815c2_rover_py.html +++ b/htmlcov/d_bf1a7b34124815c2_rover_py.html @@ -22,8 +22,8 @@ Show keyboard shortcuts

- 48 statements   - + 55 statements   +

@@ -52,81 +52,90 @@
-

1import dataclasses 

-

2 

+

1from dataclasses import dataclass, field 

+

2from typing import Tuple 

3 

-

4@dataclasses.dataclass 

-

5class Rover: 

-

6 x: int 

-

7 y: int 

-

8 direction: str 

-

9 obstacles: list[list[int]] = dataclasses.field(default_factory=list) 

-

10 

-

11 valid_commands = ("F", "B", "R", "L") 

+

4 

+

5@dataclass(frozen=True) 

+

6class Coordinates: 

+

7 x: int 

+

8 y: int 

+

9 left: str 

+

10 right: str 

+

11 

12 

-

13 direction_map = { 

-

14 "NORTH": (0, 1, "WEST", "EAST"), 

-

15 "EAST": (1, 0, "NORTH", "SOUTH"), 

-

16 "SOUTH": (0, -1, "EAST", "WEST"), 

-

17 "WEST": (-1, 0, "SOUTH", "NORTH"), 

-

18 } 

+

13@dataclass 

+

14class Rover: 

+

15 x: int 

+

16 y: int 

+

17 direction: str 

+

18 obstacles: list[list[int]] = field(default_factory=list) 

19 

-

20 def is_valid_command(self, command): 

-

21 for ch in command: 

-

22 if ch not in self.valid_commands: 

-

23 return False 

-

24 

-

25 return True 

-

26 

-

27 def is_obstacle(self, x: int, y: int): 

-

28 return [x, y] in self.obstacles 

-

29 

-

30 def move(self, command: str): 

-

31 if not self.is_valid_command(command): 

-

32 raise ValueError("invalid command. The rover does not move") 

+

20 valid_commands = ("F", "B", "R", "L") 

+

21 

+

22 direction_map = { 

+

23 "NORTH": Coordinates(0, 1, "WEST", "EAST"), 

+

24 "EAST": Coordinates(1, 0, "NORTH", "SOUTH"), 

+

25 "SOUTH": Coordinates(0, -1, "EAST", "WEST"), 

+

26 "WEST": Coordinates(-1, 0, "SOUTH", "NORTH"), 

+

27 } 

+

28 

+

29 def is_valid_command(self, command: str) -> bool: 

+

30 for ch in command: 

+

31 if ch not in self.valid_commands: 

+

32 return False 

33 

-

34 for ch in command: 

-

35 x, y, direction = self._compute_new_coordinates(ch) 

-

36 if obstacle := self.is_obstacle(x, y): 

-

37 return self.new_coordinates_output(obstacle) 

+

34 return True 

+

35 

+

36 def is_obstacle(self, x: int, y: int) -> bool: 

+

37 return [x, y] in self.obstacles 

38 

-

39 self.apply_new_coordinates(x, y, direction) 

-

40 

-

41 return self.new_coordinates_output() 

+

39 def move(self, command: str): 

+

40 if not self.is_valid_command(command): 

+

41 raise ValueError("invalid command. The rover does not move") 

42 

-

43 def new_coordinates_output(self, obstacle: bool = False): 

-

44 if obstacle: 

-

45 return self.x, self.y, self.direction, "STOPPED" 

-

46 return self.x, self.y, self.direction 

+

43 for ch in command: 

+

44 x, y, direction = self._compute_new_coordinates(ch) 

+

45 if obstacle := self.is_obstacle(x, y): 

+

46 return self._new_coordinates_output(obstacle) 

47 

-

48 def _compute_new_coordinates(self, command): 

-

49 x = self.x 

-

50 y = self.y 

-

51 direction = self.direction 

-

52 

-

53 if command == "B": 

-

54 x -= self.direction_map[self.direction][0] 

-

55 y -= self.direction_map[self.direction][1] 

-

56 if command == "F": 

-

57 x += self.direction_map[self.direction][0] 

-

58 y += self.direction_map[self.direction][1] 

-

59 if command == "R": 

-

60 direction = self.direction_map[self.direction][3] 

-

61 if command == "L": 

-

62 direction = self.direction_map[self.direction][2] 

-

63 

-

64 return x, y, direction 

-

65 

-

66 def apply_new_coordinates(self, x, y, direction): 

-

67 self.x = x 

-

68 self.y = y 

-

69 self.direction = direction 

+

48 self._apply_new_coordinates(x, y, direction) 

+

49 

+

50 return self._new_coordinates_output() 

+

51 

+

52 def _new_coordinates_output(self, obstacle: bool = False): 

+

53 if obstacle: 

+

54 return self.x, self.y, self.direction, "STOPPED" 

+

55 return self.x, self.y, self.direction 

+

56 

+

57 def _compute_new_coordinates(self, command) -> Tuple[int, int, str]: 

+

58 x = self.x 

+

59 y = self.y 

+

60 direction = self.direction 

+

61 

+

62 if command == "B": 

+

63 x -= self.direction_map[self.direction].x 

+

64 y -= self.direction_map[self.direction].y 

+

65 if command == "F": 

+

66 x += self.direction_map[self.direction].x 

+

67 y += self.direction_map[self.direction].y 

+

68 if command == "R": 

+

69 direction = self.direction_map[self.direction].right 

+

70 if command == "L": 

+

71 direction = self.direction_map[self.direction].left 

+

72 

+

73 return x, y, direction 

+

74 

+

75 def _apply_new_coordinates(self, x, y, direction) -> None: 

+

76 self.x = x 

+

77 self.y = y 

+

78 self.direction = direction 

diff --git a/htmlcov/index.html b/htmlcov/index.html index ac0c549..b6f1904 100644 --- a/htmlcov/index.html +++ b/htmlcov/index.html @@ -53,10 +53,10 @@ Total - 76 + 83 0 0 - 100% + 100% @@ -69,10 +69,10 @@ rover/rover.py - 48 + 55 0 0 - 100% + 100% tests/__init__.py @@ -105,7 +105,7 @@

coverage.py v6.0b1, - created at 2021-08-27 11:47 +0200 + created at 2021-08-27 15:09 +0200

diff --git a/htmlcov/status.json b/htmlcov/status.json index 792bb67..b72452a 100644 --- a/htmlcov/status.json +++ b/htmlcov/status.json @@ -1 +1 @@ -{"format":2,"version":"6.0b1","globals":"9bd57ebde26eb2f4bf8646481dbf1d3b","files":{"d_bf1a7b34124815c2___init___py":{"hash":"3c77fc9ef7f887ac2508d4109cf92472","index":{"nums":[0,1,0,0,0,0,0,0],"html_filename":"d_bf1a7b34124815c2___init___py.html","relative_filename":"rover/__init__.py"}},"d_a44f0ac069e85531___init___py":{"hash":"3c77fc9ef7f887ac2508d4109cf92472","index":{"nums":[0,1,0,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531___init___py.html","relative_filename":"tests/__init__.py"}},"d_a44f0ac069e85531_test_rover_py":{"hash":"56847e16289e9409f1589e7acd9df071","index":{"nums":[0,1,23,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_rover_py.html","relative_filename":"tests/test_rover.py"}},"d_bf1a7b34124815c2_rover_py":{"hash":"d446e537c9a216d7ef2cb3482a0b83a6","index":{"nums":[0,1,48,0,0,0,0,0],"html_filename":"d_bf1a7b34124815c2_rover_py.html","relative_filename":"rover/rover.py"}},"d_a44f0ac069e85531_conftest_py":{"hash":"131dc5829333225ffeda75380140e18e","index":{"nums":[0,1,5,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_conftest_py.html","relative_filename":"tests/conftest.py"}}}} \ No newline at end of file +{"format":2,"version":"6.0b1","globals":"9bd57ebde26eb2f4bf8646481dbf1d3b","files":{"d_bf1a7b34124815c2___init___py":{"hash":"3c77fc9ef7f887ac2508d4109cf92472","index":{"nums":[0,1,0,0,0,0,0,0],"html_filename":"d_bf1a7b34124815c2___init___py.html","relative_filename":"rover/__init__.py"}},"d_a44f0ac069e85531___init___py":{"hash":"3c77fc9ef7f887ac2508d4109cf92472","index":{"nums":[0,1,0,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531___init___py.html","relative_filename":"tests/__init__.py"}},"d_a44f0ac069e85531_test_rover_py":{"hash":"56847e16289e9409f1589e7acd9df071","index":{"nums":[0,1,23,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_test_rover_py.html","relative_filename":"tests/test_rover.py"}},"d_bf1a7b34124815c2_rover_py":{"hash":"8c147885437640f233a8415d41aa413a","index":{"nums":[0,1,55,0,0,0,0,0],"html_filename":"d_bf1a7b34124815c2_rover_py.html","relative_filename":"rover/rover.py"}},"d_a44f0ac069e85531_conftest_py":{"hash":"131dc5829333225ffeda75380140e18e","index":{"nums":[0,1,5,0,0,0,0,0],"html_filename":"d_a44f0ac069e85531_conftest_py.html","relative_filename":"tests/conftest.py"}}}} \ No newline at end of file diff --git a/rover/rover.py b/rover/rover.py index 8fbacf6..f8367a1 100644 --- a/rover/rover.py +++ b/rover/rover.py @@ -1,30 +1,39 @@ -import dataclasses +from dataclasses import dataclass, field +from typing import Tuple -@dataclasses.dataclass +@dataclass(frozen=True) +class Coordinates: + x: int + y: int + left: str + right: str + + +@dataclass class Rover: x: int y: int direction: str - obstacles: list[list[int]] = dataclasses.field(default_factory=list) + obstacles: list[list[int]] = field(default_factory=list) valid_commands = ("F", "B", "R", "L") direction_map = { - "NORTH": (0, 1, "WEST", "EAST"), - "EAST": (1, 0, "NORTH", "SOUTH"), - "SOUTH": (0, -1, "EAST", "WEST"), - "WEST": (-1, 0, "SOUTH", "NORTH"), + "NORTH": Coordinates(0, 1, "WEST", "EAST"), + "EAST": Coordinates(1, 0, "NORTH", "SOUTH"), + "SOUTH": Coordinates(0, -1, "EAST", "WEST"), + "WEST": Coordinates(-1, 0, "SOUTH", "NORTH"), } - def is_valid_command(self, command): + def is_valid_command(self, command: str) -> bool: for ch in command: if ch not in self.valid_commands: return False return True - def is_obstacle(self, x: int, y: int): + def is_obstacle(self, x: int, y: int) -> bool: return [x, y] in self.obstacles def move(self, command: str): @@ -34,36 +43,36 @@ class Rover: for ch in command: x, y, direction = self._compute_new_coordinates(ch) if obstacle := self.is_obstacle(x, y): - return self.new_coordinates_output(obstacle) + return self._new_coordinates_output(obstacle) - self.apply_new_coordinates(x, y, direction) + self._apply_new_coordinates(x, y, direction) - return self.new_coordinates_output() + return self._new_coordinates_output() - def new_coordinates_output(self, obstacle: bool = False): + def _new_coordinates_output(self, obstacle: bool = False): if obstacle: return self.x, self.y, self.direction, "STOPPED" return self.x, self.y, self.direction - def _compute_new_coordinates(self, command): + def _compute_new_coordinates(self, command: str) -> Tuple[int, int, str]: x = self.x y = self.y direction = self.direction if command == "B": - x -= self.direction_map[self.direction][0] - y -= self.direction_map[self.direction][1] + x -= self.direction_map[self.direction].x + y -= self.direction_map[self.direction].y if command == "F": - x += self.direction_map[self.direction][0] - y += self.direction_map[self.direction][1] + x += self.direction_map[self.direction].x + y += self.direction_map[self.direction].y if command == "R": - direction = self.direction_map[self.direction][3] + direction = self.direction_map[self.direction].right if command == "L": - direction = self.direction_map[self.direction][2] + direction = self.direction_map[self.direction].left return x, y, direction - def apply_new_coordinates(self, x, y, direction): + def _apply_new_coordinates(self, x: int, y: int, direction: str) -> None: self.x = x self.y = y self.direction = direction