Code for 4180 mini project
Dependencies: 4DGL-uLCD-SE SDFileSystem mbed-rtos mbed wave_player
Fork of Pacman by
main.cpp
- Committer:
- rollschild
- Date:
- 2015-10-21
- Revision:
- 2:610d5194c64e
- Parent:
- 1:b86030cf57c4
File content as of revision 2:610d5194c64e:
#include "mbed.h" #include "rtos.h" #include "uLCD_4DGL.h" #include "SDFileSystem.h" #include "wave_player.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 DigitalIn left_pb(p21); // push button DigitalIn right_pb(p22); // push button DigitalIn up_pb(p23); // push button DigitalIn down_pb(p24); // push button SDFileSystem sd(p5, p6, p7, p8, "sd"); //SD card AnalogOut DACout(p18); wave_player waver(&DACout); // wave player 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((gx1==x) && (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; if(lose) //Print game over message if lose (determined in the follow function) { uLCD.cls(); uLCD.printf("Sorry\nGame Over"); // if loss, ring the buzzer FILE *wave_file; wave_file=fopen("/sd/test.wav","r"); waver.play(wave_file); fclose(wave_file); Thread::wait(1000); return void(); } } 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(); } CHECKclearRIGHT(); //If the user remains in the loop, check for a boundary to the right Thread::wait(10); break; } } //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(); } CHECKclearLEFT(); Thread::wait(10); break; } } //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(); } CHECKclearUP(); Thread::wait(10); break; } } //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(); } CHECKclearDOWN(); Thread::wait(10); break; } } //Read the input from pushbuttons void checkMOVE(void) { if(!right_pb) { CHECKclearRIGHT(); PACmoveRIGHT(); } else if(!left_pb) { CHECKclearLEFT(); PACmoveLEFT(); } else if(!up_pb){ CHECKclearUP(); PACmoveUP(); } else if(!down_pb){ 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(1); } } //Thread controlling the movement of the blue ghost void blueGHOST(void const *args) { while(!win) { pickMOVE(); } } int main() { left_pb.mode(PullUp); // The variable left_pb will be zero when the pushbutton for moving the player left is pressed right_pb.mode(PullUp); // The variable rightt_pb will be zero when the pushbutton for moving the player right is pressed up_pb.mode(PullUp); // the variable fire_pb will be zero when the pushbutton for firing a missile is pressed down_pb.mode(PullUp); // the variable fire_pb will be zero when the pushbutton for firing a missile is pressed 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(3000000000); // 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(1); } Thread::wait(500); // Wait .5 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){Thread::wait(1);} }