@STER0

Логика игры «Пятнашки» на Python?

Как можно реализовать логику создания колоды фишек, так чтобы игру можно было всегда решить?
вот код:

import sys
import copy
import pygame
import random


def init():
    # Инициализация Pygame и модуля для работы со шрифтами.
    pygame.init()
    pygame.font.init()

    # Создание заголовка и иконки игры.
    pygame.display.set_caption("Пятнашки")
    icon = pygame.image.load("Pyatnashki\image\icon.png")
    pygame.display.set_icon(icon)

    # Создание окна и объекта шрифта.
    return pygame.display.set_mode((600, 600)), pygame.font.SysFont('Consolas', 36)

def start_screen(screen, font):
    # Отрисовка стартового экрана.
    screen.fill(pygame.Color(255, 255, 255))
    fontsurf = font.render("Пятнашки", True, pygame.Color(0, 0, 0))
    left, top = (screen.get_width() / 2 - fontsurf.get_width() / 2, screen.get_height() / 4 - fontsurf.get_height() / 2)
    screen.blit(fontsurf, (left, top))
    fontsurf = font.render("Нажмите любую кнопку для начала игры", True, pygame.Color(0, 0, 0))
    left, top = (screen.get_width() / 2 - fontsurf.get_width() / 2, screen.get_height() / 2 - fontsurf.get_height() / 2)
    screen.blit(fontsurf, (left, top))
    pygame.display.flip()

    # Ожидание нажатия кнопки для начала игры.
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.KEYDOWN:
                screen.fill('black')
                return

def create_board():
    # Определение начального и перемешанного игрового поля.
    board = [
        [1,2,3,4],
        [5,6,7,8],
        [9,10,11,12],
        [13,14,15,0]
    ]
    board_copy = copy.deepcopy(board)
    for i in range(4):
        for j in range(4):
            x1, y1 = i, j
            x2, y2 = random.randint(0, 3), random.randint(0, 3)
            board_copy[x1][y1], board_copy[x2][y2] = board_copy[x2][y2], board_copy[x1][y1]  # Перемешивание фишек.
    return board, board_copy

def is_solvable(board):
    flat_board = [elem for row in board for elem in row]  # Преобразование двумерного списка в одномерный.
    inversions = sum(1 for i in range(len(flat_board)) for j in range(i + 1, len(flat_board)) if flat_board[i] > flat_board[j])  # Подсчет количества инверсий.
    return inversions % 2 == 0  # Проверка на четность количества инверсий.

def handle_events():
    # Обработка событий (нажатие на кнопку "выход" и клик мышью).
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN:
            return pygame.mouse.get_pos()  # Возвращение координат клика мыши.
    return None

def draw_board(screen, font, board):
    # Отрисовка игрового поля на экране.
    for x in range(4):
        for y in range(4):
            rect = pygame.Rect(150*x+10, 150*y+10, 130, 130)  # Определение координат фишек.
            pygame.draw.rect(screen, pygame.Color(255, 255, 255), rect)  # Отрисовка фишек.

            piece = board[y][x]  # Получение цифры на фишке.
            if piece:
                fontsurf = font.render(str(piece), True, pygame.Color(255, 0, 0))  # Создание текстовой поверхности.
                left, top = (rect.x + (rect.width / 2 - fontsurf.get_width() / 2),
                             rect.y + (rect.height / 2 - fontsurf.get_height() / 2))  # Центрирование текста на фишке.
                screen.blit(fontsurf, (left, top))  # Отображение цифры на фишке.

def update_board(board, pos):
    # Обновление игрового поля при кликах пользователя.
    if pos:
        x, y = pos
        x, y = (x - 10) // 150, (y - 10) // 150  # Вычисление координат фишки, на которую кликнул пользователь.
        current_piece = board[y][x]  # Получение цифры на фишке.
        if x > 0 and board[y][x-1] == 0:
            board[y][x-1] = current_piece
            board[y][x] = 0
        if x < 3 and board[y][x+1] == 0:
            board[y][x+1] = current_piece
            board[y][x] = 0
        if y > 0 and board[y-1][x] == 0:
            board[y-1][x] = current_piece
            board[y][x] = 0
        if y < 3 and board[y+1][x] == 0:
            board[y+1][x] = current_piece
            board[y][x] = 0  # Обновление игрового поля.

def check_win(board,board_copy):
    # Проверка, выиграл ли пользователь.
    if board == board_copy:
        return True

def show_win_screen(screen, font):
    # Отображение окна победы и кнопки для перезапуска игры.
    screen.fill(pygame.Color(255, 255, 255))
    fontsurf = font.render("Вы выиграли!", True, pygame.Color(0, 0, 0))
    left, top = (screen.width() / 2 - fontsurf.width() / 2, screen.height() / 4 - fontsurf.height() / 2)
    screen.blit(fontsurf, (left, top))
    fontsurf = font.render("Нажмите любую кнопку для перезапуска игры", True, pygame.Color(0, 0, 0))
    left, top = (screen.width() / 2 - fontsurf.width() / 2, screen.height() / 2 - fontsurf.height() / 2)
    screen.blit(fontsurf, (left, top))
    pygame.display.flip()


def main():
    # Основная функция игры.
    screen, font = init()  # Инициализация окна и объекта шрифта.
    start_screen(screen, font)
    board, board_copy = create_board()  # Создание и перемешивание игрового поля.
    if not is_solvable(board_copy):
            board, board_copy = create_board()  # Создание и перемешивание игрового поля.


    while True:
        pos = handle_events()  # Обработка событий.
        draw_board(screen, font, board_copy)  # Отрисовка игрового поля на экране.
        update_board(board_copy, pos)  # Обновление игрового поля при кликах пользователя.
        pygame.display.flip()  # Обновление экрана.

        if check_win(board,board_copy):  # Проверка на победу.
            print("Вы выиграли!")
            # pygame.time.wait(1000)  # Пауза перед началом новой игры.
            # board, board_copy = create_board()  # Создание и перемешивание нового игрового поля.
            show_win_screen(screen, font)
if __name__ == "__main__":
    main()  # Запуск основной функции игры.
  • Вопрос задан
  • 633 просмотра
Решения вопроса 1
wataru
@wataru Куратор тега Алгоритмы
Разработчик на С++, экс-олимпиадник.
Надо, чтобы "четность" перестановки совпадала с четностью финального поля (1).
Занумеруйте все 16 позиций слева направо сверху вниз.
чтобы подсчитать четность, рассматривайте каждую пару заполненных позиций (15\*14/2=105 пар) - если числа идут не в том порядке (большее число на позиции с меньшим номером) - то прибавьте 1 к ответу. В конце возьмите ответ по модулю 2. Это и будет четность перестановки.

Чтобы получить поле, которое можно собрать, сгенерируйте любую перестановку (случайно перемешайте 15 чисел), а потом посчитайте ee четность. Если четность плохая, то поменйте местами любые 2 соседних элемента (выберите случайно, или меняйте первые 2 всегда - на вероятности всех возможных полей это не влияет).

Edit: Но вы это почти все итак знатете, ибо функция is_solvable в вашем коде как раз инверсии уже считает.
Значит, Но вы знаете, что плохое поле от хорошего отличается лишь четностью, значит, если поле плохое - меняйте местами 2 соседних по порядку элемента. Например верхний левый со вторым в верхней строке.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы