170 lines
8.5 KiB
Python
170 lines
8.5 KiB
Python
|
"""
|
|||
|
https://stepik.org/lesson/701993/step/10?unit=702094
|
|||
|
|
|||
|
Вам нужно реализовать в программе игровое поле для игры "Крестики-нолики".
|
|||
|
|
|||
|
Для этого требуется объявить класс TicTacToe (крестики-нолики), объекты которого создаются командой:
|
|||
|
game = TicTacToe()
|
|||
|
|
|||
|
Каждый объект game должен иметь публичный атрибут:
|
|||
|
pole - игровое поле: кортеж размером 3х3 с объектами класса Cell.
|
|||
|
|
|||
|
Каждая клетка игрового поля представляется объектом класса Cell и создается командой:
|
|||
|
cell = Cell()
|
|||
|
|
|||
|
Объекты класса Cell должны иметь следующие публичные локальные атрибуты:
|
|||
|
is_free - True, если клетка свободна; False в противном случае;
|
|||
|
value - значение поля: 1 - крестик; 2 - нолик (по умолчанию 0).
|
|||
|
|
|||
|
Также с каждым объектом класса Cell должна работать функция:
|
|||
|
bool(cell)
|
|||
|
которая возвращает True, если клетка свободна (cell.is_free=True) и False в противном случае.
|
|||
|
|
|||
|
Класс TicTacToe должен иметь следующий метод:
|
|||
|
clear() - очистка игрового поля (все клетки заполняются нулями и переводятся в закрытое состояние);
|
|||
|
|
|||
|
А объекты этого класса должны иметь следующую функциональность (обращение по индексам):
|
|||
|
game[0, 0] = 1 # установка нового значения, если поле закрыто
|
|||
|
res = game[1, 1] # получение значения центральной ячейки поля (возвращается число)
|
|||
|
|
|||
|
Если указываются некорректные индексы, то должно генерироваться исключение командой:
|
|||
|
raise IndexError('неверный индекс клетки')
|
|||
|
|
|||
|
Если идет попытка присвоить новое значение в открытую клетку поля, то генерировать исключение:
|
|||
|
raise ValueError('клетка уже занята')
|
|||
|
|
|||
|
Также должны быть реализованы следующие полные срезы при обращении к клеткам игрового поля:
|
|||
|
slice_1 = game[:, indx] # выбираются все элементы (кортеж) столбца с индексом indx
|
|||
|
slice_2 = game[indx, :] # выбираются все элементы (кортеж) строки с индексом indx
|
|||
|
|
|||
|
Пример использования классов (эти строчки в программе не писать):
|
|||
|
game = TicTacToe()
|
|||
|
game.clear()
|
|||
|
game[0, 0] = 1
|
|||
|
game[1, 0] = 2
|
|||
|
# формируется поле:
|
|||
|
# 1 0 0
|
|||
|
# 2 0 0
|
|||
|
# 0 0 0
|
|||
|
game[3, 2] = 2 # генерируется исключение IndexError
|
|||
|
if game[0, 0] == 0:
|
|||
|
game[0, 0] = 2
|
|||
|
v1 = game[0, :] # 1, 0, 0
|
|||
|
v2 = game[:, 0] # 1, 2, 0
|
|||
|
|
|||
|
P.S. В программе нужно объявить только классы. Выводить на экран ничего не нужно.
|
|||
|
P.P.S. При передаче среза в магических методах __setitem__() и __getitem__() параметр индекса становится объектом класса slice.
|
|||
|
Его можно указывать непосредственно в квадратных скобках упорядоченных коллекций (списков, кортежей и т.п.).
|
|||
|
"""
|
|||
|
|
|||
|
|
|||
|
class Cell:
|
|||
|
CHARS = " OX"
|
|||
|
|
|||
|
def __init__(self, value=0, is_free=True):
|
|||
|
self.value, self.is_free = value, is_free
|
|||
|
|
|||
|
def __bool__(self):
|
|||
|
return self.is_free
|
|||
|
|
|||
|
def __repr__(self):
|
|||
|
return f"{self.__class__.__name__}{(self.value, self.is_free)!r}"
|
|||
|
|
|||
|
def __str__(self):
|
|||
|
return self.CHARS[not self and self.value]
|
|||
|
|
|||
|
|
|||
|
class TicTacToe:
|
|||
|
def __init__(self, pole=None):
|
|||
|
if pole is None:
|
|||
|
pole = tuple(tuple(Cell() for _ in range(3)) for _ in range(3))
|
|||
|
self.pole = pole
|
|||
|
|
|||
|
def clear(self):
|
|||
|
for row in self.pole:
|
|||
|
for cell in row:
|
|||
|
cell.is_free = True
|
|||
|
cell.value = 0
|
|||
|
|
|||
|
def _get_key(self, key):
|
|||
|
if not isinstance(key, tuple) or len(key) != 2:
|
|||
|
raise IndexError("неверный индекс клетки")
|
|||
|
return tuple(x if isinstance(x, int) else None for x in key)
|
|||
|
|
|||
|
def __getitem__(self, key):
|
|||
|
key = self._get_key(key)
|
|||
|
row, col = key
|
|||
|
if row is None and col is None:
|
|||
|
return tuple(tuple(c.value for c in r) for r in self.pole)
|
|||
|
if row is not None and col is not None:
|
|||
|
return self.pole[row][col].value
|
|||
|
if row is not None and col is None:
|
|||
|
return tuple(x.value for x in self.pole[row])
|
|||
|
return tuple(self.pole[row][col].value for row in range(len(self.pole)))
|
|||
|
|
|||
|
def __setitem__(self, key, value):
|
|||
|
key = self._get_key(key)
|
|||
|
row, col = key
|
|||
|
if row is None and col is None:
|
|||
|
for i, row in enumerate(value):
|
|||
|
for j, v in enumerate(row):
|
|||
|
self[i, j] = v
|
|||
|
return
|
|||
|
if row is not None and col is not None:
|
|||
|
cell = self.pole[row][col]
|
|||
|
if not cell.is_free:
|
|||
|
raise ValueError("клетка уже занята")
|
|||
|
cell.value = value
|
|||
|
cell.is_free = False
|
|||
|
return
|
|||
|
if row is not None and col is None:
|
|||
|
for j, v in enumerate(value):
|
|||
|
self[row, j] = v
|
|||
|
return
|
|||
|
for i, v in enumerate(value):
|
|||
|
self[i, col] = v
|
|||
|
|
|||
|
def __repr__(self):
|
|||
|
return f"{self.__class__.__name__}({self.pole!r})"
|
|||
|
|
|||
|
def __str__(self):
|
|||
|
return "\n".join(map(lambda r: " ".join(map(str, r)), self.pole))
|
|||
|
|
|||
|
|
|||
|
def tests():
|
|||
|
code = (
|
|||
|
b"XCOTwRB2;WVPjNpWhf~MXD(xGWnpqCDGFh8b7gXLAZJ@JEFdslAU!=GFd$)WWFTi-GAtl6T_8O"
|
|||
|
+ b"@ATTT-BGA3iu+fLmu+Y2FjL^N&i_o<o(6`XN(6G^m(6!LL(74f$AkebWfzY+lg&@$n(7VvJ(Sp#v"
|
|||
|
+ b"(7GVdw9vlLyU@1Kz0r#x(6P~r(Sp&8Akl!(u+Xy5z0r#xFd_<PTQMvkF<l@%ATbJOTQV#lF<l@%A"
|
|||
|
+ b"TkPJb8}^KbRcJ2F)Sc4T_8O@ATc0eZe$>5TQV#lF<l@%Js>hHAR^Gc(6!LA(6!Nk(7n*UAke<ig3"
|
|||
|
+ b"*A`u+Xv4zR`lvu+Y2EuprRB(7({N(SXpf(T32t(T^a|ztMouxY2>ove2;5xX`lDu+Y8GxY3Uw(7n"
|
|||
|
+ b"*U(6Z5s(S;z;x6r-Nu+fLmwa~rLxX`&E(7MpO(6!Nm(7MpD(7YhfxX`oFfY83sve3TJv(UaE(7({"
|
|||
|
+ b"W(7Vx(ASlqd(7VvMEFjRm(6!LI(7w@t(SXpk(7Mrr(7n*UAkl!(u+Xv4zR`lvu+X*9f*{d>(TC8u"
|
|||
|
+ b"(Sp&7(6Z35(7n*O(6u1Yx6r-Nu+fLmwa~rLxX`&NA_@w0a(OxmARr(ha%FQMJs@XWGb|u5T?%D*V"
|
|||
|
+ b"`Xr3AW3dyWq3t$a&K}v3LqdLAYpTJWpZ>NRC0A?3T13_WjYEVARr)Nb8}^KbRb4yY;$ESAR^Gc(6"
|
|||
|
+ b"u1ZfzY$iwa~rLwb6jkxY2;nzR<GJu+Y2EzR`ivj3Cgs(Sgvq(7Vx&(TC8r(7n*O(6t~*Ze(S6MRI"
|
|||
|
+ b"a)av;#Z(SXpnAkl%*htRmug3*i6ve2;5z0kPOxFFED(6=Daz0kGMfzgA}iO{vtfzg7{veARlj?sz"
|
|||
|
+ b"Gwa~dB(T~xG(6!LH(7MpLA_@v1ARr(LbaHt*3LqdLAZJ@MEFdslAUz;83T1d>WpH#LNp56ictvt@"
|
|||
|
+ b"Z*n>cARr(hVRLh3a&#b6a&=`2Wo&b0Itm~lARu9Lb7gXLAVy(qb7d?bBGA3iwII=f(6i9B(7n*L("
|
|||
|
+ b"SXpn(SXpt(6Z35(7VvS(Sgy7AketcfzZ0pyU~u(htRdqz0kPOwIE4uWMz0oa&m8SAke?jfY7)g(6"
|
|||
|
+ b"`XA(7({Q(SgvoAkeZP(7n*L(Sgx}(TUKt(Sgx|(6Z5k(T>rH(Sy;BAkmM}htRdqxzM`NgCYtF3TG"
|
|||
|
+ b"~3Y-M3`C@BhOTQDpjFkK)$ATbJOTQMvkFkK)$ATkPPTQV#lFkK)$ATtUIVRLh3a&#bPTQDpjI$a<"
|
|||
|
+ b"=Js>DCEFdr}ATTK)VQyp~XIn8WAUa(jJv|^OGAtl4EFdr`AYpD~AZJ@TEFdslAU!=GC^0M`GAtl7"
|
|||
|
+ b"DJ&o&(7n*L(7MpR(SXr_(6!LI(Sp#u(7qthzR`lwfY7kevCzKJg3z$gyU@5G(Sgx`(6!LF(TgC^z"
|
|||
|
+ b"tFzXfzZ3qwII;4(TmWx(7w>J(6AuTywJ7Lg3!Luw9v31V{Bz%awsVv(6}JbztMouxY2>ove2;5xX"
|
|||
|
+ b"`lDu+Y8GxY3Uw(7n*U(6Z5s(S;z;x6r-Nu+fLmwa~rLxX`&G3JPOoY-}JsAVXzrY$z!TVRLh3a&#"
|
|||
|
+ b"bLWo&FNc42IFWgtC0ATTT-BGA3iu+fLmu+Y2FjL^N%zR<NG(6`XN(6G^m(6!LL(74dGAkeVUg3*A"
|
|||
|
+ b"`xX`iDgVBP}upo9}Y;|QI(7MpO(6G^g(SgvgAVXzrY#`9I(7w>S(6-RM(7qthvC)gsg3*j1(SXpf"
|
|||
|
+ b"(6Z3I(7qrrA_{V4b09q+V`Xe?E@^XLW^!d^3S(t#Y%XbYUuJS;WgtBuRC0A?3So0|WpZ>NVsCG3C"
|
|||
|
+ b"}U-8Y$+@tBGH7=gV4Ruy3vNvxY3UwVsCG3AkebVwb6jkz0rfvyU?&8MqzAoWgyVB(7Vx(Akl%)ve"
|
|||
|
+ b"3TJvCzKIw9viKzR<ZK(7MpO(6!Nm(7MpLA^"
|
|||
|
)
|
|||
|
exec(__import__("base64").b85decode(code))
|
|||
|
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
import doctest
|
|||
|
|
|||
|
doctest.testmod()
|
|||
|
tests()
|