Ce script fait partie des projets Python de HyperSkill.
j'ai fait toute la formation python de Hyperskill pendant le Confinement 2020.
| # write your code here | |
| import random | |
| from math import inf | |
| class Move: | |
| def __init__(self): | |
| self._index = None | |
| self._score = None | |
| @property | |
| def index(self): | |
| return self._index | |
| @property | |
| def score(self): | |
| return self._score | |
| @index.setter | |
| def index(self, value): | |
| self._index = value | |
| @score.setter | |
| def score(self, value): | |
| self._score = value | |
| class Minimax: | |
| def __init__(self): | |
| self.player = "X" | |
| self.ia = "O" | |
| self.iter = 0 | |
| @staticmethod | |
| def available(board): | |
| return [i for i, _ in enumerate(board) if _ == " "] | |
| @staticmethod | |
| def winning(board, player): | |
| if any([(board[0] == player and board[1] == player and board[2] == player) or | |
| (board[3] == player and board[4] == player and board[5] == player) or | |
| (board[6] == player and board[7] == player and board[8] == player) or | |
| (board[0] == player and board[3] == player and board[6] == player) or | |
| (board[1] == player and board[4] == player and board[7] == player) or | |
| (board[2] == player and board[5] == player and board[8] == player) or | |
| (board[0] == player and board[4] == player and board[8] == player) or | |
| (board[2] == player and board[4] == player and board[6] == player)]): | |
| return True | |
| else: | |
| return False | |
| def minimax(self, board, player): | |
| available = self.available(board) | |
| if self.winning(board, "X"): | |
| return -10 | |
| elif self.winning(board, "O"): | |
| return 10 | |
| elif len(available) == 0: | |
| return 0 | |
| moves = [] | |
| for i in range(len(available)): | |
| move = Move() | |
| move.index = available[i] | |
| board[available[i]] = player | |
| if player == self.ia: | |
| g = self.minimax(board, self.player) | |
| else: | |
| g = self.minimax(board, self.ia) | |
| move.score = g if type(g) == int else g.score | |
| board[available[i]] = move.index | |
| moves.append(move) | |
| best_move = None | |
| if player == self.ia: | |
| best_score = -inf | |
| for i in range(len(moves)): | |
| if moves[i].score > best_score: | |
| best_score = moves[i].score | |
| best_move = i | |
| else: | |
| best_score = +inf | |
| for i in range(len(moves)): | |
| if moves[i].score < best_score: | |
| best_score = moves[i].score | |
| best_move = i | |
| return moves[best_move] | |
| class TicTacToe: | |
| def __init__(self): | |
| self.raw_cells = " " | |
| self.char = {0: "X", 1: "O"} | |
| self.players = {} | |
| self.turn = 0 | |
| self.cells = [self.raw_cells[i:i + 3] for i in range(0, len(self.raw_cells), 3)] | |
| self.mini_cells = list(self.raw_cells) | |
| self.vertical = [[self.cells[i][r] for i in range(3)] for r in range(3)] | |
| self.cells_dict = {k: v for k, *v in zip([3, 2, 1], *self.vertical)} | |
| self.bot_cells = {0: 3, 1: 2, 2: 1} | |
| self.bot_diag_cells = {3: 1, 2: 2, 1: 3} | |
| self.numbers = [0, 1, 2], [2, 1, 0] | |
| self.minimax = Minimax().minimax | |
| for _ in self.cells_dict: | |
| self.cells_dict[_] = dict(zip(range(1, len(self.cells_dict[_]) + 1), self.cells_dict[_])) | |
| def update(self, data, bot=False): | |
| if bot: | |
| self.raw_cells = data | |
| else: | |
| self.raw_cells = ''.join([data[i][x] for i in data for x in data[i]]) | |
| self.cells = [self.raw_cells[i:i + 3] for i in range(0, len(self.raw_cells), 3)] | |
| self.vertical = [[self.cells[i][r] for i in range(3)] for r in range(3)] | |
| self.cells_dict = {k: v for k, *v in zip([3, 2, 1], *self.vertical)} | |
| self.mini_cells = list(self.raw_cells) | |
| for _ in self.cells_dict: | |
| self.cells_dict[_] = dict(zip(range(1, len(self.cells_dict[_]) + 1), self.cells_dict[_])) | |
| @staticmethod | |
| def check(data): | |
| check = [list(x)[0] for x in [set(data[i]) for i in range(3)] if len(x) == 1 and list(x)[0] != " "] | |
| if len(check) == 0: | |
| return False | |
| if len(check) == 1: | |
| return check[0] | |
| return False | |
| def check_horizontal(self): | |
| return self.check(self.cells) | |
| def check_vertical(self): | |
| return self.check(self.vertical) | |
| def check_diagonal(self): | |
| diagonal_cells = [[self.cells[i][i] for i in self.numbers[0]], | |
| [self.cells[i][self.numbers[1][i]] for i in self.numbers[0]]] | |
| check = [set(diagonal_cells[i]) for i in range(2)] | |
| if any(True if i == {' '} else False for i in list(check)): | |
| return False | |
| if any([True if len(set(diagonal_cells[i])) == 1 else False for i in range(2)]): | |
| return [r.pop() for r in check if len(r) == 1] | |
| return False | |
| def check_victory(self): | |
| hor_check = self.check_horizontal() | |
| if hor_check and len(hor_check) == 1 and hor_check[0] != "_": | |
| return f"{hor_check} wins" | |
| ver_check = self.check_vertical() | |
| if ver_check: | |
| return f"{ver_check} wins" | |
| dia_check = self.check_diagonal() | |
| if dia_check and len(dia_check) == 1 and dia_check[0] != "_": | |
| return f"{dia_check[0]} wins" | |
| if all([set(self.raw_cells) == {'X', 'O'}, | |
| abs(self.raw_cells.count("X") - self.raw_cells.count("O")) == 1]): | |
| return "Draw" | |
| if any([len(hor_check) > 1 if type(hor_check) is not bool else False, | |
| len(ver_check) > 1 if type(ver_check) is not bool else False, | |
| len(dia_check) > 1 if type(dia_check) is not bool else False]): | |
| return "Impossible" | |
| if abs(self.raw_cells.count("X") - self.raw_cells.count("O")) > 1: | |
| return "Impossible" | |
| return False | |
| def show_table(self): | |
| print("---------\n" | |
| "| {0} {1} {2} |\n" | |
| "| {3} {4} {5} |\n" | |
| "| {6} {7} {8} |\n" | |
| "---------".format(*self.raw_cells)) | |
| def ask_player(self): | |
| raw_coordinates = input("Enter the coordinates: ").split(" ") | |
| if len(raw_coordinates) < 2 or not raw_coordinates[0].isdigit() and not raw_coordinates[1].isdigit(): | |
| print("You should enter numbers!") | |
| return self.ask_player() | |
| if not all([1 <= int(raw_coordinates[0]) <= 3, 1 <= int(raw_coordinates[1]) <= 3]): | |
| print("Coordinates should be from 1 to 3!") | |
| return self.ask_player() | |
| coordinates = [int(raw_coordinates[0]), int(raw_coordinates[1])] | |
| self.add_entry(*coordinates) | |
| def add_entry(self, x, y): | |
| if self.cells_dict[y][x] != " ": | |
| print("This cell is occupied! Choose another one!") | |
| return self.ask_player() | |
| self.cells_dict[y][x] = self.char[self.turn] | |
| self.turn = int(not self.turn) | |
| self.update(self.cells_dict) | |
| def bot_easy_play(self): | |
| bot_x = random.choice([i for i, x in enumerate(self.raw_cells) if x == " "]) | |
| data = list(self.raw_cells) | |
| data[bot_x] = self.char[self.turn] | |
| self.turn = int(not self.turn) | |
| self.update(data, True) | |
| def bot_medium_play(self): | |
| horizontal = [x for x in range(len(self.cells)) if " " in set(self.cells[x]) and | |
| (self.cells[x].count("X") == 2 or self.cells[x].count("O") == 2)] | |
| verticals_cells = [[self.vertical[i][0], self.vertical[i][1], self.vertical[i][2]] for i in range(3)] | |
| vertical = [x for x in range(len(verticals_cells)) if " " in set(verticals_cells[x]) and | |
| (verticals_cells[x].count("X") == 2 or verticals_cells[x].count("O") == 2)] | |
| diagonal_cells = ["".join([self.cells[i][i] for i in self.numbers[0]]), | |
| "".join([self.cells[i][self.numbers[1][i]] for i in self.numbers[0]])] | |
| diagonal = [x for x in range(len(diagonal_cells)) if " " in set(diagonal_cells[x]) and | |
| (diagonal_cells[x].count("X") == 2 or diagonal_cells[x].count("O") == 2)] | |
| if horizontal: | |
| x = self.bot_cells[horizontal[0]] | |
| y = list(self.cells_dict[x].values()).index(" ") + 1 | |
| self.cells_dict[x][y] = self.char[self.turn] | |
| self.turn = int(not self.turn) | |
| self.update(self.cells_dict) | |
| elif vertical: | |
| cell = ["".join([self.vertical[i][0], self.vertical[i][1], self.vertical[i][2]]) for i in range(3)] | |
| cells = [[self.vertical[i][0], self.vertical[i][1], self.vertical[i][2]] for i in range(3)] | |
| x = vertical[0] | |
| if len(vertical) > 1: | |
| for i in vertical: | |
| if self.char[self.turn] in list(set(cell[i])): | |
| x = i | |
| y = cell[x].index(" ") | |
| cells[x][y] = self.char[self.turn] | |
| cells = "".join(["".join([cells[i][r] for i in range(3)]) for r in range(3)]) | |
| self.turn = int(not self.turn) | |
| self.update(cells, True) | |
| elif diagonal: | |
| y = diagonal_cells[diagonal[0]].index(" ") + 1 | |
| x = self.bot_diag_cells[y] | |
| if diagonal[0] == 0: | |
| self.cells_dict[x][y] = self.char[self.turn] | |
| self.turn = int(not self.turn) | |
| self.update(self.cells_dict) | |
| elif diagonal[0] == 1: | |
| y = self.bot_diag_cells[y] | |
| self.cells_dict[x][y] = self.char[self.turn] | |
| self.turn = int(not self.turn) | |
| self.update(self.cells_dict) | |
| else: | |
| self.bot_easy_play() | |
| def bot_hard_play(self): | |
| index = self.minimax(self.mini_cells, self.char[self.turn]).index | |
| self.mini_cells = list(self.raw_cells) | |
| self.mini_cells[index] = self.char[self.turn] | |
| self.turn = int(not self.turn) | |
| self.update(''.join(self.mini_cells), True) | |
| def game(self): | |
| self.update(list(" " * 9), True) | |
| self.turn = 0 | |
| while True: | |
| self.show_table() | |
| victory = self.check_victory() | |
| if victory: | |
| print(victory) | |
| break | |
| if self.players["player1"] == "user": | |
| self.ask_player() | |
| elif self.players["player1"] == "easy": | |
| print('Making move level "easy"') | |
| self.bot_easy_play() | |
| elif self.players["player1"] == "medium": | |
| print('Making move level "medium"') | |
| self.bot_medium_play() | |
| elif self.players["player1"] == "hard": | |
| print('Making move level "hard"') | |
| self.bot_hard_play() | |
| self.show_table() | |
| victory = self.check_victory() | |
| if victory: | |
| print(victory) | |
| break | |
| if self.players["player2"] == "user": | |
| self.ask_player() | |
| elif self.players["player2"] == "easy": | |
| print('Making move level "easy"') | |
| self.bot_easy_play() | |
| elif self.players["player2"] == "medium": | |
| print('Making move level "medium"') | |
| self.bot_medium_play() | |
| elif self.players["player2"] == "hard": | |
| print('Making move level "hard"') | |
| self.bot_hard_play() | |
| def run(self): | |
| while True: | |
| choice = input("Input command (help): ").split(" ") | |
| if len(choice) < 3: | |
| if choice[0] == "exit": | |
| exit(0) | |
| elif choice[0] == "help": | |
| print("help - bring this panel") | |
| print("exit - close the game") | |
| print("start {player1} {player2} - start a game with player1 or player2") | |
| print("start {player1} {player2} - start a game with player1 or player2") | |
| print("You can choose between several different players :") | |
| print("user -- a human player") | |
| print("easy -- a easy bot") | |
| print("medium -- a medium bot") | |
| print("hard -- a hard bot") | |
| elif choice[0] == "start": | |
| if choice[1] not in ["user", "easy", "medium", "hard"] or \ | |
| choice[2] not in ["user", "easy", "medium", "hard"]: | |
| print("ERROR: Unknown player type, please refer to `help`") | |
| else: | |
| self.players["player1"] = choice[1] | |
| self.players["player2"] = choice[2] | |
| self.game() | |
| else: | |
| print("Bad parameters!") | |
| TicTacToe().run() |
cyvax - 2026