Ohne Dich demotivieren zu wollen, aber das, was Du da zeigst, hat mit (gutem) Programmieren noch nicht viel zu tun
Das kann man leicht überprüfen:
Was machst Du, wenn ich jetzt folgenden Dungeon haben will?
Code: Alles auswählen
'------'
'|.....|'
'|.....|'
'|...x.|'
'|.....|'
'------'
Du müsstest ja zig if-Konstrukte umschreiben!
Daran erkennst Du, dass Du nicht wirklich flexiblen, vom konkreten Problem unabhängigen Code geschrieben hast. Und genau das ist die wahre Kunst beim Programmieren!
Stelle Dir vor, Du schreibst eine Sortierfunktion, die folgendes Problem lösen kann: [4, 2, 6, 1]. Wäre doch doof, wenn die das Problem [10, 3, 43, 2, 5] nicht auch sortieren könnte, oder?
Dein Problem ist: Du hast keine (gute) Datenstruktur gefunden, dein Dungeon darzustellen. Ich denke da hapert es einfach bei Dir. Du musst Dich mit den grundlegenden Datenstrukturen in Python befassen. Du bindest nur Strings an nummerierte Namen und prüfst dann mit exakt auf die Höhe und Breite des Dungeons angepassten if-Statements und Funktionen, wie der neue Zustand aussehen muss.
Du musst Dir also eine Datenstruktur für Deinen Level ausdenken - diese ist zunächst absolut unabhängig von der späteren Darstellung! Mein Tipp: Schau Dir mal Listen an!
So könnte eine solche Struktur aussehen:
Code: Alles auswählen
In [13]: level = [
....: [1, 1, 1, 1, 1],
....: [1, 0, 0, 1, 1],
....: [1, 0, 1, 0, 1],
....: [1, 0, 1, 0, 1],
....: [1, 0, 0 ,0, 1],
....: [1, 1, 1, 1, 1]
....: ]
Ich habe einfach eine Liste mit Listen als Einträgen erstellt. "1" bedeutet, dass ein Feld nicht betreten werden kann, "0" bedeutet, das Feld kann betreten werden. Ok, sehr simpel für den Anfang, aber das reicht ja erst einmal.
Mit Hilfe von zwei Indexwerten kann ich nun abfragen, was sich hinter einem bestimmten Feld befindet:
Code: Alles auswählen
In [18]: level[0][0]
Out[18]: 1
In [19]: level[1][2]
Out[19]: 0
Dabei ist der erste Index mit der Y-Koordinate und der zweite mit der X-Koordinate auf einem Karopapier vergleichbar. Damit wiederum kann das Level nun beliebige Ausmaße haben. Ich muss nur die aktuelle Position meines Helden kennen und wissen, an welche Koordinate er ziehen will.
Letzteres kann ich berechnen: Will er nach "oben" gehen, muss ich vom Y-Wert meiner aktuellen Position einfach den Wert 1 abziehen (bei einem Schritt). Will er nach "rechts" gehen, so muss ich auf den X-Wert 1 drauf addieren.
Ich definiere, dass mein Held den Wert 2 haben soll. Damit kann ich ihn einfach mal an eine Position im Level setzen:
Code: Alles auswählen
In [20]: level[1][1] = 2
In [21]: level
Out[21]:
[[1, 1, 1, 1, 1],
[1, 2, 0, 1, 1],
[1, 0, 1, 0, 1],
[1, 0, 1, 0, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 1]]
Da ich die Position meines Helden ja kennen muss, merke ich mir das zusammen mit einem Namen mal in einer Struktur:
Code: Alles auswählen
In [22]: hero = ["Hyperion", 1, 1]
In [23]: hero[0]
Out[23]: 'Hyperion'
In [24]: hero[1]
Out[24]: 1
In [25]: hero[2]
Out[25]: 1
In [26]: hero[1] = 2
In [27]: hero
Out[27]: ['Hyperion', 2, 1]
Ich definiere für mich, dass zuerst die X und danach die Y-Koordinate in der `hero`-Liste steht. Will ich auf x, oder y zugreifen, muss ich also den Index 1 bzw. 2 verwenden.
Nun kann ich eine Funktion schreiben, die meinen Helden von seiner aktuellen Position auf eine neue bewegt:
Code: Alles auswählen
In [77]: def move(x, y, hero, level):
....: level[hero[2]][hero[1]] = 0
....: hero[1] += x
....: hero[2] += y
....: level[hero[2]][hero[1]] = 2
....:
....:
In [32]: hero[1] = 1
In [33]: level = [
[1, 1, 1, 1, 1],
[1, 0, 0, 1, 1],
[1, 0, 1, 0, 1],
[1, 0, 1, 0, 1],
[1, 0, 0 ,0, 1],
[1, 1, 1, 1, 1]
]
In [41]: move(0, 0, hero, level)
In [42]: level
Out[42]:
[[1, 1, 1, 1, 1],
[1, 2, 0, 1, 1],
[1, 0, 1, 0, 1],
[1, 0, 1, 0, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 1]]
In [43]: move(1, 0, hero, level)
In [44]: level
Out[44]:
[[1, 1, 1, 1, 1],
[1, 0, 2, 1, 1],
[1, 0, 1, 0, 1],
[1, 0, 1, 0, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 1]]
Zuerst habe ich meinen Helden wieder die Koordinaten (1, 1) gegeben, dann den Level "resettet". Wenn ich bei x und y jeweils 0 angebe, so wird der Held an seine aktuellen Koordinaten gesetzt. Gebe ich ein "delta" an, so bewegt sich der Held durch den Level

Ich kann die Bewegung als
berechnen und muss nicht endlose, fixe if-Statements schreiben, a la "wenn der held auf x=3 war, dann muss er nun x=4 haben. Wenn er x=4 war, dann muss er nun x=5 haben, ...", wie Du es zur Zeit machst. Das ist eben aufgrund der Indizierbarkeit von Listen möglich.
Wichtig bei der Funktion ist es, die Koordinaten des Helden neu zu berechnen und diese nicht nur im Level zu markieren, sondern sich diese auch in der `hero`-Liste zu setzen. (Hatte ich doch glatt vergessen zunächst

)
Es sind bei Werten größer / kleiner als (-1, 0, 1) auch "Sprünge" drin.
Bevor ich meinen Helden bewegen kann, muss ich ja feststellen, ob ein Feld betreten werden darf. Dazu kann ich eine Funktion schreiben, die für eine gegebene Koordinate in der Levelstruktur prüft, ob das Feld betreten werden darf.
Code: Alles auswählen
In [14]: def is_passable(x, y, level):
....: return level[y][x] == 0
....:
In [16]: is_passable(0, 0, level)
Out[16]: False
In [17]: is_passable(3, 2, level)
Out[17]: True
Bevor ich meine `move`-Funktion nun aufrufe, muss ich erst mal prüfen, ob der gewünschte Zug gültig ist:
Code: Alles auswählen
In [88]: walk_x, walk_y = 1, 0
In [89]: is_passable(hero[1] + walk_x, hero[2] + walk_y, level)
Out[89]: False
In [90]: walk_x, walk_y = -1, 0
In [91]: is_passable(hero[1] + walk_x, hero[2] + walk_y, level)
Out[91]: True
Das ganze verpacke ich nun in eine Funktion `do_move`. Diese prüft also zuerst, ob ein Zug möglich ist und führt danach die Bewegung durch:
Code: Alles auswählen
In [101]: def do_move(x, y, hero, level):
.....: if is_passable(hero[1]+x, hero[2]+y, level):
.....: print "gehe nach", hero[1] + x, hero[2] + y
.....: move(x, y, hero, level)
In [102]: level
Out[102]:
[[1, 1, 1, 1, 1],
[1, 2, 0, 1, 1],
[1, 0, 1, 0, 1],
[1, 0, 1, 0, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 1]]
In [103]: hero
Out[103]: ['Hyperion', 1, 1]
In [104]: do_move(1, 0, hero, level)
gehe nach 2 1
In [105]: do_move(1, 0, hero, level)
In [106]: do_move(0, 1, hero, level)
In [107]: do_move(-1, 0, hero, level)
gehe nach 1 1
In [108]: do_move(0, 1, hero, level)
gehe nach 1 2
In [109]: do_move(0, 1, hero, level)
gehe nach 1 3
In [110]: level
Out[110]:
[[1, 1, 1, 1, 1],
[1, 0, 0, 1, 1],
[1, 0, 1, 0, 1],
[1, 2, 1, 0, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 1]]
Man erkennt, dass die Bewegung an "Mauern" scheitert. Sinnvoller Weise hätte man noch eine Warnung ausgeben können, damit man beim Debuggen sieht, dass eine Bewegung nicht klappt und ggf. wieso nicht.
Das mal so auf die Schnelle. Vieles hier ist... naja, suboptimal. Zunächst einmal würde ich ein Objekt für einen Helden eher in einem Dictionary darstellen oder - was noch besser ist - in einer Klasse. Diese könnte dann die `move` und `do_move`-Methoden besitzen.
Integer als Feld-Typen sind ggf. auch eher suboptimal. Dann ist da noch die Frage, ob es verschiedene Ebenen im Level gibt. Also werden sich bewegende Objekte in einer anderen Liste gespeichert als starre. Gibt es für verschiedene Arten von Helden verschiedene Beschränkungen? (Ein Held kann schwimmen und damit wäre Wasser kein Problem mehr für ihn; für einen anderen Helden eben doch)
Du siehst, die zugrunde liegende Struktur ist einfach enorm wichtig.
Mein Ansatz soll nur ein Denkanstoß sein - nicht mehr und nicht weniger. Also sei nicht so töricht und kopiere Dir hier alles zusammen
