diff --git a/mod_oop/5.6_01_sea_battle.py b/mod_oop/5.6_01_sea_battle.py index 3a76cfa..ede99e1 100644 --- a/mod_oop/5.6_01_sea_battle.py +++ b/mod_oop/5.6_01_sea_battle.py @@ -86,11 +86,35 @@ True >>> s[0] = 2 >>> s._is_move False +>>> s = Ship(1) >>> s.get_start_coords() -(None, None) +(0, 0) >>> s.set_start_coords(2, 3) >>> s.get_start_coords() (2, 3) +>>> s.move(1) +>>> s.get_start_coords() +(3, 3) +>>> s.move(-1) +>>> s.get_start_coords() +(2, 3) +>>> s[0] = 2 +>>> s.move(2) +>>> s.get_start_coords() +(2, 3) +>>> Ship(1).is_collide(Ship(2, 1, 2, 3)) +False +>>> Ship(2, 1, 2, 3).is_collide(Ship(3, 2, 3, 2)) +True +>>> Ship(2, 1, 0, 0).is_collide(Ship(1, 2, 2, 2)) +True +>>> Ship(1).is_out_pole(10) +False +>>> Ship(3, 1, 8, 1).is_out_pole(10) +True +>>> Ship(3, 2, 1, 8).is_out_pole(10) +True + # --- @@ -167,9 +191,11 @@ P.S. Для самых преданных поклонников програм Сыграйте в эту игру и выиграйте у компьютера. """ -from typing import Optional +from typing import List, Optional from enum import Enum from functools import total_ordering +from collections import namedtuple + @total_ordering class EnumOrdering(Enum): @@ -177,12 +203,13 @@ class EnumOrdering(Enum): if isinstance(other, self.__class__): return self.value == other.value return self.value == other - + def __lt__(self, other): if isinstance(other, self.__class__): return self.value < other.value return self.value < other + class IntEnumField: def __init__(self, enum_cls: Enum): self.enum_cls = enum_cls @@ -207,82 +234,150 @@ class ListEnumField: def __init__(self, enum_cls: Enum, length_key): self.enum_cls = enum_cls self.length_key = length_key - + def __set_name__(self, owner, name): self.name = name + "_" - + def __get__(self, instance, owner): if instance is None: return self return [x.value for x in getattr(instance, self.name)] - + def get_item(self, instance, index): return getattr(instance, self.name)[index] - + def set_item(self, instance, index, value): getattr(instance, self.name)[index] = self.enum_cls(value) - + def __set__(self, instance, value): - length = getattr(instance, str(self.length_key)) + length = getattr(instance, str(self.length_key)) new_value = list(map(self.enum_cls, value)) if len(new_value) != length: raise ValueError(f"{self.name} must be {length} elements long") setattr(instance, self.name, new_value) + +class NonNegativeIntField: + def __set_name__(self, owner, name): + self.name = name + "_" + + def __get__(self, instance, owner): + if instance is None: + return self + return getattr(instance, self.name) + + def __set__(self, instance, value): + if not isinstance(value, int): + raise TypeError(f"{self.name} must be an integer") + if value < 0: + raise ValueError(f"{self.name} must be non-negative") + setattr(instance, self.name, value) + + class Ship: class ShipSize(EnumOrdering): ONE_DECK = 1 TWO_DECKS = 2 THREE_DECKS = 3 FOUR_DECKS = 4 - + class ShipOrientation(EnumOrdering): HORIZONTAL = 1 VERTICAL = 2 - + class DeckStatus(EnumOrdering): OK = 1 DAMAGED = 2 pole_size: int = 10 - _length = IntEnumField(ShipSize) - _tp = IntEnumField(ShipOrientation) - _cells = ListEnumField(DeckStatus, _length) + _length: int = IntEnumField(ShipSize) + _tp: int = IntEnumField(ShipOrientation) + _cells: List[int] = ListEnumField(DeckStatus, _length) + _x: int = NonNegativeIntField() + _y: int = NonNegativeIntField() - def __init__(self, length: int, tp: int = 1, x: Optional[int] = None, y: Optional[int] = None, cells: Optional[list[DeckStatus]] = None): + Rect = namedtuple("Rect", ["left", "top", "right", "bottom"]) + + def __init__( + self, + length: int, + tp: int = 1, + x: int = 0, + y: int = 0, + cells: Optional[list[DeckStatus]] = None, + ): self._length, self._tp, self._x, self._y = length, tp, x, y self._cells = cells or [self.DeckStatus.OK] * self._length def __repr__(self): return f"{self.__class__.__name__}{(self._length, self._tp, self._x, self._y, self._cells)!r}" - + def __len__(self): return self._length - + def __getitem__(self, key): return self.__class__._cells.get_item(self, key).value def __setitem__(self, key, value): return self.__class__._cells.set_item(self, key, value) - + @property def _is_move(self) -> bool: return all(cell == self.DeckStatus.OK for cell in self) + @property + def is_alive(self) -> bool: + return any(cell == self.DeckStatus.OK for cell in self) + def set_start_coords(self, x: int, y: int): self._x, self._y = x, y def get_start_coords(self): return self._x, self._y - + + def move(self, go: int): + if self._is_move: + if self._tp == self.ShipOrientation.HORIZONTAL: + self._x += go + else: + self._y += go + + @property + def rect(self): + x, y = self.get_start_coords() + if self._tp == self.ShipOrientation.HORIZONTAL: + return self.Rect(x, y, x + self._length, y + 1) + else: + return self.Rect(x, y, x + 1, y + self._length) + + def is_collide(self, other: "Ship") -> bool: + return ( + self.rect.left <= other.rect.right + and self.rect.right >= other.rect.left + and self.rect.top <= other.rect.bottom + and self.rect.bottom >= other.rect.top + or any( + abs(getattr(a.rect, x) - getattr(b.rect, y)) == 0 + for a, b in ((self, other), (other, self)) + for x, y in (("left", "right"), ("top", "bottom")) + ) + ) + + def is_out_pole(self, size: int) -> bool: + return ( + self.rect.left < 0 + or self.rect.top < 0 + or self.rect.right > size + or self.rect.bottom > size + ) + class GamePole: def __init__(self, size: int = 10): ... - def tests(): code = ( b"b7*OBAUz;cXlZaLGARmkXlZaDJs?wPX>ceqEFdu{3Ug>_a3DP(Q)p>$C^IY|GAtl4EFdr`3JPI"