- PVSM.RU - https://www.pvsm.ru -
В прошлый раз [1] я показал, как можно легко и быстро написать игру «змейка» на языке программирования FBD [2], загрузить программу в контроллер и наконец-то заставить аппаратуру [3], занимающуюся управлением и автоматическим регулированием производства электроэнергии и тепла для нас всех, заняться еще и чем-нибудь «полезным».
Однако от людей, не понаслышке знакомых с работой оперативного персонала на электростанциях, я получил важное замечание о том, что «змейка» категорически не подходит для АСУ ТП по объективным причинам. Во-первых, несмотря на то, что со всей автоматикой на станции управляется система АСУ ТП и она же занимается регулированием и защитами, реалии жизни таковы, что от оператора также требуется следить за технологическим процессом для оперативного вмешательства в него по мере необходимости. Поэтому игрушка должна быть такой, чтобы (в отличие от ранее представленной) не занимать полностью внимание оператора, позволять ему без ущерба для игры переключаться между приложениями и делать что-нибудь. А во-вторых, сама игрушка «змейка» весьма динамичная и требует быстрого (а на высоких уровнях вообще мгновенного и ювелирного) нажимания на кнопки, что может легко привести скажем к небольшой ошибке: например вместо управления игрушкой можно случайно поуправлять каким-нибудь важным технологическим оборудованием, которое при данном режиме работы трогать было ну никак нельзя. Разумеется ничего страшного не произойдет т.к. в любом случае отработает защита, но останов турбины или отключение котла защитой, это вещи, приводящие к солидным финансовым потерям и лишнему труду по запуску их обратно в работу.
Все это наводить на очевидную мысль, что реализовывать нужно спокойные игрушки на логику. Как вариант — всевозможные пасьянсы или всем известный «Сапер». Ввиду того, что пасьянсы всех видов требуют изображения карт, который не были сходу найдены в интернете (разумеется рисунки карт найти легко, но у меня были особые требования к размеру и качеству а так же оформлению карт) было решено реализовать игрушку «Сапер» [4].
Помимо этой есть еще одна важная причина в реализации именно этой игры. А именно желание еще раз наглядно показать как легко и весело программировать на языке FBD.
Но для начала несколько вступительных слов (для тех кто еще не слишком устал читать).
Наверное каждый, кто прочтет этот пост до конца, скажет (или подумает): «Фигня! Да я то же самое на С (С++, Delphi, JS, и т.д.) напишу в 30 строчек кода». И я с этим согласен. Но есть одно но. Прежде чем написать что нибудь на языке высокого уровня в 30 строчек кода нужно всего-навсего
изучить этот язык высокого уровня.
А писать программы на FBD может начать любой. Более того, основные навыки программирования на FBD прививают детям дошкольного возраста.
Другими словами, программирование на языке FBD просто и понятно на интуитивном уровне.
Deranged, 20 декабря 2013 в 12:25
Это и не FBD. Там есть поддержка типов данных, в том числе структур. Так же данные делятся на потенциальные (мгновенное значение) и команды (буферизуемые значения). Так же имеется встроенная поддержка качества значений. Можно проводить обратные связи, они выделяются зеброй. При этом в качестве начальных значений берутся значения типа данных по умолчанию (прописывается в самом типе).
В общем там вагон всего, еще я планировал впилить туда контроль порядка выполнения в виде связей и поддержку условий. Тогда был бы полный Франкенштейн из FBD, SFC и обычных блок-схем.
Разумеется и для этого языка есть серьезные алгоритмы, борьба за ресурсы контроллера, оптимизацию выполнения техпрограммы, оптимизацию памяти и быстродействия в целом. Умные люди ломают головы над тем, как создать идеальные алгоритмы из простейших блоков. Но т.к. эта статья носит чисто ознакомительный характер, то мы опустим все эти тонкости и перейдем наконец непосредственно к самому программированию.
Но перед этим небольшой вопрос на сообразительность. Кто сам догадается — «респект и уважуха», для нетерпеливых же — ответ в спойлере ниже.
Так вот — игра «Змейка» и игра «Сапер» это одна и та же игра с точки зрения программирования на FBD. Почему? И что у них общего, что позволяет сделать такое утверждение?
Итак, начинаем программировать нашу задачу.
Лично мое мнение заключается в том, что 80% программирования на языке FBD заключается в том, чтобы четко себе представить, а что же такое мы хотим получить в конечном итоге. И так только мы добиваемся такого понимания, то 80% задачи решены и остается буквально немного работы по наброске кода и его причесыванию. Сейчас попробую продемонстрировать этот принцип на практике.
Предположим что поле для «сапера» у нас будет по аналогии со «змейкой» 20х20 клеток. Следовательно нам понадобится 400 ячеек памяти, в каждой из которых будет обрабатываться каждая клетка поля. Чтобы структурировать программу — разобьем все ячейки на строки. Таким образом нам понадобится двадцать алгоритмов строк, каждый из которых будет состоять из 20 алгоритмов ячеек. Т.е. мы пришли к пониманию основы программы.
Рассмотрим подробнее алгоритм строки. А точнее то, какие данные нам нужно иметь на входе и какие — на выходе этого алгоритма. Предлагаю начать с конца — т.е. с выходных данных.
— Во-первых, нам понадобится выходная строка данных, определяющая состояние каждой ячейки для отрисовки ее в человеко-машинном интерфейсе (или проще говоря в операторской станции).
— Во-вторых, нужен вектор, показывающий в каких ячейках находятся бомбы, а какие ячейки безопасны.
— В-третьих, нужен признак того, что игрок «наступил» на поле с бомбой и игра проиграна, назовем этот выход «Big_Bada_Boom».
— В-четвертых, для удобства в «сапере» заложена фишка, что при попадании на клетку не имеющую соседства с бомбой, автоматически открываются все пустые клетки, граничащие с ней. Т.е. нам понадобится выход, посылающий обратную команду «открыть клетку» самой программой.
— В-пятых, для алгоритма установки бомб нам понадобится выход, показывающий, сколько бомб находится в данной строке. Разумеется это значение можно получить напрямую из вектора, описанного в пункте 2, достаточно просто сложить все ненулевые биты. Но для удобства внесем эту функцию внутрь макроса.
— В-шестых, для ведения статистики нам понадобится знать, сколько еще закрытых клеток осталось в строке.
Вот и все, что нам нужно для полного функционала игры «сапер».
Всего 6 выходов с данными.
Теперь подумаем, что же нам нужно иметь на входе, чтобы суметь сформировать требуемые нам выходы.
— Логично предположить, что главный вход алгоритма это команда от игрока «Открыть ячейку». Ведь это основной смысл игры.
— Для наглядности программирования задействуем еще один вход — «Поставить на клетку флаг», помечающий что там бомба и не дающий случайно нажать на эту клетку и взорваться.
— Для формирования статуса каждой ячейки (а мы то с вами помним, что если в ячейке нет бомбы, то она показывает сколько бомб находится в клетках, граничащих с ней) понадобится завести вектора со строки выше и строки ниже. Сделаем алгоритм универсальным и добавим три входа: вход для вектора с бомбами в строке выше текущей, вход для вектора с бомбами в текущей строке и вход для вектора с бомбами в строке ниже текущей.
— Для запуска новой игры и переписывания значений ячеек добавим логический вход «Новая игра».
— Как уже упоминалось ранее, если клетка пустая и рядом нет клеток с бомбами, то должны автоматически открываться соседние с ней клетки. Добавим для этого вход «Открыть программой».
— Ну и разумеется какая же это игра без заминированного поля. Значит нам понадобится вход «Установить бомбы».
Всего получилось 8 входов.
Вот собственно и все, что нам нужно от главного алгоритма программы.
Набиваем быстро макрос с нашими входами и выходами и получаем вот такой алгоритм:
или если раскрыть его:
Теперь наполним наш алгоритм смыслом. Как я уже говорил — основа наполнения нашего главного алгоритма это макрос «Ячейка памяти», которых будет 20 штук.
Попробуем продумать, что нам может понадобиться от каждой ячейки поля игры «сапер», и что мы должны для этого подавать на вход макроса. Начнем опять с конца, т.е. с выходов. А когда сформулируем все данные, что нам нужны, то станет понятно, что нужно иметь на входе, чтобы их получить.
— Для начала у ячейки должен быть выход, показывающий статус этой ячейки. Вы никогда не задумывались, сколько состояний может принимать каждая ячейка поля в игре «сапер»?
— Отдельно для подсчетов и прочей обработки вынесем логические выходы «Ячейка закрыта» и «В ячейке установлена бомба».
— Как уже говорилось — если открыта пустая ячейка, то она должна автоматически открыть соседние с ней ячейки. соответственно нам нужнен еще логический признак «Открыть соседей».
— Ну и в конце-концов если игрок ошибся и «наступил» на поле с бомбой, то нужно сформировать логический признак того, что игра проиграна. назовем его «Bada_Boom».
Итого всего 5 выходов.
Попробуем определиться, что же нам нужно для того, чтобы сформировать требуемые выходы.
— Прежде всего алгоритм должен принимать команду от игрока «Открыть поле».
— Для простоты отдельной командой заводим сюда еще и команду «Поставить флаг».
— Как я уже говорил ранее — каждая клетка в поле должна знать, сколько заложено бомб в соседних клетках. заведем для этого отдельный вход «Соседи».
— Для того, чтобы случайным образом установить вначале бомбы нужен логический признак «Здесь бомба» по которому клетка будет минироваться.
— Ну и разумеется для начала новой игры должен быть признак сброса, который обнуляет всю информацию в клетках.
Набиваем и этот макрос получая следующий алгоритм:
Теперь запрограммируем сам алгоритм «Ячейка памяти». Он предельно простой:
Вот этот примитивный алгоритм и есть основа всей игры. остались только некоторые причесывания программы и допиливание функционала.
Самые наблюдательные читатели уже заметили, что здесь выполняется ну явно лишняя операция. Я получаю биты, описывающие соседей, упаковываю их в вектор, а затем опять распаковываю и суммирую. Т.е. операции по упаковке-распаковке явно избыточны. Но я оставил такой вариант для отладки и наглядности.
Отдельно хотел упомянуть про еще один макрос. Перед стартом игры нам нужно случайным образом расположить в поле мины. Но что делать, если нет генератора случайных числе и команда «Random» недоступна. Разумеется в интернете есть множество алгоритмов формирования псевдослучайных последовательностей. Но они довольно сложные и их реализация сама по себе заслуживает отдельной статьи. Поэтому пришлось пойти привычным путем и сделать макрос «Генератор Random».
Итак, все части нашей программы готовы. Соединяем их между собой и делаем обвязку:
Алгоритмы 36-40 служат для проверки корректности заданного числа бомб игроком. Здесь я для отладки ограничил минимальное и максимальное количество бомб в 0 и 400 штук соответственно. При задании числа, выходящего за этот диапазон, оно автоматически приравнивается к ближайшей границе.
Проверка на выполнение условий победы предельно простая. Считаются все закрытые клетки. Как только их количество станет равным заданному количеству бомб и триггер «RS1» («Game_over») не взведен, взводится триггер «RS3» («Victory»).
Нам осталось рассмотреть всего 2 алгоритма:
Алгоритм «ИЛИ2». На нем по логическому ИЛИ собираются выходы всех трех триггеров. И по выходу алгоритма «ИЛИ2» (когда он равен единице) выставляется блокировка на действия игрока. Т.е. пока хоть один из триггеров взведен (Т.е. или игрок уже победил, или он уже проиграл, или идет установка мин для новой игры) игроку запрещаются любые нажатия в поле. Он не может ни открывать ячейки, ни ставить флажки.
Алгоритм «Секундомер1» служит секундомером для отсчета времени, затраченного на игру. Подсчитываем количество закрытых ячеек. В начальный момент каждой игры оно равно 400. Как только число закрытых ячеек становится меньше чем 400, запускается таймер, который останавливается по взведению одного из трех триггеров.
Вот собственно и вся программа.
Делается это буквально за пару минут. Рисуется 12 картинок для клетки (я для этого использовал paint) и ставится их отображение в зависимости от статуса ячейки. Далее эта клетка копируется 400 раз (тут пугаться не надо, т.к. 400 клеток это всего навсего девять операций Ctrl+c — Ctrl+v) и получается минное поле. Находим в интернете первую же попавшуюся подходящую по смыслу картинку для логотипа «сапера». Прикручиваем отображение таймера и кнопку «Новая игра».
А теперь осталось самое сложное — выбрать картинки для победы и поражения в игре. Тут я взял первые же попавшиеся в поисковике картинки по запросам «атомный взрыв» и «Victory». И все — игра готова.
Тот, кто смог осилить этот пост целиком наверняка заметил, что мы написали пусть и простую, но все-таки не уровня «Hello, World!» программу, и при этом нам не потребовалось абсолютно никаких справочников, хелпов, сидений на форумах программистов, курения мануалов и т.п. Все что было нужно мы реализовали за пару часов (и то лично у меня большая часть времени ушла на красивую расстановку алгоблоков для картинок), при этом использовав понятные даже школьнику алгоритмы сложения, вычитания, умножения и алгоритмы логики «И», «ИЛИ». Плюс задействовали несколько простейших RS-триггеров. Самый сложный алгоритм, использованный в программе, это «Интегратор». И то мы с вами использовали его просто как сумматор с обратной связью и проверкой границ. Т.е. фактически можно заменить интегратор на алгоритм «Сложение» и два алгоритма «Сравнение».
Другими словами, программировать на FBD просто и весело, и на начальном уровне не требуется вообще никаких знаний и навыков кроме понимания элементарной логики. Главным при этом является четкое представление себе конечного результата. Если оно имеется, то сама программа является его логическим довершением. Если же представить «А что же мы хотим в итоге получить» пока не получается, то возможно стоит четче сформулировать задачу. Сразу оговорюсь, что все вышесказанное применимо не ко всем проектам, реализованным на FBD, а к простым маленьким задачкам, вроде той, что приведена в нашем примере. Т.к. для огромных проектов на десятки тысяч сигналов соединить это все в голове наверное не получится ни у кого. Но и в этом случае огромный проект разбивается на небольшие подзадачи, где наш подход уже становится вполне применим (разумеется с учетом общих требований к проекту).
Т.к. язык FBD универсальный и мы в программе задействовали простейшие алгоритмы, являющиеся базовыми для любых реализаций, то данную программу можно с минимальными усилиями воспроизвести на любом контроллере, как отечественной разработки, так и иностранных (например на контроллерах фирмы Siemens или ABB).
Нереализованными остались три функции классической игры сапер.
Первая, это возможность в настройках задавать размер поля. Делается это несложно. Собственно говоря просто алгоритмы «Ячейка памяти» копируются столько раз, сколько клеток должно быть в поле. Проблема тут одна: отсутствие динамической памяти, т.е. невозможности «на лету» добавлять или убирать какие либо данные. А из этого следует простой вывод — сделать изменяющиеся размеры поля очень просто — нужно сначала запрограммировать задачу под максимальный размер поля, а потом просто выставлять логический признак на незадействованные ячейки, которые по этому признаку не будут отображаться в графическом интерфейсе и не будут обрабатываться при игре.
Вторая — это подмена ячейки при первом клике, если в ней изначально находилась бомба. Т.е. в нашей программе в отличие от «сапера» в Windows есть шанс проиграть на первом же клике. Это делается тоже легко, достаточно проверять пришедший сигнал на триггер «Game_over» и количество закрытых клеток. Если у нас число закрытых клеток 399 и пришел сигнал на взведение триггера «Game_over», то нужно заблокировать этот сигнал, сбросить ту ячейку, на которую был произведен клик и запустить алгоритм случайной установки еще одной мины, при этом заблокировав возможность установки мины в уже открытую ячейку.
Ну и как видно из описания — не реализована функция сбора статистики. Т.е. не запоминается лучший результат, имя человека, поставившего рекорд. Соотношение побед и поражений и т.п. Но это все настолько тривиально, что делается буквально парой щелчков мыши, поэтому эту функцию оставляю на реализацию всем желающим.
Спасибо всем, кто дочитал этот пост до конца. Надеюсь было интересно.
Автор: OPCSenator
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/news/59109
Ссылки в тексте:
[1] прошлый раз: http://habrahabr.ru/post/205324/
[2] FBD: http://ru.wikipedia.org/wiki/FBD
[3] аппаратуру: http://ru.wikipedia.org/wiki/%D0%9F%D0%A2%D0%9A_%D0%9A%D0%B2%D0%B8%D0%BD%D1%82
[4] «Сапер»: http://ru.wikipedia.org/wiki/%D0%A1%D0%B0%D0%BF%D1%91%D1%80_(%D0%B8%D0%B3%D1%80%D0%B0)
[5] Источник: http://habrahabr.ru/post/218057/
Нажмите здесь для печати.