+ 3.x tictactoe (done)

This commit is contained in:
Dmitry Belyaev 2024-04-17 23:49:24 +03:00
parent 5906af0f69
commit 5145c20156

View File

@ -83,6 +83,11 @@ P.S.S. Домашнее задание: завершите создание эт
""" """
from enum import Enum from enum import Enum
import random
class GameOverException(Exception):
...
class Cell: class Cell:
@ -90,8 +95,8 @@ class Cell:
class State(Enum): class State(Enum):
FREE = 0 FREE = 0
HUMAN = 1 COMPUTER = 1
COMPUTER = 2 HUMAN = 2
@classmethod @classmethod
def wrap(cls, v): def wrap(cls, v):
@ -124,10 +129,10 @@ class Cell:
return self.is_free return self.is_free
def __repr__(self): def __repr__(self):
return f"{self.__class__.__name__}{(self.value)!r}" return f"{self.__class__.__name__}({self.value!r})"
def __str__(self): def __str__(self):
return self.CHARS[not self and self.value] return self.CHARS[self.value]
class TicTacToe: class TicTacToe:
@ -145,10 +150,13 @@ class TicTacToe:
def is_ended(self): def is_ended(self):
return self is not self.ACTIVE return self is not self.ACTIVE
def __init__(self, pole=None): def __init__(self, size=3, pole=None):
self.size = size
self.state = self.State.ACTIVE self.state = self.State.ACTIVE
if pole is None: if pole is None:
pole = tuple(tuple(Cell() for _ in range(3)) for _ in range(3)) pole = tuple(
tuple(Cell() for _ in range(self.size)) for _ in range(self.size)
)
self.pole = pole self.pole = pole
def init(self): def init(self):
@ -167,18 +175,18 @@ class TicTacToe:
for col in zip(*self.pole): for col in zip(*self.pole):
if all(cell.value == value for cell in col): if all(cell.value == value for cell in col):
return True return True
if all(self[i, i] == value for i in range(len(self.pole))): if all(self[i, i] == value for i in range(self.size)):
return True return True
if all(self[i, len(self.pole) - i + 1] == value for i in range(len(self.pole))): if all(self[i, self.size + ~i] == value for i in range(self.size)):
return True return True
def _update_state(self): def _update_state(self):
if not any(cell.is_free for row in self.pole for cell in row): if self._find_line(Cell.State.HUMAN.value):
self.state = self.State.DRAW
elif self._find_line(Cell.State.HUMAN.value):
self.state = self.State.HUMAN_WIN self.state = self.State.HUMAN_WIN
elif self._find_line(Cell.State.COMPUTER.value): elif self._find_line(Cell.State.COMPUTER.value):
self.state = self.State.COMPUTER_WIN self.state = self.State.COMPUTER_WIN
elif not any(cell.is_free for row in self.pole for cell in row):
self.state = self.State.DRAW
@property @property
def is_human_win(self): def is_human_win(self):
@ -193,54 +201,93 @@ class TicTacToe:
return self.state is self.State.DRAW return self.state is self.State.DRAW
def _get_key(self, key): def _get_key(self, key):
if not isinstance(key, tuple) or len(key) != 2: if (
not isinstance(key, tuple)
or len(key) != 2
or any(not isinstance(x, int) or x < 0 or x >= len(self.pole) for x in key)
):
raise IndexError("неверный индекс клетки") raise IndexError("неверный индекс клетки")
return tuple(x if isinstance(x, int) else None for x in key) return key
def __getitem__(self, key): def __getitem__(self, key):
key = self._get_key(key) row, col = 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 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): def __setitem__(self, key, value):
key = self._get_key(key) if not self:
row, col = key raise GameOverException("игра закончена")
if row is None and col is None:
for i, row in enumerate(value): row, col = self._get_key(key)
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] cell = self.pole[row][col]
if not cell.is_free: if not cell.is_free:
raise ValueError("клетка уже занята") raise ValueError("клетка уже занята")
cell.value = value cell.value = value
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
self._update_state() self._update_state()
def human_go(self): def human_go(self):
... pending = True
prompt = f"Введите координаты клетки через пробел (0-{self.size - 1}, сначала строка)\nДля выхода введите Q\nВаш ход: "
while pending:
answer = input(prompt)
if answer.lower() == "q":
print("Вы сдались!")
self.state = self.State.COMPUTER_WIN
pending = False
continue
try:
row, col = map(int, answer.split())
except ValueError:
print("Неверный формат ввода")
continue
try:
self[row, col] = Cell.State.HUMAN
pending = False
except IndexError:
print("Неверные координаты клетки")
except ValueError:
print("Клетка уже занята")
except GameOverException:
print("Эй, что происходит?!")
pending = False
def computer_go(self): def computer_go(self):
... free_cells = [
(i, j) for i in range(self.size) for j in range(self.size) if not self[i, j]
]
row, col = random.choice(free_cells)
print("Я выбрал клетку", row, col, end="!\n")
self[row, col] = Cell.State.COMPUTER
def show(self): def show(self):
print(self) print(self)
def play(self):
human_turn = True
turns = self.computer_go, self.human_go
while self:
if human_turn:
self.show()
turns[human_turn]()
human_turn = not human_turn
print("Игра окончена!")
self.show()
if self.is_human_win:
print("Вы выиграли!")
elif self.is_computer_win:
print("Ура! Я выиграл!")
elif self.is_draw:
print("Ничья!")
else:
print("Что-то пошло не так...")
def __len__(self):
return self.size
def __str__(self): def __str__(self):
c = 2 c = self.size - 1
result = ( result = (
f"╭─{'─┬─' * c}─╮\n" f"╭─{'─┬─' * c}─╮\n"
+ f"├─{'─┼─' * c}─┤\n".join( + f"├─{'─┼─' * c}─┤\n".join(
@ -251,17 +298,22 @@ class TicTacToe:
return result return result
def __repr__(self): def __repr__(self):
return f"{self.__class__.__name__}({self.pole!r})" args = []
if self.size != 3:
args.append(f"size={self.size!r}")
if sum(self[i, j] for i in range(self.size) for j in range(self.size)):
args.append(f"pole={self.pole!r}")
args = ", ".join(args)
return f"{self.__class__.__name__}({args})"
t = TicTacToe() import sys
t[1, 1] = 2
t[0, 1] = 1 argc = len(sys.argv)
t[0, 0] = 2 if argc > 1 and sys.argv[1] == "play":
t[2, 1] = 1 game = TicTacToe(argc > 2 and int(sys.argv[2]) or 3)
t[2, 2] = 2 game.play()
t.show() exit()
print(t.is_computer_win, t.is_human_win, t.is_draw)
def tests(): def tests():