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 - 2024