This program runs through one level of Pacman. Be advised. This game is not fully functional. The ghost character was commented out due to its seeming inability to coexist with the Pacman character using my current code.

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

main.cpp

Committer:
srigdon3
Date:
2014-03-25
Revision:
1:b86030cf57c4
Parent:
0:0a900ff9a788

File content as of revision 1:b86030cf57c4:

/*
So far, this program will run through one level of what will hopefully be a full Pacman game.
As of now, only Pacman will run all the way through, so the thread to start the blue ghost has
been commented out.  This program only runs through one level of the game.
*/

#include "mbed.h"
#include "rtos.h"
#include "uLCD_4DGL.h"

#define PAC_SIZE    5   //The radius of Pacman and the ghost
#define STEP_SIZE   8   //The number of pixels each character moves at once
#define CLEARANCE   12  //The number of pixels each character checks ahead before moving

AnalogIn jsx(p19);  // The joysticks origin is about 1.6V in both directions
AnalogIn jsy(p20);  // For just three states in each direction, thresholds .33V and 3V were used
uLCD_4DGL uLCD(p28, p27, p29);
Mutex lcd_mutex;

void checkMOVE(void);   //This function is defined below.  It was written here since other functions return to it that it also calls.

//several variables are used by multiple threads
volatile bool win=false;    //True when pacman has eaten all coins
volatile bool lose=false;   //True when the position of the ghost and pacman are the same
volatile int x = 64;        //x and y are pacman's position.  The starting position is defined here.
volatile int y = 88;
volatile int gx1 = 64;      //Starting position of the blue ghost
volatile int gy1 = 40;
int i;
bool clearRIGHT,clearLEFT,clearUP,clearDOWN,bgcr,bgcl,bgcu,bgcd;

//An array containing the locations of the 81 coins pacman must eat
int coins[81][2] = {
        {40,88},{48,88},{56,88},{72,88},{80,88},{88,88},
        {40,40},{48,40},{56,40},{64,40},{72,40},{80,40},{88,40},
        {40,48},{40,56},{40,64},{40,72},{40,80},
        {88,48},{88,56},{88,64},{88,72},{88,80},
        {56,96},{56,104},{56,112},
        {48,112},{40,112},{32,112},{24,112},{16,112},
        {16,104},{16,96},{16,88},{16,80},{16,72},
        {24,64},{32,64},
        {16,64},{16,56},{16,48},{16,40},{16,32},{16,24},{16,16},
        {24,16},{32,16},{40,16},{48,16},{56,16},
        {56,24},{56,32},
        {72,96},{72,104},{72,112},
        {80,112},{88,112},{96,112},{104,112},{112,112},
        {112,104},{112,96},{112,88},{112,80},{112,72},
        {104,64},{96,64},
        {112,64},{112,56},{112,48},{112,40},{112,32},{112,24},{112,16},
        {104,16},{96,16},{88,16},{80,16},{72,16},
        {72,24},{72,32}
    };

//This function is used in the ghost thread to replace coins as it passes over them
void replaceCOINS(void)
{
    for(int n=0; n<81; n++)
    {
        lcd_mutex.lock();   //The coins array is used by both threads
        if(gx1 == coins[n][0] && gy1 == coins[n][1])
        {
            uLCD.filled_circle(gx1,gy1,1,0xFFFF00);     //compare the set of coins to the ghost's previous position and if there is a match redraw coin   
        }
        lcd_mutex.unlock();
    }
}

//Checks if the ghost can move right (there is no boundary immediately to the right)
void BGclearRIGHT(void)
{
    bgcr = true;
    for(int p=gx1; p <= gx1+CLEARANCE; p++)
    {
        lcd_mutex.lock();
        if(uLCD.read_pixel(p,gy1)==uLCD.read_pixel(4,4))
        {
            bgcr = false;   //compare the pixels immediately in front of the ghost to the boundary up to the spec. clearance
        }                   //if they are the same color, determine the ghost can't move right
        lcd_mutex.unlock();
    }
}

//Checks if ghost can move left
void BGclearLEFT(void)
{
    bgcl = true;
    for(int p=gx1; p >= gx1-CLEARANCE; p--)
    {
        lcd_mutex.lock();
        if(uLCD.read_pixel(p,gy1)==uLCD.read_pixel(4,4))
        {
            bgcl = false;
        }
        lcd_mutex.unlock();
    }
}

//Checks if ghost can move up
void BGclearUP(void)
{
    bgcu = true;
    for(int p=gy1; p >= gy1-CLEARANCE; p--)
    {
        lcd_mutex.lock();
        if(uLCD.read_pixel(gx1,p)==uLCD.read_pixel(4,4))
        {
            bgcu = false;
        }
        lcd_mutex.unlock();
    }
}

//Checks if ghost can move down
void BGclearDOWN(void)
{
    bgcd = true;
    for(int p=gy1; p <= gy1+CLEARANCE; p++)
    {
        lcd_mutex.lock();
        if(uLCD.read_pixel(gx1,p)==uLCD.read_pixel(4,4))
        {
            bgcd = false;
        }
        lcd_mutex.unlock();
    }
}

//Moves the blue ghost to the right
void bgRIGHT(void)
{
    Thread::wait(50);
    lcd_mutex.lock();
    uLCD.filled_rectangle(gx1-PAC_SIZE,gy1-PAC_SIZE,gx1+PAC_SIZE,gy1+PAC_SIZE,BLACK);   //erase the previous ghost drawing
    lcd_mutex.unlock();
    replaceCOINS();     //replace the coin the ghost was just on if there was one
    if(gx1>124)         //This will cause the ghost to wrap around to the left side of the screen if there were no boundary on the far right
    {
        gx1 = 0;
    }
    gx1 = gx1+STEP_SIZE;    //Move one step size in the x direction
    lcd_mutex.lock();
    //redraw the ghost at the new position
    uLCD.filled_circle(gx1,gy1,PAC_SIZE,BLUE);
    uLCD.filled_rectangle(gx1-PAC_SIZE,gy1,gx1+PAC_SIZE,gy1+PAC_SIZE,BLUE);
    uLCD.filled_circle(gx1+2,gy1-2,1,BLACK);
    uLCD.filled_circle(gx1-2,gy1-2,1,BLACK);
    lcd_mutex.unlock();
}

//Moves the blue ghost left
void bgLEFT(void)
{
    Thread::wait(50);
    lcd_mutex.lock();
    uLCD.filled_rectangle(gx1-PAC_SIZE,gy1-PAC_SIZE,gx1+PAC_SIZE,gy1+PAC_SIZE,BLACK);
    lcd_mutex.unlock();
    replaceCOINS();
    if(gx1<4)
    {
        gx1 = 124;
    }
    gx1 = gx1-STEP_SIZE;
    lcd_mutex.lock();
    uLCD.filled_circle(gx1,gy1,PAC_SIZE,BLUE);
    uLCD.filled_rectangle(gx1-PAC_SIZE,gy1,gx1+PAC_SIZE,gy1+PAC_SIZE,BLUE);
    uLCD.filled_circle(gx1+2,gy1-2,1,BLACK);
    uLCD.filled_circle(gx1-2,gy1-2,1,BLACK);
    lcd_mutex.unlock();
}

//Moves the blue ghost up
void bgUP(void)
{
    Thread::wait(50);
    lcd_mutex.lock();
    uLCD.filled_rectangle(gx1-PAC_SIZE,gy1-PAC_SIZE,gx1+PAC_SIZE,gy1+PAC_SIZE,BLACK);
    lcd_mutex.unlock();
    replaceCOINS();
    if(gy1<4)
    {
        gy1 = 124;
    }
    gy1 = gy1-STEP_SIZE;
    lcd_mutex.lock();
    uLCD.filled_circle(gx1,gy1,PAC_SIZE,BLUE);
    uLCD.filled_rectangle(gx1-PAC_SIZE,gy1,gx1+PAC_SIZE,gy1+PAC_SIZE,BLUE);
    uLCD.filled_circle(gx1+2,gy1-2,1,BLACK);
    uLCD.filled_circle(gx1-2,gy1-2,1,BLACK);
    lcd_mutex.unlock();
}

//Moves the blue ghost down
void bgDOWN(void)
{
    Thread::wait(50);
    lcd_mutex.lock();
    uLCD.filled_rectangle(gx1-PAC_SIZE,gy1-PAC_SIZE,gx1+PAC_SIZE,gy1+PAC_SIZE,BLACK);
    lcd_mutex.unlock();
    replaceCOINS();
    if(gy1>124)
    {
        gy1 = 0;
    }
    gy1 = gy1+STEP_SIZE;
    lcd_mutex.lock();
    uLCD.filled_circle(gx1,gy1,PAC_SIZE,BLUE);
    uLCD.filled_rectangle(gx1-PAC_SIZE,gy1,gx1+PAC_SIZE,gy1+PAC_SIZE,BLUE);
    uLCD.filled_circle(gx1+2,gy1-2,1,BLACK);
    uLCD.filled_circle(gx1-2,gy1-2,1,BLACK);
    lcd_mutex.unlock();
}

//Force ghost to chase Pacman
void follow(void)
{
    if(x==gx1 && y==gy1)    //if the ghost and Pacman are at the same position trigger losing condition
    {
        win = true;         //This is set to true just to exit the check for a win loop and terminate other loops without writing additional conditions
        lose = true;
    }
    while(x==gx1 && gy1<y && !win)  //If the ghost is directly above Pacman check to see if moving down is possible, then move down
    {
        BGclearDOWN();  
        bgDOWN();
    }
    while(x==gx1 && gy1>y && !win)
    {
        BGclearUP();
        bgUP();
    }
    while(y==gy1 && gx1<x && !win)
    {
        BGclearRIGHT();
        bgRIGHT();
    }
    while(y==gy1 && gx1>x && !win)
    {
        BGclearLEFT();
        bgLEFT();
    }
}

//Ghost selects a direction to move
void pickMOVE(void)
{
    while((gx1==x || gy1==y) && abs(x-gx1)+abs(y-gy1)<=16 && !win)  //If Pacman is close by give chase
    {
        follow();
    }
    int dec = rand()%4; //randomly generate a number from the set 0,1,2,3, which serves as the direction decision
    if(dec == 0)
    {
        BGclearRIGHT();
        while(bgcr && !win)     //If decision 0 was reached, check to the the move right until a boundary is reached
        {
            bgRIGHT();
            BGclearRIGHT();
        }
    }
    else if(dec == 1)
    {
        BGclearLEFT();
        while(bgcl && !win)
        {
            bgLEFT();
            BGclearLEFT();
        }
    }
    else if(dec == 2)
    {
        BGclearUP();
        while(bgcu && !win)
        {
            bgUP();
            BGclearUP();
        }
    }
    else
    {
        BGclearDOWN();
        while(bgcd && !win)
        {
            bgDOWN();
            BGclearDOWN();
        }
    }
}        

//Check if Pacman can move one step size to the right (Essentially the same as checking for the ghost)
void CHECKclearRIGHT(void)
{
    clearRIGHT = true;
    for(i=x; i <= x+CLEARANCE; i++)
    {
        lcd_mutex.lock();
        if(uLCD.read_pixel(i,y)==uLCD.read_pixel(4,4))
        {
            clearRIGHT = false;
        }
        lcd_mutex.unlock();
    }
}

//Check if Pacman can move left
void CHECKclearLEFT(void)
{
    clearLEFT = true;
    for(i=x; i >= x-CLEARANCE; i--)
    {
        lcd_mutex.lock();
        if(uLCD.read_pixel(i,y)==uLCD.read_pixel(4,4))
        {
            clearLEFT = false;
        }
        lcd_mutex.unlock();
    }
}

//Check if Pacman can move up
void CHECKclearUP(void)
{
    clearUP = true;
    for(i=y; i >= y-CLEARANCE; i--)
    {
        lcd_mutex.lock();
        if(uLCD.read_pixel(x,i)==uLCD.read_pixel(4,4))
        {
            clearUP = false;
        }
        lcd_mutex.unlock();
    }
}

//Check if Pacman can move down
void CHECKclearDOWN(void)
{
    clearDOWN = true;
    for(i=y; i <= y+CLEARANCE; i++)
    {
        lcd_mutex.lock();
        if(uLCD.read_pixel(x,i)==uLCD.read_pixel(4,4))
        {
            clearDOWN = false;
        }
        lcd_mutex.unlock();
    }
}

//This function tracks the coin Pacman eats as he passes over it
void changeCOINS(void)
{
    for(int m=0; m<81; m++)
    {
        lcd_mutex.lock();
        if(x == coins[m][0] && y == coins[m][1])    //Compare Pacman's position to the set of coins
        {
            coins[m][0]=64;                         //If there is a match, change that coins location to the center of the board where Pacman
            coins[m][1]=64;                         //cannot go, but do not draw the coin
        }
        lcd_mutex.unlock();
    }
}

//Move Pacman one step size to the right
void PACmoveRIGHT(void)
{
    while(clearRIGHT && !win)   //Not win indicates the game has not ended
    {
        lcd_mutex.lock();
        uLCD.filled_circle(x,y,PAC_SIZE,BLACK);     //Erase Pacman at his last location
        lcd_mutex.unlock();
        if(x>124)       //wrap around if moving off the board
        {
            x = 0;
        }
        x = x+STEP_SIZE;    //move Pacman one step size to the right
        changeCOINS();  //Track the coin that was eaten at the last location
        
        if(x%(2*STEP_SIZE) == 0)    //There are two drawings provided for Pacman.  The if statement causes Pacman to open his mouth every other move.
        {
            lcd_mutex.lock();
            uLCD.filled_circle(x,y,PAC_SIZE,0xFFFF00);
            uLCD.filled_rectangle(x+2,y-2,x+PAC_SIZE,y+2,BLACK);
            lcd_mutex.unlock();
        }
        else
        {
            lcd_mutex.lock();
            uLCD.filled_circle(x,y,PAC_SIZE,0xFFFF00);
            uLCD.filled_rectangle(x+2,y,x+PAC_SIZE,y+1,BLACK);
            lcd_mutex.unlock();
        }
        if(jsx <= .9)       //If the user is still holding the joystick to the right, remain in this loop
        {
            checkMOVE();
        }
        CHECKclearRIGHT();  //If the user remains in the loop, check for a boundary to the right
        Thread::wait(10);
    }
}

//Move Pacman left
void PACmoveLEFT(void)
{
    while(clearLEFT && !win)
    {
        lcd_mutex.lock();
        uLCD.filled_circle(x,y,PAC_SIZE,BLACK);
        lcd_mutex.unlock();
        if(x<4)
        {
            x = 128;
        }
        x = x-STEP_SIZE;
        changeCOINS();
        if(x%(2*STEP_SIZE) == 0)
        {
            lcd_mutex.lock();
            uLCD.filled_circle(x,y,PAC_SIZE,0xFFFF00);
            uLCD.filled_rectangle(x-2,y-2,x-PAC_SIZE,y+2,BLACK);
            lcd_mutex.unlock();
        }
        else
        {
            lcd_mutex.lock();
            uLCD.filled_circle(x,y,PAC_SIZE,0xFFFF00);
            uLCD.filled_rectangle(x-2,y,x-PAC_SIZE,y+1,BLACK);
            lcd_mutex.unlock();
        }
        if(jsx >= .1)
        {
            checkMOVE();
        }
        CHECKclearLEFT();
        Thread::wait(10);
    }
}

//Move Pacman up
void PACmoveUP(void)
{
    while(clearUP && !win)
    {
        lcd_mutex.lock();
        uLCD.filled_circle(x,y,PAC_SIZE,BLACK);
        lcd_mutex.unlock();
        if(y<4)
        {
            y = 128;
        }
        y = y-STEP_SIZE;
        changeCOINS();
        if(y%(2*STEP_SIZE) == 0)
        {
            lcd_mutex.lock();
            uLCD.filled_circle(x,y,PAC_SIZE,0xFFFF00);
            uLCD.filled_rectangle(x-2,y-2,x+2,y-PAC_SIZE,BLACK);
            lcd_mutex.unlock();
        }
        else
        {
            lcd_mutex.lock();
            uLCD.filled_circle(x,y,PAC_SIZE,0xFFFF00);
            uLCD.filled_rectangle(x,y-2,x+1,y-PAC_SIZE,BLACK);
            lcd_mutex.unlock();
        }
        if(jsy <= .9)
        {
            checkMOVE();
        }
        CHECKclearUP();
        Thread::wait(10);
    }
}

//Move Pacman down
void PACmoveDOWN(void)
{
    while(clearDOWN && !win)
    {
        lcd_mutex.lock();
        uLCD.filled_circle(x,y,PAC_SIZE,BLACK);
        lcd_mutex.unlock();
        if(y>124)
        {
            y = 0;
        }
        y = y+STEP_SIZE;
        changeCOINS();
        if(y%(2*STEP_SIZE) == 0)
        {
            lcd_mutex.lock();
            uLCD.filled_circle(x,y,PAC_SIZE,0xFFFF00);
            uLCD.filled_rectangle(x-2,y+2,x+2,y+PAC_SIZE,BLACK);
            lcd_mutex.unlock();
        }
        else
        {
            lcd_mutex.lock();
            uLCD.filled_circle(x,y,PAC_SIZE,0xFFFF00);
            uLCD.filled_rectangle(x,y+2,x+1,y+PAC_SIZE,BLACK);
            lcd_mutex.unlock();
        }
        if(jsy >= .1)
        {
            checkMOVE();
        }
        CHECKclearDOWN();
        Thread::wait(10);
    }
}

//Read the input from the joystick and select a direction to move
//The thresholds are set near the end of their ranges to eliminate unintentional moves as much as possible
void checkMOVE(void)
{
    if(jsx > .9)
    {
        CHECKclearRIGHT();
        PACmoveRIGHT();
    }
    else if(jsx < .1)
    {
        CHECKclearLEFT();
        PACmoveLEFT();
    }
    if(jsy > .9)
    {
        CHECKclearUP();
        PACmoveUP();
    }
    else if(jsy < .1)
    {
        CHECKclearDOWN();
        PACmoveDOWN();
    }
}

//Draw the boudaries for the game using the uLCD graphics commands
void drawBORDERS(void)
{
    //Outer Border
    uLCD.rectangle(4,4,124,124,RED);
    uLCD.line(8,8,8,120,RED);
    uLCD.line(8,8,62,8,RED);
    uLCD.line(62,8,62,32,RED);
    uLCD.line(62,32,66,32,RED);
    uLCD.line(66,32,66,8,RED);
    uLCD.line(66,8,120,8,RED);
    uLCD.line(120,8,120,120,RED);
    uLCD.line(120,120,66,120,RED);
    uLCD.line(66,120,66,96,RED);
    uLCD.line(66,96,62,96,RED);
    uLCD.line(62,96,62,120,RED);
    uLCD.line(62,120,8,120,RED);
    //Inner Rectangle
    uLCD.rectangle(52,52,76,76,RED);
    uLCD.rectangle(48,48,80,80,RED);
    //Upper Left Corner
    uLCD.line(48,24,24,24,RED);
    uLCD.line(24,24,24,56,RED);
    uLCD.line(24,56,32,56,RED);
    uLCD.line(32,56,32,32,RED);
    uLCD.line(32,32,48,32,RED);
    uLCD.line(48,32,48,24,RED);
    //Upper Right Corner
    uLCD.line(80,24,104,24,RED);
    uLCD.line(104,24,104,56,RED);
    uLCD.line(104,56,96,56,RED);
    uLCD.line(96,56,96,32,RED);
    uLCD.line(96,32,80,32,RED);
    uLCD.line(80,32,80,24,RED);
    //Lower Left Corner
    uLCD.line(48,104,24,104,RED);
    uLCD.line(24,104,24,72,RED);
    uLCD.line(24,72,32,72,RED);
    uLCD.line(32,72,32,96,RED);
    uLCD.line(32,96,48,96,RED);
    uLCD.line(48,96,48,104,RED);
    //Lower Right Corner
    uLCD.line(80,104,104,104,RED);
    uLCD.line(104,104,104,72,RED);
    uLCD.line(104,72,96,72,RED);
    uLCD.line(96,72,96,96,RED);
    uLCD.line(96,96,80,96,RED);
    uLCD.line(80,96,80,104,RED);
}

void placeCOINS(void)
{
    for(int j=0; j<81; j++)
    {
        uLCD.filled_circle(coins[j][0],coins[j][1],1,0xFFFF00);     //Draw the coins in their initial locations
    }
}

//Draw all the initial states of the game
void initialize(void)
{
    drawBORDERS();
    placeCOINS();
    uLCD.filled_circle(x,y,PAC_SIZE,0xFFFF00);
    uLCD.filled_rectangle(x-2,y-2,x-PAC_SIZE,y+2,BLACK);
}

//Check to see if all the coins have been eaten
void checkWIN(void)
{
    win = true;
    for(int k=0; k<81; k++)
    {
        lcd_mutex.lock();
        if(coins[k][0]!=64 || coins[k][1]!=64)  //Check the locations of all coins and if 1 has coordinates other than (64,64) the user has not won
        {
            win = false;
        }
        lcd_mutex.unlock();
    }
}

//Thread supervising the joystick inputs and moving Pacman accordingly
void pacMOVE(void const *args)
{
    while(!win)
    {
        checkMOVE();
        Thread::wait(10);
    }
}

//Thread controlling the movement of the blue ghost
void blueGHOST(void const *args)
{
    while(!win)
    {
        pickMOVE();
    }
}

int main()
{
    uLCD.cls();
    uLCD.baudrate(BAUD_3000000);
    initialize();                   //Draw the level setup
    Thread pm(pacMOVE);             //Start the thread for moving Pacman
    //Thread bg(blueGHOST);         //Start the thread for moving the blue ghost
    
    Thread::wait(5000);             //Wait some time before checking the win conditions since it will take around 30 secs to eat all 81 coins
    while(!win)                     //Check to see if there was a win once every  tenth of a second
    {
        checkWIN();
        Thread::wait(100);
    }
    
    Thread::wait(1000);             //Wait one second before displaying end message
    
    if(lose)                        //Print game over message if lose (determined in the follow function)
    {
        uLCD.cls();
        uLCD.printf("Sorry\nGame Over");
    }
    else
    {
        uLCD.cls();
        uLCD.printf("Congratulations!\nYou Won!");
    }
    
    while(1){}
}