Created
June 25, 2020 19:21
-
-
Save Hemie143/8856fe5205153f377914bb73a2acea32 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
import random | |
class Board: | |
def __init__(self): | |
self.field = list(EMPTY_CELL * 9) | |
# self.cells = set(range(9)) | |
def show_field(self): | |
print('---------') | |
for i in range(3): | |
print('|', self.field[3 * i], self.field[3 * i + 1], self.field[3 * i + 2], '|') | |
print('---------') | |
def isvalid(self, coordinates): | |
if is_valid_coordinates(coordinates): | |
return self.field[coordinate_change(*coordinates)] == EMPTY_CELL | |
def move(self, index, side): | |
self.field[index] = side | |
def judge(self, side): | |
for i, j, k in ROWS: | |
if self.field[i] == self.field[j] == self.field[k] == side: | |
return True | |
return False | |
class Player: | |
def __init__(self, side, level): | |
self.side = side | |
self.level = level | |
class HumanPlayer(Player): | |
def info_before_move(self): | |
pass | |
def get_index(self, board): | |
while True: | |
coordinates = input('Enter the coordinates: ').split() | |
if board.isvalid(coordinates): | |
return coordinate_change(*coordinates) | |
else: | |
invalid_message() | |
class AIPlayer(Player): | |
def info_before_move(self): | |
print('Making move level "{}"'.format(self.level)) | |
def get_random_index(self, board): | |
while True: | |
index = random.randint(0, 8) | |
if board.field[index] == EMPTY_CELL: | |
break | |
return index | |
def get_win_index(self, board): | |
for i, j, k in ROWS: | |
if [board.field[i], board.field[j], board.field[k]].count(self.side) == 2: | |
for index in [i, j, k]: | |
if board.field[index] == ' ': | |
return index | |
return -1 | |
def get_no_lose_index(self, board): | |
if self.side == 'X': | |
opponent_side = 'O' | |
else: | |
opponent_side = 'X' | |
for i, j, k in ROWS: | |
if [board.field[i], board.field[j], board.field[k]].count(opponent_side) == 2: | |
for index in [i, j, k]: | |
if board.field[index] == ' ': | |
return index | |
return -1 | |
def get_index_minimax(self, board): | |
score, index = minimax(board, self, self.side) | |
return index | |
class EasyPlayer(AIPlayer): | |
def get_index(self, board): | |
return self.get_random_index(board) | |
class MediumPlayer(AIPlayer): | |
def get_index(self, board): | |
index = self.get_win_index(board) | |
if index != -1: | |
return index | |
index = self.get_no_lose_index(board) | |
if index != -1: | |
return index | |
return self.get_random_index(board) | |
class HardPlayer(AIPlayer): | |
def get_index(self, board): | |
return self.get_index_minimax(board) | |
def is_valid_coordinates(coordinates): | |
valid_coordinates = ['1', '2', '3'] | |
for coordinate in coordinates: | |
if coordinate not in valid_coordinates: | |
return False | |
if len(coordinates) != 2: | |
return False | |
else: | |
return True | |
def invalid_message(): | |
print('Bad parameters!') | |
def coordinate_change(*coordinates): | |
return 8 + int(coordinates[0]) - 3 * int(coordinates[1]) | |
def minimax(board, player, side): | |
opponent_side = get_opponent_side(player.side) | |
if board.judge(player.side): | |
return 10, -1 | |
if board.judge(opponent_side): | |
return -10, -1 | |
if not any(board.field[i] == EMPTY_CELL for i in range(9)): | |
return 0, -1 | |
next_side = get_opponent_side(side) | |
moves = {} | |
for i in range(9): | |
if board.field[i] == EMPTY_CELL: | |
board_after_move = Board() | |
board_after_move.field = board.field[:] | |
board_after_move.field[i] = side | |
score, index = minimax(board_after_move, player, next_side) | |
moves[i] = score | |
best_index = -1 | |
if player.side == side: | |
best_score = -100 | |
for index, score in moves.items(): | |
if score > best_score: | |
best_score = score | |
best_index = index | |
else: | |
best_score = 100 | |
for index, score in moves.items(): | |
if score < best_score: | |
best_score = score | |
best_index = index | |
return best_score, best_index | |
def get_opponent_side(side): | |
if side == 'X': | |
return 'O' | |
if side == 'O': | |
return 'X' | |
return None | |
EMPTY_CELL = ' ' | |
SIDES = ['X', 'O'] | |
TYPES = ['user', 'easy', 'medium', 'hard'] | |
ROWS = [(0, 3, 6), (1, 4, 7), (2, 5, 8), | |
(0, 1, 2), (3, 4, 5), (6, 7, 8), | |
(0, 4, 8), (2, 4, 6)] | |
while True: | |
command = input('Input command: ') | |
if command == 'exit': | |
break | |
commands = command.split() | |
if len(commands) == 3 and commands[0] == 'start' \ | |
and commands[1] in TYPES and commands[2] in TYPES: | |
players = [None, None] | |
for i in range(2): | |
if commands[i + 1] == 'user': | |
players[i] = HumanPlayer(SIDES[i], commands[i + 1]) | |
elif commands[i + 1] == 'easy': | |
players[i] = EasyPlayer(SIDES[i], commands[i + 1]) | |
elif commands[i + 1] == 'medium': | |
players[i] = MediumPlayer(SIDES[i], commands[i + 1]) | |
else: | |
players[i] = HardPlayer(SIDES[i], commands[i + 1]) | |
new_board = Board() | |
new_board.show_field() | |
step = 0 | |
while step < 9: | |
players[step % 2].info_before_move() | |
new_board.move(players[step % 2].get_index(new_board), players[step % 2].side) | |
new_board.show_field() | |
if new_board.judge(players[step % 2].side): | |
print(players[step % 2].side, 'wins') | |
step = -1 | |
break | |
step += 1 | |
if step == 9: | |
print('Draw') | |
else: | |
invalid_message() | |
continue |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment