From f38a83f938e8c950efa8429df001319c68df7aa8 Mon Sep 17 00:00:00 2001 From: Olexandr Bastov Date: Tue, 13 Aug 2024 16:08:15 +0300 Subject: [PATCH] solution --- app/main.py | 125 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 100 insertions(+), 25 deletions(-) diff --git a/app/main.py b/app/main.py index 626f41cf..a1216c02 100644 --- a/app/main.py +++ b/app/main.py @@ -1,34 +1,109 @@ 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: + self.is_drowned = is_drowned + self.decks = [] + if start == end: + self.decks.append(Deck(start[0], start[1])) + elif start[0] == end[0] and start[1] != end[1]: + for num in range(end[1] - start[1] + 1): + self.decks.append(Deck(start[0], start[1] + num)) + else: + for num in range(end[0] - start[0] + 1): + self.decks.append(Deck(start[0] + num, start[1])) - def get_deck(self, row, column): - # Find the corresponding deck in the list - pass + def get_deck(self, row: int, column: int) -> Deck: + for deck in self.decks: + if deck.row == row and deck.column == column: + return deck - 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) -> bool: + deck = self.get_deck(row, column) + if not self.is_drowned and deck.is_alive: + deck.is_alive = False + for deck in self.decks: + if deck.is_alive: + return self.is_drowned + self.is_drowned = True + + return self.is_drowned 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[tuple, tuple]]) -> None: + self.field = {} + for ship in ships: + current_ship = Ship(ship[0], ship[1]) + for deck in current_ship.decks: + self.field[(deck.row, deck.column)] = current_ship + + def fire(self, location: tuple) -> str: + if location in self.field: + sunk = self.field[location].fire(*location) + return "Sunk!" if sunk else "Hit!" + + return "Miss!" + + def _validate_field(self) -> None: + ships = set(self.field.values()) + if not ( + len(ships) == 10 + or self._validate_ships_count + or self._validate_ship_location + ): + raise InvalidBattleshipField( + "Incorrect ship coordinates in the passed list" + ) + + def print_field(self) -> None: + empty = [["~" for _ in range(10)] for _ in range(10)] + for ship in set(self.field.values()): + marker = "*" + if ship.is_drowned: + marker = "x" + for deck in ship.decks: + if deck.is_alive: + empty[deck.row][deck.column] = u"\u25A1" + else: + empty[deck.row][deck.column] = marker + filled_field = "" + + for row in empty: + filled_field += "\t".join(row) + "\n" + + print(filled_field) + + def _validate_ships_count(self) -> bool: + ships_count = [0, 0, 0, 0] + for ship in set(self.field.values()): + ships_count[len(ship.decks) - 1] += 1 + return ships_count == [4, 3, 2, 1] + + def _validate_ship_location(self) -> bool: + directions = [ + (-1, -1), (1, 1), (-1, 0), (0, -1), + (-1, 1), (1, -1), (1, 0), (0, 1), + ] + + for (row, column), ship in self.field.items(): + for dr, dc in directions: + adjacent = (row + dr, column + dc) + if adjacent in self.field and self.field[adjacent] != ship: + return False + + return False + + +class InvalidBattleshipField(Exception): + pass