Hunt the Lunpus is a handheld adaptation of the classic 1970s text-based game Hunt the Wumpus, built on the ATmega328p microcontroller. It is based on Douglas McInnes's Lunpus project, which provided the foundation for this adaptation.
Hunt the Lunpus is a handheld game console where the player explores a dangerous cave system filled with traps and a monstrous creature, the Lunpus. The objective is to avoid the deadly hazards, locate the Lunpus, and defeat it before it awakens. It uses seven-segment displays to represent the cave walls and hazards. This project builds on Douglas's work with additional hardware features - adds visual feedback in the form of LED indicators with ATmega328p microcontroller for its additional capabilities.
- Start the Game: Press the
ARROWbutton to begin the game. - Movement: Use the directional buttons (
NORTH,SOUTH,EAST,WEST) to navigate the cave system. The walls of the cave will appear on the seven-segment display. - Avoid Hazards:
- Superbats: Transport you to a random location if encountered.
- Slime Pits: Instant death upon falling into one.
- Lunpus: The sleeping monster will eat you if disturbed.
- Hazard Warnings:
- LED Indicators: The
Wind LEDwarns of nearby Slime Pits, while theStench LEDlights up when close to the Lunpus. - Sound Alerts: Wind sounds indicate pits, and snoring sounds warn that the Lunpus is near.
- LED Indicators: The
- Fire Arrows: Press the
ARROWbutton to check your remaining arrows. Press a directional button to fire an arrow in that direction. - Game Over: If you die or run out of arrows, press the
ARROWbutton to restart.
The pcb directory includes EasyEDA project files that outline the schematic and PCB design.

void loop() {
static unsigned long timer = millis();
(*currentStateFn)(timer); // State-specific processing
updateAudio(timer); // Audio management
sevsegshift.refreshDisplay(); // Display refresh
}- Provides non-blocking time tracking:
- In the
loop()function,timer = millis()captures the current time in milliseconds. Various functions then use thistimerto determine when to perform actions without stopping the entire program execution. For example, inrenderText(), it uses timer comparisons to manage timing:
- In the
static unsigned long nextAction = 0;
if (nextAction > timer) {
return;
}
nextAction = timer + 400;- State functions using timer like
updateAudio()check timer without blocking game progression
void updateAudio(unsigned long timer) {
// Non-blocking audio processing
//- Tracks next note timing
//- Manages song progression dynamically
if (nextNoteTime == 0) return;
if (timer < nextNoteTime) return;
playNote(currentSong[currentNote]);
nextNoteTime = duration + timer;
}-
No
delay()functions used -
Supports pseudo-random timing variations:
- Uses
random()with timer. The combination of timer and random() creates a more natural, preventing monotonous, mechanical-feeling animations or interactions.
- Uses
nextAction = timer + 400 + random(600); // Randomized bat animation- Enables concurrent process management:
- Simultaneously manages:
- Audio updates
- Display refreshes
- Hazard warnings
- Player input processing
- Simultaneously manages:

