BattleBlock Tiyatro Yapbozunu çözün


13

Oyun BattleBlock Theatre zaman zaman Lights Out genelleştirilmiş bir versiyonu olan bir bulmaca içerir . Her biri çubuklarla birlikte 1 ile 4 arasında bir seviye olduğunu belirten üç bitişik bloğunuz var, örneğin:

|
||||
||

Bir bloğa dokunursanız, bu blok ve herhangi bir bitişik blok seviyesini yükseltir (4'ten 1'e geri sarma). Bulmaca, her üç blok da aynı seviyeyi gösterdiğinde çözülür (hangi seviyenin önemi yoktur). Bloklara dokunma sırası önemli olmadığından, her bloğa ne sıklıkta dokunduğuna göre bir çözüm belirtiriz. Yukarıdaki giriş için en uygun çözüm 201:

|    --> || --> |||     |||
||||     |      ||      |||
||       ||     ||  --> |||

Oyun bazı bloklar için tüm konfigürasyonlar çözülemese de, herhangi bir sayıda bloğu çok kolay bir şekilde genelleştirir.

Meydan okuma

Bir dizi blok seviyesi göz önüne alındığında, bulmacayı çözmek için her bloğa ne sıklıkta dokunulması gerektiğini döndürün. Örneğin yukarıdaki örnek sonuç olarak verilebilir 142ve 201sonuç olarak verilebilir . Çözüm yoksa, tüm olası çözümlerden, örneğin boş bir dizgiden ayırt edilebilen, tutarlı bir çıktı alın -1.

Bir işlev veya program yazabilir, STDIN, komut satırı argümanı veya işlev bağımsız değişkeni, uygun herhangi bir liste veya dize biçiminde girdi alabilir ve benzer şekilde bir dönüş değeri aracılığıyla veya STDOUT'a yazdırarak çıktı alabilirsiniz.

Kodunuz, makul bir makinede bir dakika içinde tüm test senaryoları için doğru sonuçları döndürmelidir. (Bu tamamen katı bir sınır değildir, bu nedenle çözümünüz bir dakika on saniye sürüyorsa, bu iyi, ancak 3 dakika sürüyorsa, öyle değil. İyi bir algoritma bunları saniyeler içinde kolayca çözecektir.)

Bu kod golf, yani en kısa cevap (bayt cinsinden) kazanır.

Örnekler

Çözümler benzersiz değildir, bu nedenle farklı sonuçlar alabilirsiniz.

Input                          Output

1                              0
11                             00
12                             No solution
142                            201
434                            101
222                            000
4113                           0230
32444                          No solution
23432                          10301
421232                         212301
3442223221221422412334         0330130000130202221111
22231244334432131322442        No solution
111111111111111111111222       000000000000000000000030
111111111111111111111234       100100100100100100100133
412224131444114441432434       113013201011001101012133

Bildiğim kadarıyla, blok sayısının 0 mod 3 veya 1 mod 3 olduğu her giriş için tam olarak 4 çözüm var ve 2 mod 3 olduğu 0 veya 16 çözüm var.


Optimal bir çözüm üretmeniz mi gerekiyor?
xnor

@xnor Hayır, bilmiyorsun.
Martin Ender

Tam olarak bir çözüm yazdırmamız mı gerekiyor yoksa hepsini de yazdırabilir miyiz?
Jakube

@ Jakube Kesinlikle bir lütfen. Tüm / en uygun çözüm için bir bonus eklemeliydim, ama bunu yeterince erken düşünmedim, bu yüzden herhangi bir (tek) çözüm.
Martin Ender

Yanıtlar:


10

Python 2, 115 bayt

n=input()
for F in range(4):
 t=[F];b=0;exec"x=(-n[b]-sum(t[-2:]))%4;t+=x,;b+=1;"*len(n)
 if x<1:print t[:-1];break

Bu, Martin ile olan sorunu tartışırken yazdığım programın golf versiyonudur.

Giriş STDIN üzerinden bir listedir. Çıktı, bir çözüm varsa bulunan son çözümü temsil eden veya yoksa sıfır bulunan bir listedir. Örneğin:

>>>
[1, 4, 2]
[2, 1, 1]
>>>
[1, 2]
0
>>>
map(int,"3442223221221422412334")
[2, 3, 3, 2, 1, 3, 2, 0, 0, 2, 1, 3, 2, 2, 0, 0, 2, 2, 3, 1, 1, 3]

Pyth, 32 29 bayt

V4J]NVQaJ%_+s>J_2@QN4)I!eJPJB

Zorunlu liman. 3 byte tasarruf için @ Jakube'a teşekkürler.

Giriş yöntemi yukarıdaki ile aynıdır, çevrimiçi deneyin .


Açıklama (uzun ve mantık dolu!)

İlk olarak, iki temel gözlem:

  • Gözlem 1: Bloklara hangi sırayı dokunduğunuz önemli değil

  • Gözlem 2: Bir bloğa 4 kez dokunursanız, bir kez dokunmakla eşdeğerdir

Başka bir deyişle, bir çözüm varsa, dokunma sayısının 0 ile 3 arasında olduğu bir çözüm vardır.

Modulo 4 çok güzel olduğundan, bunu bloklarla da yapalım. Bu açıklamanın geri kalanı için, 0 blok seviyesi 4 blok seviyesine eşdeğerdir.

Şimdi a[k]mevcut blok seviyesi kve bir çözümde x[k]bloğa dokunma sayımız olduğunu gösterelim k. Ayrıca ntoplam blok sayısı olsun . @ Jakube'un belirttiği gibi, bir çözüm:

  a[0]   + x[0] + x[1]
= a[1]   + x[0] + x[1] + x[2]
= a[2]          + x[1] + x[2] + x[3]
= a[3]                 + x[2] + x[3] + x[4]
...
= a[n-1]                                     ...  + x[n-2] + x[n-1] + x[n]
= a[n]                                       ...           + x[n-1] + x[n]
= C

Cson blok nerede tüm bloklar biter, 0 ile 3 arasında (seviye 4'e seviye 0 olarak davrandığımızı unutmayın) ve yukarıdaki tüm denklemler gerçekten uyum modüloğudur 4.

Şimdi işin eğlenceli kısmı:

  • Gözlem 3 : Bir çözüm mevcutsa, herhangi bir son blok seviyesi için bir çözüm vardır 0 <= C <= 3.

Modulo 3 bloklarının sayısına dayalı üç durum vardır. Her biri için açıklama aynıdır - herhangi bir sayıda blok için, her birine bir kez dokunduğunuzda tüm blok seviyelerini artıran bir blok alt kümesi vardır. tam olarak 1.

0 mod 3 (touch every third block starting from the second):
    .X. / .X. / .X.

1 mod 3 (touch every third block starting from the first):
    X. / .X. / .X. / .X

2 mod 3 (touch every third block starting from either the first or second):
    X. / .X. / .X. / .X.
    .X. / .X. / .X. / .X

Bu 4 çözüm bulunmaktadır açıklıyor 0 mod 3ve 1 mod 3ve genellikle 16 çözümler 2 mod 3. Zaten bir çözümünüz varsa, yukarıdaki bloklara dokunmak, daha yüksek bir blok seviyesinde (etrafı sararak) sonuçlanan başka bir çözüm sağlar.

Peki bu ne anlama geliyor? İstediğimiz son blok seviyesini seçebiliriz C! Seçelim C = 0, çünkü bu baytlarda tasarruf sağlar.

Şimdi denklemlerimiz:

0 = a[0] + x[0] + x[1]
0 = a[1] + x[0] + x[1] + x[2]
0 = a[2] + x[1] + x[2] + x[3]
0 = a[3] + x[2] + x[3] + x[4]
...
0 = a[n-1] + x[n-2] + x[n-1] + x[n]
0 = a[n] + x[n-1] + x[n]

Ve yeniden düzenleyin:

x[1] = -a[0] - x[0]
x[2] = -a[1] - x[0] - x[1]
x[3] = -a[2] - x[1] - x[2]
x[4] = -a[3] - x[2] - x[3]
...
x[n] = a[n-1] - x[n-2] - x[n-1]
x[n] = a[n] - x[n-1]

Gördüğümüz şey, eğer varsa x[0], sonuncusu hariç tüm denklemleri birbirimizi bulmak için kullanabiliriz x[k]. Son denklem kontrol etmemiz gereken ek bir durumdur.

Bu bize bir algoritma verir:

  • İçin tüm değerleri deneyin x[0]
  • Diğer denklemleri çözmek için yukarıdaki denklemleri kullanın x[k]
  • Son durumun karşılanıp karşılanmadığını kontrol edin. Eğer öyleyse, çözümü kaydedin.

Bu yukarıdaki çözümü verir.

Öyleyse neden bazen çözüm bulamıyoruz 2 mod 3? Şimdi bu iki desene tekrar bakalım:

X. / .X. / .X. / .X.
.X. / .X. / .X. / .X

Şimdi bu pozisyonlardaki denklemleri düşünün, yani ilkinde:

0 = a[0] + x[0] + x[1]
0 = a[3] + x[2] + x[3] + x[4]
0 = a[6] + x[5] + x[6] + x[7]
0 = a[9] + x[8] + x[9] + x[10]

Onları ekleyin:

0 = (a[0] + a[3] + a[6] + a[9]) + (x[0] + x[1] + ... + x[9] + x[10])

İkincisi için:

0 = a[1] + x[0] + x[1] + x[2]
0 = a[4] + x[3] + x[4] + x[5]
0 = a[7] + x[6] + x[7] + x[8]
0 = a[10] + x[9] + x[10]

Onları tekrar ekleyin:

0 = (a[1] + a[4] + a[7] + a[10]) + (x[0] + x[1] + ... + x[9] + x[10])

Yani eğer eşit değilse (a[1] + a[4] + a[7] + a[10])ve (a[0] + a[3] + a[6] + a[9])eşit değilse, o zaman çözümümüz yoktur. Fakat eğer eşitlerlerse, 16 çözüm elde ederiz. Bu n = 11durum söz konusuydu, ama elbette bu herhangi bir sayıya genelleme yapıyor 2 mod 3- ikinciden başlayarak her üçüncü öğenin toplamını alın ve birinciden başlayarak her üçüncü öğenin toplamıyla karşılaştırın.

Son olarak, x[0]tüm olasılıkları denemek yerine ne olması gerektiğini bulmak mümkün müdür ? Sonuçta, hedef seviyemizi C0 ile sınırladığımız için x[0], 0 mod 3veya 1 mod 3durumda (as 4 solutions / 4 final levels = 1 solution for a specific final level) bir çözüm veren sadece bir tane var .

Cevap Evet! Bunu aşağıdakiler için yapabiliriz 0 mod 3:

 .X..X
.X..X.

Bunun anlamı:

0 = a[2] + x[1] + x[2] + x[3]   -> 0 = (a[2] + a[5]) + (x[1] + ... + x[5])
0 = a[5] + x[4] + x[5]          /


0 = a[1] + x[0] + x[1] + x[2]   -> 0 = (a[1] + a[4]) + (x[0] + x[1] + ... + x[5])
0 = a[4] + x[3] + x[4] + x[5]   /

Çıkarma verir:

x[1] = (a[2] + a[5]) - (a[1] + a[4])

Benzer şekilde, 1 mod 3bu deseni yapabiliriz:

 .X..X.
X..X..X

Hangi verir:

x[0] = (a[2] + a[5]) - (a[0] + a[3] + a[6])

Bunlar elbette endeksleri 3'lük artışlarla genişleterek genelleme yaparlar.

Çünkü 2 mod 3, her bloğu kapsayan iki altkümemiz olduğu için, aslında herhangi birini seçebiliriz x[0]. Aslında, bu doğrudur x[0], x[1], x[3], x[4], x[6], x[7], ...(her 2 mod 3iki alt kümenin kapsamında olmadığı için temelde uyumlu olmayan herhangi bir indeks ).

Yani x[0]tüm olasılıkları denemek yerine bir seçim yapmamız gerekiyor ...

... ama kötü haber, bunun bayttan (124 bayt) tasarruf etmemesidir:

def f(n):s=[];L=len(n);B=sum(n[~-L%3::3])-sum(n[-~L%3::3]);x=A=0;exec"s+=B%4,;A,B=B,-n[x]-A-B;x+=1;"*L*(L%3<2or B<1);print s

Zeki. Dilimleme yerine son öğeyi açarsanız, 1 Jyerine Hve 2 karakter kullanarak kaydedebilirsiniz PJ. <J_1. V4J]NVQaJ%_+s>J_2@QN4)I!eJPJB
Jakube

@ Jakube Ah teşekkürler. Pop okuduğumda, listeden kaldırırken son öğeyi döndüren Python pop'u olduğunu düşündüm. Şimdi görüyorum ki durum böyle değil.
Sp3000

4

Pyth, 72 76 73 66 39 38 karakter

Ph+f!eTmu+G%+&H@G_3-@QH@QhH4UtQd^UT2]Y

edit 4: Gerçekleştirilen, bu hesaplamalar Q[N]-Q[N+1]+solution[-3]ve Q[-2]-Q[-1]+solution[-3]özdeş. Bu nedenle, çözümü 1 ile aşırı hesaplar ve son girişi 0 olan çözümleri filtrelerim. Sonra son girişi başlatırım. Neyse ki özel vakaların bu yaklaşımla ekstra bir tedaviye ihtiyacı yoktur. -27 karakter

edit 3: FryAmTheEggman'dan bazı golf hileleri uygulama: -7 karakter

değiştir 2: Filtre kullanma, azaltma ve eşleme: -3 karakter

edit 1: İlk versiyonumda, çözüm olmasaydı hiçbir şey basmadım. Buna izin verilmediğini sanmıyorum, bu yüzden +4 karakter.

Tamsayıların bir listesini girdi olarak bekler [1,4,2]ve [2,0,1]varsa boş bir çözüm , aksi takdirde boş bir liste çıkarır [].

Açıklama:

Izin vermek Q5 düzeyleri Ylistesi ve çözüm listesi. Aşağıdaki denklemler tutmak zorundadır:

  Q0 + Y0 + Y1 
= Q1 + Y0 + Y1 + Y2
= Q2      + Y1 + Y2 + Y3
= Q3           + Y2 + Y3 + Y4
= Q4                + Y3 + Y4

Biz herhangi birini kullanmak Dolayısıyla eğer Y0ve Y1biz hesaplayabilir Y2, Y3ve Y4şu şekilde.

Y2 = (Q0 - Q1     ) mod 4
Y3 = (Q1 - Q2 + Y0) mod 4
Y4 = (Q2 - Q3 + Y1) mod 4

Son seviye hariç tüm seviyeler eşittir (çünkü denklemi kullanmadık = Q4 + Y3 + Y4. Bu son seviyenin diğer seviyelere de eşit olup olmadığını kontrol etmek için, sadece (Q3 - Q4 + Y2) mod 4 == 0sol kısmın değer olup olmadığını kontrol edebiliriz . Y5Çözümün 6. bölümünü hesaplarsam, sıfır olup olmadığını kontrol edebilirim.

Benim yaklaşımımda sadece tüm olası başlangıçlar ( [0,0], - [3,3]) üzerinde yineleme ve uzunluk (giriş) -1 daha fazla giriş hesaplamak ve sıfır ile biten tüm çözümleri filtre.

mu+G%+&H@G_3-@QH@QhH4UtQd^UT2   generates all possible solutions

temelde şu:

G = start value           //one of "^UT2", [0,0], [0,1], ..., [9,9]
                          //up to [3,3] would be enough but cost 1 char more
for H in range(len(Q)-1): //"UtQ"
   G+=[(H and G[-3])+(Q(H)-Q(H+1))%4] //"+G%+&H@G_3-@QH@QhH4"
   //H and G[-3] is 0, when H is empty, else G[-3]

o zaman bu olası çözümleri geçerli olanlar için filtreliyorum:

f!eT //only use solutions, which end in 0

Bu çözüm listesine boş bir liste ekliyorum, böylece içinde en az bir öğe var

 +....]Y

ve ilk çözümü alın h, son elemanı açın pve yazdırın

 Ph

Sadece bir blok varsa, bunun da işe yaradığına dikkat edin. Benim yaklaşımımda başlangıç ​​pozisyonunu [0,0] alıyorum ve uzatmıyorum. Son giriş 0 olduğundan çözümü [0] yazdırır.

İkinci özel durum (2 blok) sonuçta o kadar da özel değil. Emin değilim, neden daha önce karmaşıklaştım.


Ben hiçbir şey yazdırmak tek durumda iff hiçbir çözüm yazdırmak için iyi olduğunu düşünüyorum. Yine de onaylamak için @ MartinBüttner almak zorunda
Sp3000

?**lQ]0qhQeQ<lQ3h+f!%-+ePQ@T_3eQ4mu+G]%+&H@G_3-@QH@QhH4UttQd^UT2]Y66 bayttır. Performans biraz etkilendi, ancak yine de benim için <1s'de en büyük test senaryosunu çalıştırıyor. Bazı golf açıklamaları istiyorsanız bana ping; Bu yorumda yeterli alan yok;) Umarım
Pyth'i

+<list><int>ile aynı etkiye sahiptir +<list>]<int>, böylece ilkini kaldırabilirsiniz ]. Ayrıca, çok güzel bir çözüm.
isaacg

@isaacg Aynı şey için de geçerli ~mi? Denediğimde öyle görünmüyordu
Sp3000

@ SP3000 Hemen yerini ~ile a- ~<list>]<int>eşdeğerdir a<list><int>. ~olduğu +=süre aise.append()
isaacg

3

Yakut, 320 313 karakter

m=gets.chop.chars.map{|x|x.to_i-1}
a=m.map{0}
t=->n{m[n]+=1
m[n-1]+=1if n>0
m[n+1]+=1if n<m.size-1
m.map!{|x|x%4}
a[n]=(a[n]+1)%4}
t[0]until m[0]==1
(2...m.size).map{|n|t[n]until m[n-1]==1}
r=0
while m.uniq.size>1&&m[-1]!=1
(0...m.size).each_with_index{|n,i|([1,3,0][i%3]).times{t[n]}}
(r+=1)>5&&exit
end
$><<a*''

Kesinlikle daha fazla golf edilebilir. Çözülemez bulmacalar için hiçbir şey çıktılar.

Ungolfed sürümü:

#!/usr/bin/ruby

nums = gets.chomp.chars.map {|x| x.to_i-1 }
touches = nums.map {0}

# our goal: make all the numbers 1
# utility function
touch = ->n {
    nums[n] += 1
    nums[n-1] += 1 if n > 0
    nums[n+1] += 1 if n < (nums.length-1)
    nums.map! {|x| x % 4 }
    touches[n] = (touches[n] + 1) % 4
}

# first, start with the very first number
touch[0] until nums[0] == 1

# then, go from index 2 to the end to make the previous index right
(2...nums.length).each {|n|
    touch[n] until nums[n-1] == 1
}

iters = 0
if nums.uniq.length != 1
    # I have no idea why this works
    while nums[-1] != 1
        (0...nums.length).each_with_index {|n, i|
            ([1, 3, 0][i % 3]).times { touch[n] }
        }
        if (iters += 1) > 5
            puts -1
            exit
        end
    end
end

puts touches * ''

Tamam, bu eğlenceliydi. Örneklerden birinde gösterildiği gibi, {n}yukarıdaki sayıdaki n "dokunma" ları temsil eden temel algoritma n:

we want each number to be a 1
first make the first number a 1
3442223221221422412334
2}
1242223221221422412334
 {3} now keep "touch"ing until the number to the left is a 1
1131223221221422412334
  {2}
1113423221221422412334
   {2}
1111243221221422412334
... (repeat this procedure)
1111111111111111111110

Burada biraz güldüm. Bunu 111...1110aynı sayı dizisine nasıl dönüştürebilirim? Bu yüzden çözümümü ve doğru çözümü karşılaştırdım (not: "dokunma" sayıları, giriş 1-dizinli, çıkış 0-dizinli olduğu için olması gerekenden daha büyüktür):

3033233103233301320210
0330130000130202221111

Her sayının doğru olandan bir tane olduğunu fark ettim mod 4, bu yüzden onları +s, -s ve =s ile işaretledim :

3033233103233301320210 original program output
+-=+-=+-=+-=+-=+-=+-=+ amount to modify by (+1, -1, or 0 (=))
4334534404534602621511 result (the correct answer)

0330130000130202221111 (the original solution, digits equal to result mod 4)

Ben bazen nihai sonucu olduğunu fark kadar bir süre çalıştı 111...11112ya 11...1113da! Neyse ki, defalarca anlamsız ama işe yarayan büyülü formülü tekrar tekrar uygulamak bunları çözdü.

Yani, işte burada. Anlamlı olmaya başlayan ancak gittikçe daha çirkin bir hileliğe dönüşen bir program. Bence bir kod golf çözümü için oldukça tipik. :)


1
Kodunuzdaki son yorumu çok seviyorum :). Sen değiştirerek 2 karakter kaydedebilirsiniz exit if (r+=1)>5için (r+=1)>5&&exit. Ayrıca, (code)while condsözdizimi daha kısadır while cond \n code \n end.
Cristian Lupascu

2

Python 2, 294.289.285.281 273 bayt

n=input();l=len(n);s=[0]*l
for i in range(2,l):
 a=(n[i-2]-n[i-1])%4;s[i]+=a;n[i-1]+=a;n[i]+=a
 if i+1<l:n[i+1]+=a
 n=[a%4for a in n]
if l%3>1 and n!=[n[0]]*l:print"x"
else:
 for i in range(l%3,l-1,3):s[i]+=(n[l-1]-n[l-2])%4
 m=min(s);s=[(a-m)%4 for a in s];print s

DEMO

Eminim bu daha fazla golf yapılabilir ..

İşte test senaryolarının sonuçları:

[1]
-> [0]

[1,1]
-> [0, 0]

[1,2]
-> x

[1,4,2]
-> [2, 0, 1]

[4,3,4]
-> [1, 0, 1]

[2,2,2]
-> [0, 0, 0]

[4,1,1,3]
-> [0, 2, 3, 0]

[3,2,4,4,4]
-> x

[2,3,4,3,2]
-> [0, 0, 3, 3, 1]

[4,2,1,2,3,2]
-> [2, 0, 2, 3, 3, 1]

[3,4,4,2,2,2,3,2,2,1,2,2,1,4,2,2,4,1,2,3,3,4]
-> [0, 3, 3, 0, 1, 3, 0, 0, 0, 0, 1, 3, 0, 2, 0, 2, 2, 2, 1, 1, 1, 1]

[2,2,2,3,1,2,4,4,3,3,4,4,3,2,1,3,1,3,2,2,4,4,2]
-> x

[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2]
-> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0]

[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,3,4]
-> [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 3, 3]

[4,1,2,2,2,4,1,3,1,4,4,4,1,1,4,4,4,1,4,3,2,4,3,4]
-> [1, 0, 3, 0, 0, 3, 2, 3, 1, 0, 0, 1, 0, 3, 1, 1, 3, 1, 0, 0, 2, 1, 2, 3]

Algoritma önce son blok dışındaki tüm blokların değerlerinin aynı olmasını sağlar (ilk 2 dışındaki tüm blokların "dokunma sayılarını" yineleyerek ve "dokunma sayılarına" ekleyerek). Sonra, blok sayısı izin veriyorsa ( (num_of_blocks - 1) % 3 != 1) geri döner ve blokların geri kalanının değerlerinin son blokla eşleştiğinden emin olur. xÇözüm yoksa yazdırır .

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.