#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int PIN_BTN_JUMP = 7;
const int PIN_BTN_RESET = 8;
const int PIN_BUZZER = 9;
byte dinoRun1[8] = { B00000, B00111, B00111, B10110, B11111, B01110, B01010, B00011 };
byte dinoRun2[8] = { B00000, B00111, B00111, B10110, B11111, B01110, B01010, B11000 };
byte dinoJump[8] = { B00111, B00111, B10110, B11111, B01110, B01010, B10001, B00000 };
byte cactus[8] = { B00100, B00101, B10101, B10111, B11100, B00100, B00100, B00100 };
enum GameState { MENU, PLAYING, GAME_OVER };
GameState currentState = MENU;
int dinoRow = 1;
int obstacleCol = 15;
bool isJumping = false;
unsigned long jumpStartTime = 0;
const unsigned long JUMP_DURATION = 550;
// Μεταβλητές για Edge Detection (ανίχνευση νέου πατήματος)
bool lastJumpState = HIGH;
bool lastResetState = HIGH;
unsigned long lastTickTime = 0;
int gameSpeed = 200;
unsigned long score = 0;
bool legToggle = false;
void playJumpSound() { tone(PIN_BUZZER, 600, 40); }
void playGameOverSound() {
tone(PIN_BUZZER, 300, 150);
delay(160);
tone(PIN_BUZZER, 150, 300);
}
void resetGame() {
score = 0;
gameSpeed = 200;
obstacleCol = 15;
dinoRow = 1;
isJumping = false;
currentState = PLAYING;
lcd.clear();
}
void setup() {
pinMode(PIN_BTN_JUMP, INPUT_PULLUP);
pinMode(PIN_BTN_RESET, INPUT_PULLUP);
pinMode(PIN_BUZZER, OUTPUT);
lcd.begin(16, 2);
lcd.createChar(0, dinoRun1);
lcd.createChar(1, dinoRun2);
lcd.createChar(2, dinoJump);
lcd.createChar(3, cactus);
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("CARDBOARD-BOY");
lcd.setCursor(1, 1);
lcd.print("Press to Start");
}
void loop() {
unsigned long currentMillis = millis();
int currentJumpReading = digitalRead(PIN_BTN_JUMP);
int currentResetReading = digitalRead(PIN_BTN_RESET);
// Edge detection: LOW τώρα και HIGH στο προηγούμενο κύκλο (μόλις πατήθηκε)
bool jumpJustPressed = (currentJumpReading == LOW && lastJumpState == HIGH);
bool resetJustPressed = (currentResetReading == LOW && lastResetState == HIGH);
lastJumpState = currentJumpReading;
lastResetState = currentResetReading;
if (currentState == MENU) {
if (jumpJustPressed || resetJustPressed) resetGame();
return;
}
if (currentState == GAME_OVER) {
if (resetJustPressed || jumpJustPressed) resetGame();
return;
}
// Άλμα μόνο τη στιγμή που πατιέται το κουμπί
if (jumpJustPressed && !isJumping) {
isJumping = true;
jumpStartTime = currentMillis;
dinoRow = 0;
playJumpSound();
}
// Επιστροφή στο έδαφος μόλις περάσει ο χρόνος
if (isJumping && (currentMillis - jumpStartTime >= JUMP_DURATION)) {
isJumping = false;
dinoRow = 1;
}
// Game Engine Frame Update
if (currentMillis - lastTickTime >= (unsigned long)gameSpeed) {
lastTickTime = currentMillis;
score++;
if (score % 25 == 0 && gameSpeed > 80) gameSpeed -= 10;
obstacleCol--;
if (obstacleCol < 0) obstacleCol = 15;
// Collision check
if (obstacleCol == 1 && dinoRow == 1) {
currentState = GAME_OVER;
playGameOverSound();
lcd.clear();
lcd.setCursor(3, 0);
lcd.print("GAME OVER!");
lcd.setCursor(2, 1);
lcd.print("Score: ");
lcd.print(score);
return;
}
// Render frame
lcd.clear();
lcd.setCursor(1, dinoRow);
if (isJumping) {
lcd.write(byte(2));
} else {
lcd.write(legToggle ? byte(0) : byte(1));
legToggle = !legToggle;
}
lcd.setCursor(obstacleCol, 1);
lcd.write(byte(3));
lcd.setCursor(10, 0);
lcd.print("S:");
lcd.print(score);
}
}