/*
* RimeOfAncientMariner v1.0
* Code by: Gray Mack
* License: MIT License
* https://choosealicense.com/licenses/mit/
* Created: 7/11/2023
* Published at: https://wokwi.com/projects/370005451557698561
* Description: Random code mushings based on a poem
* Why I made this: fun
* Board: Arduino Uno or compatible
* Select Board: Arduino Uno
* Processor: ATMEGA328p
* Simulation: https://wokwi.com/projects/370005451557698561
*
* Connections:
* connect mini 16ohm speaker/piezo element to pin 9 (and the other lead to GND)
* connect 4 LEDs on pins 2,3,4,5
*
* 07/11/2023 initial code creation
*
Rime of the Ancient Mariner
One after one, by the star-dogged Moon
Too quick for groan or sigh,
Each turned his face with a ghastly pang,
And cursed me with his eye.
Four times fifty living men,
(And I heard nor sigh nor groan)
With heavy thump, a lifeless lump,
They dropped down one by one.
The souls did from their bodies fly,—
They fled to bliss or woe!
And every soul, it passed me by,
Like the whizz of my CROSS-BOW!
*/
// ----[ included libraries ]--------------------------------
// none
// ----[ configuration ]------------------------------------
const char* PROGRAM_TITLE = "Code of the Ancient Mariner v1.0";
const unsigned int RimeBaud = 9600; // aka Rhyme
// ----[ pin definitions ]-----------------------------------
const int SPKR_PIN = 9;
const int LED1_PIN = 2; // 4 leds in sequential pins
const int LED2_PIN = 3;
const int LED3_PIN = 4;
const int LED4_PIN = 5;
const int TotalLeds = 4;
// ----[ constants ]-----------------------------------------
const int Mariner = 1;
const int TotalMen = 4*50+Mariner;
const int TotalAlbatrosses = 1;
const int MaxDropTimeMsec = 1000;
const int TooQuickMsec = 250;
const int CurseSoundFreq = 1000;
const int CurseDurationMsec = 100;
const int ThumpSoundFreq = 50;
const int ThumpDurationMsec = 100;
const unsigned long LengthOfNightMsec = 12UL*60UL*60UL*1000UL;
enum Gender : uint8_t { Male, Female, Other, Unspecified };
const char* GenderStr[] = { "Man", "Woman", "Someone", "Something" };
const char* PronounStr[] = { "his", "her", "their", "its" };
enum FleeDirection : uint8_t { Bliss = 0, Woe = 1 };
// ----[ predeclarations ]-----------------------------------
// ----[ global variables ]----------------------------------
int LivingMen = TotalMen;
int LivingAlbatrosses = TotalAlbatrosses;
int SoulsToBliss = 0;
int SoulsToWoe = 0;
int CursesUponMariner = 0;
// ----[ code ]----------------------------------------------
void setup() {
// setup the rhyme
Serial.begin(RimeBaud);
Serial.println(PROGRAM_TITLE);
Serial.println();
for(int groupOf50 = LED1_PIN; groupOf50<=LED4_PIN; groupOf50++)
{
pinMode(groupOf50, OUTPUT);
digitalWrite(groupOf50, HIGH);
}
pinMode(SPKR_PIN, OUTPUT);
SlayAlbatros(Mariner);
}
void loop() {
// insert main day after day, day after day activity here
if( ! CheckStarDoggedMoon()) Done();
bool isCursed = Curse();
Pang(GetNextCrewMemberGender(), isCursed);
if(isCursed) {
int dropTimeMsec = random(MaxDropTimeMsec);
if(dropTimeMsec < TooQuickMsec) dropTimeMsec = TooQuickMsec; // too quick! enforce minimum drop time
Drop(dropTimeMsec);
}
else
DontDrop();
ListenForSighOrGroan(LivingMen);
if(LivingMen <= Mariner) Done();
}
void SlayAlbatros(int who)
{
if(who == Mariner)
Serial.print("The mariner");
else
Serial.print("Some other fool");
Serial.println(" has killed an albatros!");
LivingAlbatrosses--;
}
bool CheckStarDoggedMoon() {
return millis() < LengthOfNightMsec;
}
bool Curse() {
static bool FirstTime = true;
if(LivingAlbatrosses == TotalAlbatrosses)
return false; // no curse, a happy ending is possible?
else if(FirstTime)
{
BeginCurse();
FirstTime = false;
}
bool checkCurse = random(100) > 5; // probably so
tone(SPKR_PIN, ThumpSoundFreq, ThumpDurationMsec);
delay(ThumpDurationMsec+25);
if(checkCurse) tone(SPKR_PIN, CurseSoundFreq, CurseDurationMsec);
return checkCurse;
}
void BeginCurse()
{
Serial.println("A terrible curse has begun...");
// todo: expand on this, praying, hang dead bird around mariners neck, etc...
}
Gender GetNextCrewMemberGender()
{
return Male; // Ugh, they didn't want bad luck
}
void Drop(int dropTime)
{
CursesUponMariner++;
Serial.println(" <Died/Zombie>");
LivingMen--;
FlySoulFromBody();
delay(dropTime);
}
void DontDrop()
{
Serial.println();
}
bool Pang(Gender gender, bool ghastly) {
Serial.print(GenderStr[gender]);
Serial.print(" ");
Serial.print(LivingMen);
Serial.print(" turned ");
Serial.print(PronounStr[gender]);
Serial.print(" face");
if(ghastly)
Serial.print(" and Curse (with eye)");
Serial.print(".");
return ghastly;
}
// sorry, no microphone, so we won't actually listen for any groaning
// extinguish living souls indicator as lump intensifies
void ListenForSighOrGroan(int souls)
{
const int groupings = TotalMen/TotalLeds;
int lifelessLump = (souls+groupings-Mariner-1)/groupings;
//Serial.print(" [soul "); Serial.print(souls); Serial.print(" group "); Serial.print(groupings); Serial.print(" lump "); Serial.print(lifelessLump); Serial.println("]");
switch(lifelessLump) {
case 0:
digitalWrite(LED1_PIN, LOW);
break;
case 1:
digitalWrite(LED2_PIN, LOW);
break;
case 2:
digitalWrite(LED3_PIN, LOW);
break;
case 3:
digitalWrite(LED4_PIN, LOW);
break;
default:
break;
}
}
void FlySoulFromBody()
{
FleeDirection passType = random(2) == 0 ? Bliss : Woe;
if(passType == Bliss)
SoulsToBliss++;
else if(passType == Woe)
SoulsToWoe++;
//Whizz(); // removed, CROSS-BOW was metaphorical
}
void Done() {
Serial.println();
Serial.println("Ship returned to port");
Serial.println("Hermit rescues mariner");
Serial.print("Total souls to bliss: ");
Serial.println(SoulsToBliss);
Serial.print("Total souls to woe: ");
Serial.println(SoulsToWoe);
Serial.print("Curses upon mariner: ");
Serial.println(CursesUponMariner);
Serial.println("------[ The end ]------");
while(true) delay(1);
}