qvalentin eac4c87639 | ||
---|---|---|
Code | ||
images | ||
.gitignore | ||
README.md | ||
requirements.txt |
README.md
Game of Life über mehrere Monitore
Implementierung des berühmten Conway's Game of Life als verteiltes System. Jeder Teilnehmer hat einen Bereich in dem er Game of Life berechnet und seine Kanten mit den andren Teilnehmer austauscht. Die gesamte Koordination erfolgt dezentral, Kommunikation ist immer p2p-basiert.
Requirements
- Python 3+
pip install -r requirements.txt
Ausführen
python -m Code.Main own_ip own_port [ neighbour_ip neighbour_port (LEFT|RIGHT) ]
Beispiel (auf lokalem Rechner)
Als erster Teilnehmer
python -m Code.Main 0.0.0.0 8080
Als zweiter Teilnehmer
python -m Code.Main 0.0.0.0 8081 0.0.0.0 8080 RIGHT
Als dritter Teilnehmer
python -m Code.Main 0.0.0.0 8082 0.0.0.0 8080 LEFT
Steuerung
Es gibt zwei Zustände, pausiert und nicht pausiert. Zwischen diesen kann mit 'Leertaste' gewechselt werden. Im pausierten Zustand können mit der linken Maustaste eigene Strukturen gezeichnet werden. Mit der unteren Reihe der Tastatur (y,x,v,b,n,m) können vorgefertigte Strukturen (Glider und Spaceships) gespawnt werden.
Einstieg
Um an dem Spiel teilzunehmen, muss jeder Teilnehmer (außer der erste) beim Starten der Anwendung die IP-Adresse und den Port eines anderen Teilnehmers angeben, mit dem er sich verbinden will. Zusätzlich muss die Richtung der Kante, mit welcher er sich mit diesem verbinden will, angegeben werden. Da jeder Teilnehmer an jeder seiner Kanten maximal mit einem anderen Teilnehmer verbunden sein kann, ist es möglich, dass die Kante, die beim Einstieg gewählt wird, bereits besetzt ist. Ist dies der Fall, wird der Anfragende automatisch an denjenigen weitergeleitet, der diese Kante besetzt. Dies passiert so lange bis eine freie Kante gefunden wird und sich somit zwei Teilnehmer verbinden. Wenn man sich also rechts von Teilnehmer 1 verbinden will, ist es nicht garantiert, dass man direkt mit Teilnehmer 1 verbunden wird, sondern nur, dass man räumlich rechts von Teilnehmer 1 liegt.
Suche
Die Suche ist recht simpel. Jeder Teilnehmer kennt nur die Knoten, welche die für ihn relevanten Informationen haben und diese verändern sich im Laufe der Zeit auch nicht.
Verbreitung
Eine relevante Information, die an das gesamte Netz verbreitet werden muss, ist eine Zustandsänderung bezüglich der Pausierung der Simulation. Hierfür wird einfaches Flooding verwendet. D.h. jeder Teilnehmer schickt den neuen Zustand an alle seine Nachbarn und diese senden ihn wiederum an ihre Nachbarn. Da es nur zwei mögliche Zustände für den Pause-Zustand geben kann, ist Flooding ausreichend effizient, zyklische Nachrichten werden vermieden, indem ein Teilnehmer die Information nicht mehr weiterleitet, wenn er sie bereits bekommen hat, sich also schon im richtigen Zustand befindet.
Zeitliche Synchronisation
Um sicherzustellen, dass alle Teilnehmer gleichzeitig den Entwicklungsschritt durchführen und somit der Randaustausch auch korrekt funktioniert, wird eine vereinfachte Version von Lamport Clocks verwendet. Jeder Prozess hat einen Counter, den er bei jedem Entwicklungsschritt um eins erhöht. Da für jeden Entwicklungsschritt (Tick) zuerst die Ränder der benachbarten Teilenehmer abgefragt werden müssen und dies blockierend geschieht, sind alle Teilnehmer zu jedem Zeitpunkt um maximal 1 bezüglich ihres Counters versetzt. Damit man jedoch mit seinem Entwicklungsschritt nicht warten muss, bis alle Nachbar den Rand angefragt haben, hält jeder Prozess eine Kopie seiner Ränder vom vorherigen Zeitpunkt. Bei der Anfrage nach dem Rand wird der nachgefragte Counter mitgeschickt, dieser muss dem aktuellen oder dem vorherigen Counter entsprechen oder um eins größer sein, als der aktuelle Counter. Ist der angefragte Counter um eins größer als der aktuelle, liegt also quasi in der Zukunft, wird der Request blockiert, bis der Angefragte den nächsten Entwicklungsschritt durchgeführt hat. Somit wird das gesamte Game of Life nur so schnell ausgeführt, wie der langsamste Teilenehmer ist.
Die Synchronisation kann auch als Variante des Awerbuch Synchronisierers angesehen werden. Die Nachrichten über den Randaustausch entsprechen den Nachrichten beim Awerbuch Synchronisier, da sie den Counter/Tick enthalten und blockierend sind, es wird also auf ein ACK gewartet (das wir quasi durch die tiefer liegenden Schichten gemacht). Anstatt jedoch ein Safe zu verschicken, starte jeder Teilnehmer sofort den nächsten Tick und speichert seine Werte vom vorherigen Tick zwischen. Die Option, den Entwicklungsschritt erst durchzuführen, nachdem alle Nachbarn die Kanten angefragt haben, wurde bei der Implementierung erwogen, die Zwischenspeicherung der vorherigen Ränder erschien jedoch einfacher.
Technische Implementierung
Das GUI ist mit pygame implementiert. Parallel dazu läuft in einem separaten Thread ein simpler Webserver, über den die Kommunikation mit den anderen Teilnehmern erfolgt.
Dieser stellt folgende Endpoints bereit:
- /connect: Für den Verbindungsaufbau
- /pause: Für das Pausieren und Starten von GOL
- /border: Für den Randaustausch
TODOS/Probleme (won't fix)
- Sauberer Shutdown
- Joinen wenn nicht pausiert
- Zwei Nachbarn als Parameter angeben
- 2D Gebiete (also verbinden an allen vier Seiten)