Seite 2 von 3
Re: Textadv "FinalF"
Verfasst: Sonntag 14. April 2013, 12:48
von BlackJack
@kevind: Ich hatte doch Beispiele gegeben wo Funktionalität und damit Wissen in der falschen Klasse (oder Funktion) stecken. Zum Beispiel beim „Cooldown”. Das Spiel sollte nur wissen dass es so etwas gibt und den `Character` bitten das durchzuführen, der `Character` sollte diese Bitte an die einzelnen `Attack`-Objekte weitergeben. Ähnlich beim ermitteln einer zufälligen Attacke. Hier mal skizziert wie man das aufteilen kann:
Code: Alles auswählen
class Attack(object):
# ...
def cooldown(self):
self.cooldown_counter -= self.cooldown
def is_usable(self):
return self.cooldown_counter <= self.cooldown
# ...
class Character(object):
# ...
def cooldown(self):
for attack in self.attacks:
attack.cooldown()
def get_usable_attacks(self):
return [attack for attack in self.attacks if attack.is_usable()]
def get_random_attack(self):
return random.choice(self.get_usable_attacks())
# ...
class Game(object):
# ...
def battle(self, opponent):
# ...
self.player.cooldown()
# ...
attack = opponent.get_random_attack()
# ...
# ...
Jetzt kann man in der Attacke ändern wie „Cooldown” funktioniert oder was benutzbar genau bedeutet ohne dass man in `Character` oder `Game` etwas ändern muss. Genau so kann man in `Character` die Angriffe in einer Liste, in einem Wörterbuch, oder in einem eigenen Datentyp verwalten, ohne das `Game` wissen muss, wie das genau gemacht wird.
Re: Textadv "FinalF"
Verfasst: Dienstag 16. April 2013, 14:47
von kevind
@BlackJack
ich bin jetzt wieder ein stück weiter...
Wie stellst du dir das mit den Konstanten vor ? Zahlen ? Steh grad (oder schon länger seit ich darüber nachdenke

) aufm Schlauch
Das heisst, das object "opponent" wenn es besiegt wurde "löschen" und dann neu in einer besseren variante instanzieren ?
Die unterschiede zu "is" und "==" werde ich noch recherchieren...
Ich glaube jetzt habe ich auch das mit dem "Wissen" der Klassen gut im Griff. Vl. könntest du mir dazu noch ein kurzes feedback geben.
Danke vielmals !
Re: Textadv "FinalF"
Verfasst: Dienstag 16. April 2013, 17:53
von Sirius3
Hallo kevind,
Du hast noch viel Aufräumarbeiten vor Dir:
Funktionen sollten entweder einen Rückgabewert haben oder nicht, und nicht je nachdem ob eine Bedingung erfüllt ist oder nicht (siehe »battle_end«).
»character_menu« weiß noch zu viel von »player«.
Re: Textadv "FinalF"
Verfasst: Dienstag 16. April 2013, 22:07
von kevind
Ich brauche aber das return True wie du in der "battle sequence" siehst. Wäre folgendes exemplar besser?
Code: Alles auswählen
def battle_end(self):
if self.player.defeated() is True:
print("{0} has been defeated by {1}".format(self.player.name,
self.opponent.name))
self.state = "over"
return True
elif self.opponent.defeated() is True:
self.round += 1
print("{0} has been defeated by {1}, prepare for round {2}"
.format(self.opponent.name, self.player.name, self.round))
self.player.improve()
self.opponent.improve()
self.opponent.name = character.random_name()
self.state = "newround"
return True
else:
return False
Das character_menu weiß deshalb noch zuviel da ich mit der ganzen Menüführung unzufrieden bin... ich versuche das auch etwas Objektorientiert zu lösen aber wollte mir bisher noch nicht recht gelingen.
Danke für dein Feedback Sirius3
Re: Textadv "FinalF"
Verfasst: Mittwoch 17. April 2013, 06:42
von snafu
@kevind: Nein, du kannst einfach `if self.player.defeated():` schreiben (ohne die explizite Angabe des Wahrheitswertes). Außerdem vergleicht man Werte mit `==` und nicht mit `is`. Letzteres testet auf Objekt*identität* - also darauf, ob es sich um ein und dasselbe Objekt handelt.
Identität ist etwas anderes als Gleichheit. Alle identischen Dinge sind zwar gleich, aber alle gleichen Dinge sind nicht unbedingt identisch. Wenn ich 2 Autos mit exakt derselben Ausstattung (bzw "Konfiguration") habe, dann sind diese zwar völlig gleich, jedoch nicht identisch.
`is` braucht man in Python eigentlich ziemlich selten. Ab besten merkst du dir für den Anfang erstmal, dass du es überhaupt nicht brauchst. Es gibt nur einen Sonderfall: Wenn man wissen will, ob etwas `None` ist, dann benutzt man `if xyz is None:`.
Re: Textadv "FinalF"
Verfasst: Mittwoch 17. April 2013, 09:00
von kevind
@snafu
Okay das war wohl etwas ungeschickt, den nicht überarbeiteten Code nochmal zu posten.
So wie ich es verstanden habe bemängelte Sirius3 dass ich bei der Funktion "battle_end" nur True zurückgebe und kein False. Deswegen habe ich nun als letzte möglichkeit (das "else") ein False zurückgegeben.
Aber danke, du hast mir nun das Googeln bzgl. den verschiedenen Operatoren abgenommen

Re: Textadv "FinalF"
Verfasst: Mittwoch 17. April 2013, 21:52
von kevind
Ich habe das ganze nochmal etwas überarbeitet.
Habe das Menü nun auch in eine Klasse gepackt, mach ich das vom handling her richtig ? Funktionieren tut es mal
Macht langsam echt fun mit Klassen zu arbeiten

Re: Textadv "FinalF"
Verfasst: Mittwoch 17. April 2013, 22:30
von nomnom
Was mich ein bisschen irritiert sind diese "ungarisch anmutenden" Variablennamen mit Typpräfix (
m_ /
m). Vor allem:
Code: Alles auswählen
def __init__(self, mID, mlabel, mtype): # dass diese drei Dinge was mit dem Menü zu tun haben, ist offensichtlich.
is_main und
is_battle finde ich unnötig, warum nicht menu.type == "x"? Ansonsten ist "Menu" ein unglücklicher Bezeichner, weil das gar kein Menü ist, sondern ein Menüeintrag! Und die Vermischung von dem, was außerhalb der Klasse definiert wird, und dem, was innerhalb der Klasse steht, finde ich auch unsauber, macht die Klasse zumindest schwer wiederverwertbar (
if self.mID == ..., obwohl
mID außerhalb angegeben werden kann / intransparentes Verhalten).
Edit:
ist auch lustig. Ist Enter "any key"?

Re: Textadv "FinalF"
Verfasst: Mittwoch 17. April 2013, 22:32
von snafu
@kevind: Boah, bitte schreibe in die Commit-Messages doch nicht immer nur "Update xyz.py"...
Dass du Dateien erneuerst, ist ziemlich üblich für nen Commit. Du sollst eher beschreiben, was genau du *am Code* verändert hast. Und jetzt auch nicht immer nur "Update function `foo()`" (auch wenn man das manchmal vielleicht machen kann), sondern lieber etwas wirklich Aussagekräftiges, sodass man die Commits auf einem Blick unterscheiden kann und idealerweise möglichst einfach nachvollziehen kann, was wann geändert wurde. Du hast den Sinn dahinter scheinbar noch nicht so ganz verstanden.

Re: Textadv "FinalF"
Verfasst: Mittwoch 17. April 2013, 22:45
von kevind
Das steht wohl atomatisch da... Eigentlich nutze ich das nur um nicht dauernd bei irgendwelche pastbins voll zu spamen...
Aber könnte ich machen..
Re: Textadv "FinalF"
Verfasst: Mittwoch 17. April 2013, 22:48
von nomnom
Du hast zwei "clear"-Funktionen. Einmal main.clear() und dialog.cls(). Da wäre auch ein Modul mit allen diesen Werkzeugen, die du in mehreren Modulen brauchst, sinnvoll.
Ansonsten schreibt man Konstanten normalerweise GROSS. Gibt auch ein paar Tippfehler im Code. So heißt es "successful" und "wrapped", nicht "successfull" und "wraped".
dialog.msg()
Auch hast du in dialog.msg (warum abgekürzt? message ist besser, und bedeutet mit Code-Completion sogar gefühlt kürzer) in allen Zweigen der if-Abfrage als erstes "box_width = 50".
Typen vergleicht man mit "is_instance":
Code: Alles auswählen
>>> is_instance("foo", str) # besser als type("foo") == str
True
...

Die Zeile liefert immer True!!!! Sie wird nämlich so ausgewertet:
Und da "list" immer True ist, ... Zudem wiederholst du den Code:
Code: Alles auswählen
for string in wraped_text:
print("{0} {1}".format(box_symbol_side, string))
dialog.line()
Das fänd' ich schöner:
Re: Textadv "FinalF"
Verfasst: Mittwoch 17. April 2013, 22:50
von snafu
Immer noch vieles Unschönes drin.
Zum Beispiel in `main.py` neben der schon erwähnten schlechten Namensvergabe fällt mir noch auf, dass du `print()` anscheinend nicht richtig verstanden hast. Aus dem ``print("{0} {1}".format(self.mID, self.mlabel))`` in `Menu.print_menu_entry()` lässt sich nämlich viel einfacher ein ``print(self.mID, self.mlabel)`` machen. Die `print`-Funktion sorgt da schon selbst für die nötigen Leerzeichen zwischen den Argumenten, da es die Default-Einstellung für den Trenner ist. Nebenbei ist auch dies ein schlechter Name, denn eine Methode der `Menu`-Klasse muss nicht nochmal erwähnen, dass sie sich auf das Menü bezieht. Hier würde `Menu.print_entry()` besser klingen. Abgesehen davon ist es ja - wie auch schon gesagt wurde - eher ein `MenuEntry`. Und im Grunde ist überhaupt keine `.print()`-Methode nötig. Du könntest besser `__str__()` implementieren und dort die von dir genutzte Formatierung als String zurückliefern. Ein Benutzer der Klasse kann dann (mehr oder weniger implizit) auf die String-Repräsentation zurückgreifen, wenn er die Menüeinträge anzeigen will. Ich denke da an eine `Menu`-Klasse, die eine Liste von `MenuEntry`s als Instanzattribut beherbergt und zum "Rendern" ein ``'\n'.join(self.entries)`` benutzt. Das ist aber wahrscheinlich alles noch ein bißchen zu hoch für dich und von daher sei dir verziehen.
`Menu.is_main()` und `Menu.is_battle()` sind auch nicht schön. Du brauchst dort keinen expliziten Wahrheitswert anzugeben. Es würde reichen, jeweils ``return self.mtype == 'main'`` bzw ``return self.mtype == 'battle'`` anzugeben.
Das mit der `mID` ist viel zu undurchsichtig. Hier solltest du besser Konstanten definieren.
So, und jetzt hab ich keine Lust mehr, zu nörgeln und geh ins Bett...

Re: Textadv "FinalF"
Verfasst: Donnerstag 18. April 2013, 08:32
von kevind
Oh mann da denkt man jedesmal "jetzt ist es uper" aber dann wird hier einem schnell wieder die Augen geöffnet
Was versteht ihr genau unter Konstanten ? Ich kenne den "variablen typ" konstante, oder eine konstante in form von "123" zb. Wie soll ich das in verbindung mit mID bringen ?
nomnom hat geschrieben:...Und die Vermischung von dem, was außerhalb der Klasse definiert wird, und dem, was innerhalb der Klasse steht, finde ich auch unsauber...
Meinst du das ich auf Methoden von "Game" innerhalb des Menüs zugreiffe ?
Mir ist auch aufgefallen das es eigentlich kein Menü ist... Die einzelnen Instanzen der Klasse Menü sind eigentlich wie erwähnt Menü Einträge wobei jeder Eintrag alle Funktionen des ganzen "Menüs" enthält. Finde ich nicht so schön. Mir ist bisher leider kein besserer Weg eingefallen. (Für Ideen bin ich offen

)
@snafu
Naja ich verwende "print" hab es aber nicht studiert... der Grund warum ich print mit {} vewendet habe ist damit alle print gleich sind. (Zugegeben sind meine Ideen auch nicht immer die besten

)
Bzgl. der Menü Klasse mit den Entrys, meinst du dass beim erstellen eines "MenüEntrys" sich dieser automatisch in ein "Menü Klassenobjekt" hinzufügt ?
Danke für deine aufbauenden Worte

Re: Textadv "FinalF"
Verfasst: Donnerstag 18. April 2013, 08:54
von Sirius3
Hallo kevind,
Deine neue »Attribute«-Klasse enthält lauter Getter die völlig unnötig sind. In Python greift man direkt auf Attribute zu und benutzt im Gegenteil Properties, falls sich doch noch mehr dahinter versteckt.
»vitality« und »max_vitality« scheinen mir eine einziges Attribut mit Maximal-Wert zu sein. Dieses besondere Verhalten solltest Du nicht in Deiner Character-Klasse abbilden sondern dafür eine neue Attributklasse erzeugen.
In CreateGame.damage_calc gibt es den Zweig »not npc«, »npc« und was soll dann noch das dritte sein?
Re: Textadv "FinalF"
Verfasst: Donnerstag 18. April 2013, 09:04
von kevind
Hey Sirius3,
du meinst hier "name, value, ID" ?
Wie ich das mit Vitality noch anders lösen könnte werde ich überlegen.
Ja du hast recht das ist quatsch. Da würde eigentlich "if, else" reichen.
Danke soweit !
Re: Textadv "FinalF"
Verfasst: Mittwoch 24. April 2013, 13:42
von Sirius3
@kevind: ja genau
Dann wird »Attribute« einfach:
Code: Alles auswählen
class Attribute():
def __init__(self, atr_value, atr_name):
self.value = atr_value
self.name = atr_name
def reduce(self, amount=5):
self.value -= amount
def improve(self, amount=5):
self.value += amount
def improve_for_newround(self):
self.value += random_value() + random_value()
def __str__(self):
return "{}: {}".format(self.name, self.value)
class AttributeWithMaxValue(Attribute):
def __init__(self, atr_value, atr_name, max_value):
Attribute.__init__(self, atr_value, atr_name)
self.max_value = max_value
def improve_for_newround(self):
self.max_value += random_value() + random_value()
self.value = self.max_value
def __str__(self):
return "{}: {}/{}".format(self.name, self.value,self.max_value)
ich habe noch die ID entfernt, weil überflüssig, eine Attribut mit Maximalwert eingeführt und eine __str__-Methode hinzugefügt, z.B. für:
Code: Alles auswählen
def details(self, game_round):
print("\n\tName: {0} \t\t Level: {1}".format(self.name, self.level))
print("\tExp: {0}/{1} \t\t Round:".format(self.exp,
self.exp_levelup, game_round))
print("\n{}\t{}\n{}\t{}\n".format(self.strength, self.vitality, self.dextery, self.defense)
Re: Textadv "FinalF"
Verfasst: Donnerstag 25. April 2013, 11:16
von kevind
Genial, danke!
Ich hab mir in der zwischenzeit auch das __str__ statement angeschaut. Dies ist aber auch nur anwendbar wenn die Methode 1 String zurückgeben soll oder?
Wenn ich jetzt das Menü neu aufbaue möchte. Müsste ich dann die Game Instanz von Menüpunkt zu Menüpunkt übergeben oder wie stell ich sowas ordentlich an ?
Kann mir da jemand ein bischen "Starthilfe" geben

?
Gruss, Kev
Re: Textadv "FinalF"
Verfasst: Dienstag 21. Mai 2013, 10:05
von kevind
Servus,
meine Character Klasse arbeitet mit den Werten der Attribute Klasse. Nun möchte ich vermeiden das ich innerhalb der Character Klasse direkt auf die Attribute zugreife. (Klassen sollen ja nichts über das Innenleben einer anderen Klasse wissen oder ?)
Nun würde ich gerne zb folgendes machen:
um dieses hier zu vermeiden:
Code: Alles auswählen
damage = character.atk.value - character.defense.value
Ich dachte ich könnte das irgendwie mit der __str__ methode lösen aber das funktioniert nicht.
Die Attribut Klasse sieht momentan so aus:
Code: Alles auswählen
class Attribute():
def __init__(self, atr_ID, atr_value, atr_name):
self.ID = atr_ID
self.value = atr_value
self.name = atr_name
def __str__(self):
return(self.value)
def reduce(self, amount):
self.value -= amount
def improve(self):
self.value += 5
def improve_for_newround(self):
self.value += random_value() + random_value()
def is_ID(self, possible_id):
if self.ID == possible_id:
return True
else:
return False
Danke für eure Ratschläge.
ps: Ja das mit der ID muss ich noch regeln... Eins nach dem Anderen

Re: Textadv "FinalF"
Verfasst: Dienstag 21. Mai 2013, 11:28
von BlackJack
@kevind: Was hat denn `__str__()` damit zu tun? Das soll eine Zeichenkette zurück geben. Und man müsste dazu dann auch `str()` mit dem Exemplar aufrufen. Zeichenketten kann man schlecht voneinander abziehen.
Re: Textadv "FinalF"
Verfasst: Dienstag 21. Mai 2013, 11:38
von kevind
Ich dachte vl. lässt sich das noch in INT umwandeln. Aber vergessen wir das mal.
Was würde sich für diese Situation am besten eignen ?