From ce6da10ae1dff97a189688225921e6de3544bf5c Mon Sep 17 00:00:00 2001 From: mar77i Date: Tue, 20 Aug 2024 00:14:50 +0200 Subject: [PATCH] red frame the destination button on the panel --- elevator.py | 169 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 124 insertions(+), 45 deletions(-) diff --git a/elevator.py b/elevator.py index 70b8b57..ef7458f 100755 --- a/elevator.py +++ b/elevator.py @@ -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: -- 2.47.0