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
ARROW
button 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 LED
warns of nearby Slime Pits, while theStench LED
lights 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
ARROW
button 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
ARROW
button 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 thistimer
to 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: