[Kivy] Sokoban

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Astorek
User
Beiträge: 72
Registriert: Samstag 24. Januar 2009, 15:06
Kontaktdaten:

Hi @ all,

Vielleicht erinnert sich noch der ein oder andere Forenmember: Ich hab mal vor ewigen Zeiten (2010) bereits einen Sokoban-Clone geschrieben, der quelltextmäßig alles andere als gut war. Ich dachte, ich hätte in den letzten Jahren etwas gelernt und wollte erneut eine Umsetzung des Spiels schreiben^^.

Zu Sokoban selbst: Ziel des Spiels ist es, Kisten zu Kistenzielen zu schieben. Allerdings hat der Spieler nur begrenzt Platz und kann Kisten auch wirklich nur schieben, nicht ziehen. Was die Ganze Sache zu der einen oder anderen Kopfnuss führt; im Grunde ist es also ein Rätsel- oder Knobelspiel. Bedienung ist: WASD, Pfeil- oder Nummerntasten für die Bewegung, "u" für einen Schritt zurück.

Downloadlink der Sourcen: https://mkcloud.goip.de/downloads/mksok ... an_src.zip (ca. 811 KB groß)

Programm wurde unter Python 2.7 und 3.6 erfolgreich unter Windows und Android getestet, sollte aber auch unter Linux und Macs laufen. Wer das Spiel auf Android zum Laufen bringen möchte, lädt sich vom Google PlayStore den "Kivy Launcher" runter und kopiert das Verzeichnis "mksokoban" in den Ordner "/sdcard/kivy/" rein (ggf. erstellen). Unter Windows lässt sich Kivy mit der entsprechenden Einrichtungsseite sehr leicht installieren (das auf der Seite erwähnte, optionale "kivy.deps.gstreamer" braucht ihr nicht, um Sokoban ausführen zu können). Linux-User müssen über den Paketmanager entsprechende Kivy-Pakete runterladen und installieren.

Screens:
Bild Bild

Ich dachte, es wäre vielleicht interessant, meine Gedanken während des Programmierens festzuhalten, weshalb ich euch nun damit behellige/langweile^^:
  • Irgendwie hab ich mir angewöhnt, "MVC"-mäßig das Ganze aufzuteilen (was im Laufe des Projekts aber nicht immer gut klappte^^). Da gibts etwa die "models.py", welche quasi die kompletten Spielregeln und Geschäftslogik beinhaltet. Zum Testen hab ich mir einen Viewer geschrieben, der in der Kommandozeile bedient wird ("cmdviewer.py"). Sollte übrigens immernoch einwandfrei laufen, wenn man in der "main.py" die Zeilen 5 und 6 entsprechend anpasst. Auf alle Fälle ist das Ding perfekt dafür geeignet, das Modell auf Herz und Nieren zu testen, bevor man sich an die Umsetzung der Grafiken wagt. Die Kommunikation findet über Events statt: Der Viewer schnappt sich die Modelle und bindet die Events, die besagte Modelle bereitstellt, an eigene Methoden. Auf einen echten "Controller" in dem Sinne habe ich verzichtet...
  • Fallstrick: Ich hielt es beim Modell für eine gute Idee, "verbesserte" Versionen zu erstellen und davon abzuleiten (z.B. das "SokobanGame"-Modell mit einem Schritt-Zähler zu erweitern). Erst im Nachhinein fielen mir Methoden auf, die bei Fehlern einfach aus der Methode rausspringen, aber beim "Erben" besagter Methoden logischerweise nicht. Was leider zu fünf, sechs Zeilen kruden Code zu den entsprechenden Methoden geführt hat und ich keinen Nerv mehr hatte, das Ganze neu zu überdenken.
  • GUI-Programmierung nervt. Wahrscheinlich habe ich auch nicht genügend Kenntnisse in dem Bereich... Ich hab trotzdem nicht damit gerechnet, dass beim "kivyviewer.py" ein Drittel mehr Quelltextzeilen existieren als bei der "model.py".
  • Generell fand ich es nach längerer Zeit ziemlich dumm von mir, jegliche mögliche Klasse beim Viewer in ein- und derselben Datei unterzubringen. Am Anfang hielt ich das für eine gute Idee (bin eher ein Freund von wenigen Quelltextdateien), später war ich froh, in einer IDE mit Komfortfunktionen programmiert zu haben beim dauernden Hin- und Herspringen^^. Lektion ist diesbezüglich gelernt, sowas werde ich nie wieder machen und war gegen Ende richtig frustrierend für mich^^.
  • Was mir auffällt: Ich weiß, Singletons sind in den allermeisten Fällen phöse, trotzdem finden sich bei Quelltextbeispielen - gerade wenn es um die Entwicklung von Spielen geht - überraschend viele Beispiele dafür. Ob das auf Unwissenheit beruht, wage ich stellenweise ernsthaft zu bezweifeln, da ich hier z.T. recht aktuelle Bücher über Spieleprogrammierung habe und ebenfalls hie und da Singletons verwenden.
    • Andererseits eignen sich Singletons (leider) perfekt dafür, nicht so ganz durchdachte Kapselung der Klassen und Methoden zu kaschieren (*hust*). Ich hab mich auch das ein oder andere Mal dabei ertappt, dass ich eigentlich eine Info einer Methodenvariable bräuchte, die sich allerdings in einer anderen Klasse befand...
  • Achja, pep8: Hab ich zum Großteil umgesetzt, aber rund 99% der Fehler werden von Leerzeilen geworfen, die eingerückt wurden. Sorry, werd ich auch nicht ändern, da ich sonst mit JEDER IDE wahnsinnig werden würde und zudem die Lesbarkeit wirklich nicht einschränkt - es sei denn, jemand will sichtbare Leerzeichen haben...
  • Generell muss ich sagen, dass mich insbesondere die Programmierung der GUI ziemlich geschlaucht hat. Ich glaub, ich hab mehr als 50% der Zeit nur damit verbracht. Alleine deswegen verstehe ich jetzt (besonders bei umfangreichen Programmen) mehr als deutlich, warum es nicht zu jedem Programm eine grafische Benutzeroberfläche gibt^^. Ich will in nächster Zeit von Kivy echt nichts mehr sehen und bin froh, das Programm endlich fertiggestellt zu haben und es in nächster Zeit nicht mehr anzufassen^^.
Tjoar, wünsche euch viel Spaß beim Ausprobieren! :)
Benutzeravatar
BigZ
User
Beiträge: 30
Registriert: Mittwoch 1. Juli 2015, 21:18
Wohnort: Hamburg
Kontaktdaten:

Warum sind Singletons Böse? :O

Programm schaue ich mir gleich auch mal an :D
Schon überlegt es mit buildozer zu kompilieren und im Playstore zu veröffentlichen? :mrgreen:
Das Spielprinzip klingt ja schon mal ganz cool, oder ist es noch nicht in einem Zustand das man es auf die breite Öffentlichkeit los lassen kann?
Und UI Programmierung ist doch eigentlich immer anstrengend, oder? :mrgreen:
Darum gibt es ja auch Programme mit denen man sich UIs zusammen klicken kann. :D
Sogar für Kivy gibt es da schon was, aber das ist wohl noch ziemlich in der Alpha Phase klick
Ich hoffe ja darauf das die Entwicklung da noch ein bisschen voran geht, aber am entspanntesten ist es wohl immer noch in Java Apps zu programmeiren.
Danke fürs teilen! *und gleich mal ausprobier*
"Ist noch λ?"
"Ja, aber das ϕ ist noch ϱ"
BlackJack

@BigZ: Globaler Zustand ist Böse™. Und Singletons sind globaler Zustand.
Astorek
User
Beiträge: 72
Registriert: Samstag 24. Januar 2009, 15:06
Kontaktdaten:

Argh, natürlich muss mir noch ein mieser Fehler unterlaufen: Die GUI fürs Levelauswahl wird nicht richtig gelöscht; dann kann es passieren, dass man mitten im Spiel plötzlich zu einem anderen Level teleportiert wird, wenn man auf einen "Nicht-Button" klickt (bzw. mit der Hand keinen Button sondern vermeintlich den Hintergrund erwischt). Sollte nun nichtmehr auftreten; hab die Datei vor ca. einer Minute ausgetauscht...
Astorek
User
Beiträge: 72
Registriert: Samstag 24. Januar 2009, 15:06
Kontaktdaten:

Hab mal mit PyInstaller eine EXE-Datei erstellt, für alle Windows-User, die weder Python noch Kivy installieren möchten:
https://www.dropbox.com/s/khlqpg3mex8zo0y/mksokoban.zip (ca. 25 MB groß; enthält auch Quelltext)
BigZ hat geschrieben:Warum sind Singletons Böse?
Ums sehr vereinfacht zu sagen: Weil Singletons es viel zu einfach machen, dem eigentlichen Ziel von OOP-Code (nämlich die Wiederverwendbarkeit) entgegenzuarbeiten. Singletons verleiten gerade dazu, sich nicht näher mit OOP zu beschäftigen, schließlich hat man ja ein globales Objekt, dem man einfach neue Methoden ranflanschen kann...

Über Sinn und Unsinn von Singletons hab ich schon Forenbeiträge (in anderen Foren) mit teils über 50 Seiten gesehen, da gibts einige recht interessante Ansichten dazu^^.

Ich muss ja selbst zugeben, dass ich nur deswegen einen Singleton verwende (nämlich beim Lesen und Schreiben einer Konfigurationsdatei), weil mir viel zu spät aufgefallen ist, dass die Verteilung der einzelnen Fenster in meinem Programm Murks ist^^.
Schon überlegt es mit buildozer zu kompilieren und im Playstore zu veröffentlichen?
Wär Just4Fun mal eine Idee, hab aber derzeit kein Linux zur Hand um "mal eben schnell" eine APK zu erstellen. Zumal es mit dem Kivy Launcher auch einwandfrei läuft und es für Sokoban-Freunde schon mehr als genug (z.T. auch deutlich bessere) Varianten des Spiels gibt (und auch alle die Levels nutzen, die mal ein paar freundliche Zeitgenossen ins Internet gestellt haben^^).
Das Spielprinzip klingt ja schon mal ganz cool, oder ist es noch nicht in einem Zustand das man es auf die breite Öffentlichkeit los lassen kann?
Eigentlich wollte ich noch "on_touch"-Methoden einbauen (beim Wischen in die entsprechende Richtung sollte der Spieler auch dahin gehen), hatte aber keine Motivation mehr dafür^^. Vielleicht später mal... Ansonsten ist es IMHO in einem guten und stabilen Zustand... Denke ich^^.
Und UI Programmierung ist doch eigentlich immer anstrengend, oder?
Darum gibt es ja auch Programme mit denen man sich UIs zusammen klicken kann.
Sogar für Kivy gibt es da schon was, aber das ist wohl noch ziemlich in der Alpha Phase klick
Ich halte ehrlichgesagt nicht soviel davon, mit einem GUI-Designer zu arbeiten, wenn man die Funktionsweise des GUI-Toolkits (wenigstens in groben Maßen) noch nicht verstanden hat, und das war bei mir der Fall^^. Es kann halt vorkommen, dass ein GUI-Designer Quelltext erstellt, den man selbst nicht 100%ig versteht und wenn dann ein Bug auftritt, sitzt man da und darf erstmal in Erfahrung bringen, wie welcher Bug wann warum ausgelöst wurde^^...
Benutzeravatar
BigZ
User
Beiträge: 30
Registriert: Mittwoch 1. Juli 2015, 21:18
Wohnort: Hamburg
Kontaktdaten:

Ich hab ihn bislang auch immer nur dann benutzt wenn ich mal eine einzelne Instanz brauchte die kein zweites Mal geladen werden soll zur Laufzeit.
So was wie ne Konfigdatei, oder in Verbindung mit next.js als Singleton Store der den State der aktuellen Sitzung hält über die views hinweg wo ich einen einzelnen Store brauchte.
Ich hätte nie gedacht das es Leute gibt die Singletons für jeden Scheiß missbrauchen ^^

Spiel funzt bei mir btw. super.
Krieg bislang aber noch nicht mal die ersten Level geknackt x(
"Ist noch λ?"
"Ja, aber das ϕ ist noch ϱ"
Astorek
User
Beiträge: 72
Registriert: Samstag 24. Januar 2009, 15:06
Kontaktdaten:

BigZ hat geschrieben:Spiel funzt bei mir btw. super.
Krieg bislang aber noch nicht mal die ersten Level geknackt x(
Stimmt, das sind schon etwas haarsträubende Levels dabei^^. Wobei die von David W. Skinner noch die Einfachsten sind was ich so gefunden habe, andere Levelbauer sind da recht heftig^^.

Hab btw. eine neue Version hochgeladen mit mehr kostenlosen Levelsets von anderen Leuten, Touch-Funktion und jetzt einer APK-Datei für Android :!: . Hab jeweils die Downloadlinks erneuert (d.h. jeder von mir hier gepostete Link führt zur jeweils aktuellen Version), halte hier aber einfach nochmal alle Downloadlinks der Übersicht halber fest:
  • Sourcecode: mksokoban_src.zip (ca. 1 MB; unter Android lauffähig mit dem "Kivy Launcher" aus Googles PlayStore, wenn man die ZIP-Datei in "/sdcard/kivy/" entpackt)
  • EXE-Datei für Windows-Benutzer (inkl. Sourcecode): mksokoban.zip - Dropbox-Link (ca. 25 MB)
  • Ganz neu: Android-APK mksokoban.apk (ca. 8 MB; Die Installation von Apps unbekannter Herkunft muss ggf. eingeschaltet werden; gebaut mit "Buildozer" im Debug-Mode)
Source wurde erfolgreich unter Python 2.7 und 3.6 unter Windows 10 und Ubuntu 16.04 getestet; "Kivy" muss allerdings ordnungsgemäß installiert worden sein, wenn man den Sourcecode mit Python starten möchte. Startdatei ist die "main.py". Spiel sollte (egal ob als Source, als EXE oder als APK), soweit ich das beurteilen kann, bugfrei und stabil sein. Und ja, ich geb zu, ich bin etwas stolz auf das Progrämmchen :)
BlackJack

@Astorek: Entweder hat die `__init__.py` nichts in dem Verzeichnis verloren, oder Du solltest aus so einigen relativen Importen besser absolute machen. Und ich würde die `__init__.py` dort lassen. Und eventuell aus der `main.py` eine `__main__.py` machen.

Ich habe dann mal in den `kiviviewer` rein geschaut.

So einige Kommentare über Funktionen etc. wären als Doctrings geeignet.

`Configfile` ist ja nicht so wirklich ein Singleton. Zumindest ”interessant” implementiert.

Keine der ”Methoden” ist tatsächlich eine, weil `self` nicht verwendet wird. Was aber problemlos möglich wäre, denn man kann dort statt `Configfile` überall `self` verwenden — mit einer Ausnahme: der Zuweisung an `is_open`.

`get()` und `put()` bieten sich IMHO *sehr* dazu an sie als `__getitem__()` und `__setitem__()` zu implementieren.

Bei `get_stat()` und `put_stat()` wiederholt sich der Code um den Schlüssel zu erstellen. Und der ist fehlerhaft. Wenn Levelsetnamen mit Ziffern enden und Level aus Ziffern bestehen, kann es hier zu Mehrdeutigkeiten kommen. `get_stat()` ist auch ein bisschen umständlich ausgedrückt.

Code: Alles auswählen

    def get_stat(self, levelset, level):
        """Gibt Stats eines Levelsets zurueck.
        
        Existieren keine Stats, wird None zurueckgegeben.
        """
        key = '{}_{}'.format(levelset, level)
        if key in Configfile.FILE:
            stat = self.FILE.get(key)
            return [stat['steps'], stat['time']]
        return None
Ein ``if``/``else`` im dann in Abhängigkeit der Bedingung etwas auf `True` oder False` zu setzen ist unnötig umständlich, denn die Bedingung ist ja bereits dieser Wert auf den man etwas setzen möchte. Also:

Code: Alles auswählen

            if Configfile().get(option):
                switch.active = True
            else:
                switch.active = False

            # <=>

            switch.active = Configfile().get(option)  # ggf. noch ein `bool()`.

            # Oder falls `__getitem__` statt `get()`:

            switch.active = Configfile()[option]
Ähnlich dann bei der umgekehrten Operation:

Code: Alles auswählen

            if switch.active:
                value = 1
            else:
                value = 0
            Configfile().put(option, value)

            # <=>

            Configfile().put(option, int(switch.active))

            # oder falls `__setitem__` statt `put()`:

            Configfile()[option] = int(switch.active)
Die `Level`-Klasse erscheint mir ein bisschen zu monolithisch.

`str()`-Aufrufe für Argumente von `format()` sind nicht nötig.

Code nach folgendem dem Muster wiederholt sich zu oft:

Code: Alles auswählen

        if Configfile().get('sound_move'):
            self.snd_move.stop()
            self.snd_move.play()
Ich würde versuchen das ``if`` durch ein Null-Objekt zu beseitigen, den Soundobjekten die dort verwendet werden eine `play()`-Methode verpassen die automatisch vorher `stop()` aufruft, so dass hier am Ende nur noch das hier stehen bleibt:

Code: Alles auswählen

        self.snd_move.play()
Astorek
User
Beiträge: 72
Registriert: Samstag 24. Januar 2009, 15:06
Kontaktdaten:

Ich weiß, ich bin minimal spät dran (2 Monate *hust*), aber völlig unbeantwortet will ich den Beitrag von BlackJack auch nicht lassen^^ (Und Danke für deinen Beitrag, der mir sehr hilft^^):
BlackJack hat geschrieben:@Astorek: Entweder hat die `__init__.py` nichts in dem Verzeichnis verloren, oder Du solltest aus so einigen relativen Importen besser absolute machen. Und ich würde die `__init__.py` dort lassen. Und eventuell aus der `main.py` eine `__main__.py` machen.
Ist notiert. Die __init__.py ist irgendeine Eigenheit der eric6-IDE, die ich verwende. Das Ding erstellt zu jedem neuen Projekt ungefragt besagte Datei^^.
`Configfile` ist ja nicht so wirklich ein Singleton. Zumindest ”interessant” implementiert.
So wie meine Mathelehrerin in der Schule immer sagte, dass meine Lösungswege "interessant" waren? :D Stimmt, strenggenommen ist es kein Singleton, ich nutze es nur auf diese Art und Weise, in der ich fast nur Klassen- statt Objektvariablen nutze^^.
Keine der ”Methoden” ist tatsächlich eine, weil `self` nicht verwendet wird. Was aber problemlos möglich wäre, denn man kann dort statt `Configfile` überall `self` verwenden — mit einer Ausnahme: der Zuweisung an `is_open`.
Das war zugegeben auch der Sinn dahinter. Ich wollte mehr oder weniger eine "Funktion", mit der ich einfach in eine Datei lesen und schreiben konnte, wie ich lustig bin, ohne mich dauernd mit dem Öffnen/Schließen der Datei beschäftigen zu müssen. Ich weiß, strenggenommen eher ein Anti-Pattern...
Bei `get_stat()` und `put_stat()` wiederholt sich der Code um den Schlüssel zu erstellen. Und der ist fehlerhaft. Wenn Levelsetnamen mit Ziffern enden und Level aus Ziffern bestehen, kann es hier zu Mehrdeutigkeiten kommen.
:o Das habe ich garnicht bedacht. Danke für den Hinweis, das wär komplett an mir vorbeigegangen^^.
Die `Level`-Klasse erscheint mir ein bisschen zu monolithisch.
Das stimmt, ich hab immernoch ein wenig Probleme damit, "prozedurales Denken" aus dem Kopf zu bekommen^^. Ist auch wieder so ein Ding, wo mir später Ideen zur Erweiterung kamen und im Nachhinein fälschlicherweise überzeugt davon war, dass ich die dort reinstecken muss, statt das weiter zu kapseln (bspw. der Levelauswahl-Bildschirm, der hat eigentlich dort nichts verloren und werde ich beim nächsten Projekt sicher anders machen...)

Nochmal Danke für die vielen Verbesserungsvorschläge. :)

---

Noch kurz zum Spiel selbst für alle, die es interessiert: Hab den mittlerweile in Googles Playstore hochgeladen:
https://play.google.com/store/apps/deta ... .mksokoban

Warum? Weil ichs kann :P . Was ich auch dringend dazusagen will (weil ich die Erfahrung gemacht habe, dass die Android-Vorstellungsforen etwas anders funktionieren): Ich hab das Ding tatsächlich nur deswegen im Playstore hochgeladen, weil ichs wollte und es irgendwo cool finde^^. Dort steckt weder Gewinn- noch Werbeabsichten dahinter, die App hat keinerlei Werbung oder Ad-/Spyware integriert, und auch sonst sehe ich mein Projekt weiterhin "nur" von der Hobby-Perspektive. Ich verdiene mein Geld anders. Und ja, das musste ich extra erwähnen, weil ich woanders (in anderen Foren) dafür eins auf den Deckel gekriegt hatte, dass ich das nicht klargemacht habe^^. (In Android-Foren ist es scheinbar üblich, dass selbst simpelste Spiele der Marke "TicTacToe" von 16-Jährigen Hobbyprogrammierern Werbung schalten, womit ich nicht gerechnet hatte, aber das ist ein anderes Thema...) Die Berechtigung der App, auf SD-Inhalte lesend und schreibend darauf zugreifen zu können, existiert nur deswegen, weil ich keine Ahnung habe wie man das in Kivy ausstellen kann und es dafür auch einen Bugreport bei Kivy gibt^^.

Wer gerade am PC surft und die Google PlayStore-Seite mit QR-Code aufrufen will:
https://imgur.com/Hlju2MF

(Ich gebs ja zu, ich gebe ein wenig an damit ;) . Ich finds toll eine App im PlayStore zu haben^^)
Antworten