#include <MD_MAX72xx.h>
#include <SPI.h>
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
// Pinout
const uint8_t BTN_LEFT = 3;
const uint8_t BTN_RIGHT = 5;
const uint8_t N_BLOCKS = 20;
const uint16_t FAST_FRAME_DELAY = 250;
const uint16_t SLOW_FRAME_DELAY = 1000;
// Types
typedef struct Point {
int8_t x;
int8_t y;
};
enum Direction {
UNDEFINED = -1,
UP,
UP_LEFT,
UP_RIGHT,
DOWN,
DOWN_LEFT,
DOWN_RIGHT
};
// Variables
Point blocks[N_BLOCKS];
Point bar;
Point ball;
enum Direction ball_direction;
uint16_t frame_delay = SLOW_FRAME_DELAY;
MD_MAX72XX matrix = MD_MAX72XX(HARDWARE_TYPE, /*CS*/ 10, 1); // SPI hardware interface
void setup() {
Serial.begin(115200);
pinMode(BTN_LEFT, INPUT_PULLUP);
pinMode(BTN_RIGHT, INPUT_PULLUP);
matrix.begin();
matrix.control(MD_MAX72XX::INTENSITY, MAX_INTENSITY/2);
matrix.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
setStartState(); // Init snake world
}
void loop() {
static unsigned long int last_ts = millis();
matrix.clear(); // Clear screen
drawFrame();
// Non-blockant delay
while (last_ts + frame_delay > millis()) {
if (checkButtonsInput()) {
DEBUG_print("Different button pressed");
delay(100);
drawBar();
}
}
last_ts = millis();
DEBUG_print((frame_delay == FAST_FRAME_DELAY) ? "Fast frame..." : "Normal frame...");
// Try to generate next position
if (!nextFrame()) {
drawLoser();
while (!buttonPressed()); // Wait
setStartState();
}
}
void setStartState() {
matrix.clear();
uint8_t blocks_count = 0;
uint8_t row = 0;
while (blocks_count < N_BLOCKS) {
for (uint8_t column = 0; column < 8; column++) {
blocks[blocks_count].x = column;
blocks[blocks_count].y = row;
blocks_count++;
if (blocks_count == N_BLOCKS) {
break;
}
}
row++;
}
bar.x = 4;
bar.y = 7;
ball.x = 4;
ball.y = 4;
ball_direction = DOWN;
}
/* Tries to generate */
bool nextFrame() {
auto old_direction = ball_direction;
Point next_position = ball;
if (old_direction == DOWN) {
next_position.y++;
} else if (old_direction == UP) {
next_position.y--;
} else if (old_direction == UP_RIGHT) {
next_position.x++;
next_position.y--;
} else if (old_direction == UP_LEFT) {
next_position.x--;
next_position.y--;
} else if (old_direction == DOWN_RIGHT) {
next_position.x++;
next_position.y++;
} else if (old_direction == DOWN_LEFT) {
next_position.x--;
next_position.y++;
}
// Compute bounce if exists
ball_direction = getBounceDirection(next_position);
if (old_direction == ball_direction) {
ball = next_position;
} else {
nextFrame(); // Recompute bounce
}
// Break briks if is necesary
breakBricks(next_position);
return true;
}
enum Direction getBounceDirection(Point point) {
auto no_changed_direction = ball_direction;
// Bar collision
if (point.y != bar.y) {
return no_changed_direction;
}
if (point.x == bar.x - 1) {
return UP_LEFT;
} else if (point.x == bar.x) {
return UP;
} else if (point.x == bar.x + 1) {
return UP_RIGHT;
} else {
return no_changed_direction;
}
}
bool isPlatform(Point point) {
return ((point.y == bar.y) && (point.x == bar.x - 1 || point.x == bar.x || point.x == bar.x + 1));
}
bool isBrick(Point point) {
return false;
}
enum Direction newDirection() {
return DOWN;
}
void drawBar() {
for (uint8_t i = 0; i < 8; i++) {
matrix.setPoint(i, 7, false);
}
matrix.setPoint(bar.x, bar.y, true);
matrix.setPoint(bar.x - 1, bar.y, true);
matrix.setPoint(bar.x + 1, bar.y, true);
}
void drawFrame() {
for (uint8_t i = 0; i < N_BLOCKS; i++) {
matrix.setPoint(blocks[i].x, blocks[i].y, true);
}
matrix.setPoint(ball.x, ball.y, true);
drawBar();
}
/* Draws loser screen */
void drawLoser() {
matrix.clear();
}
/* Draws winner screen */
void drawWinner() {
matrix.clear();
delay(500);
bool invert = false;
for (uint8_t times = 0; times < 5; times++) {
for (uint8_t x = 0; x < 8; x++) {
for (uint8_t y = 0; y < 8; y++) {
if ((x + y) % 2 == 0) { // Checkboard pattern
matrix.setPoint(x, y, invert);
} else {
matrix.setPoint(x, y, !invert);
}
}
}
delay(500);
invert = !invert;
}
}
/* Function to change the snake direction.
* (Call it with the higher frequency possible)
*/
bool checkButtonsInput() {
if (!digitalRead(BTN_LEFT) && bar.x > 1) {
bar.x--;
return true;
} else if (!digitalRead(BTN_RIGHT) && bar.x < 6) {
bar.x++;
return true;
}
return false;
}
bool buttonPressed() {
return (!digitalRead(BTN_LEFT) || !digitalRead(BTN_RIGHT));
}
void DEBUG_print(const char* str) {
Serial.print("["); Serial.print((float)millis()/1000.0); Serial.print("] ");
Serial.println(str);
}