From 0379b0883d2a8d367da8f07061abf139469e843f Mon Sep 17 00:00:00 2001 From: Julia Alexeeva Date: Sun, 29 Dec 2024 12:42:27 +0200 Subject: [PATCH 1/3] Solution --- app/main.py | 214 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 189 insertions(+), 25 deletions(-) diff --git a/app/main.py b/app/main.py index 626f41cf..a3739ee6 100644 --- a/app/main.py +++ b/app/main.py @@ -1,34 +1,198 @@ +class BattleshipActivationError(Exception): + def __init__( + self, + message: str = "Creating ships doesn't meet the conditions!" + ) -> None: + super().__init__(message) + + class Deck: - def __init__(self, row, column, is_alive=True): - pass + def __init__( + self, + row: int, + column: int, + is_alive: bool = True + ) -> None: + self.row = row + self.column = column + self.is_alive = is_alive class Ship: - def __init__(self, start, end, is_drowned=False): - # Create decks and save them to a list `self.decks` - pass + def __init__( + self, + start: tuple, + end: tuple, + is_drowned: bool = False + ) -> None: + """ Create decks and save them to a list `self.decks`""" + self.start = start + self.end = end + self.is_drowned = is_drowned + self.forbidden_cells = set() + self.decks = self.set_decks_list(self.start, self.end) + + def set_decks_list( + self, + start_point: tuple, + end_point: tuple + ) -> list[Deck]: + row_s, column_s = start_point + row_e, column_e = end_point + self.get_forbidden_cells(row_s, column_s, row_e, column_e) + if row_s == row_e: + return [ + Deck(row_s, point) for point + in range(column_s, column_e + 1) + ] + else: + return [ + Deck(point, column_s) for point + in range(row_s, row_e + 1) + ] + + def get_forbidden_cells( + self, + row_start: int, + column_start: int, + row_end: int, + column_end: int + ) -> None: + for row in range(max(0, row_start - 1), min((row_end + 2), 10)): + for column in range( + max(0, column_start - 1), + min((column_end + 2), 10) + ): + self.forbidden_cells.add((row, column)) - def get_deck(self, row, column): - # Find the corresponding deck in the list - pass + def get_deck(self, row: int, column: int) -> Deck: + """ Find the corresponding deck in the list""" + for el in self.decks: + if el.row == row and el.column == column: + return el - def fire(self, row, column): - # Change the `is_alive` status of the deck - # And update the `is_drowned` value if it's needed - pass + def fire(self, row: int, column: int) -> None: + """ + Change the `is_alive` status of the deck + And update the `is_drowned` value if it's needed + """ + self.get_deck(row, column).is_alive = False + if all(not deck.is_alive for deck in self.decks): + self.is_drowned = True class Battleship: - def __init__(self, ships): - # Create a dict `self.field`. - # Its keys are tuples - the coordinates of the non-empty cells, - # A value for each cell is a reference to the ship - # which is located in it - pass - - def fire(self, location: tuple): - # This function should check whether the location - # is a key in the `self.field` - # If it is, then it should check if this cell is the last alive - # in the ship or not. - pass + def __init__(self, ships: list[tuple]) -> None: + """ + Create a dict `self.field`. + Its keys are tuples - the coordinates of the non-empty cells, + A value for each cell is a reference to the ship + which is located in it + """ + self.ships = [Ship(*ship) for ship in ships] + self.field = self.get_field() + self._validate_field(self.ships) + + def get_field(self) -> dict: + field = {} + for ship in self.ships: + for deck in ship.decks: + field[(deck.row, deck.column)] = ship + return field + + @staticmethod + def _validate_field(ships: list[Ship]) -> None: + if len(ships) != 10: + raise BattleshipActivationError( + "The total number of the ships should be 10!" + ) + + deck_counter = { + 1: [0, 4, "single-deck"], + 2: [0, 3, "double-deck"], + 3: [0, 2, "three-deck"], + 4: [0, 1, "four-deck"] + } + for ship in ships: + deck_counter[len(ship.decks)][0] += 1 + for key, value in deck_counter.items(): + if value[0] != value[1]: + raise BattleshipActivationError( + f"There should be {value[1]} {value[2]} " + f"ships, you have {value[0]}!" + ) + + forbidden_area = set() + for ship in ships: + for el in ((deck.row, deck.column) for deck in ship.decks): + if el in forbidden_area: + raise BattleshipActivationError( + f"It is impossible to place the ship here {el}!" + ) + forbidden_area.update(ship.forbidden_cells) + + def fire(self, location: tuple) -> str: + """ + This function should check whether the location + is a key in the `self.field` + If it is, then it should check if this cell is the last alive + in the ship or not. + """ + if location not in self.field.keys(): + return "Miss!" + else: + ship = self.field[location] + ship.fire(*location) + if ship.is_drowned: + return "Sunk!" + else: + return "Hit!" + + def print_field(self) -> None: + base_field = [["~" for _ in range(10)] for _ in range(10)] + + for location, ship in self.field.items(): + row, column = location + if ship.is_drowned: + base_field[row][column] = "X" + elif ship.get_deck(row, column).is_alive: + base_field[row][column] = "\u25A1" + else: + base_field[row][column] = "\u26A1" + + for line in base_field: + print(line) + print("___________________________________________________") + print("'\u25A1' - alive, '\u26A1' - hit, 'X' - drowned\n") + + +if __name__ == "__main__": + try: + battle_ship = Battleship( + ships=[ + ((0, 0), (0, 3)), + ((0, 5), (0, 6)), + ((0, 8), (0, 9)), + ((2, 0), (4, 0)), + ((2, 4), (2, 6)), + ((2, 8), (2, 9)), + ((9, 9), (9, 9)), + ((7, 7), (7, 7)), + ((7, 9), (7, 9)), + ((9, 7), (9, 7)), + ] + ) + battle_ship.print_field() + print( + battle_ship.fire((0, 4)), # Miss! + battle_ship.fire((0, 3)), # Hit! + battle_ship.fire((0, 2)) # Hit! + ) + print(battle_ship.print_field()) + print( + battle_ship.fire((0, 1)), # Hit! + battle_ship.fire((0, 0)), # Sunk! + ) + battle_ship.print_field() + except BattleshipActivationError as e: + print(e) From 64a58f4d19afc536c4336d0fb80826a099520d12 Mon Sep 17 00:00:00 2001 From: create <103182540+julia4406@users.noreply.github.com> Date: Mon, 30 Dec 2024 00:30:39 +0200 Subject: [PATCH 2/3] Solution_ Removed methods descriptions and executable block "__main__" --- app/main.py | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/app/main.py b/app/main.py index a3739ee6..9111cfea 100644 --- a/app/main.py +++ b/app/main.py @@ -25,7 +25,6 @@ def __init__( end: tuple, is_drowned: bool = False ) -> None: - """ Create decks and save them to a list `self.decks`""" self.start = start self.end = end self.is_drowned = is_drowned @@ -66,16 +65,11 @@ def get_forbidden_cells( self.forbidden_cells.add((row, column)) def get_deck(self, row: int, column: int) -> Deck: - """ Find the corresponding deck in the list""" for el in self.decks: if el.row == row and el.column == column: return el def fire(self, row: int, column: int) -> None: - """ - Change the `is_alive` status of the deck - And update the `is_drowned` value if it's needed - """ self.get_deck(row, column).is_alive = False if all(not deck.is_alive for deck in self.decks): self.is_drowned = True @@ -83,12 +77,6 @@ def fire(self, row: int, column: int) -> None: class Battleship: def __init__(self, ships: list[tuple]) -> None: - """ - Create a dict `self.field`. - Its keys are tuples - the coordinates of the non-empty cells, - A value for each cell is a reference to the ship - which is located in it - """ self.ships = [Ship(*ship) for ship in ships] self.field = self.get_field() self._validate_field(self.ships) @@ -132,12 +120,6 @@ def _validate_field(ships: list[Ship]) -> None: forbidden_area.update(ship.forbidden_cells) def fire(self, location: tuple) -> str: - """ - This function should check whether the location - is a key in the `self.field` - If it is, then it should check if this cell is the last alive - in the ship or not. - """ if location not in self.field.keys(): return "Miss!" else: @@ -164,35 +146,3 @@ def print_field(self) -> None: print(line) print("___________________________________________________") print("'\u25A1' - alive, '\u26A1' - hit, 'X' - drowned\n") - - -if __name__ == "__main__": - try: - battle_ship = Battleship( - ships=[ - ((0, 0), (0, 3)), - ((0, 5), (0, 6)), - ((0, 8), (0, 9)), - ((2, 0), (4, 0)), - ((2, 4), (2, 6)), - ((2, 8), (2, 9)), - ((9, 9), (9, 9)), - ((7, 7), (7, 7)), - ((7, 9), (7, 9)), - ((9, 7), (9, 7)), - ] - ) - battle_ship.print_field() - print( - battle_ship.fire((0, 4)), # Miss! - battle_ship.fire((0, 3)), # Hit! - battle_ship.fire((0, 2)) # Hit! - ) - print(battle_ship.print_field()) - print( - battle_ship.fire((0, 1)), # Hit! - battle_ship.fire((0, 0)), # Sunk! - ) - battle_ship.print_field() - except BattleshipActivationError as e: - print(e) From 769aea471d258d2fb5eb82a35841624e09083f6c Mon Sep 17 00:00:00 2001 From: Julia Alexeeva Date: Thu, 2 Jan 2025 14:34:06 +0200 Subject: [PATCH 3/3] Removed redundance (with 'else's) --- app/main.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/app/main.py b/app/main.py index 9111cfea..048a9620 100644 --- a/app/main.py +++ b/app/main.py @@ -44,11 +44,10 @@ def set_decks_list( Deck(row_s, point) for point in range(column_s, column_e + 1) ] - else: - return [ - Deck(point, column_s) for point - in range(row_s, row_e + 1) - ] + return [ + Deck(point, column_s) for point + in range(row_s, row_e + 1) + ] def get_forbidden_cells( self, @@ -122,13 +121,11 @@ def _validate_field(ships: list[Ship]) -> None: def fire(self, location: tuple) -> str: if location not in self.field.keys(): return "Miss!" - else: - ship = self.field[location] - ship.fire(*location) - if ship.is_drowned: - return "Sunk!" - else: - return "Hit!" + ship = self.field[location] + ship.fire(*location) + if ship.is_drowned: + return "Sunk!" + return "Hit!" def print_field(self) -> None: base_field = [["~" for _ in range(10)] for _ in range(10)]