Saper to popularna gra logiczna, w której musisz odkryć, które kafelki są „kopalniami”, bez klikania na te kafelki. Zamiast tego klikasz na pobliskie kafelki, aby odsłonić liczbę sąsiadujących min. Jednym minusem gry jest to, że można skończyć w scenariuszu, w którym istnieje wiele prawidłowych odpowiedzi i można się tylko domyślać. Na przykład weź następującą tablicę:
1110
2*31
3*??
2*4?
112?
W tym formacie liczba reprezentuje liczbę sąsiednich min, *
reprezentuje znaną minę i „?” reprezentuje potencjalną kopalnię. Niefortunną rzeczą w tej konkretnej układance jest to, że istnieją cztery różne i ważne potencjalne rozwiązania:
1110 1110 1110 1110
2*31 2*31 2*31 2*31
3*4* 3*5* 3**2 3**1
2*42 2*4* 2*4* 2*42
112* 1121 1121 112*
Oznacza to, że plansza jest nierozwiązywalna . Oto przykład rozwiązywalnej planszy:
1121
1??*
12?*
0122
Tę tablicę można rozwiązać, ponieważ istnieje tylko jedno możliwe prawidłowe rozwiązanie:
1121
1*4*
12**
0122
Twoim zadaniem jest napisanie programu lub funkcji, która pobiera prawidłową tablicę trałowców i określa, czy jest ona do rozwiązania, czy nie. Przez „prawidłową planszę trałowca” rozumiem, że dane wejściowe będą zawsze prostokątne, mają co najmniej jedno rozwiązanie i nie będą zawierały żadnych nieprawidłowych znaków.
Dane wejściowe mogą być tablicą znaków, tablicą ciągów, ciągiem zawierającym znaki nowego wiersza itp. Wynik musi być prawdziwą wartością, jeśli można ją rozwiązać, i wartością fałszowania, jeśli nie jest. Nie martwię się zbytnio o wydajność, ale twoje rozwiązanie musi teoretycznie działać dla każdego rozmiaru wejściowego.
Jak zwykle obowiązują standardowe luki i wygrywa najkrótsze rozwiązanie w bajtach!
Przykłady:
Wszystkie poniższe przykłady można rozwiązać:
1121
1??*
12?*
0122
1110
1???
1110
0000
1110
3???
??20
*310
****
****
****
****
0000
0000
0000
0000
1100
*100
2321
??*2
13*2
1221
1*10
1110
1121
2*??
2*31
2220
1*10
Następujących przykładów nie da się rozwiązać:
1110
2*31
3*??
2*4?
112?
01??11*211
12??2323*1
1*33*2*210
12?2122321
13?3101**1
1***101221
1***
3*52
2*31
12??
02??
01??
00000111
000012*1
00001*21
22101110
**100111
?31123*1
?311**31
**113*20
źródło
2?
nie ma rozwiązań, co oznacza, że nie może pochodzić z prawdziwej gry Saper. Dlatego nie jest uważany za „planszęOdpowiedzi:
GNU Prolog, 493 bajtów
Dodatkowy predykat, który może być przydatny do testowania (nie stanowi części zgłoszenia):
Prolog jest zdecydowanie właściwym językiem do rozwiązania tego zadania z praktycznego punktu widzenia. Ten program w zasadzie określa tylko zasady Saperów i umożliwia rozwiązanie problemu ograniczeniom GNU Prolog.
z
ii
są funkcjami użytkowymi (z
wykonuje rodzaj operacji składania, ale na zestawach trzech sąsiednich elementów zamiast 2;i
transponuje 3 listy n elementów na listę n 3-krotek). Wewnętrznie przechowujemy komórkę jako , gdzie x wynosi 1 dla kopalni, a 0 dla kopalni, a y jest liczbą sąsiednich kopalń; wyraża to ograniczenie na tablicy. dotyczy każdego rzędu planszy; i dlatego sprawdza, czy jest to ważna tablica.x/y
c
r
c
z(r,M)
M
Niestety format wejściowy wymagany do bezpośredniego działania tej funkcji jest nieuzasadniony, więc musiałem także dołączyć parser (który prawdopodobnie odpowiada za więcej kodu niż rzeczywisty silnik reguł i przez większość czasu spędzanego na debugowaniu; silnik reguł Saper właściwie działał za pierwszym razem, ale parser był pełen myśli).
q
konwertuje pojedynczą komórkę między kodem znaków a naszym formatem. konwertuje jedną linię planszy (pozostawiając jedną komórkę, o której wiadomo, że nie jest kopalnią, ale z nieznaną liczbą sąsiadujących min, na każdej krawędzi linii jako granicę);x/y
l
p
konwertuje całą planszę (w tym dolną ramkę, ale wyłączając górną). Wszystkie te funkcje można uruchamiać do przodu lub do tyłu, dzięki czemu można zarówno analizować, jak i ładnie drukować tablicę. (Jest trochę denerwujące poruszanie się z trzecim argumentem dop
, który określa szerokość tablicy; dzieje się tak, ponieważ Prolog nie ma typu matrycy, a jeśli nie ograniczę płytki do prostokątności, program przejdzie do nieskończona pętla próbująca stopniowo poszerzać granice wokół planszy.)m
jest główną funkcją rozwiązywania Saperów. Analizuje ciąg wejściowy, generując tablicę z poprawną ramką (poprzez użycie rekurencyjnego przypadkup
do konwersji większości tablicy, a następnie wywołanie skrzynki bazowej bezpośrednio w celu wygenerowania górnej granicy, która ma taką samą strukturę jak dolna granica). Potem dzwoniz(r,[R|M])
do uruchomienia silnika reguł Saper, który (przy tym wzorcu wywołań) staje się generatorem generującym tylko prawidłowe tablice. W tym momencie tablica jest nadal wyrażana jako zestaw ograniczeń, co jest dla nas potencjalnie niezręczne; moglibyśmy mieć jeden zestaw ograniczeń, które mogłyby reprezentować więcej niż jedną tablicę. Ponadto nie określono jeszcze nigdzie, że każdy kwadrat zawiera co najwyżej jedną kopalnię. W związku z tym musimy wyraźnie „zwinąć kształt fali” każdego kwadratu, wymagając, aby była to konkretnie (pojedyncza) kopalnia lub kopalnia, a najłatwiejszym sposobem na to jest przepuszczenie go przez parser do tyłu (var(V)
naq(63,V)
obudowa jest zaprojektowana w taki sposób, aby zapobiec?
przesuwaniu się obudowy do tyłu, a zatem odłożenie płyty wymusza jej pełną znajomość). Na koniec zwracamy przeanalizowaną tablicęm
;m
w ten sposób staje się generatorem, który bierze częściowo nieznaną płytkę i generuje wszystkie znane tablice z nią zgodne.To naprawdę wystarczy, aby rozwiązać Saper, ale pytanie wyraźnie dotyczy sprawdzenia, czy istnieje dokładnie jedno rozwiązanie, zamiast znalezienia wszystkich rozwiązań. Jako taki napisałem dodatkowy predykat,
s
który po prostu przekształca generatorm
w zestaw, a następnie zapewnia, że zestaw ma dokładnie jeden element. Oznacza to, żes
zwróci prawdę (yes
), jeśli rzeczywiście istnieje dokładnie jedno rozwiązanie, lub falsey (no
), jeśli istnieje więcej niż jedno lub mniej niż jedno.d
nie jest częścią rozwiązania i nie jest uwzględniony w bajcie; jest to funkcja służąca do drukowania listy ciągów, tak jakby to była matryca, co umożliwia sprawdzenie wygenerowanych kartm
(domyślnie GNU Prolog drukuje ciągi jako listę kodów ASCII, ponieważ traktuje oba synonimy; ten format jest dość trudny do odczytania). Jest przydatny podczas testowania lub jeśli chcesz używać gom
jako praktycznego solvera Saperów (ponieważ używa solvera ograniczeń, jest bardzo wydajny).źródło
Haskell,
193169168 bajtówPrzykład użycia:
g "1121 1??* 12?* 0122"
->True
.Jak to działa: make lista wszystkich możliwych desek z
?
wyrazami albo*
albo!
(!
środki ignorować później). Odbywa się to poprzezmapM c
, ale zanim dodamy i dodamy kilka spacji do ciągu wejściowego, aby nasze indeksowanie nie było poza zakresem. Dla każdej takiej tablicy sprawdź, czy jest to poprawna tablica, zapętlając wszystkie elementy (indeksj
), a jeśli jest liczbą (d>'/'
) również nad sąsiadami (indeksn
,m
), policz*
i porównaj z liczbą. Na koniec sprawdź długość listy prawidłowych tablic.źródło
Mathematica
214192190180176174168165 bajtówZawiera U + F4A1 (użytek prywatny). Ta nienazwana funkcja znajduje wszystkie możliwe kombinacje dla
"?"
(tj. Zastępuje wszystkie"?"
s"*"
lub0
) i sprawdza, czy istnieje tylko jedno prawidłowe rozwiązanie.Wyjaśnienie
Ustaw
b
na"*"
.Ustaw
q
ciąg"?"
. Sprawdź, czy jest"?"
na wejściu.Jeśli
True
...Zamień pierwsze wystąpienie
q
(="?"
) nab
(="*"
) lub0
(tj. Dwa wyjścia) i zastosuj ponownie całą funkcję.Jeśli
False
...Wypełnij wejście jedną warstwą
0
.Podziel dane wejściowe na macierze 3 x 3 z przesunięciem 1. Dla każdej partycji zastosuj funkcję taką, że jeśli środkowa wartość to
b
(="*"
), to wyjście tob
(="*"
), a jeśli środkowa wartość nie jestb
(="*"
), wyjście to liczbab
(="*"
) na wejściu. Ten krok ponownie ocenia wszystkie komórki liczbowe.Ze wszystkich wyników znajdź te, które pasują do danych wejściowych
Sprawdź, czy wejście ma długość 1.
źródło
Perl, 215 bajtów
213 bajtów kodu +
-p0
flagi (2 bajty).Ideą kodu jest przetestowanie każdej możliwości i sprawdzenie, czy istnieje jedna i tylko jedna, która prowadzi do w pełni wypełnionej planszy, która jest ważna.
Bardziej czytelny kod wygląda następująco:
O wyrażeniu regularnym w środku:
Pamiętaj, że
[^V]
oznacza „dowolny znak, w tym \ n”.Pomysł jest następujący: 3 znaki na linii, następnie 3 na następnej (ze
$i
środkiem), a następnie 3 na następnej. te 3 grupy 3 liczb są wyrównane dzięki,[^V]{$c}
a liczba, którą jesteśmy zainteresowani, znajduje się pośrodku.A następnie
"$1$2$3"=~y%*%%
liczy liczbę*
(bomb) wśród tych 9 znaków: jeśli jest różny od$i
, dodajemy pusty ciąg do dopasowania (""
=> natychmiastowe dopasowanie, wyrażenie regularne zwraca wartość true), w przeciwnym razie wymuszamy niepowodzenie, próbując dopasowaćR
( który nie może być w ciągu).Jeśli regex meczów, następnie płyta nie jest ważna, więc ruszyliśmy
$r
do0
z$r&=!/.../
.I dlatego dodajemy trochę
A
wszędzie wokół każdej linii: więc nie musimy się martwić o przypadki liczb na krawędziach, które znajdują się w pobliżu krawędzi planszy: będą mieliA
jak sąsiedzi, którzy nie są kopalniami (oczywiście z grubsza każdy char może mieć pracę, WybrałemA
).Możesz uruchomić program z wiersza poleceń w następujący sposób:
Złożoność nie może być najgorsza:
O(m*9^n)
tutajn
jest liczba?
na planszy im
liczba komórek na planszy (nie licząc złożoności wyrażenia regularnego w środku, co jest prawdopodobnie dość złe). Na mojej maszynie działa dość szybko dla maksymalnie 4?
i zaczyna być wolniejszy 5, zajmuje 6 minut dla 6, a ja nie próbowałem większych liczb.źródło
JavaScript (ES6), 221
229Jeśli oczekuje się, że wszystkie dane wejściowe będą poprawne - czyli z co najmniej 1 rozwiązaniem - to mogę zapisać zmianę bajtu zas==1
pomocąs<2
Mniej golfa
Test
źródło
JavaScript (Node.js) , 167 bajtów
Wypróbuj online!
Chociaż op mówi „dane wejściowe zawsze będą prostokątne, mają co najmniej jedno rozwiązanie”, fałszywa próbka 3 nie pasuje, więc wciąż potrzebuję 1 rozwiązania, a nie <2 rozwiązania
źródło