Como obtenho mais Klotski na minha vida?


15

Eu realmente amo quebra-cabeças deslizantes, mas recentemente não tive tempo para eles. Por isso, preciso de um programa para me dar a minha solução de quebra-cabeças deslizantes, especificamente quebra-cabeças de Klotski.

Sua entrada estará no seguinte formato:

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

onde #representa paredes, .representa uma área aberta, grepresenta a meta e números adjacentes representam blocos diferentes. Você pode assumir que:

  1. Não haverá mais de 10 blocos
  2. Não haverá dois blocos com o mesmo número
  3. Todos os blocos serão fechados por paredes
  4. A grade é retangular
  5. O 0bloco é grande o suficiente para cobrir todos os quadrados de gol.
  6. Existe uma solução válida

Você precisa retornar uma sequência de movimentos que colocarão o 0bloco para que cubra todos os quadrados de gol. Os blocos não podem atravessar paredes ou outros blocos. Para o quebra-cabeça acima, uma sequência apropriada seria

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

enquanto representa mover o 2bloco 1 quadrado para a esquerda, o 1bloco 2 quadrados para a direita (no topo da meta), depois 1 quadrado para baixo e, em seguida, o 0bloco 3 quadrados para a direita.

Na verdade, existem várias seqüências que funcionarão para o problema acima, e a produção de qualquer uma delas é aceitável. Sua solução deve ser ótima, o que significa que deve produzir uma sequência que resolva o quebra-cabeça o menor número possível de etapas.

A sequência deve ser impressa como acima, mas pode ser separada por vírgula, nova linha ou espaço. Não me importo se houver vírgulas ou espaços em branco. Você deve produzir a saída em tempo razoável (máximo de 120 segundos nos quebra-cabeças abaixo).

Quebra-cabeça 1:

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

Enigma 2:

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

Enigma 3:

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

Quebra-cabeça 4:

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

Isso é código-golfe, então a solução mais curta (em bytes) vence!


Apenas um pensamento - ao ler isso, achei algo um pouco confuso. Os objetivos, sendo "ocultos", eram difíceis de ver às vezes. No exemplo que você tem, eles podem ser "adivinhados" com precisão razoável, no entanto, no caso de um bloco cobrir completamente a meta, você deve ter uma maneira de indicar claramente a meta inteira. E se: Cartas para blocos, maiúsculas quando esse ponto estiver em um objetivo? . por espaço, * por objetivo? tudo o mesmo? isso seria mais claro?
Ditto

@Ditto: nunca existe um caso em que um bloco comece em um quadrado de gol. O último exemplo simplesmente possui dois quadrados de meta desconectados.
27515 Nathan

Podemos assumir que todo quebra-cabeça de entrada tem uma solução?
orlp

@ orlp sim, vou adicionar isso à declaração do problema.
Nathan Merrill

@NathanMerrill Para garantir que estamos fazendo as coisas corretamente, você poderia adicionar a quantidade ideal de movimentos para o quebra-cabeça 1-4?
orlp

Respostas:


5

Python, 1761

Meio que me queimei com essa questão, por isso não consegui jogar. De qualquer forma, agora é a única solução que resolve tudo dentro do prazo (o mais longo, nº 3, leva 27 segundos).

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

Uau, ótimo! E, certamente, não no mais rápido idioma
edc65

Parece uma abordagem totalmente diferente e eu não entendo bem o Python. Mas gosto da ideia de encontrar peças da mesma forma. Isso poderia reduzir muito o espaço da posição visitada no meu código. Posso emprestá-lo para a minha solução?
Edc65

@ edc65 Claro. Porém, não é uma abordagem diferente, também faço uma primeira pesquisa ampla - simplesmente não olho para o mesmo quadro duas vezes (e blocos com a mesma forma trocam contagens que o mesmo quadro).
Orlp

4

JavaScript (ES6), 446 388

Largura da primeira pesquisa; portanto, a primeira solução encontrada é a mais curta.
Enquanto eu ainda acho que é uma boa solução, não é boa o suficiente. Mesmo depois de verificar milhões de posições (tempo de execução de vários minutos), não consegui encontrar uma solução, por exemplo, 2 e 3.

Edite a versão modificada do ES6 para superar o limite de tempo de execução do javascript. Quebra-cabeça 3 resolvido em 7min, 145 etapas. Quebra-cabeça 2 resolvido em 10min, 116 passos

Editar 2 Grande aceleração, usando a idéia de @ orlp de considerar igual a dois blocos da mesma forma (excluindo o bloco '0' que é especial). Isso reduz o espaço das posições visitadas durante o BSF. Por exemplo, para o quebra-cabeça 2, qualquer posição com o bloco 1,2,3 ou 5 trocado é realmente a mesma.

Tempo: o mais longo é o quebra-cabeça 3, ~ 20 segundos, no meu laptop.

Use o Firefox para jogar com o novo JsFiddle para testar.

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

Desatualizado

EcmaScript 6 (FireFox) JSFiddle

EcmaScript 5 (Chrome) JSFiddle

Exemplo

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

Quebra-cabeça 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

Quebra-cabeça 4

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

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

Para verificar sua solução (e outras soluções), você pode postar o número de movimentos que você recebe para cada problema que publiquei?
Nathan Merrill
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.