Как я делал змейку на LabVIEW

«от нефиг делать / just for lulz» посвящается…



Змейка. Игра старая (как утверждает Wikipedia: середины или даже конца 1970х годов), но от того не менее интересная, по крайней мере в качестве примера несложного но интересного алгоритма для иллюстрации возможностей графического программирования на LabVIEW 2009.



Собственно алгоритм.



В основу игры положим двумерный массив целых чисел, который и будет представлять собою игровое поле. В этом массиве и будет располагаться змея. При движении головы змеи в новое поле массива (в зависимости от направления движения змейки) будет добавляться время жизни конкретной клетки.

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

Увеличение длины змеи происходит при съедании некоей «еды». Еда — точка на игровом поле, описываемая лишь двумя ее координатами. Как только она съедается, то есть координаты головы змеи совпадают с координатами еды, длина змейки увеличивается на 1, а еда получает новую пару случайно выбранных координат.

Уменьшение длины змеи происходит при откусывании собственного хвоста. Да, я понимаю, что это получается уже не змея, а червяк какой-то, и что в классической змейке следовало бы заканчивать игру гамовером, но не суть. Это делается так же просто: если в какой-то момент голова змеи попадает в ячейку уже занятую её телом, то дина её уменьшается на то значение, которое записано в месте укуса. Оставшийся хвост тихонько долежит на поле, исчерпывая свое время жизни.

Задачка проста и понятна. Обратимся теперь к LabVIEW.

Интерфейс игры.



Создадим на форме элемент — массив и простейший из булевых индикаторов «Flat Square Button» с панели инструментов ClassicBoolean.



У булева индикатора скроем его заголовок и раскрасим его так, чтобы при значении false он сам и его рамка были бы белыми, а при true – темно-зеленым и салатовой соответственно. Размеры индикатора зададим равными 10 х 10 пиксель.

Поместим индикатор в массив. Сделаем массив двумерным, а так же скроем индикаторы индекса массива и его заголовок. Сам же массив растащим так, чтобы он отображал 32 х 24 копии индикатора.

Раскрасим форму в приятные цвета. Этот пункт необязателен, но является моим любимым.

После всех манипуляций, игровое поле со случайным набором true и false значений выглядит так.



Основной цикл программы.



В основе программы будет положен цикл While, завершаемый по условию false, то есть никогда. Внутрь цикла поместим терминал нашего массива, а так же подключим к массиву следующие переменные, которые будут хранить основные параметры игры. Это координаты головы змеи, направление ее движения и ее длина, координаты еды, объединенные в соответствующие кластеры, а также целочисленный массив, упоминавшийся в первой главе. Выглядит все это так:



Содержимое цикла


Внутри цикла добавим Event Structure при помощи которой мы будем впоследствии захватывать события с ц целью обработки сигналов с клавиатуры о нажатии управляющих стрелок, и последовательную структуру, внутри которой шаг за шагом расположим всю последовательность обработки каждого шага змейки. И первым шагом станет пробежка по всему массиву с уменьшением всех ненулевых элементов на 1. Делается это при помощи двух вложенных один в другой циклов for.



Следующий шаг — изменение координат головы в соответствии с направлением движения и изменение длины змеи в случае поедания еды, либо откусывания собственного хвоста. Изменение координат происходит при помощи Case — структуры к терминалу условия которой подключена переменная направление. В зависимости от значения направления выполняется одна из вкладок структуры, на которой на 1 увеличивается либо уменьшается соответствующая координата. Если при этом координата выходит за пределы максимума либо нуля, то она обнуляется, либо устанавливается максимальной, чтобы обеспечить так называемое «прохождение сквозь край экрана».

Если новые координаты попарно равны с координатами еды (два оператора сравнения и один оператор And) то длина увеличивается на 1, а для еды генерируются новые координаты при помощи генератора случайных чисел (от 0 до 1) и коэффициентов. Иначе (вкладка false не показана) все три переменные получают прежние значения.

Если элемент массива с координатами нового положения головы не равен нулю, то от длины змеи отнимается значение этого элемента. Если при этом значение длины змеи становится меньше либо равным нулю, то ему присваивается значение 1, поскольку голова у змеи остается всегда.

Кластеры значений собираются обратно вбирая в себя новые значения. Значения которые на данном шаге не изменялись можно заново не вбирать – они останутся прежними и так.



В следующем шаге, выполняется запись значения длины змеи в новое поле в качестве его времени жизни, генерация булева массива и выгрузка его в индикатор на переднюю панель. Это не трудно. Не забудем только непосредственно перед выгрузкой дополнительно добавить true в ячейку с координатами, равными координатам еды.



Управление



Вот и вся, собственно говоря змейка, осталось только прикрутить управление. Для него мы заранее уже определили Event — структуру, или говоря по-русски структуру событий. Эта многовкладочная структура ждет в течение заданного интервала времени наступления одного из предопределенных в ней событий, и выполняет соответствующую вкладку когда дождется (ну или вкладку timeout если не дождется). События могут быть разнообразными, начиная от кликов мышкой по передней панели приложения, и заканчивая системными событиями. Нас же будет интересовать событие «Key down» клавиатуры.

Переместим структуру событий в последний кадр последовательной структуры и заведем в нее проводник, содержащий кластер Змея (поскольку именно его значение будем изменять при нажатии на клавиши клавиатуры).

Добавим событие, в качестве источника события (Event Source) выберем: . А в качестве события: Key -> Key Down.

Из параметров события возвращаемых структурой нас интересует лишь параметр VKey, содержащий код клавиши, спровоцировавшей событие. Добавим Case — структуру, которая будет выдавать 0 если была нажата клавиша влево, 1 — если вниз, 2 — вправо, а 3 — вверх. а в случае нажатия любой другой клавиши значение направления оставалось бы неизменным. дополнительная задержка добавлена, с той целью, чтобы срабатывание структуры event не укорачивало бы интервал между тактами.



Вот собственно и все. Можно запускать и играться. Конечно неплохо было бы еще выводить на переднюю панель количество набранных очков, уровень сложности, от которого бы зависела скорость игры (величина задержки между тактами) и еще многое другое, ну да улучшать что угодно можно до бесконечности а иллюстративных целей дальнейшие улучшения не несут.

Для желающих, ознакомиться с полной схемой приложения можно тут.
Поделиться публикацией
Комментарии 24
    0
    а нельзя ее научить поворачивать если в следующей клетке препятствие?
      +4
      А смысл игры тогда в чем?
        +1
        В самом деле все возможно, но зачем? Если только в качестве следующей идеи для AI-Challenge битва змеек +)
          0
          именно для битвы змеек
          0
          upd1: Если идея интересна могу дополнительно описать реализацию алгоритма поиска пути A* на LabVIEW и прицепить ее к змейке.
          –4
          Мсье знает толк в извращениях!
            +8
            Не такое уж и извращение. А скорее просто напросто иллюстрация графического ЯП
              +3
              На самом деле, как мне кажется, это одна из фраз, за счет которых набирают плюсики :))
                +1
                Ну, как видите, не всегда)
            0
            Как только открыл пост, сразу вспомнились школьные года и RoboLab от Lego RCX.
              +1
              Да, а мы как то университете делали на LabView керлинг, как это не извращенно звучит, агенты должны были приложить силу к броску камня и изменять свойства льда перед камнем, что бы камень попал в цель. Правда так и не доделали, семестр кончился.
              +7
              Думаю надо перенести в «Ненормальное програмирование», ибо действительно ненормальное…
                +3
                Да особо «ненормального» в общем-то нет ничего. Ненормальное программирование — это Brainfuck или змейка на bash. А LabVIEW — это немного непривычно по сравнению с традиционными текстовыми языками — это да. На самом деле там самые обычные конструкции for, while, if then и т.д. Я вот всё надеюсь, что когда-нибудь на хабре появится отдельный блог по LabVIEW.
                  +1
                  Можно сделать блог «визуальное программирование», и свести туда LabVIEW, Simulink и иже с ними.

                  Перед катом в каждом посте будет обязательная строка: «Внимание! Много картинок!»)))
                +3
                Вспоминаю, как я, ярый фанат CLI, ненавидел LabView на третьем курсе универа. Автор — молодец. Добавьте еще управление через джойстик, это не должно составить для вас труда. LabView всё-таки. И звуковые эффекты.
                  0
                  Спасибо за предложения. Пожалуй и в самом деле стоит написать продолжение этой статьи.
                  +2
                  >>Таким образом время жизни клетки и определяет длину змейки.

                  Интересный подход, мне такое не приходило в голову.
                    +5
                    А вот это, признаться, мое «know how» и я им немножко горжусь™.

                    На меня снизошло озарение во время игры в змейку на моем телефоне. Спасибо Nokia за это.
                    +2
                    Подход к «отсеченному» хвосту (оставляем умирать) мне представляется неверным. Рассмотрите вариант — голова врезается в середину тела и начинает двигаться в сторону старого хвоста.
                    При каждом таком шаге длина змейки будет уменьшаться на несколько единиц, а должна сохраняться.
                    Причем кончик нового хвоста (как производный от длины) будет прыгать сразу на несколько ячеек.
                    Если Вы его подсветите, то будет выглядеть так, как-будто змейка «испражняется» своим телом.
                      0
                      Да, вы правы. Пожалуй стоило бы или убирать хвост с поля совсем (уменьшив все времена жизни ячеек на откушенную длину) или переводить хвост в новый массив — массив препятствий.
                      +1
                      Отлично! Помню программировал на Labview, после перла и С этот чудесный графический язык перевернул сознание) Некоторые вещи на нем можно сделать за день, когда как на CLI языках на это потребовалось бы недели. Очень хорошая среда для RAD.
                        +1
                        Вообще если вынести Event Structure из основного цикла в отдельный параллельный, будет более по LabVIEW-шному.
                          0
                          Задумался на счет того, что какое максимальная длина змейк. Например в квадрате 32х32
                            0
                            Спасибо за идею, решил рассмотреть такую задачку на своеобразном мастер классе))
                            Есть ещё один вроде не упомянутый в дискуссии баг, при употреблении «еды» длинна змейки возрастает только через число ходов равное её текущей длине, к тому моменту, как новое значение «головы» попадает в последнюю ячейку хвоста. Чтобы избежать этого, можно в кейсе, где увеличивается значение длинны при условии совпадения координат «головы» и «еды» делать инкремент всех ненулевых ячеек. Тогда длинна возрастёт на итерации следующей за поеданием.
                            Хотя непосредственно в процессе игры этот лаг всё равно не очень заметен.

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое