Problem mit Kampfskript

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Hallo,

ich bin noch ein ziehmlicher Anfänger und habe da gerade ein Problem:

bei einen Skript, dass Kämpfe einfachster Art lösen soll, ist irgendwo ein Fehler. Ich finde ihn nicht. Hier das Skript:

Code: Alles auswählen

def fight():
    while a <= k.Kampfliste1[-1]:
        if k.Kampfliste1==[]:
            del k.Kampfliste1[0:-1]
            del k.Kampfliste3[0:-1]
            break
        else:
            k.Kampfliste1[a]=k.Kampfliste1[a]-k.Kampfliste4[a]
            k.Kampfliste3[a]=k.Kampfliste3[a]-k.Kampfliste2[a]
        if k.Kampfliste1[a] <= 0:
            del k.Kampfliste1[a]
        if k.Kampfliste3[a] <= 0:
            del k.Kampfliste3[a]
        a+=1
        print k.Kampfliste1,k.Kampfliste2,k.Kampfliste3,k.Kampfliste4
in Kampfliste1 sind die LP eines Kämpfers von Team1 gespeichert, in Kampfliste 2 der Schaden eines Kämpfers von Team1. Kampfliste3 und 4 speichern das gleiche nur für TEam 2.

Ich bedanke mich schon mal im vorraus.

Bamba

Edit (Leonidas): Code in Python-Tags gesetzt.
Bamba
BlackJack

Was ist denn das Problem? Zufällig ein `NameError` das `a` oder `k` nicht gefunden werden?

Drück mal in Worten aus was die ``while``-Bedingung prüfen soll und das was sie *jetzt* gerade prüft. Ich habe den Verdacht das ist was anderes.

Wenn man ein Element in einer Liste löscht, dann rücken alle danach einen Listenplatz nach vorne, also wird auch ihr Index um eins kleiner. Falls Du einen `IndexError` bekommst und scheinbar nur jeder zweite Kämpfer berücksichtigt wird, dann solltest Du da mal darüber nachdenken.

Und warum kannst Du den einzelnen Listen nicht gleich vernünftige Namen geben? So muss man immer auf die Ziffer am Ende schauen und nachdenken wofür die eigentlich steht.

Wenn man mehrere Listen auf diese Weise parallel behandelt, dann ist das ein Zeichen das man eigentlich eine Liste haben möchte mit Zusammengesetzten Werten, also in diesem Fall eine Liste mit einem Eintrag pro Kämpfer der LP *und* Schadenspunkte enthält.

Falls ich die Kampfregeln richtig interpretiert habe (ungetestet):

Code: Alles auswählen

class Fighter(object):
    def __init__(self, life, damage):
        self.life = life
        self.damage = damage
    
    def is_dead(self):
        return self.life <= 0


def fight(team_a, team_b):
    while team_a and team_b:
        fighter_a = team_a[0]
        fighter_b = team_b[0]
        fighter_a.life -= fighter_b.damage
        fighter_b.life -= fighter_a.damage
        if fighter_a.is_dead():
            team_a.pop(0)
        if fighter_b.is_dead():
            team_b.pop(0)
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Hallo,

ich habe das einfach in diese vier Listen aufgeteilt, warum weiß ich auch nicht. Eigentlich soll es ungefähr so ablaufen: Das Element a der 1. LIste wird um das Element a der 4. Liste verringert, genauso bei der 3. und 2. Liste.

Wenn ich die Funkltion das erste mal laufen lasse, erhalte ich folgende Ausgabe:
[2, 3, 3] [1, 1, 1] [3, 4, 4] [1, 1, 1]
[2, 2, 3] [1, 1, 1] [3, 3, 4] [1, 1, 1]
[2, 2, 2] [1, 1, 1] [3, 3, 3] [1, 1, 1]
Danach:
[1, 2, 2] [1, 1, 1] [2, 3, 3] [1, 1, 1]
[1, 1, 2] [1, 1, 1] [2, 2, 3] [1, 1, 1]
[1, 1, 1] [1, 1, 1] [2, 2, 2] [1, 1, 1] was auch noch richtig ist
aber danach:
[1, 1] [1, 1, 1] [1, 2, 2] [1, 1, 1]
[1] [1, 1, 1] [1, 1, 2] [1, 1, 1]
Hier sollte LIste 1 eigentlich komplett leer sein, während Liste 3 nur noch 1sen haben dürfte.
Und wenn ich dann noch laufen lasse, erhatle ich IndexError: list index out of range. WAs heißt, dass meine if-Abfrage vollkommen falsch ist, aber ich kapier das irgendwie nicht.

Bamba
Bamba
BlackJack

Bamba hat geschrieben:ich habe das einfach in diese vier Listen aufgeteilt, warum weiß ich auch nicht. Eigentlich soll es ungefähr so ablaufen: Das Element a der 1. LIste wird um das Element a der 4. Liste verringert, genauso bei der 3. und 2. Liste.

Wenn ich die Funkltion das erste mal laufen lasse, erhalte ich folgende Ausgabe:
[2, 3, 3] [1, 1, 1] [3, 4, 4] [1, 1, 1]
[2, 2, 3] [1, 1, 1] [3, 3, 4] [1, 1, 1]
[2, 2, 2] [1, 1, 1] [3, 3, 3] [1, 1, 1]
Na dann habe ich schonmal die Kampfregeln falsch interpretiert. :-)
Danach:
[1, 2, 2] [1, 1, 1] [2, 3, 3] [1, 1, 1]
[1, 1, 2] [1, 1, 1] [2, 2, 3] [1, 1, 1]
[1, 1, 1] [1, 1, 1] [2, 2, 2] [1, 1, 1] was auch noch richtig ist
aber danach:
[1, 1] [1, 1, 1] [1, 2, 2] [1, 1, 1]
[1] [1, 1, 1] [1, 1, 2] [1, 1, 1]
Hier sollte LIste 1 eigentlich komplett leer sein, während Liste 3 nur noch 1sen haben dürfte.
Und wenn ich dann noch laufen lasse, erhatle ich IndexError: list index out of range. WAs heißt, dass meine if-Abfrage vollkommen falsch ist, aber ich kapier das irgendwie nicht.
Das sie nicht leer ist liegt daran das Du das letzte Element mit -1 nicht erwischt:

Code: Alles auswählen

In [30]: a = range(5)

In [31]: a
Out[31]: [0, 1, 2, 3, 4]

In [32]: a[0:-1]
Out[32]: [0, 1, 2, 3]

In [33]: del a[0:-1]

In [34]: a
Out[34]: [4]
Wenn Du die Grenzen einfach weglässt, dann wird die gesamte Liste geleert:

Code: Alles auswählen

In [35]: a = range(5)

In [36]: a
Out[36]: [0, 1, 2, 3, 4]

In [37]: del a[:]

In [38]: a
Out[38]: []
Aber kommen wir nochmal auf den Anfang zurück: Was *soll* die ``while``-Bedingung prüfen und was prüft sie in Wirklichkeit?

Und nochmal: wenn Du ein Element in einer Liste löschst, dann rücken alle nachfolgenden eins nach vorne. Wenn Du `a` erhöhst, dann hast Du einen Kämpfer übersprungen, der ja jetzt an die Stelle des verstorbenen getreten ist.

Und was macht Deine Funktion wenn das zweite Team komplett tot ist? Das sieht auch nach `IndexError` aus.

Wenn Du immer den n. Kämpfer aus Team A gegen den n. Kämpfer aus Team B antreten lassen willst, dann ist die sauberste Methode wohl die jeweiligen Partner mit der `zip()` Funktion zu paaren. Diesmal getestet:

Code: Alles auswählen

class Fighter(object):
    def __init__(self, life, damage):
        self.life = life
        self.damage = damage
    
    def __repr__(self):
        return 'Fighter(%r, %r)' % (self.life, self.damage)
    
    def is_dead(self):
        return self.life <= 0


def filter_dead_fighters(fighters):
    return [fighter for fighter in fighters if not fighter.is_dead()]


def fight(team_a, team_b):
    for fighter_a, fighter_b in zip(team_a, team_b):
        fighter_a.life -= fighter_b.damage
        fighter_b.life -= fighter_a.damage
    return map(filter_dead_fighters, (team_a, team_b))


def main():
    team_a = [Fighter(life, damage)
              for (life, damage) in ((2,1), (3, 1), (3, 1))]
    team_b = [Fighter(life, damage)
              for (life, damage) in ((3,1), (4, 1), (4, 1))]
    
    while team_a and team_b:
        print 'A:', team_a
        print 'B:', team_b
        print '-' * 20
        team_a, team_b = fight(team_a, team_b)

    if team_a:
        winner_name = 'A'
        winner_team = team_a
    else:
        winner_name = 'B'
        winner_team = team_b
    print 'Winner is %s: %r' % (winner_name, winner_team)


if __name__ == '__main__':
    main()
Ausgabe ist:

Code: Alles auswählen

A: [Fighter(2, 1), Fighter(3, 1), Fighter(3, 1)]
B: [Fighter(3, 1), Fighter(4, 1), Fighter(4, 1)]
--------------------
A: [Fighter(1, 1), Fighter(2, 1), Fighter(2, 1)]
B: [Fighter(2, 1), Fighter(3, 1), Fighter(3, 1)]
--------------------
A: [Fighter(1, 1), Fighter(1, 1)]
B: [Fighter(1, 1), Fighter(2, 1), Fighter(2, 1)]
--------------------
Winner is B: [Fighter(1, 1), Fighter(2, 1)]
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Vielen Dank. ICh werde das gleich ausprobieren.

Bamba
Bamba
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

VIelen Dank, das funktioniert tadellos! Jedoch stellt sich da ein weiteres Problem auf: Ich habe eine Funktion vorgesehen, in der man sich einen Kämpfer ausbilden kann, dieser wird nach meiner alten Weise an eine LIste angehängt. Und mit einer Funkltion namens verteilen() (falls man sich entschließt ihn in dem Kampf zuschicken) an meine alte Kampfliste angehängt. Wie muss ich den Code jetzt umschreiben, wenn die Funkltion verteilen() aus einer Liste von Kämpfern einen nimmt und ihn an die main() Funktion (siehe oben) anhängt.
Es bringt jetzt nichts den Code zu posten, da er komplett auf den alten Funktionen beruht.
Bamba
Bamba
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

So, mit

Code: Alles auswählen

def new_fighter():
    k=Fighter(3,1)
    Liste1.append(k)
    print Liste1
wird ein neuer Kämpfer erstellt. Und (wenn man ihn in dem Kampf schicken will) mit

Code: Alles auswählen

def verteilen():
    Wahl=raw_input("Wie viele Kämpfer sollen in den Kampf: ")
    team_b.extend(Liste1[0:Wahl])
in den Kampf geschickt. Warum funktioniert verteilen() nicht und wie kann ich gleichzeitig die in den Kampf geschickten KÄmpfer aus Liste1 löschen?

Vielen Dank

Bamba
Zuletzt geändert von Bamba am Dienstag 31. Oktober 2006, 14:58, insgesamt 2-mal geändert.
Bamba
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Hallo noch mal,

ich hab noch ein paar Sachen umgeändert, erhalte aber trotdem Fehler:

verteilen() funktioniert jetzt. Allerdings erhalte ich jetzt bei dem etwas umgeänderten mein() part eine Fehlermeldung:

Code: Alles auswählen

def main():
    while team_a and team_b:
        print 'A:', team_a
        print 'B:', team_b
        print '-' * 20
        team_a, team_b = fight(team_a, team_b)

    if team_a:
        winner_name = 'A'
        winner_team = team_a
    else:
        winner_name = 'B'
        winner_team = team_b
    print 'Winner is %s: %r' % (winner_name, winner_team)

if __name__ == '__main__':
    global team_a
    team_a = [Fighter(life, damage)
              for (life, damage) in ((2,1), (3, 1), (3, 1))]
    global team_b
    team_b = [Fighter(life, damage)
              for (life, damage) in ((3,1), (4, 1), (4, 1))]
Hat jemand eine Ahnung, woran das liegt?

Bamba
Zuletzt geändert von Bamba am Dienstag 31. Oktober 2006, 14:57, insgesamt 2-mal geändert.
Bamba
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

Du hast vergessen, die Fehlermeldung mitzuteilen (und die Pythontags).
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

UnboundLocalError: local variable 'team_a' referenced before assignment

WAs meinst Du mit Pythontags?

Bamba
Bamba
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

mit pythontags meinte ich, dass du, damit der code hier im forum syntaxhighlited und korrekt eingerückt wird, diese BB-tag-geschichte nutzt.

Es ist meist günstiger, wenn du die ganze Fehlermeldung, so mit Traceback und so, angbist, zumindest aber mit der Zeilenzahl.

Die Fehlermeldung bedeutet, dass du lesend auf eine variable zugreifst, die noch nicht existiert. Wenn du die Zeile sagst, kann man da sicher mehr rausfinden.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Bamba hat geschrieben:WAs meinst Du mit Pythontags?
Die Python-Tags die der Leonidas in deinem ersten Posting hinzugefügt hat, und die du übersehen hast. Du hast vielleicht gemerkt, dass wenn du den Code einfach nur so reinklatscht, die Formatierung weg ist und damit der Programm nicht mehr funktioniert.

Also bitte deine Posts nochmal editieren und Code-Tags hinzufügen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Also hier noch mal der Code:

Code: Alles auswählen

class Fighter(object):
    def __init__(self, life, damage):
        self.life = life
        self.damage = damage
   
    def __repr__(self):
        return 'Fighter(%r, %r)' % (self.life, self.damage)
   
    def is_dead(self):
        return self.life <= 0

def nSoldatLevel1():
        k=Fighter(3,1)
        Liste1.append(k)
        print Liste1
        menue()

def verteilen():
    print len(Liste1),"Kaempfer stehen zur Verfuegung"
    Wahl=input("Wahl: ")
    team_b.extend(Liste1[0:Wahl])
            
def filter_dead_fighters(fighters):
    return [fighter for fighter in fighters if not fighter.is_dead()]

def fight(team_a, team_b):
    for fighter_a, fighter_b in zip(team_a, team_b):
        fighter_a.life -= fighter_b.damage
        fighter_b.life -= fighter_a.damage
    return map(filter_dead_fighters, (team_a, team_b))


def main():
    while team_a and team_b:
        print 'A:', team_a
        print 'B:', team_b
        print '-' * 20
        team_a, team_b = fight(team_a, team_b)

    if team_a:
        winner_name = 'A'
        winner_team = team_a
    else:
        winner_name = 'B'
        winner_team = team_b
    print 'Winner is %s: %r' % (winner_name, winner_team)

if __name__ == '__main__':
    global team_a
    team_a = [Fighter(life, damage)
              for (life, damage) in ((2,1), (3, 1), (3, 1))]
    global team_b
    team_b = [Fighter(life, damage)
              for (life, damage) in ((3,1), (4, 1), (4, 1))]
Zuletzt geändert von Bamba am Dienstag 31. Oktober 2006, 14:59, insgesamt 1-mal geändert.
Bamba
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Leonidas hat geschrieben:Also bitte deine Posts nochmal editieren und Code-Tags hinzufügen.
Nein, ich sag nix mehr -.-
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

und jetzt vielleich noch die genaue Fehlermeldung, mit zeile und traceback, und man könnte dir vielleicht sogar helfen.
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Entschuldigt:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Dokumente und Einstellungen\Sebastian\Desktop\Programmieren\sadf.py", line 99, in ?
    menue()
  File "C:\Dokumente und Einstellungen\Sebastian\Desktop\Programmieren\sadf.py", line 87, in menue
    main()
  File "C:\Dokumente und Einstellungen\Sebastian\Desktop\Programmieren\sadf.py", line 58, in main
    while team_a and team_b:
UnboundLocalError: local variable 'team_a' referenced before assignment
Bamba
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

das Problem scheint in der Art des Aufrufes des Programmes zu bestehen.

In deinem Traceback ist eine Funktion menue(), die in deinen Listings nicht auftaucht. Wenn du das Programm nicht mit "python sadf.py" startest (oder deine IDE was vergleichbares tut), sondern du z.B. eine andere Datei aufrufst, die

Code: Alles auswählen

from sadf import main
main()
macht, ist __name__ != '__main__', was bedeutet, dass deine globalen Variablen team_a/b nicht gesetzt werden.

Generell bin ich der Meinung, du solltest nicht eine Funktion main haben, die globale Variablen als Teams nutzt, sondern eine Funktion mainfight(team_a, team_b), also eine Funktion, die die Teams als Argumente übergeben bekommt. Globale Variablen sind imho meist ein Zeichen für schlechtes Design.
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Darin besteht das Problem glaube ich nicht. Die Funktion ist in der gleichen Datei, ich hatte sie nur noicht hier gepostet. Wenn ich eine Funkliton habe, der die Werte TEam_a und Team_b übergeben werden, müsste es doch:

Code: Alles auswählen

def fight(team_a, team_b):
heißen, oder?

Und wenn ich dann mit

Code: Alles auswählen

f __name__ == '__main__':
    team_a = [Fighter(life, damage)
              for (life, damage) in ((2,1), (3, 1), (3, 1))]
    team_b = [Fighter(life, damage)
              for (life, damage) in ((3,1), (4, 1), (4, 1))]
team_a und team_b festlege, bekomme ich folgenden fehler:

Code: Alles auswählen

line 87, in menue
    fight()
TypeError: fight() takes exactly 2 arguments (0 given)
Sorry, dass ich das nicht verstehe, aber ich bin noch Anfänger.

Bamba
Bamba
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Der TypeError resultiert daraus, dass du beim Aufruf der Funktion nicht so viele Parameter übergibst, wie sie erwartet (offenbar zwei). Der Aufruf lautet deinefunktion(einwert, einandererwert), also in deinem Fall wohl fight(team_a, team_b).
BlackJack

Bamba hat geschrieben:Also hier noch mal der Code:
Da fehlt wie schon jemand bemerkt hat die `menue()`-Funktion. Ich sehe auch gar nicht wie das als Programm überhaupt irgendetwas macht weil der Teil in der ``if __name__ ...`` Abfrage nur die beiden Listen mit Kämpfern erstellt.

Code: Alles auswählen

def nSoldatLevel1():
        k=Fighter(3,1)
        Liste1.append(k)
        print Liste1
        menue()
Hier fehlt vollkommen die Information wo `Liste1` herkommt. Damit das hier funktioniert muss die irgendwo auf Modulebene mal definiert worden sein. Das ist aber furchtbar unübersichtlich, darum macht man so etwas nicht. Auf Modulebene sollten soweit wie möglich nur Konstanten definiert werden. Alles andere sollte in spezifischeren Namensräumen stecken und Werte sollten Funktionen über Argumente betreten und als Rückgabewerte verlassen. Dann lässt sich ein Programm viel einfacher nachvollziehen und verstehen.

Wenn man mal den Aufruf von `menue()` weglässt, dann kann man den Aufruf dieser Funktion auch einfach durch ein ``Liste1.append(Fighter(3, 1))`` ersetzen. Das ist ein bischen zu einfach um extra einen Namen dafür zu vergeben. IMHO.

Code: Alles auswählen

def verteilen():
    print len(Liste1),"Kaempfer stehen zur Verfuegung"
    Wahl=input("Wahl: ")
    team_b.extend(Liste1[0:Wahl])
Hier auch wieder: `Liste1` und `team_b` sollten als Argumente in die Funktion kommen. Dann lässt sich diese Funktion auch für beide Teams verwenden.

Wie man die dann aus `Liste1` löscht solltest Du aber eigentlich schon selbst können.

Code: Alles auswählen

def filter_dead_fighters(fighters):
    return [fighter for fighter in fighters if not fighter.is_dead()]

def fight(team_a, team_b):
    for fighter_a, fighter_b in zip(team_a, team_b):
        fighter_a.life -= fighter_b.damage
        fighter_b.life -= fighter_a.damage
    return map(filter_dead_fighters, (team_a, team_b))


def main():
    while team_a and team_b:
        print 'A:', team_a
        print 'B:', team_b
        print '-' * 20
        team_a, team_b = fight(team_a, team_b)

    if team_a:
        winner_name = 'A'
        winner_team = team_a
    else:
        winner_name = 'B'
        winner_team = team_b
    print 'Winner is %s: %r' % (winner_name, winner_team)

if __name__ == '__main__':
    global team_a
    team_a = [Fighter(life, damage)
              for (life, damage) in ((2,1), (3, 1), (3, 1))]
    global team_b
    team_b = [Fighter(life, damage)
              for (life, damage) in ((3,1), (4, 1), (4, 1))]
Das ``global``, dessen blosse Existenz Du am besten sofort komplett vergisst, hat an dieser Stelle keine Wirkung. Das macht nur innerhalb von Funktionen Sinn.

Zu der Namensgebung: Ich ging beim ersten Versuch ja davon aus, das in `fight()` der komplette Kampf bis zum bitteren Ende abgewickelt wird. Von den jetzt implementierten Kampfregeln wäre der Name `fight()` für die `main()` passender und die alte `fight()` würde eher `round()` heissen.
Antworten