Небольшая игрушка «Сапер» не в 30 строк

в 13:31, , рубрики: Без рубрики

Здравствуйте.

В прошлый раз я показал, как можно легко и быстро написать игру «змейка» на языке программирования FBD, загрузить программу в контроллер и наконец-то заставить аппаратуру, занимающуюся управлением и автоматическим регулированием производства электроэнергии и тепла для нас всех, заняться еще и чем-нибудь «полезным».

Однако от людей, не понаслышке знакомых с работой оперативного персонала на электростанциях, я получил важное замечание о том, что «змейка» категорически не подходит для АСУ ТП по объективным причинам. Во-первых, несмотря на то, что со всей автоматикой на станции управляется система АСУ ТП и она же занимается регулированием и защитами, реалии жизни таковы, что от оператора также требуется следить за технологическим процессом для оперативного вмешательства в него по мере необходимости. Поэтому игрушка должна быть такой, чтобы (в отличие от ранее представленной) не занимать полностью внимание оператора, позволять ему без ущерба для игры переключаться между приложениями и делать что-нибудь. А во-вторых, сама игрушка «змейка» весьма динамичная и требует быстрого (а на высоких уровнях вообще мгновенного и ювелирного) нажимания на кнопки, что может легко привести скажем к небольшой ошибке: например вместо управления игрушкой можно случайно поуправлять каким-нибудь важным технологическим оборудованием, которое при данном режиме работы трогать было ну никак нельзя. Разумеется ничего страшного не произойдет т.к. в любом случае отработает защита, но останов турбины или отключение котла защитой, это вещи, приводящие к солидным финансовым потерям и лишнему труду по запуску их обратно в работу.

Все это наводить на очевидную мысль, что реализовывать нужно спокойные игрушки на логику. Как вариант — всевозможные пасьянсы или всем известный «Сапер». Ввиду того, что пасьянсы всех видов требуют изображения карт, который не были сходу найдены в интернете (разумеется рисунки карт найти легко, но у меня были особые требования к размеру и качеству а так же оформлению карт) было решено реализовать игрушку «Сапер».

Помимо этой есть еще одна важная причина в реализации именно этой игры. А именно желание еще раз наглядно показать как легко и весело программировать на языке FBD.

Несколько вступительных слов

Но для начала несколько вступительных слов (для тех кто еще не слишком устал читать).

Наверное каждый, кто прочтет этот пост до конца, скажет (или подумает): «Фигня! Да я то же самое на С (С++, Delphi, JS, и т.д.) напишу в 30 строчек кода». И я с этим согласен. Но есть одно но. Прежде чем написать что нибудь на языке высокого уровня в 30 строчек кода нужно всего-навсего

Не удержался

Небольшая игрушка «Сапер» не в 30 строк

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

Думаю многие вспомнят как в детстве...
… под видом развивающих игр из кубиков с буквами собирали слова:

Например из букв...

Небольшая игрушка «Сапер» не в 30 строк

Собирали слово...

Небольшая игрушка «Сапер» не в 30 строк

Другими словами, программирование на языке FBD просто и понятно на интуитивном уровне.

Небольшая ремарка
Тут я по старой привычке говорю FBD, хотя по словам одного из главных разработчиков всего этого:

Deranged, 20 декабря 2013 в 12:25
Это и не FBD. Там есть поддержка типов данных, в том числе структур. Так же данные делятся на потенциальные (мгновенное значение) и команды (буферизуемые значения). Так же имеется встроенная поддержка качества значений. Можно проводить обратные связи, они выделяются зеброй. При этом в качестве начальных значений берутся значения типа данных по умолчанию (прописывается в самом типе).
В общем там вагон всего, еще я планировал впилить туда контроль порядка выполнения в виде связей и поддержку условий. Тогда был бы полный Франкенштейн из FBD, SFC и обычных блок-схем.

Разумеется и для этого языка есть серьезные алгоритмы, борьба за ресурсы контроллера, оптимизацию выполнения техпрограммы, оптимизацию памяти и быстродействия в целом. Умные люди ломают головы над тем, как создать идеальные алгоритмы из простейших блоков. Но т.к. эта статья носит чисто ознакомительный характер, то мы опустим все эти тонкости и перейдем наконец непосредственно к самому программированию.

Но перед этим небольшой вопрос на сообразительность. Кто сам догадается — «респект и уважуха», для нетерпеливых же — ответ в спойлере ниже.
Так вот — игра «Змейка» и игра «Сапер» это одна и та же игра с точки зрения программирования на FBD. Почему? И что у них общего, что позволяет сделать такое утверждение?

Ответ

Все очень просто. И в игре «Змейка» и в игре «Сапер» присутствует поле, состоящее из ячеек. Просто ячейки в этом поле принимают разные значения и по разному отображаются. Т.е… другими словами мы можем взять программу игры «Змейка», ее графический интерфейс, и за полчаса-час работы переделать это все в игру «Сапер», ведь по сути нам придется изменить только один алгоритм и перерисовать одну ячейку поля..

Начинаем программировать

Итак, начинаем программировать нашу задачу.
Лично мое мнение заключается в том, что 80% программирования на языке FBD заключается в том, чтобы четко себе представить, а что же такое мы хотим получить в конечном итоге. И так только мы добиваемся такого понимания, то 80% задачи решены и остается буквально немного работы по наброске кода и его причесыванию. Сейчас попробую продемонстрировать этот принцип на практике.

Предположим что поле для «сапера» у нас будет по аналогии со «змейкой» 20х20 клеток. Следовательно нам понадобится 400 ячеек памяти, в каждой из которых будет обрабатываться каждая клетка поля. Чтобы структурировать программу — разобьем все ячейки на строки. Таким образом нам понадобится двадцать алгоритмов строк, каждый из которых будет состоять из 20 алгоритмов ячеек. Т.е. мы пришли к пониманию основы программы.

Алгоритм «Строка»

Рассмотрим подробнее алгоритм строки. А точнее то, какие данные нам нужно иметь на входе и какие — на выходе этого алгоритма. Предлагаю начать с конца — т.е. с выходных данных.

Выходы алгоритма

— Во-первых, нам понадобится выходная строка данных, определяющая состояние каждой ячейки для отрисовки ее в человеко-машинном интерфейсе (или проще говоря в операторской станции).
— Во-вторых, нужен вектор, показывающий в каких ячейках находятся бомбы, а какие ячейки безопасны.
— В-третьих, нужен признак того, что игрок «наступил» на поле с бомбой и игра проиграна, назовем этот выход «Big_Bada_Boom».
— В-четвертых, для удобства в «сапере» заложена фишка, что при попадании на клетку не имеющую соседства с бомбой, автоматически открываются все пустые клетки, граничащие с ней. Т.е. нам понадобится выход, посылающий обратную команду «открыть клетку» самой программой.
— В-пятых, для алгоритма установки бомб нам понадобится выход, показывающий, сколько бомб находится в данной строке. Разумеется это значение можно получить напрямую из вектора, описанного в пункте 2, достаточно просто сложить все ненулевые биты. Но для удобства внесем эту функцию внутрь макроса.
— В-шестых, для ведения статистики нам понадобится знать, сколько еще закрытых клеток осталось в строке.
Вот и все, что нам нужно для полного функционала игры «сапер».

Всего 6 выходов с данными.

Теперь подумаем, что же нам нужно иметь на входе, чтобы суметь сформировать требуемые нам выходы.

Входы алгоритма

— Логично предположить, что главный вход алгоритма это команда от игрока «Открыть ячейку». Ведь это основной смысл игры.
— Для наглядности программирования задействуем еще один вход — «Поставить на клетку флаг», помечающий что там бомба и не дающий случайно нажать на эту клетку и взорваться.
— Для формирования статуса каждой ячейки (а мы то с вами помним, что если в ячейке нет бомбы, то она показывает сколько бомб находится в клетках, граничащих с ней) понадобится завести вектора со строки выше и строки ниже. Сделаем алгоритм универсальным и добавим три входа: вход для вектора с бомбами в строке выше текущей, вход для вектора с бомбами в текущей строке и вход для вектора с бомбами в строке ниже текущей.
— Для запуска новой игры и переписывания значений ячеек добавим логический вход «Новая игра».
— Как уже упоминалось ранее, если клетка пустая и рядом нет клеток с бомбами, то должны автоматически открываться соседние с ней клетки. Добавим для этого вход «Открыть программой».
— Ну и разумеется какая же это игра без заминированного поля. Значит нам понадобится вход «Установить бомбы».

Всего получилось 8 входов.

Вот собственно и все, что нам нужно от главного алгоритма программы.
Набиваем быстро макрос с нашими входами и выходами и получаем вот такой алгоритм:
Небольшая игрушка «Сапер» не в 30 строк или если раскрыть его: Небольшая игрушка «Сапер» не в 30 строк
Теперь наполним наш алгоритм смыслом. Как я уже говорил — основа наполнения нашего главного алгоритма это макрос «Ячейка памяти», которых будет 20 штук.

Алгоритм «Ячейка памяти»

Попробуем продумать, что нам может понадобиться от каждой ячейки поля игры «сапер», и что мы должны для этого подавать на вход макроса. Начнем опять с конца, т.е. с выходов. А когда сформулируем все данные, что нам нужны, то станет понятно, что нужно иметь на входе, чтобы их получить.

Выходы алгоритма

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

Состояния ячеек поля

Ответ 12. именно так — 12 состояний, соответствующих 12-ти различным отображениям ячейки.
Я закодировал их следующим образом:
-1 — ячейка закрыта.
0-8 — ячейка открыта и показывает количество бомб в соседних ячейках.
9 — ячейка открыта и в ней оказалась бомба.
10 — ячейка закрыта и на ней установлен флаг.

— Отдельно для подсчетов и прочей обработки вынесем логические выходы «Ячейка закрыта» и «В ячейке установлена бомба».
— Как уже говорилось — если открыта пустая ячейка, то она должна автоматически открыть соседние с ней ячейки. соответственно нам нужнен еще логический признак «Открыть соседей».
— Ну и в конце-концов если игрок ошибся и «наступил» на поле с бомбой, то нужно сформировать логический признак того, что игра проиграна. назовем его «Bada_Boom».
Итого всего 5 выходов.

Входы алгоритма

Попробуем определиться, что же нам нужно для того, чтобы сформировать требуемые выходы.
— Прежде всего алгоритм должен принимать команду от игрока «Открыть поле».
— Для простоты отдельной командой заводим сюда еще и команду «Поставить флаг».
— Как я уже говорил ранее — каждая клетка в поле должна знать, сколько заложено бомб в соседних клетках. заведем для этого отдельный вход «Соседи».
— Для того, чтобы случайным образом установить вначале бомбы нужен логический признак «Здесь бомба» по которому клетка будет минироваться.
— Ну и разумеется для начала новой игры должен быть признак сброса, который обнуляет всю информацию в клетках.

Набиваем и этот макрос получая следующий алгоритм:
Небольшая игрушка «Сапер» не в 30 строк
Теперь запрограммируем сам алгоритм «Ячейка памяти». Он предельно простой:
Небольшая игрушка «Сапер» не в 30 строк

Краткое пояснение принципа работы алгоритма

— На алгоблок «БитДешифр1» приходит вектор, сформированный из состояния соседей, который распаковывается на этой алгоритме и далее на алгоблоке «Слож1» подсчитывается количество единичек (бомб) в векторе. Сразу возникает вопрос: почему здесь 9 бит, когда у клетки всего 8 соседей максимум. Отвечаю: для универсальности. Дело в том, что как видно будет позже, я написал еще один макрос для нахождения соседей у каждой клетки. И для того, чтобы макрос получился универсальный для всех случаев (а клетка может иметь не только 8 соседей, когда она находится в центре поля, но и всего 3, когда она стоит в углу, или 5, когда она стоит на краю поля) пришлось задействовать девять бит.
— Триггер «RSтриг1» служит для установки и сброса «флажка» на клетке. Первая команда взводит триггер, а повторная команда сбрасывает. Как видно из схемы, взведенный триггер, блокирует прохождение команды «Открыть» на алгоблоке «И2» т.к. значение с выхода триггера заведено на вход алгоритма «И» с инверсией. Т.е. пока триггер взведен, на второй вход алгоритма «И» поступает «False» и выход алгоритма «И» тоже равен «False» независимо от значения на первом входе. Получается, что команда «Открыть» не может сбросить триггер «RS2» и мы застрахованы от случайного открытия клетки, которую не хотим открывать.
— Триггер «RS2» служит для формирования статуса ячейки: закрыта она или открыта. Как видно из схемы триггер взводится командой «Сброс» (начало новой игры) и сбрасывается только по приходу команды «Открыть».
— Триггер «RS1» служит для индикации наличия бомбы в этой ячейке. Он взводится при поступлении от программы команды «Здесь бомба», которая устанавливает бомбы в ячейку и сбрасывается с началом новой игры.
— Алгоритм «И3» один из самых важных в этой схеме. на нем по И складывается значение триггера «RS1» (тут установлена бомба) и прихода команды «открыть» в эту ячейку. Если оба условия выполнились, то на выходе формируется «True», означающее, что игрок наступил на поле с бомбой. Это значение подается на выход «Bada_Boom», которые потом со всех ячеек собираются на выходе «Big_Bada_Boom» и означают проигрыш.
— Три алгоритма «Выбор», расположенные каскадом, формируют статус ячейки. На алгоритме «Выбор1» формируется, что подавать на выход в случае открытия ячейки: числа «0-8», соответствующие количеству бомб в ячейках по соседству, или число «9» соответствующее заминированной клетке. Если поле у нас еще закрыто (Триггер «RS2» взведен), то на алгоритме «Выбор2» устанавливается значение "-1", которое, как мы договорились ранее, соответствует статусу закрытой ячейки. Если поле закрыто (Триггер «RS2» взведен) и одновременно (алгоритм «И4») взвелся триггер «RSтриг1», то на алгоритме «Выбор3» значение подменяется принудительно на «10», соответствующее установленному флагу.
— Последний алгоритм сравнения служит для автоматического открытия соседних клеток. Если на выходе «Выбор3» мы получили ноль, значит игрок открыл клетку, рядом с которой нет бомб. Тогда отправляется команда на открытие соседних клеток.

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

Теперь приступим к макросу «Строка», состоящему из 20 «Ячеек памяти».

Сам макрос и описание. Рисунок большой.

Небольшая игрушка «Сапер» не в 30 строк
Сам макрос предельно простой. 20 алгоритмов «Ячейка памяти» связанные со своими входами и выходами общего макроса. Для статуса ячейки эта связь идет напрямую, для остальных — либо через алгоритмы «БитШифратор» для упаковки логических значений в вектор, либо через сумматоры для подсчета количества нужных элементов в строке, либо через алгоритм «ИЛИ» для формирования выходной команды открытия соседних клеток или окончания игры («Big_Bada_Boom»).
Отдельного внимания заслуживают только блоки «Открыть», «Преобразование вектора» и «Открыть соседей».
Рассмотрим каждый такой блок по отдельности.

Начнем с блока: Открыть

Небольшая игрушка «Сапер» не в 30 строк
Как несложно заметить, здесь просто складываются по «ИЛИ» команды от игрока и от программы, когда она открывает соседей пустой клетки.

Рассмотрим блок: Преобразование вектора

Небольшая игрушка «Сапер» не в 30 строк
Идея очень проста. К нам приходит три вектора и нужно посчитать, сколько же мин окружают каждую клетку на конкретной позиции. Для этого я намеревался просто сдвинуть вектор вправо на (позицию — 2) и посчитать количество единичек в первых трех битах каждого вектора после сдвига. Но тут я наткнулся на забавную вещь. А что делать если позиция клетки первая? тогда по аналогии с остальными я должен сдвинуть вектор на отрицательную величину (т.е. не вправо, а влево). Разумеется все передвинутые слева биты будут нулевые, но зато общий принцип подсчета будет сохранен. Однако тонкости реализации алгоритма не позволили это сделать, поэтому пришлось ставить проверку на позицию клетки и для первой позиции клетки проводить отдельную обработку. Собственно говоря эта обработка заключается только в обработке второго бита в текущей строке и третьих битов во всех строках. Помните, я писал о том, что для стандартного макроса мне понадобилось формировать 9 бит соседей, а не 8, как того требует логика. Это связано именно с этой ситуацией.

Самые наблюдательные читатели уже заметили, что здесь выполняется ну явно лишняя операция. Я получаю биты, описывающие соседей, упаковываю их в вектор, а затем опять распаковываю и суммирую. Т.е. операции по упаковке-распаковке явно избыточны. Но я оставил такой вариант для отладки и наглядности.

Рассмотрим блок: Открыть соседей

Небольшая игрушка «Сапер» не в 30 строк
Тут вообще все очень просто. Если клетка пустая, то она должна открывать не одну, а целых 8 клеток-соседей. Для унификации и упрощения жизни пусть она открывает не 8 соседних клеток, а 9 (да-да и саму себя тоже. В языке FBD порой даже простейшее допущение, никак не сказывающееся ни на работе программы ни на ресурсах, сильно упрощает жизнь). Таким образом алгоритм работы макроса прост. Мы проверяем позицию клетки. Если клетка стоит на второй и более позиции — формируем вектор из первых подряд идущих битов и сдвигаем их (помните по аналогии с обработкой вектора, только уже не вправо, а влево, т.к. мы не приводим какой то участок вектора к началу, а наоборот, начальный вектор сдвигаем на нужный нам участок) на (позиция -2). Если же клетка у нас первая, то мы формируем начальный вектор из двух битов и сдвигаем его на (позиция -1) = 0 т.е. никуда не двигаем на самом деле.
Далее нам остается только сложить все подготовленные вектора по «ИЛИ» и выдать на главный выход.

Отдельно хотел упомянуть про еще один макрос. Перед стартом игры нам нужно случайным образом расположить в поле мины. Но что делать, если нет генератора случайных числе и команда «Random» недоступна. Разумеется в интернете есть множество алгоритмов формирования псевдослучайных последовательностей. Но они довольно сложные и их реализация сама по себе заслуживает отдельной статьи. Поэтому пришлось пойти привычным путем и сделать макрос «Генератор Random».
Небольшая игрушка «Сапер» не в 30 строк

Краткое описание принципа работы макроса

Итак, генератора случайных чисел у нас нет. Ну что ж. сделаем его сами!
Идея примитивна и уже применялась мной в предыдущей программе, просто тут она получила некоторое развитие.
На интеграторе формируется очень быстро меняющееся по пиле значение с настраиваемым диапазоном (за диапазон отвечают входы «Верхний порог» и «Нижний порог»). Далее на алгоритме «Секунды» у нас подсчитывается время работы контроллера с момента старта, которое так же непрерывно нарастает. Делим время работы контроллера на наше быстро меняющееся число, при этом берем остаток от деления. Далее выделяем из остатка нужные нам разряды (для пущей случайности я взял 4-5 знаки после запятой для координаты Х, и 6-7 знаки после запятой для координаты У). Выделяются разряды очень просто. Умножаем остаток от деления на 1000000 и делим с остатком на 100. В результате в остатке получаем значение [0..100). При этом нужно понимать, что это значение может сколь угодно близко подходить к 100, но никогда не будет ему равно. Требуемое нам случайное значение лежит в диапазоне [1..20], следовательно делим наш остаток на 5 (алгоритм «ДелОст3»), берем целочисленную часть от деления и преобразовываем в целое число с помощью конвертера типов. Как уже говорилось — делимое никогда не будет равно 100, а значит наше частное лежит в интервале натуральный чисел [0..19]. Проблему решаем просто добавив к результату единичку. Вот и готово наше случайное число. Ввиду того, что неизвестно в какой момент игрок нажмет на кнопку «Новая игра» и какое значение на интеграторе будет в этот момент, а так же то, что мы берем значения в 4-7 знаке после запятой, то можно с уверенностью утверждать, что у нас получился хороший генератор случайных чисел.

Соединяем все части вместе и добавляем красоты

Итак, все части нашей программы готовы. Соединяем их между собой и делаем обвязку:

Рисунок основной программы.Большой.

Небольшая игрушка «Сапер» не в 30 строк

Небольшое пояснение

Как видно из рисунка — самый главный действующий алгоритм тут — «РучСелектор1» который выдает на выходе логическую единицу на один цикл и запускает новую игру. Запуск новой игры заключается в том, что сигнал подается на все макросы «Строка» на вход «Новая_игра» и по этому сигналу происходит установка триггера «SR2» в макросе «Ячейка_памяти» и сброс всех остальных триггеров, отвечающих за наличие бомб и флагов в ячейке.
Одновременно этот же выход алгоритма «РучСелектор1» сбрасывает триггера «RS1» («Game_over» — этот триггер взводится в случае открытия игроком клетки, в которой пряталась бомба, и триггер «RS3» («Victory»), который взводится в случае победы игрока, т.е. обнаружении всех спрятанных в поле бомб. Одновременно тот же самый сигнал посылается на вход алгоритма «Память1» для записи в память количества устанавливаемых бомб в текущей игре (игрок моет менять количество бомб в поле в любой момент, но вот в программу эти настройки попадут только с началом новой игры).
Далее с задержкой на цикл (алгоритм «Задержка1») (который нужен чтобы сбросить все триггера в ячейках памяти) взводится триггер «RS2» («Ставим_бомбы»), который включает процедуру установки бомб. Т.е. передачи наших случайных координат, сформированных на выходах макросов «Генератор_Random» на алгоритмы «Столбец» и «Строка», посылающие по заданным координатам команду на взвод триггера «RS1» в «Ячейке памяти».
Контроль количества установленных бомб простой. Подсчитывается общее количество установленных бомб и как только оно сравнивается с числом, заданным на алгоритме «Память1», сбрасывается триггер «SR2» тем самым прекращая установку новых бомб.

Алгоритмы 36-40 служат для проверки корректности заданного числа бомб игроком. Здесь я для отладки ограничил минимальное и максимальное количество бомб в 0 и 400 штук соответственно. При задании числа, выходящего за этот диапазон, оно автоматически приравнивается к ближайшей границе.

Проверка на выполнение условий победы предельно простая. Считаются все закрытые клетки. Как только их количество станет равным заданному количеству бомб и триггер «RS1» («Game_over») не взведен, взводится триггер «RS3» («Victory»).

Нам осталось рассмотреть всего 2 алгоритма:
Алгоритм «ИЛИ2». На нем по логическому ИЛИ собираются выходы всех трех триггеров. И по выходу алгоритма «ИЛИ2» (когда он равен единице) выставляется блокировка на действия игрока. Т.е. пока хоть один из триггеров взведен (Т.е. или игрок уже победил, или он уже проиграл, или идет установка мин для новой игры) игроку запрещаются любые нажатия в поле. Он не может ни открывать ячейки, ни ставить флажки.
Алгоритм «Секундомер1» служит секундомером для отсчета времени, затраченного на игру. Подсчитываем количество закрытых ячеек. В начальный момент каждой игры оно равно 400. Как только число закрытых ячеек становится меньше чем 400, запускается таймер, который останавливается по взведению одного из трех триггеров.

Вот собственно и вся программа.

Прикручиваем графический интерфейс

Делается это буквально за пару минут. Рисуется 12 картинок для клетки (я для этого использовал paint) и ставится их отображение в зависимости от статуса ячейки. Далее эта клетка копируется 400 раз (тут пугаться не надо, т.к. 400 клеток это всего навсего девять операций Ctrl+c — Ctrl+v) и получается минное поле. Находим в интернете первую же попавшуюся подходящую по смыслу картинку для логотипа «сапера». Прикручиваем отображение таймера и кнопку «Новая игра».
А теперь осталось самое сложное — выбрать картинки для победы и поражения в игре. Тут я взял первые же попавшиеся в поисковике картинки по запросам «атомный взрыв» и «Victory». И все — игра готова.

Начальная позиция:

Небольшая игрушка «Сапер» не в 30 строк

Играем:

Небольшая игрушка «Сапер» не в 30 строк

Проигрываем

Небольшая игрушка «Сапер» не в 30 строк
Предупреждая вопросы: да, вон та черная штука, похожая на букашку с ножками, это бомба так нарисована.

И побеждаем!

Небольшая игрушка «Сапер» не в 30 строк

Подводим итоги

Тот, кто смог осилить этот пост целиком наверняка заметил, что мы написали пусть и простую, но все-таки не уровня «Hello, World!» программу, и при этом нам не потребовалось абсолютно никаких справочников, хелпов, сидений на форумах программистов, курения мануалов и т.п. Все что было нужно мы реализовали за пару часов (и то лично у меня большая часть времени ушла на красивую расстановку алгоблоков для картинок), при этом использовав понятные даже школьнику алгоритмы сложения, вычитания, умножения и алгоритмы логики «И», «ИЛИ». Плюс задействовали несколько простейших RS-триггеров. Самый сложный алгоритм, использованный в программе, это «Интегратор». И то мы с вами использовали его просто как сумматор с обратной связью и проверкой границ. Т.е. фактически можно заменить интегратор на алгоритм «Сложение» и два алгоритма «Сравнение».
Другими словами, программировать на FBD просто и весело, и на начальном уровне не требуется вообще никаких знаний и навыков кроме понимания элементарной логики. Главным при этом является четкое представление себе конечного результата. Если оно имеется, то сама программа является его логическим довершением. Если же представить «А что же мы хотим в итоге получить» пока не получается, то возможно стоит четче сформулировать задачу. Сразу оговорюсь, что все вышесказанное применимо не ко всем проектам, реализованным на FBD, а к простым маленьким задачкам, вроде той, что приведена в нашем примере. Т.к. для огромных проектов на десятки тысяч сигналов соединить это все в голове наверное не получится ни у кого. Но и в этом случае огромный проект разбивается на небольшие подзадачи, где наш подход уже становится вполне применим (разумеется с учетом общих требований к проекту).
Т.к. язык FBD универсальный и мы в программе задействовали простейшие алгоритмы, являющиеся базовыми для любых реализаций, то данную программу можно с минимальными усилиями воспроизвести на любом контроллере, как отечественной разработки, так и иностранных (например на контроллерах фирмы Siemens или ABB).

Из того, что осталось нереализованным

Нереализованными остались три функции классической игры сапер.
Первая, это возможность в настройках задавать размер поля. Делается это несложно. Собственно говоря просто алгоритмы «Ячейка памяти» копируются столько раз, сколько клеток должно быть в поле. Проблема тут одна: отсутствие динамической памяти, т.е. невозможности «на лету» добавлять или убирать какие либо данные. А из этого следует простой вывод — сделать изменяющиеся размеры поля очень просто — нужно сначала запрограммировать задачу под максимальный размер поля, а потом просто выставлять логический признак на незадействованные ячейки, которые по этому признаку не будут отображаться в графическом интерфейсе и не будут обрабатываться при игре.
Вторая — это подмена ячейки при первом клике, если в ней изначально находилась бомба. Т.е. в нашей программе в отличие от «сапера» в Windows есть шанс проиграть на первом же клике. Это делается тоже легко, достаточно проверять пришедший сигнал на триггер «Game_over» и количество закрытых клеток. Если у нас число закрытых клеток 399 и пришел сигнал на взведение триггера «Game_over», то нужно заблокировать этот сигнал, сбросить ту ячейку, на которую был произведен клик и запустить алгоритм случайной установки еще одной мины, при этом заблокировав возможность установки мины в уже открытую ячейку.
Ну и как видно из описания — не реализована функция сбора статистики. Т.е. не запоминается лучший результат, имя человека, поставившего рекорд. Соотношение побед и поражений и т.п. Но это все настолько тривиально, что делается буквально парой щелчков мыши, поэтому эту функцию оставляю на реализацию всем желающим.

Спасибо всем, кто дочитал этот пост до конца. Надеюсь было интересно.

Автор: OPCSenator

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js