GNU Prolog, 493 байта
z(_,[_,_]).
z(F,[A,B,C|T]):-call(F,A,B,C),z(F,[B,C|T]).
i([],[],[],[]).
i([H|A],[I|B],[J|C],[H-I-J|T]):-i(A,B,C,T).
c(A/_-B/_-C/_,D/_-_/T-E/_,F/_-G/_-H/_):-T#=A+B+C+D+E+F+G+H.
r(A,B,C):-i(A,B,C,L),z(c,L).
q(63,V):-var(V).
q(42,1/_).
q(X,0/Y):-Y#=X-48.
l([],[0/_]).
l([H|T],[E|U]):-q(H,E),l(T,U).
p([],[[0/_,0/_]],0).
p([],[[0/_|T]],N):-M#=N-1,p([],[T],M).
p([H|T],[[0/_|E]|U],N):-p(T,U,N),l(H,E).
m([H|A],B):-length(H,N),p([],[R],N),p([H|A],M,N),z(r,[R|M]),p(B,M,N).
s(A):-setof(B,m(A,B),[_]).
Дополнительный предикат, который может быть полезен для тестирования (не является частью представления):
d([]).
d([H|T]):-format("~s~n",[H]),d(T).
Пролог, безусловно, является подходящим языком для решения этой задачи с практической точки зрения. Эта программа в значительной степени просто устанавливает правила Minesweeper и позволяет решателю ограничений GNU Prolog решить проблему оттуда.
z
и i
являются служебными функциями ( z
выполняет своего рода операцию, подобную складке, но на наборах из трех смежных элементов, а не 2; i
переносит 3 списка из n элементов в список из n 3-х кортежей). Мы внутренне сохраняем ячейку как , где x равно 1 для мин и 0 для немина, а y - количество смежных мин; выражает это ограничение на доске. применяется к каждому ряду доски; и поэтому проверяет, является ли доска действительной.x/y
c
r
c
z(r,M)
M
К сожалению, формат ввода, необходимый для непосредственной работы, нецелесообразен, поэтому мне также пришлось включить синтаксический анализатор (который, вероятно, учитывает больше кода, чем фактический механизм правил, и большую часть времени проводил отладку; механизм правил Minesweeper в значительной степени работал первый раз, но парсер был полон мыслителей). q
преобразует одну ячейку между кодом символа и нашим форматом. преобразует одну строку доски (оставляя одну ячейку, которая, как известно, не является миной, но с неизвестным количеством соседних мин, на каждом краю линии как граница);x/y
l
p
преобразует всю доску (включая нижнюю границу, но исключая верхнюю). Все эти функции могут быть запущены как вперед, так и назад, что позволяет как анализировать, так и печатать доску. (Есть несколько раздражающих шатаний с третьим аргументом p
, который задает ширину доски; это потому, что у Пролога нет матричного типа, и если я не ограничу доску прямоугольной, программа перейдет в бесконечный цикл, пытающийся постепенно расширить границы вокруг доски.)
m
является главной функцией решения Сапер. Он анализирует входную строку, генерируя доску с правильной границей (используя рекурсивный регистр p
для преобразования большей части доски, затем вызывая базовый случай напрямую для генерации верхней границы, которая имеет ту же структуру, что и нижняя граница). Тогда это вызываетz(r,[R|M])
запустить механизм правил Minesweeper, который (с этим шаблоном вызовов) становится генератором, генерирующим только допустимые доски. На этом этапе правление все еще выражается в виде набора ограничений, что потенциально неудобно для нас; возможно, у нас может быть один набор ограничений, который может представлять более одной доски. Кроме того, мы еще нигде не указали, что каждый квадрат содержит не более одной шахты. Таким образом, нам необходимо явно «свернуть форму волны» каждого квадрата, требуя, чтобы он был конкретно либо (одиночным), либо минным, и самый простой способ сделать это - запустить его через анализатор в обратном направлении ( var(V)
на q(63,V)
Корпус предназначен для предотвращения ?
обратного хода корпуса, и, таким образом, отклонение доски заставляет ее быть полностью известной). Наконец, мы возвращаем разобранную доску изm
; m
таким образом, становится генератором, который принимает частично неизвестную доску и генерирует все известные платы в соответствии с ней.
Этого действительно достаточно, чтобы решить Сапер, но вопрос явно просит проверить, есть ли точно одно решение, а не найти все решения. Поэтому я написал дополнительный предикат, s
который просто преобразует генератор m
в набор, а затем утверждает, что набор содержит ровно один элемент. Это означает, что s
вернет truey ( yes
), если действительно есть только одно решение, или falsey ( no
), если существует более одного или меньше одного.
d
не является частью решения и не входит в число байтов; это функция для печати списка строк, как если бы это была матрица, которая позволяет проверять платы, сгенерированные m
(по умолчанию, GNU Prolog печатает строки в виде списка кодов ASCII, потому что он обрабатывает две эти функции как синонимы; этот формат довольно трудно читать). Это полезно во время тестирования или если вы хотите использовать m
в качестве практического решателя Minesweeper (потому что он использует решатель ограничений, он очень эффективен).
2?
не имеет решений, что означает, что он не может быть получен из реальной игры Сапер. Следовательно, он не считается «доской Сапер» ... да?)