From 5145c201563c168fa4299e0adee5944d6029ff8b Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 17 Apr 2024 23:49:24 +0300 Subject: [PATCH] + 3.x tictactoe (done) --- mod_oop/3.x_01_tictactoe.py | 158 ++++++++++++++++++++++++------------ 1 file changed, 105 insertions(+), 53 deletions(-) diff --git a/mod_oop/3.x_01_tictactoe.py b/mod_oop/3.x_01_tictactoe.py index 819ce0c..404cb40 100644 --- a/mod_oop/3.x_01_tictactoe.py +++ b/mod_oop/3.x_01_tictactoe.py @@ -83,6 +83,11 @@ P.S.S. Домашнее задание: завершите создание эт """ from enum import Enum +import random + + +class GameOverException(Exception): + ... class Cell: @@ -90,8 +95,8 @@ class Cell: class State(Enum): FREE = 0 - HUMAN = 1 - COMPUTER = 2 + COMPUTER = 1 + HUMAN = 2 @classmethod def wrap(cls, v): @@ -124,10 +129,10 @@ class Cell: return self.is_free def __repr__(self): - return f"{self.__class__.__name__}{(self.value)!r}" + return f"{self.__class__.__name__}({self.value!r})" def __str__(self): - return self.CHARS[not self and self.value] + return self.CHARS[self.value] class TicTacToe: @@ -145,10 +150,13 @@ class TicTacToe: def is_ended(self): 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 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 def init(self): @@ -167,18 +175,18 @@ class TicTacToe: for col in zip(*self.pole): if all(cell.value == value for cell in col): 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 - 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 def _update_state(self): - if not any(cell.is_free for row in self.pole for cell in row): - self.state = self.State.DRAW - elif self._find_line(Cell.State.HUMAN.value): + if self._find_line(Cell.State.HUMAN.value): self.state = self.State.HUMAN_WIN elif self._find_line(Cell.State.COMPUTER.value): 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 def is_human_win(self): @@ -193,54 +201,93 @@ class TicTacToe: return self.state is self.State.DRAW 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("неверный индекс клетки") - return tuple(x if isinstance(x, int) else None for x in key) + return 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))) + row, col = self._get_key(key) + return self.pole[row][col].value 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 - 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 + if not self: + raise GameOverException("игра закончена") + + row, col = self._get_key(key) + cell = self.pole[row][col] + if not cell.is_free: + raise ValueError("клетка уже занята") + + cell.value = value self._update_state() 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): - ... + 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): 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): - c = 2 + c = self.size - 1 result = ( f"╭─{'─┬─' * c}─╮\n" + f"├─{'─┼─' * c}─┤\n".join( @@ -251,17 +298,22 @@ class TicTacToe: return result 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() -t[1, 1] = 2 -t[0, 1] = 1 -t[0, 0] = 2 -t[2, 1] = 1 -t[2, 2] = 2 -t.show() -print(t.is_computer_win, t.is_human_win, t.is_draw) +import sys + +argc = len(sys.argv) +if argc > 1 and sys.argv[1] == "play": + game = TicTacToe(argc > 2 and int(sys.argv[2]) or 3) + game.play() + exit() def tests():