Created
June 25, 2020 19:22
-
-
Save Hemie143/61d43aad475763ac6197ca257402fdec to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from string import digits | |
import random | |
class Mesh: | |
def __init__(self, state=" " * 9): | |
self.state = list(state) | |
self.turn = "O" if abs(state.count("X") - state.count("O")) == 1 else "X" | |
self.status = "Game not finished" | |
self.start() | |
def start(self): | |
player_types = ['easy', 'medium', 'hard', 'user'] | |
while True: | |
conditions = input().split() | |
if conditions[0] not in ['exit', 'start']: | |
print('Bad parameters!') | |
continue | |
if conditions[0] == 'exit': | |
exit() | |
if conditions[0] == 'start': | |
if len(conditions) != 3 or any(i not in player_types for i in conditions[1:3]): | |
print('Bad parameters!') | |
continue | |
self.players = {"X": self.get_player(conditions[1]), "O": self.get_player(conditions[2])} | |
self.players['X'].turn = 'X' | |
self.players['O'].turn = 'O' | |
break | |
def __str__(self): | |
str_ = "-" * 9 + '\n' | |
for row in [list(self.state[i * 3:(i + 1) * 3]) for i in range(3)]: | |
str_ += f"| {' '.join(row)} |\n" | |
str_ += "-" * 9 | |
return str_ | |
def get_player(self, player_type): | |
if player_type == "user": | |
return User(self) | |
elif player_type == "easy": | |
return Easy(self) | |
elif player_type == "medium": | |
return Medium(self) | |
elif player_type == "hard": | |
return Hard(self) | |
@staticmethod | |
def get_winner(state): | |
str_ = '' | |
for letter in "XO": | |
for row in [[state[x] for x in row] for row in Mesh.get_rows()]: | |
if row.count(letter) == 3: | |
str_ += letter | |
return str_ | |
@staticmethod | |
def get_rows(): | |
t = [int(i) for i in digits[:-1]] | |
return [t[0: 3], t[3: 6], t[6: 9], t[0: 9: 3], t[1: 9: 3], t[2: 9: 3], [0, 4, 8], | |
[2, 4, 6]] | |
@staticmethod | |
def check_draw(state): | |
return ' ' not in state | |
@staticmethod | |
def get_empty(state): | |
empty_pos = [] | |
for i in range(9): | |
if state[i] == ' ': | |
empty_pos.append(i) | |
return empty_pos | |
def check(self): | |
if Mesh.check_draw(self.state): | |
self.status = "Draw" | |
winner = Mesh.get_winner(self.state) | |
if len(winner) == 1: | |
self.status = f"{winner} wins" | |
if len(winner) > 1 or abs(self.state.count("X") - self.state.count("O")) > 1: | |
self.status = "Impossible" | |
if self.status != "Game not finished": | |
print(self.status) | |
return self.status != "Game not finished" | |
def play(self): | |
print(self) | |
while True: | |
self.players[self.turn].move() | |
print(self) | |
if self.check(): | |
break | |
class Player: | |
def __init__(self, mesh): | |
self.mesh = mesh | |
def make_move(self, x): | |
self.mesh.state[x] = self.mesh.turn | |
self.mesh.turn = "X" if self.turn == "O" else "O" | |
class User(Player): | |
def move(self): | |
while True: | |
coords = input("Enter the coordinates:").split()[:2] | |
if [x for x in coords if x not in digits] or not coords: | |
print("You should enter numbers!") | |
continue | |
if [x for x in coords if x not in "123"]: | |
print("Coordinates should be from 1 to 3!") | |
continue | |
i = int(coords[0]) - 1 + 3 * (int(coords[1]) - 1) | |
if self.mesh.state[i] == ' ': | |
self.make_move(i) | |
break | |
else: | |
print("This cell is occupied! Choose another one!") | |
class Easy(Player): | |
def __init__(self, mesh): | |
super(Easy, self).__init__(mesh) | |
self.difficulty = 'easy' | |
def move_random(self): | |
self.make_move(random.choice(Mesh.get_empty(self.mesh.state))) | |
def move(self): | |
print(f'Making move level "{self.difficulty}"') | |
self.move_random() | |
class Medium(Easy): | |
def __init__(self, mesh): | |
super(Medium, self).__init__(mesh) | |
self.difficulty = 'medium' | |
def fihish_him(self): | |
for row in Mesh.get_rows(): | |
act_row = [self.mesh.state[i] for i in row] | |
if act_row.count(" ") == 1 and (act_row.count("X") == 2 or act_row.count("O") == 2): | |
self.make_move(row[act_row.index(" ")]) | |
return True | |
def move(self): | |
print(f'Making move level "{self.difficulty}"') | |
if self.fihish_him(): | |
return | |
super().move_random() | |
class Hard(Player): | |
depth = 100 | |
@staticmethod | |
def minimax(new_state, player, ai, depth=0): | |
op = 'O' if ai == 'X' else 'X' | |
# available spots | |
avail_spots = Mesh.get_empty(new_state) | |
# checks for the terminal states such as win, lose, and tie and returning a value accordingly | |
win = Mesh.get_winner(new_state) | |
if win == op: | |
return {'score': -10} | |
elif win == ai: | |
return {'score': 10} | |
elif Mesh.check_draw(new_state) or depth >= Hard.depth: | |
return {'score': 0} | |
# an array to collect all the objects | |
moves = [] | |
for i in range(len(avail_spots)): | |
# create an object for each and store the index of that spot that was stored as a number in the object's index key | |
move = dict() | |
move['index'] = avail_spots[i] | |
# set the empty spot to the current player | |
new_state[avail_spots[i]] = player | |
# if collect the score resulted from calling minimax on the opponent of the current player | |
if player == ai: | |
result = Hard.minimax(new_state, op, ai, depth+1) | |
move['score'] = result['score'] | |
else: | |
result = Hard.minimax(new_state, ai, ai, depth+1) | |
move['score'] = result['score'] | |
# reset the spot to empty | |
new_state[avail_spots[i]] = ' ' | |
# push the object to the array | |
moves.append(move) | |
# if it is the computer's turn loop over the moves and choose the move with the highest score | |
if player == ai: | |
best_move = max(moves, key=lambda x: x['score']) | |
else: | |
# else loop over the moves and choose the move with the lowest score | |
best_move = min(moves, key=lambda x: x['score']) | |
return best_move | |
def move(self): | |
print('Making move level "hard"') | |
mesh_copy = self.mesh.state.copy() | |
self.make_move(Hard.minimax(mesh_copy, self.turn, self.turn,0)['index']) | |
if __name__ == "__main__": | |
mesh1 = Mesh() | |
mesh1.play() | |
#print(Mesh.get_rows()) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment