Toggle Navigation
Hatchery
Eggs
Dactyl
__init__.py
Users
Badges
Login
Register
MCH2022 badge?
go to mch2022.badge.team
__init__.py
raw
Content
import display, keypad, time import math import sndmixer import random from machine import Timer # Constants VOL = 30 # Sound volume level GREEN = 0x008800 YELLOW = 0x888800 ORANGE = 0x884700 RED = 0x880000 WHITE = 0x888888 BRIGHT_PURPLE = 0xFF00FF OFF = 0x000000 # No light INITIAL_TIME_LIMIT = 2000 # 2 seconds for bomb expiration INITIAL_BOMB_INTERVAL = 1200 # Initial bomb planting interval (in ms) INTERVAL_DECREASER = 0.95 MIN_BOMB_INTERVAL = 100 # Minimum interval for bomb planting PLANT_TONE = 390 # Frequency for success sound SUCCESS_TONE = 440 # Frequency for success sound FAILURE_TONE = 330 # Frequency for failure sound def displaydigit(colom, digit): for bit in range(4): if digit & (1 << bit): display.drawPixel(3 - bit, colom, WHITE) # Color transition thresholds (percentage of time remaining) COLOR_THRESHOLDS = [ (0, RED), # Less than 33% time remaining (0.33, ORANGE), # Less than 33% time remaining (0.66, YELLOW), # Less than 66% time remaining (1.00, GREEN) # Full time remaining ] def flush_display(): display.flush() class BombGame: def __init__(self): # Initialize sound sndmixer.begin(16) self.channels = [None] * 16 # Game state self.active_bombs = {} # Dictionary to track active bomb positions and their spawn times self.last_active_bomb = -1 self.game_over = False self.showed_score = False self.score = 0 self.bomb_interval = INITIAL_BOMB_INTERVAL self.last_bomb_time = 0 def reset_game(self): """Reset the game state to initial conditions.""" self.active_bombs.clear() self.game_over = False self.showed_score = False self.score = 0 self.bomb_interval = INITIAL_BOMB_INTERVAL self.last_bomb_time = 0 display.drawFill(OFF) flush_display() def get_bomb_color(self, elapsed_time): """Determine bomb color based on elapsed time.""" time_ratio = elapsed_time / INITIAL_TIME_LIMIT for threshold, color in COLOR_THRESHOLDS: if 1 - time_ratio <= threshold: return color return GREEN def update_bomb_colors(self, current_time): """Update the color of all active bombs based on their time remaining.""" for pos, spawn_time in self.active_bombs.items(): elapsed = time.ticks_diff(current_time, spawn_time) color = self.get_bomb_color(elapsed) x, y = pos % 4, pos // 4 display.drawPixel(x, y, color) flush_display() def plant_bomb(self): """Plant a bomb at a random available position.""" available_positions = [i for i in range(16) if i not in self.active_bombs and i != self.last_active_bomb] if available_positions: bomb_position = random.choice(available_positions) current_time = time.ticks_ms() self.active_bombs[bomb_position] = current_time x, y = bomb_position % 4, bomb_position // 4 display.drawPixel(x, y, GREEN) # Start with green color flush_display() self.last_bomb_time = current_time self.play_tone(PLANT_TONE, 30) def play_tone(self, frequency, duration_ms, vol=VOL): """Play a tone with the given frequency and duration.""" synth = sndmixer.synth() sndmixer.volume(synth, vol) sndmixer.waveform(synth, 0) sndmixer.freq(synth, frequency) sndmixer.play(synth) [time.sleep_ms(1) for _ in range(duration_ms)] sndmixer.stop(synth) def handle_key(self, key_index, pressed): """Handle key press events.""" if self.game_over: if pressed: self.play_tone(SUCCESS_TONE, 500) if self.showed_score: self.reset_game() else: self.showed_score = True self.display_score() return if not pressed: return x, y = key_index % 4, key_index // 4 if key_index in self.active_bombs: del self.active_bombs[key_index] self.last_active_bomb = key_index display.drawPixel(x, y, OFF) flush_display() # Successful bomb defusal self.play_tone(SUCCESS_TONE, 30) self.score += 1 # Make game harder self.bomb_interval = max(MIN_BOMB_INTERVAL, self.bomb_interval * INTERVAL_DECREASER) else: display.drawPixel(x, y, BRIGHT_PURPLE) flush_display() # Wrong button pressed - game over self.handle_game_over("Game Over because of wrong button! Score: " + str(self.score)) def display_score(self): display.drawFill(OFF) for col in range(0, 4): displaydigit(col, math.floor((self.score / (10 ** (3 - col))) % 10)) flush_display() def update(self): """Update game state - plant new bombs and check for expired ones.""" if self.game_over: if self.showed_score: self.display_score() return current_time = time.ticks_ms() # Plant new bomb if interval has passed if time.ticks_diff(current_time, self.last_bomb_time) > self.bomb_interval: self.plant_bomb() # Update colors of existing bombs self.update_bomb_colors(current_time) # Check for expired bombs expired = [pos for pos, spawn_time in self.active_bombs.items() if time.ticks_diff(current_time, spawn_time) > INITIAL_TIME_LIMIT] if expired: failMsg = "Time ran out! Game Over! Score: " + str(self.score) self.handle_game_over(failMsg) def handle_game_over(self, failMsg): self.game_over = True self.play_tone(FAILURE_TONE, 1000, vol=VOL * 2) print(failMsg) time.sleep_ms(500) # Create game instance and set up game = BombGame() game.reset_game() game.plant_bomb() keypad.add_handler(game.handle_key) def do_update(arg): game.update() # Create a Timer object interval_timer = Timer(0) # Initialize the timer to call the function every 1000 ms (1 second) interval_timer.init(period=50, mode=Timer.PERIODIC, callback=do_update)