Spiel-Entwicklungs-Projekt. Angelehnt an PORT ROYAL

Du hast eine Idee für ein Projekt?
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

Hey Leute,

ich bin diese Jahr mit meinem ABI fertig geworden und hatte das Verlangen angelehnt an Port Royal ein spiel zu entwickeln. Jedoch mit meinen Vorstellungen. Es gibt schließlich einiges in dem Spiel, was man hätte besser machen können. ^^

Mein aktueller Stand ist, dass ich Spielerdaten und Co. als eine .txt Datei speicher und alles schön retro auf Texteingabe basiert.
> Jetzt kam mir aber der Gedanke, dass es evtl. praktisch sei, wenn man zu speichernde Daten in eine Datenbank speichert. Jedoch blicke ich bei all den DBs nicht durch.
Welche wäre geeignet für eine lokalen gebrauch? -Sqlite und Mysql, welche ich schonmal kenne, fallen meiner Ansicht nach weg-
> Und wegen der Textorientierung rechtfertige ich mich, indem ich einfach mal behaupte, dass später, mit Grafik, die Befehle (bzw. der Eingabetext im Textgame) durchs klicken der Maus in einem bestimmten Quadranten aufgerufen und ausgeführt werden.

In voller ungedult auf eure Antwort
Der Lebkuchen v.v
BlackJack

@TheLebkuchen24: Warum fallen SQLite und MySQL weg? Und was speicherst Du in den Textdateien?
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

Momentan sowas hier:
Name: ich
Rang: Kapitaen
Heimatstadt: Start
Schiff: Schaluppe
Matrosen: 60
Hp: 100
Guthaben: 1000
lvl: 0
x_pos: 10
y_pos: 15

-> Aber ich möchte gerne Konvois (Gruppe von Schiffen). Das bedeutet jedoch, dass Ich für jedes einzelne Schiff in dem Konvoi Hp, Manschaft, Kanonen, Munition und Fracht speichern muss. Und da scheinen mir DB weniger Rechenleistung zu saugen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

TheLebkuchen24 hat geschrieben: -> Aber ich möchte gerne Konvois (Gruppe von Schiffen). Das bedeutet jedoch, dass Ich für jedes einzelne Schiff in dem Konvoi Hp, Manschaft, Kanonen, Munition und Fracht speichern muss. Und da scheinen mir DB weniger Rechenleistung zu saugen.
Ich würde das Problem zunächst einmal eher von der Domänenseite her betrachten und Persistenz außen vor lassen. Überlege Dir erst einmal, *wie* Du im Programm solche Sachverhalte modellierst. Daraus ergibt sich dann ggf. auch, wie Du das alles speichern kannst.

Wenn Du sowieso alles im RAM halten kannst, brauchst Du evtl. gar keine Datenbank, sondern kannst alles in gängige Textformate wie JSON oder XML serialisieren.

So ein selbst definiertes Textformat ist aber auf jeden Fall keine gute Idee ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@TheLebkuchen24: Ich weiss nicht ob Rechenleistung hier das vorrangige Thema ist. Als erstes könnte man mal überlegen auf ein Standardformat wie JSON für die Textdateien umzusteigen. Dann hast Du auch gleich die Möglichkeit komplexer strukturiertere Daten als nur nur ”flache” Schlüssel/Wert-Paare zu speichern.
Benutzeravatar
/me
User
Beiträge: 3557
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

TheLebkuchen24 hat geschrieben:Was ist denn diese JSON ?
JSON

In der Standardbibliothek findest du ein passendes Modul für JSON.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hast Du denn überhaupt schon den Großteil der Domänenlogik implementiert oder modelliert? Also gibt es den "Konvoi" schon im Code? Daraus ergibt sich imho dann auch die Umsetzung in der Persistenz, die sich dann ggf. leicht über ein Mapping machen ließe, was BlackJack schon beschrieben hat.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
/me
User
Beiträge: 3557
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Hyperion hat geschrieben:Hast Du denn überhaupt schon den Großteil der Domänenlogik implementiert oder modelliert?
Ich vermute, dass es noch nicht mal schriftliche Anforderungen gibt. Ich brauche jetzt nicht unbedingt in UML modellierte Diagramme, aber ein gewisses Konzept sollte man zumindest in Textform niederlegen.
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

Joah ... öhm ... bisher habe ich mir das so vorgestellt:

- Konvoi (es soll später mehr als nur einen geben)
> Schifftyp (können mehrfach vorkommen)
Jeder Schifftyp soll Richtwerte haben. Damit nicht zu viel Ladung auf dem Schifftyp ist und es praktisch untergehen müsste.
> Fracht, welche auf die einzelnen Schifftypen per Hand aufgeteilt werden kann. (weiß aber noch nicht, was für Frachtgüter es geben wird)

Anzumerken hierbei ist, dass die Schiffe auch Schaden nehmen, die Matrosen in Schlachten sterben, Fracht ein und ausgeladen wird und und und....


Ich hoffe man versteht so langsam, was ich vorhabe ;D

Jetzt weiß ich halt nicht, wie flexibel diese Textdateien sind.
Sirius3
User
Beiträge: 17797
Registriert: Sonntag 21. Oktober 2012, 17:20

@TheLebkuchen24: Wir verstehen schon, was Du vorhast. Du mußt Dir aber zuerst klar werden, was Du brauchst. Zum Speichern gibt es JSON, Pickle, SQlite, um nur ein paar zu nennen. Wenn man Serialisierung und Spielelogik kapselt, spielt das Format ja auch nur eine untergeordnete Rolle. Jetzt mußt Du erst einmal Spielzustände und Spielaktionen definieren.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

TheLebkuchen24 hat geschrieben: Jetzt weiß ich halt nicht, wie flexibel diese Textdateien sind.
Vergiss doch mal die Textdateien (davon abgesehen ist JSON sehr flexibel, also keine Angst!).

Fang erst einmal an, das ganze in Python zu entwickeln. Überlege Dir, wie Du die gerade formulierten Anforderungen umsetzen könntest.

Ich sehe da Dinge wie Fracht und Schiffe, um bei den beiden Basiselementen zu bleiben. Was muss denn ein Frachtgut haben? Ich könnte mir vorstellen, dass man anhand des Namens die Klassifizierung machen möchte, also "Bananen", "Zucker", "Rum", usw. Zudem hat jedes Gut pro Einheit ein Gewicht. Ich würde der Einfachheit halber nur ganze Einheiten zulassen. Das Gewicht würde ich fest definieren; da bei Schiffen in Tonnen gemessen wird, könnte man das als Basis nehmen, Kilogramm o.ä. wären natürlich auch möglich. Für mich wäre das einfach nur ein netter Buchstabe, aber ohne inhaltliche Bedeutung (also z.B. keinerlei Umrechnungen!). Man kann sich natürlich noch Gedanken machen, ob man verschiedene Einheiten irgend wie modellieren will? Iirc gab es dazu eine nette Python-Lib. Nun kommt schon das erste Problem: Entspricht eine Ware einem Frachtgut? Oder will man eine "Fracht" als zusätzliche Abstraktion, die ggf. die Menge beinhaltet. Ich würde wohl zunächst vom einfachen Fall ausgehen und Fracht == Ware == Gut setzen und das erst einmal versuchen in simplen Code zu gießen:

Code: Alles auswählen

class Product:
    
    def __init__(self, name, weight = 0):
        self.name = name
        self.weight = weight
        
    def __str__(self):
        return "{}: {}t".format(self.name, self.weight)
Ziemlich einfach bisher...

Aber man kann damit schon spielen:

Code: Alles auswählen

banane = Product("Bananen", .1)
zucker = Product("Zucker", .01)
# simples Lager als Dictionary
stock = {zucker: 200, banane: 50}

for product, amount in stock.items():
    print("{}t {}".format(amount * product.weight, product.name))
> 5.0t Bananen
> 2.0t Zucker
Jetzt kommen wir zum Schiff... da wird es schon spannender.

Ein Schiff muss eine Art Typ haben, den man einfach über einen String abbilden könnte. Dazu kommt noch eine maximale Kapazität an Gewicht. Ein Schiff muss irgend wie Fracht beladen und entladen können.

Wie könnte man das machen? Was passiert, wenn zu viel beladen oder entladen wird? Wie kann man elegant auf verschiedene Güter an Bord zugreifen? (Also etwa 20 Einheiten "Bananen" entladen, aber nichts vom "Zucker")

Du siehst, da gibt es eine Menge Dinge, die total unabhängig von der späteren Darstellung der Daten in einer Datei oder Datenbank sind :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

return "{}: {}t".format(self.name, self.weight)

was hat das mit den {} / {}t und der Methode format() auf sich? Sowas ist mir bisher noch nicht begegnet.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das nennt sich String Formatting. In der Dokumentation gibt es dazu einen eigenen Abschnitt.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

TheLebkuchen24 hat geschrieben:return "{}: {}t".format(self.name, self.weight)

was hat das mit den {} / {}t und der Methode format() auf sich? Sowas ist mir bisher noch nicht begegnet.
Öffne eine Python-Shell und probiere es aus ;-)

Oder lies es nach: Link zur Doku :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

Deiner Idee einfach mal in meinen Augen provisorisch loszulegen hat mir gut gefallen. Somit kann ich erstmal weitermachen, obwohl ich das Gefühl habe, dass ich es später bereuen werde. Ist nur so ein Gefühl :D

@Hyperion: Deine Klasse habe ich etwas modifiziert. Denn ich habe mich entschieden, dass man von allem jeweils nur Tonnen kaufen kann. Deshlab musste dann das Attribut wert her. Aber ist es essenziel Quellcode auf englisch zu schreiben? Ich habe mich für die Deutschsprachigkeit im Code entschieden.

Code: Alles auswählen

class Produkt():
    def __init__(self, name, wert):
        self.name = name
        self.gewicht = 1
        self.wert = wert

    def __str__(self):
        return '{}t {} cost {}$'.format(self.gewicht, self.name, self.wert)
Passend dazu habe ich gleich mal eine Klasse Lager entwickelt.

Code: Alles auswählen

class Lager():
    def __init__(self):
        self.lager = {}
        self.produkte = [Produkt('Zucker', 20),
                         Produkt('Brot', 10),
                         Produkt('Getreide', 5)]

        
    def einladen(self, name, menge):

        if name in self.lager:
            self.lager[name] = self.lager.get(name) + menge
        
        else:
            for produkt in self.produkte:
                if name == produkt.name:
                    self.lager[produkt.name] = menge

    def ausladen(self, name, menge):

        if name in self.lager:
            lagerBestand = self.lager.get(name)
            if menge > lagerBestand:
                print('Du hast vom Produkt "{}" nur {}t'.format(name, lagerBestand))
                
            elif menge == lagerBestand:
                del self.lager[name]

            else:
                self.lager[name] = self.lager.get(name) - menge
Was ich bei mir etwas unglücklich finde ist die Tatsache, dass ich in der Klasse die Produktsparte initialisiere und deklariere. Aber als provisorieum gefällt mir die Klasse Lager schon ganz gut.

@Hyperion: Und du hast Recht. Schon jetzt kann man damit rumalbern ^^

Code: Alles auswählen

l = Lager()
l.einladen('Zucker', 10)
l.einladen('Brot', 15)
l.lager
> {'Zucker': 10, 'Brot': 15}
l.ausladen('Brot', 15)
l.lager
> {'Zucker': 10}
Bei den Schiffen muss ich noch überlegen, wie man das provisorisch umsetzen kann.

-> Und schonmal vielen Dank für die ganze Hilfe. Ich hätte nie gedacht so schnell schon Rückmeldung zu bekommen!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hallo,

das Attribut "wert" habe ich jetzt nicht verstanden!

Man muss seinen Code nicht in Englisch schreiben; viele bevorzugen das, weil Englisch eben die Sprache beim Programmieren ist. D.h. Python selber hat englische Schlüsselwörter (``for``, ``while`` usw.) und auch viele Namen von Typen und Funktionen sind Englisch (``list.append``) usw. Bei allen fremden Libs wirst Du auch eher Englisch finden. Insofern kommt es früher oder später dann zu einer Mischung bei Dir im lokalen Quellcode. Und Englisch lesen können musst Du ja beim Programmieren so oder so :-)

Ich würde Dir ans Herz legen, Dich mal mit Unit Tests auseinander zu setzen! Gerade in der Phase, in der Du jetzt bist, kannst Du alle diesen Domänen-Objekte und Funktionen prima automatisiert testen. Damit fällt es Dir leichter, Änderungen am Code vorzunehmen.

``print`` solltest Du immer von der eigentlichen Logik trennen. Wenn jemand mehr "ausladen" will, als er auf Lager hat, dann wirf eine Exception - diese kannst Du dann dort verarbeiten (z.B. durch eine Meldung), wo Du sie aufrufst.

Ich würde die Initialisierungen mit konkreten Produkten im Lager wegnehmen! Schreibe Dir eine Factory-Funktion, die Dir ein Lager mit den drei vorgesehenen Produkten generiert, also in etwa so:

Code: Alles auswählen

def generate_stock():
    stock = Stock()
    stock.produkte.append(Produkt('Zucker', 20))
    stock.produkte.append(Produkt('Brot', 10))
    # ...
    return stock
Deinem "Lager" fehlt aber noch Kapselung! Aktuell muss ich auf das *interne* Attribut ``produkte`` zugreifen; das sollte man imho hinter Methoden verstecken, mit denen man Produkte hinzufügen oder entfernen kann. (LoD)

Richtig glücklich finde ich die Aufteilung aber noch nicht. Was bringt Dir die Liste ``Lager.produkte`` eigentlich? eigentlich reicht doch ein Mapping, welches ein vorhandenes Produkt (ggf. reicht der Name!) auf die im Lager definierte Menge abbildet...

Wie Du siehst, kann und muss man da eine Menge überlegen... :-)

Aber es ist gut, dass Du mit einer Shell gleich mit Deiner API "herumspielst". Damit - und mit Unit Tests - bekommst Du ein Gefühl dafür, ob Deine API sich "gut" anfühlt oder noch etwas fehlt oder umständlich gelöst ist. Beides ist imho sehr wichtig und es ist schade, dass man User sich dem gegenüber so verschließt :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

Das Attribut 'wert' ist deshlab drinnen, das jede Stadt unterschiedliche Preise zahlt und verlangt für Produkte.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

TheLebkuchen24 hat geschrieben:Das Attribut 'wert' ist deshlab drinnen, das jede Stadt unterschiedliche Preise zahlt und verlangt für Produkte.
Jetzt koppelst Du aber den Wert *direkt* an ein Produkt. Damit wären ja "Bananen" in zwei Städten (oder bei zwei Anbietern) zwei unterschiedliche Produkte...

Ich würde das Produkt als solches wirklich nur auf "global" gültigen Größen beschränken.

Alles variable sollte in einer separaten Struktur vorrätig sein.

Vielleicht kannst Du das über einen neuen Typen "Artikel" lösen, der eben aus einem Produkt und einem Preis besteht. Ein Händler / Markt oder der Spieler selber besitzt dann nicht eine Menge an Produkten, sondern eine Menge aus Artikeln, die sich bezüglich des Preises unterscheiden, aber bezüglich des Produktes identisch sind.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
TheLebkuchen24
User
Beiträge: 17
Registriert: Sonntag 18. Mai 2014, 14:27
Kontaktdaten:

Hyperion hat geschrieben:Ich würde Dir ans Herz legen, Dich mal mit Unit Tests auseinander zu setzen!
Evtl. habe ich doch kein Talent oder man "installiert" es auf solch eine abwägige Weise, wie sie nur lang Eingesessene noch kennen. Und die Installationsanleitung ist eher knapp und für mich nichtssagend. Hilfe! :K
Antworten