Dependencies:   mbed wave_player mbed-rtos 4DGL-uLCD-SE SDFileSystem

main.cpp

Committer:
rushib1
Date:
2020-04-27
Revision:
4:c1da839b41b1
Parent:
3:6e41a5ce16c2
Child:
5:269d04e12a9e

File content as of revision 4:c1da839b41b1:

#include "mbed.h"
#include "uLCD_4DGL.h"
#include "rgb.h"
#include "SDFileSystem.h"
#include "wave_player.h"
#include "rtos.h"

#include <string>
#include <iostream>
#include <fstream>
#include <algorithm>

enum DIFF_LEVEL{NA=0, EASY=1, MED=2, HARD=3, SCORE=4};
enum SIGN{PLUS=0, MINUS=1, MULT=2};

struct Gen_Question{
    int num1;
    int num2;
    int sign;
    int corr_ans;
    int ans[4];
};

struct Game_Difficulty{
    bool is_selected;
    enum DIFF_LEVEL level;
    int ans_time;
    int max_num;
    int num_of_q;
    int score_mult;
};

struct Game_Settings{
    Game_Difficulty new_diff;
    int score;
    int live_left; 
    int q_on;
    bool is_timeout;
    bool is_lose;
};


Game_Settings new_game = {.q_on=1, .live_left = 3, .new_diff.is_selected=false, .is_timeout=true, .is_lose =false};


void print_high_score();
int* read_high_score();

/*
------------------CONSTS
*/
//const int EASY_TIME =20;
//const int MED_TIME =15;
//const int HARD_TIME =10;

volatile bool homescreen = true;
volatile bool boot_vid = true;

/*
------------------I/O VARS
*/
SDFileSystem sd(p5, p6, p7, p8, "sd");
RawSerial bluemod(p28, p27);
Serial pc(USBTX, USBRX);
uLCD_4DGL uLCD(p9, p10, p11);
DigitalIn pb(p12);

/*
------------------LED VARS
*/

DigitalOut life[] = {(p16), (p19), (p20)};
RGBLed myRGBled(p21, p24, p23);
DigitalOut onboard_led(LED1);

/*
------------------AUDIO VARS
*/

FILE *wave_file;
AnalogOut DACount(p18);
wave_player waver(&DACount);

/*
------------------GENERAL VARS
*/
volatile char bnum = '0';


Mutex lcd_mutex;
Mutex blue_mutex;
Thread thread1, thread2, thread3;

void main_screen()
{
    //pc.printf("MAIN_SCREEN\r\n");
    uLCD.cls();
    uLCD.text_height(1);
    uLCD.text_width(1);
    uLCD.color(0xF0F6F7);
    uLCD.locate(6, 1);
    uLCD.printf("MATH FUN");
    uLCD.locate(1, 4);
    uLCD.printf("Difficulty");
    uLCD.text_height(1);
    uLCD.text_width(1);
    uLCD.locate(3, 6);
    uLCD.color(GREEN);
    uLCD.printf("1) Easy");
    uLCD.locate(3, 8);
    uLCD.color(0xFFFF00);
    uLCD.printf("2) Not as Easy");
    uLCD.locate(3, 10);
    uLCD.color(RED);
    uLCD.printf("3) Very Uneasy");
    uLCD.color(0x669DB2);
    uLCD.locate(1, 13);
    uLCD.printf("4) High Scores");
    uLCD.rectangle(0, 0, 127, 127, 0xA89C94);
    homescreen = false;
}

bool count_distinct(int arr[], int n)
{
    int res = 1;
    // Pick all elements one by one
    for (int i = 1; i < n; i++)
    {
        int j = 0;
        for (j = 0; j < i; j++)
            if (arr[i] == arr[j])
                break;
    // If not printed earlier, then print it
        if (i == j)
            res++;
    }
    if (res == 4)
    {
        return false;
    }
    else
    {
        return true;
    }
}

void gen_ans(Gen_Question *gen_new_q)
{
    //rand()%(max-min + 1) + min;
    gen_new_q->num1 = rand() % (new_game.new_diff.max_num + 1 - 0) + 0;
    gen_new_q->num2 = rand() % (new_game.new_diff.max_num + 1 - 1) + 1;
    gen_new_q->sign = rand() % (3 + 1 - 1) + 1;
    
    switch (gen_new_q->sign)
    {
    case 1:
        gen_new_q->sign = '*';
        gen_new_q->ans[0] = gen_new_q->num1 * gen_new_q->num2;
        gen_new_q->ans[1] = (gen_new_q->num1 + (rand() % (10 + 1 - 1) + 1)) * gen_new_q->num2;
        gen_new_q->ans[2] = (gen_new_q->num1 * gen_new_q->num2) + (rand() % (20 + 1 - 1) + 1);
        gen_new_q->ans[3] = (gen_new_q->num1 * gen_new_q->num2) + (rand() % (20 + 1 - 1) + 1);
        break;
    case 2:
        gen_new_q->sign = '+';
        gen_new_q->ans[0] = gen_new_q->num1 + gen_new_q->num2;
        gen_new_q->ans[1] = (gen_new_q->num1 + (rand() % (10 + 1 - 1) + 1)) + gen_new_q->num2;
        gen_new_q->ans[2] = (gen_new_q->num1 + gen_new_q->num2) - (rand() % ((gen_new_q->num1 + gen_new_q->num2) + 1 - 1) + 1);
        gen_new_q->ans[3] = (gen_new_q->num1 * gen_new_q->num2) + (rand() % (10 + 1 - 1) + 1);
        break;
    case 3:
        gen_new_q->sign = '-';
        while (gen_new_q->num1 == gen_new_q->num2)
        {
            gen_new_q->num1 = (rand() % ((gen_new_q->num1 + 2) - 1 + 1) + 1);
        }
        gen_new_q->ans[0] = gen_new_q->num1 - gen_new_q->num2;
        gen_new_q->ans[1] = (gen_new_q->num1 - gen_new_q->num2) * -1 + (rand() % (10 + 1 - 1) + 1);
        gen_new_q->ans[2] = gen_new_q->num1 + gen_new_q->num2 - (rand() % (10 + 1 - 1) + 1);
        gen_new_q->ans[3] = gen_new_q->num1 * gen_new_q->num2 - (rand() % (20 + 1 - 1) + 1);
        break;
    }
}

void game_questions(Gen_Question *gen_new_q)
{
    gen_ans(gen_new_q);
    bool reroll = count_distinct(gen_new_q->ans, 4);
    while (reroll)
    {
        gen_ans(gen_new_q);
        reroll = count_distinct(gen_new_q->ans, 4);
        //pc.printf("reroll\r\n");
    }

    gen_new_q->corr_ans = gen_new_q->ans[0];
    //pc.printf("%i\r\n", gen_new_q->ans[0]);
    //pc.printf("%i\r\n", gen_new_q->corr_ans);
    random_shuffle(&(gen_new_q->ans[0]), &(gen_new_q->ans[3]));
    uLCD.cls();
    uLCD.locate(2, 1);
    uLCD.text_height(2);
    uLCD.text_width(2);
    uLCD.color(0xFC766A);
    uLCD.printf("Q%i",new_game.q_on);
    uLCD.text_height(1);
    uLCD.text_width(1);

    uLCD.locate(9, 1);
    uLCD.printf("TIME:");
    uLCD.line(55, 0, 55, 27, 0xA89C94);

    uLCD.rectangle(0, 0, 127, 127, 0xA89C94);
    uLCD.line(0, 27, 127, 27, 0xA89C94);
    uLCD.line(0, 50, 127, 50, 0xA89C94);

    uLCD.color(0x669DB2);
    uLCD.locate(2, 4);
    uLCD.text_height(2);
    uLCD.text_width(2);
    uLCD.printf("%i%c%i=", gen_new_q->num1, gen_new_q->sign, gen_new_q->num2);
    uLCD.text_height(1);
    uLCD.text_width(1);

    uLCD.color(0xFC766A);
    uLCD.locate(2, 7);
    uLCD.printf("1)");

    uLCD.color(0x669DB2);
    uLCD.locate(8, 7);
    uLCD.printf("%i", gen_new_q->ans[0]);

    uLCD.color(0xFC766A);
    uLCD.locate(2, 9);
    uLCD.printf("2)");

    uLCD.color(0x669DB2);
    uLCD.locate(8, 9);
    uLCD.printf("%i", gen_new_q->ans[1]);

    uLCD.color(0xFC766A);
    uLCD.locate(2, 11);
    uLCD.printf("3)");

    uLCD.color(0x669DB2);
    uLCD.locate(8, 11);
    uLCD.printf("%i", gen_new_q->ans[2]);

    uLCD.color(0xFC766A);
    uLCD.locate(2, 13);
    uLCD.printf("4)");

    uLCD.color(0x669DB2);
    uLCD.locate(8, 13);
    uLCD.printf("%i", gen_new_q->ans[3]);

    uLCD.line(0, 115, 127, 115, 0xA89C94);
}

void game_over()
{
    uLCD.cls();
    uLCD.locate(0, 7);
    uLCD.text_height(2);
    uLCD.text_width(2);
    uLCD.color(0xFC776A);
    uLCD.printf("GAME OVER");
    uLCD.rectangle(0, 0, 127, 127, 0xA89C94);
}

void win_game()
{
    uLCD.cls();
    uLCD.locate(2, 7);
    uLCD.text_height(2);
    uLCD.text_width(2);
    uLCD.color(GREEN);
    uLCD.printf("YOU WIN");
    uLCD.rectangle(0, 0, 127, 127, 0xA89C94);

}

void score_game()
{
    uLCD.cls();
    uLCD.locate(1, 3);
    uLCD.text_height(2);
    uLCD.text_width(2);
    uLCD.color(GREEN);
    uLCD.printf("SCORE");
    uLCD.rectangle(0, 0, 127, 127, 0xA89C94);
    uLCD.text_height(1);
    uLCD.text_width(1);
    uLCD.locate(7, 7);
    uLCD.printf("%i",new_game.score);
}

void life_count_check()
{
    life[0] = 0;
    life[1] = 0;
    life[2] = 0;

    for (int i = 0; i < new_game.live_left; i++)
    {
        life[i] = 1;
    }
    if (new_game.live_left == 0)
    {
        new_game.is_lose = true;

    }
}

void check_correct_ans(Gen_Question check_new_q)
{
    if (new_game.is_timeout)
    {
        //pc.printf("TIMEOUT\r\n");
        uLCD.cls();
        uLCD.locate(5, 7);
        uLCD.text_height(2);
        uLCD.text_width(2);
        uLCD.color(0xF0F6F7);
        uLCD.printf("TIME");
        new_game.live_left--;
        //wait(2);
    }
    else
    {
        //pc.printf("%i\r\n", corr_ans);
        //pc.printf("%i\r\n", ans[(bnum-'0' -1)]);

        if (check_new_q.corr_ans == check_new_q.ans[(bnum - '0') - 1])
        {

            uLCD.cls();
            uLCD.locate(2, 7);
            uLCD.text_height(2);
            uLCD.text_width(2);
            uLCD.color(GREEN);
            uLCD.printf("CORRECT");
            new_game.score = new_game.score + new_game.new_diff.score_mult;
            //wait(2);
            //pc.printf("CORRECT\r\n");
        }
        else
        {
            //pc.printf("INCORRECT\r\n");
            uLCD.cls();
            uLCD.locate(0, 7);
            uLCD.text_height(2);
            uLCD.text_width(2);
            uLCD.color(RED);
            uLCD.printf("INCORRECT");
            new_game.live_left--;
            //wait(2);
        }
    }
}


bool get_bluetooth_button()
{
    //pc.printf("BLUETOOTH\r\n");
    bnum = '0';
    //blue_mutex.lock();
    if (bluemod.getc() == '!')
    {
        if (bluemod.getc() == 'B')
        {
            //button number
            bnum = bluemod.getc();
            //button data
            char bhit = bluemod.getc();
            if (bluemod.getc() == char(~('!' + 'B' + bnum + bhit)))
            {
                if (bhit == '1')
                {
                    //pc.printf("%c\r\n", bnum);
                    blue_mutex.unlock();
                    return false;
                }
            }
        }
    }
    //blue_mutex.unlock();
    return true;
}

void get_button()
{
    int timer = new_game.new_diff.ans_time;
    new_game.is_timeout = true;
    while (timer != 0)
    {
        uLCD.locate(15, 1);
        uLCD.color(0xF0F6F7);
        uLCD.printf("%i", timer);
        if (bluemod.readable())
        {
            new_game.is_timeout = get_bluetooth_button();
            if(new_game.is_timeout == false){
                break;
            }
        }
        wait(1);
        uLCD.locate(15, 1);
        uLCD.color(BLACK);
        uLCD.printf("%i", timer);
        timer = timer - 1;

    }
}

void bluetooth_thread()
{
    while (true)
    {
        if (bluemod.readable())
        {
            bool n_timeout = get_bluetooth_button();
        }
        else
        {
            Thread::yield();
        }
        Thread::wait(100);
    }
}

void rgb_led_difficulty()
{
    //pc.printf("%i\r\n",bnum - 48);
    new_game.new_diff.level = static_cast<DIFF_LEVEL>(bnum - 48);
    //pc.printf("%i\r\n",new_game.new_diff.level);
    if (new_game.new_diff.level == EASY)
    {
        myRGBled.write(0.0, 1.0, 0.0); //green
        new_game.new_diff.is_selected = true;
        new_game.new_diff.ans_time = 20;
        new_game.new_diff.max_num = 10;
        new_game.new_diff.num_of_q = 7; // 7
        new_game.score = 50;
        new_game.new_diff.score_mult = 10;
    }
    else if (new_game.new_diff.level == MED)
    {
        myRGBled.write(1.0, 0.2, 0.0); //yellow = red + some green
        new_game.new_diff.is_selected = true;
        new_game.new_diff.ans_time = 15;
        new_game.new_diff.max_num = 15;
        new_game.new_diff.num_of_q = 10;
        new_game.score = 200;
        new_game.new_diff.score_mult = 50;
    }
    else if (new_game.new_diff.level == HARD)
    {
        myRGBled.write(1.0, 0.0, 0.0); //red
        new_game.new_diff.is_selected = true;
        new_game.new_diff.ans_time = 10;
        new_game.new_diff.max_num = 10;
        new_game.new_diff.num_of_q = 15;
        new_game.score = 300;
        new_game.new_diff.score_mult = 100;
    }
    else if (new_game.new_diff.level == SCORE)
    {
        myRGBled.write(0.5, 0.5, 0.5);
        new_game.new_diff.is_selected = true;
        print_high_score();
    }
}

void wav_thread()
{
    while (true)
    {
        //pc.printf("AUDIO\r\n");
        wave_file = fopen("/sd/audio/intro.wav", "r");
        waver.play(wave_file);
        fclose(wave_file);
    }
}

void boot_video_thread()
{
    while (true)
    {
        //pc.printf("BootVID\r\n");
        //PLAY VIDEO BOOT
        if (boot_vid)
        {
            //lcd_mutex.lock();
            uLCD.media_init();
            uLCD.set_sector_address(0x0, 0x0);
            uLCD.display_video(0, 0);
            boot_vid = false;
            //lcd_mutex.unlock();
        }
        else
        {
            Thread::yield();
        }
    }
}

void write_high_score(int* score_arr){
    std::ofstream score_file;
    score_file.open("/sd/score/score.txt");
    if (score_file.is_open())
    {
            //score_file << "300\n200\n100\n";
            score_file << score_arr[0] << "\n" << score_arr[1] << "\n" << score_arr[2] << "\n";
            score_file.close();
    }
}


int* read_high_score()
{
    int* score_val = new int[3];
    //printf("read_high_score\n\r");
    std::ifstream infile("/sd/score/score.txt");
    wait(0.2);
    if (infile.is_open())
    {
        std::string line;
        int i = 0;
        while (std::getline(infile, line)) {
            // using printf() in all tests for consistency
            //printf("%s", line.c_str());
            score_val[i] = atoi(line.c_str());
            i++;
        }
        infile.close();
    }
    pc.printf("%i \n\r", score_val[0]);
    pc.printf("%i \n\r", score_val[1]);
    pc.printf("%i \n\r", score_val[2]);
    
    return score_val;
}

void print_high_score(){
    
    int* score_arr= read_high_score();
        
    uLCD.cls();
    uLCD.text_height(1);
    uLCD.text_width(1);
    uLCD.color(0xF0F6F7);
    uLCD.locate(4, 1);
    uLCD.printf("HIGH SCORES");
    uLCD.locate(3, 6);
    uLCD.color(0xFC776A);
    uLCD.printf("1) %i", score_arr[0]);
    uLCD.locate(3, 8);
    uLCD.color(0x669DB2);
    uLCD.printf("2) %i",  score_arr[1]);
    uLCD.locate(3, 10);
    uLCD.color(0xFC776A);
    uLCD.printf("3) %i",  score_arr[2]);
    uLCD.rectangle(0, 0, 127, 127, 0xA89C94);
    delete[] score_arr;    
}

void check_new_highscore(){
    int* new_score_arr = read_high_score();
    for (int i = 0; i < 3; i++)
        {
            if (new_score_arr[i]< new_game.score){
                int j = 1;
                while(i<=j){
                    new_score_arr[j+1] = new_score_arr[j];
                    j--;
                }
                new_score_arr[i] = new_game.score;
                pc.printf("high score found");
                write_high_score(new_score_arr);
                break;
            }
        }
    delete[] new_score_arr;
}

void reset_scores(){
    mkdir("/sd/score", 0777);
    std::ofstream score_file;
    score_file.open("/sd/score/score.txt");
    if (score_file.is_open())
    {
            score_file << "0\n0\n0\n";
            score_file.close();
    }
}

void init()
{
//    while(1){
//        reset_scores();
//        wait(2);
//    }
    uLCD.baudrate(3000000);
    srand(time(NULL));
    life[0] = 1;
    life[1] = 1;
    life[2] = 1;
    //thread1.start(boot_video_thread);
    boot_vid = false;
}


int main()
{
    init();
//    pc.printf("MAIN_INIT\r\n\n");
    thread2.start(wav_thread);
    while (true)
    {
        //pc.printf("MAIN_LOOP\r\n");
        //ONCE VIDEO STOPS PLAYING
        if (boot_vid == false)
        {
            thread1.terminate();
            //IF HOMESCREEN PRINTED ONCE DONT PRINT AGAIN
            if (homescreen)
            {
                main_screen();
            }
            //pc.printf("MAIN_PRINT\r\n");
            if (new_game.new_diff.is_selected == false)
            {
                thread3.start(bluetooth_thread);
                rgb_led_difficulty();
            }
            else
            {
                thread3.terminate();
                break;
            }
        }
        Thread::wait(100);
    }
    if(new_game.new_diff.level != SCORE){
        while (new_game.q_on <= new_game.new_diff.num_of_q)
        {
            thread3.terminate();
            Gen_Question new_q;
            game_questions(&new_q);
            get_button();
            check_correct_ans(new_q);
            new_game.q_on++;
            wait(2.2);
            life_count_check();
            if(new_game.is_lose == true){
                game_over();
                break;
            }
        }
        if(new_game.is_lose == false){
            win_game();
        }
        wait(2.5);
        new_game.score = new_game.score + new_game.live_left * 100;
        score_game();
        wait(2);
        check_new_highscore();
        wait(0.5);
        print_high_score();
    }
}