Compare commits
2 Commits
3cd8ca1d0d
...
c958cc6634
Author | SHA1 | Date |
---|---|---|
qvalentin | c958cc6634 | |
qvalentin | f60eaf1da6 |
20
Code/Main.py
20
Code/Main.py
|
@ -11,6 +11,7 @@ if __name__ == "__main__":
|
|||
|
||||
"""
|
||||
Getting the args
|
||||
- own ip
|
||||
- own port
|
||||
|
||||
Optional: A neighbour:
|
||||
|
@ -22,22 +23,25 @@ if __name__ == "__main__":
|
|||
args = sys.argv
|
||||
|
||||
print(args)
|
||||
if len(args) >= 2:
|
||||
own_port = int(args[1])
|
||||
if len(args) >= 3:
|
||||
own_ip = args[1]
|
||||
own_port = int(args[2])
|
||||
else:
|
||||
print("using default ip 0.0.0.0")
|
||||
print("using default port 8080")
|
||||
own_ip = "0.0.0.0"
|
||||
own_port = 8080
|
||||
|
||||
neighbours = Neighbours(own_process=Member("0.0.0.0", own_port))
|
||||
neighbours = Neighbours(own_process=Member(own_ip, own_port))
|
||||
game_state = GameState(neighbours)
|
||||
server = Server(neighbours, game_state)
|
||||
|
||||
n_direction = Direction.LEFT
|
||||
if len(args) > 4:
|
||||
n_direction = args[4]
|
||||
if len(args) >= 4:
|
||||
n_ip = args[2]
|
||||
n_port = int(args[3])
|
||||
if len(args) > 5:
|
||||
n_direction = args[5]
|
||||
if len(args) >= 5:
|
||||
n_ip = args[3]
|
||||
n_port = int(args[4])
|
||||
neighbours.connect(Direction[n_direction], n_ip, n_port)
|
||||
|
||||
serverThread = threading.Thread(target=server.start)
|
||||
|
|
67
README.md
67
README.md
|
@ -4,12 +4,52 @@ Implementierung des berühmten [Conway's Game of Life](https://en.wikipedia.org/
|
|||
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+
|
||||
|
||||
```shell
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Ausführen
|
||||
|
||||
```shell
|
||||
python -m Code.Main own_ip own_port [ neighbour_ip neighbour_port (LEFT|RIGHT) ]
|
||||
```
|
||||
|
||||
### Beispiel (auf lokalem Rechner)
|
||||
|
||||
Als erster Teilnehmer
|
||||
|
||||
```shell
|
||||
python -m Code.Main 0.0.0.0 8080
|
||||
```
|
||||
|
||||
Als zweiter Teilnehmer
|
||||
|
||||
```shell
|
||||
python -m Code.Main 0.0.0.0 8081 0.0.0.0 8080 RIGHT
|
||||
```
|
||||
|
||||
Als dritter Teilnehmer
|
||||
|
||||
```shell
|
||||
python -m Code.Main 0.0.0.0 8082 0.0.0.0 8080 LEFT
|
||||
```
|
||||
|
||||
### Mit zwei Teilnehmern auf dem lokalen Rechner
|
||||
|
||||
```shell
|
||||
python -m Code.Main 0.0.0.0 8080 & sleep 2 && python -m Code.Main 0.0.0.0 8081 0.0.0.0 8080 RIGHT
|
||||
```
|
||||
|
||||
## 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,
|
||||
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.
|
||||
|
@ -19,30 +59,35 @@ verbunden wird, sondern nur, dass man räumlich **rechts** von Teilnehmer 1 lieg
|
|||
![Schema Verbindungsaufbau](./images/Verbindungsaufbau.drawio.svg)
|
||||
|
||||
## 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
|
||||
## 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
|
||||
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
|
||||
|
||||
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 zuerst die Ränder der benachbarten Teilenehmer abgefragt werden müssen und dies blockieriend 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 seine Randes 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.
|
||||
Da für jeden Entwicklungsschritt zuerst die Ränder der benachbarten Teilenehmer abgefragt werden müssen und dies blockieriend
|
||||
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 seine Randes 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.
|
||||
|
||||
|
||||
|
||||
<!-- LocalWords: Counter
|
||||
-->
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
pygame~=2.1.2
|
||||
requests~=2.27.1
|
Loading…
Reference in New Issue