Skip to content

Instantly share code, notes, and snippets.

@Hemie143
Created June 25, 2020 19:22
Show Gist options
  • Save Hemie143/61d43aad475763ac6197ca257402fdec to your computer and use it in GitHub Desktop.
Save Hemie143/61d43aad475763ac6197ca257402fdec to your computer and use it in GitHub Desktop.
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