Toggle Navigation
Hatchery
Eggs
BenVLED
ledgame.py
Users
Badges
Login
Register
MCH2022 badge?
go to mch2022.badge.team
ledgame.py
raw
Content
import ugfx, badge, utime, random try: import wifi except Exception as e: print("[LEDGAME] Emulator I suppose? ;)") # import gc ''' BenV's LedGame The concept is simple, we light up one (or more later) random LED which fades over time. The player has to press the corresponding button the keyboard before the led is off to score point. Wrong button presses cost points, the sooner you press the correct button the more points you score. Missing a light (i.e. not pressing the right button in the given time) costs you a live, whenever you're out out lives it's game over. Later we introduce levels/rounds, after X lights the speed goes up and we start flipping button<->led 'wires'. (so instead of pressing LEFT for the 1st button you might have to press LEFT for the 4th button and START for the 1st) The whole input disaster (that is: lack of documentation on the wiki) of the badge had me reshuffle the code a few times. Seems like this setup is working for now. The game loop keeps track of which leds were actived when (ledsactive[led] = utime.ticks_ms()) and fades the led over $ledtime. When the user presses an invalid button we have (ledsfail[led] = utime.ticks_ms()) to blink it red. When the user presses the right button we have (ledsscore[led] = utime.ticks_ms()) to blink it green. NOTE: maybe the ticks_ms isn't the best, but the fine documentation advised against time() due to precision. We'll debug that later. TODO: - countdown before we start ''' class LedGame: def action_up(self, pressed): if pressed: self.button_pressed = "UP" def action_down(self, pressed): if pressed: self.button_pressed = "DOWN" def action_left(self, pressed): if pressed: self.button_pressed = "LEFT" def action_right(self, pressed): if pressed: self.button_pressed = "RIGHT" def action_select(self, pressed): if pressed: self.button_pressed = "SELECT" def action_start(self, pressed): if pressed: self.button_pressed = "START" def action_b(self, pressed): if pressed: self.button_pressed = "B" def action_a(self, pressed): if pressed: self.button_pressed = "A" def instructions(self): print("[LEDGAME] instructions.") ugfx.clear(ugfx.BLACK) ugfx.string_box(0,0,300,20,"Instructions","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,20,280,20,"A random LED will light up","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,30,280,45,"The objective is to press the corresponding button before it dies out","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,65,280,50,"Have fun! Press A/B to start, SELECT to quit.","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.flush() def __init__(self): print("[LEDGAME] init.") ugfx.init() badge.init() ugfx.input_init() badge.leds_init() badge.vibrator_init() try: wifi.init() except Exception as e: print("[LEDGAME] Still emulator.... (no wifi)") self.leddata = bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) self.ledschanged = False def leds_off(self): badge.leds_send_data(bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), 24) def leds_on(self, brightness = 255): if brightness == 255: brightness = self.maxbright brightness = int(brightness) badge.leds_send_data(bytes([brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness, brightness]), 24) def set_led(self, led, r, g, b, w): if led > 5: print("[LEDGAME] BUG - NO SUCH LED " + str(led)) return if len(self.leddata) != 24: self.leddata = bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) print("[LEDGAME] Led " + str(led) + " to RGBW %d/%d/%d/%d" % (r, g, b, w)) # Detect changes oldleddata = self.leddata # GRBW self.leddata[led * 4 + 0] = g self.leddata[led * 4 + 1] = r self.leddata[led * 4 + 2] = b self.leddata[led * 4 + 3] = w if self.leddata != oldleddata: self.ledschanged = True def update_leds(self): badge.leds_send_data(bytes(self.leddata), len(self.leddata)) self.ledschanged = False def checkLedHit(self): # Check button map and if led was active if self.button_pressed == None: return print("[LEDGAME] checkLedHit() called, button pressed: " + str(self.button_pressed)) if self.lives <= 0: print("[LEDGAME] Lives 0 -- no score check.") return if self.button_pressed not in self.ledsmap: print("[LEDGAME] Button not mapped!") self.button_pressed == None return lednumber = self.ledsmap[self.button_pressed] if self.ledsactive[lednumber] > 0: points = int(utime.ticks_diff(utime.ticks_ms(), self.ledsactive[lednumber]) / float(self.ledtime) * 10.0) print("[LEDGAME] SCORE! Button " + self.button_pressed + " mapped to led " + str(lednumber) + " which was active for " + str(points) + " points!") self.score = self.score + points self.ledsactive[lednumber] = 0 self.ledsscore[lednumber] = utime.ticks_ms() if self.buzzer: badge.vibrator_activate(3) self.add_led() # TODO: Bonus system / combo? Light more leds? else: print("[LEDGAME] MISS! Button " + self.button_pressed + " mapped to led " + str(lednumber) + " which was NOT active! Lost 1 point! Active was: " + str(self.ledsactive)) if self.buzzer: badge.vibrator_activate(1) self.ledsfailed[lednumber] = utime.ticks_ms() self.score = self.score - 1 # Detect last button press self.lastbuttontime = utime.ticks_ms() self.button_pressed = None def game_start(self): print("[LEDGAME] Game start.") # Bookkeeping self.score = 0 self.lastbuttontime = 0 self.lives = 3 self.level = 1 self.ledtime = 2000 self.lastadded = None self.ledsactive = [0] * 6 self.ledsscore = [0] * 6 self.ledsfailed = [0] * 6 self.ledsmap = { "LEFT" : 5, "UP" : 5, "RIGHT" : 4, "DOWN" : 4, "SELECT": 3, "START" : 2, "B" : 1, "A" : 0 } self.maxbright = 255 - self.nightmode # Draw button connections ugfx.clear(ugfx.BLACK) self.draw_connections() ugfx.flush() self.add_led() # Main loop while self.lives > 0: if not self.wifi: try: self.wifi = wifi.sta_if.isconnected() except Exception as e: pass if self.wifi: print("[LEDGAME] Wifi connected.") # Update all LEDs, first the active ones self.checkLedHit() # Prevent WDT from killing the badge, hopefully? ugfx.clear(ugfx.BLACK) self.draw_connections() ugfx.string_box(0,0,280,50,"Score: " + str(self.score), "Roboto_Regular12",ugfx.WHITE,ugfx.justifyRight) ugfx.string_box(0,90,280,50,"Lives left: " + str(self.lives), "Roboto_Regular12",ugfx.WHITE,ugfx.justifyRight) ugfx.string_box(0,90,280,50,"Level: " + str(self.level), "Roboto_Regular12",ugfx.WHITE,ugfx.justifyLeft) ugfx.flush() newcount = 0 # print("[LEDGAME] Free memory: " + str(gc.mem_free())) for led in range(0,6): if self.ledsactive[led] > 0: # Fade it based on the current time, if it's 0 the user takes a hit. timeleft = self.ledtime - utime.ticks_diff(utime.ticks_ms(), self.ledsactive[led]) if timeleft > 0: # Scale new RGB value based on time left. val = int(float(timeleft / self.ledtime) * self.maxbright) print("[LEDGAME] Active Led " + str(led) + " timer has timeleft[" + str(timeleft) + "] -> val(" + str(val) +")") # print("[LEDGAME] Free memory: " + str(gc.mem_free())) self.set_led(led, val, val, val, val) else: self.lives = self.lives - 1 print("[LEDGAME] Life lost! Too late, active led expired. Lifes left: " + str(self.lives)) # print("[LEDGAME] Free memory: " + str(gc.mem_free())) self.ledsactive[led] = 0 if self.lives > 0: newcount = newcount + 1 self.set_led(led, 0, 0, 0, 0) if self.ledsfailed[led] > 0: timeleft = self.ledtime - utime.ticks_diff(utime.ticks_ms(), self.ledsfailed[led]) if timeleft > 0: # Scale new RGB value based on time left. val = int(float(timeleft / self.ledtime) * self.maxbright) print("[LEDGAME] Failed Led " + str(led) + " timer has timeleft[" + str(timeleft) + "] -> val(" + str(val) +")") # print("[LEDGAME] Free memory: " + str(gc.mem_free())) self.set_led(led, val, 0, 0, 0) else: self.set_led(led, 0, 0, 0, 0) if self.ledsscore[led] > 0: timeleft = self.ledtime - utime.ticks_diff(utime.ticks_ms(), self.ledsscore[led]) if timeleft > 0: # Scale new RGB value based on time left. val = int(float(timeleft / self.ledtime) * self.maxbright) print("[LEDGAME] Score Led " + str(led) + " timer has timeleft[" + str(timeleft) + "] -> val(" + str(val) +")") # print("[LEDGAME] Free memory: " + str(gc.mem_free())) # do some color cycling, why not. rgb = random.randint(0,3) if rgb == 0: self.set_led(led, 0, val, 0, 0) elif rgb == 1: self.set_led(led, 0, val, 0, val) elif rgb == 2: self.set_led(led, 0, val, val, 0) elif rgb == 3: self.set_led(led, val, val, val, 0) else: self.set_led(led, 0, 0, 0, 0) # Check score for level increase if (self.score > (self.level*100)): self.level = self.level + 1 self.ledtime = int(self.ledtime / 1.5) if self.ledtime <= 0: self.ledtime = 1 print("[LEDGAME] LEVEL UP! Level is now: " + str(self.level) + " with " + str(self.ledtime) + " time per led.") if self.level > 3: print("[LEDGAME] Now you are fscked, extra LED for this level ;)") self.add_led() # Light random new LEDs for newcount, but not an already active one for num in range(0, newcount): self.add_led() # Finally update leds. self.update_leds() self.game_over() def draw_connections(self): # We'll assume 296 for now. 296 / 6 =~ 49 ugfx.line(03, 0, 03, ugfx.height(), ugfx.WHITE) ugfx.line(50, 0, 50, ugfx.height(), ugfx.WHITE) ugfx.line(111, 0, 115, ugfx.height(), ugfx.WHITE) ugfx.line(163, 0, 170, ugfx.height(), ugfx.WHITE) ugfx.line(219, 0, 229, ugfx.height(), ugfx.WHITE) ugfx.line(280, 0, 296, ugfx.height(), ugfx.WHITE) def add_led(self): led = random.randint(0,5) while self.ledsactive[led] > 0 or self.lastadded == led: led = random.randint(0,5) self.ledsactive[led] = utime.ticks_ms() self.lastadded = led self.ledsscore[led] = 0 self.ledsfailed[led] = 0 self.set_led(led, self.maxbright, self.maxbright, self.maxbright, self.maxbright) def game_over(self): print("[LEDGAME] main menu.") self.button_pressed = None ugfx.clear(ugfx.BLACK) ugfx.string_box(0,0,300,20,"GAME OVER","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,20,280,20,"Start : instructions","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,30,280,45,"Select: Quit game ","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,40,280,50,"A/B : Start new game","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) if self.score <= 0: ugfx.string_box(0,70,280,50,"You scored " + str(self.score) + " points... is your badge malfunctioning?", "Roboto_Regular12",ugfx.WHITE,ugfx.justifyLeft) elif self.score > 100: ugfx.string_box(0,70,280,50,"You scored a whopping " + str(self.score) + " points and reached level " + str(self.level) + ". Nice!", "Roboto_Regular18",ugfx.WHITE,ugfx.justifyLeft) else: ugfx.string_box(0,70,280,50,"You scored only " + str(self.score) + " points.", "Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.flush() self.leds_off() self.leds_on(self.maxbright) if self.buzzer: badge.vibrator_activate(7) utime.sleep(0.1) self.leds_off() if self.buzzer: badge.vibrator_activate(3) utime.sleep(0.1) self.leds_on(self.maxbright / 2) if self.buzzer: badge.vibrator_activate(7) utime.sleep(0.1) self.leds_off() if self.buzzer: badge.vibrator_activate(0) ugfx.flush() def draw_menu(self): ugfx.clear(ugfx.BLACK) ugfx.string_box(0,0,300,20,"Menu","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,20,280,20,"Start : instructions","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,30,280,45,"Select: Quit game ","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,50,280,45,"UP : Night mode (" + ("br. redux " + str(self.nightmode) if self.nightmode else "disabled") + ")","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,65,280,50,"B or A: Start game","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.string_box(0,80,280,50,"LEFT : Toggle buzzer (" + ("enabled" if self.buzzer else "disabled") + ")","Roboto_Regular12",ugfx.WHITE,ugfx.justifyCenter) ugfx.flush() def program_main_menu(self): print("[LEDGAME] main menu.") self.button_pressed = None self.nightmode = 0 self.maxbright = 255 self.buzzer = 1 # enable buzzer now = utime.time() tm = utime.localtime(now) if tm[3] > 20 or tm[3] < 5: print("[LEDGAME] Defaulted to night mode due to the time....") self.nightmode = 192 ugfx.input_attach(ugfx.BTN_SELECT, self.action_select) ugfx.input_attach(ugfx.BTN_START, self.action_start) ugfx.input_attach(ugfx.BTN_B, self.action_b) ugfx.input_attach(ugfx.BTN_A, self.action_a) ugfx.input_attach(ugfx.JOY_UP, self.action_up) ugfx.input_attach(ugfx.JOY_DOWN, self.action_down) ugfx.input_attach(ugfx.JOY_LEFT, self.action_left) ugfx.input_attach(ugfx.JOY_RIGHT, self.action_right) # Wait for WiFi connection try: self.wifi = wifi.sta_if.isconnected() except Exception as e: print("[LEDGAME] Still emulator....") self.wifi = 0 self.draw_menu() while True: if not self.wifi: try: self.wifi = wifi.sta_if.isconnected() except Exception as e: pass if self.wifi: print("[LEDGAME] Wifi connected.") if self.button_pressed == None: utime.sleep_ms(10) pass elif self.button_pressed == "SELECT": print("[LEDGAME] Quit request. Bye") import appglue appglue.home() return elif self.button_pressed == "LEFT": self.button_pressed = None self.buzzer = 0 if self.buzzer else 1 self.draw_menu() elif self.button_pressed == "UP": self.button_pressed = None self.nightmode = self.nightmode + 16 if self.nightmode > 255: self.nightmode = 0 self.draw_menu() elif self.button_pressed == "DOWN": self.button_pressed = None self.nightmode = self.nightmode - 16 if self.nightmode < 0: self.nightmode = 240 self.draw_menu() elif self.button_pressed == "START": self.button_pressed = None self.instructions() elif self.button_pressed == "A" or self.button_pressed == "B": self.button_pressed = None print("[LEDGAME] Start game!") self.game_start() def run(self): self.program_main_menu() # Start game LedGame().run()