#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#include "bitmaps.h"
// Pin definitions
#define UP_BUTTON_PIN 5
#define DOWN_BUTTON_PIN 2
#define LEFT_BUTTON_PIN 4
#define RIGHT_BUTTON_PIN 3
// Constantes du jeu
const int kScreenWidth = 128, kScreenHeight = 64, kGameWidth = 64, kGameHeight = 32, kMaxLength = 464, kStartLength = 6;
// SSD1306 display object
Adafruit_SSD1306 lcd(kScreenWidth, kScreenHeight, &Wire, -1);
// Class button functionality
class PushButton {
unsigned char last_state, is_down, pin;
public:
// Constructor to initialize pin and set mode to INPUT
PushButton(int pin) : last_state(0), is_down(0), pin(pin) {
pinMode(pin, INPUT);
}
// Update button state
void update() {
int state = digitalRead(pin);
if (state != last_state && state == HIGH) {
is_down = true;
}
last_state = state;
}
// Get the current state of the button
bool isPressed() {
bool down = is_down;
is_down = false;
return down;
}
};
// Initialize buttons with preprocessor pin definitions
PushButton up_button(UP_BUTTON_PIN), down_button(DOWN_BUTTON_PIN), left_button(LEFT_BUTTON_PIN), right_button(RIGHT_BUTTON_PIN);
// Structure to represent a position on the screen
struct Position {
signed char x, y;
bool operator==(const Position& other) const {
return x == other.x && y == other.y;
}
Position& operator+=(const Position& other) {
x += other.x;
y += other.y;
return *this;
}
};
// Function to draw a square at a given position
void drawSquare(Position pos, int color = WHITE) {
lcd.fillRect(pos.x * 2, pos.y * 2, 2, 2, color);
}
// Function to test if a position is occupied
bool isPositionOccupied(Position pos) {
return lcd.getPixel(pos.x * 2, pos.y * 2);
}
// Array to represent directions (up, right, down, left)
const Position kDirections[4] = {
{0, -1}, {1, 0}, {0, 1}, {-1, 0}
};
// Structure to represent the player (snake)
struct Player {
Position pos;
unsigned char tail[kMaxLength];
unsigned char direction;
int size, moved;
// Constructor to reset player state
Player() {
reset();
}
// Reset player to initial state
void reset() {
pos = {32, 16};
direction = 1; // Start moving right
size = kStartLength;
memset(tail, 0, sizeof(tail));
moved = 0;
}
// Turn the player left
void turnLeft() {
direction = (direction + 3) % 4;
}
// Turn the player right
void turnRight() {
direction = (direction + 1) % 4;
}
// Turn the player up
void turnUp() {
direction = 0;
}
// Turn the player down
void turnDown() {
direction = 2;
}
// Update the player's position and tail
void update() {
// Shift tail positions
for (int i = kMaxLength - 1; i > 0; --i) {
tail[i] = tail[i] << 2 | ((tail[i - 1] >> 6) & 3);
}
tail[0] = tail[0] << 2 | ((direction + 2) % 4);
// Move the player
pos += kDirections[direction];
if (moved < size) {
moved++;
}
}
// Render the player on the screen
void render() const {
drawSquare(pos);
if (moved < size) {
return;
}
Position tailpos = pos;
for (int i = 0; i < size; ++i) {
tailpos += kDirections[(tail[(i >> 2)] >> ((i & 3) * 2)) & 3];
}
drawSquare(tailpos, BLACK);
}
};
// Initialize player
Player player;
// Structure to represent an item (food) on the screen
struct Item {
Position pos;
// Spawn the item at a random position
void spawn() {
pos.x = random(1, 63);
pos.y = random(1, 31);
}
// Render the item on the screen
void render() const {
drawSquare(pos);
}
};
// Initialize item
Item item;
// Function to wait for user input
void waitForInput() {
do {
right_button.update();
left_button.update();
up_button.update();
down_button.update();
} while (!right_button.isPressed() && !left_button.isPressed() && !up_button.isPressed() && !down_button.isPressed());
}
// Function to display "Push to start" message
void displayPushToStart() {
lcd.setCursor(26, 57);
lcd.print(F("Commencer "));
}
// Function to flash the screen
void flashScreen() {
lcd.invertDisplay(true);
delay(100);
lcd.invertDisplay(false);
delay(200);
}
// Function to play the introduction screen
void playIntro() {
lcd.clearDisplay();
lcd.drawBitmap(18, 0, kSplashScreen, 92, 56, WHITE);
displayPushToStart();
lcd.display();
waitForInput();
flashScreen();
}
// Function to play the game over screen
void displayGameOver() {
flashScreen();
lcd.clearDisplay();
//lcd.drawBitmap(4, 0, kGameOver, 124, 38, WHITE);
int score = player.size - kStartLength;
lcd.setCursor(28, 2);
lcd.print(F("Bg ! Game over "));
lcd.setCursor(26, 34);
lcd.print(F("Score: "));
lcd.print(score);
int hiscore;
EEPROM.get(0, hiscore);
if (score > hiscore) {
EEPROM.put(0, score);
hiscore = score;
lcd.setCursor(4, 44);
// lcd.print(F("NEW"));
}
lcd.setCursor(26, 44);
// lcd.print(F("Hi-Score: "));
// lcd.print(hiscore);
displayPushToStart();
lcd.display();
waitForInput();
}
// Function to reset the game state
void resetGame() {
lcd.clearDisplay();
// Draw game borders
for (char x = 0; x < kGameWidth; ++x) {
drawSquare({x, 0});
drawSquare({x, 31});
}
for (char y = 0; y < kGameHeight; ++y) {
drawSquare({0, y});
drawSquare({63, y});
}
player.reset();
item.spawn();
}
// Function to update the game state
void updateGame() {
player.update();
if (player.pos == item.pos) {
player.size++;
item.spawn();
} else if (isPositionOccupied(player.pos)) {
displayGameOver();
resetGame();
}
}
// Function to handle user input
void handleInput() {
right_button.update();
if (right_button.isPressed()) {
player.turnRight();
}
left_button.update();
if (left_button.isPressed()) {
player.turnLeft();
}
up_button.update();
if (up_button.isPressed()) {
player.turnUp();
}
down_button.update();
if (down_button.isPressed()) {
player.turnDown();
}
}
// Function to render the game state on the screen
void renderGame() {
player.render();
item.render();
lcd.display();
}
// Setup function, runs once at startup
void setup() {
lcd.begin(SSD1306_SWITCHCAPVCC, 0x3C);
lcd.setTextColor(WHITE);
playIntro();
resetGame();
}
// Main game loop, runs repeatedly
void loop() {
handleInput();
updateGame();
renderGame();
delayMicroseconds(150);
}