from st3m.application import Application, ApplicationContext from st3m.run import run_view import leds import os.path UP = 0 LEFT = 1 DOWN = 2 RIGHT = 3 class Random: def __init__(self): self._state = 0x9ec5_9ec5 # (two gecs) # idk its fine its just for choosing delay and direction def next(self): self._state *= 37 self._state &= 0xffff_ffff return self._state >> 24 def direction(self): return self.next() & 3 def delay(self): return Quox.TURN_DELAY_BASE + self.next() # screen coords range ±120 each side class Quox(Application): STEP_DELAY = 250 TURN_DELAY_BASE = 1400 MOVE_DELAY = 100 TOP = -90 BOTTOM = 70 # so its always a bit visible LEFT = -90 RIGHT = 70 # start at top and go clockwise, 100px from center, 36° apart # y = 100 cos θ; x = 100 sin θ BOX_COORDS = \ [(0, -100), (59, -81), (95, -31), (95, 31), (59, 81), (0, 100), (-59, 81), (-95, 31), (-95, -31), (-59, -81)] @staticmethod def image_path(direction, step): dir = os.path.dirname(__file__) bases = {UP: 'back', LEFT: 'left', DOWN: 'front', RIGHT: 'right'} base = bases[direction] return f"{dir}/sprites/{base}{step}.png" def __init__(self, app_ctx): super().__init__(app_ctx) self.random = Random() self.boxes = [] self.x = 0 self.y = 0 self.step = 0 self.step_delay = Quox.STEP_DELAY self.move_delay = Quox.MOVE_DELAY self.new_direction() self.new_turn_delay() leds.set_gamma(2, 2, 2) leds.set_auto_update(False) leds.set_slew_rate(255) def adjusted_x(self): if self.direction == UP or self.direction == DOWN: return self.x - 32 else: return self.x - 44 def adjusted_y(self): if self.direction == UP or self.direction == DOWN: return self.y - 30 else: return self.y - 28 def draw(self, ctx): self.clear(ctx) # self.text(ctx) self.quox_sprite(ctx) self.update_leds() def clear(self, ctx): ctx.rgb(0, .1, .15) \ .rectangle(-120, -120, 240, 240) \ .fill() def quox_sprite(self, ctx): path = Quox.image_path(self.direction, self.step) ctx.image(path, self.adjusted_x(), self.adjusted_y(), -1, -1) @staticmethod def coord(d): if d == UP: return (-20, -100) elif d == LEFT: return (-100, -20) elif d == RIGHT: return (100, 20) elif d == DOWN: return (20, 100) def text(self, ctx): msg = f"{self.target()}" ctx.font_size = 16.0 ctx.text_align = ctx.CENTER ctx.rgb(1, 1, 1).move_to(0, -100).text(msg) def think(self, state, Δ): super().think(state, Δ) self.update_delay(Δ) self.add_boxes(state) self.pick_direction() self.pick_step() self.maybe_move() def add_boxes(self, state): for i, button in enumerate(state.captouch.petals): if button.pressed: if i not in self.boxes: self.boxes.append(i) else: if i in self.boxes: self.boxes.remove(i) def update_delay(self, Δ): self.turn_delay -= Δ self.step_delay -= Δ self.move_delay -= Δ def pick_direction(self): if self.turn_delay <= 0 or not self.towards_target(): old_delay = min(self.turn_delay, 0) self.new_direction() self.new_turn_delay(old_delay) def pick_step(self): if self.step_delay <= 0: self.step_delay = Quox.STEP_DELAY self.step = (self.step + 1) & 3 def maybe_move(self): if self.move_delay <= 0: self.move_delay += Quox.MOVE_DELAY self.move() def new_direction(self): d = self.random.direction() while not self.towards(d, self.target()): d = self.random.direction() self.direction = d def new_turn_delay(self, subtract=0): self.turn_delay = self.random.delay() + subtract def at_edge(self): return ((self.direction == UP and self.y <= Quox.TOP) or (self.direction == DOWN and self.y >= Quox.BOTTOM) or (self.direction == LEFT and self.x <= Quox.LEFT) or (self.direction == RIGHT and self.x >= Quox.RIGHT)) def flip_direction(self): if self.direction == UP: self.direction = DOWN elif self.direction == LEFT: self.direction = RIGHT elif self.direction == DOWN: self.direction = UP elif self.direction == RIGHT: self.direction = LEFT def move(self): if self.at_edge(): self.flip_direction() if self.direction == UP: self.y -= 5 elif self.direction == LEFT: self.x -= 5 elif self.direction == DOWN: self.y += 5 elif self.direction == RIGHT: self.x += 5 def target(self): if self.boxes: return Quox.BOX_COORDS[self.boxes[0]] else: return None def towards(self, direction, point): if not point: return True else: x, y = point if direction == UP: return self.y > y elif direction == LEFT: return self.x > x elif direction == RIGHT: return self.x < x elif direction == DOWN: return self.y < y def towards_target(self): return self.towards(self.direction, self.target()) def update_leds(self): leds.set_all_hsv(0, 0, 1) for i in self.boxes: center = i * 4 left = (center - 1) % 40 right = (center + 1) % 40 for led in [left, right, center]: leds.set_hsv(led, ((i + 4) * 36) % 360, 1, 1) leds.update() if __name__ == '__main__': run_view(Quox(ApplicationContext()))