Pyzzle 15, игра известная в нашей стране, как «Пятнашки». «Пятнашки» – это логическая игра-головоломка. Цель игры – упорядочить пронумерованные плитки на игровом размером 4Х4.

Пишем компьютерную версию игры на языке программирования Python с использованием библиотеки Pygame Zero.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""

pgzrun.go()      # Запускается главный игровой цикл

Лист. 1. Подключаем библиотеку pgzrun (pygame) и запускаем главный игровой цикл.

В программе листинг 1 метод filterwarnings из библиотеки warnings устраняет сообщение о не оптимально скомпилированной для вашего компьютера библиотеке pgzrun (pygame).  Функция go() из библиотеки pgzrun запускает главный игровой цикл.

Рис. 1. Окно, созданное программой листинг 1.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    pass


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    pass


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    pass


pgzrun.go()      # Запускается главный игровой цикл

Лист. 2. В программу добавлены функции, периодически запускаемые из цикла в функции pgzrun().

Если вы в своей программе создадите функции с именами draw, on_mouse_down, on_key_down и некоторыми другими, то эти функции будут вызываться (запускаться на выполнение) автоматически, так как их вызовы прописаны в функции pgzrun.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    pass


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    pass


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 3. В программу добавлены 3 глобальные константы и добавлена функция очистки экрана в определение функции draw.

  • Обязательные глобальные переменные которые использует функция pgzrun:
    • WIDTH = 800 # Ширина окна (обязательно)
    • HEIGHT = 600 # Высота окна (обязательно)
    • TITLE = "Game" # Заголовок окна (рекомендуется)
  • Основные функции, которые вызывает pgzrun:
    • Отрисовка:
      • def draw(): # Вызывается каждый кадр (60 FPS)
      • def draw(): # или с параметром (редко используется)
    • Обработка ввода:
      • def on_mouse_down(pos): # Нажатие кнопки мыши
      • def on_mouse_up(pos): # Отпускание кнопки мыши
      • def on_mouse_move(pos): # Движение мыши
      • def on_key_down(key): # Нажатие клавиши
      • def on_key_up(key): # Отпускание клавиши
    • Игровой цикл:
      • def update(): # Вызывается каждый кадр ДО draw()
      • def update(dt): # С параметром delta-time
    • Дополнительные возможности:
      • Акторы (спрайты):# Создание акторов
        • actor = Actor('image_name') # Загружает image_name.png
        • actor.pos = (100, 100) # Позиция актора. pgzrun автоматически отрисует всех созданных акторов
      • Звуки:
        • sounds.sound_name.play() # Воспроизводит sound_name.wav
      • Таймеры:
        • clock.schedule(func, delay) # Вызов функции через delay секунд
        • clock.schedule_interval(func, interval) # Периодический вызов

Рис. 2. Окно, созданное программой листинг 3.

Образец Код Название CSS Название
          #F0F8FF aliceblue блекло-голубой
         #FAEBD7 antiquewhite античный белый
     #00FFFF aqua синий
          #7FFF00 chartreuse фисташковый
          #F5F5DC azure лазурь
          #F5F5DC beige бежевый
          #FFE4C4 bisque бисквитный
          #FFF8DC cornsilk темно-зеленый
       #FFEBCD blanchedalmond светло-кремовый
       #0000FF blue голубой
       #8A2BE2 blueviolet светло-фиолетовый
       #B8860B darkgoldenrot темный красно-золотой
       #006400 darkgreen темно-зеленый
       #8B008B darkmagenta темный фуксин
       #FF8C00 darkorange темно-оранжевый
    #8B0000 darkred темно-красный
    #8FBC8F darkseagreen темный морской волны
    #2F4F4F darkslategray темный сине-серый
    #9400D3 darkviolet темно-фиолетовый
    #00BFFF deepskyblue темный небесно-голубой
    #1E90FF dodgerblue тускло-васильковый
    #FFFAF0 floralwhite цветочно-белый
    #FF00FF fuchsia фуксии
    #DCDCDC gainsboro гейнсборо
    #DAA520 goldenrod красного золота
    #008000 green зеленый
    #F0FFF0 honeydew свежего меда
    #CD5C5C indianred ярко-красный
    #FFFFF0 ivory слоновой кости
    #E6E6FA lavender бледно-лиловый
    #7CFC00 lawngreen зеленой лужайки
    #ADD8E6 lightblue светло-голубой
    #E0FFFF lightcyan светло-циановый
    #90EE90 lightgreen светло-зеленый
    #FFB6C1 lightpink светло-розовый
    #20B2AA lightseagreen светлый морской волны
    #778899 lightslategray светлый сине-серый
    #FFFFE0 lightyellow светло-желтый
    #32CD32 limegreen зеленовато-известковый
    #FF00FF фуксин blanchedalmond
    #66CDAA mediumaquamarine умеренно-аквамариновый
    #3CB371 mediumseagreen умеренный морской волны
    #BA55D3 mediumorchid умеренно-орхидейный
    #00FA9A mediumspringgreen умеренный сине-серый
    #0C71585 mediumvioletred умеренный красно-фиолетовый
    #0F5FFFA mintcream мятно-кремовый
    #FFE4B5 moccasin болотный
    #000080 navy морской
    #808000 olive оливковый
    #FFA500 orange оранжевый
    #DA70D6 orchid орхидейный
    #98FB98 palegreen бледно-зеленый
    #DB7093 palevioletred бледный красно-фиолетовый
    #FFDAB9 peachpuff персиковый
    #FFC0CB pink розовый
    #B0E0E6 powderblue туманно-голубой
    #FF0000 red красный
    #4169E1 royalblue королевский голубой
    #FA8072 salmon оранжево-розовый
    #2E8B57 seagreen морской зеленый
    #A0522D sienna охра
        #87CEEB skyblue небесно-голубой
        #708090 slategray сине-серый
        #00FF7F springgreen весенне-зеленый
        #D2B48C tan желто-коричневый
        #D8BFD8 thistle чертополоха
        #40E0D0 turquoise бирюзовый
        #F5DEB3 wheat пшеничный
        #F5F5F5 whitesmoke белый дымчатый
        #9ACD32 yellowgreen желто-зеленый
          #A52A2A brown коричневый
         #DEB887 burlywood старого дерева
     #5F9EA0 cadetblue блеклый серо-голубой
          #FF7F50 coral коралловый
          #D2691E chocolate шоколадный
          #6495ED cornflowerblue васильковый
          #000000 black черный
          #DC143C crimson малиновый
       #00FFFF cyan циан
       #00008B darkblue темно-голубой
       #008B8B darkcyan темный циан
    #A9A9A darkgray темно-серый
       #BDB76B darkkhaki темный хаки
       #556B2F darkolivegreen темно-оливковый
    #9932CC darkorchid темно-орхидейный
    #E9967A darksalmon темно-оранжево-розовый
    #483D8B darkslateblue темный сине-серый
    #00CED1 darkturqueise темно-бирюзовый
    #FF1493 deeppink темно-розовый
    #696969 dimgray тускло-серый
    #B22222 firebrick огнеупорного кирпича
    #228B22 forestgreen лесной зеленый
    #F8F8FF ghostwhite туманно-белый
    #FFD700 gold золотой
    #808080 gray серый
    #ADFF2F greenyellow желто-зеленый
    #FF69B4 hotpink ярко-розовый
    #4B0082 indigo индиго
    #F0E68C khaki хаки
    #FFF0F5 lavenderblush бледный розово-лиловый
    #FFFACD lemonchiffon лимонный
    #F08080 lightcoral светло-коралловый
    #FAFAD2 lightgoldenrodyellow светлый золотисто-желтый
    #D3D3D3 lightgrey светло-серый
    #FFA07A lightsalmon светлый оранжево-розовый
    #87CEFA lightskyblue светлый небесно-голубой
    #B0C4DE lightsteelblue светло-стальной
    #00FF00 lime цвета извести
    #FAF0E6 linen льняной
    #800000 maroon оранжево-розовый
    #0000CD mediumblue умеренно-голубой
    #7B68EE mediumslateblue умеренный серо-голубой
    #9370DB mediumpurple умеренно-пурпурный
    #48D1CC mediumturquose умеренно-бирюзовый
    #191970 midnightblue полуночный синий
    #FFE4E1 mistyrose туманно-розовый
    #FFDEAD navajowhite грязно-серый
    #FDF5E6 oldlace старого коньяка
    #6B8E23 olivedrab
    #FF4500 orangered оранжево-красный
    #EEE8AA palegoldenrod бледно-золотистый
    #AFEEEE paleturquoise бледно-бирюзовый
    #FFEFD5 papayawhip дынный
    #CD853F peru коричневый
    #DDA0DD plum сливовый
    #800080 purple пурпурный
    #BC8F8F rosybrown розово-коричневый
    #8B4513 saddlebrown старой кожи
    #F4A460 sandybrown рыже-коричневый
    #FFF5EE seashell морской пены
    #C0C0C0 silver серебристый
        #6A5ACD slateblue серо-голубой
        #FFFAFA snow снежный
        #4682B4 steelblue сине-стальной
        #008080 teal чайный
        #FF6347 tomato томатный
        #EE82EE violet фиолетовый
        #FFFFFF white белый
        #FFFF00 yellow желтый
        #7FFFD4 aquamarine аквамарин

Табл. 1. Таблица цветов с кодами и названиями, которые применимы в Python с библиотекой Pygame, pgzrun.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    pass


def draw_numbers():
    """
    Выводит в клетках игрового поля цифры,
    соответствующие игровой ситуации
    """
    pass


def draw_text():
    """
    Выводит на игровое поле текстовую информацию,
    приглашение к началу игры:
    Клавиша пробел - Старт
    """
    pass


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана
    draw_grid()                                 # нарисовать клетки
    draw_numbers()                              # вывести цифры
    draw_text()                                 # показать текстовую информацию


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    pass


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    pass


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 4. В программу добавлены определения функций draw_grid(), draw_numbers(), draw_text() и их вызов из функции draw().

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
SIZE = 4                                        # размер игрового поля (4x4)
CELL_SIZE = WIDTH // SIZE                       # размер клетки
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки
GRID_COLOR = 'dimgray'                          # цвет сетки


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH, CELL_SIZE):                        # линии сетки
        screen.draw.line((n, 0), (n, HEIGHT), GRID_COLOR)       # вертикальные линии
        screen.draw.line((0, n), (WIDTH, n), GRID_COLOR)        # горизонтальные линии


def draw_numbers():
    """
    Выводит в клетках игрового поля цифры,
    соответствующие игровой ситуации
    """
    pass


def draw_text():
    """
    Выводит на игровое поле текстовую информацию,
    приглашение к началу игры:
    Клавиша пробел - Старт
    """
    pass


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана
    draw_grid()                                 # нарисовать клетки
    draw_numbers()                              # вывести цифры
    draw_text()                                 # показать текстовую информацию


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    pass


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    pass


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 5.

Рис. 3. Окно, созданное программой листинг 5.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
SIZE = 4                                        # размер игрового поля (4x4)
CELL_SIZE = WIDTH // SIZE                       # размер клетки
DIGITS_SIZE = int(CELL_SIZE / 2.2)              # размер цифр
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки
GRID_COLOR = 'dimgray'                          # цвет сетки
DIGITS_COLOR = 'sienna'                         # цвет цифр
playground = list(range(1, SIZE**2)) + [' ']    # виртуальное игровое поле


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH, CELL_SIZE):                        # линии сетки
        screen.draw.line((n, 0), (n, HEIGHT), GRID_COLOR)       # вертикальные линии
        screen.draw.line((0, n), (WIDTH, n), GRID_COLOR)        # горизонтальные линии


def xy_to_n(x, y):
    """
    Конвертирует пару координат x, y в номер клетки
    """
    col = x // (WIDTH // SIZE)
    row = y // (HEIGHT // SIZE)
    return row * SIZE + col


def draw_numbers():
    """
    Выводит в клетках игрового поля цифры,
    соответствующие игровой ситуации
    """
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    for x in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
        for y in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
            n = xy_to_n(x, y)
            screen.draw.text(str(playground[n]), center=(x, y), **dig_params)


def draw_text():
    """
    Выводит на игровое поле текстовую информацию,
    приглашение к началу игры:
    Клавиша пробел - Старт
    """
    pass


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана
    draw_grid()                                 # нарисовать клетки
    draw_numbers()                              # вывести цифры
    draw_text()                                 # показать текстовую информацию


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    pass


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    pass


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 6.

Рис. 4.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
SIZE = 4                                        # размер игрового поля (4x4)
CELL_SIZE = WIDTH // SIZE                       # размер клетки
DIGITS_SIZE = int(CELL_SIZE / 2.2)              # размер цифр
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки
GRID_COLOR = 'dimgray'                          # цвет сетки
DIGITS_COLOR = 'sienna'                         # цвет цифр
playground = list(range(1, SIZE**2)) + [' ']    # виртуальное игровое поле


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH, CELL_SIZE):                        # линии сетки
        screen.draw.line((n, 0), (n, HEIGHT), GRID_COLOR)       # вертикальные линии
        screen.draw.line((0, n), (WIDTH, n), GRID_COLOR)        # горизонтальные линии


def xy_to_n(x, y):
    """
    Конвертирует пару координат x, y в номер клетки
    """
    col = x // (WIDTH // SIZE)
    row = y // (HEIGHT // SIZE)
    return row * SIZE + col


def draw_numbers():
    """
    Выводит в клетках игрового поля цифры,
    соответствующие игровой ситуации
    """
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    for x in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
        for y in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
            n = xy_to_n(x, y)
            screen.draw.text(str(playground[n]), center=(x, y), **dig_params)


def draw_text():
    """
    Выводит на игровое поле текстовую информацию,
    приглашение к началу игры:
    Клавиша пробел - Старт
    """
    pass


def play(n):
    """
    Делает ход из клетки с номером n
    в пустую клетку, если это возможно!
    """
    m = playground.index(' ')                   # индекс пустой клетки
    playground[n], playground[m] = playground[m], playground[n]


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана
    draw_grid()                                 # нарисовать клетки
    draw_numbers()                              # вывести цифры
    draw_text()                                 # показать текстовую информацию


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    play(xy_to_n(*pos))                         # сделать ход


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    pass


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 7.

Рис. 5.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
SIZE = 4                                        # размер игрового поля (4x4)
CELL_SIZE = WIDTH // SIZE                       # размер клетки
DIGITS_SIZE = int(CELL_SIZE / 2.2)              # размер цифр
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки
GRID_COLOR = 'dimgray'                          # цвет сетки
DIGITS_COLOR = 'sienna'                         # цвет цифр
playground = list(range(1, SIZE**2)) + [' ']    # виртуальное игровое поле


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH, CELL_SIZE):                        # линии сетки
        screen.draw.line((n, 0), (n, HEIGHT), GRID_COLOR)       # вертикальные линии
        screen.draw.line((0, n), (WIDTH, n), GRID_COLOR)        # горизонтальные линии


def xy_to_n(x, y):
    """
    Конвертирует пару координат x, y в номер клетки
    """
    col = x // (WIDTH // SIZE)
    row = y // (HEIGHT // SIZE)
    return row * SIZE + col


def draw_numbers():
    """
    Выводит в клетках игрового поля цифры,
    соответствующие игровой ситуации
    """
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    for x in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
        for y in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
            n = xy_to_n(x, y)
            screen.draw.text(str(playground[n]), center=(x, y), **dig_params)


def draw_text():
    """
    Выводит на игровое поле текстовую информацию,
    приглашение к началу игры:
    Клавиша пробел - Старт
    """
    pass


def play(n):
    """
    Делает ход из клетки с номером n
    в пустую клетку, если это возможно!
    """
    m = playground.index(' ')                   # индекс пустой клетки
    # если клетки n и m находятся по соседству в одном ряду или колонке
    if n in [m+1, m-1] and m // SIZE == n // SIZE or n in [m-SIZE, m+SIZE]:
        playground[n], playground[m] = playground[m], playground[n]


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана
    draw_grid()                                 # нарисовать клетки
    draw_numbers()                              # вывести цифры
    draw_text()                                 # показать текстовую информацию


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    play(xy_to_n(*pos))                         # сделать ход


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    pass


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 8. В функцию play добавлена проверка выбранного хода на соответствие правилам игры.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
from random import choice                       # выбрать случайный элемент списка
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
SIZE = 4                                        # размер игрового поля (4x4)
CELL_SIZE = WIDTH // SIZE                       # размер клетки
DIGITS_SIZE = int(CELL_SIZE / 2.2)              # размер цифр
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки
GRID_COLOR = 'dimgray'                          # цвет сетки
DIGITS_COLOR = 'sienna'                         # цвет цифр
playground = list(range(1, SIZE**2)) + [' ']    # виртуальное игровое поле


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH, CELL_SIZE):                        # линии сетки
        screen.draw.line((n, 0), (n, HEIGHT), GRID_COLOR)       # вертикальные линии
        screen.draw.line((0, n), (WIDTH, n), GRID_COLOR)        # горизонтальные линии


def xy_to_n(x, y):
    """
    Конвертирует пару координат x, y в номер клетки
    """
    col = x // (WIDTH // SIZE)
    row = y // (HEIGHT // SIZE)
    return row * SIZE + col


def draw_numbers():
    """
    Выводит в клетках игрового поля цифры,
    соответствующие игровой ситуации
    """
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    for x in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
        for y in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
            n = xy_to_n(x, y)
            screen.draw.text(str(playground[n]), center=(x, y), **dig_params)


def draw_text():
    """
    Выводит на игровое поле текстовую информацию,
    приглашение к началу игры:
    Клавиша пробел - Старт
    """
    pass


def play(n):
    """
    Делает ход из клетки с номером n
    в пустую клетку, если это возможно!
    """
    m = playground.index(' ')                   # индекс пустой клетки
    # если клетки n и m находятся по соседству в одном ряду или колонке
    if n in [m+1, m-1] and m // SIZE == n // SIZE or n in [m-SIZE, m+SIZE]:
        playground[n], playground[m] = playground[m], playground[n]


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана
    draw_grid()                                 # нарисовать клетки
    draw_numbers()                              # вывести цифры
    draw_text()                                 # показать текстовую информацию


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    play(xy_to_n(*pos))                         # сделать ход


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    if key == keys.SPACE :                      # выбор кнопки Старт -> "Пробел"
        counter = 1000                          # количество перемешиваний
        while(counter != 0):
            n = choice(range(SIZE**2))          # выбор случайного хода
            play(n)
            counter -= 1


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 9.

Рис. 6.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
from random import choice                       # выбрать случайный элемент списка
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
SIZE = 4                                        # размер игрового поля (4x4)
CELL_SIZE = WIDTH // SIZE                       # размер клетки
DIGITS_SIZE = int(CELL_SIZE / 2.2)              # размер цифр
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки
GRID_COLOR = 'dimgray'                          # цвет сетки
DIGITS_COLOR = 'sienna'                         # цвет цифр
playground = list(range(1, SIZE**2)) + [' ']    # виртуальное игровое поле


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH, CELL_SIZE):                        # линии сетки
        screen.draw.line((n, 0), (n, HEIGHT), GRID_COLOR)       # вертикальные линии
        screen.draw.line((0, n), (WIDTH, n), GRID_COLOR)        # горизонтальные линии


def xy_to_n(x, y):
    """
    Конвертирует пару координат x, y в номер клетки
    """
    col = x // (WIDTH // SIZE)
    row = y // (HEIGHT // SIZE)
    return row * SIZE + col


def draw_numbers():
    """
    Выводит в клетках игрового поля цифры,
    соответствующие игровой ситуации
    """
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    for x in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
        for y in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
            n = xy_to_n(x, y)
            screen.draw.text(str(playground[n]), center=(x, y), **dig_params)


def draw_text():
    """
    Выводит на игровое поле текстовую информацию,
    приглашение к началу игры:
    Клавиша пробел - Старт
    """
    pass


def play(n):
    """
    Делает ход из клетки с номером n
    в пустую клетку, если это возможно!
    """
    m = playground.index(' ')                   # индекс пустой клетки
    # если клетки n и m находятся по соседству в одном ряду или колонке
    if n in [m+1, m-1] and m // SIZE == n // SIZE or n in [m-SIZE, m+SIZE]:
        playground[n], playground[m] = playground[m], playground[n]
        return True                             # если сделан ход
    return False                                # ходить было невозможно


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана
    draw_grid()                                 # нарисовать клетки
    draw_numbers()                              # вывести цифры
    draw_text()                                 # показать текстовую информацию


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    play(xy_to_n(*pos))                         # сделать ход


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    if key == keys.SPACE :                      # выбор кнопки Старт -> "Пробел"
        counter = 1000                          # количество перемешиваний
        while(counter != 0):
            n = choice(range(SIZE**2))          # выбор случайного хода
            if play(n):                         # если был сделан ход
                counter -= 1


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 10. Теперь функция play возвращает значение True или False, и это значение используется в функции on_key_down при перемешивании фишек.

В начале игры или в процессе игры когда фишки с числами упорядочены, необходимо запретить игроку ходить пока он не нажмёт на клавишу "пробел" и не перемешает фишки случайным образом.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
from random import choice                       # выбрать случайный элемент списка
import pgzrun
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
SIZE = 4                                        # размер игрового поля (4x4)
CELL_SIZE = WIDTH // SIZE                       # размер клетки
DIGITS_SIZE = int(CELL_SIZE / 2.2)              # размер цифр
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки
GRID_COLOR = 'dimgray'                          # цвет сетки
DIGITS_COLOR = 'sienna'                         # цвет цифр
playground = list(range(1, SIZE**2)) + [' ']    # виртуальное игровое поле


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH, CELL_SIZE):                        # линии сетки
        screen.draw.line((n, 0), (n, HEIGHT), GRID_COLOR)       # вертикальные линии
        screen.draw.line((0, n), (WIDTH, n), GRID_COLOR)        # горизонтальные линии


def xy_to_n(x, y):
    """
    Конвертирует пару координат x, y в номер клетки
    """
    col = x // (WIDTH // SIZE)
    row = y // (HEIGHT // SIZE)
    return row * SIZE + col


def draw_numbers():
    """
    Выводит в клетках игрового поля цифры,
    соответствующие игровой ситуации
    """
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    for x in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
        for y in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
            n = xy_to_n(x, y)
            screen.draw.text(str(playground[n]), center=(x, y), **dig_params)


def draw_text():
    """
    Выводит на игровое поле текстовую информацию,
    приглашение к началу игры:
    Клавиша пробел - Старт
    """
    pass


def in_order():
    """
    Проверка порядка цифр
    """
    if playground == list(range(1, SIZE**2)) + [' ']:
        return True
    return False


def play(n):
    """
    Делает ход из клетки с номером n
    в пустую клетку, если это возможно!
    """
    m = playground.index(' ')                   # индекс пустой клетки
    # если клетки n и m находятся по соседству в одном ряду или колонке
    if n in [m+1, m-1] and m // SIZE == n // SIZE or n in [m-SIZE, m+SIZE]:
        playground[n], playground[m] = playground[m], playground[n]
        return True                             # если сделан ход
    return False                                # ходить было невозможно


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана
    draw_grid()                                 # нарисовать клетки
    draw_numbers()                              # вывести цифры
    draw_text()                                 # показать текстовую информацию


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    if in_order():              # невозможно сделать ход пока цифры упорядочены
        return
    play(xy_to_n(*pos))                         # сделать ход


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    if key == keys.SPACE :                      # выбор кнопки Старт -> "Пробел"
        counter = 1000                          # количество перемешиваний
        while(counter != 0):
            n = choice(range(SIZE**2))          # выбор случайного хода
            if play(n):                         # если был сделан ход
                counter -= 1


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 11. Добавлена функция in_order.

Добавим глобальный счётчик ходов. Пусть счётчик обнуляется когда игрок нажимает на клавишу пробел и тем самым перемешивает фишки с числами.

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
from random import choice                       # выбрать случайный элемент списка
import pgzrun, pygame
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
SIZE = 4                                        # размер игрового поля (4x4)
CELL_SIZE = WIDTH // SIZE                       # размер клетки
DIGITS_SIZE = int(CELL_SIZE / 2.2)              # размер цифр
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки
GRID_COLOR = 'dimgray'                          # цвет сетки
DIGITS_COLOR = 'sienna'                         # цвет цифр
playground = list(range(1, SIZE**2)) + [' ']    # виртуальное игровое поле
counter = 0                                     # счётчик ходов


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH, CELL_SIZE):                        # линии сетки
        screen.draw.line((n, 0), (n, HEIGHT), GRID_COLOR)       # вертикальные линии
        screen.draw.line((0, n), (WIDTH, n), GRID_COLOR)        # горизонтальные линии


def xy_to_n(x, y):
    """
    Конвертирует пару координат x, y в номер клетки
    """
    col = x // (WIDTH // SIZE)
    row = y // (HEIGHT // SIZE)
    return row * SIZE + col


def draw_numbers():
    """
    Выводит в клетках игрового поля цифры,
    соответствующие игровой ситуации
    """
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    for x in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
        for y in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
            n = xy_to_n(x, y)
            screen.draw.text(str(playground[n]), center=(x, y), **dig_params)


def draw_text():
    """
    Выводит на игровое поле текстовую информацию,
    приглашение к началу игры:
    Клавиша пробел - Старт
    """
    pass


def in_order():
    """
    Проверка порядка цифр
    """
    if playground == list(range(1, SIZE**2)) + [' ']:
        return True
    return False


def play(n):
    """
    Делает ход из клетки с номером n
    в пустую клетку, если это возможно!
    """
    m = playground.index(' ')                   # индекс пустой клетки
    # если клетки n и m находятся по соседству в одном ряду или колонке
    if n in [m+1, m-1] and m // SIZE == n // SIZE or n in [m-SIZE, m+SIZE]:
        playground[n], playground[m] = playground[m], playground[n]
        return True                             # если сделан ход
    return False                                # ходить было невозможно


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана
    draw_grid()                                 # нарисовать клетки
    draw_numbers()                              # вывести цифры
    draw_text()                                 # показать текстовую информацию
    pygame.display.set_caption(TITLE)           # обновление заголовка


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    global counter, TITLE
    if in_order():              # невозможно сделать ход пока цифры упорядочены
        return
    if play(xy_to_n(*pos)):                     # сделать ход
        counter += 1                            # увеличиваем счётчик ходов
        TITLE = "Ход № " + str(counter)         # обновляем заголовок окна


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    global counter, TITLE
    if key == keys.SPACE :                      # выбор кнопки Старт -> "Пробел"
        counter = 1000                          # количество перемешиваний
        while(counter != 0):
            n = choice(range(SIZE**2))          # выбор случайного хода
            if play(n):                         # если был сделан ход
                counter -= 1
        TITLE = "Головоломка 15"                # обновляем заголовок окна


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 12. Добавлена библиотека pygame и глобальная переменная counter. Изменились функции draw, on_mouse_down и on_key_down.

Рис. 7. Обратите внимание, в заголовке окна выводится значение счётчика ходов.

В начале игры или в процессе игры когда фишки с числами упорядочены, игроку ходить запрещено, пока он не нажмёт на клавишу "пробел" и не перемешает фишки случайным образом. В это время, на игровом поле должна отображаться подсказка "Клавиша пробел - Старт".

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*AVX2.*")
from random import choice                       # выбрать случайный элемент списка
import pgzrun, pygame
"""
Pygame Zero (pgzrun) - это обёртка над Pygame,
которая упрощает создание игр,
автоматизируя многие рутинные задачи.
"""


# Настройки игры, глобальные константы и переменные
TITLE = "Головоломка 15"                        # заголовок окна
WIDTH = HEIGHT = 500                            # ширина и высота окна
SIZE = 4                                        # размер игрового поля (4x4)
CELL_SIZE = WIDTH // SIZE                       # размер клетки
DIGITS_SIZE = int(CELL_SIZE / 2.2)              # размер цифр
BACKGROUND_COLOR = 'khaki'                      # цвет кнопки
GRID_COLOR = 'dimgray'                          # цвет сетки
DIGITS_COLOR = 'sienna'                         # цвет цифр
FONT_COLOR = 'olive'                            # Цвет шрифта сообщений
playground = list(range(1, SIZE**2)) + [' ']    # виртуальное игровое поле
counter = 0                                     # счётчик ходов


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH, CELL_SIZE):                        # линии сетки
        screen.draw.line((n, 0), (n, HEIGHT), GRID_COLOR)       # вертикальные линии
        screen.draw.line((0, n), (WIDTH, n), GRID_COLOR)        # горизонтальные линии


def xy_to_n(x, y):
    """
    Конвертирует пару координат x, y в номер клетки
    """
    col = x // (WIDTH // SIZE)
    row = y // (HEIGHT // SIZE)
    return row * SIZE + col


def draw_numbers():
    """
    Выводит в клетках игрового поля цифры,
    соответствующие игровой ситуации
    """
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    for x in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
        for y in range(CELL_SIZE // 2, WIDTH, CELL_SIZE):
            n = xy_to_n(x, y)
            screen.draw.text(str(playground[n]), center=(x, y), **dig_params)


def draw_text():
    """
    Выводит на игровое поле текстовую информацию,
    приглашение к началу игры:
    Клавиша пробел - Старт
    """
    fnt_params = {'color': FONT_COLOR,'fontsize': DIGITS_SIZE // 2}
    screen.draw.text("Клавиша", (WIDTH - CELL_SIZE + 20, HEIGHT - CELL_SIZE + 30), **fnt_params)
    screen.draw.text("пробел-", (WIDTH - CELL_SIZE + 20, HEIGHT - CELL_SIZE + 50),  **fnt_params)
    screen.draw.text("Старт", (WIDTH - CELL_SIZE + 20, HEIGHT - CELL_SIZE + 70),  **fnt_params)


def in_order():
    """
    Проверка порядка цифр
    """
    if playground == list(range(1, SIZE**2)) + [' ']:
        return True
    return False


def play(n):
    """
    Делает ход из клетки с номером n
    в пустую клетку, если это возможно!
    """
    m = playground.index(' ')                   # индекс пустой клетки
    # если клетки n и m находятся по соседству в одном ряду или колонке
    if n in [m+1, m-1] and m // SIZE == n // SIZE or n in [m-SIZE, m+SIZE]:
        playground[n], playground[m] = playground[m], playground[n]
        return True                             # если сделан ход
    return False                                # ходить было невозможно


def draw():
    """
    draw() особая функция, вызывается автоматически каждый кадр,
    обычно, 60 FPS. Доступны объекты: screen, actors и др.
    Назначение: отрисовка всего, что должно быть на экране.
    """
    screen.fill(BACKGROUND_COLOR)               # очистка экрана
    draw_grid()                                 # нарисовать клетки
    draw_numbers()                              # вывести цифры
    if in_order():                              # если числа упорядочены
        draw_text()                             # показать текстовую информацию
    pygame.display.set_caption(TITLE)           # обновление заголовка


def on_mouse_down(pos):
    """
    on_mouse_down(pos) особая функция, вызывается автоматически при клике мыши
    Через параметр pos в функцию передаётся кортеж из пары координат (x, y)
    """
    global counter, TITLE
    if in_order():              # невозможно сделать ход пока цифры упорядочены
        return
    if play(xy_to_n(*pos)):                     # сделать ход
        counter += 1                            # увеличиваем счётчик ходов
        TITLE = "Ход № " + str(counter)         # обновляем заголовок окна


def on_key_down(key):
    """
    on_key_down(pos) особая функция, вызывается автоматически при нажатии
    клавиши на клавиатуре. Через параметр key в функцию передаётся
    код нажатой клавиши.
    """
    global counter, TITLE
    if key == keys.SPACE :                      # выбор кнопки Старт -> "Пробел"
        counter = 1000                          # количество перемешиваний
        while(counter != 0):
            n = choice(range(SIZE**2))          # выбор случайного хода
            if play(n):                         # если был сделан ход
                counter -= 1
        TITLE = "Головоломка 15"                # обновляем заголовок окна


pgzrun.go()                                     # Запускается главный игровой цикл

Лист. 13.

В программе листинг 13 появилась глобальная переменная FONT_COLOR - цвет шрифта сообщений.

Функция draw_text теперь выводит в 16-ю клетку игрового поля сообщение "Клавиша пробел - Старт".

Функция  функция draw изменилась таким образом, что функция draw_text теперь запускается если функция in_order возвращает значение True.

Рис. 8. Функция draw_text() выводит в 16-ю клетку игрового поля сообщение "Клавиша пробел - Старт".

Теперь нам не хватает сообщения о победе. Создадим функцию draw_win и будем вызывать её из функции draw в случае, если числа на игровом поле упорядочены, а счётчик ходов не равен 0.

Лист. 14.

Лист.

Лист.

Лист.

Лист.

Лист.

Лист.