Projekt Schiffeversenken
Die Datenbank
Kurzer Exkurs zu Datenbanken, bevor wir das programmieren.
Der Begriff "Datenbank" wird oft synonym mit "Datenbanktabelle" oder kurz nur "Tabelle" genutzt. Zur Abgrenzung werde ich versuchen hier so genau wie möglich zu sein.
Die Daten selbst liegen in der Datenbanktabelle. Die Organisation der Datenbanktabelle obliegt dabei dem Datenbanksystem, in unserem Fall also SQLite. In einer Datenbank können viele verschiedene Datenbankabellen gespeichert sein, diese sollten allerdings immer einen fachlichen Bezug untereinander haben.
Zur Veranschaulichung mal ein Beispiel einer Autovermietung gemacht, da hatten wir eine Datenbanktabelle mit den Informationen zum "Kunden", eine weitere mit den Informationen zu unserem "Fuhrpark" und eine dritte Datenbanktabelle zur "Vermietung" selbst. Also 3 Datenbanktabellen in einer Datenbank namens "Autovermietung".
Machen wir es für unser Projekt konkret, die Datenbank wird bei uns "battle.db" heißen, die Datenbanktabelle in der die Informationen abgelegt sind, heißt "game".
Nun stellt sich die Frage, welche Informationen müssen wir überhaupt speichern? Dazu überlegen wir uns, welche Subjekte es in unserem Spiel gibt und welche Informationen wir zu diesen Subjekten benötigen.
| Subjekt | Erklärung |
|---|---|
| Kennung des Spiels | Ein Spiel besteht aus genau 2 Spielern, und diese müssen sich über eine eindeutige Kennung des Spiels zusammenfinden |
| Name Spieler 1 | Name des ersten Spielers |
| Spielfeld Spieler 1 | Informationen zum Spielfeld, also Position der Schiffe, Treffer- oder Wasserschüsse, des Gegners |
| Status Spieler 1 | z.B. "Spiel eröffnet", "wartet auf Zug", "hat gewonnen" |
| Name Spieler 2 | Name des zweiten Spielers |
| Spielfeld Spieler 2 | Wie oben, allerdings die Abbildung der gegnerischen Schiffe |
| Status Spieler 2 | z.B. "Spiel beigetreten", "am Zug", "hat verloren" |
Wenn wir diese Tabelle jetzt kippen, also Spalten werden zu Zeilen, dann haben wir schon die Struktur für eine Datenbanktabelle. Geben wir noch eine eindeutige ID dazu und füllen das gleich noch mit ein paar hypothetischen Inhalten, sehen wir auch, ob das Konzept passt:
| ID | Kennung des Spiels | Name Spieler 1 | Spielfeld Spieler 1 | Status Spieler 1 | Name Spieler 2 | Spielfeld Spieler 2 | Status Spieler 2 |
|---|---|---|---|---|---|---|---|
| 1 | ABCDEF | Adam | 1100TW... | am Zug | Adele | 1111WW... | wartet |
| 2 | GHIJKL | Berta | 1111100... | wartet | Ben | WW0000... | am Zug |
| 3 | MNOPQ | Casper | Spiel gestartet |
Legen wir uns zuerst eine neue version_02 an und schauen kurz auf die Änderungen in der Struktur (neue Komponenten = rot umrandet):
Wir erstellen den Ordner instance, die Datenbank wird dann durch die Anwendung selbst erzeugt.
Zusätzlich brauchen wir noch 2 Module, models.py und script.py.
Das Modul models.py enthält die Klassendefinition, das script.py ist eine Hilfe zur Erzeugung und Pflege der Datenbank.
Erweitert werden die Module routes.py und app_init.py.
Hinweis
Bevor Ihr alles per Hand kopiert, könnt Ihr Euch auch die Sourcen hier oder über den Button unten herunterladen.
Nachdem wir die leeren Hüllen angelegt haben, geht es weiter mit app_init.py. Der neue Code ist folgender:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
# Erstelle die db Instanz
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
# Konfiguration laden
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///battle.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'IrgendeinWortOderEineZahlWie42'
# Datenbank initialisieren
db.init_app(app)
# Blueprints registrieren
from routes import main
app.register_blueprint(main)
return app
Neu ist jetzt models.py. Hier tragen wir die Datenbanktabellenfelder als Klasse ein:
from app_init import db
class Game(db.Model):
id = db.Column(db.Integer, primary_key=True)
game_code = db.Column(db.String(6), nullable=False)
p_1_name = db.Column(db.String(100), nullable=True)
p_1_board = db.Column(db.String(100), nullable=True)
p_1_status = db.Column(db.String(100), nullable=True)
p_2_name = db.Column(db.String(100), nullable=True)
p_2_board = db.Column(db.String(100), nullable=True)
p_2_status = db.Column(db.String(100), nullable=True)
def __repr__(self):
return f"{self.game_code}"
Damit kommen wir zum Modul script.py. Nachfolgenden Code könnt Ihr einfach übernehmen:
from app_init import create_app, db
from models import Game
from sqlalchemy import create_engine, inspect
# Initialwerte
game_code_init = 'ABCDEF'
p_1_name_init = 'Adam'
p_1_board_init = '1000T0…'
p_1_status_init = 'am Zug'
p_2_name_init = 'Adele'
p_2_board_init = '1110T0…'
p_2_status_init = 'wartet'
app = create_app()
engine = create_engine('sqlite:///instance/battle.db', echo=True)
def drop_table():
inspector = inspect(engine)
if 'game' in inspector.get_table_names():
Game.__table__.drop(engine)
print("Tabelle 'Game' wurde gelöscht.")
else:
print("Tabelle 'Game' existiert nicht, Step wird übersprungen.")
def create_table():
with app.app_context():
try:
db.create_all() # Erstellt alle Tabellen
print("Tabellen wurden erstellt.")
except Exception as e:
print(f"Fehler beim Erstellen der Tabellen: {e}")
return # Funktion abbrechen, wenn Tabellen nicht erstellt werden konnten
try:
new_game = Game(game_code = game_code_init,
p_1_name = p_1_name_init,
p_1_board = p_1_board_init,
p_1_status = p_1_status_init,
p_2_name = p_2_name_init,
p_2_board = p_2_board_init,
p_2_status = p_2_status_init
)
db.session.add(new_game)
db.session.commit()
print("Initiales Spiel erfolgreich angelegt.")
except Exception as e:
db.session.rollback() # wichtig: Rollback bei Fehlern
print(f"Fehler beim Einfügen des initialen Spiels: {e}")
if __name__ == '__main__':
print('Start')
drop_table()
create_table()
print('Ende')
Okay, cool, showtime!
Da wir mit if __name__ == '__main__': in Zeile 50 den autonomen Programmstart des Skripts festgelegt haben, können wir das Skript in VSCode einfach mit dem Run-Button oben rechts starten. Bei mir kommt dann ein Terminal-Fenster mit der Info:
Wie können wir den Erfolg nun kontrollieren? Wie greifen wir auf die Datenbank zu?
Zum Betrachten der Inhalte reicht das Plugin "SQLIte Viewer" in VSCode:
Wenn wir das installieren, können wir die Datei per Doppelklick öffnen, das sieht dann so aus:
Als kostenlose Alternative habe ich wie oben beschrieben SQLiteStudio installiert. Die Installation von oben hat ja funktioniert, der erste Aufruf sollte bei Euch dann etwa so aussehen:
Mit "Database/Add a database"...
... und Navigation zu unserer battle.db in der version_02,...
...dann oben links auf "Connect to database"...
...und Rechtsklick auf game ein Select-Fenster aufmachen:
Im Fenster rechts dann erscheint die Abfrage mit allen Feldern. Oben auf den blauen Pfeil klicken und die Abfrage wird ausgeführt. In der Mitte sieht man dann das Ergebnis der Abfrage, unsere Daten sind angekommen, also alles okay!
0,000 Sekunden – das ist mal schnell...
Wunderbar, damit haben wir die Datenbank battle.db mit der Datenbanktabelle game erfolgreich eingebunden!
Eine genauere Beschreibung gibt es in der Projektdoku (Download hier). Den Sourcecode könnt Ihr hier oder über den Button unten herunterladen.
Inhaltsverzeichnis:
1. Vorwort2. Das Projekt
3. Vorarbeiten
4. Das Projekt „Schiffeversenken“
4.1. Der Funktionsumfang
4.2. Die Planung der Umsetzung
4.3. Das Coden
4.3.1 Arbeiten mit Flask
4.3.2 Die Datenbank
4.3.3 Der Spielstart
4.3.4 Der Spielcode
4.3.5 Die Spielfelder
4.3.6 Setzen der Schiffe
4.3.7 Das Spielen
4.4. Die Veröffentlichung
5. Abschluss









