]> git.mar77i.info Git - elevator/commitdiff
red frame the destination button on the panel
authormar77i <mar77i@protonmail.ch>
Mon, 19 Aug 2024 22:14:50 +0000 (00:14 +0200)
committermar77i <mar77i@protonmail.ch>
Mon, 19 Aug 2024 22:49:43 +0000 (00:49 +0200)
elevator.py

index 70b8b572b854188dc23eeeb6257c1ae8f228c9d3..ef7458f9507367ab00031bba188b6c0a63703c85 100755 (executable)
@@ -13,6 +13,7 @@ tau = pi * 2
 
 
 class Vec(pygame.math.Vector2):
+    # don't want no dot product
     def __mul__(self, other):
         return super().elementwise().__mul__(other)
 
@@ -76,7 +77,7 @@ def beep():
     if pygame.mixer.get_busy():
         return
 
-    freq, volume, length_sec = 432 * 2 ** 1.5, 0.125, 1.5
+    freq, volume, length_sec = 432 * 2 ** (5 / 4), 0.125, 1.5
     SAMPLERATE, FORMAT, CHANNELS = pygame.mixer.get_init()
 
     bio = BytesIO()
@@ -90,6 +91,8 @@ def beep():
 class Door:
     OPEN = 0
     CLOSED = 100
+    OPEN_DIRECTION = -1
+    CLOSE_DIRECTION = 1
     BACKGROUND = "black"
     FOREGROUND = "brown"
     BUTTON_COLOR = "black"
@@ -103,41 +106,48 @@ class Door:
     DEFAULT_STATE = CLOSED
     OPEN_TIMEOUT_SEC = 5
 
-    def __init__(self, rect):
-        self.rect = rect
+    def __init__(self, elevator, surf_size, level, num_levels):
+        self.elevator = elevator
+        pad = Door.PADDING * surf_size
+        size = Vec(Door.WIDTH, 1 / num_levels) * surf_size
+        self.level = level
+        self.rect = Rect(
+            pad + Vec(0, size[1] * (num_levels - 1 - level)), size - 2 * pad
+        )
         self.button_rect = Rect(
-            rect.topleft + self.BUTTON_OFFSET * rect.size,
-            Vec(rect.height, rect.height) // 3,
+            self.rect.topleft + self.BUTTON_OFFSET * self.rect.size,
+            Vec(self.rect.height, self.rect.height) // 3,
         )
         self.state = self.DEFAULT_STATE
         self.direction = 0
         self.timeout = None
 
-    @classmethod
-    def get_doors(cls, surf_size, num_levels):
-        pad = cls.PADDING * surf_size
-        size = Vec(cls.WIDTH, 1 / num_levels) * surf_size
-        return [
-            cls(Rect(pad + Vec(0, size[1] * (num_levels - 1 - i)), size - 2 * pad))
-            for i in range(num_levels)
-        ]
-
     def open(self):
-        if self.direction == 1:
-            return
-        self.direction = -1
+        """
+        Open the door, even while closing it
+        """
+        self.direction = self.OPEN_DIRECTION
 
     def close(self):
-        if self.direction == -1:
-            return
-        self.direction = 1
-        self.timeout = None
+        """
+        Close the door. It must be open.
+        """
+        if self.state == self.OPEN:
+            self.direction = self.CLOSE_DIRECTION
+            self.timeout = None
 
     def update(self):
+        """
+        If a timeout is set and expired, toggle the door.
+        If a direction is set, move the door, and check if we're done opening/closing
+        """
         dirty = self.direction != 0
         if self.timeout is not None and time() >= self.timeout:
-            self.direction = -1 if self.state == self.CLOSED else 1
             self.timeout = None
+            if self.state == self.CLOSED:
+                self.direction = self.OPEN_DIRECTION
+            elif self.state == self.OPEN:
+                self.direction = self.CLOSE_DIRECTION
             dirty = True
         self.state += self.direction
         if self.direction:
@@ -153,7 +163,12 @@ class Door:
     def draw(self, surf):
         # clean me up!
         subsurf = surf.subsurface(self.rect)
-        subsurf.fill(self.BACKGROUND)
+        level, mod = divmod(self.elevator.current_level, Elevator.LEVEL_STEPS)
+        if mod == 0 and level == self.level and self.elevator.destination in (None, level):
+            background = Elevator.CABIN_COLOR
+        else:
+            background = self.BACKGROUND
+        subsurf.fill(background)
         left_open = self.DOOR_MARGIN[0]
         left_closed = (1 - self.DOOR_GAP) / 2
         right_closed = (1 + self.DOOR_GAP) / 2
@@ -210,18 +225,42 @@ class Elevator:
     LEVEL_STEPS = 100000
     SPEED = 800
 
-    def __init__(self, doors):
-        self.doors = doors
+    def __init__(self, surf_size, num_levels):
+        self.doors = [Door(self, surf_size, i, num_levels) for i in range(num_levels)]
         self.current_level = 0
         self.destination = None
         self.queued_dest = None
+        self.call_queue = []
+
+    def pick_destination(self):
+        """
+        If not all doors are closed, bail.
+        If we have a queued_dest, go there
+        If we have something on the call_queue, go there
+        """
+        if not all(door.state == door.CLOSED for door in self.doors):
+            return False
+        if self.queued_dest is not None:
+            self.destination = self.queued_dest
+            while self.destination in self.call_queue:
+                self.call_queue.remove(self.destination)
+            self.queued_dest = None
+            return True
+        elif len(self.call_queue) > 0:
+            self.destination = self.call_queue.pop(0)
+            if self.destination == self.queued_dest:
+                self.destination = None
+            return True
+        return False
 
     def update(self):
+        """
+        If no destination is available, try picking one
+        If we arrived at a destination, open the door and discard the destination
+        Also move the cabin.
+        """
         if self.destination is None:
-            if all(door.state == door.CLOSED for door in self.doors):
-                self.destination = self.queued_dest
-                self.queued_dest = None
-            return False
+            return self.pick_destination()
         dest = self.destination * self.LEVEL_STEPS
         if dest == self.current_level:
             state = self.doors[self.destination].state
@@ -260,20 +299,53 @@ class Elevator:
         )
         pygame.draw.rect(surf, self.BACKGROUND, rect)
         pygame.draw.rect(surf, self.CABIN_COLOR, cabin_rect)
-        pygame.draw.line(surf, self.CABLE_COLOR, rect.midtop, cabin_rect.midtop)
+        pygame.draw.line(
+            surf, self.CABLE_COLOR, rect.midtop, (rect.centerx, cabin_rect.top)
+        )
 
     def goto(self, level):
-        self.queued_dest = level
+        """
+        Set a queued_dest if no destination or queued_dest is set.
+        """
+        if self.destination is None and self.queued_dest is None:
+            self.queued_dest = level
 
-    def open(self):
+    def get_whole_level(self):
         level, mod = divmod(self.current_level, self.LEVEL_STEPS)
-        if mod == 0:
-            self.doors[level].open()
+        if mod == 0 and self.destination in (None, level):
+            return level
+        return None
+
+    def call_from(self, call_level):
+        """
+        If we are at the destination and the door is open, disregard
+        If level is not already a destination, add it to the call_queue
+        """
+        if self.doors[call_level].state == Door.OPEN:
+            return
+        level_queue = {
+            self.get_whole_level(), *self.call_queue, self.queued_dest, self.destination
+        }
+        if call_level not in level_queue:
+            self.call_queue.append(call_level)
+
+    def open(self):
+        """
+        If we are on a whole level, open the door
+        """
+        level = self.get_whole_level()
+        if level is None:
+            return
+        self.doors[level].open()
 
     def close(self):
-        level, mod = divmod(self.current_level, self.LEVEL_STEPS)
-        if mod == 0:
-            self.doors[level].close()
+        """
+        If we are on a whole level, close the door
+        """
+        level = self.get_whole_level()
+        if level is None:
+            return
+        self.doors[level].close()
 
 
 class ElevatorPanel:
@@ -284,17 +356,19 @@ class ElevatorPanel:
     BUTTON_COLOR = "navajowhite"
     LABEL_COLOR = "black"
     ALARM_COLOR = "goldenrod"
+    GLOW_COLOR = "red"
     BUTTON_SIZE = 0.9
 
-    def __init__(self, surf_size, num_levels):
-        self.num_levels = num_levels
+    def __init__(self, elevator, surf_size, num_levels):
+        self.elevator = elevator
         self.outer_rect = Rect(*(v * surf_size for v in self.OUTER_RECT))
+        self.num_levels = num_levels
         inner_margin = ((1, 1) - self.INNER_SIZE) / 2
         self.inner_rect = Rect(
             self.outer_rect.topleft + inner_margin * self.outer_rect.size,
             self.INNER_SIZE * self.outer_rect.size,
         )
-        num_buttons = self.num_levels + 1
+        num_buttons = num_levels + 1
         button_outer_size = int(self.inner_rect.height / num_buttons)
         button_inner_size = int(self.inner_rect.height * self.BUTTON_SIZE / num_buttons)
         button_space = button_outer_size - button_inner_size
@@ -337,6 +411,10 @@ class ElevatorPanel:
                 fs,
                 (button.centerx - fs_size[0] // 2, button.centery - fs_size[1] // 2)
             )
+            if (
+                self.elevator.queued_dest is None and self.elevator.destination == i - 1
+            ) or self.elevator.queued_dest == i - 1:
+                pygame.draw.rect(surf, self.GLOW_COLOR, button, 2)
 
         for shape in BELL:
             vector_draw(surf, self.buttons[0], shape)
@@ -353,8 +431,8 @@ class ElevatorPanel:
 class ElevatorApp:
     FPS = 60
     SET_MODE_KWARGS = {
-        #"flags": pygame.FULLSCREEN,
-        "size": (1024, 768),
+        "flags": pygame.FULLSCREEN,
+        #"size": (1024, 768),
     }
     BACKGROUND = "green4"
 
@@ -367,8 +445,8 @@ class ElevatorApp:
         self.clock = pygame.time.Clock()
         self.num_levels = 5
         surf_size = surf.get_size()
-        self.elevator = Elevator(Door.get_doors(surf_size, self.num_levels))
-        self.elevator_panel = ElevatorPanel(surf_size, self.num_levels)
+        self.elevator = Elevator(surf_size, self.num_levels)
+        self.elevator_panel = ElevatorPanel(self.elevator, surf_size, self.num_levels)
         self.last_pos = None
 
     def handle_event(self, ev):
@@ -381,7 +459,7 @@ class ElevatorApp:
             if ev.button == 1:
                 for i, door in enumerate(self.elevator.doors):
                     if door.button_rect.collidepoint(ev.pos):
-                        self.elevator.goto(i)
+                        self.elevator.call_from(i)
                         break
                 for i, button in enumerate(self.elevator_panel.buttons):
                     if button.collidepoint(ev.pos):
@@ -389,6 +467,7 @@ class ElevatorApp:
                             beep()
                         elif i - 1 < self.num_levels:
                             self.elevator.goto(i - 1)
+                            self.dirty = True
                         elif i == self.num_levels + 1:
                             self.elevator.open()
                         elif i == self.num_levels + 2: