Wie bekomme ich mehr Klotski in meinem Leben?

15

Ich liebe es wirklich, Puzzles zu schieben, aber in letzter Zeit hatte ich keine Zeit dafür. Daher brauche ich ein Programm, mit dem ich meine Schiebepuzzlespiele, insbesondere Klotski-Puzzlespiele, reparieren kann.

Ihre Eingabe erfolgt in folgendem Format:

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

wobei #Wände darstellt, .stellt einen offenen Bereich, gstellt das Ziel, und benachbarte Zahlen repräsentieren verschiedene Blöcke. Sie können davon ausgehen, dass:

  1. Es wird nicht mehr als 10 Blöcke geben
  2. Es wird nicht zwei Blöcke mit der gleichen Nummer geben
  3. Alle Blöcke werden von Mauern umschlossen
  4. Das Raster ist rechteckig
  5. Der 0Block ist groß genug, um alle Torfelder abzudecken.
  6. Es gibt eine gültige Lösung

Sie müssen eine Folge von Zügen zurückgeben, mit denen der 0Block alle Zielfelder abdeckt. Blöcke können keine Wände oder andere Blöcke passieren. Für das obige Puzzle wäre eine geeignete Reihenfolge

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

Während das Verschieben des 2Blocks 1 Quadrat nach links darstellt, wird der 1Block 2 mit dem Quadrat nach rechts (über dem Tor), dann mit dem Quadrat nach unten und dann mit dem 0Quadrat nach rechts mit dem Block 3 verschoben.

Tatsächlich gibt es mehrere Sequenzen, die für das obige Problem geeignet sind, und das Erzeugen einer von ihnen ist akzeptabel. Ihre Lösung sollte optimal sein, was bedeutet, dass eine Sequenz erstellt werden sollte, die das Rätsel in so wenigen Schritten wie möglich löst.

Die Sequenz sollte wie oben gedruckt werden, kann jedoch durch Kommas, Zeilenumbrüche oder Leerzeichen getrennt sein. Es ist mir egal, ob es nachgestellte Kommas oder Leerzeichen gibt. Sie sollten die Ausgabe in angemessener Zeit erstellen (maximal 120 Sekunden bei den unten aufgeführten Rätseln).

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

Das ist Code-Golf, also gewinnt die kürzeste Lösung (in Bytes)!

Nathan Merrill
quelle
Nur ein Gedanke - als ich das las, fand ich etwas verwirrend. Die Ziele, "versteckt" zu sein, waren manchmal schwer zu sehen. In dem Beispiel, das Sie haben, können sie mit vernünftiger Genauigkeit "erraten" werden. Wenn jedoch ein Block das Ziel vollständig abdeckt, sollten Sie eine Möglichkeit haben, das gesamte Ziel klar zu kennzeichnen. Was ist, wenn: Buchstaben für Blöcke, Großbuchstaben, wenn sich diese Stelle auf einem Tor befindet? . für den Weltraum, * für das Ziel? alles andere gleich? Wäre das klarer?
Ditto
@Da gibt es nie einen Fall, in dem ein Block auf einem Torfeld beginnt. Das letzte Beispiel hat einfach zwei getrennte Torquadrate.
Nathan Merrill
Können wir annehmen, dass jedes Eingabe-Puzzle eine Lösung hat?
Orlp
@orlp Ja, das werde ich zur Problemstellung hinzufügen.
Nathan Merrill
@ NathanMerrill Um sicherzustellen, dass wir alles richtig machen, können Sie die optimale Anzahl von Zügen für die Rätsel 1-4 hinzufügen?
Orlp

Antworten:

5

Python, 1761

Diese Frage ist irgendwie durchgebrannt, deshalb konnte ich mich nicht dazu bringen, Golf zu spielen. In beiden Fällen ist es derzeit die einzige Lösung, die alles innerhalb des Zeitlimits löst (die längste, Nummer 3, dauert 27 Sekunden).

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
quelle
Wow großartig! Und sicherlich nicht in der schnellsten Sprache
edc65
Es scheint ein völlig anderer Ansatz zu sein und ich verstehe Python nicht gut. Aber ich mag die Idee, Stücke mit der gleichen Form zu finden. Das könnte den Platz der besuchten Position in meinem Code stark reduzieren. Darf ich es für meine Lösung ausleihen?
Edc65
@ edc65 Sicher. Es ist jedoch kein anderer Ansatz, ich mache auch eine breitere erste Suche - ich schaue nur nicht zweimal in dasselbe Board (und Blöcke mit derselben Form werden getauscht wie dasselbe Board).
Orlp
4

JavaScript (ES6), 446 388

Breite erste Suche, daher ist die erste gefundene Lösung die kürzeste.
Ich denke immer noch, dass das eine gute Lösung ist, aber es ist nicht gut genug. Selbst nachdem ich Millionen von Positionen überprüft hatte (Laufzeit mehrere Minuten), konnte ich keine Lösung für Beispiel 2 und 3 finden.

Bearbeiten Sie die von ES6 geänderte Version , um das Zeitlimit für die Ausführung von JavaScript zu überschreiten. Puzzle 3 in 7 Minuten, 145 Schritten gelöst. Puzzle 2 gelöst in 10min, 116 Schritten

Bearbeiten Sie 2 Big speedup, indem Sie die Idee von @ orlp verwenden, gleich zwei Blöcke mit derselben Form zu betrachten (mit Ausnahme des speziellen Blocks '0'). Dies reduziert den Platz der besuchten Positionen während der BSF. Zum Beispiel ist für Puzzle 2 jede Position mit ausgetauschten Blöcken 1, 2, 3 oder 5 wirklich dieselbe.

Timing: Das längste ist Puzzle 3, ~ 20 Sek. Auf meinem Laptop.

Verwenden Sie Firefox, um mit dem neuen JsFiddle zu spielen und zu testen.

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

Veraltet

EcmaScript 6 (FireFox) JSFiddle

EcmaScript 5 (Chrome) JSFiddle

Beispiel

#######
#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
quelle
Können Sie zur Überprüfung Ihrer Lösung (und anderer Lösungen) die Anzahl der Züge angeben, die Sie für jedes Problem erhalten, das ich veröffentlicht habe?
Nathan Merrill