Bagaimana saya mendapatkan lebih banyak Klotski dalam hidup saya?

15

Saya sangat suka menggeser puzzle ubin, tetapi baru-baru ini, saya belum punya waktu untuk itu. Oleh karena itu, saya memerlukan program untuk memberi saya perbaikan teka-teki geser ubin, khususnya teka-teki Klotski.

Masukan Anda akan dalam format berikut:

#######
#001gg#
##.222#
.######

di mana #mewakili dinding, .mewakili area terbuka, gmewakili tujuan, dan angka yang berdekatan mewakili blok yang berbeda. Anda dapat mengasumsikan bahwa:

  1. Tidak akan lebih dari 10 blok
  2. Tidak akan ada dua blok dengan nomor yang sama
  3. Semua blok akan ditutup oleh dinding
  4. Grid berbentuk persegi panjang
  5. The 0blok cukup besar untuk menutup semua kotak gawang.
  6. Ada solusi yang valid

Anda harus mengembalikan urutan gerakan yang akan menempatkan 0blok sehingga mencakup semua kotak sasaran. Blok tidak bisa melewati dinding atau blok lainnya. Untuk teka-teki di atas, urutan yang tepat adalah

2L,1R,1R,1D,0R,0R,0R

sementara mewakili memindahkan 2blok 1 persegi kiri, 1blok 2 kotak kanan (di atas gawang) kemudian 1 persegi bawah, dan kemudian 0blok 3 kotak kanan.

Sebenarnya ada beberapa urutan yang akan bekerja untuk masalah di atas, dan menghasilkan salah satu dari mereka dapat diterima. Solusi Anda harus optimal, artinya harus menghasilkan urutan yang memecahkan teka-teki sesedikit mungkin langkah.

Urutan harus dicetak seperti di atas, tetapi dapat berupa koma, baris baru, atau spasi terpisah. Saya tidak peduli jika ada tanda koma atau spasi. Anda harus menghasilkan output dalam waktu yang wajar (maksimum 120 detik pada teka-teki di bawah).

Teka-teki 1:

..####..
..#00#..
###00###
#......#
#.1122.#
##3124##
.#3344#.
.##55##.
..#gg#..
..####..

Teka-teki 2:

######
#1002#
#1002#
#3445#
#3675#
#8gg9#
######

Teka-teki 3:

.####.
##1g##
#22g3#
#4255#
#4.56#
#.006#
#7008#
######

Teka-teki 4:

.####.
##00##
#.00g#
#.0.1#
#..g2#
######

Ini adalah kode-golf, jadi solusi terpendek (dalam byte) menang!

Nathan Merrill
sumber
Hanya sebuah pemikiran - ketika saya membaca ini, saya menemukan sesuatu yang agak membingungkan. Tujuan, karena "tersembunyi" kadang-kadang sulit dilihat. Dalam contoh yang Anda miliki, mereka dapat "ditebak" dengan akurasi resonable, namun, jika suatu blok menutupi seluruh tujuan, Anda harus memiliki cara untuk menunjukkan dengan jelas seluruh tujuan. Bagaimana jika: Surat untuk blok, Huruf besar ketika tempat itu ada di tujuan? . untuk ruang, * untuk sasaran? semuanya sama? apakah itu akan lebih jelas?
Ditto
@Lakukan tidak pernah ada kasus di mana blok dimulai pada kuadrat tujuan. Contoh terakhir hanya memiliki dua kotak gawang yang terputus.
Nathan Merrill
Bisakah kita menganggap setiap teka-teki input memiliki solusi?
orlp
@ Atau ya, saya akan menambahkannya ke pernyataan masalah.
Nathan Merrill
@NathanMerrill Untuk memastikan kami melakukan hal yang benar, dapatkah Anda menambahkan jumlah gerakan optimal untuk puzzle 1-4?
orlp

Jawaban:

5

Python, 1761

Agak lelah dengan pertanyaan ini, jadi saya tidak bisa bermain golf. Apa pun itu, sekarang ini satu-satunya solusi yang menyelesaikan semuanya dalam batas waktu (terpanjang, # 3, membutuhkan waktu 27 detik).

pieces = {}
taken = set()
goals = set()

y = 0
while True:
    try:
        for x, c in enumerate(input()):
            if c == ".": continue
            if c == "g":
                goals.add((x, y))
            else:
                if c in "0123456789":
                    if c not in pieces: pieces[c] = set()
                    pieces[c].add((x, y))
                taken.add((x, y))

        y += 1

    except: break

def translate_comp(coords):
    o = min(sorted(coords))
    return set((c[0] - o[0], c[1] - o[1]) for c in coords)

similar = {}
for piece in pieces:
    k = tuple(translate_comp(pieces[piece]))
    if k not in similar: similar[k] = []
    similar[k].append(piece)


seen = set()
states = [(pieces, taken, ())]
while states:
    state = states.pop(0)
    if not goals - state[0]["0"]:
        names = {
            (-1, 0): "L",
            (1, 0): "R",
            (0, 1): "D",
            (0, -1): "U",
        }

        print(len(state[2]))
        print(" ".join(piece + names[d] for d, piece in state[2]))
        break

    for piece in pieces:
        for d in ((-1, 0), (1, 0), (0, 1), (0, -1)):
            new_pieces = state[0].copy()
            new_pieces[piece] = {(c[0] + d[0], c[1] + d[1]) for c in state[0][piece]}
            new_taken = state[1] - state[0][piece]

            # Collision
            if new_pieces[piece] & new_taken:
                continue

            gist = tuple(frozenset().union(*(new_pieces[piece] for piece in similar_set))
                         for similar_set in similar.values())

            if gist in seen:
                continue

            seen.add(gist)
            new_taken |= new_pieces[piece]
            states.append((new_pieces, new_taken, state[2] + ((d, piece),)))
orlp
sumber
Wow LUAR BIASA! Dan tentu saja tidak dalam bahasa tercepat
edc65
Tampaknya pendekatan yang sama sekali berbeda dan saya tidak mengerti Python dengan baik. Tapi saya suka ide menemukan potongan dengan bentuk yang sama. Itu bisa mengurangi banyak ruang posisi yang dikunjungi dalam kode saya. Bolehkah saya meminjamnya untuk solusi saya?
edc65
@ edc65 Tentu. Ini bukan pendekatan yang berbeda, saya juga melakukan pencarian pertama yang luas - Saya hanya tidak melihat ke papan yang sama dua kali (dan blok dengan bentuk yang sama bertukar dianggap sebagai papan yang sama).
orlp
4

JavaScript (ES6), 446 388

Breadth First Search, jadi solusi pertama yang ditemukan adalah yang terpendek.
Meskipun saya masih berpikir itu solusi yang baik, itu tidak cukup baik. Bahkan setelah memeriksa jutaan posisi (runtime beberapa menit), saya tidak dapat menemukan solusi misalnya 2 dan 3.

Edit versi modifikasi ES6 untuk mengatasi batas waktu eksekusi javascript. Puzzle 3 diselesaikan dalam 7 menit, 145 langkah. Puzzle 2 dipecahkan dalam 10 menit, 116 langkah

Edit 2 speedup besar, menggunakan ide @ orlp untuk mempertimbangkan sama dengan dua blok dengan bentuk yang sama (tidak termasuk blok '0' yang khusus). Ini mengurangi ruang posisi yang dikunjungi selama BSF. Misalnya, untuk puzzle 2, posisi apa pun dengan blok 1,2,3 atau 5 yang ditukar benar-benar sama.

Pengaturan waktu: yang terpanjang adalah puzzle 3, ~ 20 detik di laptop saya.

Gunakan Firefox untuk bermain dengan JsFiddle baru untuk menguji.

F=g=>(o=>{
for(u=[i=s=''],v={},h=[],b={},k={'.':-1},l={},
g=[...g].map((c,p)=>c>'f'?(h.push(p),'.'):c<'0'?c:
l[k[c]?k[c]+=','+(p-b[c]):k[b[c]=p,c]=~(c>0),k[c]]=c),
b=Object.keys(b),b.map(c=>k[c]=l[k[c]]);
h.some(p=>g[p]!='0');[s,g]=u[++i])
b.map(b=>[-1,1,o,-o].map((d,i)=>
g.every((c,p)=>c!=b?1:(c=g[p+d])!=b&c!='.'?0:m[g[p-d]!=b?m[p]='.':1,p+d]=b,m=g.slice(0))
&&!v[q=m.map(c=>k[c]||'')+'']?v[q]=u.push([s+b+'LRUD'[i]+' ',m]):0))
})(o=~g.search(/\n/))||s

Ketinggalan jaman

EcmaScript 6 (FireFox) JSFiddle

EcmaScript 5 (Chrome) JSFiddle

Contoh

#######
#001gg#
##.222#
.######
T(ms) 10,Len7
1R 0R 1R 0R 2L 1D 0R

Teka-teki 1

..####..
..#00#..
###00###
#......#
#.1122.#
##3124##
.#3344#.
.##55##.
..#gg#..
..####..

T(ms) 8030,Len70
1U 2U 3U 4U 5U 5L 4D 2R 1R 3U 5U 4L 4D 5R 5R 3D 1L 3D 1L 5L 5U 5U 2D 5R 
1R 5R 1R 1D 0D 4D 1D 0D 0L 0L 1U 1U 1U 1U 2L 2L 2U 5D 2R 0R 3R 3R 0D 0D
2L 2L 2L 5U 0U 3U 3U 4U 4U 4R 0D 3L 3U 5D 5L 5L 5L 4U 4U 0R 0D 0D

Teka-teki 2

######
#1002#
#1002#
#3445#
#3675#
#8gg9#
######

T(ms) 646485, Checked 10566733, Len 116
8R 3D 4L 7U 9L 5D 7R 4R 3U 8L 9L 5L 7D 4R 6U 9U 8R 3D 6L 4L 2D 7D 2D 0R
1R 6U 6U 3U 3U 9L 8L 5L 7L 7U 2D 4R 5U 8R 8R 5D 1D 6R 3U 9U 5L 1D 1D 9R
9U 4L 4L 2U 8R 7D 2L 8U 7R 2D 4R 3D 6L 9U 4R 1U 1U 2L 8L 8D 4D 0D 9R 6R
3U 9R 6R 1U 5U 2U 8L 8L 7L 7L 4D 0D 6D 6R 1R 2U 2U 0L 6D 9D 6D 9D 1R 2R
3R 5U 5U 0L 9L 6U 4U 7R 8R 7R 8R 0D 9L 9L 6L 6L 4U 8U 8R 0R

Teka-teki 3

.####.
##1g##
#22g3#
#4255#
#4.56#
#.006#
#7008#
######

T(ms) 433049, Checked 7165203, Len 145
3L 3U 5U 6U 0U 7U 8L 8L 8L 0D 0R 7R 7U 7R 4D 2D 8R 4D 2D 5L 5L 3D 1R 3R
1D 1D 5R 5U 3L 6U 2U 4U 7R 1D 8L 0L 7D 1R 2R 4U 4U 8U 8U 0L 2D 3D 3L 6L  
1U 7D 2R 0R 8D 4D 8D 4D 3L 3U 4U 4R 8U 8U 0L 7L 2D 1D 6R 4R 4U 1L 1L 1U
2U 2L 6D 6D 4R 1R 1U 2U 2L 6L 6U 4D 1R 6U 7U 7U 0R 8D 0R 2D 3D 8D 2D 3D
7L 6D 5D 5L 1L 1U 1L 6U 4U 7R 7R 6D 6L 4L 4U 7U 7U 0U 0U 2R 3D 2R 3R 3D 
6D 5D 1D 1L 4L 7L 7U 0U 2U 3R 6D 5D 4D 7L 3R 6R 8R 5D 4D 7D 4L 7D 7D 0L 
0U

Teka-teki 4

.####.
##00##
#.00g#
#.0.1#
#..g2#
######

T(ms) 25,Len6
1L 1D 1L 1L 0D 0R
edc65
sumber
Untuk memverifikasi solusi Anda (dan solusi lainnya), dapatkah Anda memposting jumlah gerakan yang Anda dapatkan untuk setiap masalah yang saya posting?
Nathan Merrill