Source code of my entry for Retro gaming contest (www.outrageouscircuits.com)

Dependencies:   MMA8453 mbed

Fork of LCD_DEMO by Chris Taylor

NOTE: I don't why mbed says that it's a fork form LCD_DEMO by Chris Taylor -> I don't use that library

SHAKE THE MAZE!!!

Shake your RETRO board and solve the maze!!! /media/uploads/gbr1mbed/dscn0738.jpg

Different maze everytime! /media/uploads/gbr1mbed/dscn0739.jpg

Here a case I built. /media/uploads/gbr1mbed/dscn0742.jpg

More details in main.cpp comments

video:

main.cpp

Committer:
gbr1mbed
Date:
2015-03-01
Revision:
1:600980390cf7
Parent:
0:7260a2716edf

File content as of revision 1:600980390cf7:

/*   
 *   SHAKE THE MAZE!!!
 *   
 *   Author: BRUNO Giovanni di Dio [ aka GBr1 ]
 *   site: www.gbr1industries.altervista.org
 *
 *   video: http://youtu.be/puCMuQ1Li1I
 *
 *
 *
 *   HOW TO PLAY:
 *      1. You need only the RETRO board by Outrageous Circuit
 *      2. After loaded the file (I use "cp" command in mac osx terminal )
 *          Note: file .bin is about 32KB I don't try if Windows drag and drop works well :(
 *      3. Reset
 *      4. Left LED is turned on
 *      5. Shake the board
 *      6. Press start button (alien spaceship button)
 *      7. Left LED is turned off
 *      8. Wait beep end
 *      9. Move the board to put green ball into red square (finish)
 *     10. When you finish Right LED is turned on
 *     11. Press start to play again
 *     12. Right LED is turned off
 *     13. You have a new maze to solve (go to 8)
 *
 *
 *      NOTE: don't update to a newer version
 */






#include "mbed.h"
//RETRO board display
#include "DisplayN18.h"
//C++ standard stack class
#include <stack>
//RETRO board accelerometer
#include "MMA8453.h"

//accelerometer
MMA8453 acc(P0_5,P0_4);
//x axis
double xx=0;
//y axis
double yy=0;
//z axis (it will be used in initRand())
double zz=0;

//Alien Spaceship button
DigitalIn start(P0_1,PullUp);
//Left LED -> capturing accelerometer data to random seed
DigitalOut ledL(P0_9);
//Right LED -> Do you want to play?
DigitalOut ledR(P0_8);
//Buzzer (due low program memory I use software PWM)
DigitalOut buzzer(P0_18);

//numbers for random function
unsigned int m_z=0;
unsigned int m_w=0;

//display object
DisplayN18 Surface;
//colors used
static const unsigned short CLINE = DisplayN18::BLUE;               //blue
static const unsigned short CBALL = DisplayN18::GREEN;              //green
static const unsigned short CEND = DisplayN18::RED;                 //red
static const unsigned short CBACK = DisplayN18::BLACK;              //black
static const unsigned short CTEXT1 = 0xF81F;                        //yellow
static const unsigned short CTEXT2 = 0xFFE0;                        //violet
static const unsigned short CTEXT3 = 0xFFFF;                        //white

//constants to indicate cardinal coordinates
//I used 0 to 3 because I will do random from 0 to 3
const int NORTH=0;
const int SOUTH=1;
const int EAST=2;
const int WEST=3;
//maze blocks size
const int SIZEX=32;
const int SIZEY=23;

//create maze variables
int nGood = 0;
int locX = 1, locY = 1;

//ball and generally print coordinates
int x=0;
int y=0;


/*my function to explore maze and generate random values*/
//pseudo-random function
//on lpc11u24 random doesn't work (it equals rand() )
unsigned int rnd();

//random seed & initial window (it equals srand and a simple text show function)
void initRand();

//estabilish on which type of pattern you are
//you have to remember that in this case N=1 S=2 E=4 W=8 it allows you to have unique combinations
int evaluateCell(int i, int j,bool grid[SIZEY][SIZEX]);

//redraw ball and check if there are collisions
void updateBall(int direction, bool grid[SIZEY][SIZEX]);

//draw the maze
void printGrid(bool grid[SIZEY][SIZEX]);


/*maze creation functions*/
//I used recoursive backtracker http://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_backtracker

//move on x axis
int moveEW(int direction, int i);
//move on y axis
int moveNS(int direction, int j);
//check if it can go here
bool isGood(int i, int j, int direction, bool grid[SIZEY][SIZEX]);
//create maze
void createMaze(bool grid[SIZEY][SIZEX]);
//draw finish pattern and mark it on the maze and check also if it will be possible to go there
void checkEnd(bool grid[SIZEY][SIZEX]);


/*---game functions---*/

//simple beep
void sound();
//initialize game
void initGame(bool grid[SIZEY][SIZEX]);
//function which allows you to play
void coreGame(bool grid[SIZEY][SIZEX]);


/*---main---*/
int main(){ 
    //create grid
    //using recoursive function is better if grid is here
    bool grid[SIZEY][SIZEX];
    
    //initialize random (first screen)
    initRand();
    //initialize game (second screen - play)
    initGame(grid);   
    //play ;)
    while(1){
        coreGame(grid);   
    }
}




/*---implementations---*/

unsigned int rnd(){
    //pseudo-random function
    m_z = 36969 * (m_z & 65535) + (m_z >>16);
    m_w = 18000 * (m_w & 65535) + (m_w >>16);
    return ((m_z <<16) + m_w);
}

void initRand(){
    //preserve for pressing button before the game
    while(start==0);
    //show initial screen
    ledL=1;
    Surface.clear();
    Surface.drawString(16,30,"Shake it!!!",CTEXT1,CBACK,2);
    Surface.drawString(32,90,"press start",CTEXT2,CBACK);
    Surface.drawString(48,100,"to continue...",CTEXT2,CBACK);
    //wait for checking alien spaceship button
    while(start==1){
        //simple function to read values and sum them
        //NOTE: you need to do abs because accelerometer values could be negative
        acc.getXYZ(xx,yy,zz);
        xx=abs(xx)*200;
        yy=abs(yy)*200;
        zz=abs(zz)*200;
        m_z=m_z+xx*yy+zz;
        m_w=m_w+xx+yy*zz;
    }
    ledL=0;
}   
    


int evaluateCell(int i, int j,bool grid[SIZEY][SIZEX]){
    int cell=0;
    //estabilish type of pattern (just sum values of walls
    if ((i>=0)&&(i<(SIZEX-1))){
        if (grid[j][i+1]==true) {
            cell+=4;
        }
    }
    if ((i>0)&&(i<=(SIZEX-1))){
        if (grid[j][i-1]==true) {
            cell+=8;
        }
    }
    if ((j>=0)&&(j<(SIZEY-1))){
        if (grid[j+1][i]==true) {
            cell+=2;
        }
    }
    if ((j>0)&&(j<=(SIZEY-1))){
        if (grid[j-1][i]==true) {
            cell+=1;
        }
    }
    return cell;
}
    
void updateBall(int direction, bool grid[SIZEY][SIZEX]){
    //clear old ball
    Surface.fillCircle(x+2,y+15,2,CBACK);
    //check if is possible to move ball in "direction"
    //it checks every situation 
    int cell=evaluateCell(x/5,y/5,grid);
    if ((direction==NORTH)&&((cell%2)==0)){
        y=y-5;
    }
    if ((direction==SOUTH)&&((cell==0)||(cell==1)||(cell==4)||(cell==5)||(cell==8)||(cell==9)||(cell==12)||(cell==13))){
        y=y+5;
    }
    if ((direction==EAST)&&((cell==0)||(cell==1)||(cell==2)||(cell==3)||(cell==8)||(cell==9)||(cell==10)||(cell==11))){
        x=x+5;
    }
    if ((direction==WEST)&&((cell>=0)&&(cell<=7))){
        x=x-5;
    }
    //draw ball
    Surface.fillCircle(x+2,y+15,2,CBALL);
}


void printGrid(bool grid[SIZEY][SIZEX]){
    Surface.clear();
    Surface.drawString(2,2,"Shake the Maze!!!  GBr1_15",CTEXT3,CBACK);
    int cell=0;
    for (int j = 0; j < SIZEY; j++){
        for(int i = 0; i < SIZEX; i++){
            cell=0;
            if (grid[j][i]==true) {
                cell=evaluateCell(i,j,grid);
                //offset and 5pixels maze block
                x=i*5;
                y=(j*5)+13;
                //show 16 different cases of maze patterns
                //using N=1 E=4 S=2 W=8  -> do combination
                //example 11 is
                //
                //         OXO
                //         XXO 
                //         OXO 
                //where O is free pattern X is a wall
                
                switch (cell) {
                    case 0:
                        Surface.setPixel(x+2,y+2,CLINE);
                        break;
                    case 1:
                        Surface.drawLine(x+2,y,x+2,y+3,CLINE);
                        break;
                    case 2:
                        Surface.drawLine(x+2,y+2,x+2,y+5,CLINE);
                        break;
                    case 3:
                        Surface.drawLine(x+2,y,x+2,y+5,CLINE);
                        break;
                    case 4:
                        Surface.drawLine(x+2,y+2,x+5,y+2,CLINE);
                        break;
                    case 5:
                        Surface.drawLine(x+2,y,x+2,y+3,CLINE);
                        Surface.drawLine(x+3,y+2,x+5,y+2,CLINE);
                        break;
                    case 6:
                        Surface.drawLine(x+2,y+2,x+5,y+2,CLINE);
                        Surface.drawLine(x+2,y+3,x+2,y+5,CLINE);
                        break;
                    case 7:
                        Surface.drawLine(x+2,y,x+2,y+5,CLINE);
                        Surface.drawLine(x+3,y+2,x+5,y+2,CLINE);
                        break;
                    case 8:
                        Surface.drawLine(x,y+2,x+3,y+2,CLINE);
                        break;
                    case 9:
                        Surface.drawLine(x+2,y,x+2,y+3,CLINE);
                        Surface.drawLine(x,y+2,x+2,y+2,CLINE);
                        break;
                    case 10:
                        Surface.drawLine(x+2,y+2,x+2,y+5,CLINE);
                        Surface.drawLine(x,y+2,x+2,y+2,CLINE);
                        break;
                    case 11:
                        Surface.drawLine(x+2,y,x+2,y+5,CLINE);
                        Surface.drawLine(x,y+2,x+2,y+2,CLINE);
                        break;
                    case 12:
                        Surface.drawLine(x,y+2,x+5,y+2,CLINE);
                        break;
                    case 13:
                        Surface.drawLine(x+2,y,x+2,y+2,CLINE);
                        Surface.drawLine(x,y+2,x+5,y+2,CLINE);
                        break;
                    case 14:
                        Surface.drawLine(x+2,y+3,x+2,y+5,CLINE);
                        Surface.drawLine(x,y+2,x+5,y+2,CLINE);
                        break;
                    case 15:
                        Surface.drawLine(x+2,y,x+2,y+5,CLINE);
                        Surface.drawLine(x,y+2,x+5,y+2,CLINE);
                        break;
                } 
            }
        }
    }
}


int moveEW(int direction, int i){
    if (direction == EAST)
        return i + 1;
    else if (direction == WEST)
        return i - 1;
    else
        return i;
}

int moveNS(int direction, int j){
    if (direction == NORTH)
        return j - 1;
    else if (direction == SOUTH)
        return j + 1;
    else
        return j;
}

bool isGood(int i, int j, int direction, bool grid[SIZEY][SIZEX]){
    i = moveEW(direction,i);
    j = moveNS(direction,j);
    //check if it should be the end
    if ((grid[j][i]==false)||(i>=(SIZEX - 1))||(i<=0)||(j<= 0)||(j>=(SIZEY - 1))){
        return false;
    }
    //check cardinal directions
    if (direction == NORTH){
        if ((grid[j][i-1]!=false)&&(grid[j-1][i]!=false)&&(grid[j][i+1]!=false)&&(grid[j-1][i-1]!=false)&&(grid[j-1][i+1]!=false)){
            return true;
        }
    }
    if (direction == SOUTH){
        if ((grid[j][i-1]!=false)&&(grid[j+1][i]!=false)&&(grid[j][i+1]!=false)&&(grid[j+1][i-1]!=false)&&(grid[j+1][i+1]!=false)){
            return true;
        }
    }
    if (direction == EAST){
        if ((grid[j][i+1]!=false)&&(grid[j-1][i]!=false)&&(grid[j+1][i]!=false)&&(grid[j-1][i+1]!=false)&&(grid[j+1][i+1]!=false)){
            return true;
        }
    }
    if (direction == WEST){
        if ((grid[j][i-1]!=false)&&(grid[j-1][i]!=false)&&(grid[j+1][i]!=false)&&(grid[j-1][i-1]!=false)&&(grid[j+1][i-1]!=false)){
            return true;
        }
    }
    return false;
}

void createMaze(bool grid[SIZEY][SIZEX]){
    //initialize grid
    for (int i = 0; i < SIZEY; i++){
        for(int j = 0; j < SIZEX; j++){
            grid[i][j] = true;
        }
    }
    //initialize stacks for xy coordinates
    stack<int> xValues;
    stack<int> yValues;
    
    nGood = 0;
    int direction = 0;
    
    do{
        //find n good moves
        for (int i = 0; i < 4; i++){
            if (isGood(locX,locY,i,grid))
                nGood++;
        }
        // if only 1 good move, move there
        if (nGood == 1){
            if (isGood(locX,locY,NORTH,grid))
                locY = moveNS(NORTH,locY);
            else if (isGood(locX,locY,SOUTH,grid))
                locY = moveNS(SOUTH,locY);
            else if (isGood(locX,locY,EAST,grid))
                locX = moveEW(EAST,locX);
            else if (isGood(locX,locY,WEST,grid))
                locX = moveEW(WEST,locX);
        }
        // if no good moves, move back in stack
        else if (nGood == 0){
            locX = xValues.top();
            locY = yValues.top();
            xValues.pop();
            yValues.pop();
        }
        //if more than 1 good move, push stack
        else if (nGood > 1){
            xValues.push(locX);
            yValues.push(locY);
            //direction to move randomly chosen
            do{
                direction = rnd()%4;
            }while (!isGood(locX,locY,direction,grid));
            
            locX = moveEW(direction,locX);
            locY = moveNS(direction,locY);
        }
        //set grid
        grid[locY][locX] = false;
        //reset nGood value
        nGood = 0; 
    }while(!xValues.empty());
}
    
void checkEnd(bool grid[SIZEY][SIZEX]){
    //check low right angle
    if (grid[SIZEY-2][SIZEX-2]==true){
        grid[SIZEY-2][SIZEX-2]=false;
    }
    
    int k=1;
    
    while(((grid[SIZEY-3][SIZEX-2]==false)&&(grid[SIZEY-2][SIZEX-2-k]==false))&&(k<SIZEX-3)){
        grid[SIZEY-2][SIZEX-2-k]=false;
        Surface.fillRect((SIZEX-2-k)*5,(SIZEY-2)*5+13,5,5,CBACK);
        k++;
    }
    
    if(k>=SIZEX-3){
        initGame(grid);
    }

    //draw finish square
    Surface.fillRect((SIZEX-2)*5,(SIZEY-2)*5+13,5,5,CEND);
}

void sound(){
    //simulate pwm
    for(int i=0; i<1000; i++){
        buzzer=1;
        wait(0.0005);
        buzzer=0;
    }
}
    
void initGame(bool grid[SIZEY][SIZEX]){
    createMaze(grid);
    printGrid(grid); 
    checkEnd(grid);     //it is for checking the possible maze finish (low right angle)
    x=5;
    y=5;
    Surface.fillCircle(x+2,y+15,2,CBALL);
    sound();
}

void coreGame(bool grid[SIZEY][SIZEX]){
    //read x and y on accelerometer
    xx=acc.getX();
    yy=acc.getY();
    //increase accelerometer value (generally 0.xx and standard value 0.5G and not G); 
    xx=2*xx*10;
    yy=2*yy*10;
    //conditions and relative next move
    //I put 2 as filter value so, if you leave you RETRO on a table ball will not move      
    if(xx<-2){
       updateBall(WEST,grid);
    }
    else{
       if(xx>2){
            updateBall(EAST,grid);
       }
    }
    if(yy>2){
       updateBall(SOUTH,grid);
    }
    else{
       if(yy<-2){
           updateBall(NORTH,grid);
       }
    }
    //check if the ball is at the end of the pattern (low right angle)
    if ((x/5==(SIZEX-2))&&(y/5==SIZEY-2)){
        ledR=1;
        sound();
        //wait for start button
        while(start==1);
        //init game
        initGame(grid);
        ledR=0;
    }
}