Цель проекта – Создать графический интерфейс на основе PyQt для игры в шахматы для 2 игроков
Вложение | Размер |
---|---|
proekt_shahmat.docx | 28.29 КБ |
Муниципальное образовательное учреждение Гимназия №6
г.о. Красноармейск
Муниципальная конференция «Шаг в профессию инженер»
Номинация: «Программирование»
Тема:
«Программное приложение на Python – игра «Шахматы»»
Автор работы: Калин Егор Олегович, 9класс
Научный руководитель: Коркунова Н.И.
Красноармейск
2021г.
Оглавление
| 1 |
| 2 |
| 2-6 |
| 6-8 |
| 8 |
| 8 |
| 8 |
| 9 |
Введение
Python очень обширый язык программирования, на котором можно создать бесконечное множество разнообразных проектов. Свой первый проект я решил посвятить шахматам – одной из древнейших настольных игр с достаточно простыми правилами но в то же время заставляющей человека думать и планировать.
Актуальность выбранной темы проекта связана с актуальностью профессионального изучения информатики и программирования в современном мире.
Новизна проекта заключается в сочетании теории и практики, использовании различных ресурсов для написания кода.
Цель проекта – Создать графический интерфейс на основе PyQt для игры в шахматы для 2 игроков
Задачи проекта:
Основная часть
Классы фигур
Все фигуры имеют некоторые одинаковые механики, поэтому для начала создадим базовый класс, от которого будем наследовать другие.
Если представить доску в виде матрицы, то можно определять местоположение фигур с помощью координат х и у.
Пешка:
Пешка может двигаться на 1 клетку по вертикали (вверх если белая и вниз если черная), а также на 2 клетки, если её ещё ни разу не двигали. Исходя из этих данных создаем класс:
class Pawn(Piece):
ch = 'P'
def can_move(self, board, row, col, row1, col1):
# Пешка может ходить только по вертикали
# "взятие на проходе" не реализовано
if col != col1:
return False
if not self.is_path_clear(board, row, col, row1, col1):
return False
# Пешка может сделать из начального положения ход на 2 клетки
# вперёд, поэтому поместим индекс начального ряда в start_row.
if self.color == WHITE:
direction = 1
start_row = 1
else:
direction = -1
start_row = 6
# ход на 1 клетку
if row + direction == row1:
return True
# ход на 2 клетки из начального положения
if row == start_row and row + 2 * direction == row1:
return True
return False
def can_attack(self, board, row, col, row1, col1):
direction = 1 if (self.color == WHITE) else -1
return (row + direction == row1
and (col + 1 == col1 or col - 1 == col1))
Ладья:
Эта фигура может двигаться только по вертикали и горизонтали, т.е. должны совпадать координаты до передвижения и после лтбо по вертикали либо по горизонтали. Зная это, реализуем класс:
class Rook(Piece):
ch = 'R'
def can_move(self, board, row, col, row1, col1):
# Невозможно сделать ход в клетку, которая не лежит в том же ряду
# или столбце клеток.
if row != row1 and col != col1:
return False
if not self.is_path_clear(board, row, col, row1, col1):
return False
return True
Конь:
Конь двигается буквой «Г», т.е. на 2 по вертикали и 1 по горизонтали и наоборот. При этом направление не важно, значит нужно использовать ф-цию abs(), т.е. модуль.
class Knight(Piece):
ch = 'N'
def can_move(self, board, row, col, row1, col1):
# Конь двигается буквой Г (2 вертикально, 1 горизонтально)
if abs(row - row1) == 2 and abs(col - col1) == 1:
return True
# Конь двигается буквой Г (1 вертикально, 2 горизонтально)
if abs(row - row1) == 1 and abs(col - col1) == 2:
return True
return False
Слон:
Слон двигается по диагонали т.е. его смещение по вертикали должно быть равно смещению по горизонтали:
class Bishop(Piece):
ch = 'B'
def can_move(self, board, row, col, row1, col1):
if not self.is_path_clear(board, row, col, row1, col1):
return False
# Слон двигается по диагонали
# Смещение по строка должно равняться смещению по столбцам
if abs(row - row1) == abs(col - col1):
return True
return False
Королева(Ферзь):
Королева двигается по всем сторонам (по горизонтали, вертикали, диагонали). Копируем эти механики из слона и ладьи и получаем:
class Queen(Piece):
ch = 'Q'
def can_move(self, board, row, col, row1, col1):
if not self.is_path_clear(board, row, col, row1, col1):
return False
# Ферзь двигается вертикально
if col == col1:
return True
# Ферзь двигается горизонтально
if row == row1:
return True
# Ферзь двигается по диагонали
# Смещение по строка должно равняться смещению по столбцам
if abs(row - row1) == abs(col - col1):
return True
return False
Король:
Король двигается в любую сторону на расстояние 1:
class King(Piece):
ch = 'K'
def can_move(self, board, row, col, row1, col1):
if not self.is_path_clear(board, row, col, row1, col1):
return False
# Король двигается в любую клетку с рассотоянием равным 1
if max(abs(row - row1), abs(col - col1)) == 1:
return True
return False
А теперь необходимо реализовать некоторые особые механики игры, например «Превращение пешки» и «Рокировка».
Когда пешка доходит до противоположного конца доски она может стать Ферзем, Ладьей, Слоном или Конем. Для этого мы можем просто изменить класс фигуры, в том месте, где стоит пешка.
Для рокировки нам нужно знать: двигались ли король и ладья, есть ли между ними фигуры, приведет ли рокировка к шаху короля. От этого будет зависеть успешность рокировки или её невозможность.
Реализация доски
Представим доску в виде матрицы на координатной оси. Тогда расположение фигур на доске будет записано в двухмерном списке. Записыаем стартовые значения матрицы:
self.field = [[None] * 8 for _ in range(8)]
self.field[0] = [
Rook(WHITE), Knight(WHITE), Bishop(WHITE), Queen(WHITE),
King(WHITE), Bishop(WHITE), Knight(WHITE), Rook(WHITE)
]
self.field[1] = [
Pawn(WHITE), Pawn(WHITE), Pawn(WHITE), Pawn(WHITE),
Pawn(WHITE), Pawn(WHITE), Pawn(WHITE), Pawn(WHITE)
]
self.field[6] = [
Pawn(BLACK), Pawn(BLACK), Pawn(BLACK), Pawn(BLACK),
Pawn(BLACK), Pawn(BLACK), Pawn(BLACK), Pawn(BLACK)
]
self.field[7] = [
Rook(BLACK), Knight(BLACK), Bishop(BLACK), Queen(BLACK),
King(BLACK), Bishop(BLACK), Knight(BLACK), Rook(BLACK)
]
Цвет фигуры определяют константы WHITE и BLACK.
Каждая клетка имеет свое значение, так белая пешка обозначена как «wP». В дальнейшем это обозначение определит графическую модель фигурки на доске.
В этом классе создаем ф-цию проверки возможности движения:
def check_move(self, row: int, col: int, row1: int, col1: int):
"""Проверка перемещения фигуру из точки (row, col) в точку (row1, col1).
Если перемещение возможно, метод выполнит его и вернет True.
Если нет --- вернет False"""
check = self.check() # Есть ли шах к перед ходом
if not correct_coords(row, col) or not correct_coords(row1, col1):
return False
if row == row1 and col == col1:
return False # нельзя пойти в ту же клетку
piece = self.field[row][col]
target = self.field[row1][col1]
if piece is None:
return False
if piece.get_color() != self.color:
return False
if isinstance(piece, King) and self.is_under_attack(row1, col1, opponent(self.current_player_color())):
return False
if not (piece.can_move(self.field, row, col, row1, col1) and target is None) and \
not (piece.can_attack(self.field, row, col, row1, col1) and
(target is not None and not isinstance(target, King)
and target.get_color() == opponent(self.current_player_color()))):
return False
old_field = [x.copy() for x in self.field] # Сохранить поле
self.field[row][col] = None # Снять фигуру.
self.field[row1][col1] = piece # Поставить на новое место.
if check and self.check(): # В результате хода избежать шаха не удалось
# Возвращаемся в исходное состояние
self.field = old_field
return False
# Возвращаемся в исходное состояние
self.field = old_field
return True
Ф-ция проверяет сразу все факторы: выход за пределы доски, походила ли фигурка на свое же место, приведет ли ход короля к его шаху или мату и т.д.
Реализуем ф-цию движения в которой используется ф-ция проверки:
def move_piece(self, row, col, row1, col1):
"""Переместить фигуру из точки (row, col) в точку (row1, col1).
Если перемещение возможно, метод выполнит его и вернет True.
Если нет --- вернет False"""
if self.check_move(row, col, row1, col1):
piece = self.field[row][col]
self.field[row][col] = None # Снять фигуру.
piece.moved()
self.field[row1][col1] = piece # Поставить на новое место.
if self.check_promotion(piece, row1):
self.promotion = True
self.color = opponent(self.color)
self.change = True
return True
return False
Также здесь реализуем механику атаки:
def is_under_attack(self, row, col, color):
for row1 in range(8):
for col1 in range(8):
if self.field[row1][col1] is not None:
if self.field[row1][col1].get_color() == color and \
self.field[row1][col1].can_attack(self.field, row1, col1, row, col):
return True
return False
Для короля мы должны реализовать «Шах» и «Мат». Это довольно таки сложно из-за того, что у короля пропадает много возможностей, например «Рокировка». Поэтому я все ещё не полностью смог воссоздать эту механику из шахмат. Но все же в случае шаха игроку будут недоступны ходы, которые приведут к мату.
Таймер и обработка событий мыши
Графический интерфейс
С помощью класса QtTimer от PyQt реализуем таймер для каждого игрока с механикой смены после окончания хода. Также с помощью механизма слотов и сигналов PyQt мы сможем обрабатывать события мыши для удобного взаимодействия с графическим интерфейсом.
Реализация графического интерфейса, пожалуй, одна из самых сложных задач проекта, так как PyQt я знаю ещё очень поверхностно. Но что есть – то есть. Создаем модели фигурок, а также модели иконок и изображение поля. Настраиваем механику обработки мыши под границы моделей клеток и иконок.
Дополняем последние детали в виде кнопок «Новая игра» и «Выйти».
Заключение
Проект завершен, создан рабочий вариант шахмат.
Во время разработки проекта я узнал о многих механиках программирования, получил знания о классах объектов, познакомился с QT. Этот проект ждет ещё множество новых механик и нововведений: добавление ИИ, реализация более сложных механик, которыми пользуются проффесиональные игроки(прим.: «Взятие на проходе»). В дальнейшем я не собираюсь останавливаться на этом проекте, так как меня ждет ещё множество нереализованных идей, ждущих своего часа.
Библиография
Три коробки с орехами
Заколдованная буква
Стеклянный Человечек
Эта весёлая планета
Новогодние гирлянды