Mayınları temizleyebilir miyim?


29

Mayın Tarlası , hangi karolara tıklayıp hangi karoların "mayın" olduğunu keşfetmeniz gereken popüler bir bulmaca oyunudur. Bunun yerine, bitişik mayın sayısını ortaya çıkarmak için yakındaki karolara tıklayın. Oyunun bir dezavantajı, birden fazla geçerli cevabın bulunduğu bir senaryoda son bulmanın mümkün olmasıdır ve yalnızca tahmin edebilirsiniz. Örneğin, aşağıdaki tahtayı alın:

1110
2*31
3*??
2*4?
112?

Bu formatta, bir sayı bitişik mayın sayısını, *bir bilinen bir mayını ve bir "?" Yi temsil eder. potansiyel bir mayını temsil eder. Bu bilmecenin talihsiz hali, dört farklı ve geçerli potansiyel çözüm olduğudur:

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*

Bu, tahtanın çözülemez olduğu anlamına gelir . İşte çözülebilir bir tahta örneği :

1121
1??*
12?*
0122

Bu kart çözülebilir çünkü yalnızca bir geçerli geçerli çözüm var:

1121
1*4*
12**
0122

Göreviniz, geçerli bir mayın tarama tahtasına giren ve çözülebilir olup olmadığını belirleyen bir program veya işlev yazmaktır. "Geçerli mayın tarama gemisi panosu" ile, girişin her zaman dikdörtgen olacağını, en az bir çözüme sahip olduğunu ve geçersiz karakterler içermediğini kastediyorum.

Girişiniz bir karakter dizisi, bir dizge dizisi, yeni satırlar içeren bir dize vs. Performans konusunda son derece endişeli değilim, ancak çözümünüz teorik olarak herhangi bir boyut girişi için çalışmalıdır.

Her zaman olduğu gibi standart boşluklar uygulanır ve bayt cinsinden en kısa çözüm kazanır!

Örnekler:

Aşağıdaki örneklerin tümü çözülebilir:

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

Aşağıdaki örneklerin tümü çözülemez:

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

Tahtanın en az bir hücre ile dikdörtgen olduğunu varsayabilir miyiz? Ayrıca, girişin her zaman en az bir çözümü kabul edeceğini varsayabilir miyiz? (Örneğin 2?, hiçbir çözümü yoktur, bu gerçek bir Mayın Tarlası oyunundan gelemeyeceği anlamına gelir. Bu nedenle "Mayın Tarlası panosu" olarak kabul edilmez ... evet?)
mathmandan

2
MineSweeper'da burada eksik olan ek bir bilgiye sahip olmanızın hiçbir önemi yoktur: mayın sayısı.
edc65

@ mathmandan Evet, giriş her zaman en az bir hücre ve en az bir geçerli çözüm ile dikdörtgen olacaktır.
DJMcMayhem

Yanıtlar:


20

GNU Prolog, 493 bayt

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),[_]).

Sınama için yararlı olabilecek bir ekstra tahmin (gönderimin bir parçası değil):

d([]).
d([H|T]):-format("~s~n",[H]),d(T).

Prolog kesinlikle bu görevi pratik bakış açısıyla çözmek için doğru dildir. Bu program hemen hemen Mayın Tarlası kurallarını belirtir ve GNU Prolog'un kısıtlayıcı çözücüsünün sorunu oradan çözmesini sağlar.

zve iişlevler: ( zyapar işlem kat benzeri ancak üç bitişik elemanların yerine 2 setlerinde bir tür; iaktarır 3 listeleri , n bir liste halinde elemanları , n 3-tuple). Dahili olarak bir hücreyi , x için bir mayın için 1, bir madenin için 0 olmadığı ve y'nin bitişik mayın sayısı olduğu; tahtadaki bu kısıtı ifade eder. yönetim kurulunun her satırı için geçerlidir ; ve böylece geçerli bir tahta olup olmadığını kontrol eder .x/ycrcz(r,M)M

Ne yazık ki, bu çalışmayı doğrudan yapmak için gereken giriş biçimi makul değildir, bu yüzden bir çözümleyici de (muhtemelen gerçek kural motorundan daha fazla kod hesaba katar ve hata ayıklamak için harcanan zamanın çoğunu; Mayın Tarlası kuralları motoru oldukça işe yaradı) ilk kez, ancak çözümleyici thinkos doluydu). qTek bir hücreyi karakter koduyla formatımız arasında dönüştürür. panelin bir çizgisini dönüştürür (bir maden olmadığı, ancak bilinmeyen sayıda komşu mayın ile, çizginin her kenarına kenarlık olarak bırakılan bir hücreyi terk eder);x/ylptahtanın tamamını dönüştürür (alt kenarlık dahil, ancak üstteki hariç). Bu işlevlerin tümü ileriye veya geriye doğru çalıştırılabilir, böylece tahtayı hem ayrıştırıp hem de güzel şekilde yazdırabilirsiniz. ( pTahtanın genişliğini belirten üçüncü argümanla etrafında sinir bozucu bazı kıpırdama var ; bunun nedeni Prolog'un bir matris türüne sahip olmamasıdır ve tahtayı dikdörtgen biçiminde kısıtlamıyorsam, programın içine girer. tahta etrafında giderek daha geniş sınırları deneyen sonsuz bir döngü.)

mAna Mayın Tarlası çözme işlevidir. Giriş dizesini doğru bir kenarlıklı bir tahta oluşturarak ayrıştırır (özyinelemeli ptahtanın çoğunu dönüştürmek için özyinelemeli halini kullanarak , sonra da alt kenarlık ile aynı yapıya sahip üst sınırı oluşturmak için taban kasasını doğrudan çağırır). Sonra çağırırz(r,[R|M])Mayın Tarlası kuralları motorunu çalıştırmak için, (bu çağrı modeliyle) yalnızca geçerli panolar üreten bir jeneratör olur. Bu noktada, kurul hala bizim için potansiyel olarak zor olan bir dizi kısıt olarak ifade edilmektedir; belki birden fazla tahtayı temsil edebilecek tek bir kısıtlama kümesine sahip olabiliriz. Ek olarak, her karenin en fazla bir mayın içerdiği herhangi bir yeri henüz belirtmedik. Bu nedenle, açıkça her bir karenin dalga biçimini "daraltmamız" gerekir, bunun özel olarak (tek) bir mayın veya bir mayın olmamasını gerektirir ve bunu yapmanın en kolay yolu, onu ayrıştırıcıdan geriye doğru çalıştırmaktır var(V). q(63,V)kasa, ?kasanın geriye doğru hareket etmesini önleyecek şekilde tasarlanmıştır ve bu nedenle tahtanın devre dışı bırakılması onu tamamen bilinmeye zorlar). Son olarak, ayrıştırılmış tahtayım; mBöylece, kısmen bilinmeyen bir tahta alan ve onunla tutarlı olarak bilinen tüm tahtaları üreten bir jeneratör haline gelir.

Bu Mayın Tarlası'nı çözmek için gerçekten yeterli, ancak soru açıkça tüm çözümleri bulmak yerine tam olarak bir çözüm olup olmadığını kontrol etmenizi istiyor. Dolayısıyla s, jeneratörü mbir kümeye dönüştüren fazladan bir yazı yazdım ve kümenin tam olarak bir elemente sahip olduğunu iddia ediyorum . Bu , tam olarak bir çözüm varsa struthy ( yes) no, birden fazla veya birden fazla varsa falsey ( ) anlamına gelir .

dçözümün bir parçası değildir ve bayt sayısında bulunmaz; bu, bir matrismiş gibi dizelerin bir listesini yazdırmak için bir işlevdir; bu, tarafından oluşturulan panoları incelemeyi mümkün kılar m(GNU Prolog, dizeleri ASCII kodlarının bir listesi olarak yazdırır; okumak oldukça zordur). Test sırasında veya mpratik bir Mayın Tarlası çözücüsü olarak kullanmak istiyorsanız (kısıtlayıcı bir çözücü kullandığından, oldukça verimlidir) faydalıdır .


11

Haskell, 193 169 168 bayt

c '?'="*!"
c x=[x]
g x|t<-x>>" ",w<-length(words x!!0)+1=1==sum[1|p<-mapM c$t++x++t,and[sum[1|m<-[-1..1],n<-[j-w,j,j+w],p!!(m+n)=='*']==read[d]|(j,d)<-zip[0..]p,d>'/']]

Kullanım örneği: g "1121 1??* 12?* 0122"-> True.

Nasıl çalışır: ile tüm olası kurullarının yapmak listesi ?ile değiştirilir ya *ya !( !vasıta sonradan görmezden). Bu işlem yapılır mapM c, ancak indekslememiz aralık dışında olmayacak şekilde girdi dizisine bir sürü boşluk hazırlayıp eklemeden önce. Her tür tahta kontrol için tüm unsurları (endeks üzerinde döngü tarafından geçerli bir tahta ise j) ve bir sayı (eğer d>'/'ayrıca komşuları (endeks üzerinden) n, m), saymak *ve sayıları karşılaştırıldığında. Sonunda geçerli panoların listesinin uzunluğunu kontrol edin.


7

Mathematica, 214 192 190 180 176 174 168 165 bayt

0&/@Cases[b="*";If[!FreeQ[#,q="?"],(x#0@MapAt[x&,#,#&@@#~Position~q])/@{b,0},BlockMap[If[#[[2,2]]==b,b,Count[#,b,2]]&,#~ArrayPad~1,{3,3},1]]&@#,#/.q->_,All]=={0}&

U + F4A1 (Özel kullanım) içerir. Bu isimlendirilmemiş işlev, tüm olası kombinasyonları bulur "?"(yani tüm "?"s'leri "*"veya ile değiştirerek 0) ve yalnızca bir geçerli çözüm olup olmadığını kontrol eder.

açıklama

b="*";

Set biçin "*".

!FreeQ[#,q="?"]

Set qdizeye "?". "?"Girişte olup olmadığını kontrol edin .

If[ ..., (x#0 ... ,0}, BlockMap[ ... ]]

Eğer True...

(x#0@MapAt[x&,#,#&@@#~Position~q])/@{b,0}

q(= "?") ' Nin ilk oluşumunu b(= "*") veya 0(yani iki çıkış) ile değiştirin ve tüm işlevi tekrar uygulayın.


Eğer False...

#~ArrayPad~1

Girdiyi bir katmanla doldurun 0.

BlockMap[If[#[[2,2]]==b,b,Count[#,b,2]]&, ... ,{3,3},1]

Giriş, ofset 1 ile 3 x 3 matrislere bölünür. Her bölüm için, orta değer b(= "*") ise, çıktı b(= "*"), orta değer b(= "*") değilse , çıktı b(="*" girişteki ) . Bu adım, tüm sayı hücrelerini yeniden değerlendirir.


Cases[ ... ,#/.q->_,All]

Tüm sonuçlardan, girdiyle eşleşen sonuçları bulun.

0&/@ ... =={0}

Girişin uzunluk 1 olup olmadığını kontrol edin.


7

Perl, 215 bayt

213 bayt kod + -p0işaretler (2 bayt).

/.*/;$c="@+";$_=A x$c."
$_".A x$c;s/^|$/A/mg;sub t{my($_)=@_;if(/\?/){for$i(0..8,"*"){t(s/\?/$i/r)}}else{$r=1;for$i(/\d/g){$r&=!/(...)[^V]{$c}(.$i.)[^V]{$c}(...)(??{"$1$2$3"=~y%*%%!=$i?"":R})/}$e+=$r}}t$_;$_=$e==1

Kodun amacı, her olasılığı test etmek ve geçerli olan tam dolu bir tahtaya yol açan tek bir tane olup olmadığını görmek.

Daha okunabilir, kod gibi görünüyor:

/.*/ ; $ c = "@ +" ; # bir çizginin boyutunu sayar. 
$ _ = A x $ c . "\ n $ _" . A $ c ; # başında bir "A" satırı ve sonunda bir tane daha ekleyin. 
s / ^ | $ / A / mg ; # Her satırın başına ve sonuna bir "A" ekleyin.                     

# Alt problemi çözen fonksiyon
 t { my $ _ = pop ; # Parametreyi alın, $ _ içinde saklayın (regex'in varsayılan argümanı). if ( / \? / ) { # başka bilinmeyen karakter varsa. $ i # için bilinmeyen karakter yok, o zaman burada tahtanın geçerli olup olmadığını kontrol ederiz 
        $ r = 1 ; Sonunda r == 1, daha sonra tahta geçerli olup olmadığını #, aksi takdirde değil için $ i ( / \ d / 
     
        
         ( 0. . 8 , "*" ) { # her olasılık deneyin 
            t ( s / \? / $ i / r ) birinci bilinmeyen karakter değiştirilmiştir # reccursive çağrı } } else {    
        
       
         g ) { kartı her sayıda mevcut arasında # aşağıdaki normal ifade eder bir sayı çevrili bulunmaktadır halinde ya # çok fazla ya da çok az mayınla. # (nasıl çalışır: sihir!) 
         $ r & =! /(...)[^V]{$c}(.$i.)[^V]{$c}(...)(??{"$1$2$3"=~y%*%%! = $ i? "": R}) / } 
        $ e + = $ r # Geçerli kartların sayısını arttır. } } 
t $ _ ;  
            
            
             
        
    
 # Önceki işlevi çağırın 
$ _ = $ e == 1 # Yalnızca bir geçerli pano olup olmadığını kontrol eder (-p bayrağı sayesinde $ _ dolaylı olarak yazdırılır). 

Ortadaki regex hakkında:

/(...)[^V]{$c}(.$i.)[^V]{$c}(...)(??{"$1$2$3"=~y%*%%!=$i?"":R})/

Not [^V]sadece "Herhangi dahil karakteri, \ n" anlamına gelir.
Yani fikir şu: Bir satırda 3 karakter, sonra 3 kişi ( $iortada), sonra 3 kişi. Bu 3 sayıdan oluşan 3 grup aynı hizada, [^V]{$c}çünkü ilgimizi çeken sayı ortada.
Ve sonra, bu 9 karakter arasındaki (bomba) "$1$2$3"=~y%*%%sayısını sayar *: eğer ondan farklıysa $i, eşleşecek boş bir dize ekleriz ( ""=> anlık eşleme, regex true döndürür), aksi halde eşleştirmeyi denemeyi zorlarız R( hangi dizede olamaz).
Belirlediğimiz böylece Normal ifade eşleşirse, o zaman tahta, geçerli değil $riçin 0birlikte $r&=!/.../.
Ve bu yüzden biraz ekledikAher çizginin her yerinde: bu nedenle, tahtanın kenarlarına yakın sayı sayılarının kenarları için endişe etmemiz gerekmiyor: Akomşuları olacak, bunlar mayın olmayan (elbette, herhangi bir karakterin çalışabileceği) seçtiğim A).

Programı aşağıdaki gibi komut satırından çalıştırabilirsiniz:

perl -p0E '/.*/;$c="@+";$_=A x$c."\n$_".A x$c;s/^|$/A/mg;sub t{my($_)=@_;if(/\?/){for$i(0..8,"*"){t(s/\?/$i/r)}}else{$r=1;for$i(/\d/g){$r&=!/(...)[^V]{$c}(.$i.)[^V]{$c}(...)(??{"$1$2$3"=~y%*%%!=$i?"":R})/}$e+=$r}}t$_;$_=$e==1' <<< "1121
1??*
12?*
0122"

Karmaşıklığı kötü olamazdı: bu kadar O(m*9^n)nerede nsayısıdır ?gemide ve m(muhtemelen oldukça kötü olduğu ortasında, regex karmaşıklığını saymadan) gemide hücrelerin sayısıdır. Makinemde 4'e kadar hızlı çalışıyor ?ve 5 yaşına kadar yavaşlıyor, 6'da birkaç dakika sürüyor ve daha büyük rakamlar için denemedim.


3

JavaScript (ES6), 221 229

g=>(a=>{for(s=i=1;~i;g.replace(x,c=>a[j++],z=j=0).replace(/\d/g,(c,p,g)=>([o=g.search`
`,-o,++o,-o,++o,-o,1,-1].map(d=>c-=g[p+d]=='*'),z|=c)),s-=!z)for(i=a.length;a[--i]='*?'[+(c=a[i]<'?')],c;);})(g.match(x=/\?/g)||[])|!s

Tüm giriş geçerli olması beklenen ise - en azından 1 solüsyon ile - o zaman ben değişen bir bayt kaydedebilirsiniz s==1iles<2

Daha az golf oynadı

g=>{
  a = g.match(/\?/g) || []; // array of '?' in a
  s = 1; // counter of solutions
  for(i=0; ~i;) // loop to find all configurations of ? and *
  {
    // get next configuration
    for(i = a.length; a[--i] = '*?'[+( c = a[i] < '?')], c; );
    z = 0; // init at 0, must stay 0 if all cells count is ok
    g
    .replace(/\?/g,c=>a[j++],j=0) // put ? and * at right places
    .replace(/\d/g,(c,p,g)=>(
       // look for mines in all 8 directions
       // for each mine decrease c
       // if c ends at 0, then the count is ok
       [o=g.search`\n`,-o,++o,-o,++o,-o,1,-1].map(d=>c-=g[p+d]=='*'),
       z|=c // z stays at 0 if count is ok
    )) // check neighbour count
    s-=!z // if count ok for all cells, decrement number of solutions
  }
  return s==0 // true if exactly one solution found
}

Ölçek

F=
g=>(a=>{for(s=i=1;~i;g.replace(x,c=>a[j++],z=j=0).replace(/\d/g,(c,p,g)=>([o=g.search`
`,-o,++o,-o,++o,-o,1,-1].map(d=>c-=g[p+d]=='*'),z|=c)),s-=!z)for(i=a.length;a[--i]='*?'[+(c=a[i]<'?')],c;);})(g.match(x=/\?/g)||[])|!s

out=x=>O.textContent+=x+'\n'

Solvable=['1121\n1??*\n12?*\n0122'
,'1110\n1???\n1110\n0000'
,'1110\n3???\n??20\n*310'
,'****\n****\n****\n****'
,'0000\n0000\n0000\n0000'
,'1100\n*100\n2321\n??*2\n13*2\n1221\n1*10\n1110'
,'1121\n2*??\n2*31\n2220\n1*10']
Unsolvable=['1110\n2*31\n3*??\n2*4?\n112?'
,'01??11*211\n12??2323*1\n1*33*2*210\n12?2122321\n13?3101**1\n1***101221'
,'1***\n3*52\n2*31\n12??\n02??\n01??'
,'00000111\n000012*1\n00001*21\n22101110\n**100111\n?31123*1\n?311**31\n**113*20']
out('Solvable')
Solvable.forEach(t=>out(t+'\n'+F(t)+'\n'))
out('Unsolvable')
Unsolvable.forEach(t=>out(t+'\n'+F(t)+'\n'))
<pre id=O></pre>


Op bu baytı golf oynayabileceğini söyledi.
Yıkılabilir Limon

@DrructibleWatermelon teşekkürler, hepsini gözden geçirdim ve daha fazla bayt kurtardım
edc65

0

JavaScript (Node.js) , 167 bayt

s=>g=(r=c='',[p,...q]=s,w)=>w?0:p?(g(r+0,q,p=='*')+g(r+1,q,1/p),c==1):c-=-![...s].some((p,i)=>p>' '&&[-1,1,-q,-1-q,-2-q,q,q+1,q+2].map(j=>p-=~~r[i+j])|p,q=s.search`
`)

Çevrimiçi deneyin!

Her ne kadar op "giriş her zaman dikdörtgen olacak, en az bir çözüme sahip olacak" demesine rağmen, yanlış örnek 3 eşleşmiyor, bu yüzden hala <2 çözelti değil 1 çözüme ihtiyacım var

s=>(        // p.s. Here "block" can also mean \n
  c=0,          // possible mine count
  g=(           // recursive
    r='',       // mine states
    [p,...q]=s, // known info to check possible state for a block
    w           // invert condition, stop if true
  )=>
    w?0:
      p?(       // for each block
        g(r+0,q,p=='*')+   // possibly not bomb if doesn't say so
        g(r+1,q,1/p),      // number/newline can't be bomb
        c==1               // only one bomb
      ):
        c-=-![...s].some(  // no block doesn't satisfy
          (p,i)=>
            p>' '&& // \n don't mean number
                    // other symbols turn into NaN when counting
            [-1,1,-q,-1-q,-2-q,q,q+1,q+2].map(j=>p-=~~r[i+j])
                    // subtract each neighbor, OOB = 0
            |p,     // difference between intended and actual
            q=s.search('\n') // how many blocks in a line
        )
)

"eşleşme yok" yazım hatası gibi görünüyor, bunu düzeltmek 4 çözüm sağlıyor
l4m2
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.