/*
Flappy Bit Version 1.0
By Daniel Ratcliffe
http://www.twitter.com/DanTwoHundred
*/
// CONFIGURATION
// Set these values to the pin numbers you're using on your Arduino.
// You may set any value to zero to ignore this input or output
int rowPins[] = { 5, 10, A3, 8, 2, A2, A0, A1 };
int columnPins[] = { 9, 4, 3, 6, A5, 7, 11, 12 };
int buttonPin = A4;
// STATE
boolean menu = true;
boolean gameOver = false;
int lastButtonState = 0;
int birdPos = 2;
int pipePos = 8;
int pipeGap = 3;
int ticks = 0;
int inGameScreen[] = {
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,
};
int menuScreen[] = {
0,0,0,1,1,0,0,0,
0,0,1,0,0,1,0,0,
0,0,1,0,1,1,0,0,
1,1,1,0,0,1,0,0,
1,0,0,1,1,1,1,0,
1,0,1,0,1,0,0,1,
0,1,0,0,1,1,1,0,
0,0,1,1,1,0,0,0,
};
// UTILITY
void drawInGameScreen( int colour )
{
if( birdPos >= 0 && birdPos < 8 )
{
if( !gameOver || (gameOver && ((ticks / 24) % 2) == 1) )
{
inGameScreen[ 1 + birdPos*8 ] = colour;
}
}
for( int y=0; y<8; ++y )
{
if( y < pipeGap || y >= pipeGap + 3 )
{
if( pipePos >= 0 && pipePos < 8 )
{
inGameScreen[ pipePos + y*8 ] = colour;
}
if( pipePos >= -1 && pipePos < 7 )
{
inGameScreen[ pipePos + 1 + y*8 ] = colour;
}
}
}
}
// MAIN
void setup()
{
// Set all pin modes
for( int n=0; n<8; ++n )
{
int column = columnPins[n];
if( column > 0 )
{
pinMode( column, OUTPUT );
digitalWrite( column, HIGH );
}
int row = rowPins[n];
if( row > 0 )
{
pinMode( row, OUTPUT );
digitalWrite( row, LOW );
}
}
if( buttonPin > 0 )
{
pinMode( buttonPin, INPUT );
}
}
void loop()
{
// Update
int buttonState = (buttonPin > 0 && digitalRead( buttonPin ) == HIGH) ? 1 : 0;
if( menu )
{
// IN MENU
++ticks;
if( (ticks % 12) == 0 )
{
// Animate the eye
if( rand()%30 == 0 )
{
menuScreen[ 19 ] = 1;
menuScreen[ 20 ] = 0;
}
else
{
menuScreen[ 19 ] = 0;
menuScreen[ 20 ] = 1;
}
}
if( buttonState != lastButtonState && buttonState )
{
// Start game
menu = false;
ticks = 0;
birdPos = 2;
pipePos = 20;
pipeGap = 3;
gameOver = false;
drawInGameScreen( 1 );
}
}
else
{
// IN GAME
// Clear the screen
drawInGameScreen( 0 );
// Update the state
++ticks;
if( !gameOver && ((ticks % 12) == 0) )
{
// Move the pipe
int lastPipePos = pipePos;
pipePos--;
if( pipePos < -1 )
{
pipePos = 7;
pipeGap = 1 + rand()%4;
}
// Move the bird
int lastBirdPos = birdPos;
if( buttonState )
{
birdPos = max( birdPos - 1, 0 );
}
else
{
birdPos = birdPos + 1;
if( birdPos >= 8 )
{
gameOver = true;
pipePos = lastPipePos;
birdPos = lastBirdPos;
ticks = 0;
}
}
// Test for pipe collision
if( (pipePos == 1 || pipePos == 0) && (birdPos < pipeGap || birdPos >= pipeGap + 3) )
{
gameOver = true;
pipePos = lastPipePos;
birdPos = lastBirdPos;
ticks = 0;
}
}
else if( gameOver && ticks >= 96 )
{
// Return to menu
menu = true;
}
// Redraw the screen
if( !menu )
{
drawInGameScreen( 1 );
}
}
lastButtonState = buttonState;
// Render
int* screen = menu ? menuScreen : inGameScreen;
for( int x=0; x<8; ++x )
{
for( int y=0; y<8; ++y )
{
int row = rowPins[y];
if( row > 0 )
{
if( screen[ (7-y)+x*8 ] > 0 ) // Uncomment to rotate screen orientation
//if( screen[ x+y*8 ] > 0 )
{
digitalWrite( row, HIGH );
}
else
{
digitalWrite( row, LOW );
}
}
}
int column = columnPins[x];
if( column > 0 )
{
digitalWrite( column, LOW );
}
delay( 2 );
if( column > 0 )
{
digitalWrite( column, HIGH );
}
}
}