В текущей реализации шашек на tkinter, несмотря на то, что вроде как вынес просчёт ходов в отдельный поток, тормозит интерфейс при вычислении компьютером оптимального хода (при средней глубине просчёта).
Вот ключевые методы:
def mouse_down(self, event: Event):
'''Событие нажатия мышки'''
x, y = (event.x - BOARD_BORDER) // CELL_SIZE, (event.y - BOARD_BORDER) // CELL_SIZE
# Если точка не внутри поля
if not (self.__field.is_within(x, y)): return
if MULTIPLAYER:
...
else:
if not (self.__player_turn): return
if (PLAYER_SIDE == SideType.WHITE):
player_checkers = WHITE_CHECKERS
elif (PLAYER_SIDE == SideType.BLACK):
player_checkers = BLACK_CHECKERS
else:
return
# Если нажатие по шашке игрока, то выбрать её
if (self.__field.type_at(x, y) in player_checkers):
self.__selected_cell = Point(x, y)
self.__draw()
elif (self.__player_turn):
move = Move(self.__selected_cell.x, self.__selected_cell.y, x, y)
# Если нажатие по ячейке, на которую можно походить
if (move in self.__get_moves_list(PLAYER_SIDE)):
self.__handle_player_turn(move)
# Если не ход игрока, то ход противника
if not (self.__player_turn):
self.test_field = self.__field
self.handle_enemy_turn_async()
...
def handle_enemy_turn_async(game_instance):
def run():
game_instance.__handle_enemy_turn()
threading.Thread(target=run).start()
def __handle_enemy_turn(self):
'''Обработка хода противника (компьютера)'''
self.__player_turn = False
self.calc = True
optimal_moves_list = self.__predict_optimal_moves(SideType.opposite(PLAYER_SIDE))
self.calc = False
for move in optimal_moves_list:
self.__handle_move(self.__field, move)
self.__player_turn = True
self.__check_for_game_over()
def __predict_optimal_moves(self, side: SideType) -> list[Move]:
'''Предсказать оптимальный ход'''
best_result = 0
optimal_moves = []
self.test_field = Field.copy(self.__field)
predicted_moves_list = self.__get_predicted_moves_list(side)
if (predicted_moves_list):
field_copy = Field.copy(self.__field)
self.test_field = Field.copy(self.__field)
field_copy_test = Field.copy(self.test_field)
for moves in predicted_moves_list:
for move in moves:
self.__handle_move(self.test_field, move, draw=False)
try:
if (side == SideType.WHITE):
result = self.test_field.white_score / self.test_field.black_score
elif (side == SideType.BLACK):
result = self.test_field.black_score / self.test_field.white_score
except ZeroDivisionError:
result = inf
if (result > best_result):
best_result = result
optimal_moves.clear()
optimal_moves.append(moves)
elif (result == best_result):
optimal_moves.append(moves)
self.test_field = Field.copy(field_copy_test)
optimal_move = []
if (optimal_moves):
# Фильтрация хода
for move in choice(optimal_moves):
if (side == SideType.WHITE and self.__field.type_at(move.from_x, move.from_y) in BLACK_CHECKERS):
break
elif (side == SideType.BLACK and self.__field.type_at(move.from_x, move.from_y) in WHITE_CHECKERS):
break
optimal_move.append(move)
return optimal_move
def __get_predicted_moves_list(self, side: SideType, current_prediction_depth: int = 0,
all_moves_list: list[Move] = [], current_moves_list: list[Move] = [],
required_moves_list: list[Move] = []) -> list[Move]:
'''Предсказать все возможные ходы'''
if (current_moves_list):
all_moves_list.append(current_moves_list)
else:
all_moves_list.clear()
if (required_moves_list):
moves_list = required_moves_list
else:
now = False
moves_list = self.__get_moves_list(side)
if (moves_list and current_prediction_depth < MAX_DEPTH):
field_copy = Field.copy(self.test_field)
for move in moves_list:
field_for_check = Field.copy(self.test_field)
has_killed_checker = self.__handle_move(self.test_field, move, draw=False)
required_moves_list = list(filter(
lambda required_move: move.to_x == required_move.from_x and move.to_y == required_move.from_y,
self.__get_required_moves_list(side, self.test_field)))
# Если есть ещё ход этой же шашкой
if (has_killed_checker and required_moves_list):
self.__get_predicted_moves_list(side, current_prediction_depth, all_moves_list,
current_moves_list + [move], required_moves_list)
else:
self.__get_predicted_moves_list(SideType.opposite(side), current_prediction_depth + 1,
all_moves_list, current_moves_list + [move])
self.test_field = Field.copy(field_copy)
return all_moves_list