diff --git a/Code/UI/Field.py b/Code/UI/Field.py index 0c778aa..8396e72 100644 --- a/Code/UI/Field.py +++ b/Code/UI/Field.py @@ -11,11 +11,22 @@ class Field: self.width = GeneralConfig.fields_amount_x + 2 self.height = GeneralConfig.fields_amount_y + 2 self.field_shift = -10 + self.shapes = Shapes() self.squares = self._creat_squares() - self.shapes = {Shape.VERTICAL_GLIDER: Shapes().creat_vertical_glieder} + + def get_square_on_click_pos(self, mousclick_pos): + index_x = 0 + index_y = 0 + for line in self.squares: + index_y += 1 + index_x = 0 + for square in line: + index_x += 1 + if square.rect.collidepoint(mousclick_pos): + return (index_y, index_x) def create_shape(self, shape: Shape, start_square_pos): - self.squares = self.shapes[shape](start_square_pos, self.squares) + self.squares = self.shapes.create_shape(start_square_pos,self.squares, shape) def _creat_squares(self): squares = [ diff --git a/Code/UI/PlayingField.py b/Code/UI/PlayingField.py index 604b82d..6d6e83f 100644 --- a/Code/UI/PlayingField.py +++ b/Code/UI/PlayingField.py @@ -33,18 +33,23 @@ class GameState: self.is_evolving = not self.is_evolving self.neighbours.toggle_pause(self.is_evolving) if event.key == pygame.K_v: - index_x = 0 - index_y = 0 - - for line in self.field.squares: - index_y += 1 - index_x = 0 - for square in line: - index_x += 1 - if square.rect.collidepoint(pygame.mouse.get_pos()): - self.field.create_shape(Shape.VERTICAL_GLIDER, - (int(index_y ), - int(index_x ))) + self.field.create_shape(Shape.VERTICAL_GLIDER_LEFT, + self.field.get_square_on_click_pos(pygame.mouse.get_pos())) + if event.key == pygame.K_b: + self.field.create_shape(Shape.VERTICAL_GLIDER_RIGHT, + self.field.get_square_on_click_pos(pygame.mouse.get_pos())) + if event.key == pygame.K_n: + self.field.create_shape(Shape.GLIDER_LEFT_DOWN, + self.field.get_square_on_click_pos(pygame.mouse.get_pos())) + if event.key == pygame.K_m: + self.field.create_shape(Shape.GLIDER_RIGHT, + self.field.get_square_on_click_pos(pygame.mouse.get_pos())) + if event.key == pygame.K_y: + self.field.create_shape(Shape.VERTICAL_GLIDER_RIGHT, + self.field.get_square_on_click_pos(pygame.mouse.get_pos())) + if event.key == pygame.K_x: + self.field.create_shape(Shape.VERTICAL_GLIDER_LEFT, + self.field.get_square_on_click_pos(pygame.mouse.get_pos())) def update_field_with_input(self, event): for line in self.field.squares: @@ -91,7 +96,7 @@ def run_game(game_state: GameState): time_elapsed_since_last_action += clock.get_time() if game_state.is_evolving: - if time_elapsed_since_last_action > 100: + if time_elapsed_since_last_action > 10: # start = ti.time() game_state.update_borders() game_state.evolve() diff --git a/Code/UI/Shape.py b/Code/UI/Shape.py index f9076fc..412aa5e 100644 --- a/Code/UI/Shape.py +++ b/Code/UI/Shape.py @@ -2,4 +2,11 @@ from enum import Enum class Shape(Enum): - VERTICAL_GLIDER = 1 + HORIZONTAL_GLIDER_LEFT = 1 + HORIZONTAL_GLIDER_RIGHT=2 + GLIDER_LEFT_DOWN = 3 + GLIDER_RIGHT=4 + VERTICAL_GLIDER_RIGHT=5 + VERTICAL_GLIDER_LEFT=6 + + diff --git a/Code/UI/Shapes.py b/Code/UI/Shapes.py index a7813e4..c6f54a2 100644 --- a/Code/UI/Shapes.py +++ b/Code/UI/Shapes.py @@ -1,23 +1,40 @@ +from operator import itemgetter + from Code import Config +from Code.UI.Shape import Shape class Shapes: - def creat_vertical_glieder(self, start_square_pos: tuple, squares): - points =[(0,0),(1,0),(2,0),(2,1),(1,2)] - if self._check_bounderies((6, 6), start_square_pos, squares): + def create_shape(self, start_square_pos: tuple, squares, type): + return self._create_shape(start_square_pos, squares, Structures.shapes[type]) + + def _get_size(self, points): + x = max(max(points, key=itemgetter(0))) + y = max(max(points, key=itemgetter(1))) + return (x, y) + + def _create_shape(self, start_square_pos: tuple, squares, points): + if self._check_bounderies(self._get_size(points), start_square_pos, squares): for point in points: - squares[start_square_pos[0]+point[1]][start_square_pos[1]+point[0]].active = True + squares[start_square_pos[0] + point[1]][start_square_pos[1] + point[0]].active = True return squares - - def _creat_shape(self,start_square_pos: tuple, squares): - - def _check_bounderies(self, bounderies, start_square_pos: tuple, field): - x_valid = (Config.GeneralConfig.fields_amount_x-2 - start_square_pos[1]) > bounderies[0] - y_valid = (Config.GeneralConfig.fields_amount_y-2 - start_square_pos[1]) > bounderies[1] - + print(bounderies) + delta_start_pos_x = start_square_pos[0] + delta_start_pos_y = start_square_pos[1] + x_valid = (Config.GeneralConfig.fields_amount_x - 2 - int(delta_start_pos_x)) > bounderies[0] + y_valid = (Config.GeneralConfig.fields_amount_y - 2 - int(delta_start_pos_y)) > bounderies[1] return x_valid and y_valid +class Structures: + shapes = {Shape.GLIDER_LEFT_DOWN: [(0, 0), (1, 0), (2, 0), (2, 1), (1, 2)], + Shape.HORIZONTAL_GLIDER_LEFT: [(0, 0), (1, 0), (2, 0), (3, 0), (4, 1), (4, 3), (0, 1), (0, 2), (1, 3)], + Shape.HORIZONTAL_GLIDER_RIGHT: [(0, 1), (0, 3), (1, 0), (3, 0), (2, 0), (4, 0), (4, 1), (4, 2), (3, 3)], + Shape.GLIDER_RIGHT: [(0, 2), (1, 0), (1, 2), (2, 1), (2, 2)], + Shape.VERTICAL_GLIDER_RIGHT: [(1, 0), (3, 0), (0, 1), (0, 3), (0, 2), (0, 4), (1, 4), (2, 4), (3, 3)], + Shape.VERTICAL_GLIDER_LEFT: [(0, 0), (0, 1), (0, 2), (0, 3), (1, 4), (3, 4), (1, 0), (2, 0), (3, 1)] + + } diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f9b730 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Game of Life über mehrere Monitore + +Implementierung des berühmten [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_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. + + +## 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. + +![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 +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. diff --git a/images/Verbindungsaufbau b/images/Verbindungsaufbau new file mode 100644 index 0000000..2292fb6 --- /dev/null +++ b/images/Verbindungsaufbau @@ -0,0 +1 @@ +1VjbcpswEP0aP9bDHecxdtJ20stkJmmTPnUUWECtQFTINs7XV4AEyCS+pGlIXhLt0e4i7e5Z1kzsRVp+YChPvtAQyMQywnJin00syzT9mfhXIZsG8U2jAWKGQ6nUAVf4HiSo1JY4hEJT5JQSjnMdDGiWQcA1DDFG17paRIn+1BzFMACuAkSG6A0OedKgM9fo8I+A40Q92TTkToqUsgSKBIV03YPs84m9YJTyZpWWCyBV8FRcGrv3j+y2B2OQ8UMM6EXp399/Kv6cej/n32buBY/Sd7Y8G9+oC0Mo7i9FynhCY5ohct6hc0aXWQiVV0NInc5nSnMBmgL8BZxvZDLRklMBJTwlchdKzG976x+Vq6nlSvGslK5rYaOEjLPNbV/om1VyZ1dLyrC5YXWtRwMnoYIuWQA7oqUKELEY+A49q02v4AXQFMR5hB0Dgjhe6edAskDjVq/LoVjINB6RUul3hchSPulaQB5KRXLm2V2RtzHpZX2dYA5XOaqvvxZU1jOGirwhV4TLKvPSwQoYh3J3WIdhUAa+JIbsDJYn5XWPZxJKehRT2LMHzhmTC4bGBf9ALpg6F/wX5IJ1IBceKYIX4sKQDF9BrCyPiDPP75hYxdXqO7A7nIXLLH57DdGcGqajF4I3O9lTCbV0CQyLQAOTYIQJWVBCWX1z2zPQiekLvOCM/obejnXme8ZIBeX9Y0HVpqeMoU1PIac440XP82UFdM3Ksbaa1WzrZbtH3xXJ1Iu5OUFX2u1Vnl7t1rDzW6+h09vGK+v0/iBQHf8LlEWsGgoHPYJBkFQ1so2P+tqYuv2eceAENXX77cLc0yqekePem3hpGGNm9InvgCMHgVE7/aFVMOoY7T04ObAh/a8BkwyStNobv9duT9WOP3KvnQ3ieAMiKoxUf7NByIIlW9U0aio6PK1+wwsxIKgocKAHUCfd/+9vT61YmRtj6jjGiZYe2x1kx30gOwo7bsgZTCXtVxX1dGcr6w11pdWu8WafoyZSA0fHjl1tWapqNrSxSx+jOmvlnkZRAXyyXcI7Ry0hdt9kGvXuy5Z9/hc= \ No newline at end of file diff --git a/images/Verbindungsaufbau.drawio.svg b/images/Verbindungsaufbau.drawio.svg new file mode 100644 index 0000000..0151560 --- /dev/null +++ b/images/Verbindungsaufbau.drawio.svg @@ -0,0 +1,4 @@ + + + +
T1 
T1 
Neue
Verbindung
Neue...
T2
T2
Verbindungsanfrage
rechts
Verbindungsanfrage...
Neuer
Teilnehmer
Neuer...
Weiterleiten
Weiterleit...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/images/Verbindungsaufbau.png b/images/Verbindungsaufbau.png new file mode 100644 index 0000000..b2ba945 Binary files /dev/null and b/images/Verbindungsaufbau.png differ