Головоломка «заполнение узором плиткой» - PullRequest
17 голосов
/ 26 июля 2010

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

Постановка задачи:

Допустим, у вас есть все или подмножество следующих плиток (это комбинация всех возможных 4-битных комбинаций, отображаемых в направлениях вправо, вверх, влево и вниз):

альтернативный текст http://img189.imageshack.us/img189/3713/basetileset.png

Вам предоставляется сетка, в которой некоторые ячейки помечены (true), а другие нет (false). Это может быть сгенерировано, например, алгоритмом перлин-шума. Цель состоит в том, чтобы заполнить это пространство плитками, чтобы было как можно больше сложных плиток. В идеале все плитки должны быть связаны. Для некоторых входных значений может не быть решения (доступные плитки + шаблон). Всегда есть хотя бы одно решение, если доступен верхний левый, не связанный элемент (то есть все ячейки шаблона могут быть заполнены этим элементом).

Пример:

Изображения слева направо: наличие плитки (можно использовать зеленую плитку, красная - нет), шаблон для заполнения и решение

альтернативный текст http://img806.imageshack.us/img806/2391/sampletileset.png + альтернативный текст http://img841.imageshack.us/img841/7/samplepattern.png = альтернативный текст http://img690.imageshack.us/img690/2585/samplesolution.png

Что я пробовал:

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

Примечания:

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

Ответы [ 3 ]

6 голосов
/ 26 июля 2010

Я полагаю, что для экземпляров из 100 ячеек динамическая программа, основанная на резьбовой декомпозиции входного графа, может соответствовать всем требованиям.

Резное разложение

В теории графов резное разложение графа является рекурсивным бинарным разбиением его вершин. Например, вот график

1--2--3
|  |
|  |
4--5

и одно из его резьбовых разложений

     {1,2,3,4,5}
     /         \
  {1,4}        {2,3,5}
  /   \        /     \
{1}   {4}  {2,5}     {3}
           /   \
         {2}   {5}.

Ширина резного разложения - это максимальное количество ребер, выходящих из одного из его разбиений. В этом случае {2,5} имеет исходящие ребра 2--1, 2--3 и 5--4, поэтому ширина равна 3. Ширина раздела в стиле дерева kd сетки 10 x 10 равна 13.

Ширина резьбы графика - это минимальная ширина декомпозиции резьбы. Известно, что плоские графы (в частности, подграфы сеточных графов) с n вершинами имеют ширину резьбы O (√n), а константа big-O относительно мала.

Динамическая программа

При заданном входном графе из n-вершин и резьбовом разложении ширины w существует алгоритм времени O (2 w n) для вычисления оптимального выбора тайла. Это время работы быстро растет в w, поэтому вы должны попытаться вручную разложить некоторые входные данные, чтобы понять, какую производительность ожидать.

Алгоритм работает на дереве декомпозиции снизу вверх. Пусть X - разбиение, и пусть F - множество ребер, покидающих X. Мы составляем таблицу, отображающую каждую из 2 | F | возможностей наличия или отсутствия ребер в F в оптимальную сумму на X при указанных ограничениях (-Infinity, если решения не существует). Например, с разделом {1,4} у нас есть записи

{} -> ??
{1--2} -> ??
{4--5} -> ??
{1--2,4--5} -> ??

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

Например, предположим, что у нас есть кусочки

                       |
.    .-    .-    -.    .
     |                 

Таблица для {1} составляет

{} -> 0
{1--2} -> 1
{1--4} -> -Infinity
{1--2,1--4} -> 2

Таблица для {4} составляет

{} -> 0
{1--4} -> 1
{4--5} -> 1
{1--4,4--5} -> -Infinity

Теперь давайте вычислим таблицу для {1,4}. Для {}, без края 1--4 у нас есть оценка 0 для {1} (запись {}) плюс оценка 0 для {4} (запись {}). С ребром 1--4 у нас есть оценка -Infinity + 1 = -Infinity (записи {1--4}).

{} -> 0

Для {1--2} баллы: 1 + 0 = 1 без 1--4 и 2 + 1 = 3 с.

{1--2} -> 3

Продолжение.

{4--5} -> 0 + 1 = 1 (> -Infinity = -Infinity + (-Infinity))
{1--2,4--5} -> 1 + 1 = 2 (> -Infinity = 2 + (-Infinity))

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

Нахождение резного разложения

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

4 голосов
/ 26 июля 2010

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

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

2 голосов
/ 30 июля 2010

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

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

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

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

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

...