Решение головоломки с ограничением в прологе

В настоящее время я начинаю изучать ограничения в прологе, используя SICStus Prolog. Хотя я знаю, как решать простые задачи, используя это, у меня есть одно упражнение, в котором я должен решить головоломку. Однако я понятия не имею, как это решить, так как у меня будет несколько разных элементов с разными свойствами (кусками). Может ли кто-нибудь дать мне пример того, как представить список кусков в прологе и какие ограничения я должен использовать?


person user697110    schedule 01.12.2011    source источник
comment
Можете ли вы дать точную информацию о самой головоломке? Головоломки — это довольно большая категория из того, что я вижу при гуглении.   -  person m09    schedule 01.12.2011
comment
Сама головоломка такова: jaapsch.net/puzzles/rapids.htm хотя для Мне достаточно примера того, как сделать простую головоломку из частей, подходящих друг к другу, чтобы начать.   -  person user697110    schedule 01.12.2011
comment
В большинстве головоломок ориентация частей ограничена четырьмя направлениями, и их расположение примерно соответствует декартовой сетке. Ваш пример головоломки немного более ограничен тем, что для каждой части разрешены только две ориентации (слева или справа), а размещение точно на декартовой сетке 3x4. Граница головоломки обеспечивает граничные условия в примере, тогда как в обычной головоломке краевые части должны быть различимы своими прямыми краями (или углами) и собраны, чтобы обеспечить краевые условия для внутренних частей.   -  person hardmath    schedule 01.12.2011


Ответы (1)


Для первой попытки я бы сделал что-то в этом роде:

Для каждого поля используйте 5 переменных. Первая переменная обозначает часть головоломки, которая выходит на поле. Каждая часть имеет свой идентификатор. В случае головоломки, которую вы упомянули в своем комментарии выше, есть 12 частей, поэтому каждая переменная будет иметь домен {1..12}:

P #:: 1..12,

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

[EL,EU,ER,ED] #:: 0..1,

Кусочек головоломки можно закодировать следующим образом:

piece(1,  [](0,1,1,0)).

Это, например, часть А в описании вашей головоломки на веб-сайте, которое вы дали в комментарии.

Теперь вы можете определить отношение соседства между двумя соседними полями. Поскольку у вас есть логические переменные, а вкладка должна соответствовать вмятине, вы устанавливаете ограничение (не «ограничение»), требующее, чтобы сумма была равна единице:

R1 + L2 #= 1,

То есть правый край фигуры на поле один должен совпадать с левым краем фигуры на поле 2.

Вы также устанавливаете аналогичные ограничения для краев вдоль границ.

Вы устанавливаете ограничение, требующее, чтобы все поля содержали разные части:

alldifferent(Fields),

(где Fields — список, содержащий переменные P)

Вам нужна переменная, которая обозначает ориентацию частей головоломки:

O #:: 0..1,

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

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

 once(piece(N, [](L,U,V,D))),
 ( ((P#=N) #/\ (O#=0)) #=> ((EL#=L) #/\ (EU#=  U) #/\ (ER#=R) #/\ (ED#=  D)) ),
 ( ((P#=N) #/\ (O#=1)) #=> ((EL#=R) #/\ (EU#=1-D) #/\ (ER#=L) #/\ (ED#=1-U)) ),

(синтаксис не для Sicstus, а для ECLiPSe. Однако библиотеки ограничений FD должны быть достаточно похожими)

Обратите внимание, что мне пришлось инвертировать кодировку нижнего края при переворачивании куска вверх ногами. Это позволило мне сохранить ограничения «сумма равна единице» для ребер вверх/вниз, но это неоптимально, так как это мешало мне легко распространять информацию из переменных ребра в переменные части (кусок может получить ту же кодировку, что и другой в перевернутом виде). Но мне было лень менять кодировку для первой попытки.

(Редактировать: это должно быть легко исправить, инвертируя кодировку для нижнего края, например: piece(1, [](0,1,1,1))., и используя ограничения, которые требуют, чтобы верхние и нижние края соседних частей имели одинаковое значение, а не имели сумму один.)

Собрав все вместе и просто пометив переменные P (после первого создания экземпляра переменной ориентации), мы получим два решения:

?- rapids(S,O).
S = []([](5, 2, 8, 6), [](11, 10, 1, 4), [](3, 9, 12, 7))
O = 0
Yes (0.01s cpu, solution 1, maybe more)
S = []([](5, 3, 11, 12), [](8, 9, 7, 4), [](2, 10, 6, 1))
O = 1
Yes (0.04s cpu, solution 2, maybe more)
No (0.05s cpu)

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

person twinterer    schedule 01.12.2011