Skip to content

Instantly share code, notes, and snippets.

@JeffersGlass
Created April 26, 2020 20:27
Show Gist options
  • Save JeffersGlass/6ef4fc474e37bec29ebdf1d57035bded to your computer and use it in GitHub Desktop.
Save JeffersGlass/6ef4fc474e37bec29ebdf1d57035bded to your computer and use it in GitHub Desktop.
od.//The three pins connected to their respective ports
//on the shift registers
int CLK_PIN = 10;
int DATA_PIN = 11;
int LATCH_PIN = 12;
//Button order is up, right, down, left
int BUTTON_UP_PIN = 9;
int BUTTON_RIGHT_PIN = 8;
int BUTTON_DOWN_PIN = 7;
int BUTTON_LEFT_PIN = 6;
int numButtons = 4;
int buttons[] = {BUTTON_UP_PIN, BUTTON_RIGHT_PIN, BUTTON_DOWN_PIN, BUTTON_LEFT_PIN};
///////////////////////Game Data/////////
long gameUpdatePeriod = 400000; //Microseconds between updating lines of display
long lastGameUpdateTime = 0; //Stores the last time we updated the display
int snakeLength = 4; //Starting length of the snake
int snakeHeadPosition[] = {0,0}; //Stores where the snake's "head" is, (x,y)
int foodPosition[] = {0,0}; //Stores where the "food" is, (x,y)
long foodUpdatePeriod = 100000; //Microseconds between flashing the food on and off
long lastFoodUpdateTime = 0; //Stores the last time we updated the flashing food
int foodState = 0; //Should the food dot be on or off? 0 = off, 1 = on
//dir in the index of the direction that the snake is currently travelling
//0 = UP, 1 = RIGHT, 2 = DOWN, 3 = LEFT
//By store this as a numerical value with the directions offset from their
//opposite by 2, we can do some clever numerical tricks later
int dir = 0;
//For each direction that we're travelling, how much should the x and y
//values change for each game step?
int dirCoords[4][2] = {{0,-1}, {1,0}, {0,1}, {-1,0}};
//The current change in the snake's head's X and Y coordinates with
//each game step
int snakeDirection[2] = {dirCoords[0][0], dirCoords[0][1]};
///////////////////////Display Data//////////
//If a given row is to be "turned on" (in our case, pulled to ground),
//which bit in the shift registers does that correspond to?
int ROW0 = 9;
int ROW1 = 14;
int ROW2 = 8;
int ROW3 = 12;
int ROW4 = 1;
int ROW5 = 7;
int ROW6 = 2;
int ROW7 = 5;
const int NUMROWS = 8;
int rows[] = {ROW0, ROW1, ROW2, ROW3, ROW4, ROW5, ROW6, ROW7};
//If a given column is to be "turned on" (in our case, pulled high
//through a resistor), hich bit in the shift registers
//does that correspond to?
int COL0 = 13;
int COL1 = 3;
int COL2 = 4;
int COL3 = 10;
int COL4 = 6;
int COL5 = 11;
int COL6 = 15;
int COL7 = 16;
const int NUMCOLS = 8;
int cols[] = {COL0, COL1, COL2, COL3, COL4, COL5, COL6, COL7};
//If all of the rows and columns were off, what would the bits in the shift
//registers look like? We could build this data from the row/column data
//above; I have built it here manually
int allOff = (B11001011 * 256) + B10010100;
//The 2D array that will hold our board data. Starts as all 0's.
int board[NUMROWS][NUMCOLS] = { {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}};
//A 1D Array for example:
//int numbers[] = {0, 1, 2, 3, 4, 5, 6 ,7, 8, 9};
long displayUpdatePeriod = 500; //Microseconds between updating lines of display
long lastDisplayUpdateTime = 0; //Last time (from micros()) that we updated the display
//We'll step through the display multiplexing row-by-row - this is the row we're currently on
int currentRow = 0;
void setup() {
//Configure out shift-register outputs
pinMode(CLK_PIN, OUTPUT);
pinMode(DATA_PIN, OUTPUT);
pinMode(LATCH_PIN, OUTPUT);
//Configure our button inputs
for (int i = 0; i < numButtons; i++){
pinMode(buttons[i], INPUT_PULLUP);
}
//Make sure that random really is random
randomSeed(analogRead(A0));
initializeGameplay();
}
void loop() {
//Check input - have any buttons been pressed? Update variables to account for what buttons have been pressed.
//check gamestate - has enough time passed for us to do a "tick" of gamestate? If so update the appropriate variables
// and the board display
//check displayUpdate - has enough time passed to need to update the display? If so, take the appropriate actions
// to update the state of the board
checkInput();
checkGamestate();
displayUpdate();
}
void checkInput(){
//length should match
bool buttonState[] = {false, false, false, false};
for (int i = 0; i < 4; i++){ //read direction buttons
if (digitalRead(buttons[i]) == LOW) buttonState[i] = true; //is any individual button being pressed?
}
if (dir % 2 == 0){ //travelling up or down
if (buttonState[1]) setNewDirection(1);
else if (buttonState[3]) setNewDirection(3);
}
else{
if (buttonState[0]) setNewDirection(0);
else if (buttonState[2]) setNewDirection(2);
}
}
void setNewDirection(int newDir){
dir = newDir;
snakeDirection[0] = dirCoords[newDir][0];
snakeDirection[1] = dirCoords[newDir][1];
}
void checkGamestate(){
if (micros() > lastGameUpdateTime + gameUpdatePeriod){
//Move the snake's using the snakeDirection vector
snakeHeadPosition[0] += snakeDirection[0];
snakeHeadPosition[1] += snakeDirection[1];
//Subtract 1 from every board position. This mean's the snake's "tail" will go from 1 to zero
//and will dissapear.
subtractBoard();
//Set the value of underneath the snake's head to the current length of the snake
board[snakeHeadPosition[0]][snakeHeadPosition[1]] = snakeLength;
lastGameUpdateTime = micros();
}
//Do we need to update the food-flicker effect?
if (micros() > lastFoodUpdateTime + foodUpdatePeriod){
foodState = 1-foodState; //0 becomes 1, 1 becomes 0
board[foodPosition[0]][foodPosition[1]] = foodState;
lastFoodUpdateTime = micros();
}
}
void displayUpdate(){
//Check whether we need to update the display
if (micros() > lastDisplayUpdateTime + displayUpdatePeriod){
currentRow = (currentRow + 1) % NUMROWS; //Advance to the next row
displayRow(currentRow); //Display the current row
lastDisplayUpdateTime = micros();
}
}
void initializeGameplay(){
resetBoard();
snakeHeadPosition[0] = 2;
snakeHeadPosition[1] = 6;
board[2][6] = 4;
board[3][6] = 3;
board[4][6] = 2;
board[5][6] = 1;
dir = 3;
snakeDirection[0] = dirCoords[dir][0];
snakeDirection[1] = dirCoords[dir][1];
makeFood();
}
//Picks a new empty location on the board for the food to be
void makeFood(){
int x;
int y;
do{
x = random(0, NUMROWS);
y = random(0, NUMCOLS);
} while (board[x][y] != 0);
foodPosition[0] = x;
foodPosition[1] = y;
}
//Subtract 1 from every non-zero, non-food position on the board
//Used to move the snake
void subtractBoard(){
for (int i = 0; i < NUMROWS; i++){
for (int j = 0; j < NUMCOLS; j++){
if (board[i][j] > 0) board[i][j] -= 1; // && !(i == foodPosition[0] && j == foodPosition[1])) board[i][j] -= 1;
}
}
}
//Makes the board entirely empty
void resetBoard(){
for (int i = 0; i < NUMROWS; i++){
for (int j = 0; j < NUMCOLS; j++){
board[i][j] = 0;
}
}
}
void displayRow(int rowNum){
//Start with everything turned off
int value = allOff;
//Turn on the current row (in our case, by sending it low/0, so we need to clear a bit)
bitClear(value, 15-(rows[rowNum] - 1));
//For each dot in this row, if the associated row-value is non-zero,
//Turn the associated column-bit on(in our case, by sending it high/1, so we used bitSet)
for (int i = 0; i < NUMCOLS; i++){
if (board[rowNum][i] > 0){
bitSet(value, 15-(cols[i] - 1));
}
}
//output the generated data to the shiftRegisters
sendIntToDisplay(value);
}
void sendIntToDisplay(int input){
//Make sure clock and latch are low before we start, since they
//Trigger on a rising-edge
digitalWrite(CLK_PIN, LOW);
digitalWrite(LATCH_PIN, LOW);
//Clock out the data
shiftOut(DATA_PIN, CLK_PIN, LSBFIRST, lowByte(input));
shiftOut(DATA_PIN, CLK_PIN, LSBFIRST, highByte(input));
//Latch the data in
digitalWrite(LATCH_PIN, HIGH);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment