-
-
Save JeffersGlass/6ef4fc474e37bec29ebdf1d57035bded to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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