¿Cómo consigo más Klotski en mi vida?

15

Realmente me encantan los rompecabezas de fichas deslizantes, pero recientemente, no he tenido tiempo para ellos. Por lo tanto, necesito un programa que me proporcione mi solución de rompecabezas de fichas deslizantes, específicamente los rompecabezas de Klotski.

Su entrada estará en el siguiente formato:

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

donde #representa paredes, .representa un área abierta, grepresenta la meta y los números adyacentes representan diferentes bloques. Puedes asumir que:

  1. No habrá más de 10 bloques.
  2. No habrá dos bloques con el mismo número.
  3. Todos los bloques estarán encerrados por paredes.
  4. La cuadrícula es rectangular
  5. El 0bloque es lo suficientemente grande como para cubrir todos los cuadrados de la portería.
  6. Hay una solucion valida

Debes devolver una secuencia de movimientos que colocará el 0bloque para que cubra todos los cuadrados de la portería. Los bloques no pueden atravesar paredes u otros bloques. Para el rompecabezas anterior, una secuencia apropiada sería

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

mientras que representa mover el 2bloque 1 cuadro a la izquierda, el 1bloque 2 cuadros a la derecha (en la parte superior de la portería) luego 1 cuadro hacia abajo y luego 03 cuadros a la derecha.

En realidad, hay varias secuencias que funcionarán para el problema anterior, y producir cualquiera de ellas es aceptable. Su solución debe ser óptima, lo que significa que debe producir una secuencia que resuelva el rompecabezas en el menor número de pasos posible.

La secuencia debe imprimirse como se indicó anteriormente, pero puede estar separada por comas, líneas nuevas o espacios. No me importa si hay comas finales o espacios en blanco. Debe producir la salida en un tiempo razonable (máximo 120 segundos en los siguientes acertijos).

Rompecabezas 1:

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

Rompecabezas 2:

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

Rompecabezas 3:

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

Rompecabezas 4:

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

Este es el código de golf, por lo que gana la solución más corta (en bytes).

Nathan Merrill
fuente
Solo un pensamiento: mientras leía esto, encontré algo un poco confuso. Las metas, estar "oculto" eran difíciles de ver a veces. En el ejemplo que tiene, se pueden "adivinar" con una precisión razonable, sin embargo, en caso de que un bloque cubra el objetivo por completo, debe tener una manera de denotar claramente el objetivo completo. ¿Qué pasa si: Letras para bloques, mayúsculas cuando ese punto está en una meta? . por espacio, * por gol? todo lo demás igual? ¿sería eso más claro?
Ditto
@ Ídem, nunca hay un caso en el que un bloque comience en una casilla de gol. El último ejemplo simplemente tiene dos casillas de gol desconectadas.
Nathan Merrill
¿Podemos suponer que cada rompecabezas de entrada tiene una solución?
orlp
@orlp sí, lo agregaré a la declaración del problema.
Nathan Merrill
@NathanMerrill Para asegurarse de que estamos haciendo las cosas correctamente, ¿podría agregar la cantidad óptima de movimientos para el rompecabezas 1-4?
orlp

Respuestas:

5

Pitón, 1761

Un poco agotado por esta pregunta, así que no pude llevarme al golf. De cualquier manera, en este momento es la única solución que resuelve todo dentro del límite de tiempo (el más largo, # 3, toma 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),)))
orlp
fuente
¡Wow asombroso! Y seguramente no en el idioma más rápido
edc65
Parece un enfoque totalmente diferente y no entiendo bien Python. Pero me gusta la idea de encontrar piezas con la misma forma. Eso podría reducir mucho el espacio de la posición visitada en mi código. ¿Me lo prestas para mi solución?
edc65
@ edc65 Claro. Sin embargo, no es un enfoque diferente, también hago una primera búsqueda amplia: simplemente no busco en el mismo tablero dos veces (y los bloques con la misma forma intercambiada cuentan como el mismo tablero).
orlp
4

JavaScript (ES6), 446 388

Breadth First Search, por lo que la primera solución encontrada es la más corta.
Si bien sigo pensando que es una buena solución, no es lo suficientemente buena. Incluso después de verificar millones de posiciones (tiempo de ejecución varios minutos), no pude encontrar una solución, por ejemplo, 2 y 3.

Edite la versión modificada de ES6 para superar el límite de tiempo de ejecución de JavaScript. Puzzle 3 resuelto en 7 minutos, 145 pasos. Puzzle 2 resuelto en 10 minutos, 116 pasos

Edite 2 Big speedup, utilizando la idea de @ orlp de considerar iguales dos bloques con la misma forma (excluyendo el bloque '0' que es especial). Esto reduce el espacio de puestos visitados durante BSF. Por ejemplo, para el rompecabezas 2, cualquier posición con el bloque 1,2,3 o 5 intercambiado es realmente la misma.

Tiempo: el más largo es el rompecabezas 3, ~ 20 segundos en mi computadora portátil.

Usa Firefox para jugar con el nuevo JsFiddle para probar.

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

Anticuado

EcmaScript 6 (FireFox) JSFiddle

EcmaScript 5 (Chrome) JSFiddle

Ejemplo

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

Rompecabezas 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
edc65
fuente
Para verificar su solución (y otras soluciones), ¿puede publicar la cantidad de movimientos que obtiene para cada problema que publiqué?
Nathan Merrill