Серия «Программирую, пока программируется»

7

Немного о создании лабиринтов (Delphi)

Заинтересовался идеей создания двухмерных лабиринтов, после того, как случайно набрел на ресурс https://www.mazegenerator.net/.

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

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

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

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

Вот фрагмент получаемого лабиринта.

Немного о создании лабиринтов (Delphi) Программирование, Паскаль, Windows, IT, Программа, Delphi, Лабиринт, Длиннопост

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

Два этапа алгоритма:

Создание пространства путей в матрице за один ее проход

Объединение путей в единственный также за один проход

Программу писал на Паскале, поскольку это лучшее, что придумало человечество (ну или потому, что других языков не знаю), взяв за основу Эмбаркадеровскую Дельфи.

Бросил на форму два TEdit, чтобы указывать в них размерность матрицы лабиринта по Х и по Y. И компонент TImage для отрисовки лабиринта.

В первую очередь указываем тип матрицы:

type

cell = record

group:integer;

r,d:integer;

end;

И наши переменные:

var

form1: Tform1;

count:integer; //счетчик групп

cel:array of array of cell; //массив ячеек

x,y:integer;

Каждая ячейка в массиве cel имеет три поля:

group – номер группы

r и d – правая и нижняя границы соответственно (стенки лабиринта)

Создаем и заполняем массив после нажатия на кнопку:

x:=strtoint(edit1.Text); //Наши значения размера массива из эдитов

y:=strtoint(edit2.Text);

setlength(cel,x+1,y+1); //Задаем размеры массива ячеек с запасом +1, чтобы не возиться с нулевыми индексами

for j := 1 to y do //Заполняем пространство group нолями, а все стенки делаем закрытыми

for i := 1 to x do

begin

cel[i,j].group:=0;

cel[i,j].r:=1;

cel[i,j].d:=1;

end;

Все, теперь можем начинать работу алгоритма

1. Этап - создаем пространство путей, которые будут проложены трактором.

count:=1; // Наш счетчик номеров и количества групп

for j := 1 to y do //Первый проход матрицы по высоте и ширине

for I := 1 to x do

if cel[i,j].group=0 then //Находим пустую клетку и пускаем из нее трактор

begin

track(i,j);

inc(count); //Увеличиваем счетчик, после того, как трактор построил путь

end;

Собственно, код трактора:

procedure tform1.track(x1: Integer; y1: Integer);

var

k,mx,my:integer;

label start;

begin

//Процедура прохода и построения трактором лабиринта

mx:=x1;my:=y1;

cel[mx,my].group:=count; //присваиваем стартовой клетке номер новой группы

start:

k:=random(4)+1; //Случайное направление движения

case k of

1:

if (mx>1) then

if (cel[mx-1,my].group=0) then //Если слева пусто, идем туда и заодно убираем стенку

begin

cel[mx-1,my].r:=0;

cel[mx-1,my].group:=count;

mx:=mx-1;

goto start;

end;

2:

if (mx<x) then //Аналогично остальные направления

if (cel[mx+1,my].group=0) then

begin

cel[mx,my].r:=0;

cel[mx+1,my].group:=count;

mx:=mx+1;

goto start;

end;

3:

if (my>1) then

if (cel[mx,my-1].group=0) then

begin

cel[mx,my-1].d:=0;

cel[mx,my-1].group:=count;

my:=my-1;

goto start;

end;

4:

if (my<y) then

if (cel[mx,my+1].group=0) then

begin

cel[mx,my].d:=0;

cel[mx,my+1].group:=count;

my:=my+1;

goto start;

end;

end;

//Проверяем не тупик ли

if canmove(mx,my) then goto start;

end;

В конце проверка на возможность дальнейшего движения для трактора:

function tform1.canmove(x1: Integer; y1: Integer): LongBool;

begin

//Проверка на возможность хода

result:=false;

if x1>1 then if cel[x1-1,y1].group=0 then

begin

result:=true;exit;

end;

if x1<x then if cel[x1+1,y1].group=0 then

begin

result:=true;exit;

end;

if y1>1 then if cel[x1,y1-1].group=0 then

begin

result:=true;exit;

end;

if y1<y then if cel[x1,y1+1].group=0 then

begin

result:=true;exit;

end;

end;

Все, наше пространство путей готово. Оно будет иметь примерно такой вид:

Немного о создании лабиринтов (Delphi) Программирование, Паскаль, Windows, IT, Программа, Delphi, Лабиринт, Длиннопост

Соответствующая ему матрица групп:

Немного о создании лабиринтов (Delphi) Программирование, Паскаль, Windows, IT, Программа, Delphi, Лабиринт, Длиннопост

Как видно из точки S в точку F дойти невозможно, тем не менее у нас уже есть четыре отдельных группы, каждая из которых представляет собой путь, пройденный трактором.

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

Осталось объединить их в один идеальный путь:

for i:= 1 to x do //Запускаем второй проход матрицы (первая ячейка всегда имеет номер группы 1)

for j:= 1 to y do

begin

if i<x then if cel[i+1,j].group<>1 then //Если находим группу справа от нас отличную от единицы то присоединяем к нашей единичной следующим образом:

begin

cel[i,j].r:=0; //Убираем заодно стенку

del(cel[i+1,j].group,i+1); //Меняем все найденные индексы группы на нашу единицу, для оптимизации процесса делаем это не с начала, а с ряда, где находится наша найденная клетка

end;

if j<y then if cel[i,j+1].group<>1 then //Аналогично смотрим клетку снизу

begin

cel[i,j].d:=0;

del(cel[i,j+1].group,i);

end;

end;

Процедура замены на единички:

procedure tform1.del(x1,x2: Integer); //x1 – наша группа, х2 – точка старта

var

i,j,f:integer;

begin

for i:= x2 to x do

begin

f:=0; //Флаг поиска групп в ряду для оптимизации

for j:= 1 to y do

if cel[i,j].group=x1 then

begin

cel[i,j].group:=1;

f:=1;

end;

if f=0 then exit; //Если не найдено групп в хотя бы в одном ряду, то в оставшихся рядах и искать нет смысла, чтобы не тратить ресурсы ПК

end;

end;

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

Осталось только нарисовать любым доступным способом, например, так:

image1.width:=x*20;

image1.Height:=y*20;

image1.Picture.Bitmap.Width:=x*20;

image1.Picture.Bitmap.Height:=y*20;

with image1.Canvas do

begin

brush.Color:=clwhite;

fillrect(rect(0,0,width,height));

pen.Color:=clblack;

pen.Style:=psSolid;

pen.Width:=2;

Rectangle(1,1,x*20,y*20);

for j := 1 to y do

for i := 1 to x do

begin

moveto(i*20,(j-1)*20);

if cel[i,j].r=1 then lineto(i*20,j*20);

moveto((i-1)*20,j*20);

if cel[i,j].d=1 then lineto(i*20,j*20);

end;

Ну тут и объяснять ничего не надо.

Из особенностей этого алгоритма можно отметить высокую скорость работы – генерация матрицы размером 5000 на 5000 без отрисовки заняла всего 2 минуты, на моем не самом быстром домашнем ПК, а это 25 миллионов ячеек. Генерация 500х500 с отрисовкой – всего 6 секунд!

У меня нет возможности сравнить с другими алгоритмами, но там, в статье как раз на скорость и жаловались. Генерация маленьких лабиринтов – мгновенна. Стандартный размер 200х200 делает как раз за секунду.

Естественно рисовать таким способом большой лабиринт нет смысла – никакой памяти не хватит, можно делать частичную отрисовку на TImage и распечатывать частями. Но для лабиринтов размером до 300-400 ячеек на сторону подходит вполне.

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

Еще недостаток – на мой взгляд, не слишком высокая сложность, но, если сделать хотя бы 15х15 уже сразу глазом не зацепишься за правильное решение.

Готовую программу выложил на Гитхабе, если кому интересно – можете скачать здесь. Хотелось бы услышать мнение специалистов.

Другие проекты:

Игра в пазлы: https://github.com/stalkself/SuperPuzzleMaster

Показать полностью 3

Бесполезное

Попался на глаза псто: Бесполезный сайт.

Заинтересовал алгоритм, решил попробовать воссоздать его на Дельфи.

Оказалось, он достаточно прост, не пришлось долго думать.

Ну собственно, получилось такое:

Бесполезное Delphi, Паскаль, Программирование, Алгоритм, IT

Скачать, если интересно, можно здесь:

https://disk.yandex.ru/d/TqO3ppfOquhXlQ

3

Игра на Дельфи - кольца

Попалась как-то на глаза интересная игра - кольца: https://www.min2win.ru/game/koltsa.html

Решил попробовать сделать такую с нуля, используя Дельфи. Собственно результат:

Игра на Дельфи - кольца Компьютерные игры, Программирование, Игры, Gamedev, Инди игра, Разработка, Инди, Delphi, Паскаль

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

Думаю это был интересный опыт.

Скачать, если интересно, можно здесь: https://disk.yandex.ru/d/3AM_Pbo8mBikEg

Все бесплатно. Никакой рекламы телеграм каналов, донатов и попрошайничества.

Считаю, что творчество должно быть бесплатным и доступным для всех.

Другие мои проекты на Дельфи:

Игра ИнфиниЛуп:

https://disk.yandex.ru/d/qdss12g6u71Z7A

+ предыдущая версия игры (на прохождение 100 уровней):

https://disk.yandex.ru/d/Hf5w_XL00GypyA

Игра "Веселый водопроводчик": https://disk.yandex.ru/d/3onnil49XyTNDQ

Пазлы (есть сохранение, можно создавать пазлы любого размера и формы): https://disk.yandex.ru/d/8RccyBgVWKevgA

Игра 2048: https://disk.yandex.ru/d/3Ks13TA2dDy4ow

Показать полностью 1
5

Игра на Дельфи

Решил улучшить свою игруху, добавив процедурную генерацию уровней, сделав ее таким образом бесконечной. Также улучшил по-возможности графоний и сделал возможность выбрать сложность (количество блоков).

Игра на Дельфи Компьютерные игры, Игры, Delphi, Программирование, Инди игра

Скачать можно тут:

https://disk.yandex.ru/d/qdss12g6u71Z7A

Все бесплатно. Никакой рекламы ТГ, просто рекламы, донатов и попрошайничества.

Другие мои проекты на Дельфи:

Предыдущая версия игры (на прохождение 100 уровней):

https://disk.yandex.ru/d/Hf5w_XL00GypyA

Игра "Веселый водопроводчик": https://disk.yandex.ru/d/3onnil49XyTNDQ

Пазлы (есть сохранение, можно создавать пазлы любого размера и формы): https://disk.yandex.ru/d/8RccyBgVWKevgA

Игра 2048: https://disk.yandex.ru/d/3Ks13TA2dDy4ow

Показать полностью 1
75

Игры на Дельфи

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

Игры на Дельфи Игры, Компьютерные игры, Delphi, Инди игра, Головоломка, Программирование

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

Скачать можно здесь, если есть желание: https://disk.yandex.ru/d/Hf5w_XL00GypyA

Другие мои проекты на Дельфи:

Игра "Веселый водопроводчик": https://disk.yandex.ru/d/3onnil49XyTNDQ

Пазлы (есть сохранение, можно создавать пазлы любого размера и формы): https://disk.yandex.ru/d/8RccyBgVWKevgA

Игра 2048: https://disk.yandex.ru/d/3Ks13TA2dDy4ow

Все бесплатно, никакой рекламы и донатов, никаких ссылок на дзен, телегу и яндекс-кошелек.

Показать полностью 1
4

2048 на Дельфи

Попалась на глаза нехитрая онлайн-игра 2048.

Решил попробовать сделать самостоятельно на Дельфи. Алгоритм достаточно простой, но возможно кому-то будет интересно поиграть на ПК.

На всякий случай вот ссылка на скачивание. Все бесплатно.

2048 на Дельфи Программирование, Delphi, Игры, Компьютерные игры
Показать полностью 1

С++ и муки проникновения

Всем известно, что дехканину доступны три пути: вебкам, закладки и айти.

Давненько подумывал о том, чтобы сбросить задницу с дивана и попробовать последний вариант, но все как-то было недосуг.

Интереса ради, решил хотя бы составить хотя бы первичное приблизительное представление о таком всем известном монстре, как С++, и для начала рискнул перевести на него имеющийся мини-проект пазлов, реализованный в старом добром Дельфи.

Выбрал естественно эмбаркадеровскую IDE, благо она процентов на 99 идентична их же дельфийской. Соответственно оказалось достаточно просто скопировать форму со всей мешаниной контролов на ней, и вставить в новый проект, не прибегая к утомительному растаскиванию кнопок и других компонентов.

Неделя страданий и вуаля:

С++ и муки проникновения Разработка, Программирование, C++, Delphi, Программист, IT, Длиннопост

Имею два идентичных ехешника разного размера, скачать которые можно по ссылкам:


С++ вариант:

https://mega.nz/file/ZEEzGLJQ#3htC3RMzhlbw0-xS9DZ0SwtyqPOhXV...

Delphi:

https://mega.nz/file/RVl1wKQI#frhBOl5nGsrT_dVGbPYM3e7dOg4rMp...


Каких-то особых отличий в эффективности и скорости и не заметил. По-моему вариант на Delphi более стабилен и работает чуть быстрее. С другой стороны С++ вариант весит меньше, но не критично.

Осталось еще попробовать зафигачить алгоритм на Яве, чтобы сделать браузерный вариант игры.


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

Ну и собственно немного размышлений на тему.

Первое впечатление - C++ это неудобный и отвратительный язык, непонятно зачем существующий. Он не является достаточно низкоуровневым, и при этом неудобен для конечного пользователя.

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

Что выбесило:

Опять же, невозможность использовать многие нужные классы и методы напрямую, только через указатели, причем опытные сиплюсплюисты рекомендовали использовать умные указатели, из-за которых код начинает превращаться в какой-то маловразумительный гипертекст

Отсутствие динамических массивов - не, ну так-то по факту они есть, но объявляются опосредованно, по типу:

int a[lenght]; где lenght требуемая длина из переменной, но по факту это все то же самое статическое объявление в процедуре, поскольку не зная конкретно значение, объявить массив глобальным, доступным для других процедур не получится. Для Дельфи - это вообще не проблема. Можно объявлять когда угодно и где угодно.

Исключение векторы - но с ними опять же заметно усложняются самые простые вещи.

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

Такое отношение к нему считаю недопустимым.

Далее циклы:

Тот же for по сути является все тем же while, для которого просто вынесена в заголовок операция изменения условия (счетчика цикла).

Цикл от нуля до десяти:

for (i==0;i<=10;++i) {}

это все то же самое

while (i<=10) с заранее заданным значением i, у которого операция со счетчиком осуществляется внутри тела.

Отвратительное сравнение ==. Я потратил почти целый день, не понимая почему программа вдруг стала вести себя непредсказуемым образом. Оказалось, пропустил одно "=" в условии.

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

Если разделить целое i на вещественное j, есть шанс получить округленное значение (в моем случае до нуля). Приходится сначала приводить целое к вещественному типу, а только потом уже делить его.

Отвратительный синтаксис. Как же осточертело вместо точек рисовать стрелочки. Тем более, что для некоторых объектов нужно все-таки использовать точки. Это невозможно понять, это нужно запомнить. Вообще в этом языке отсутствует логика. Если в Дельфи ты точно знаешь, как работать с методами, то тут возможны варианты - в некоторых случаях так, в других этак, в третьих вообще никак, ну или через дядю Васю из соседней деревни.

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

Короче, С++, давай до свидания. Чувствую не выйдет из меня айтишника. Уже подумываю о покупке вебки.

Показать полностью 1

Сколько нужно времени, чтобы уложить теплый пол?

Точно не скажем, но в нашем проекте с этим можно справиться буквально за минуту одной левой!

Попробовать

Паззлы на дельфи V2

Всем привет. Выкладывал ранее программку, написанную на Delphi, но решил улучшить ее, выбрав совсем другой алгоритм нарезки паззлов из любой произвольной картинки. Получилось вот такое:

Паззлы на дельфи V2 Программирование, Delphi, Дельфи, Разработка, Gamedev, Инди игра

Что исправил:

Внешний вид паззлов можно настраивать, изменяя их до неузнаваемости.

Более простое меню, достаточно поочередно нажать три картинки:

Паззлы на дельфи V2 Программирование, Delphi, Дельфи, Разработка, Gamedev, Инди игра

Работает намного быстрее - теперь не нужно утомительно ждать, пока программа начертит паззлы, все занимает буквально 3-4 секунды, за счет отказа от использования медленных функций GDI.

Пофиксил ошибки, связанные с переполнением памяти и неправильной обработкой картинок.

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

Скачать, если кому интересно можно здесь:


https://cloud.degoo.com/share/CUc-RIcDNG2xxySI4ZtQXg


Отсутствует реклама, донат и попрошайничество. Все полностью бесплатно и свободно.

Единственное - у меня нет сертификата издателя для Delphi, поэтому могут ругаться некоторые антивирусы. Если есть сомнения - всегда можно проверить антивирусом.

Если найдутся баги, всегда буду рад их исправить.

Показать полностью 2
Отличная работа, все прочитано!