Skip to content

Instantly share code, notes, and snippets.

@teamtofu teamtofu/main.cpp Secret
Last active Dec 5, 2018

Embed
What would you like to do?
C++ code for the FEH software development project
/************************************************/
/* Names: Jake Osterhout, Russell Steadman */
/* File: SDP_GAME.cpp */
/* Instructor: PARKE 03:00 */
/************************************************/
// Include necessary libraries
#include <FEHIO.h>
#include <FEHUtility.h>
#include <FEHLCD.h>
#include <string.h>
#include <stdlib.h>
// Define screen dimensions and game map dimensions
#define SCRX 320
#define SCRY 240
#define MX 16
#define MY 12
#define COLORA 0xB574DB
#define COLORB 0xF23D30
#define COLORC 0x45ECEF
class Character {
public:
Character(int x = 0, int y = 0, int color = COLORA) : x(x), y(y), xPrior(x), yPrior(y), color(color) {
direction = 0;
onOrange = false;
}
/*
Directions:
0 - North
1 - East
2 - South
3 - West
*/
int direction;
int x;
int y;
int color;
int xPrior;
int yPrior;
bool onOrange;
};
// Stores all of the changing information in the game
struct Memory {
/*
The map describes the position of walls, free space, ghosts, oranges, and pac-man
Codes:
0 - Free Space
1 - Wall
2 - Ghost
3 - Orange
4 - Pac-man
*/
int map[MX][MY];
// List of players with the highest scores
struct Player {
char name[30];
int score;
} leaderboard[6];
Character pacman;
Character ghosts[3];
int points;
bool paused;
bool won;
bool ended;
};
class Game {
public:
Game() {
const char players[6][15] = {"Dr. Parke", "Alex", "Derge", "Max", "Tara", "Kyle"};
for (int i = 0; i < 6; i++) {
strcpy(store.leaderboard[i].name, players[i]);
store.leaderboard[i].score = 0;
}
}
void openRoute(int type);
void modCoord(int direction, int *x, int *y);
void renderCoord(int x, int y);
bool checkDirection(int direction, int x, int y);
bool onlyDirection(int direction, int x, int y);
struct Memory store;
};
void displayMenu(Game *game);
void displayPlay(Game *game);
void displayResults(Game *game);
void displayBoard(Game *game);
void displayRules(Game *game);
void displayCredits(Game *game);
void renderMap(Game *game);
void moveGhosts(Game *game);
void movePacman(Game *game);
int absV(int num);
int main() {
// Set the long side as the x-axis
LCD.SetOrientation(FEHLCD::North);
srand(TimeNow());
// Initialize the game object
Game game;
// Open the menu automatically
game.openRoute(0);
}
void Game::openRoute(int type) {
// Clear the screen and reset colors to default
LCD.Clear();
LCD.SetFontColor(WHITE);
LCD.SetBackgroundColor(BLACK);
/*
Run certain functions based on the route
Route Types:
0 - Menu
1 - Play Game
2 - Game Session Results
3 - Leaderboard
4 - Instructions/Rules
5 - Credits
*/
if (type == 0) {
displayMenu(this);
} else if (type == 1) {
displayPlay(this);
} else if (type == 2) {
displayResults(this);
} else if (type == 3) {
displayBoard(this);
} else if (type == 4) {
displayRules(this);
} else if (type == 5) {
displayCredits(this);
}
}
double absV(double num) {
if (num < 0) return num * -1;
return num;
}
// displayMenu renders the menu and responds to touches
void displayMenu(Game *game) {
// Set the number of options and text to show
int optNum = 5;
const char options[5][30] = {"How to Play FEHeck Yeah", "Play the Game", "Leaderboard", "Reset Stats", "Credits"};
// Draw vertical bounding lines for the menu
for (int i = 0; i < optNum + 1; i++) {
LCD.FillRectangle(0, SCRY / optNum * i - 3, SCRX, 3);
LCD.FillRectangle(0, SCRY / optNum * i, SCRX, 3);
}
// Draw horizontal bounding lines for the menu
LCD.FillRectangle(0, 0, 6, SCRY);
LCD.FillRectangle(SCRX - 6, 0, 6, SCRY);
// Write out the options to the screen
for (int i = 0; i < optNum; i++)
LCD.WriteAt(options[i], SCRX / 2 - strlen(options[i]) / 2 * 12, SCRY * (i * 2. + 1.)/(optNum * 2.) - 8.);
// Wait for the user to touch something
while (true) {
// Record a touch and proceed after the finger has been removed
float xTemp, yTemp, y;
while (!LCD.Touch(&xTemp, &yTemp));
y = yTemp;
while (LCD.Touch(&xTemp, &yTemp));
// Determine which option the user touched
int selected = (y / SCRY * optNum);
if (selected == 0) {
return game->openRoute(4);
} else if (selected == 1) {
return game->openRoute(1);
} else if (selected == 2) {
return game->openRoute(3);
} else if (selected == 3) {
for (int i = 0; i < 6; i++) game->store.leaderboard[i].score = 0;
return game->openRoute(3);
} else if (selected == 4) {
return game->openRoute(5);
}
}
}
// displayPlay renders the game, responds to user interaction, and time-based changes
void displayPlay(Game *game) {
// Set up the map using the specification listed in the Memory struct
int defaultMap[MY][MX] = {
{0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0},
{0,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0},
{0,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0},
{1,1,1,0,1,0,0,0,1,0,1,0,0,0,1,0},
{0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0},
{0,1,0,1,1,0,1,0,0,0,0,0,1,0,1,0},
{0,1,0,1,1,0,1,0,1,0,1,1,1,0,1,0},
{0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0},
{0,1,0,1,1,0,0,0,0,0,1,1,1,1,1,1},
{0,0,0,0,0,0,1,0,1,0,1,1,1,1,1,1},
{1,0,1,1,1,0,1,0,1,0,1,1,1,1,1,1},
{1,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1}
};
for (int x = 0; x < MX; x++) {
for (int y = 0; y < MY; y++) {
// Copy the default into memory
game->store.map[x][y] = defaultMap[y][x];
// Each free space has a 40% chance of containing an orange
if (game->store.map[x][y] == 0 && rand() % 5 < 2) {
game->store.map[x][y] = 3;
}
}
}
// Initialize all of the characters
Character pacman(15, 0, YELLOW);
Character ghost1(0, 0, COLORA);
Character ghost2(1, 11, COLORB);
Character ghost3(9, 8, COLORC);
// Store the characters to memory
game->store.pacman = pacman;
game->store.ghosts[0] = ghost1;
game->store.ghosts[1] = ghost2;
game->store.ghosts[2] = ghost3;
// Initialize game state variables
game->store.points = 0;
game->store.paused = false;
game->store.won = false;
game->store.ended = false;
// Initialize the time and touch variables
double start = TimeNow();
float tX, tY, Xi, Yi, Xf, Yf;
// Change the UI to reflect the map in memory
renderMap(game);
while (true) {
// Stop if the game has ended
if (game->store.ended) return;
// Wait for a user touch or a game tick
while (!LCD.Touch(&tX, &tY) && (TimeNow() - start < 0.5 || game->store.paused));
// If the event was a user touch
if (TimeNow() - start >= 0.5 && !game->store.paused) {
// Reset the start time
start = TimeNow();
// Move pacman in the specified direction
movePacman(game);
// Move the ghosts randomly
moveGhosts(game);
// Check if oranges remain on the board
bool hasOranges = false;
// Does the board have oranges
for (int x = 0; x < MX; x++) {
for (int y = 0; y < MY; y++) {
if (game->store.map[x][y] == 3) {
hasOranges = true;
break;
}
}
if (hasOranges) break;
}
// Are the ghosts on top of oranges
for (int i; i < 3; i++) {
if (game->store.ghosts[i].onOrange) hasOranges = true;
}
// If there are no oranges, then the player wins
if (!hasOranges) {
game->store.ended = true;
game->store.won = true;
return game->openRoute(2);
}
} else if (LCD.Touch(&tX, &tY)) {
// Set the initial touch position
Xi = tX;
Yi = tY;
while (LCD.Touch(&tX, &tY)) {
if (tX != -1) {
// Set the final touch position
Xf = tX;
Yf = tY;
}
}
/*
Detect swipes via displacement of initial & final touch
Conditions:
* Initial touch on map
* Swipe must be at least 10 px
*/
if ((Xi < 200 || Yi < 160) && (absV(Xf - Xi) >= 10 || absV(Yf - Yi) >= 10)) {
if (absV(Xf - Xi) > absV(Yf - Yi)) {
if (Xf - Xi > 0) {
game->store.pacman.direction = 1; // East
} else {
game->store.pacman.direction = 3; // West
}
} else {
if (Yf - Yi > 0) {
game->store.pacman.direction = 2; // South
} else {
game->store.pacman.direction = 0; // North
}
}
}
/*
Detect 6 buttons at the bottom of the screen
*/
if (Xi > 200 && Xi <= 240) {
if (Yi > 160 && Yi <= 200) {
// Toggle pause
game->store.paused = !game->store.paused;
renderMap(game);
} else if (Yi > 200) {
// Left
game->store.pacman.direction = 3;
}
} else if (Xi > 240 && Xi <= 280) {
if (Yi > 160 && Yi <= 200) {
// Up
game->store.pacman.direction = 0;
} else if (Yi > 200) {
// Down
game->store.pacman.direction = 2;
}
} else if (Xi > 280) {
if (Yi > 160 && Yi <= 200) {
// Stop
game->store.ended = true;
return game->openRoute(2);
} else if (Yi > 200) {
// Right
game->store.pacman.direction = 1;
}
}
}
}
}
// renderMap renders the entire game map
void renderMap(Game *game) {
/*
Changes each map square based on its type
* All map squares are 20px by 20px
*/
for (int x = 0; x < MX; x++) {
for (int y = 0; y < MY; y++) {
game->renderCoord(x, y);
}
}
// Draw the buttons at the bottom of the screen
LCD.SetBackgroundColor(WHITE);
LCD.SetFontColor(BLACK);
if (game->store.paused) LCD.WriteAt("PLY", 202, 172);
if (!game->store.paused) LCD.WriteAt("PSE", 202, 172);
LCD.WriteAt("UP", 248, 172);
LCD.WriteAt("END", 282, 172);
LCD.WriteAt("LFT", 202, 212);
LCD.WriteAt("DWN", 242, 212);
LCD.WriteAt("RGT", 282, 212);
LCD.FillRectangle(200, 199, 120, 2);
LCD.FillRectangle(239, 160, 2, 80);
LCD.FillRectangle(279, 160, 2, 80);
}
// renderCoord renders a single map coordinate
void Game::renderCoord(int x, int y) {
if (store.map[x][y] == 0) {
// Draw a black box for free space
LCD.SetFontColor(BLACK);
LCD.FillRectangle(x * 20, y * 20, 20, 20);
} else if (store.map[x][y] == 1) {
// Draw a white box for a wall
LCD.SetFontColor(WHITE);
LCD.FillRectangle(x * 20, y * 20, 20, 20);
} else if (store.map[x][y] == 2) {
// Draw a ghost
LCD.SetFontColor(BLACK);
LCD.FillRectangle(x * 20, y * 20, 20, 20);
LCD.SetFontColor(COLORA);
for (int i = 0; i < 3; i++) {
if (store.ghosts[i].x == x && store.ghosts[i].y == y) {
LCD.SetFontColor(store.ghosts[i].color);
}
}
LCD.FillRectangle(x * 20 + 2, y * 20 + 2, 16, 16);
} else if (store.map[x][y] == 3) {
// Draw an orange
LCD.SetFontColor(BLACK);
LCD.FillRectangle(x * 20, y * 20, 20, 20);
LCD.SetFontColor(ORANGE);
LCD.FillCircle(x * 20 + 10, y * 20 + 10, 5);
} else if (store.map[x][y] == 4) {
// Draw pacman
LCD.SetFontColor(BLACK);
LCD.FillRectangle(x * 20, y * 20, 20, 20);
LCD.SetFontColor(YELLOW);
LCD.FillCircle(x * 20 + 10, y * 20 + 10, 8);
}
}
// Modify the coordinates based on the direction
void Game::modCoord (int direction, int *x, int *y) {
if (direction == 0) *y = *y - 1; // North
if (direction == 1) *x = *x + 1; // East
if (direction == 2) *y = *y + 1; // South
if (direction == 3) *x = *x - 1; // West
}
// Check if the direction is off the board or a wall
bool Game::checkDirection(int direction, int x, int y) {
modCoord(direction, &x, &y);
if (x < 0 || y < 0 || y == MY || x == MX) return false; // Off screen
if (store.map[x][y] == 1) return false; // Wall
return true;
}
// Check if the backwards direction is the only direction
bool Game::onlyDirection(int direction, int x, int y) {
int dirOptions = 0;
// Loop through each direction to check
for (int i = 0; i < 4; i++) {
if (checkDirection(i, x, y)) dirOptions++;
}
if (dirOptions == 1) return true;
return false;
}
// moveGhosts randomly moves the ghosts to new positions
void moveGhosts(Game *game) {
// Loop through each ghost
for (int i = 0; i < 3; i++) {
int direction, y, x, yP = game->store.ghosts[i].yPrior, xP = game->store.ghosts[i].xPrior;
do {
// Generate a random direction 0 to 3
direction = rand() % 4;
// Reset the x and y coordinates to the current position
y = game->store.ghosts[i].y;
x = game->store.ghosts[i].x;
// Modify the x and y to the new coordinates
game->modCoord(direction, &x, &y);
// If the direction is off screen or to a wall, regenerate a direction
if (!game->checkDirection(direction, game->store.ghosts[i].x, game->store.ghosts[i].y)) continue;
// If the direction is backwards and it is not the only direction, regenerate
if ((x == xP && y == yP) && !game->onlyDirection(direction, x, y)) continue;
break;
} while (true);
// The current position is now the prior
yP = game->store.ghosts[i].y;
xP = game->store.ghosts[i].x;
// Update the memory variables
game->store.ghosts[i].yPrior = yP;
game->store.ghosts[i].xPrior = xP;
game->store.ghosts[i].y = y;
game->store.ghosts[i].x = x;
// Remove the ghost from the prior position
if (game->store.ghosts[i].onOrange) {
game->store.map[xP][yP] = 3;
} else if (game->store.map[xP][yP] == 2) {
game->store.map[xP][yP] = 0;
}
game->renderCoord(xP, yP);
// Add the ghost to the new position
if (game->store.map[x][y] == 0) {
game->store.ghosts[i].onOrange = false;
game->store.map[x][y] = 2;
} else if (game->store.map[x][y] == 3) {
game->store.ghosts[i].onOrange = true;
game->store.map[x][y] = 2;
} else if (game->store.map[x][y] == 4) {
// Handle game over
game->store.ended = true;
return game->openRoute(2);
}
game->renderCoord(x, y);
}
}
// movePacman updates Pac-Man's location regularly
void movePacman(Game *game) {
int direction = game->store.pacman.direction, xP = game->store.pacman.x, yP = game->store.pacman.y, x, y;
// The x and y positions are the current ones
x = xP;
y = yP;
// If the new position is valid, change the coordinates
if (game->checkDirection(direction, x, y)) game->modCoord(direction, &x, &y);
// Store the new positions to memory
game->store.pacman.x = x;
game->store.pacman.y = y;
// Remove pacman from the prior position
game->store.map[xP][yP] = 0;
game->renderCoord(xP, yP);
// Add the ghost to the new position
if (game->store.map[x][y] == 0) {
game->store.map[x][y] = 4;
} else if (game->store.map[x][y] == 3) {
game->store.points++;
game->store.map[x][y] = 4;
} else if (game->store.map[x][y] == 2) {
// Handle game over
game->store.ended = true;
return game->openRoute(2);
}
game->renderCoord(x, y);
}
// displayResults renders the results of the game
void displayResults(Game *game) {
LCD.SetFontColor(BLACK);
LCD.FillRectangle(0, 0, SCRX, SCRY);
// Display text based on game outcome
LCD.SetFontColor(WHITE);
if (game->store.won) {
LCD.WriteAt("Flex on 'em! You won.", SCRX / 2 - 126, 40);
} else {
LCD.WriteAt("Rats! You lost.", SCRX / 2 - 90, 40);
}
// Display the number of points earned
LCD.WriteAt("Points Earned:", SCRX / 2 - 84, 80);
LCD.WriteAt(game->store.points, SCRX / 2 - 12, 105);
// Prompt the user to pick a character
LCD.WriteAt("Give points to:", SCRX / 2 - 90, 145);
char *lecture, *lab;
// Write all of the professors & TAs to the screen
for (int i = 0; i < 3; i++) {
lecture = game->store.leaderboard[i].name;
lab = game->store.leaderboard[i + 3].name;
LCD.WriteAt(lecture, SCRX * (i * 2. + 1.) / 6. - strlen(lecture) / 2 * 12, 172);
LCD.WriteAt(lab, SCRX * (i * 2. + 1.) / 6. - strlen(lab) / 2 * 12, 212);
}
while (true) {
// Record a touch and proceed after the finger has been removed
float xTemp, yTemp, x, y;
while (!LCD.Touch(&xTemp, &yTemp));
x = xTemp;
y = yTemp;
while (LCD.Touch(&xTemp, &yTemp));
// Detect pressing the professors and TAs, then proceed to the leaderboard
if (x <= 107) {
if (y > 160 && y <= 200) {
game->store.leaderboard[0].score += game->store.points;
return game->openRoute(3);
} else if (y > 200) {
game->store.leaderboard[3].score += game->store.points;
return game->openRoute(3);
}
} else if (x <= 214) {
if (y > 160 && y <= 200) {
game->store.leaderboard[1].score += game->store.points;
return game->openRoute(3);
} else if (y > 200) {
game->store.leaderboard[4].score += game->store.points;
return game->openRoute(3);
}
} else {
if (y > 160 && y <= 200) {
game->store.leaderboard[2].score += game->store.points;
return game->openRoute(3);
} else if (y > 200) {
game->store.leaderboard[5].score += game->store.points;
return game->openRoute(3);
}
}
}
}
// displayBoard renders the leaderboard
void displayBoard(Game *game) {
// Display title and instructions
LCD.WriteAt("Leaderboard", SCRX / 2 - 66, 7);
LCD.WriteAt("**Tap to continue**", SCRX / 2 - 114, 25);
// Display the name and points of each professor and TA
for (int i = 0; i < 6; i++) {
LCD.WriteAt(game->store.leaderboard[i].name, 40, 67 + 30 * i);
LCD.WriteAt(game->store.leaderboard[i].score, 180, 67 + 30 * i);
}
// Proceed after the finger has been removed
float x, y;
while (!LCD.Touch(&x, &y));
while (LCD.Touch(&x, &y));
// Open the menu screen
return game->openRoute(0);
}
// displayRules explains how to play
void displayRules(Game *game) {
int page = 0;
while (true) {
LCD.Clear();
LCD.WriteLine("FEHeck Yeah Guide");
LCD.WriteLine("**Tap to continue**");
LCD.WriteLine(" ");
switch (page) {
case 0:
LCD.WriteLine("Goal:");
LCD.WriteLine("* Collect all oranges");
LCD.WriteLine(" ");
LCD.WriteLine("Rules:");
LCD.WriteLine("* Don't touch ghosts");
LCD.WriteLine("* Manuver around walls");
break;
case 1:
LCD.WriteLine("Instructions:");
LCD.WriteLine("* Tap UP to go up");
LCD.WriteLine("* Tap DWN to go down");
LCD.WriteLine("* Tap RGT to go right");
LCD.WriteLine("* Tap LEFT to go left");
LCD.WriteLine("* Tap PSE to pause");
LCD.WriteLine("* Tap PLY to play");
LCD.WriteLine("* Tap END to end game");
break;
case 2:
LCD.WriteLine("Instructions (cont'):");
LCD.WriteLine("* You can also swipe");
LCD.WriteLine(" on the map to change");
LCD.WriteLine(" direction");
break;
default:
return game->openRoute(0);
}
// Proceed after the finger has been removed
float x, y;
while (!LCD.Touch(&x, &y));
while (LCD.Touch(&x, &y));
// Increment the page
page++;
}
}
// displayCredits attributes developers
void displayCredits(Game *game) {
int page = 0;
while (true) {
LCD.Clear();
LCD.WriteLine("FEHeck Yeah Credits");
LCD.WriteLine("**Tap to continue**");
LCD.WriteLine(" ");
if (page == 0) {
LCD.WriteLine("Developers:");
LCD.WriteLine("* Jake Osterhout");
LCD.WriteLine("* Russell Steadman");
LCD.WriteLine(" ");
LCD.WriteLine("References:");
LCD.WriteLine("* u.osu.edu/fehproteus");
LCD.WriteLine("* Pac-Man by BANDAI");
LCD.WriteLine(" NAMCO Entertainment");
} else {
return game->openRoute(0);
}
// Proceed after the finger has been removed
float x, y;
while (!LCD.Touch(&x, &y));
while (LCD.Touch(&x, &y));
// Increment the page
page++;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.