Spiel-Entwicklungs-Projekt. Angelehnt an PORT ROYAL

Du hast eine Idee für ein Projekt?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Du solltest bei deinen Schiffen ernsthaft über die Verwendung einer Klasse nachdenken, bzw. mindestens den Einsatz von Named-Tupeln in Erwägung ziehen. Ein Dutzend Dictionaries mit den selben Attributen sind ein deutliches Indiz dafür.
TheLebkuchen24 hat geschrieben:Btw. Hat Jemand eine Idee, wie man den Preis in Hinblick auf Angebot und Nachfrage gestalten könnte?
Das kommt auf die Sichtweise an. Ein Preis für eine Ware ist fair (aus Käufersicht), wenn die Ware zu dem Preis verkauft wird, die der Bieter zahlen würde, der gerde so nicht den Zuschlag bekommen hat. Welchen Preis ein Käufer zahlen kann, das ergibt sich aus dem zu erreichenden Gewinn, bzw. dem möglichen Verlust. Wenn du eine KI schreiben willst, dann musst du das irgendwie abschätzen. Das wird u.U. schon bei sehr einfachen Systemen schwierig. Der Verkäufer will natürlich einen möglichst hohen Gewinn haben. Das musst du jetzt nur noch alles modellieren ;-)

Oder du machst es dir ein wenig einfacher und überlegst selbst eine Funktion. Beispielsweise

Code: Alles auswählen

preis = max(minimalpreis, basispreis * nachfrage**f)
Die "nachfrage" reicht dann von 0 bis unendlich, mit f knnast du den Preisanstieg festlegen. f=1 ist nichts anderes als doppelter Preis für doppelte Nachfrage. f > 1 dürfte wohl etwas spannender sein.
Das Leben ist wie ein Tennisball.
BlackJack

Wobei man die Frage stellen kann ob das mit Angebot und Nachfrage überhaupt so genau modelliert werden muss. Also wenn man das machen möchte, dann natürlich schon, aber viele Handelssimulation haben auch einfach nur einen Preis für eine Ware der in bestimmten Abständen zufällig nach oben oder unten angepasst wird.
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

Daran hatte ich auch schon gedacht. Scheint auch für den Anfang einfacher, als eine komplexe Analyse für die KI hinzulegen (zumindest für mich ^^ ). Später kann man das ja noch machen, wenn das Gerüst steht.

Eine Klasse für Schiffe oder gar der Einsatz von "Named-Tupel". hm... ich habe mich mal daraufhin im Internet kund getan. Aber so ganz verstehe ich noch nicht den Unterschied zwischen dicts und named-tupeln. Wenn es nur auf Lesbarkeit abzielt, finde ich die Variante mit den dicts besser, da man später im Programmcode weiß, was da angefordert wird. Aber ist nur eine Sicht eines Anfängers, der mit dem Projekt und euch Fachkundigen Python vertiefen will. :wink:
BlackJack

@TheLebkuchen24: Bei Wörterbüchern weiss man ja gerade *nicht* was da ”gefordert” wird, weil man nur die Daten sieht die man bekommt, aber nirgends eine ”feste” Definition die man nachlesen kann. Ansonsten haben `namedtuple` doch die gleichen Informationen wie Wörterbücher, nur dass es einfacher zu schreiben ist, und man das später leichter durch eine Klasse ersetzen kann, weil sich die Syntax für den Zugriff auf die Daten nicht ändert.
nezzcarth
User
Beiträge: 1633
Registriert: Samstag 16. April 2011, 12:47

BlackJack hat geschrieben:Wobei man die Frage stellen kann ob das mit Angebot und Nachfrage überhaupt so genau modelliert werden muss. Also wenn man das machen möchte, dann natürlich schon, aber viele Handelssimulation haben auch einfach nur einen Preis für eine Ware der in bestimmten Abständen zufällig nach oben oder unten angepasst wird.
Na ja, für ein Spiel, bei dem die Handelsfunktionen nur Beiwerk sind (z.B. RPGs), kann man das so machen, aber da es sich ja nun um eine Wirtschaftssimulation handelt, sollte man in den Bereich vielleicht etwas mehr Energie stecken. Es muss ja kein komplexes Modell sein, das volkswirtschaftliche Adäquatheit anstrebt, aber eine Marktlogik, die in irgendeinerweise einen interessanten Aspekt hat, wäre für ein Spiel in dem Genre doch schon ganz nett; und sei es nur, dass man irgendwie verhindert, dass ein Händler tonnenweise Kram aufkauft, obwohl sein Lagerhaus damit bis zum Rand gefüllt ist.

Man kann ja ruhig mit so einem simplen Zufallssystem anfangen. Trotzdem würde ich mir grob überlegen, wie das Wirtschaftssystem aussehen soll, und das Spiel entsprechend so entwerfen, dass man dies zumindest einigermaßen einfach stückweise einbauen kann, ohne das gesamte Spiel zu verändern.
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

Ich habe mir Gedanekn zur Preisbildung gemacht und die ganzen Ratschläge durch den Kopf gehen lassen.

Für das Gesamtkonzept werd ich für jedes Produkt einen festen Mindestpreis, für Kauf sowie Verkauf, setzen. Desweiteren wird der Kaufpreis immer unter dem Verkaufpreis liegen, was irgendwo auch Sinn macht. Dieser aber abhängig vom Konsum der Stadt, Lagerbestand und geographischen Faktoren (nicht jedes wird Produkt wird in einer Stadt hergestellt) sein wird.
Bedeutet soviel wie, dass wenn in der Stadt Tabak angebaut wird, der dort billig ist und sich dem Mindestpreis annähert. Je nachdem die viel davon im Lager ist. Damit lohnt es sich dort Tabak einzukaufen, aber nicht zu verkaufen. Ebenfalls gehen durch den Konsum manche Produkte schneller weg als Andere. Damit wird eine dauerhafte Nachfrage gewährleistet.

Wäre dann ein gesetzter Maximalpreis angemessen?

Aktuelles Problem ist, dass ich die KI hinten anstelle und Städte im Anfangsstadium schnell nur noch die Güter besitzen werden, welche sie "herstellen".
Aber gut. Alles zu seiner Zeit ;D

@nezzcarth: Händler die Ihre Lagerhäuser überfüllen kommen evtl. später mit der KI. :wink:
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

So... Auf Bitbucket kann man nun den bereits entwickelten Programmcode einsehen. Bedenkt bitte, dass alles von einem Newbe entwickelt wurde. Darum bitte ich um Verbesserungsvorschläge. Auch wenn es so wirkt, als ob ich mich dagegen sträube sie anzunehmen :D
BlackJack

@TheLebkuchen24: Module mit Sonderzeichen wie Klammern im Namen kann man nicht importieren.

So etwas wie 'Klasse' gehört nicht in Modulnamen und man sollte auch nicht pro Modul grundsätzlich nur eine Klasse definieren. Das ist Python und nicht Java. Damit wird die Organisationseinheit Modul entwertet, wenn man sie nicht nutzt.

Sternchenimporte sind böse. So wie flächendeckend wie Du Die verwendest werden auch wieder die Module entwertet. Wozu macht man sich die Mühe Code auf Module aufzuteilen wenn man dann doch wieder überall alle Namen per * zusammenwirft. Zumal das hier echt unübersehbare Ausmasse hat, denn das funktioniert ja transitiv — wenn Du per Sternchen alles aus Modul B in Modul A holst und in Modul B alles per Sternchen aus Modul C, dann landet am Ende *alles* aus B *und* C auch in A.

Bezüglich der Namensschreibweisen könntest Du auch mal einen Blick in den Style Guide for Python Code werfen. Und ich würde auf Englisch wechseln. Alles andere ist in Englisch, damit bekommt man komische Mischformen und läuft Gefahr das man etwas auf Deutsch so nennt wie etwas vorhandenes auf Englisch und das ist verwirrend.

Ich würde grössere Projekte in ein Package stecken. Damit vermeidet man Namenskollisionen mit der Standardbibliothek und anderen Bibliotheken.

In die ``setup.py`` gehört kein Testcode sondern das was in der Python-Dokumentation beschrieben ist um das Projekt zu installieren.

Edit: `Waren` und `Fracht` sehen ja echt *sehr* ähnlich aus. Das sind so keine zwei verschiedenen Klassen.
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

Hey hoh,
ich melde mich zurück. Im Urlaub habe ich weiter gemacht. Jetzt kann man sein Schiff upgraden zu einem Anderen. Güter in Städten gekauft, sowie verkauft werden. Für Schnellspielzwecke hat der Benutzer, wenn er die Brigg als Startschiff wählt einen kleinen Boni in Sachen Buget. Auslaufen kann man erst wenn man mehr als 8 Matrosen hat. Matrosen bekommt man in der Taverne. Desweiteren ist es möglich mit 'L' aus Gebäuden/Städten rauszugehen, mit 'i' Informationen über Datum, Fracht/Ladung, Matrosenanzahl und Schifftyp zu bekommen. Und einige Kleinigkeiten mehr ;D


@BlackJAck: Die Klammerung hatte den Sinn, dass ich es mir optisch darstellen wollte, wo ich in naher Zukunft weiter dran arbeiten wollte. Mitlerweile finde ich es praktischer dies auf einen Blattpapier zu notieren. Welches dann zu den ganzen anderen Papieren, die was mit dem Projekt zu tun haben, kommt.

hm... die Sternchenimporte habe ich mal 'versucht' zu minimieren. Irgendwie habe ich Angst da was zu wenig importiert zu haben.

Uff.... Ich wusste, dass es kleine Regeln -bzw. Einigungen- unter den Informatikern gibt, wie was am besten zu schreiben hat. Evtl. setze ich mich da dran, wenn ich leerlauf habe.

Größere Projekte in Packages. Das versteh ich noch nicht so recht. Ist damit gemeint, dass man einen speziellen extra Ordner anlegt?

Ups.... seit wann ist die setup.py für installationen? Und wenn wir schon dabei sind. Im Internet gibt es viele Anleitungen für seinem Pythencode eine .exe zu erstellen, mit der man wiederum auf Rechnern mit Win und ohne Python, laufen lassen kann. Aber so recht verstanden habe ich das nicht, was da im einzelnen dann gemacht wird. Wäre für mich nur ein blindes Auswendiglernen ohne zu verstehen, was da passiert. - Hat ja jedoch noch Zeit -
Ich habe sie jetzt einfach als die auszuführende Instanz gesehen.


=> Nun zurück. Wenn man eine Zeitlang spielt, wünscht man sich einen besseren Überblick. Die Textform ist ja ganz süß. Jedoch bestimmt mit der Zeit überladen. Darum wollt ich mal fragen, ob es nicht einen Editor/Engine gibt, die für meine Zwecke geeignet wäre. Klar könnte man das bestimmt alles auch selbst zusammenschrauben. Aber das traue ich mir dann doch nicht zu.
Meine Anforderungen wären, dass man einmal das Datum sehen kann, seine eigene Ladung des Schiffes, die Stadt in Form von zum Bsp. Quadraten. Und sobald man draufklickt dann das zugehörige Fenster auftaucht (Klick-Markt : Ein Fenster öffnet sich mit den Waren/Frachtgütern, deren Preis und deren Menge im Lager).
Die Seefahrt muss dargestellt werden.
Warum jetzt eine grafische Oberfläche? Naja, Übersichtlichkeit und auch Interesse, wie sowas umsetzbar wäre.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Kurz zu Deinen Vorstellungen zu einer GUI: Ja, es gibt einige Projekte, die interessant für Dich sein könnten (FIFE-Engine z.B.), aber nein, *imho* bist Du noch nicht so weit. Du musst noch weiter an den Grundlagen üben und an Deinem Code feilen!

Ich habe mal (wahllos) folgendes Beispiel aus Deinem Code herausgegriffen. Es handelt sich um das Modul ``Klasse_Kampf``:

Code: Alles auswählen

from Klasse_Schiff import neuesSchiff

import random

def summeMatrosen(konvoi):
    matrosen = 0
    for i in konvoi:
        matrosen += i.matrosen
    return matrosen
    
def summeKanonen(konvoi):
    kanonen = 0
    for i in konvoi:
        kanonen += i.lager.fracht.ladung.get('Kanone')
    return kanonen

def summeMunition(konvoi):
    munition = 0
    for i in konvoi:
        munition += i.lager.fracht.ladung.get('Kanonenkugel')
    return munition

def summeHP(konvoi):
    hp = 0
    for i in konvoi:
        hp += i.hp
    return hp

class Kampf():
    def __init__(self, angreiferKonvoi, verteidigerKonvoi):
        self.aggressor = angreiferKonvoi
        self.verteidiger = verteidigerKonvoi
        self.kugelschaden = 1.2

    def automatisch(self, modus='versenken'):
        angreifer_matrosen = summeMatrosen(self.aggressor)
        angreifer_kanonen = summeKanonen(self.aggressor)
        angreifer_munition = summeMunition(self.aggressor)
        angreifer_hp = summeHP(self.aggressor)

        verteidiger_matrosen = summeMatrosen(self.verteidiger)
        verteidiger_kanonen = summeKanonen(self.verteidiger)
        verteidiger_munition = summeMunition(self.verteidiger)
        verteidiger_hp = summeHP(self.verteidiger)

        if modus == 'versenken':
            spieler = random.randint(0,1)
            while True:
                if spieler == 0:
                    print('Spieler: Angreifer')
                    schaden = (0.5 * angreifer_kanonen)* self.kugelschaden
                    verteidiger_hp -= schaden
                    print('hat {} Schaden angerichtet - {}'.format(schaden, verteidiger_hp))
                    input()
                    if verteidiger_hp <= 0:
                        print('Angreifer hat gewonnen')
                        break
                    spieler = 1

                if spieler == 1:
                    print('Spieler: Verteidiger')
                    schaden = (0.5 * verteidiger_kanonen) * self.kugelschaden
                    angreifer_hp -= schaden
                    print('hat {} Schaden angerichtet - {}'.format(schaden, angreifer_hp))
                    input()
                    if angreifer_hp <= 0:
                        print('Verteidiger hat gewonnen')
                        break
                    spieler = 0
- Modulnamen sollten eigentlich eher klein geschrieben werden. Dazu sollte in PEP8 etwas stehen; man kann sich aber mal einfach die Doku angucken und runterscrollen... da sind alle Module der Standard-Lib klein geschrieben.

- Es fehlen einfach alle üblichen Kommentare! Schau Dir mal Code von lunar an; da siehst Du, was so alles an Kommentaren in Code gehört (Lizenz, Docstrings). Bei Python nutzt man Restructured Text in den Document-Strings; die speziellen Auszeichnungen für Python findest Du hier. Du kannst Sphinx auch erst einmal "weglassen", aber zumindest Doc-Strings solltest Du schreiben und da gehört auch einer für das Modul dazu. Denn nur so erfährt man, was in dem Modul zu finden ist.

- als nächstes fällt auf, dass das, was Du importierst, einen falschen Namen hat. MixedCase gibt es in Python nicht! Lies Dir bitte PEP8 durch und refactor den Code dahingegend. Auch wenn es Mühe macht, es lohnt sich. Insbesondere auch, wenn Du hier im Forum fragst. Solchen inkonformen Code mag sich niemand gerne angucken. Und es kommt zu Unklarheiten - was genau ist ``neuesSchiff``? Eine Klasse? Eine Funktion? Eine Konstante? Iirc gibt es auch ein PEP8-Prüftool, welches einem Verletzungen meldet.. da musste mal googlen. (evtl. kann pyflakes das - das ist ein allgemeiner gefasstes "Stil-Untersuchungstool"). Die PEP8-Verletzungen betreffen dann auch alle Funktionen

- Die ganzen Funktionen haben schlechte Namen. Eine Funktion sollte idR. ein Verb + Objekt sein. Du kombinierst aber zwei Substantive. ``summeMatrosen`` wäre also eigentlich ``matrosen_summieren``. Aber: Das ist letztlich immer noch ein schlechter Name, denn da werden ja keine "Matrosen"-Objekte summiert, sondern aus einem ``Konvoi``-Objekt die Anzahl an Matrosen bestimmt. Ergo würde ich ``zaehle_matrosen_im_konvoi`` vorschlagen. (Ja das ist ziemlich lang; das liegt auch am "Deutsch" für die Namen - das Englische erlaubt oftmals kürzere Konstrukte). Grundsätzlich frage ich mich da aber, wieso ein ``Konvoi``-Objekt nicht selber eine ``zaehle_matrosen``-Methode besitzt? (Fortschrittlicher wäre dafür eine Property zu verwenden - aber das muss als Anfänger nicht sein).

- Die Funktionen sind alle analog - und eher unschön - aufgebaut. ``i`` ist ein schlechter Name für eine Laufvariable. ``i`` nutzt man idR. für Indizes, also etwa bei ``for i in range(...)``-Konstrukten. Da mir auch immer noch unklar ist, was für ein Objekt sich hinter ``Konvoi`` verbirgt, hätte ich jetzt etwa ``schiff`` o.ä. erwartet. Grundsätzlich kann man solche Summationen einfacher als Generator-Expression schreiben:

Code: Alles auswählen

# statt
matrosen = 0
for i in konvoi:
    matrosen += i.matrosen

# einfacher
sum(i.matrosen for i in konvoi) # ``i`` nur deswegen, weil ich nicht *weiß*, was man stattdessen nehmen sollte!
- Deine Klasse ist eigentlich keine. Denn sie hat keinen inneren Zustand. Du hättest ``automatisch`` einfach als Funktion schreiben können! Generell ist ``automatisch`` ein Adjektiv und wieder kein Verb; also ein schlechter Name.

- ``automatisch`` verletzt das DRY-Prinzip! D.h. die Routinen für Angreifer und Verteidiger sind fast identisch; ergo sollte man die Gemeinsame Basis zusammen fassen und generalisieren, z.B. als separate Funktion, die man mit den sich unterscheidenden Merkmalen aufruft.

- Du holst Dir in ``automatisch`` Werte, die Du dann gar nicht mehr benutzt!

- Dieses ``if modus = ...``-Konstrukt lässt vermuten, dass Du noch andere Kampfmöglichkeiten einbauen willst. Dies solltest Du *niemals nie* über solch ein ``if... elif... elif_n... else``-Konstrukt lösen! Zum einen wird solch eine Kaskade unübersichtlich (insbesondere, wenn man neben dem "Dispatching" auch die Funktionalität in dieselbe Funktion schreibt), zum anderen verletzt sie das Open-Closed-Prinzip. Du kannst so etwas vermeiden, indem Du die Funktionen oder allgemein "Callables" in eine Datenstruktur packst (Liste, Set, Dictionary usw.) und durch einen für Dich passenden Mechanismus auswählst (= Dispacthing). Ein Beispiel, welches das Prinzip an sich aufgreift, findest Du in einem Tutorial von mir.

Mal vereinfacht könnte das in die Richtung gehen:

Code: Alles auswählen

from random import choice

def versenken(...):
    print("versenke Schiff")

def als_prise_nehmen(...):
    print("entere Schiff und erobere es")

def in_brand_setzen(...):
    print("setze Schiff in Brand")

kampf_optionen = [
    versenken,
    prise_nehmen,
    in_brand_setzen
]

# wählen (Dispatching!) hier per Zufall. Könnte aber auch per Index, Schlüssel usw. funktionieren
gewaehlte_kampf_funktion = choice(kampf_optionen)

# aufrufen
gewaehlte_kampf_funktion(...)
- Wie BlackJack zuletzt schon monierte, ist Deine Aufteilung imho viel zu granular. Man kann und darf in Python wirklich mehr in ein Modul schreiben, als eine Klasse ;-) Imho kann man alles, was an Basistypen bei Dir so rumschwirrt, in *ein* Modul packen. Der Ablauf des Spiels, also Kämpfe und Aktionen usw. könnte man dann ggf. in ein separates Modul stecken.

- Du solltest Dich neben all den hier angeschnittenen Themen wirklich mal mit dem Thema Unit-Testing auseinander setzen. Dein Code ist schon dermaßen komplex, dass Dir ein ordentliches Refactoring jetzt vermutlich schwer fallen wird. Iirc hatte ich Dir dazu irgend wann im Thread mal etwas geschrieben.

Ansonsten: Lass Dich nicht entmutigen! Solange Du Kritik aufnimmst, durchdenkst und als Anstoß zum Lernen siehst, wirst Du Dich auch verbessern. Und wenn nicht, hast Du ja für Dich selber dennoch Deinen Spaß :-)

Ich sage das nur, da hier jetzt wenig "positives" steht - das ist halt bei solchen Code-Reviews normal und hat nichts abwertendes an sich! (Viele User lasten uns Regulars hier immer einen rüden Tonfall an, weil sie genau das nicht begreifen; daher sage ich das heute mal explizit :-) )
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@TheLebkuchen24: Eine projektbegleitende Blattsammlung kenne ich noch von früher. :-) Heute landet so etwas üblicherweise in Textdateien beim Rest des Projekts oder in einem Wiki.

Wenn Du zu wenig importierst, dann funktioniert das Programm ja nicht mehr. Das fällt also schnell auf. Wenn man Unit-Tests schreibt, die das Programm abdecken, sollte so etwas sofort auffallen.

Mit Packages meine ich Packages. Halt nicht eine lose Sammlung von Modulen die im gleichen Namensraum liegen wie die Module in der Standardbibliothek, beziehungsweise im aktuellen Arbeitsverzeichnis, also in dem Verzeichnis in dem normalerweise als *erstes* gesucht wird, sondern in einem eigenen Namensraum, so dass die eigenen Module eindeutig von gleichnamigen Modulen in anderen Projekten unterschieden werden können.

Die ``setup.py`` war schon immer für Installationen. Für was sollte sie sonst sein? Das klassische Vorgehen um Python-Module/Packages zu installieren ist: Quelltextarchiv herunterladen, entpacken, in den Ordner wechseln, und dort ``python setup.py install`` aufrufen. Siehe auch An Introduction to Distutils in der Python-Dokumentation um zu sehen was in dieses Skript normalerweise hinein gehört.
Antworten