Laden...

Labyrinth mit der Breitensuche lösen

Erstellt von gizzmo25 vor 10 Jahren Letzter Beitrag vor 9 Jahren 12.616 Views
G
gizzmo25 Themenstarter:in
4 Beiträge seit 2013
vor 10 Jahren
Labyrinth mit der Breitensuche lösen

Hallo zusammen,

wir haben im Studium eine Aufgabe gestellt bekommen, an der ich gerade etwas verzweifle und ich hoffe ihr könnt mir diesbezüglich helfen.

Gegeben ist ein Labyrinth, das automatisch mit einer Breitensuche gelöst werden soll.

Der "Spielstein" wird in der Mitte platziert und von dort aus soll dann die Breitensuche beginnen. Das Spielfeld ist durch # begrenzt und auf dem Weg liegen ° die eingesammelt werden müssen. Das Spiel ist beendet, wenn alle ° eingesammelt sind.

Die Position des Spielsteins trage ich in eine Queue ein und durchlaufe danach eine while Schleife so lange, bis sie leer ist. In der Schleife wird immer das erste Koordinaten Paar aus der Queue gelöscht und in eine Hash Tabelle übertragen.

Zum Schluss habe ich dann eine Hash Tabelle, in der jedes Koordinaten Paar nur einmal vorkommt.


public void bSearch(int iAmount)
        {
            int[] result = new int[iAmount];
            Queue qList = new Queue(iAmount);

            Hashtable htList = new Hashtable();

            string sKoordinatenStart = ixC.ToString() + "," + iyC.ToString();
            string sKoordinatenInList = "";

            qList.Enqueue(sKoordinatenStart);
        
            while (qList.Count != 0)
            {
                // holt das erste Koordinatenpaar aus der Queue
                sKoordinatenInList = qList.Dequeue().ToString();
                
                string[] sarrKoordinaten = sKoordinatenInList.Split(',');                

                ixC = Convert.ToInt16(sarrKoordinaten[0]);
                iyC = Convert.ToInt16(sarrKoordinaten[1]);

                if (!(iarrFieldMirror[ixC, iyC - 1] == 1))
                {
                    iyC--;
                    if (!htList.ContainsKey(ixC.ToString() + "," + iyC.ToString()) && 
sKoordinatenStart != (ixC.ToString() + "," + iyC.ToString()))
                    {
                        qList.Enqueue(ixC.ToString() + "," + iyC.ToString());
                        htList.Add((object)ixC.ToString() + "," + iyC.ToString(), (object)sKoordinatenInList);
                        iyC++;
                        continue;
                    }
                    iyC++;
                }
                if (!(iarrFieldMirror[ixC, iyC + 1] == 1))
                {
                    iyC++;
                    if (!htList.ContainsKey(ixC.ToString() + "," + iyC.ToString()) && 
sKoordinatenStart != (ixC.ToString() + "," + iyC.ToString()))
                    {
                        qList.Enqueue(ixC.ToString() + "," + iyC.ToString());
                        htList.Add((object)ixC.ToString() + "," + iyC.ToString(), (object)sKoordinatenInList);

                        iyC--;
                        continue;
                    }
                    iyC--;
                }
                if (!(iarrFieldMirror[ixC - 1, iyC] == 1))
                {
                    ixC--;
                    if (!htList.ContainsKey(ixC.ToString() + "," + iyC.ToString()) &&
sKoordinatenStart != (ixC.ToString() + "," + iyC.ToString()))
                    {
                        qList.Enqueue(ixC.ToString() + "," + iyC.ToString());
                        htList.Add((object)ixC.ToString() + "," + iyC.ToString(), (object)sKoordinatenInList);

                        ixC++;
                        continue;
                    }
                    ixC++;
                }
                if (!(iarrFieldMirror[ixC + 1, iyC] == 1))
                {
                    ixC++;
                    if (!htList.ContainsKey(ixC.ToString() + "," + iyC.ToString()) && 
sKoordinatenStart != (ixC.ToString() + "," + iyC.ToString()))
                    {
                        qList.Enqueue(ixC.ToString() + "," + iyC.ToString());
                        htList.Add((object)ixC.ToString() + "," + iyC.ToString(), (object)sKoordinatenInList);
                        ixC--;
                        continue;
                    }
                    ixC--;
                }
            }

            Stack myStack = new Stack(htList.Count);

            // Stack füllen
            //for (int i = 0; i < htList.Count; i++)
            //  myStack.Push(htList[i]);

            foreach (string sValue in htList.Values)
            {
                myStack.Push(sValue);
            }
            PrintStack(myStack);


            //for (int i = 0; i < arrlKeys.Count; i++)
            //{
            //    if (sGetValue.Equals(String.Empty) || htList.ContainsKey(arrlKeys[i]))
            //    {
            //        sGetValue = htList[arrlKeys[i]].ToString();
            //    }
            //}
        }

So und jetzt kommt das Problem.
Wie kann ich jetzt das Labyrinth lösen?
In der Aufgabenstellung ist davon die Rede, dass man an diesem Punkt einfach Rückwärts durch die Hash Tabelle zurück zum Ausgangspunkt müsse und hätte somit den optimalen Weg gefunden, was ich jedoch nicht nachvollziehen kann, denn sobald ich an eine Stelle komme, an der ich schon mal durchgelaufen bin, bleibt das Spiel hängen.

Ich hoffe ich konnte mich einigermassen verständlich ausdrücken.

Danke und viele Grüsse

P
660 Beiträge seit 2008
vor 10 Jahren

Hallo,

vllt hilft dir ja der Beitrag und dessen Antwort weiter

Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch

MfG
ProGamer*Der Sinn Des Lebens Ist Es, Den Sinn Des Lebens Zu Finden! *"Wenn Unrecht zu Recht wird dann wird Widerstand zur Pflicht." *"Ignorance simplifies ANY problem." *"Stoppt die Piraterie der Musikindustrie"

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo gizzmo25,

Pacman lässt grüßen. 😃

Dadurch, dass nicht nur ein Weg von A nach B gesucht wird, sondern alle Futterstücke "gegessen" werden müssen, ist das nicht so ganz der Fall einer klassischen Breitensuche. Denn wenn es zwei verschiedene Wege gibt vom Start zu einem Punkt X gibt, reicht es nicht von Punkt X aus gemeinsam weiterzusuchen, sondern man muss für jeden Weg, der zu X führt separat weitersuchen (zumindest wenn die Menge der auf beiden Wegen gefressenen Futterstücke sich unterscheidet).

Daher würde ich den Ansatz, dass in der Hashtable (besser: Dictionary<>) jedes Koordinatenpaar nur einmal vorkommt, in Zweifel ziehen.

herbivore

G
gizzmo25 Themenstarter:in
4 Beiträge seit 2013
vor 10 Jahren

Hallo ProGamer,

hm... vielen Dank jetzt hab ich dort etwas gesehen, was mir evtl. für später hilft, jedoch das Problem, das ich habe beschreibt er schön im letzten Satz.

Wenn es zwei Wege gibt, werden zwar beide eingezeichnet, die Länge wird allerdings nur von einem zurückgegeben.


Hallo herbivore,

vielen Dank für deine Antwort.
Der Meinung bin ich eben auch und daher meine Zweifel.

Leider wird das in der Aufgabenstellung aber explizit verlangt.

Hinweis von herbivore vor 10 Jahren

Text der Aufgabe entfernt, da fraglich ist, ob das Recht zur Veröffentlichung bestand.

G
gizzmo25 Themenstarter:in
4 Beiträge seit 2013
vor 10 Jahren

Ich hab mal versucht das Spiel in einen Baum zu übertragen, um es mir besser vorstellen zu können. (Aufgrund des Umfangs habe ich es nicht komplett aufgezeichnet).

Wenn eine Koordinate ein zweites mal auftritt, ist der Ast zu ende.

dort sieht man finde ich auch schön, dass es mit der Breitensuche meiner Meinung nach nicht so wirklich funktionieren kann.

Sehe ich das richtig?

viele Grüsse
Marco

T
3 Beiträge seit 2014
vor 9 Jahren

Witzig, das ist meine Aufgabe, die es ins Internet geschafft hat.

Gut, ich kann beruhigt sein, die "Lösung" von oben ist höchstens ein Beispiel wie man es nicht machen sollte (allein die Strings und Stringarrays ??? Was zum Geier?)
Das einzig gute an der Lösung ist, dass sie in allen vier Richtungen irgendwas macht, immerhin.
Die Visualisierung ist gelungen, das kann ich auch noch positiv vermerken.

Ich glaube, Gizmo wie auch viele andere, die diese Aufgabe problematisch fanden, haben eine Sache nicht verstanden:

Auf dem Blatt steht ein wichtiger Satz wie:

"Fall ein Item gefunden wurde ist die Breitensuche fertig und bricht ab"

und den überlesen entweder viele, oder sie ignorieren ihn oder sie gehen mit völlig falschen Annahmen an die Sache ran. Oder ich habe die Aufgabe mistig formuliert, ist auch möglich (falls dann jemand einen Hinweis hat, wie es besser geht, nur zu !).
Die Schleife von oben bricht zumindest nicht ab sobald ein Item gefunden wird.

Man soll das Labyrinth nämlich schrittweise abarbeiten, d.h. ich suche das erste Item, laufe da hin.
Von dort aus suche ich das nächste item, laufe da hin. Dann suche ich das übernächste und laufe da hin. Usw., bis das Labyrinth irgendwann leer gefressen ist.

Die falsche Annahme, von der einige hier ausgingen, auch gizzmo vermutlich, ist dass man das Labyrinth auf einmal in einem Schritt mit einer einzigen Suche, einem Baum whatever leersuchen muss. Das geht natürlich nicht! Wer's trotzdem so lösen konnte, und das Labyrinth wurde fix geleert, kein Feld doppelt betreten oder so, Glückwunsch, vermutlich haben Sie gerade das Traveling Salesman Problem in polynomialer Zeit gelöst! (ist Quatsch, das wird nicht gelingen)

Insofern ist auch der Satz

Denn wenn es zwei verschiedene Wege gibt vom Start zu einem Punkt X gibt, reicht es nicht von Punkt X aus gemeinsam weiterzusuchen, sondern man muss für jeden Weg, der zu X führt separat weitersuchen (zumindest wenn die Menge der auf beiden Wegen gefressenen Futterstücke sich unterscheidet).

irreführend. Darum geht's hier doch gar nicht (stand auch übrigens mit keinem Wort auf dem Blatt)

Schrittweise ist's aber überhaupt kein Problem: solange noch mind. ein Item im Labyrinth liegt, findet die Breitensuche das Item. Sie findet zudem immer das nächste Item zum Player (was natürlich je nach Situation mehrere sein könnten, dann wird aber einfach das erste in der Queue genommen, bzw. die Suche bricht ja schon ab, sobald auch nur E I N Item gefunden wurde).

Ansonsten, die Aufgabe mit einer Queue, einer Hashtable und einem Stack zu lösen war ein Vorschlag, wie es einigermaßen Brainless geht. Kann man auch variieren, z.B. statt der Hashtable Pointer verwenden.

Und noch was: ab jetzt werde ich höllisch aufpassen was hier passiert und wenn ich eine Lösung der Aufgabe bekomme, die aus dem Internet stammt, ist sie ungültig.

  • thomas
F
10.010 Beiträge seit 2004
vor 9 Jahren

Wir versuchen eigentlich immer zu vermeiden das wir irgendjemandem die Hausaufgaben machen.
Aber es gibt immer mal welche die uns durchrutschen, wo jeamnd dann doch die Arbeit macht. Ist aber definitiv nicht gewollt.

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo thomas@friendlyvillagers,

wir erlauben jedoch, dass jemand konkrete Fragen zu seinen (Haus-)Aufgaben stellt, solange die sich beantworten lassen, ohne den Lerneffekt der Aufgabe zu zerstören.

wenn ich eine Lösung der Aufgabe bekomme, die aus dem Internet stammt, ist sie ungültig.

Dabei solltest du unterscheiden, ob derjenige, der den Code gepostet hat, auch der ist, der ihn bei dir einreicht. In diesem Thread stammt aller Code vom Fragesteller. Wenn er diesen (nachweislich eigenen) Code später bei dir einreicht, sollte das ok sein. Problematisch ist nur, wenn sich jemand stillschweigend fremden Code zu eigen macht.

Wenn du pauschal sagen würdest, dass Code, der im Internet zu finden ist, nicht verwendet werden darf, nimmst du den Lernenden die Möglichkeit, im Netz Fragen zu ihrem selbst erstellten Code zu stellen.

Insofern ist auch der Satz ... irreführend.

Bezogen auf die eigentliche Aufgabe vielleicht. Aber der Satz bezog sich ja auf das, was der Thread-Ersteller gefragt hatte. Den Text deiner Aufgabe kannte ich zu diesem Zeitpunkt nicht. Bezogen auf deine Aufgabe kommt der Satz einfach nicht zum Tragen, weil in dem Moment, wo er relevant werden würde, die Suche laut Aufgabe sowieso abgebrochen wird.

herbivore

T
3 Beiträge seit 2014
vor 9 Jahren

Ja klar, Fragen stellen ist natürlich OK. Dann Sorry, das habe ich wohl zu drastisch formuliert.
Dennoch sollte sich niemand von anderen die Aufgabe lösen lassen, wenn's irgendwie geht. 😃
Damit betrügt man sich am Ende doch nur selbst und hat nichts davon.

  • thomas
P
1.090 Beiträge seit 2011
vor 9 Jahren

Ich muss grade etwas schmunzeln, weil ich da an mein Studium denken muss (ist ca. 15 Jahre her).
Bei einer Mathe Hausaufgabe, hatte ich Probleme und hab dann nach den Grundlagen im Internet gesucht. Was mich zu einer Musterlösung der Aufgabe führte.
Ich hab dann als Lösung, den Link angegeben. Mit grob der Aussage: „Tut mit leid aber beim Einarbeiten, bin ich im Internet auf die Lösung gestoßen. Zum Abschreiben bin ich zu Faul, aber ich bin sicher die Lösung ist Korrekt.“
Ich hab vom Prof. die volle Punktzahl für die Aufgabe bekommen und noch ein Lob das ich das Internet benutze um mich weiterzubilden.

MFG
Björn

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

G
gizzmo25 Themenstarter:in
4 Beiträge seit 2013
vor 9 Jahren

Hallo thomas@friendlyvillagers,

dafür, dass es ein Beispiel ist, wie man es nicht machen sollte, wurde die Arbeit aber erstaunlich gut bewertet ^^

Hier ging es auch nicht darum wer schreibt den schönsten und effizientesten Code, sondern um das lösen des Labyrinths, so weit ich mich erinnern kann.

Bei deiner Aufgabenstellung gab es meiner Meinung nach mehrere Probleme.
Du hast so detailliert beschrieben was du gerne sehen würdest, dass ich der Meinung war, du würdest genau das auch im Code erwarten. Auf meine Nachfrage hin hast du dann noch mehr Verwirrung gestiftet in dem du dann auf einmal von der Tiefensuche gesprochen hast.

Einfacher wäre es meiner Meinung nach, wenn du solche "Hinweise" wie das Lösen des Problems mit der Breitensuche, einfach weg lassen würdest, wenn du es nicht explizit auch im Code sehen möchtest. Oder aber du gibst zumindest an, wo man Informationen über einen solchen Algorithmus herbekommen kann, wenn du nicht willst, dass wir hier in solchen Foren nach Hilfe suchen.

Das nächste Problem ist, dass du davon ausgehst, dass alle Studenten unseres Studiengangs Hardcore Programmierer sind. Ich programmiere zwar schon seit 14 Jahren mit C#, jedoch seit Jahren nicht mehr allzu regelmässig, da es sich leider nicht mit meiner Arbeit deckt. Die meissten meiner Mitstudenten haben jedoch noch nie, bzw. relativ wenig programmiert und viele von ihnen werden nach dem Studium auch nicht mehr programmieren (was ja auch der Grund ist, warum wir nicht angewandte Informatik gewählt haben) und dafür war die Aufgabe wirklich knackig...

Einen Vorteil hat dein Post... jetzt habe ich die Aufgabe endlich verstanden ^^

Falls ein Item gefunden wurde ist die Breitensuche fertig und bricht ab

Evtl. könntest du den Satz wie folgt ergänzen:
"Falls ein Item gefunden wurde ist die Breitensuche fertig, bricht ab und beginnt an der zuletzt gefundenen Stelle von Neuem" (Rekursion!)

Sei es drum... ich hab es geschafft und bin zufrieden mit meiner Note ^^

viele Grüsse...

Hinweis von herbivore vor 9 Jahren

Wenn ein Vorgang (hier: Bereitensuche bis zum nächstliegenden Futterstück) abgeschlossen wurde und anschließend in der eingetretenen Situation (hier: Futter gefressen) neu beginnen soll, ist das eher ein Fall für Iteration statt für Rekursion (Stichwort: Endrekursion). Mal ganz abgesehen davon, dass die Fortsetzung gerade nicht Teil der Aufgabe war. Aber das nur am Rande. Auch an anderen Punkten geht deine Argumentation etwas an den Fakten vorbei. Um eine Breitensuche zu programmieren muss man nun sicher kein Hardcore-Programmierer sein. Und natürlich hat "guter" Code einen Nutzen gegenüber nur funktionierendem schlechten. Sei es drum. Jetzt hat jeder der Beteiligten seinen Standpunkt und seine Sichtweise dargelegt. Der Leser kann sich selbst ein Bild machen. Daher lasst uns das Offtopic genau hier beenden!