Introduction: MATRIX MAX7219 SNAKE GAME ON ARDUINO 31-8-2019

About: Hi, My Name Is Hamza Mahmoud, I am a 14 year old Egyptian teenager who continues studying at school, I Started My Electronics Studying Since I Was 10 Years Old I Started with a little microcontroller called Ar…

INTRODUCTION

This Tutorial will help you make a game called the snake game the snake game is usually on old phones the snake game is a game of a snake that go and eat the dot (food) then the snake will get longer the way to lose is by touching the snake tail. So i thought i could make a game of it using Arduino UNO.

Supplies

1-Arduino UNO

2-Mini Breadboard

3- 8x8 dot matrix MAX1219

4-Joystick

5- Jumper Wires Male/Male and Male/Female

6-USB Cable

Step 1: -WIRING

MATRIX WIRES

VCC To 5V on arduino

GND To GND on arduino

OIN To I/O 12 on arduino

CS To I/O 11 on arduino

CLK To I/O 10 on arduino

JOYSTICK WIRES

GND To GND on arduino

+5V To 5V on arduino

VRX To A2 on arduino

VRY To A3 on arduino

--------------------------------

SW Will Not Use

Step 2: Code

#include "LedControl.h" // LedControl library is used for controlling a LED matrix. Find it using Library Manager or download zip here: https://github.com/wayoda/LedControl

// --------------------------------------------------------------- // // ------------------------- user config ------------------------- // // --------------------------------------------------------------- /

/ there are defined all the pins struct Pin { static const short joystickX = A2; // joystick X axis pin static const short joystickY = A3; // joystick Y axis pin static const short joystickVCC = 15; // virtual VCC for the joystick (Analog 1) (to make the joystick connectable right next to the arduino nano) static const short joystickGND = 14; // virtual GND for the joystick (Analog 0) (to make the joystick connectable right next to the arduino nano)

static const short potentiometer = A7; // potentiometer for snake speed control

static const short CLK = 10; // clock for LED matrix static const short CS = 11; // chip-select for LED matrix static const short DIN = 12; // data-in for LED matrix };

// LED matrix brightness: between 0(darkest) and 15(brightest) const short intensity = 8;

// lower = faster message scrolling const short messageSpeed = 5;

// initial snake length (1...63, recommended 3) const short initialSnakeLength = 3;

void setup() { Serial.begin(115200); // set the same baud rate on your Serial Monitor initialize(); // initialize pins & LED matrix calibrateJoystick(); // calibrate the joystick home (do not touch it) showSnakeMessage(); // scrolls the 'snake' message around the matrix }

void loop() { generateFood(); // if there is no food, generate one scanJoystick(); // watches joystick movements & blinks with food calculateSnake(); // calculates snake parameters handleGameStates();

// uncomment this if you want the current game board to be printed to the serial (slows down the game a bit) // dumpGameBoard(); }

// --------------------------------------------------------------- // // -------------------- supporting variables --------------------- // // --------------------------------------------------------------- //

LedControl matrix(Pin::DIN, Pin::CLK, Pin::CS, 1);

struct Point { int row = 0, col = 0; Point(int row = 0, int col = 0): row(row), col(col) {} };

struct Coordinate { int x = 0, y = 0; Coordinate(int x = 0, int y = 0): x(x), y(y) {} };

bool win = false; bool gameOver = false;

// primary snake head coordinates (snake head), it will be randomly generated Point snake;

// food is not anywhere yet Point food(-1, -1);

// construct with default values in case the user turns off the calibration Coordinate joystickHome(500, 500);

// snake parameters int snakeLength = initialSnakeLength; // choosed by the user in the config section int snakeSpeed = 1; // will be set according to potentiometer value, cannot be 0 int snakeDirection = 0; // if it is 0, the snake does not move

// direction constants const short up = 1; const short right = 2; const short down = 3; // 'down - 2' must be 'up' const short left = 4; // 'left - 2' must be 'right'

// threshold where movement of the joystick will be accepted const int joystickThreshold = 160;

// artificial logarithmity (steepness) of the potentiometer (-1 = linear, 1 = natural, bigger = steeper (recommended 0...1)) const float logarithmity = 0.4;

// snake body segments storage int gameboard[8][8] = {};

// --------------------------------------------------------------- // // -------------------------- functions -------------------------- // // --------------------------------------------------------------- /

/ if there is no food, generate one, also check for victory void generateFood() { if (food.row == -1 || food.col == -1) { // self-explanatory if (snakeLength >= 64) { win = true; return; // prevent the food generator from running, in this case it would run forever, because it will not be able to find a pixel without a snake }

// generate food until it is in the right position do { food.col = random(8); food.row = random(8); } while (gameboard[food.row][food.col] > 0); } }

// watches joystick movements & blinks with food void scanJoystick() { int previousDirection = snakeDirection; // save the last direction long timestamp = millis();

while (millis() < timestamp + snakeSpeed) { // calculate snake speed exponentially (10...1000ms) float raw = mapf(analogRead(Pin::potentiometer), 0, 1023, 0, 1); snakeSpeed = mapf(pow(raw, 3.5), 0, 1, 10, 1000); // change the speed exponentially if (snakeSpeed == 0) snakeSpeed = 1; // safety: speed can not be 0

// determine the direction of the snake analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold ? snakeDirection = up : 0; analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold ? snakeDirection = down : 0; analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold ? snakeDirection = left : 0; analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold ? snakeDirection = right : 0;

// ignore directional change by 180 degrees (no effect for non-moving snake) snakeDirection + 2 == previousDirection && previousDirection != 0 ? snakeDirection = previousDirection : 0; snakeDirection - 2 == previousDirection && previousDirection != 0 ? snakeDirection = previousDirection : 0;

// intelligently blink with the food matrix.setLed(0, food.row, food.col, millis() % 100 < 50 ? 1 : 0); } }

// calculate snake movement data void calculateSnake() { switch (snakeDirection) { case up: snake.row--; fixEdge(); matrix.setLed(0, snake.row, snake.col, 1); break;

case right: snake.col++; fixEdge(); matrix.setLed(0, snake.row, snake.col, 1); break;

case down: snake.row++; fixEdge(); matrix.setLed(0, snake.row, snake.col, 1); break;

case left: snake.col--; fixEdge(); matrix.setLed(0, snake.row, snake.col, 1); break;

default: // if the snake is not moving, exit return; }

// if there is a snake body segment, this will cause the end of the game (snake must be moving) if (gameboard[snake.row][snake.col] > 1 && snakeDirection != 0) { gameOver = true; return; }

// check if the food was eaten if (snake.row == food.row && snake.col == food.col) { food.row = -1; // reset food food.col = -1;

// increment snake length snakeLength++;

// increment all the snake body segments for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { if (gameboard[row][col] > 0 ) { gameboard[row][col]++; } } } }

// add new segment at the snake head location gameboard[snake.row][snake.col] = snakeLength + 1; // will be decremented in a moment

// decrement all the snake body segments, if segment is 0, turn the corresponding led off for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { // if there is a body segment, decrement it's value if (gameboard[row][col] > 0 ) { gameboard[row][col]--; }

// display the current pixel matrix.setLed(0, row, col, gameboard[row][col] == 0 ? 0 : 1); } } }

// causes the snake to appear on the other side of the screen if it gets out of the edge void fixEdge() { snake.col < 0 ? snake.col += 8 : 0; snake.col > 7 ? snake.col -= 8 : 0; snake.row < 0 ? snake.row += 8 : 0; snake.row > 7 ? snake.row -= 8 : 0; }

void handleGameStates() { if (gameOver || win) { unrollSnake();

showScoreMessage(snakeLength - initialSnakeLength);

if (gameOver) showGameOverMessage(); else if (win) showWinMessage();

// re-init the game win = false; gameOver = false; snake.row = random(8); snake.col = random(8); food.row = -1; food.col = -1; snakeLength = initialSnakeLength; snakeDirection = 0; memset(gameboard, 0, sizeof(gameboard[0][0]) * 8 * 8); matrix.clearDisplay(0); } }

void unrollSnake() { // switch off the food LED matrix.setLed(0, food.row, food.col, 0);

delay(800);

// flash the screen 5 times for (int i = 0; i < 5; i++) { // invert the screen for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { matrix.setLed(0, row, col, gameboard[row][col] == 0 ? 1 : 0); } }

delay(20);

// invert it back for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { matrix.setLed(0, row, col, gameboard[row][col] == 0 ? 0 : 1); } }

delay(50);

}

delay(600);

for (int i = 1; i <= snakeLength; i++) { for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { if (gameboard[row][col] == i) { matrix.setLed(0, row, col, 0); delay(100); } } } } }

// calibrate the joystick home for 10 times void calibrateJoystick() { Coordinate values;

for (int i = 0; i < 10; i++) { values.x += analogRead(Pin::joystickX); values.y += analogRead(Pin::joystickY); }

joystickHome.x = values.x / 10; joystickHome.y = values.y / 10; }

void initialize() { pinMode(Pin::joystickVCC, OUTPUT); digitalWrite(Pin::joystickVCC, HIGH);

pinMode(Pin::joystickGND, OUTPUT); digitalWrite(Pin::joystickGND, LOW);

matrix.shutdown(0, false); matrix.setIntensity(0, intensity); matrix.clearDisplay(0);

randomSeed(analogRead(A5)); snake.row = random(8); snake.col = random(8); }

void dumpGameBoard() { String buff = "\n\n\n"; for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { if (gameboard[row][col] < 10) buff += " "; if (gameboard[row][col] != 0) buff += gameboard[row][col]; else if (col == food.col && row == food.row) buff += "@"; else buff += "-"; buff += " "; } buff += "\n"; } Serial.println(buff); }

// --------------------------------------------------------------- // // -------------------------- messages --------------------------- // // --------------------------------------------------------------- //

const PROGMEM bool snakeMessage[8][56] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0} };

const PROGMEM bool gameOverMessage[8][90] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0} };

const PROGMEM bool scoreMessage[8][58] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };

const PROGMEM bool digits[][8][8] = { { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 1, 1, 0, 1, 1, 1, 0}, {0, 1, 1, 1, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 0, 0} }, { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 0, 0, 0}, {0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 0} }, { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 0}, {0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 1, 1, 0, 0, 0, 0}, {0, 1, 1, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 0} }, { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 0}, {0, 0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 0, 0} }, { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0}, {0, 0, 1, 0, 1, 1, 0, 0}, {0, 1, 0, 0, 1, 1, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 1, 1, 0, 0} }, { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 0}, {0, 1, 1, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 0, 0} }, { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 0, 0} }, { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 0, 0, 0} }, { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 0, 0} }, { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 0, 0} } };

// scrolls the 'snake' message around the matrix void showSnakeMessage() { [&] { for (int d = 0; d < sizeof(snakeMessage[0]) - 7; d++) { for (int col = 0; col < 8; col++) { delay(messageSpeed); for (int row = 0; row < 8; row++) { // this reads the byte from the PROGMEM and displays it on the screen matrix.setLed(0, row, col, pgm_read_byte(&(snakeMessage[row][col + d]))); } }

// if the joystick is moved, exit the message if (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold || analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold || analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold || analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) { return; // return the lambda function } } }();

matrix.clearDisplay(0);

// wait for joystick co come back while (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold || analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold || analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold || analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) {}

}

// scrolls the 'game over' message around the matrix void showGameOverMessage() { [&] { for (int d = 0; d < sizeof(gameOverMessage[0]) - 7; d++) { for (int col = 0; col < 8; col++) { delay(messageSpeed); for (int row = 0; row < 8; row++) { // this reads the byte from the PROGMEM and displays it on the screen matrix.setLed(0, row, col, pgm_read_byte(&(gameOverMessage[row][col + d]))); } }

// if the joystick is moved, exit the message if (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold || analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold || analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold || analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) { return; // return the lambda function } } }();

matrix.clearDisplay(0);

// wait for joystick co come back while (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold || analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold || analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold || analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) {}

}

// scrolls the 'win' message around the matrix void showWinMessage() { // not implemented yet // TODO: implement it }

// scrolls the 'score' message with numbers around the matrix void showScoreMessage(int score) { if (score < 0 || score > 99) return;

// specify score digits int second = score % 10; int first = (score / 10) % 10;

[&] { for (int d = 0; d < sizeof(scoreMessage[0]) + 2 * sizeof(digits[0][0]); d++) { for (int col = 0; col < 8; col++) { delay(messageSpeed); for (int row = 0; row < 8; row++) { if (d <= sizeof(scoreMessage[0]) - 8) { matrix.setLed(0, row, col, pgm_read_byte(&(scoreMessage[row][col + d]))); }

int c = col + d - sizeof(scoreMessage[0]) + 6; // move 6 px in front of the previous message

// if the score is < 10, shift out the first digit (zero) if (score < 10) c += 8;

if (c >= 0 && c < 8) { if (first > 0) matrix.setLed(0, row, col, pgm_read_byte(&(digits[first][row][c]))); // show only if score is >= 10 (see above) } else { c -= 8; if (c >= 0 && c < 8) { matrix.setLed(0, row, col, pgm_read_byte(&(digits[second][row][c]))); // show always } } } }

// if the joystick is moved, exit the message if (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold || analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold || analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold || analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) { return; // return the lambda function } } }();

matrix.clearDisplay(0);

//// wait for joystick co come back //while (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold // || analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold // || analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold // || analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) {}

}

// standard map function, but with floats float mapf(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; }

Step 3: VIDEO