#include "LedControl.h"
#include "binary.h"
#include <LiquidCrystal.h>
#include "song.h"
// Configurable Variables
const int songSpeed = 200; // Speed in which the game runs; the lower the value, the faster it gets
const int scoreList[] = {100, 300, 100}; // {Early, Perfect, Late}
const int timing = 20; // Value in which songSpeed is divided by; aka how precise. Decrease in value means a lower interval.
const int difficulty = 2; // 3 easiest, 1 hardest
// Setting up pins for the LCD
const int rs = 23, en = 25, d4 = 33, d5 = 35, d6 = 37, d7 = 39;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Setting up pins for LED Matrix (8x8)
LedControl lc = LedControl(51, 52, 53, 1);
// Setup the variables for the game
const byte laneCheck[] = {B11000000, B00110000, B00001100, B00000011};
int songPos = 0;
int combo = 0;
unsigned long score = 0;
int maxTime = 0;
bool noteChecked[4];
int gracePeriod[4];
int accTracker[4];
void setup() {
lc.shutdown(0, false);
Serial.begin(9600);
lc.setIntensity(0, 100);
lcd.begin(16, 2);
for (int i = 0; i < 4; i++) pinMode(i+8, INPUT); // Button Inputs (Pin 8, 9, 10, 11)
}
void loop() {
// Display Welcome Screen
lcd.setCursor(0, 0);
lcd.print(" DDR: Mania");
lcd.setCursor(0, 1);
lcd.print("Press Any Button");
// Check for button press to start the game
for (int i = 0; i < 4; i++) {
if (digitalRead(i+9)) {
lcd.clear();
lcd.setCursor(0, 0);
printMessage(" GOOD LUCK!");
delay(1000);
lcd.clear();
animation();
startGame(); // Call function to start game
break;
}
}
}
// Function to play the game
void startGame() {
while (sizeof(song) > songPos) { // While loop continues untill song ends
if (digitalRead(7)) break; // Reset button breaks out of while loop
if (accuracyCheck(song) == 10) { // Check to see if accuracy < 10%
endScreen();
break;
}
if (millis() % songSpeed < 10) { // Updates every tick of the display
missedNote(); // Check to see if user missed a previous note
maxTime = millis() + songSpeed;
updateMatrix(); // Function to display LED Matrix
}
scoreCheck(); // Check to see if user pressed button at the right time
}
resetProgram(); // Outside while loop to reset
}
// Function to check score
void scoreCheck() {
for (int i = 0; i < 4; i++) {
byte songNote = song[songPos-1];
byte buttonState = digitalRead(i+8);
if (noteChecked[i]) continue;
if (songNote < 0) continue;
if (buttonState && songNote/laneCheck[i] % 2 == 1 && songNote) {
gracePeriod[i] = difficulty;
if (millis() > maxTime-((songSpeed/timing)+20)) {
addPoint("late", i);
} else if (millis() > ((maxTime-songSpeed) + (songSpeed/timing))+20) {
addPoint("perfect", i);
} else {
addPoint("early", i);
}
} else if (buttonState) addPoint("miss", i); // Miss due to pressing with no notes
}
updateLCD();
}
// Calls this function when user misses a note coming by
void missedNote() {
for (int i = 0; i < 4; i++) {
gracePeriod[i] -= 1;
bool hitNote = song[songPos-1]/laneCheck[i] % 2 == 1 && songPos-1 >= 0;
if (hitNote && !noteChecked[i]) addPoint("missLate", i);
noteChecked[i] = false;
}
}
// Function to check accuracy and returns an int of the accuracy
int accuracyCheck(byte songArray[]) {
int lateEarly_c = accTracker[0] + accTracker[2]; // Early + Late Count
int perfect_c = accTracker[1]; // Perfect Count
int miss = accTracker[3]; // Miss Count
int lateMult = scoreList[2]; // Late Multiplier
int earlyMult = scoreList[0]; // Early Multiplier
int perfect = scoreList[1]; // Perfect Multiplier
double N = sizeof(songArray);
double accuracy = (((perfect*(N+perfect_c))+(lateEarly_c * ((lateMult+earlyMult)/2)))/(perfect * (N + perfect_c + lateEarly_c + miss)));
if (accuracy <= 0.1) return 10;
return (accuracy * 100)-1;
}
// Adds points based on the type of point given
void addPoint(String pointType, int button) {
noteChecked[button] = true;
if (pointType == "perfect") {
score += scoreList[1];
accTracker[1] += 1;
combo++;
} else if (pointType == "early") {
score += scoreList[2];
accTracker[0] += 1;
combo++;
} else if (pointType == "late") {
score += scoreList[0];
accTracker[2] += 1;
combo++;
} else if (pointType == "miss" && gracePeriod[button] <= 0){
combo = 0;
accTracker[3] += 1;
updateLCD();
} else if (pointType == "missLate") {
combo = 0;
accTracker[3] += 1;
updateLCD();
}
}
// Function to update LED Matrix
void updateMatrix() {
lc.clearDisplay(0);
for (int i = 0; i < 8; i++) lc.setColumn(0, 7-i, song[songPos + i]);
songPos++;
}
// Function to update LCD
void updateLCD() {
lcd.setCursor(0, 0);
lcd.print(String(combo) + "x " + String(score) + " " + String(accuracyCheck(song)) + "% ");
lcd.setCursor(0,1);
lcd.print(String(accTracker[0]) + "|" + String(accTracker[1]) + "|" + String(accTracker[2]) + "|" + String(accTracker[3]));
}
// Function to reset program
void resetProgram() {
lcd.clear();
lcd.setCursor(0, 0);
printMessage(" RESETTING");
animation();
delay(1000);
lcd.clear();
songPos = 0;
score = 0;
combo = 0;
for (int i = 0; i < 4; i++) {
accTracker[i] = 0;
gracePeriod[i] = 0;
}
}
// The endscreen
void endScreen() {
lc.clearDisplay(0);
animation();
lcd.setCursor(0, 0);
printMessage(" You Failed");
lcd.setCursor(0, 1);
printMessage(" Keep Acc > 10%!");
delay(1000);
lcd.clear();
songPos = 0;
score = 0;
combo = 0;
for (int i = 0; i < 4; i++) accTracker[i] = 0;
}
void printMessage(String msg) {
for (int i = 0; i < msg.length(); i++) {
lcd.print(msg[i]);
delay(100);
}
}
void animation() {
for (int i = 0; i <= 7; i++) {
for (int j = 0; j <= 7; j++) {
delay(5);
lc.setLed(0, i, j, true);
}
}
delay(10);
for (int i = 0; i <= 7; i++) {
for (int j = 0; j <= 7; j++) {
delay(5);
lc.setLed(0, i, j, false);
}
}
}