Skip to content

Instantly share code, notes, and snippets.

@cybrox
Last active August 29, 2015 14:15
Show Gist options
  • Save cybrox/86e69f95b39e9afc6fdd to your computer and use it in GitHub Desktop.
Save cybrox/86e69f95b39e9afc6fdd to your computer and use it in GitHub Desktop.
/**
* @file sketch.ino
* @author Sven Marc 'cybrox' Gehring
* @date 2015-02-20
* @brief Pathetic guitar hero implementation for RKAG Matrix
*/
#include "Matrix_RKAG.h"
#include "SPI.h"
#include "Wire.h"
#define ENEMIES 10 /* number of max enemies */
#define INCREASE_SPEED false /* game speed will increase over time if set */
matrix matrix; /* global instance of our matrix class */
enum colNumber { /* enumerable with col type identifiers */
LEFT, /* led cols 0 - 1 */
MIDDLE, /* led cols 3 - 4 */
RIGHT /* led cols 6 - 7 */
};
/**
* @brief Struct that holds all the input values
*/
struct InputDataTag {
int poti; /* value of the on-board potentiometer */
bool button1; /* value of the left button */
bool button2; /* value of the middle button */
bool button3; /* value of the right button */
} inputData = { 0, false, false, false };
/**
* @brief Struct that holds the current game data
*/
struct currentGameTag {
bool started; /* indicator wether or not the game has started */
int speed; /* speed value calculated from the potentiomenter */
int multi; /* the multiplier for the players points */
int cRound; /* current round identifier */
double points; /* points scored in the current game */
} currentGame = { false, 0, 0, 0, 0 };
/**
* @brief Ene object data
* Each enemy object is represented by an EnemyObjectTag, its place
* in the enemy queue is freed when the player "hits" the target.
*/
typedef struct EnemyObjectTag {
enum colNumber col; /* indicator for the current column of the enemy */
int row; /* current row (from top) */
bool alive; /* indicator if the enemy is alive */
} EnemyObject_t;
static EnemyObject_t l_EnemyObject[ENEMIES]; /* static array of enemies present */
bool firstStart = false; /* first start helper */
int roundCount = 0; /* round count for delayed action */
int scoreCount = 0; /* helper to dispay score */
/* Forward declarations of functions */
void inputDataUpdate(void); /* update all input data */
void currentGameAddEnemy(void); /* add a new enemy */
void currentGameMoveEnemy(void); /* move all enemies present on the game fild */
void currentGameShootEnemy(void); /* check button press and enemy positions */
void currentGameDisplayEnemy(void); /* display all enemies in the matrix */
void currentGameResetEnemies(void); /* reset all enemies */
/**
* @brief Setup method, initializing rkag matrix
*/
void setup() {
matrix.init(); /* initialize our matrix object */
}
/**
* @brief Main loop, arduino default
* For this application, the loop will run with a default cycle delay
* of roughly 1ms.
*/
void loop() {
if(roundCount >= floor(currentGame.speed / 10)){
if(currentGame.started){ /* if the game is started, run the game loop */
if(random(0, 4) == 2) currentGameAddEnemy();/* random chance to add a new enemy to the game */
currentGameMoveEnemy(); /* move all enemies present on the game fild */
currentGameDisplayEnemy(); /* display all enemies on the matrix */
} else { /* otherwise display the user's points */
if(inputData.button1 || inputData.button2 || inputData.button3){ /* start on button press */
currentGameResetEnemies(); /* reset all enemies */
currentGame.started = true;
currentGame.points = 0;
firstStart = true; /* tell the programm that we startet at least one game */
}
if(firstStart){
if(scoreCount >= 0){
int mp = pow(10, scoreCount);
int gp = currentGame.points;
int ft = 0x30 + floor((gp % (mp*10) - gp % mp) / mp);
if(scoreCount == 2) ft = ft - 1; /* Hacky workaround for round bug */
if(ft < 48 || ft > 57) ft = 48; /* secure number area 0 - 9 */
matrix.clear();
delay(10); /* small pause to allow recognizing same numbers */
matrix.font_write(ft);
scoreCount--;
} else {
matrix.font_write('-');
scoreCount = 4;
}
} else {
matrix.write(0xFF, 0x7E, 0x3C, 0x18, 0x18, 0x3C, 0x7E, 0xFF); /* Initial game screen */
}
}
roundCount = 0; /* reset round counter for delay */
} else {
roundCount++;
}
inputDataUpdate(); /* update all our input information */
delay(10);
}
/**
* @brief Read all input values from the connected input peripherials
* This will read all input peripherials and generate the game speed and
* score multipliers at the same time.
*/
void inputDataUpdate(void){
int keyTemp = matrix.key(); /* read keys from port expander */
inputData.poti = analogRead(A0);
inputData.button1 = ((keyTemp & 0x01) == 1);
inputData.button2 = ((keyTemp & 0x02) == 2);
inputData.button3 = ((keyTemp & 0x04) == 4);
int speed = 1024 - inputData.poti; /* invert poti value for left - right speed */
currentGame.speed = 1000 - (speed / 1.2);
#if (INCREASE_SPEED == true)
currentGame.speed -= (currentGame.cRound / 2);
#endif
currentGame.multi = ceil(speed / 200); /* calculate score multiplier */
currentGameShootEnemy(); /* check if the button presses match enemy positions */
}
/**
* @brief add a new enemy to the game with a random col number
* @note Little hacky, the initial row is set to -1 because it's incremented pre-display.
*/
void currentGameAddEnemy(void){
for(int i = 0; i < ENEMIES; i++){
if(l_EnemyObject[i].alive == false){ /* search for a free (dead) enemy space */
l_EnemyObject[i].alive = true;
l_EnemyObject[i].col = (colNumber)random(0,3);
l_EnemyObject[i].row = -1;
break;
}
}
}
/**
* @brief Move all currently active enemies
* This will move every enemy down one line and check if he reached the bottom.
* If he reached line 8, the game will be terminated since that is a failure.
*/
void currentGameMoveEnemy(void){
currentGame.cRound++; /* increment current round counter */
for(int i = 0; i < ENEMIES; i++){
if(l_EnemyObject[i].alive == true){
l_EnemyObject[i].row++; /* move the enemy one line down */
if(l_EnemyObject[i].row >= 8){ /* if the enemy has reached the bottom */
delay(1000); /* delay to notify user */
l_EnemyObject[i].alive = false; /* kill enemy */
currentGame.started = false; /* end the current game */
currentGame.cRound = 0; /* reset the delay modifier */
}
}
}
}
/**
* @brief Check button presses and enemy position
* Kill enemies where button is pressed
*/
void currentGameShootEnemy(void){
for(int i = 0; i < ENEMIES; i++){
if(l_EnemyObject[i].alive && l_EnemyObject[i].row >= 5 ){
if(
l_EnemyObject[i].col == LEFT && inputData.button1 ||
l_EnemyObject[i].col == MIDDLE && inputData.button2 ||
l_EnemyObject[i].col == RIGHT && inputData.button3
){
currentGame.points += currentGame.multi;
l_EnemyObject[i].alive = false;
}
}
}
}
/**
* @brief Empty the enemies array
*/
void currentGameResetEnemies(void){
for(int i = 0; i < ENEMIES; i++) l_EnemyObject[i].alive = false;
}
/**
* @brief Place and display enemies in matrix
* @note The matrix array is char-casted here, a method overload would
* be more convenient to write the data to the matrix field.
*/
void currentGameDisplayEnemy(void){
int matrixData[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
int matrixMask[] = { 0xC0, 0x18, 0x03 };
for(int i = 0; i < ENEMIES; i++){
if(l_EnemyObject[i].alive == true){
matrixData[l_EnemyObject[i].row] |= matrixMask[l_EnemyObject[i].col];
}
}
matrix.write((char)matrixData[0], (char)matrixData[1], (char)matrixData[2],
(char)matrixData[3], (char)matrixData[4], (char)matrixData[5],
(char)matrixData[6], (char)matrixData[7]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment