GNU Prolog, 493 octets
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),[_]).
Un prédicat supplémentaire qui peut être utile pour les tests (ne fait pas partie de la soumission):
d([]).
d([H|T]):-format("~s~n",[H]),d(T).
Prolog est certainement le bon langage pour résoudre cette tâche du point de vue pratique. Ce programme énonce à peu près les règles du démineur et permet au solveur de contraintes de GNU Prolog de résoudre le problème à partir de là.
z
et i
sont des fonctions d'utilité ( z
fait une sorte d'opération de type pli mais sur des ensembles de trois éléments adjacents plutôt que 2; i
transpose 3 listes de n éléments en une liste de n 3-tuples). Nous stockons en interne une cellule comme , où xx/y
est 1 pour une mine et 0 pour une mine non, et y est le nombre de mines adjacentes; c
exprime cette contrainte au tableau. r
s'applique c
à chaque rangée du tableau; et z(r,M)
vérifie donc si M
c'est une carte valide.
Malheureusement, le format d'entrée requis pour faire ce travail directement est déraisonnable, j'ai donc également dû inclure un analyseur (qui représente probablement plus de code que le moteur de règles réel, et la plupart du temps consacré au débogage; le moteur de règles du démineur fonctionnait à peu près) première fois, mais l'analyseur était plein de thinkos). q
convertit une seule cellule entre un code de caractère et notre format. convertit une ligne du plateau (en laissant une cellule connue pour ne pas être une mine, mais avec un nombre inconnu de mines voisines, à chaque bord de la ligne comme frontière);x/y
l
p
convertit la carte entière (y compris la bordure inférieure, mais à l'exclusion de la bordure supérieure). Toutes ces fonctions peuvent être exécutées vers l'avant ou vers l'arrière, ce qui permet d'analyser et d'imprimer la carte à la fois. (Il y a des agitations agaçantes avec le troisième argument de p
, qui spécifie la largeur de la carte; c'est parce que Prolog n'a pas de type de matrice, et si je ne contrains pas la carte à être rectangulaire, le programme entrera dans une boucle infinie essayant des bordures progressivement plus larges autour de la planche.)
m
est la principale fonction de résolution du démineur. Il analyse la chaîne d'entrée, générant une carte avec une bordure correcte (en utilisant le cas récursif de p
pour convertir la majeure partie de la carte, puis en appelant directement le cas de base pour générer la bordure supérieure, qui a la même structure que la bordure inférieure). Ensuite, il appellez(r,[R|M])
pour exécuter le moteur de règles du démineur, qui (avec ce modèle d'appel) devient un générateur générant uniquement des cartes valides. À ce stade, la carte est toujours exprimée comme un ensemble de contraintes, ce qui est potentiellement gênant pour nous; nous pourrions peut-être avoir un seul ensemble de contraintes qui pourraient représenter plus d'un conseil. De plus, nous n'avons encore spécifié nulle part que chaque carré contient au plus une mine. En tant que tel, nous devons explicitement "réduire la forme d'onde" de chaque carré, en exigeant qu'il s'agisse spécifiquement d'une mine (unique) ou d'une mine non, et la manière la plus simple de le faire est de la faire passer par l'analyseur vers l'arrière (le var(V)
sur le q(63,V)
le boîtier est conçu pour empêcher le ?
boîtier de tourner en arrière, et ainsi l'analyse de la carte force à être pleinement connue). Enfin, nous renvoyons la planche analysée dem
; m
devient ainsi un générateur qui prend une carte partiellement inconnue et génère toutes les cartes connues qui lui correspondent.
C'est vraiment suffisant pour résoudre le démineur, mais la question demande explicitement de vérifier s'il y a exactement une solution, plutôt que de trouver toutes les solutions. En tant que tel, j'ai écrit un prédicat supplémentaire s
qui convertit simplement le générateur m
en un ensemble, puis affirme que l'ensemble a exactement un élément. Cela signifie que s
retournera truey ( yes
) s'il y a en effet exactement une solution, ou falsey ( no
) s'il y en a plus d'une ou moins d'une.
d
ne fait pas partie de la solution et n'est pas inclus dans le bytecount; c'est une fonction pour imprimer une liste de chaînes comme s'il s'agissait d'une matrice, ce qui permet d'inspecter les cartes générées par m
(par défaut, GNU Prolog imprime les chaînes comme une liste de codes ASCII, car il traite les deux comme synonymes; ce format est assez difficile à lire). Il est utile lors des tests ou si vous souhaitez l'utiliser m
comme un solveur de démineur pratique (car il utilise un solveur de contraintes, il est très efficace).
2?
n'a pas de solutions, ce qui signifie qu'il ne peut pas provenir d'un véritable jeu de démineur. Par conséquent, il n'est pas considéré comme un "plateau de démineur" ... oui?)