Come posso ottenere più Klotski nella mia vita?


15

Adoro i puzzle a tessere scorrevoli, ma di recente non ho avuto tempo per loro. Quindi, ho bisogno di un programma per darmi la mia soluzione di puzzle a tessere scorrevoli, in particolare i puzzle di Klotski.

Il tuo input sarà nel seguente formato:

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

dove #rappresenta i muri, .rappresenta un'area aperta, grappresenta l'obiettivo e i numeri adiacenti rappresentano blocchi diversi. Puoi presumere che:

  1. Non ci saranno più di 10 blocchi
  2. Non ci saranno due blocchi con lo stesso numero
  3. Tutti i blocchi saranno chiusi da muri
  4. La griglia è rettangolare
  5. Il 0blocco è abbastanza grande da coprire tutti i quadrati dell'obiettivo.
  6. C'è una soluzione valida

Devi restituire una sequenza di mosse che metterà il 0blocco in modo che copra tutti i quadrati dell'obiettivo. I blocchi non possono passare attraverso muri o altri blocchi. Per il puzzle di cui sopra, sarebbe una sequenza appropriata

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

mentre rappresenta lo spostamento del 2blocco 1 quadrato a sinistra, il 1blocco 2 quadrati a destra (in cima all'obiettivo) quindi 1 quadrato in basso, quindi il 0blocco 3 quadrati a destra.

Esistono in realtà diverse sequenze che funzioneranno per il problema sopra riportato e la loro produzione è accettabile. La tua soluzione dovrebbe essere ottimale, nel senso che dovrebbe produrre una sequenza che risolva il puzzle nel minor numero di passaggi possibile.

La sequenza deve essere stampata come sopra, ma può essere separata da virgola, newline o spazio. Non mi importa se ci sono virgole o spazi vuoti finali. Dovresti produrre l'output in un tempo ragionevole (massimo 120 secondi nei puzzle di seguito).

Puzzle 1:

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

Puzzle 2:

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

Puzzle 3:

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

Puzzle 4:

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

Questo è code-golf, quindi vince la soluzione più breve (in byte)!


Solo un pensiero: mentre leggevo questo, ho trovato qualcosa di un po 'confuso. Gli obiettivi, essendo "nascosti" erano difficili da vedere a volte. Nell'esempio che hai, possono essere "indovinati" con precisione ragionevole, tuttavia, nel caso in cui un blocco copra interamente l'obiettivo, dovresti avere un modo per indicare chiaramente l'intero obiettivo. Cosa succede se: lettere per blocchi, lettere maiuscole quando quel punto è su un obiettivo? . per spazio, * per obiettivo? tutto il resto uguale? sarebbe più chiaro?
Idem

@ Idem non c'è mai un caso in cui un blocco inizia su un goal square. L'ultimo esempio ha semplicemente due quadrati degli obiettivi scollegati.
Nathan Merrill,

Possiamo presumere che ogni input puzzle abbia una soluzione?
orlp

@oppure sì, lo aggiungerò alla dichiarazione del problema.
Nathan Merrill,

@NathanMerrill Per essere sicuri che stiamo facendo le cose correttamente, potresti aggiungere la quantità ottimale di mosse per il puzzle 1-4?
orlp

Risposte:


5

Python, 1761

Un po 'stanco di questa domanda, quindi non potevo portarmi a golf. Ad ogni modo, in questo momento è l'unica soluzione che risolve tutto entro il limite di tempo (il più lungo, n. 3, richiede 27 secondi).

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),)))

Wow grande! E sicuramente non nella lingua più veloce
edc65,

Sembra un approccio totalmente diverso e non capisco bene Python. Ma mi piace l'idea di trovare pezzi con la stessa forma. Ciò potrebbe ridurre notevolmente lo spazio della posizione visitata nel mio codice. Posso prenderlo in prestito per la mia soluzione?
edc65,

@ edc65 Certo. Tuttavia, non è un approccio diverso, faccio anche una prima ricerca ampia - non guardo due volte nella stessa scheda (e i blocchi con la stessa forma scambiati contano la stessa scheda).
orlp,

4

JavaScript (ES6), 446 388

Breadth First Search, quindi la prima soluzione trovata è la più breve.
Mentre penso ancora che sia una buona soluzione, non è abbastanza buona. Anche dopo aver controllato milioni di posizioni (durata diversi minuti), non sono riuscito a trovare una soluzione per esempio 2 e 3.

Modifica la versione modificata di ES6 per superare il limite di tempo di esecuzione javascript. Puzzle 3 risolto in 7 minuti, 145 passaggi. Puzzle 2 risolto in 10 minuti, 116 passi

Modifica 2 Big speedup, usando l'idea di @ orlp di considerare uguale qualsiasi due blocchi con la stessa forma (escluso il blocco '0' che è speciale). Ciò riduce lo spazio delle posizioni visitate durante BSF. Ad esempio, per il puzzle 2, qualsiasi posizione con il blocco 1,2,3 o 5 scambiato è davvero la stessa.

Tempistica: il più lungo è il puzzle 3, ~ 20 sec sul mio laptop.

Usa Firefox per giocare con il nuovo JsFiddle per testare.

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

outdated

EcmaScript 6 (FireFox) JSFiddle

EcmaScript 5 (Chrome) JSFiddle

Esempio

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

Puzzle 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

Puzzle 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

Puzzle 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

Puzzle 4

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

T(ms) 25,Len6
1L 1D 1L 1L 0D 0R

Per verificare la tua soluzione (e altre soluzioni), puoi pubblicare il numero di mosse che ricevi per ogni problema che ho pubblicato?
Nathan Merrill,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.