Стал переписывать заново вчерашний вариант. Столкнулся с неприятной особенностью циклов DO – если счетчик по каким-либо причинам проскочил граничный вариант, цикл входит в бесконечный. Потратил минут 40 пока разобрался. Очень неприятный момент.
Хотя и советуют писать корткие определения, пока это не очень получается. Все равно они довольно большие получаются.
В процессе написания игры, понял что не хватает отдельных средств для работы с двумерными массивами. Переключился на написании отдельной библиотеки для работы с двумерными массивами. Данные храняться не в словаре, а в динамической памяти, переменные выделяю USER. Стало получаться вот так.
\ Создание и работа с матрицамиUSER Матр
: СоздатьМатрицу ( ширин высот адр ) \ ширин и высота -- кол-во элементов, поэтому отсчет 1! DUP @ 1000 > IF \ Освобождение памяти, если есть указатель - DUP FREE DROP \ какое-то относит. большое положит. число THEN
ROT ( высот адр ширин ) DUP 1 < IF \ Проверка диапазона DROP 1 THEN
ROT ( адр ширин высот ) DUP 1 < IF \ Проверка диапазона DROP 1 THEN \ получили длину + 2 ячейки под размер матрицы OVER OVER * 1+ ( адр ширин высот длин ) ALLOCATE IF 2DROP 0 ! \ если не удалось выделить память -- выход, и нулевой адрес EXIT THEN ( адр ширин высот адрМатр ) ROT OVER ! ( адр высот адрМатр ) \ сохранил ширину матрицы SWAP OVER CELL+ ! ( адр адрМатр ) \ сохранил высоту матрицы SWAP ! \ сохранил адрес матрицы в переменной ;
: ШиринаМатрицы ( адрМатр ) @ \ получил адрес памяти матрицы @ \ только теперь ширину ; ( шир )
: ВысотаМатрицы ( адрМатр ) @ CELL+ @ ; ( выс )
: ЭлМатрицы ( х у адрМатр ) \ координаты начинаются с 0 !!! ROT ( y адрМатр x ) DUP 0< IF \ если х вышел за пределы диапазона, то возвращаю 0 DROP 2DROP 0 EXIT THEN ( y адрМатр x ) DUP 2 PICK ШиринаМатрицы 1- > IF \ должен быть строго меньше границы DROP 2DROP 0 EXIT THEN ( y адрМатр x ) ROT ( адрМатр x y ) DUP 0< IF \ если y вышел за пределы диапазона, то возвращаю 0 DROP 2DROP 0 EXIT THEN DUP 3 PICK ВысотаМатрицы 1- > IF DROP 2DROP 0 EXIT THEN ( адрМатр x y ) \ проверку дипапазона закончили
2 PICK ВысотаМатрицы * + \ преобразовали двумерные координат в одномерные 1+ \ с учетом 2-х служебных ячеек CELL * \ получили адрес элемента относит блока памяти SWAP @ \ адрес блока памяти + @ \ абс. адрес элемента и его значение ;
: =ЭлМатрицы ( знач х у адрМатр ) \ координаты начинаются с 0 !!! ROT ( знач y адрМатр x ) DUP 0< IF \ если х вышел за пределы диапазона, то возвращаю 0 2DROP 2DROP EXIT THEN ( знач y адрМатр x ) DUP 2 PICK ШиринаМатрицы 1- > IF \ должен быть строго меньше границы 2DROP 2DROP EXIT THEN ( знач y адрМатр x ) ROT ( знач адрМатр x y ) DUP 0< IF \ если y вышел за пределы диапазона, то возвращаю 0 2DROP 2DROP 0 EXIT THEN DUP 3 PICK ВысотаМатрицы 1- > IF DROP 2DROP 0 EXIT THEN ( знач адрМатр x y ) \ проверку дипапазона закончили
2 PICK ВысотаМатрицы * + \ преобразовали двумерные координат в одномерные 1+ \ с учетом 2-х служебных ячеек CELL * \ получили адрес элемента относит блока памяти SWAP @ \ адрес блока памяти + \ абс. адрес элемента ! \ запоминаю его значение ;
: ОчиститьМатрицу ( адрМатр ) DUP ВысотаМатрицы 0 DO DUP ШиринаМатрицы 0 DO 0 J I 3 PICK =ЭлМатрицы LOOP LOOP ;
\ --- Тестирование определений ----- 0 Матр ! 15 14 Матр СоздатьМатрицу
\ 900 14 3 Матр =ЭлМатрицы \ Работает \ 14 3 Матр ЭлМатрицы . \ Работает Матр ОчиститьМатрицу
и тут ополучил уже встреченную ранее проблему с циклом DO. Слово ОчиститьМатрицу завершалось аврийно. Стоило мне только из циклов убрать обращения к счетчикам:
: ОчиститьМатрицу ( адрМатр ) DUP ВысотаМатрицы 0 DO DUP ШиринаМатрицы 0 DO 0 10 11 3 PICK =ЭлМатрицы LOOP LOOP ;
и все работало. Причем в ходе экспериментов, выяснилось, что к краху приводило любое употребление счетчика внутри цикла. Например, следующий вариант также заканчивался печально:
: ОчиститьМатрицу ( адрМатр ) DUP ВысотаМатрицы 0 DO DUP ШиринаМатрицы 0 DO I DROP 0 10 11 3 PICK =ЭлМатрицы LOOP LOOP ;
ЕСТЬ!!! Два часа бдений и ошибка была найдена. Для этого была отключено расширение – организация словарей на основе хэш-таблиц. В spf.ini закоментирвоал обратно строку
\ REQUIRE QuickSWL ~pinka\spf\quick-swl2.f
и сразу полезли ошибки в слове ОчиститьМатрицу Методом последовательного исключения выяснил, что во время выделения памяти под Матрицу в слове СоздатьМатрицу я определял размер места в ячейках, но не масштабировал их на байты, поэтому места выделялось меньше! И соответсвенно, генерилось исключение.
Подправил слово, и тестовый вариант заработал.
: СоздатьМатрицу ( ширин высот адр ) \ ширин и высота -- кол-во элементов, поэтому отсчет 1! DUP @ 1000 > IF \ Освобождение памяти, если есть указатель - DUP FREE DROP \ какое-то относит. большое положит. число THENROT ( высот адр ширин ) DUP 1 < IF \ Проверка диапазона DROP 1 THEN
ROT ( адр ширин высот ) DUP 1 < IF \ Проверка диапазона DROP 1 THEN \ получили длину + 2 ячейки под размер матрицы OVER OVER * 1+ ( адр ширин высот длин ) CELL * \ !!!! ВОТ ОНО -- Получили длину в байтах ! ALLOCATE IF 2DROP 0 ! \ если не удалось выделить память -- выход, и нулевой адрес EXIT THEN ( адр ширин высот адрМатр ) ROT OVER ! ( адр высот адрМатр ) \ сохранил ширину матрицы SWAP OVER CELL+ ! ( адр адрМатр ) \ сохранил высоту матрицы SWAP ! \ сохранил адрес матрицы в переменной ;
Но когда запустил нормальное слово ОчиститьМатрицу она опять завершалась аврийно.
: ОчиститьМатрицу ( адрМатр ) DUP ВысотаМатрицы 0 DO DUP ШиринаМатрицы 0 DO 0 I J 3 PICK =ЭлМатрицы LOOP LOOP ;
Уже понимая, что грабли где-то в неправильном выделении памяти и работы с ней решил посмотреть на каких значениях счетчиках сыпится слово. Его переправил вот так
: ОчиститьМатрицу ( адрМатр ) DUP ВысотаМатрицы 0 DO DUP ШиринаМатрицы 0 DO I . J . 0 I J 3 PICK =ЭлМатрицы LOOP LOOP ;
И увидел, что как раз на нулевых, т.е. слово =ЭлМатрицы на нулевых координатах явно не туда залазит. Тут уже быстро сообразил где может быть. Исправленное слово =ЭлМатрицы уже выглядило так.
: =ЭлМатрицы ( знач х у адрМатр ) \ координаты начинаются с 0 !!! ROT ( знач y адрМатр x ) DUP 0< IF \ если х вышел за пределы диапазона, то возвращаю 0 2DROP 2DROP EXIT THEN ( знач y адрМатр x ) DUP 2 PICK ШиринаМатрицы 1- > IF \ должен быть строго меньше границы 2DROP 2DROP EXIT THEN ( знач y адрМатр x ) ROT ( знач адрМатр x y ) DUP 0< IF \ если y вышел за пределы диапазона, то возвращаю 0 2DROP 2DROP 0 EXIT THEN DUP 3 PICK ВысотаМатрицы 1- > IF DROP 2DROP 0 EXIT THEN ( знач адрМатр x y ) \ проверку дипапазона закончили2 PICK ВысотаМатрицы * + \ преобразовали двумерные координат в одномерные 2+ \ ВОТ ОНО!!! 2 нужно прибавлять! с учетом 2-х служ ячеек CELL * \ получили адрес элемента относит блока памяти SWAP @ \ адрес блока памяти + \ абс. адрес элемента ! \ запоминаю его значение ;
Запустил – все заработало.
Попробовал подключить вновь созданную библиотеку – не получилось. Вообще, слово INCLUDED какое-то странное. Если не находит подключаемый файл, то интерпретатор говорит, что слово INCLUDED ... не найдено! Блин, это так поначалу с толку сбивает!
Ладно, прописал ему полный путь, файл увидел, а слов нем – нет. Т.е. поведение интерепретатора такое, будто определений в подключаемом файле нет!
S" c:\spf\work\arr1.f" INCLUDED
ни хера не работает, блин!!!
Пришлось слить два файла в один.
После часа программирования игра, наконец, написана, работает. Последовательно отображает смену поколений при нажатии на любую клавишу, по клавише q – выход. Но только если ширина и высота совпадают. Где-то глУк. Завтра буду искать...
\ Life game : СоздатьМатрицу ( ширин высот адр ) \ ширин и высота -- кол-во элементов, поэтому отсчет 1! DUP @ 1000 > IF \ Освобождение памяти, если есть указатель - DUP FREE DROP \ какое-то относит. большое положит. число THENROT ( высот адр ширин ) DUP 1 < IF \ Проверка диапазона DROP 1 THEN
ROT ( адр ширин высот ) DUP 1 < IF \ Проверка диапазона DROP 1 THEN \ получили длину + 2 ячейки под размер матрицы OVER OVER * 1+ ( адр ширин высот длин ) CELL * \ Получили длину в байтах ! ALLOCATE IF 2DROP 0 ! \ если не удалось выделить память -- выход, и нулевой адрес EXIT THEN ( адр ширин высот адрМатр ) ROT OVER ! ( адр высот адрМатр ) \ сохранил ширину матрицы SWAP OVER CELL+ ! ( адр адрМатр ) \ сохранил высоту матрицы SWAP ! \ сохранил адрес матрицы в переменной ;
: ШиринаМатрицы ( адрМатр ) @ \ получил адрес памяти матрицы @ \ только теперь ширину ; ( шир )
: ВысотаМатрицы ( адрМатр ) @ CELL+ @ ; ( выс )
: ЭлМатрицы ( х у адрМатр ) \ координаты начинаются с 0 !!! ROT ( y адрМатр x ) DUP 0< IF \ если х вышел за пределы диапазона, то возвращаю 0 DROP 2DROP 0 EXIT THEN ( y адрМатр x ) DUP 2 PICK ШиринаМатрицы 1- > IF \ должен быть строго меньше границы DROP 2DROP 0 EXIT THEN ( y адрМатр x ) ROT ( адрМатр x y ) DUP 0< IF \ если y вышел за пределы диапазона, то возвращаю 0 DROP 2DROP 0 EXIT THEN DUP 3 PICK ВысотаМатрицы 1- > IF DROP 2DROP 0 EXIT THEN ( адрМатр x y ) \ проверку дипапазона закончили
2 PICK ВысотаМатрицы * + \ преобразовали двумерные координат в одномерные 2+ \ с учетом 2-х служебных ячеек CELL * \ получили адрес элемента относит блока памяти SWAP @ \ адрес блока памяти + @ \ абс. адрес элемента и его значение ;
: =ЭлМатрицы ( знач х у адрМатр ) \ координаты начинаются с 0 !!! ROT ( знач y адрМатр x ) DUP 0< IF \ если х вышел за пределы диапазона, то возвращаю 0 2DROP 2DROP EXIT THEN ( знач y адрМатр x ) DUP 2 PICK ШиринаМатрицы 1- > IF \ должен быть строго меньше границы 2DROP 2DROP EXIT THEN ( знач y адрМатр x ) ROT ( знач адрМатр x y ) DUP 0< IF \ если y вышел за пределы диапазона, то возвращаю 0 2DROP 2DROP 0 EXIT THEN DUP 3 PICK ВысотаМатрицы 1- > IF DROP 2DROP 0 EXIT THEN ( знач адрМатр x y ) \ проверку дипапазона закончили
2 PICK ВысотаМатрицы * + \ преобразовали двумерные координат в одномерные 2+ \ с учетом 2-х служебных ячеек CELL * \ получили адрес элемента относит блока памяти SWAP @ \ адрес блока памяти + \ абс. адрес элемента ! \ запоминаю его значение ;
: ОчиститьМатрицу ( адрМатр ) DUP ВысотаМатрицы 0 DO DUP ШиринаМатрицы 0 DO 0 I J 3 PICK =ЭлМатрицы LOOP LOOP DROP ;
: ПоказатьМатрицу ( адрМатр ) DUP ВысотаМатрицы 0 DO DUP ШиринаМатрицы 0 DO I J 2 PICK ЭлМатрицы . LOOP CR LOOP DROP ;
\ -------------------------------------------------------------------- \ ---------------------------- Данные -------------------------------- USER Среда 0 Среда ! USER Кол-воСоседей 0 Кол-воСоседей !
20 20 Среда СоздатьМатрицу Среда ОчиститьМатрицу 20 20 Кол-воСоседей СоздатьМатрицу Кол-воСоседей ОчиститьМатрицу
: ЗаселитьСреду Среда ВысотаМатрицы 0 DO Среда ШиринаМатрицы 0 DO I J * 5 MOD DUP 2 = OVER 3 = OR IF DROP 1 ELSE DROP 0 THEN \ определил -- живет здесь бактерия (1) или нет(0) J I Среда =ЭлМатрицы LOOP LOOP ;
: ЕстьБактерия? ( x y ) Среда ЭлМатрицы \ если есть бактерия, то 1 иначе 0 ; ( 1 | 0 )
: РассчитатьКол-воСоседей Кол-воСоседей ВысотаМатрицы 0 DO Кол-воСоседей ШиринаМатрицы 0 DO I 1- J 1- ЕстьБактерия? \ нижний левый угол I 1- J ЕстьБактерия? + \ обход по часовой I 1- J 1+ ЕстьБактерия? + I J 1+ ЕстьБактерия? + I 1+ J 1+ ЕстьБактерия? + I 1+ J ЕстьБактерия? + I 1+ J 1- ЕстьБактерия? + I J 1- ЕстьБактерия? + ( колБакт )
I J Кол-воСоседей =ЭлМатрицы \ Сохранили значение LOOP LOOP ;
: ПоказатьСовмещенно Кол-воСоседей ВысотаМатрицы 0 DO Среда ШиринаМатрицы 0 DO J I Среда ЭлМатрицы 0= IF ." " ELSE ." X" THEN LOOP
2 SPACES
Кол-воСоседей ШиринаМатрицы 0 DO J I Кол-воСоседей ЭлМатрицы . LOOP
CR LOOP ;
: ПоказатьСреду Среда ВысотаМатрицы 0 DO Среда ШиринаМатрицы 0 DO J I Среда ЭлМатрицы 0= IF ." " ELSE ." X" THEN LOOP
CR LOOP ;
: ОбновитьСреду Среда ВысотаМатрицы 0 DO Среда ШиринаМатрицы 0 DO J I Кол-воСоседей ЭлМатрицы
DUP 2 < IF DROP \ умерла от недостатка 0 J I Среда =ЭлМатрицы ELSE 4 > IF \ умерла от перенаселенности 0 J I Среда =ЭлМатрицы ELSE \ родилась новая или продолжает жить старая 1 J I Среда =ЭлМатрицы THEN THEN
LOOP LOOP ;
\ ------------------------------- : Main
ЗаселитьСреду \ Инициализировали РассчитатьКол-воСоседей
\ ПоказатьСовмещенно \ Для отладки BEGIN KEY 113 <> WHILE ПоказатьСреду ОбновитьСреду РассчитатьКол-воСоседей CR REPEAT
BYE ;
Main
А на сегодня – все!
<<< Предыдущий | Начало | Следующий >>> |
При цитировании, ссылка на оригинальный текст обязательна. Допускается копирование материалов только целиком, без внесения каких-либо изменений в оригинальный текст, меняющих смысл, структуру материала и проч. |