diff --git a/mod_oop/5.6_01_sea_battle.py b/mod_oop/5.6_01_sea_battle.py index 1bf7c52..3a76cfa 100644 --- a/mod_oop/5.6_01_sea_battle.py +++ b/mod_oop/5.6_01_sea_battle.py @@ -36,9 +36,9 @@ length - длина корабля (число палуб: целое значе tp - ориентация корабля (1 - горизонтальная; 2 - вертикальная). Объекты класса Ship должны создаваться командами: -ship = Ship(length) -ship = Ship(length, tp) -ship = Ship(length, tp, x, y) +>>> ship = Ship(length := 1) +>>> ship = Ship(length := 1, tp := 2) +>>> ship = Ship(length := 1, tp := 2, x := 1, y := 1) По умолчанию (если не указывается) параметр tp = 1, а координаты x, y равны None. @@ -49,6 +49,52 @@ _tp - ориентация корабля; _is_move - возможно ли перемещение корабля (изначально равно True); _cells - изначально список длиной length, состоящий из единиц (например, при length=3, _cells = [1, 1, 1]). +# доп проверки кораблей --- +>>> [*map(len, (Ship(1),Ship(2),Ship(3),Ship(4)))] == [*range(1, 5)] +True +>>> Ship(5) +Traceback (most recent call last): +... +ValueError: 5 is not a valid Ship.ShipSize +>>> Ship(1, 1)._tp, Ship(1, 2)._tp +(1, 2) +>>> Ship(1, 3)._tp +Traceback (most recent call last): +... +ValueError: 3 is not a valid Ship.ShipOrientation +>>> s = Ship(1) +>>> {s[0] == 1, len(s) == 1} +{True} +>>> s[0] = 2 +>>> s[0] +2 +>>> s[0] = 4 +Traceback (most recent call last): +... +ValueError: 4 is not a valid Ship.DeckStatus +>>> s2 = Ship(2) +>>> s2._cells = [1, 2] +>>> s2._cells +[1, 2] +>>> s._cells = [1, 2] +Traceback (most recent call last): +... +ValueError: _cells_ must be 1 elements long +>>> s[0] = 1 +>>> s._is_move +True +>>> s[0] = 2 +>>> s._is_move +False +>>> s.get_start_coords() +(None, None) +>>> s.set_start_coords(2, 3) +>>> s.get_start_coords() +(2, 3) + +# --- + + Список _cells будет сигнализировать о попадании соперником в какую-либо палубу корабля. Если стоит 1, то попадания не было, а если стоит значение 2, то произошло попадание в соответствующую палубу. При попадании в корабль (хотя бы одну его палубу), флаг _is_move устанавливается в False и перемещение корабля по игровому полю прекращается. @@ -66,7 +112,9 @@ ship[indx] = value # запись нового значения в коллек Класс GamePole Следующий класс GamePole должен обеспечивать работу с игровым полем. Объекты этого класса создаются командой: -pole = GamePole(size) +>>> pole = GamePole(10) +>>> pole = GamePole() + где size - размеры игрового поля (обычно, size = 10). В каждом объекте этого класса должны формироваться локальные атрибуты: @@ -119,6 +167,121 @@ P.S. Для самых преданных поклонников програм Сыграйте в эту игру и выиграйте у компьютера. """ +from typing import Optional +from enum import Enum +from functools import total_ordering + +@total_ordering +class EnumOrdering(Enum): + def __eq__(self, other): + 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 + + def __set_name__(self, owner, name): + self.name_orig = name + self.name = name + "_" + + def __str__(self): + return self.name_orig + + def __get__(self, instance, owner): + if instance is None: + return self + return getattr(instance, self.name).value + + def __set__(self, instance, value): + setattr(instance, self.name, self.enum_cls(value)) + + +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)) + 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 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) + + def __init__(self, length: int, tp: int = 1, x: Optional[int] = None, y: Optional[int] = None, 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) + + 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 + + +class GamePole: + def __init__(self, size: int = 10): + ... + + def tests(): code = ( @@ -169,4 +332,7 @@ if __name__ == "__main__": import doctest doctest.testmod() + ############################################################################################################################## + exit(0) + ############################################################################################################################## tests()