A Demo Tap-Game for the 4D Systems GEN4-24PT touch display

Dependencies:   mbed uLCD_4D_Picaso

main.cpp

Committer:
csteinmetz
Date:
2016-11-02
Revision:
0:2254d7e8172a

File content as of revision 0:2254d7e8172a:

#include "mbed.h"
#include "uLCD_4D_Picaso.h"
#include <iostream>
#include <queue>
#include <math.h>

/**
* Demo program for uLCD_4D_Picaso resistance touchscreen 
* Simple demo game-application on resistive touchscreen display displaying images and using printf.
* @version 1.0
* @author Christian Steinmetz, Sam Horwich
*/
// three pins are: TX   RX  RESET

uLCD_4D_Picaso lcd(p9, p10, p11);
uint16_t handle;    //for images (countdown 3,2,1)
double level=3.0;   //current level indicator, level=((game_level+5)/2)
int menu=0;         //menu==0 --> start menu
unsigned long xpos[]={0,40,120,200,40,120,200,40,120,200};  //centers of the nine circular buttons
unsigned long ypos[]={0,40,40,40,120,120,120,200,200,200};
Timer t;            //used for timing later
Timer randt;        //used as a random number generator
int err=0;          //error indicator
bool redo=0;        //true if same pattern shall be displayed after error
int games=0;        //number of games started


//creates a random integer out of the current time (random as human input is varying)
int rnd()
{
    return (int) randt.read_us();
}

//displays the image with name "image.gci" (created from graphics composer) at position (x,y)
void disp_im(int x, int y, char * name)
{
    handle=lcd.file_Open(name,'r');
    lcd.file_Image(x,y,handle);
}    

//colors the button number 'pos' in color col
void col(int pos, Picaso::Color col)
{
    int j=(pos-1)/3;
    int i=pos-j*3-1;
    lcd.gfx_CircleFilled(40+80*i,40+80*j,37, col);
}

//creates the setting of the beginning (white background, 9 black circular buttons)
void black()
{
    lcd.gfx_RectangleFilled(0, 0, 240, 320, Picaso::WHITE);
    for(int i=0;i<3;i++)
    {
        for(int j=0;j<3;j++)
        {
            lcd.gfx_CircleFilled(40+80*i,40+80*j,37, Picaso::BLACK);
        }
    }
}

int main() {
    randt.start();//for random numbers!
    wait(1);
    // change the baudrate to improve or decrease the latency and plotting quality
    lcd.setbaudWait(Picaso::BAUD_600000);
    
    //initialize SD card in LCD
    lcd.file_Mount();
    
    //text color
    lcd.txt_FGcolour(Picaso::BLUE);
            
    //qu: Queue in which the current pattern is stored
    //qu2: when qu is dequeued bc of human input, qu2 stores the information from qu which may later be needed in case of an error, ...
    queue <int> qu;
    queue <int> qu2;
    
    lcd.touch_Set(0);   //initialize touch screen
    int status = 0;     //stores information from touch screen: 0 if untouched, 1 if touched (2 if released, 3 if moved)
    int oldst = 0;      //stores old status (so that only changes in status result in events) [may not be needed here...]
    unsigned long x = 0;//position of finger contact (updated when screen is touched)
    unsigned long y = 0;
    while(1) {
        if(menu==0)     //start menu
        {
            lcd.txt_BGcolour(Picaso::ORANGE);
            lcd.txt_Height(4);
            lcd.txt_Width(2);
            lcd.txt_MoveCursor(1,2);
            lcd.printf(" DEMO-GAME ");
            
            lcd.txt_Height(3);
            lcd.txt_Width(1);
            lcd.txt_MoveCursor(5,9);
            lcd.printf(" Start Game ");
            
            lcd.txt_Height(3);
            lcd.txt_MoveCursor(7,8);
            lcd.printf(" Instructions ");
            lcd.txt_Height(1);
            
            oldst=status;
            status = lcd.touch_Get(0);  //get status from touch screen as explained above
            if (status&&(oldst==0))     //change in status from 0 to 1
            {
                x = lcd.touch_Get(1);
                y = lcd.touch_Get(2);
                if(x>70&&x<170&&y>170&&y<230)   //start game button-->reset everything
                {
                    lcd.txt_BGcolour(Picaso::WHITE);    //white backgound for texts
                    menu=1;                             //pattern show mode
                    redo=0;                             //in the beginning, new pattern shall be created
                    err=0;                              //no errors in the beginning
                    level=3.0;                          //beginner's level is 3 (=(game_level+5)/2
                    t.stop();                           //reset timer if used before
                    t.reset();
                    lcd.gfx_Cls();                      //clear
                    lcd.gfx_RectangleFilled(0, 0, 240, 320, Picaso::WHITE);     //white background for screen
                    if(games<5)
                    {
                        disp_im(68,110,"three.gci");         //countdown
                        wait(1);
                        disp_im(68,110,"two.gci");
                        wait(1);
                        disp_im(68,110,"one.gci");
                        wait(1);
                        games++;    //needs to be updated/needs to be updated now every time the images are displayed-->see comment below
                    }
                    //countdown without displaying images: memory space allows only to display ~16 of these images, so afterwards the screen would freeze
                    //if we still try to display them-->alternative without images is a text countdown...
                    else                    
                    {
                        lcd.txt_Height(5);
                        lcd.txt_MoveCursor(2,14);
                        lcd.txt_Width(3);
                        lcd.printf("3");
                        wait(1);
                        lcd.txt_Width(1);
                        lcd.txt_MoveCursor(2,14);
                        lcd.txt_Width(3);
                        lcd.printf("2");
                        wait(1);
                        lcd.txt_Width(1);
                        lcd.txt_MoveCursor(2,14);
                        lcd.txt_Width(3);
                        lcd.printf("1");
                        wait(1);
                        lcd.txt_Height(1);
                        lcd.txt_Width(1);
                    }
                    black();                            //game setting (9 circular black buttons)
                }
                else if(x>65&&x<175&&y>255&&y<290)      //show instructions as a text
                {
                    menu=4;                             //go to instructions menu
                    lcd.gfx_Cls();
                    lcd.txt_MoveCursor(1,1);
                    lcd.printf("Tap the buttons in the shown\n\rorder. Do not tap the wrong \n\rbuttons, as you can only    \n\rmake a maximum of 3 mistakes\n\rbefore losing.\n\r");
                    lcd.printf("\n\rThe longer you play, the \n\rharder the levels will be. \n\rThere are no time limits, so\n\rhave fun and try to remember\n\ran infinitely long \n\rcombination.\n\r");
                    lcd.printf("\n\rTap anywhere to get back to \n\rthe menu and get going!");
                }
            }
        }
        if(menu==1)     //show pattern mode
        {
            lcd.txt_MoveCursor(22,13);  //write level
            lcd.txt_Height(1);
            lcd.printf("Level");
            lcd.txt_MoveCursor(24,15);
            lcd.txt_Height(2);
            lcd.printf("%i",((int)ceil(level*2-5)));
            
            lcd.txt_Height(1);      //write errors
            lcd.txt_MoveCursor(22,3);
            lcd.printf("Lives");
            lcd.txt_MoveCursor(24,4);
            lcd.txt_Height(2);
            lcd.printf("%i/3",3-err);
            
            lcd.txt_Height(1);      //MENU-button
            lcd.txt_MoveCursor(23,22);
            lcd.txt_Height(3);
            lcd.printf("M E N U");
            lcd.txt_Height(2);
        
            if(err==3)          //in that case, you lost--> menu=3
            {
                lcd.gfx_RectangleFilled(0, 240, 240, 320, Picaso::WHITE);
                lcd.txt_MoveCursor(11,10);
                lcd.printf("GAME  OVER");
                
                lcd.txt_Height(1);
                lcd.txt_MoveCursor(25,11);
                lcd.printf("Level %2i",(int)ceil(level*2-5));
                
                lcd.txt_Height(1);
                lcd.txt_MoveCursor(23,22);
                lcd.txt_Height(3);
                lcd.printf("M E N U");
                lcd.txt_Height(2);
            
                lcd.txt_Height(1);
                lcd.txt_MoveCursor(23,2);
                lcd.txt_Height(3);
                lcd.printf("N E W");
                lcd.txt_Height(2);
                
                menu=3;
            }
            else        //show pattern
            {
                double wt=0.1*log(level/1.4);   //speed of showed pattern decreases iwth length of pattern!--> makes rnd() more random!
                wait(3*wt);
                if(redo==0)     //new pattern wanted
                {
                    for(int i=0;i<level;i++)    //creates a new pattern (integer numbers of buttons) of length ceil(level) and writes it in the queue qu
                    {
                        int pos=rnd()%9+1;  
                        qu.push(pos);
                        col(pos,Picaso::ORANGE);    //shows pattern: button blinking in orange
                        wait(wt*3);
                        col(pos,Picaso::BLACK);     //back to black
                        wait(wt);
                        if(qu2.size()>0)            //if qu2 exists, we want to clear it as it is not needed (redo==1!)
                        {
                            qu2.pop();
                        }
                    }
                }
                else        //we want to show the old pattern again (there was an error...)
                {
                    redo=0;             //want to get a new pattern next time if no mistake is made!
                    for(int i=0;i<level;i++)    //restore old pattern from qu2 in qu (afterwards, qu2 is not needed anymore-->cleared)
                    {
                        qu.push(qu2.front());
                        col(qu2.front(),Picaso::ORANGE);    //buttons blink again
                        wait(wt*3);
                        col(qu2.front(),Picaso::BLACK);
                        wait(wt);
                        qu2.pop();
                    }
                }
                menu=2;         //go to human input mode
            }
        }
        if(menu==2)     //now we try to retap the buttons in the right order
        {
            t.start();  //timer needed later
            oldst=status;
            status = lcd.touch_Get(0);
            if (status&&(oldst==0))
            {
                x = lcd.touch_Get(1);   //positions of finger contact
                y = lcd.touch_Get(2);
                
                if((unsigned long)((x-xpos[qu.front()])*(x-xpos[qu.front()])+(y-ypos[qu.front()])*(y-ypos[qu.front()]))<=1444)  //checks if we are more or less inside the circle at the first position of qu
                {
                    col(qu.front(),Picaso::GREEN);  //green==good
                    t.reset();
                    //this loop is needed so that the green light is actually shown and it is not continuously trying to get green and black again at the same time
                    //only if button is released for at least 0.15s (so not due to a too light pressure on the screen for a few milliseconds),
                    //the button goes back to black
                    while(1)                      
                    {
                        oldst=status;
                        status = lcd.touch_Get(0);
                        
                        if(status!=0){t.reset();}
                        if(status==0&&t.read()>0.15){break;}
                    }
                    col(qu.front(),Picaso::BLACK);
                    if(qu.size()==1)        //after the last element , a longer green signal shows that the pattern is finished and correct
                    {
                        col(qu.front(),Picaso::BLACK);
                        wait(.2);
                        col(qu.front(),Picaso::GREEN);
                        wait(.1);
                        col(qu.front(),Picaso::BLACK);
                        wait(.1);
                        col(qu.front(),Picaso::GREEN);
                        wait(1);
                        col(qu.front(),Picaso::BLACK);
                        wait(0.8);
                        level+=0.5;         //now, as we were correct, we can also increase our level and go back to the new show pattern-phase (menu==1)
                        menu=1;
                    }
                    qu2.push(qu.front());   //store qu in qu2 (as it may be needed above)
                    qu.pop();               //clear first element from qu
                }
                else if(y<240)          //if we press the wrong button...
                {
                    unsigned long dist=100000;
                    unsigned long min=0;    //the following writes the button in 'min' that is closest to the finger contact
                    for(int i=1;i<10;i++)
                    {
                        if((x-xpos[i])*(x-xpos[i])+(y-ypos[i])*(y-ypos[i])<dist)
                        {
                            min=i;
                            dist=(x-xpos[i])*(x-xpos[i])+(y-ypos[i])*(y-ypos[i]);
                        }
                    }
                    if(min!=qu.front()) //if closest button is the right one, but we press outside of it, nothing happens. IF NOT: Error routine in following lines
                    {
                        col(min,Picaso::RED);   //red==bad
                        wait(1.5);
                        col(min,Picaso::BLACK);
                        redo=1;     //now we want to show the right pattern again next time (good that we have qu2...)
                        menu=1;     //new show pattern-phase
                        err++;      //error number updated
                    }
                    while(qu.size()>0)  //shift remaining information from qu in qu2
                    {
                        qu2.push(qu.front());
                        qu.pop();
                    }
                }
                else if(x>170&&y>270)   //MENU-button
                {
                    menu=0;     //back to start menu
                    lcd.gfx_Cls();  //reset lcd
                    while(qu.size()>0)  //clear queues
                    {
                        qu.pop();
                    }
                    while(qu2.size()>0)
                    {
                        qu2.pop();
                    }
                }
            }
        }
        if(menu==3)     //GAME OVER...
        {
            oldst=status;
            status = lcd.touch_Get(0);
            if (status&&(oldst==0))
            {
                x = lcd.touch_Get(1);
                y = lcd.touch_Get(2);
                if(x>170&&y>270)        //MENU-button as above
                {
                    menu=0;
                    lcd.gfx_Cls();
                    while(qu.size()>0)
                    {
                        qu.pop();
                    }
                    while(qu2.size()>0)
                    {
                        qu2.pop();
                    }
                }
                else if(x<70&&y>270)    //NEW game-button
                {
                    while(qu.size()>0)  //clear queues
                    {
                        qu.pop();
                    }
                    while(qu2.size()>0)
                    {
                        qu2.pop();
                    }
                    menu=1;     //back to show-mode
                    redo=0;     //new pattern required
                    err=0;      //no errors in the beginning
                    level=3.0;  //first level is that (you should know by now)
                    t.stop();   //reset timer (as the other timer is used for random numbers, we don't reset it, an overflow would not matter at all!)
                    t.reset();
                    lcd.gfx_Cls();  //reset lcd
                    lcd.gfx_RectangleFilled(0, 0, 240, 320, Picaso::WHITE); //countdown:
                    if(games<5)
                    {
                        disp_im(68,110,"three.gci");         //countdown
                        wait(1);
                        disp_im(68,110,"two.gci");
                        wait(1);
                        disp_im(68,110,"one.gci");
                        wait(1);
                        games++;    //needs to be updated now every time the images are displayed-->see comment below
                    }
                    //countdown without displaying images: memory space allows only to display ~16 of these images, so afterwards the screen would freeze
                    //if we still try to display them-->alternative without images is a text countdown...
                    else
                    {
                        lcd.txt_Height(5);
                        lcd.txt_MoveCursor(2,14);
                        lcd.txt_Width(3);
                        lcd.printf("3");
                        wait(1);
                        lcd.txt_Width(1);
                        lcd.txt_MoveCursor(2,14);
                        lcd.txt_Width(3);
                        lcd.printf("2");
                        wait(1);
                        lcd.txt_Width(1);
                        lcd.txt_MoveCursor(2,14);
                        lcd.txt_Width(3);
                        lcd.printf("1");
                        wait(1);
                        lcd.txt_Height(1);
                        lcd.txt_Width(1);
                    }
                    black();        //first screen of game
                }
            }
        }
        if(menu==4)
        {
            oldst=status;
            status = lcd.touch_Get(0);
            if (status&&(oldst==0)) //tap ANYWHERE to get back to the menu screen
            {
                menu=0;
                lcd.gfx_Cls();
            }
        }
    }
}