Comment obtenir plus de Klotski dans ma vie?

15

J'adore vraiment les puzzles coulissants, mais récemment, je n'ai pas eu le temps pour eux. Par conséquent, j'ai besoin d'un programme pour me donner ma solution des puzzles de tuiles coulissantes, en particulier des puzzles Klotski.

Votre contribution sera au format suivant:

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

#représente les murs, .représente une zone ouverte, greprésente le but et les nombres adjacents représentent différents blocs. Vous pouvez supposer que:

  1. Il n'y aura pas plus de 10 blocs
  2. Il n'y aura pas deux blocs avec le même numéro
  3. Tous les blocs seront entourés de murs
  4. La grille est rectangulaire
  5. Le 0bloc est suffisamment grand pour couvrir toutes les cases de but.
  6. Il existe une solution valable

Vous devez renvoyer une séquence de mouvements qui mettra le 0bloc de sorte qu'il couvre toutes les cases de but. Les blocs ne peuvent pas traverser des murs ou d'autres blocs. Pour le casse-tête ci-dessus, une séquence appropriée serait

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

tandis que représente le déplacement du 2bloc 1 carré vers la gauche, le 1bloc 2 carrés vers la droite (en haut du but) puis 1 carré vers le bas, puis le 0bloc 3 carrés vers la droite.

Il existe en fait plusieurs séquences qui fonctionneront pour le problème ci-dessus, et la production de l'une d'entre elles est acceptable. Votre solution doit être optimale, ce qui signifie qu'elle doit produire une séquence qui résout le casse-tête en un minimum d'étapes.

La séquence doit être imprimée comme ci-dessus, mais peut être séparée par des virgules, des sauts de ligne ou des espaces. Peu m'importe s'il y a des virgules ou des espaces. Vous devez produire la sortie dans un délai raisonnable (maximum 120 secondes sur les puzzles ci-dessous).

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#
######

C'est du code-golf, donc la solution la plus courte (en octets) l'emporte!

Nathan Merrill
la source
Juste une pensée - en lisant ceci, j'ai trouvé quelque chose d'un peu déroutant. Les objectifs, «cachés», étaient parfois difficiles à voir. Dans l'exemple que vous avez, ils peuvent être "devinés" avec une précision raisonnable, cependant, dans le cas où un bloc couvre entièrement l'objectif, vous devriez avoir un moyen de désigner clairement l'objectif entier. Et si: Lettres pour blocs, Majuscules lorsque cet endroit est sur un but? . pour l'espace, * pour le but? tout le reste pareil? serait-ce plus clair?
Idem
@Ditto il n'y a jamais de cas où un bloc commence sur une case de but. Le dernier exemple a simplement deux carrés de but déconnectés.
Nathan Merrill
Pouvons-nous supposer que chaque puzzle d'entrée a une solution?
orlp
@orlp oui, je vais ajouter cela à l'énoncé du problème.
Nathan Merrill
@NathanMerrill Pour nous assurer que nous faisons les choses correctement, pourriez-vous ajouter la quantité optimale de mouvements pour les casse-tête 1-4?
orlp

Réponses:

5

Python, 1761

Un peu épuisé par cette question, donc je ne pouvais pas me résoudre à jouer au golf. Quoi qu'il en soit, en ce moment, c'est la seule solution qui résout tout dans le délai imparti (la plus longue, # 3, prend 27 secondes).

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
la source
Wow génial! Et sûrement pas dans la langue la plus rapide
edc65
Cela semble une approche totalement différente et je ne comprends pas bien Python. Mais j'aime l'idée de trouver des pièces de même forme. Cela pourrait réduire considérablement l'espace de position visité dans mon code. Puis-je l'emprunter pour ma solution?
edc65
@ edc65 Bien sûr. Ce n'est pas une approche différente, je fais également une première recherche étendue - je ne regarde pas deux fois le même tableau (et les blocs avec la même forme changent de compte comme le même tableau).
orlp
4

JavaScript (ES6), 446 388

Étendue Première recherche, la première solution trouvée est donc la plus courte.
Bien que je pense toujours que c'est une bonne solution, elle n'est pas suffisante. Même après avoir vérifié des millions de positions (plusieurs minutes d'exécution), je n'ai pas pu trouver de solution pour les exemples 2 et 3.

Modifier la version modifiée d' ES6 pour dépasser la limite de temps d'exécution javascript. Puzzle 3 résolu en 7min, 145 étapes. Puzzle 2 résolu en 10 minutes, 116 étapes

Edit 2 Big speedup, en utilisant l'idée de @ orlp de considérer comme égal deux blocs de même forme (à l'exclusion du bloc '0' qui est spécial). Cela réduit l'espace des positions visitées pendant BSF. Par exemple, pour le casse-tête 2, toute position avec les blocs 1, 2, 3 ou 5 échangés est vraiment la même.

Timing: le plus long est le puzzle 3, ~ 20 sec sur mon ordinateur portable.

Utilisez Firefox pour jouer avec le nouveau JsFiddle à tester.

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

Dépassé

EcmaScript 6 (FireFox) JSFiddle

EcmaScript 5 (Chrome) JSFiddle

Exemple

#######
#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
edc65
la source
Pour vérifier votre solution (et d'autres solutions), pouvez-vous publier le nombre de mouvements que vous obtenez pour chaque problème que j'ai signalé?
Nathan Merrill