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.

Обратите внимание, у функции draw в программе листинг 3 появился функционал. В функцию draw добавлен метод fill объекта screen. Fill (заливка) очищает screen (экран), заливая окно сгенерированное программой определённым цветом.

  • Обязательные глобальные переменные которые использует функция 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.

В программе листинг 3 мы создали глобальную константу BACKGROUND_COLOR со значением "khaki". Эту константу, в качестве параметра получает функция fill в определении функции draw. Функция fill интерпретирует значение "khaki" строкового типа, как цвет и использует этот цвет для заливки окна.

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

Образец Код Название 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. Каждый раз, когда в результате действий игрока или игрового алгоритма ситуация на игровом поле меняется, программа должна перерисовать игровое поле и тем самым привести его в соответствие сложившейся игровой ситуации. Для этой цели в библиотеке pgzrun, в соответствии с определённым алгоритмом, вызывается функция draw. В нашей программе, функция draw должна:

  • очистить окно программы (игровое поле)
  • нарисовать сетку игрового поля
  • вывести в клетки на сетке числа в соответствии с игровой ситуацией
  • вывести дополнительную текстовую информацию, если это требуется

В программе листинг 4 мы предусмотрели весь этот функционал.

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().

В программе листинг 4 созданы прототипы (определения) нужных нам функций, но не прописаны их тела. Так как в Python функция не может быть создана без тела, временно, в тело новых функций запишем оператор pass, который ничего не делает.

Теперь приступим к разработке этих функций и начнём с функции draw_grid.

def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    screen.draw.line((125,0),(125,500), "dimgray")      # вертикальная линия
    screen.draw.line((250,0),(250,500), "dimgray")      # вертикальная линия
    screen.draw.line((375,0),(375,500), "dimgray")      # вертикальная линия
    screen.draw.line((0,125),(500,125), "dimgray")      # горизонтальная линия
    screen.draw.line((0,250),(500,250), "dimgray")      # горизонтальная линия
    screen.draw.line((0,375),(500,375), "dimgray")      # горизонтальная линия

Определение функции draw_grid.

Чтобы код нашей программы легче было разрабатывать, а в дальнейшем поддерживать, желательно параметры, используемые в методе draw.line в функции draw_grid определить как глобальные константы и использовать имена этих констант.

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


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    screen.draw.line((CELL_SIZE,0),(CELL_SIZE,HEIGHT), GRID_COLOR)          # вертикальная линия
    screen.draw.line((CELL_SIZE*2,0),(CELL_SIZE*2,HEIGHT), GRID_COLOR)      # вертикальная линия
    screen.draw.line((CELL_SIZE*3,0),(CELL_SIZE*3,HEIGHT), GRID_COLOR)      # вертикальная линия
    screen.draw.line((0,CELL_SIZE),(WIDTH,CELL_SIZE), GRID_COLOR)           # горизонтальная линия
    screen.draw.line((0,CELL_SIZE*2),(WIDTH,CELL_SIZE*2), GRID_COLOR)       # горизонтальная линия
    screen.draw.line((0,CELL_SIZE*3),(WIDTH,CELL_SIZE*3), GRID_COLOR)       # горизонтальная линия

Определение некоторых глобальных констант и функции draw_grid.

Циклы позволяют существенно сократить количество  строк повторяющегося кода. В выше приведённом варианте функции draw_grid, желательно для этой цели использовать цикл.

В Python мы можем использовать цикл for или цикл while. Цикл for, чаще используется когда известно количество повторений тела цикла, а цикл while используют тогда, когда количество повторений тела цикла заранее не известно.

Функция draw_grid должна нарисовать на игровом поле три горизонтальные и три вертикальные линии. Если тело цикла будет состоять из двух строк одна из которых будет рисовать горизонтальные линии, а вторая - вертикальные, то цикл должен будет повторить эти действия три раза. Следовательно нам подойдёт цикл for.

Цикл for и функция range.

for n in range(0,10,1):
    print(n, end='; ')

Пример использования цикла for и функции range.

0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 

Результат работы программы с циклом. 

for n in range(0,500,125):
    print(n, end='; ')

Пример использования цикла for и функции range применимый в нашей программе.

0; 125; 250; 375; 

Результат работы программы с циклом. 

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. В программе в функции draw_grid для создания сетки на игровом поле используется цикл.

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

Приступим к разработке функции draw_numbers.

Функция draw_numbers выводит в клетках игрового поля числа, соответствующие игровой ситуации. В нашей программе состояние игры в любой момент времени может быть определено порядком чисел от 1 до 15 включительно и положением пустой клетки между ними или перед ними или после них. Следовательно, хранить и менять состояние игры мы сможем используя структуру из 16 элементов.

В языке Python определён тип данных список. Список в Python — это последовательность значений любого типа. Создаётся список в квадратных скобках путём перечисления значений элементов списка через запятую.

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

playground = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ' ']

Список playground, порядок элементов в котором соответствует порядку чисел на игровом поле.

Функция range(start, stop, step) создаёт диапазон из целых. Начало диапазона задаётся параметром start включительно, конец диапазона задаётся параметром stop, причём, само значение stop не включается в диапазон. Третий параметр функции range задаёт шаг в последовательности чисел. Первый и третий параметры функции range не обязательны. В случае, если эти параметры не указаны, start = 0, step = 1.

В нашей программе мы можем создать список playground используя функцию range, а также функцию list которая преобразует диапазон range в список.

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.

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 draw_win():
    """
    Выводит на игровое поле сообщение:
    ПОБЕДА!
    """
    x = CELL_SIZE + 1                                               # координата x верхнего левого угла
    y0 = 2 * CELL_SIZE                                              # координата y середины флага
    w = 2 * CELL_SIZE - 1                                           # ширина флага
    h = CELL_SIZE * 0.5                                             # высота секции флага
    screen.draw.filled_rect(Rect(x, y0 - h * 1.5, w, h), 'white')
    screen.draw.filled_rect(Rect(x, y0 - h // 2, w, h), 'blue')
    screen.draw.filled_rect(Rect(x, y0 + h // 2, w, h), 'red')
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    screen.draw.text("Победа!", center=(y0, y0 - h), **dig_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)           # обновление заголовка
    if counter > 0 and in_order():              # если победа
        draw_win()                              # вывести сообщение Победа!


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()                                     # Запускается главный игровой цикл

Лист. 14. Создана функция draw_win

Рис. 9.

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'                          # цвет сетки
BORDER_COLOR = 'gold'                           # цвет бордюра
DIGITS_COLOR = 'sienna'                         # цвет цифр
FONT_COLOR = 'olive'                            # Цвет шрифта сообщений
playground = list(range(1, SIZE**2)) + [' ']    # виртуальное игровое поле
counter = 0                                     # счётчик ходов


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH+1, CELL_SIZE):                              # линии бордюра
        screen.draw.filled_rect(Rect(n-7, 0, 15, HEIGHT), BORDER_COLOR) # вертикальные
        screen.draw.filled_rect(Rect(0, n-7, WIDTH, 15), BORDER_COLOR)  # горизонтальные
    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 draw_win():
    """
    Выводит на игровое поле сообщение:
    ПОБЕДА!
    """
    x = CELL_SIZE + 1                                               # координата x верхнего левого угла
    y0 = 2 * CELL_SIZE                                              # координата y середины флага
    w = 2 * CELL_SIZE - 1                                           # ширина флага
    h = CELL_SIZE * 0.5                                             # высота секции флага
    screen.draw.filled_rect(Rect(x, y0 - h * 1.5, w, h), 'white')
    screen.draw.filled_rect(Rect(x, y0 - h // 2, w, h), 'blue')
    screen.draw.filled_rect(Rect(x, y0 + h // 2, w, h), 'red')
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    screen.draw.text("Победа!", center=(y0, y0 - h), **dig_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)           # обновление заголовка
    if counter > 0 and in_order():              # если победа
        draw_win()                              # вывести сообщение Победа!


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()                                     # Запускается главный игровой цикл

Лист. 15. Изменена функция draw_grid.

Рис. 10.

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'                          # цвет сетки
BORDER_COLOR = 'gold'                           # цвет бордюра
DIGITS_COLOR = 'sienna'                         # цвет цифр
FONT_COLOR = 'olive'                            # Цвет шрифта сообщений
playground = list(range(1, SIZE**2)) + [' ']    # виртуальное игровое поле
counter = 0                                     # счётчик ходов


def draw_grid():
    """
    Рисует игровое поле в клетку
    """
    for n in range(0, WIDTH+1, CELL_SIZE):                              # линии бордюра
        screen.draw.filled_rect(Rect(n-7, 0, 15, HEIGHT), BORDER_COLOR) # вертикальные
        screen.draw.filled_rect(Rect(0, n-7, WIDTH, 15), BORDER_COLOR)  # горизонтальные
    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 draw_win():
    """
    Выводит на игровое поле сообщение:
    ПОБЕДА!
    """
    x = CELL_SIZE + 1                                               # координата x верхнего левого угла
    y0 = 2 * CELL_SIZE                                              # координата y середины флага
    w = 2 * CELL_SIZE - 1                                           # ширина флага
    h = CELL_SIZE * 0.5                                             # высота секции флага
    screen.draw.filled_rect(Rect(x, y0 - h * 1.5, w, h), 'white')
    screen.draw.filled_rect(Rect(x, y0 - h // 2, w, h), 'blue')
    screen.draw.filled_rect(Rect(x, y0 + h // 2, w, h), 'red')
    dig_params = {'color': DIGITS_COLOR,'fontsize': DIGITS_SIZE}    # цвет и размер цифр
    screen.draw.text("Победа!", center=(y0, y0 - h), **dig_params)


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


def play(n):
    """
    Сдвигает строку или колонку начиная клетки с номером n
    в пустую клетку, если это возможно!
    """
    m = playground.index(' ')                                           # индекс пустой клетки
    step = 0                                                            # Шаг и направление
    if m // SIZE == n // SIZE:                                          # m и n в одной строке
        step = -1                                                       # пустая клетка справа
        if m < n:                                                       # пустая клетка слева
            step = 1
    if m % SIZE == n % SIZE:                                            # m и n в одной колонке
        step = -SIZE                                                    # пустая клетка снизу
        if m < n:                                                       # пустая клетка сверху
            step = SIZE
    if step != 0:
        for i in range(m, n, step):
            playground[i], playground[i+step] = playground[i+step], playground[i]   # ход
        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)           # обновление заголовка
    if counter > 0 and in_order():              # если победа
        draw_win()                              # вывести сообщение Победа!


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()                                     # Запускается главный игровой цикл

Лист. 16. Изменена функция play.