Продолжу излагать свои мысли по поводу написания внешнего языка-интерпретатора для программирования поведения робота.
На этот раз я займусь представлением пространства в памяти робота.
При довольно длительном размышлении на эту тему, я решил попробовать избавить пользователей от подробностей конкретной реализации и постараться как можно дальше отодвинуть их от сложностей программирования различных деталей. Поэтому все классы, массивы, множества, списки, константы, перечисления и пр. я постараюсь создать и объявить в основной программе, а в интерфейс вынесу лишь ссылки на них, в виде простых и удобных переменных и функций (если, конечно, у меня это получится).
Итак, представление внутреннего мира у робота. Как это будет.
Для начала создаём массив 20х20 их элементов типа Kletka().
Memo[20,20] as Kletka
Конечно, делать это нужно не сначала, а после того, как будет описан сам элемент Kletka, но так уж мне удобнее излагать, не в прямом, а - в обратном порядке.
Что из себя представляет элемент Kletka?
Клетка - это по сути основной объект, с которым оперирует и взаимодействует наш робот. Так как он всегда находится в какой-нибудь клетке, и изучает (и хранит в памяти) её свойства. Какие же свойства интересны роботу у клетки?
Во-первых, его интересует был ли он уже в этой клетке или еще не был (то есть, открыта она ему в его памяти, или существует всего лишь как потенциальная возможность). И для фиксации этого состояния в объект Клетка я помещаю логическую переменную Byl, которая по умолчанию имеет состояние ЛОЖЬ, и лишь при посещении этой клетки роботом приобретает состояние ИСТИНА. По состоянию этой переменной робот всегда может понять, открыта ли им уже эта клетка или же еще нет.
Byl - логическая переменная отражающая посещение роботом этой клетки, составная часть объекта Клетка
Далее роботу будет интересно состояние изученности им стенок у данной клетки. Сверху, справа, снизу и слева. Что там? Стенка или проход? Или пока не известно?
Для этого в объекте Клетка я сделал еще четыре переменных (Verh, Pravo, Niz, Levo) перечисляемого типа, которые могут принимать всего три возможных состояния: W - (типа Wall) в этом месте стена, O - (типа Open) тут проход, и N - (типа None) состояние не определено. Проверив значение этих переменных, робот всегда сможет понять, куда ему следует двигаться, и надо ли.
type Stenka = (N, W, O)
Verh, Pravo, Niz, Levo as Stenka
Дальше будет немного сложнее.
Для того, чтобы робот мог помнить и анализировать не только факт своего посещения данной клетки, но также и направление своего положения при посещении этой клетки, я ввел еще одну переменную Napr_Byl. Это нужно например для того, чтобы робот не крутился на месте, или не впадал в другие более сложные зацикленности. Если он обнаружит вдруг, что при движении он оказался в ситуации, когда он не только попадает в ту же клетку, где он уже был, но и в точно таком же положении, то это будет означать, что он "ходит по кругу", каким бы сложным не оказался этот "круг".
Сделать такую проверку для робота оказалось делом не тривиальным, но я выкрутился.
Для начала мною была создана константа перечисляемого типа, которая на физическом уровне имеет вид последовательности чисел от 0 до 15.
type Zapis_Napr = (OOOO, OOON, OOEO, OOEN, OSOO, OSON, OSEO, OSEN, WOOO, WOON, WOEO, WOEN, WSOO, WSON, WSEO, WSEN)
Napr_Byl as Zapis_Napr
Здесь я использую особенность двоичного счисления: каждый двоичный разряд кодирует одно состояние. Всего мне нужно запомнить состояние четырех возможных направлений, причем в любых их сочетаниях. Все нули означают что робот в этой клетке не был, и ни в никаких направлениях в ней не стоял. Буковки N, E, S и W означают стороны света, и каждая сторона света кодируется своим разрядом в двоичном числе. Если робот уже был в данной клетке, то к переменной отражающей направления добавляется нужный разряд, в зависимости от того, в каком направлении робот в эту клетку вошел и как в ней крутился. Причем, с этой переменной допустимы обычные операции сложения - ничто не нарушится и не исказится, скажем, если к OSOO прибавить OOON, то получится OSON, что будет верным, причем, как в символьном, так и в двоичном числовом виде.
На всякий случай мною были сформированы массивы всех возможных комбинаций, в которых встречается какое-либо направление:
Napr_Nord := [OOON, OOEN, OSON, OSEN, WOON, WOEN, WSON, WSEN];
Napr_East := [OOEO, OOEN, OSEO, OSEN, WOEO, WOEN, WSEO, WSEN];
Napr_South := [OSOO, OSON, OSEO, OSEN, WSOO, WSON, WSEO, WSEN];
Napr_West := [WOOO, WOON, WOEO, WOEN, WSOO, WSON, WSEO, WSEN];
И тогда проверка того, был ли робот в данной клетке по заданному направлению сведется к элементарной проверке вхождения текущего состояния переменной Napr_Byl в интересующий нас массив командой in, например так:
If Napr_Byl in Napr_Nord then ...
Что при удовлетворении данного условия будет означать, что робот в этой клетке на Север уже смотрел. Что он там делал, глядя мордой на Север - это не важно, может быть, и ничего, но факт, что он в таком положении уже побывал.
И еще я подумал, что подобная, довольно-таки, впрочем, вычурная конструкция, была бы полезна и для проверки состояния стенок у клетки. Там же тоже всего четыре направления, и, к тому же, часто робота интересует, например, исследовано ли данное направление в клетке, либо еще нет. Поэтому я ввел в объект Клетка еще одну переменную Issledovano, которая имеет те же состояния, что и Napr_Byl, только она показывает сколько стенок в клетке уже исследовано и какие. Состояние этой переменной равное WSEN будет означать, что все стенки в этой клетке уже исследованы, а состояние OOOO - что еще ничего не исследовано.
Итак, объект Клетка будет у меня состоять из следующих переменных:
Kletka(Byl, Napr_Byl, Issledovano, Verh, Pravo, Niz, Levo)
И, как я уже писал выше, всё поле памяти робота будет представлять из себя двумерный массив таких вот Клеток.
Memo[20,20] as Kletka - массив 20х20 из элементов типа Kletka
Робот, попав в какую-либо клетку, помечает что он в ней был (типа, "Здесь был Вася"), и ставит отметку направления своего взгляда. Если им были исследованы стенки этой клетки, то изменяются соответствующие переменные у стенок (а так же и в соседних клетках, сопряженных с данной - именно для этого массив памяти сделан с запасом: не 19х19, а - 20х20, чтобы безбоязненно обрабатывать соседей).
И даже не будучи непосредственно в клетке, а работая с ней по памяти, робот всегда может посмотреть, был ли он в ней, а если был, то куда смотрел, а куда не смотрел, какие стенки и проходы в этой клетке уже им исследованы, а какие еще нет, и все ли исследовано. И теперь, я так думаю, у него есть все необходимое, чтобы планировать свои дальнейшие действия в этом лабиринте.
Скажите, если вы видите, что чего-то еще не хватает.
Спустя некоторое время:
Я тут немного подумал, и понял, что переменную Issledovano нет необходимости делать перечисляемого типа, как переменную Napr_Byl, достаточно сделать её логической. Так как нам от неё требуется только одно: все ли стенки в данной клетке уже исследованы или же еще не все, а для этого достаточно двоичного флага. А так - все остальное остается в силе.