/* defines */ //#define DEBUG // if defined, debug messages will be printed #define TOUCH_PORT IN_1 // touch sensor port #define LIGHT_PORT IN_2 // light sensor port #define ULTRASONIC_PORT IN_3 // ultrasonic sensor port #define FLAG_PORT OUT_A // flag motor port #define TRIGGER_PORT OUT_B // trigger motor port #define COUNTDOWN_TIME 30 // countdown duration (value is in s) #define LONG_PRESS_THRESHOLD 350 // the minimum amount of time a button press // must last to be considered long - i.e. // a dash in Morse code (value is in ms) #define ULTRASONIC_THRESHOLD 10 // if at any time the distance reported by the // ultrasonic sensor exceeds this threshold, the // bomb explodes (value is in cm) #define TOUCH_CHECK_WAIT_TIME 20 // the amount of time to wait between finishing // one touch sensor state check and beginning // the next one; button presses shorter than // this have a chance of not being registered! // (value is in ms) #define ULTRASONIC_CHECK_WAIT_TIME 250 // the amount of time to wait between two // readings from the ultrasonic sensor // (value is in ms) #define BUTTONS_CHECK_WAIT_TIME 50 // the amount of time to wait between two // checks of the buttons' state (value is in // ms) #define BUTTONS_PAUSE_TIME 1000 // the amount of time to wait TODO desc #define DRAMATIC_PAUSE_TIME 1000 // the length of pauses at the beginning of the // deactivate() and explode() functions // (value is in ms) #define EXIT_WAIT_TIME 5000 // the amount of time to wait between the bomb // deactivation or explosion and the actual end of // the program (value is in ms) /* constants */ string LETTERS[] = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; byte MORSE_CODES[][] = { {1, 2, 0}, // A {2, 1, 1, 1, 0}, // B {2, 1, 2, 1, 0}, // C {2, 1, 1, 0}, // D {1, 0}, // E {1, 1, 2, 1, 0}, // F {2, 2, 1, 0}, // G {1, 1, 1, 1, 0}, // H {1, 1, 0}, // I {1, 2, 2, 2, 0}, // J {2, 1, 2, 0}, // K {1, 2, 1, 1, 0}, // L {2, 2, 0}, // M {2, 1, 0}, // N {2, 2, 2, 0}, // O {1, 2, 2, 1, 0}, // P {2, 2, 1, 2, 0}, // Q {1, 2, 1, 0}, // R {1, 1, 1, 0}, // S {2, 0}, // T {1, 1, 2, 0}, // U {1, 1, 1, 2, 0}, // V {1, 2, 2, 0}, // W {2, 1, 1, 2, 0}, // X {2, 1, 2, 2, 0}, // Y {2, 2, 1, 1, 0}}; // Z /* global variables */ int defuseCode[] = {0, 0, 0}; // will be filled by the deactivate() task int speed = 1; // current countdown speed (larger value => faster countdown) /* task and function prototypes */ task deactivation(); task countdown(); task safeguard(); task buttons(); task explode(); task deactivate(); bool check_letter(int letter); /* tasks */ /** * Main task. */ task main() { Precedes(deactivation, countdown, safeguard, buttons); } /** * Generates and displays a random bomb defuse code, which, if correctly entered * (in Morse code, using the touch sensor), deactivates the bomb. */ task deactivation() { // generate a random three-letter bomb defuse code and print it TextOut(2, LCD_LINE1, "DEFUSE CODE:"); for (int i = 0; i < 3; i++) { defuseCode[i] = Random(26); TextOut(82 + i*6, LCD_LINE1, LETTERS[defuseCode[i]]); } // check each letter, one after another for (int i = 0; i < 3; i++) { if (check_letter(defuseCode[i]) == true) { // play a sound to confirm that the letter has been checked // successfully PlaySound(SOUND_FAST_UP); } else { // any of the checks fails => bomb explodes ExitTo(explode); } } // all checks successful => bomb is deactivated ExitTo(deactivate); } /** * Starts a countdown which, upon reaching zero, causes the bomb to explode. * The countdown is accompanied by light and sound effects, and the current * remaining time is always displayed in a "big digits" font. */ task countdown() { int time = COUNTDOWN_TIME; // print header and leading zeroes TextOut(5, 36, "TIME REMAINING:"); FontNumOut(0, 0, "BIGdigits.ric", 0); FontNumOut(24, 0, "BIGdigits.ric", 0); TextOut(49, LCD_LINE6, ":"); while (true) { while (time >= 0) { // print current time FontNumOut(52, 0, "BIGdigits.ric", time / 10); // first digit FontNumOut(75, 0, "BIGdigits.ric", time % 10); // second digit // beep and flash the light if (time >= 10) { PlayToneEx(450, 100/speed, 2, FALSE); SetSensorType(LIGHT_PORT, IN_TYPE_LIGHT_ACTIVE); Wait(200/speed); SetSensorType(LIGHT_PORT, IN_TYPE_LIGHT_INACTIVE); Wait(800/speed); } else { // in the last 10 seconds of the countdown, the frequency of the // beeping and flashing is inreased for(int i = 0; i < 5; i++) { PlayToneEx(450, 100/speed, 2, FALSE); SetSensorType(LIGHT_PORT, IN_TYPE_LIGHT_ACTIVE); Wait(100/speed); SetSensorType(LIGHT_PORT, IN_TYPE_LIGHT_INACTIVE); Wait(100/speed); SetSensorType(LIGHT_PORT, IN_TYPE_LIGHT_ACTIVE); } } time = time - 1; } break; } // end of countdown reached => bomb explodes ExitTo(explode); } /** * Triggers bomb explosion if the distance reported by the ultrasonic sensor * exceeds a certain threshold. */ task safeguard() { // initialize the ultrasound sensor SetSensorLowspeed(ULTRASONIC_PORT); while (true) { #ifdef DEBUG TextOut(82, LCD_LINE8, " "); NumOut(82, LCD_LINE8, SensorUS(ULTRASONIC_PORT)); #endif if (SensorUS(ULTRASONIC_PORT) > ULTRASONIC_THRESHOLD) { // distance threshold exceeded => bomb explodes ExitTo(explode); } Wait(ULTRASONIC_CHECK_WAIT_TIME); } } /** * Speeds up the countdown if any button (that is, not the touch sensor) is * pressed. */ task buttons() { while (true) { if (ButtonState(BTN1) || ButtonState(BTN2) || ButtonState(BTN3) || ButtonState(BTN4)) { // button pressed => increase countdown speed speed = speed * 2; // wait before (potentially) triggering another speed increase Wait(BUTTONS_PAUSE_TIME); } Wait(BUTTONS_CHECK_WAIT_TIME); } } /** * Simulates the explosion of the bomb. */ task explode() { // stop all other tasks StopTask(deactivation); StopTask(countdown); StopTask(safeguard); StopTask(buttons); ClearScreen(); Wait(DRAMATIC_PAUSE_TIME); GraphicOut(0, 0, "Boom.ric"); PlayFileEx("Ahnoo.rso", 4, false); // trigger the ballista OnFwd(TRIGGER_PORT, 50); Wait(250); Off(TRIGGER_PORT); Wait(500); OnRev(TRIGGER_PORT, 50); Wait(250); Off(TRIGGER_PORT); Wait(EXIT_WAIT_TIME); Stop(true); } /** * Simulates the deactivation of the bomb. */ task deactivate() { // stop all other tasks StopTask(deactivation); StopTask(countdown); StopTask(safeguard); StopTask(buttons); ClearScreen(); Wait(DRAMATIC_PAUSE_TIME); GraphicOut(0, 0, "Smile 01.ric"); // play a random celebration sound int sound = Random(3); if (sound == 0) { PlayFileEx("Bravo.rso", 4, false); } else if (sound == 1) { PlayFileEx("Brilliant.rso", 4, false); } else { PlayFileEx("Fantastic.rso", 4, false); } // wave the flag OnFwd(FLAG_PORT, 30); Wait(EXIT_WAIT_TIME); Stop(true); } /* functions */ /** * Monitors the touch sensor and determines whether the observed sequence of * presses corresponds to the Morse code representation of the given letter. * * @param letter letter to be checked: 0 - A, 1 - B, ..., 25 - Z */ bool check_letter(int letter) { SetSensor(TOUCH_PORT, SENSOR_TOUCH); unsigned long time; int press; int i = 0; while (MORSE_CODES[letter][i] != 0) { #ifdef DEBUG TextOut(0, LCD_LINE5, "i="); NumOut(12, LCD_LINE5, i); TextOut(24, LCD_LINE5, "waiting"); #endif // wait for the button to be pressed while (Sensor(TOUCH_PORT) == false) { Wait(TOUCH_CHECK_WAIT_TIME); } #ifdef DEBUG TextOut(0, LCD_LINE6, "i="); NumOut(12, LCD_LINE6, i); TextOut(24, LCD_LINE6, "pressed"); #endif // wait for the button to be released time = CurrentTick(); while (Sensor(TOUCH_PORT) == true) { Wait(TOUCH_CHECK_WAIT_TIME); } #ifdef DEBUG TextOut(0, LCD_LINE7, "i="); NumOut(12, LCD_LINE7, i); TextOut(24, LCD_LINE7, "released"); #endif // determine whether the press was a short, or a long one press = CurrentTick() - time >= LONG_PRESS_THRESHOLD ? 2 : 1; #ifdef DEBUG TextOut(0, LCD_LINE8, "i="); NumOut(12, LCD_LINE8, i); if (press == 1) { TextOut(24, LCD_LINE8, "was short"); } else { TextOut(24, LCD_LINE8, "was long "); } #endif // check if the press length was correct if (press != MORSE_CODES[letter][i]) return false; // move on to the next dot or dash i++; } // the whole letter was checked successfully return true; }