diff --git a/Code/Communication/APIRequests.py b/Code/Communication/APIRequests.py index ceaf50f..3c27918 100644 --- a/Code/Communication/APIRequests.py +++ b/Code/Communication/APIRequests.py @@ -14,8 +14,10 @@ class APIRequests: jsonValue = response.json() return Member(jsonValue["ip"], jsonValue["port"]) - def get_edge(self, target: Member, direction: Direction): - response = requests.get(f"http://{target.ip}:{target.port}/border/{direction.name}") + def get_edge(self, target: Member, direction: Direction,counter:int): + print(f"Getting {direction} edge from {target} with url http://{target.ip}:{target.port}/border/{direction.name}") + response = requests.get(f"http://{target.ip}:{target.port}/border/{direction.name}?counter={counter}") + print(f"Got {direction} edge from {target}") return response.json() def toggle_pause(self, neighbour: Member, new_state: bool): diff --git a/Code/Communication/EdgeSync.py b/Code/Communication/EdgeSync.py new file mode 100644 index 0000000..67965f6 --- /dev/null +++ b/Code/Communication/EdgeSync.py @@ -0,0 +1,28 @@ +from Code.Communication.Direction import Direction + + +class EdgeSync: + + def __init__(self, current_edges: dict, counter: int): + self.old_edges = {} + self.current_edges = current_edges + self.counter = counter + self.counter_old = counter - 1 + + def get_edge(self, edg_pos: Direction, counter: int) -> list: + while counter == self.counter + 1: + pass + + if counter == self.counter: + return self.current_edges[edg_pos] + elif counter == self.counter_old: + return self.old_edges[edg_pos] + else: + raise ValueError(f"Requested edge for counter {counter}, but having counter {self.counter} ") + + + def update_edges(self, new_edges, counter: int): + self.counter_old = self.counter + self.counter = counter + self.old_edges = self.current_edges + self.current_edges = new_edges diff --git a/Code/Communication/Neighbours.py b/Code/Communication/Neighbours.py index 3cb66f0..a0cc020 100644 --- a/Code/Communication/Neighbours.py +++ b/Code/Communication/Neighbours.py @@ -25,10 +25,9 @@ class Neighbours: self.neighbours[direction] = member return self.own_process, True - def get_edge(self, direction: Direction): + def get_edge(self, direction: Direction,counter:int): if direction in self.neighbours: - print(f"Getting ghost edge from {self.neighbours[direction]}") - return self.api.get_edge(self.neighbours[direction], mirror(direction)) + return self.api.get_edge(self.neighbours[direction], mirror(direction),counter) elif direction == Direction.RIGHT or direction.LEFT: return [False] * GeneralConfig.fields_amount_y diff --git a/Code/Communication/RequestHandler.py b/Code/Communication/RequestHandler.py index 9a5c5d8..0e0b484 100644 --- a/Code/Communication/RequestHandler.py +++ b/Code/Communication/RequestHandler.py @@ -1,5 +1,6 @@ import json import re +import urllib.parse from dataclasses import asdict from http.server import BaseHTTPRequestHandler @@ -16,19 +17,25 @@ class RequestHandler(BaseHTTPRequestHandler): print("got Get request") if self.path == "/": self.send_response(200, "running") + self.end_headers() + return elif self.path.startswith("/border/"): - direction = re.findall("/border/(left|right)", self.path, flags=re.IGNORECASE)[0] - cells = self.game_state.field.get_edge(Direction[direction.upper()]) - + direction = re.findall("/border/(left|right).*", self.path, flags=re.IGNORECASE)[0] + params = urllib.parse.parse_qs(self.path) + counter = int(params[list(params.keys())[0]][0]) + print(f"Getting {direction} edge for counter {counter}") + cells = self.game_state.edge_sync.get_edge(Direction[direction.upper()], counter) + print(f"Serving {direction} edge") self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(cells).encode('utf8')) + return else: self.send_response(404) self.end_headers() - + return """ /connect/right @@ -53,25 +60,30 @@ class RequestHandler(BaseHTTPRequestHandler): self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(asdict(neighbour)).encode('utf8')) + return else: self.send_response(307) self.send_header('Location', f"http://{neighbour.ip}:{neighbour.port}{self.path}") self.end_headers() + return elif self.path.startswith("/pause/"): new_state = re.findall("/pause/(stop|start)", self.path, flags=re.IGNORECASE)[0] == "start" - print(f"pause endpoint {new_state} old state {self.game_state.pause_for_input}") + print(f"pause endpoint {new_state} old state {self.game_state.is_evolving}") - if new_state == self.game_state.pause_for_input: + if new_state == self.game_state.is_evolving: print("got pause signal but already in the correct state") self.send_response(200) self.end_headers() + return else: self.send_response(200) - self.game_state.pause_for_input = new_state + self.game_state.is_evolving = new_state self.neighbours.toggle_pause(new_state) self.end_headers() + return else: self.send_response(404) self.end_headers() + return diff --git a/Code/Communication/Server.py b/Code/Communication/Server.py index b3e24a0..90f9204 100644 --- a/Code/Communication/Server.py +++ b/Code/Communication/Server.py @@ -1,10 +1,16 @@ from http.server import HTTPServer +from socketserver import ThreadingMixIn from Code.Communication.Neighbours import Neighbours from Code.Communication.RequestHandler import RequestHandler from Code.UI.PlayingField import GameState +class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): + """ This class allows to handle requests in separated threads. + No further content needed, don't touch this. """ + + class Server: def __init__(self, neighbours: Neighbours,game_state:GameState): self.server = None @@ -21,6 +27,6 @@ class Server: RequestHandler.neighbours = self.neighbours RequestHandler.game_state = self.game_state print(f"HTTP Server Running on {self.ip}: {self.port}") - self.server = HTTPServer((self.ip, self.port), RequestHandler) + self.server = ThreadedHTTPServer((self.ip, self.port), RequestHandler) self.server.serve_forever() print("Stopped server") diff --git a/Code/Config.py b/Code/Config.py index ea0ef72..6432d3a 100644 --- a/Code/Config.py +++ b/Code/Config.py @@ -11,10 +11,11 @@ class Colors: WHITE = (255, 255, 255) GREY = (84, 84, 84) + class Fonts: - pygame.init() - monospace_20 = pygame.font.SysFont("monospace", 20) - monospace_80 = pygame.font.SysFont("monospace", 80) + pygame.init() + monospace_20 = pygame.font.SysFont("monospace", 20) + monospace_80 = pygame.font.SysFont("monospace", 80) class SquareConfig: @@ -32,4 +33,4 @@ class GeneralConfig: fields_amount_y = math.trunc(height / SquareConfig.height) fps = 150 window_caption = "GOL" - evolve_speed = 360 # ziemlich slow das updated abhänig davon wie viele mill sec das game seit dem eltzten mal gelaufen ist im schnit sind das so 60 + evolve_speed = 360 # ziemlich slow das updated abhängig davon wie viele mill sec das game seit dem letzten mal gelaufen ist im schnitt sind das so 60 diff --git a/Code/UI/Field.py b/Code/UI/Field.py index 713147a..0c778aa 100644 --- a/Code/UI/Field.py +++ b/Code/UI/Field.py @@ -1,6 +1,8 @@ from Code.Communication.Direction import Direction from Code.Config import GeneralConfig, SquareConfig from Code.UI import Square +from Code.UI.Shape import Shape +from Code.UI.Shapes import Shapes from Code.UI.Square import Square @@ -10,6 +12,10 @@ class Field: self.height = GeneralConfig.fields_amount_y + 2 self.field_shift = -10 self.squares = self._creat_squares() + self.shapes = {Shape.VERTICAL_GLIDER: Shapes().creat_vertical_glieder} + + def create_shape(self, shape: Shape, start_square_pos): + self.squares = self.shapes[shape](start_square_pos, self.squares) def _creat_squares(self): squares = [ @@ -27,10 +33,12 @@ class Field: for y in x: y.draw(window) - def get_edge(self, edg_pos: Direction) -> list: - edge = {Direction.LEFT: self.get_left_edge(), Direction.RIGHT: self.get_right_edge(), + def get_edges(self) -> dict: + return {Direction.LEFT: self.get_left_edge(), Direction.RIGHT: self.get_right_edge(), Direction.TOP: self.get_top(), Direction.BOTTOM: self.get_bottom_edge()} - return edge[edg_pos] + + def get_edge(self, edg_pos: Direction) -> list: + return self.get_edges()[edg_pos] def get_top(self): top_squares = [] diff --git a/Code/UI/PlayingField.py b/Code/UI/PlayingField.py index b726c95..604b82d 100644 --- a/Code/UI/PlayingField.py +++ b/Code/UI/PlayingField.py @@ -1,19 +1,24 @@ import pygame as pygame from Code.Communication.Direction import Direction +from Code.Communication.EdgeSync import EdgeSync from Code.Communication.Neighbours import Neighbours from Code.Config import Fonts from Code.Config import GeneralConfig, Colors from Code.GameLogic.Rules import Rules from Code.UI.Field import Field +from Code.UI.Shape import Shape class GameState: def __init__(self, neighbours: Neighbours): + self.neighbours = neighbours + self.counter = 0 self.run = True - self.pause_for_input = False + self.is_evolving = False self.field = Field() + self.edge_sync = EdgeSync(self.field.get_edges(), self.counter) self.update_field_events = [] def event_handler(self): @@ -24,8 +29,22 @@ class GameState: self.update_field_events.append(event) if event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: - self.pause_for_input = not self.pause_for_input - self.neighbours.toggle_pause(self.pause_for_input) + print(f"Gamestate: set evolving to {not self.is_evolving}") + 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 ))) def update_field_with_input(self, event): for line in self.field.squares: @@ -40,15 +59,15 @@ class GameState: def redraw_field(self, window): window.fill(Colors.BLACK) self.field.draw_squares(window) - if not self.pause_for_input: + if not self.is_evolving: label_font = Fonts.monospace_80 label = label_font.render("Pause", 1, Colors.ORANGE) window.blit(label, (100, 100)) - def update_borders(self): - self.field.fill_right_ghost_edge(self.neighbours.get_edge(Direction.RIGHT)) - self.field.fill_left_ghost_edge(self.neighbours.get_edge(Direction.LEFT)) + self.field.fill_right_ghost_edge(self.neighbours.get_edge(Direction.RIGHT, self.counter)) + self.field.fill_left_ghost_edge(self.neighbours.get_edge(Direction.LEFT, self.counter)) + self.counter_old = self.counter def run_game(game_state: GameState): @@ -60,8 +79,9 @@ def run_game(game_state: GameState): time_elapsed_since_last_action = 0 while game_state.run: - print(f"running {game_state.pause_for_input}") + game_state.event_handler() + print(f"running {game_state.is_evolving}") for event in game_state.update_field_events: game_state.update_field_with_input(event) @@ -70,12 +90,13 @@ def run_game(game_state: GameState): clock.tick(GeneralConfig.fps) time_elapsed_since_last_action += clock.get_time() - if game_state.pause_for_input: - + if game_state.is_evolving: if time_elapsed_since_last_action > 100: # start = ti.time() game_state.update_borders() game_state.evolve() + game_state.counter = game_state.counter + 1 + game_state.edge_sync.update_edges(game_state.field.get_edges(), game_state.counter) # end = ti.time() # print(end - start) time_elapsed_since_last_action = 0 # reset it to 0 so you can count again diff --git a/Code/UI/Shape.py b/Code/UI/Shape.py new file mode 100644 index 0000000..f9076fc --- /dev/null +++ b/Code/UI/Shape.py @@ -0,0 +1,5 @@ +from enum import Enum + + +class Shape(Enum): + VERTICAL_GLIDER = 1 diff --git a/Code/UI/Shapes.py b/Code/UI/Shapes.py new file mode 100644 index 0000000..a7813e4 --- /dev/null +++ b/Code/UI/Shapes.py @@ -0,0 +1,23 @@ +from Code import Config + + +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): + for point in points: + 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] + + return x_valid and y_valid + + diff --git a/Code/UI/__init__.py b/Code/UI/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Code/__init__.py b/Code/__init__.py new file mode 100644 index 0000000..e69de29