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.