Dapatkah saya geser terpisah teka-teki?

38

Tulis sebuah program atau fungsi yang mengambil dalam kotak teks persegi panjang di mana setiap sel adalah suatu Aatau a B. Semua Asel akan membentuk bentuk yang terhubung sederhana , yaitu mereka semua akan terhubung secara ortogonal tanpa lubang (huruf diagonal tetangga tidak dihitung sebagai terhubung). Demikian juga, semua Bsel akan membentuk bentuk lain yang terhubung sederhana. Kotak akan selalu mengandung setidaknya satu Adan setidaknya satu B.

Bayangkan kisi-kisi itu sebenarnya adalah dua potong plastik tipis berbentuk balok, diwakili oleh bagian Adan B. Jika ditempatkan rata di atas meja, bisakah kedua potongan itu digeser terpisah sambil menjaga keduanya tetap rata di atas meja?

Cetak atau kembalikan nilai kebenaran jika keduanya Adan Bbentuk dapat dipisahkan seperti ini hanya dengan menariknya terpisah. Jika tidak, mencetak atau mengembalikan falsy nilai.

Misalnya input

AAA
ABB
AAA

benar karena BBbagian tersebut dapat digeser ke kanan, memisahkannya dari A:

AAA
A    BB
AAA

Namun inputnya

AAAA
ABBA
ABAA

palsu karena tidak ada cara untuk menggeser Adan Bmembagi bagian tanpa membuatnya tumpang tindih.

Kode terpendek dalam byte menang. Jika diinginkan, Anda dapat menggunakan dua karakter ASCII distik yang bisa dicetak untuk menggantikan Adan B.

Contoh Truthy (dipisahkan oleh garis kosong)

BBB
BAA
BBB

BA

A
B

AB
AB

AAA
BBB

AAAAB
ABBBB

ABBA
ABBA
AAAA

AAAAAABBBBBBBBB
AABBBBBBBBBBBBB
AAAAAAAAAABBBBB
AABBBBBBBBBBBBB
AAAAAAAAAAAAAAB

AAAAAAAAAAAA
ABABABABABAB
BBBBBBBBBBBB

BAAAAABB
BBAAABBB
BBBABBBB
BBBABBBB
BBBABBBB
BBBBBBBB
BBBBBBBB

AAA
BAA
AAA

Contoh-contoh Palsu

BBBB
BAAB
BABB

BBBB
BAAB
AABB

BBBBBBB
BBBBBAB
AAAAAAB
BBBBBBB

BAAA
BABA
BBBA
AABA
AAAA

AAAAAAA
ABBBBBA
AAAAABA
BBBBBBA

BAAAAABB
BBAAABBB
BBBABBBB
BBBABBBB
BBBAABBB
BBBBBBBB
BBBBBBBB

AAA
ABA
BBA
ABA
AAA
Hobi Calvin
sumber

Jawaban:

9

Siput, 14

o(t\B+~)+!(t\B

Jika puzzle dapat digeser terpisah, itu akan mencetak area input. Kalau tidak, ia mencetak 0.

Ini agak lambat untuk contoh yang lebih besar, karena membutuhkan faktorial waktu di bidang kisi.

         ,, the program will print the number of starting cells matching this pattern
o        ,, pick a cardinal direction
(
    t    ,, teleport to any cell on the grid
    \B+  ,, match "B" 1 or more times, moving in the direction set by 'o'.
         ,, when a cell is matched, it gets slimed and can't be matched again.
    ~    ,, match an out-of-bounds cell
)+       ,, do parenthesized instructions 1 or more times
!(       ,, the following must not match:
    t\B  ,, teleport to some cell and match 'B'
feersum
sumber
4
"Agak lambat .." . Tidak yakin apa yang Anda harapkan dari bahasa yang disebut Snails ...
Bassdrop Cumberwubwubwub
4
@ Menjadi Sekarang, tidak ada alasan untuk menggosok garam di luka.
Trasiva
21

CJam, 33 32 20 19 17 byte

Versi revisi, dengan dukungan besar-besaran dari @ Sp3000 dan @ MartinBüttner:

qN/_z]{:e`z,3<}/|

Cobalah online

Kontribusi

  • @ Sp3000 menyarankan penyederhanaan kritis untuk algoritma asli saya.
  • @ MartinBüttner menerapkan keterampilan bermain golfnya yang gila pada pendekatan yang direvisi, yang hampir pasti menghasilkan kode yang lebih pendek daripada yang akan saya pikirkan bahkan setelah mempertimbangkan penyederhanaan.

Algoritma dan Bukti

Berikut ini menjelaskan kriteria puzzle yang terpisah secara horizontal. Huruf vertikal dapat ditentukan dengan melihat kolom alih-alih baris, atau mentranspos matriks karakter dan melihat baris lagi.

Saya akan menggunakan istilah "stretch" untuk urutan maksimum dari huruf yang sama. Misalnya, baris berikut memiliki masing-masing 1, 2, dan 3 peregangan:

AAAAAAAA
BBBAAAAA
AABBBAAA

Saya juga akan menggunakan istilah "interlocked" untuk baris / puzzle yang tidak dapat digeser terpisah.

Pengamatan utama adalah bahwa teka-teki dapat meluncur terpisah jika dan hanya jika semua baris memiliki paling banyak 2 peregangan . Atau terbalik, itu saling terkait jika dan hanya jika ada baris dengan lebih dari 2 peregangan .

Berikut ini mungkin tidak memenuhi syarat sebagai bukti matematika yang ketat, tetapi saya percaya bahwa itu membuat penjelasan yang meyakinkan mengapa ini harus terjadi.

Sangat mudah untuk melihat bahwa teka-teki itu saling terkait jika memiliki baris lebih dari 2 peregangan. Melihat satu baris dengan 3 bagian:

BBBAAB

jelas bahwa itu mencegah puzzle dari meluncur terpisah karena Aperegangan terkunci di antara Bperegangan. Ini berarti bahwa baris saling bertautan, yang pada gilirannya membuat seluruh teka-teki saling bertautan.

Arah kebalikan dari bukti tidak begitu jelas. Kita perlu menunjukkan bahwa tidak ada teka-teki yang saling terkait di mana semua baris hanya memiliki 1 atau 2 peregangan. Dimulai dengan beberapa pengamatan:

  • Baris dengan hanya 1 regangan tidak berkontribusi pada teka-teki yang saling terkait, karena mereka dapat meluncur ke arah mana pun tanpa tabrakan.
  • Jika semua baris dengan 2 regangan memiliki urutan yang sama Adan B, puzzle jelas tidak saling bertautan. Dalam hal ini, semua Asel tersisa dari semua Bsel, atau sebaliknya, dan tidak ada tabrakan saat menggeser kedua bagian secara terpisah.

Satu-satunya kasus rumit adalah teka-teki di mana kita memiliki baris dengan 2 peregangan dengan urutan berbeda. Saya akan menunjukkan bahwa puzzle semacam itu tidak ada di bawah spesifikasi yang diberikan. Untuk menunjukkan ini, mari kita lihat sebagian puzzle yang memang memiliki konfigurasi ini, di mana .wildcard:

.......
AAABBBB
.......
BBAAAAA
.......

Sekarang, spesifikasi mengatakan bahwa kedua sel Adan Bhanya terhubung di semua teka-teki yang valid. Untuk membuat Asel terhubung di sebagian puzzle di atas, kami memiliki dua opsi:

  1. Kami melingkari salah satu peregangan B, misalnya:

    ..AAAAAA
    AAABBBBA
    .......A
    BBAAAAAA
    ........
    

    Untuk melakukan ini, kita harus memperpanjang salah satu baris untuk memiliki 3 peregangan, jadi ini tidak akan pernah memberi kita teka-teki yang valid di mana semua baris memiliki paling banyak 2 peregangan.

  2. Kami menghubungkan mereka di jalur langsung:

    .......
    AAABBBB
    ..A....
    BBAAAAA
    .......
    

    The Asel sekarang hanya terhubung, dan belum ada baris dengan lebih dari 2 membentang. Namun, Bsel - sel juga perlu dihubungkan secara sederhana. Jalur langsung sekarang diblokir oleh Asel-sel yang terhubung , dan satu-satunya cara untuk menghubungkan Bsel - sel adalah untuk loop di sekitar salah satu peregangan Asel. Ini mengarah kembali ke kasus 1, di mana kita tidak bisa melakukan itu tanpa membuat baris 3 peregangan.

Untuk menghitung peregangan, implementasinya menggunakan operator CJam RLE.

Penjelasan Kode

qN/     Get input and split at newlines.
_z      Make a transposed copy.
]       Wrap the original and transposed puzzle in an array so that we can
        loop over the two.
{       Start of loop over original and transposed puzzle.
  :e`     Apply RLE to all rows.
  z,      Transpose the matrix with the RLE rows, and take the element count of the
          result. Or in other words, take the column count. This will be the length
          of the longest row after RLE.
  3<      Check the length for less than 3.
}/      End of loop over original and transposed puzzle.
|       Or the results of the two.
Reto Koradi
sumber
9

JavaScript (ES6), 108 107 98 91 82 byte

a=>!(T=[],R=/AB+A|BA+B/).test([...a].map((c,i)=>T[i%-~a.search`
`]+=c))|!R.test(a)

Demo langsung . Diuji di Firefox. Mengambil input sebagai string yang dibatasi-baris baru.

Suntingan:

  • Disimpan 1 byte dengan mengubah \nke baris literal.
  • Disimpan 9 byte dengan melakukan tes RegExp langsung pada string multi-line alih-alih mengonversi ke array.
  • Hilangkan 9 byte lainnya dengan menggunakan pemahaman array untuk membagi string, bergerak! ke dalam gfungsi dan memanggil RegExp langsung pada array alih-alih menggunakan find.
  • Melanjutkan urutan arthmetic dengan menyimpan 9 byte lagi. Lakukan modulus pada indeks alih-alih memisahkan array dengan baris baru sebelum mengambil transpos.

Bagaimana itu bekerja

Versi sebelumnya:

a=>(T=[],a.split`
`.map(s=>s.split``.map((c,i)=>T[i]+=c)),!T.find(g=s=>/AB+A|BA+B/.test(s)))|!g(a)
  1. Ambil input adan bagi dengan baris baru menjadi array string.
  2. Transpose adan simpan di T. Gunakan mapuntuk beralih di atas setiap elemen a, pisahkan string ke dalam array karakter, dan gunakan maplagi untuk menambahkan ikarakter th pada baris ke ibaris th T. Karena setiap elemen Ttidak diinisialisasi, pada akhirnya akan terlihat seperti "undefinedAAABBA", tetapi ini tidak masalah.
  3. Buat fungsi pengujian berbasis RegExp gyang cocok dengan pola /AB+A|BA+B/. Jika cocok, potongan-potongan dikunci ke arah yang diberikan karena kemudian ada satu set Bs diapit antara dua atau lebih As atau sebaliknya.
  4. Gunakan fungsi pengujian guntuk menguji semua elemen blok adan transposinya Tuntuk kecocokan menggunakan find. Jika keduanya cocok, maka bagian-bagiannya terkunci di kedua arah, sehingga menghasilkan nilai falsy, sebaliknya yang benar.
intrepidcoder
sumber
5

Javascript (ES6), 118

slidey=
// code
a=>!a.match(R=/AB+A|BA+B/)||!(a=a.split`
`.map(b=>b.split``))[0].map((_,c)=>a.map(d=>d[c])).some(e=>e.join``.match(R))

// IO
var S =document.getElementById('S');
S.onkeyup = _=> document.getElementById('P').innerText = slidey(S.value);

document.getElementById('P').innerText = slidey(S.value);
<textarea id='S'>BAAAAABB
BBAAABBB
BBBABBBB
BBBABBBB
BBBABBBB
BBBBBBBB
BBBBBBBB</textarea>
<p id='P'></p>

Penjelasan:

a=> !/* check string horizontally */ || !/* check string vertically by transposing it and
                                            running the same horizontal check */

a=> !a.match(R=/AB+A|BA+B/) || !/* ... */
// check for lines containing something like BAAAAAB or ABBBBBBBA
// this is the only way something can get blocked horizontally
// eg AAAAAAA
//    AAABAAA <<< note the B in the middle of As here
//    AAABBBB <<< blocked from being pulled out horizontally
//    AAAAAAA

a=> /* ... */ ||!( a = a.split('\n').map(b=> b.split('')) ) // split a into 2D array
    [0].map((_,c)=>a.map(d=>d[c])) // transpose it
    .some(e=>e.join``.match(R)) // run the check again using `some` to go line by line
                                // which is shorter than .join().match() outside

a=> !/* returns null if no horizontal obstacles and an array if there are */
    || !/* same thing */
// negate both to cast to a boolean (false if obstacles, true if not)
// an input can only be unslidable if both directions are blocked
// so (no obstacles vertically? || no obstacles horizontally?) gives the answer
DankMemes
sumber
Tikus! Kalahkan aku untuk itu.
intrepidcoder
5

JavaScript (ES6) 72 74

Edit 2 byte yang disimpan thx @NotthatCharles

Saya memiliki pemahaman intuitif bahwa jika satu potong dapat meluncur hanya sebagian kecil dari langkah, maka itu gratis. Kasing saat ini mengkonfirmasi hal ini.

Jadi saya periksa hanya satu langkah di setiap arah.

Karakter yang digunakan: 1 dan 0
2 byte lebih banyak untuk menggunakan 2 karakter yang dapat dicetak seperti A dan B

Tes menjalankan cuplikan di bawah ini di peramban yang mendukung EcmaScript 6 (mendukung operator penyebaran - IE Firefox)

f=s=>[w=~s.search`
`,-w,-1,1].some(o=>![...s].some((x,p)=>x+s[p+o]==10))

// 4 bytes more- for any symbol, not just 1 and 0 (for instance A and B):
g=s=>[w=~s.search`
`,-w,-1,1].some(o=>![...s].some((x,p)=>x+s[p+o]=='AB'))

//TEST
console.log=x=>O.innerHTML+=x+'\n'

testOk = [
 '111\n100\n111',
 '10',
 '0\n1',
 '01\n01',
 '000\n111',
 '00001\n01111',
 '0110\n0110\n0000',
 '000000111111111\n001111111111111\n000000000011111\n001111111111111\n000000000000001',
 '000000000000\n010101010101\n111111111111',
 '10000011\n11000111\n11101111\n11101111\n11101111\n11111111\n11111111',
 '000\n100\n000'
]

testKo = [
 '1111\n1001\n1011',
 '1111\n1001\n0011',
 '1111111\n1111101\n0000001\n1111111',
 '1000\n1010\n1110\n0010\n0000',
 '0000000\n0111110\n0000010\n1111110',
 '10000011\n11000111\n11101111\n11101111\n11100111\n11111111\n11111111',
 '000\n010\n110\n010\n000'
]

console.log('Expecting true')
testOk.forEach(t=>console.log(t+'\n'+f(t)+'\n'))
console.log('Expecting false')
testKo.forEach(t=>console.log(t+'\n'+f(t)+'\n'))
<pre id=O></pre>

edc65
sumber
Ya, itu jenius saja. Dipukuli lagi oleh seorang profesional. :-)
ETHproduksi
s[p+o]=='0'sepertinya agak panjang. Bisakah itu diganti dengan 1-s[p+o], atau setidaknya s[p+o]==0?
ETHproduk
@ ETHproductions ya, ini panjang, perlu beberapa pemikiran lagi. Ini harus memberikan false untuk '\ n' (perbatasan vertikal, dikonversi ke 0) dan untuk tidak terdefinisi (batas atas dan bawah, dikonversi ke NaN)
edc65
=='A'dapat digantikan oleh <'B'. Sama untuk=='B'
Bukan Charles
Juga, tidak bisakah kamu melakukannya x+s[p+o]=='AB'?
Bukan Charles
3

Mathematica 100 69 byte

Dengan 31 byte besar yang disimpan, terima kasih kepada @Martin Buttner,

g=Max[Length/@Split/@#]<3&;g[c=Characters@StringSplit@#]||g@Thread@c&

Memformat input sebagai matriks karakter; itu juga membuat transpos matriks. Jika salah satu matriks atau transposinya tidak lebih dari 2 run per baris maka puzzle tersebut dapat meluncur.

{a,a,b,b,b} memiliki 2 run huruf.

{a,a,b,a,a} memiliki 3 run huruf.

{a,a,b,a,a,a,b,b,b,b,b,b,b,b} memiliki 4 huruf.

DavidC
sumber
2

Dyalog APL, 22 byte

(∨/{∧/2>+/2≠/⍵}¨)⊂∘⍉,⊂

Coba di sini. Ini adalah fungsi yang mengambil dalam array karakter 2D, dan mengembalikan 1untuk instance geser dan 0untuk yang non-sliding. Algoritma ini mirip dengan sebagian besar jawaban lain: periksa matriks dan transposinya bahwa tidak ada baris yang berisi lebih dari satu pasangan huruf yang berdekatan. Untuk matriks input 4x3

AAAA
ABBB
AAAB

fungsi dapat dipanggil sebagai

f ← (∨/{∧/2>+/2≠/⍵}¨)⊂∘⍉,⊂
f 4 3 ⍴ 'AAAAABBBAAAB'

yang menghasilkan 1.

Penjelasan

⊂∘⍉,⊂   The matrix and its transpose.
{...}¨   For each of them:
  2≠/⍵   On each row, replace each adjacent pair with 1 if they differ, with 0 otherwise
  2>+/    Take the sum on each row and check that it's less than 2
  ∧/     AND over all rows
∨/      OR over the resulting two values
Zgarb
sumber
1

JavaScript (ES6), 94 byte

x=>!(y=/AB+A|BA+B/).test(x)|(z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),!y.test(z))

Metode alternatif dengan ukuran yang sama:

x=>(t=s=>!/AB+A|BA+B/.test(s),z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),t(x)|t(z))

Ini mengembalikan 1untuk input yang benar dan 0untuk kepalsuan. Saya mulai mengerjakan ini sebelum jawaban lain telah diposting. Saya juga awalnya mencoba menggunakan pemahaman array ES7, tapi itu berakhir sekitar 10 karakter lebih lama dari metode ini.

Cobalah:

a=x=>!(y=/AB+A|BA+B/).test(x)|(z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),!y.test(z))

P.onclick=_=>Q.innerHTML='Result: '+a(O.value)
<textarea id=O cols="20" rows="8">AAAAAABBBBBBBBB
AABBBBBBBBBBBBB
AAAAAAAAAABBBBB
AABBBBBBBBBBBBB
AAAAAAAAAAAAAAB</textarea>
<button id=P>Test</button>
<p id=Q>Result: </p>

Saran diterima!

Produksi ETH
sumber
Menggunakan [... b] bukan b.split`` menyimpan 3 byte, tetapi hanya berfungsi di beberapa browser.
intrepidcoder