Created
April 29, 2018 08:42
-
-
Save tuenhidiy/ae62a1cd12b92c05d50970ea2fcb8bb1 to your computer and use it in GitHub Desktop.
Arduigames use Arduino Uno, LoLShield, MPU6050 & Microphone Module to play some simple games & animations
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
#include "Charliplexing.h" //LOL library | |
#include "Myfont.h" | |
#include "Figure.h" | |
#include "Font.h" | |
#include "Arduino.h" | |
#include "fix_fft.h" | |
#include "LOLDraw.h" | |
#include "LoLShield_Tetris.h" | |
#include <Wire.h>//gyro | |
char * test = " WELCOME TO ARDUIGAMES! "; | |
static const char autotest[]="HELLO WORLD! "; | |
int len; // length of this string | |
/** The current level. */ | |
int level; | |
/** The score of the user (number of points = speed of each killed ennemy - number of ennemies missed) */ | |
int score; | |
/** Number of lines cleared at current level. */ | |
byte linesCleared; | |
/** The game grid size. */ | |
const uint8_t GRID_HEIGHT = 14; | |
const uint8_t GRID_WIDTH = 9; | |
boolean playGrid[GRID_HEIGHT][GRID_WIDTH]; | |
const piece_t pieces[7] = { | |
{{ | |
// The single view of the square piece : | |
// 00 | |
// 00 | |
{{{1,1}, {2,1}, {1,2}, {2,2}}}, | |
{{{1,1}, {2,1}, {1,2}, {2,2}}}, | |
{{{1,1}, {2,1}, {1,2}, {2,2}}}, | |
{{{1,1}, {2,1}, {1,2}, {2,2}}}, | |
}}, | |
{{ | |
// The two views of the bar piece : | |
// 0000 | |
{{{0,1}, {1,1}, {2,1}, {3,1}}}, | |
{{{2,0}, {2,1}, {2,2}, {2,3}}}, | |
{{{0,1}, {1,1}, {2,1}, {3,1}}}, | |
{{{2,0}, {2,1}, {2,2}, {2,3}}}, | |
}}, | |
{{ | |
// The two views of the first S : | |
// 00 | |
// 00 | |
{{{0,1}, {1,1}, {1,2}, {2,2}}}, | |
{{{1,1}, {1,2}, {2,0}, {2,1}}}, | |
{{{0,1}, {1,1}, {1,2}, {2,2}}}, | |
{{{1,1}, {1,2}, {2,0}, {2,1}}}, | |
}}, | |
{{ | |
// The two views of the second S : | |
// 00 | |
// 00 | |
{{{0,2}, {1,1}, {1,2}, {2,1}}}, | |
{{{0,0}, {0,1}, {1,1}, {1,2}}}, | |
{{{0,2}, {1,1}, {1,2}, {2,1}}}, | |
{{{0,0}, {0,1}, {1,1}, {1,2}}}, | |
}}, | |
{{ | |
// The four views of the first L : | |
// 000 | |
// 0 | |
{{{0,1}, {0,2}, {1,1}, {2,1}}}, | |
{{{0,0}, {1,0}, {1,1}, {1,2}}}, | |
{{{0,1}, {1,1}, {2,0}, {2,1}}}, | |
{{{1,0}, {1,1}, {1,2}, {2,2}}}, | |
}}, | |
{{ | |
// The four views of the second L : | |
// 000 | |
// 0 | |
{{{0,1}, {1,1}, {2,1}, {2,2}}}, | |
{{{1,0}, {1,1}, {1,2}, {2,0}}}, | |
{{{0,0}, {0,1}, {1,1}, {2,1}}}, | |
{{{0,2}, {1,0}, {1,1}, {1,2}}}, | |
}}, | |
{{ | |
// The four views of the T : | |
// 000 | |
// 0 | |
{{{0,1}, {1,1}, {1,2}, {2,1}}}, | |
{{{1,0}, {1,1}, {1,2}, {2,1}}}, | |
{{{0,1}, {1,0}, {1,1}, {2,1}}}, | |
{{{0,1}, {1,0}, {1,1}, {1,2}}}, | |
}}, | |
}; | |
const int levelMultiplier[] = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5}; | |
const int linesMultiplier[] = {100, 400, 900, 2000}; | |
/** The piece being played. */ | |
const piece_t* currentPiece; | |
/** The current position and view of the piece being played. */ | |
pos_t position; | |
/* ----------------------------------------------------------------- */ | |
/* ----------------------------------------------------------------- */ | |
/* SPACE INVADER CODE !!! */ | |
/* ----------------------------------------------------------------- */ | |
/* ----------------------------------------------------------------- */ | |
// Number of fire we can use before having to wait | |
#define MAXFIRE 3 | |
// Number of lives you have : | |
#define STARTLIVES 4 | |
// Maximum number of ennemies at one : | |
#define MAXENNEMIES 8 | |
// Speed of ennemies arrival : (between 0 & 20, 20 = rare, 0 = often ) | |
#define ENNEMIESRATE 6 | |
/** The score of the user (number of points = speed of each killed ennemy - number of ennemies missed) */ | |
//int score=0; | |
/** Position of the ship between 1 & 7 */ | |
byte shippos=4; | |
/** Number of lives of the user */ | |
byte lives; | |
/** Position of the bullets of the ship, [0]=x [1]=y */ | |
byte firepos[9][2]={ | |
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0} | |
}; | |
/** Position and speed of the ennemies [0]=x [1]=y [2]=speed [3]=speed counter */ | |
byte ennemypos[8][4]={ | |
{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0} | |
}; | |
// where we read the audio voltage | |
#define AUDIOPIN 0 | |
char im[128], data[128]; | |
char data_avgs[14]; | |
int i=0,val; | |
int leng1=0; //provides the length of the char array | |
int leng2=0; //provides the length of the char array | |
unsigned char test1[]=" LEFT - INVADER!... \0"; //text has to end with '\0' !!!!!! | |
unsigned char test2[]=" RIGHT - TETRIS... \0"; //text has to end with '\0' !!!!!! | |
const uint8_t MPU6050SlaveAddress = 0x68; | |
//const uint8_t scl = A4; | |
//const uint8_t sda = A5; | |
// MPU6050 few configuration register addresses | |
const uint8_t MPU6050_REGISTER_SMPLRT_DIV = 0x19; | |
const uint8_t MPU6050_REGISTER_USER_CTRL = 0x6A; | |
const uint8_t MPU6050_REGISTER_PWR_MGMT_1 = 0x6B; | |
const uint8_t MPU6050_REGISTER_PWR_MGMT_2 = 0x6C; | |
const uint8_t MPU6050_REGISTER_CONFIG = 0x1A; | |
const uint8_t MPU6050_REGISTER_GYRO_CONFIG = 0x1B; | |
const uint8_t MPU6050_REGISTER_ACCEL_CONFIG = 0x1C; | |
const uint8_t MPU6050_REGISTER_FIFO_EN = 0x23; | |
const uint8_t MPU6050_REGISTER_INT_ENABLE = 0x38; | |
const uint8_t MPU6050_REGISTER_ACCEL_XOUT_H = 0x3B; | |
const uint8_t MPU6050_REGISTER_SIGNAL_PATH_RESET = 0x68; | |
int16_t AccelX, AccelY, AccelZ, Temperature, GyroX, GyroY, GyroZ; | |
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ; | |
int buttonPushCounter = 0; | |
int buttonState = 0; | |
int lastButtonState = 0; | |
bool running = true; | |
byte line = 0; //Row counter | |
char buffer[10]; | |
int value; | |
int x = 6; | |
int y = 4; | |
int OLDx = 1; | |
int OLDy = 1; | |
//int cxMax = 13; | |
//int cyMax = 8 ; | |
int cx, cy; | |
/* ---------------------------------------------------------------------------*/ | |
/** The figures from 0 to 9 encoded in 7 lines of 5 bits : | |
*/ | |
PROGMEM const byte figures[][7] = { | |
{14,17,17,17,17,17,14}, | |
{4,6,4,4,4,4,14}, | |
{14,17,16,14,1,1,31}, | |
{14,17,16,14,16,17,14}, | |
{8,12,10,9,31,8,8}, | |
{31,1,1,15,16,16,15}, | |
{14,17,1,15,17,17,14}, | |
{31,16,8,8,4,4,4}, | |
{14,17,17,14,17,17,14}, | |
{14,17,17,30,16,16,15}, | |
}; | |
int8_t dx,dy; | |
int8_t sh1y,sh2y,s1,s2; | |
/* ---------------------------------------------------------------------------*/ | |
void setup() { | |
//Serial.begin(9600); | |
Wire.begin(); | |
MPU6050_Init(); | |
LedSign::Init(); | |
for(int i=0; ; i++){ //get the length of the text | |
if(test1[i]==0){ | |
leng1=i; | |
break; | |
} | |
} | |
for(int j=0; ; j++){ //get the length of the text | |
if(test2[j]==0){ | |
leng2=j; | |
break; | |
} | |
} | |
//Myfont::Banner(leng1,test1); | |
len = strlen (test); | |
} | |
void loop(){ | |
Read_RawValue(MPU6050SlaveAddress, MPU6050_REGISTER_ACCEL_XOUT_H); | |
cy = AccelY; | |
cx = AccelX; | |
OLDx = x; | |
OLDy = y; | |
//Serial.print("Y: "); Serial.print(AccelY); | |
//Serial.print("X: "); Serial.print(AccelX); | |
buttonState = digitalRead(A1); | |
if (buttonState != lastButtonState) { | |
if (buttonState == HIGH) { | |
buttonPushCounter++; | |
} | |
else { | |
} | |
} | |
lastButtonState = buttonState; | |
if (digitalRead(A3)) | |
{ | |
static uint32_t counter = 0; | |
playerMovePiece(); | |
timerPieceDown(counter); | |
delay(50); | |
} | |
else if (digitalRead(A2)) | |
{ | |
moveShip(); | |
fireShip(); | |
moveFires(); | |
moveEnnemies(); | |
addEnnemies(); | |
delay(120); | |
} | |
else | |
{ | |
switch (buttonPushCounter % 17) { | |
case 0: // Scrolling text with scrolling speed base on Accelerometer of MPU6050 | |
Accel_scrolling(); | |
break; | |
case 1: // Load the Error Board | |
LoadTheErrorBoard(); | |
break; | |
case 2: // Play the Error Board Game | |
PlayGame(); | |
break; | |
case 3: // Load the Hell Board Game | |
LoadTheHellBoard(); | |
break; | |
case 4: // Play the Hell Board Game | |
PlayGame(); | |
break; | |
case 5: // Load the Picture Board Game | |
LoadThePicBoard(); | |
break; | |
case 6: // Play the Picture Board Game | |
PlayGame(); | |
break; | |
case 7: // Load Pong game | |
x = 3; | |
y = 7; | |
sh1y=3; | |
sh2y=3; | |
dx = 1; | |
dy = 1; | |
s1 = 0; | |
s2 = 0; | |
randomSeed(analogRead(2)); | |
//drawscores(); | |
break; | |
case 8: // Play Pong game | |
PlayPong(); | |
break; | |
case 9: // Spectrum Analyzer Mode | |
FFT(); | |
break; | |
case 10: // Measure Voice Value | |
LedSign::Clear(0); | |
BlowMeter(); | |
break; | |
case 11: // Accelermeter Mode | |
LedSign::Clear(0); | |
Accelerometer(); | |
break; | |
case 12: // Load Invader Game - Toggle switch to A3 | |
LedSign::Clear(0); | |
randomSeed(analogRead(2)); | |
initInvaderGame(); | |
//drawShip(); | |
//Myfont::Banner(leng1,test1); | |
break; | |
case 13: // Play Tetris Game - Toggle switch to A2 | |
LedSign::Clear(0); | |
randomSeed(analogRead(2)); | |
startTetrisGame(); | |
nextPiece(); | |
//Myfont::Banner(leng2,test2); | |
break; | |
case 14: // Hammer Animation | |
Hammer(); | |
break; | |
case 15: // Heart Animation | |
Heart(); | |
break; | |
case 16: // Tsumani Animation | |
Tsunami(); | |
break; | |
} | |
} | |
} | |
void Accel_scrolling(){ | |
LedSign::Clear(); | |
int i = map(AccelX/16, 0, 1023, 6, len * 6); // starting pixel | |
for (int j = 0; j < 14; j += 6) | |
Myfont::Draw(j, test[(i + j) / 6]); | |
delay(100); // hold that image for a moment | |
} | |
void Autoscrolling() | |
{ | |
for (int8_t x=DISPLAY_COLS, i=0; ; x--) { | |
LedSign::Clear(); | |
for (int8_t x2=x, i2=i; x2<DISPLAY_COLS;) { | |
int8_t w = Font::Draw (autotest[i2], x2, 0); | |
x2 += w, i2 = (i2+1) % strlen(autotest); | |
if (x2 <= 0) // off the display completely? | |
x = x2, i = i2; | |
} | |
delay(50); | |
} | |
} | |
void FFT(){ | |
for (i=0; i < 128; i++){ | |
val = analogRead(AUDIOPIN); // read voltage | |
data[i] = val; | |
im[i] = 0; | |
}; | |
fix_fft(data,im,7,0); | |
for (i=0; i< 64;i++){ | |
data[i] = sqrt(data[i] * data[i] + im[i] * im[i]); // this gets the absolute value of the values in the array, so we're only dealing with positive numbers | |
}; | |
// average bars together | |
for (i=0; i<14; i++) { | |
data_avgs[i] = data[i*4] + data[i*4 + 1] + data[i*4 + 2] + data[i*4 + 3]; // average together | |
data_avgs[i] = map(data_avgs[i], 0, 15, 0, 9); // remap values for LoL | |
} | |
// set LoLShield | |
for (int x=0; x < 14; x++) { | |
for (int y=0; y < 9; y++) { | |
if (y < data_avgs[13-x]) { // 13-x reverses the bars so low to high frequences are represented from left to right. | |
LedSign::Set(x,y,1); // set the LED on | |
} | |
else { | |
LedSign::Set(x,y,0); // set the LED off | |
} | |
} | |
} | |
} | |
void PlayPong(){ | |
int8_t randommove; | |
// The Ball shall bounce on the walls : | |
if (x==12 || x==1) { | |
dx=-dx; | |
// Collision detection | |
if (x==1) { | |
// check the first ship (left side) | |
if (sh1y!=y && sh1y+1!=y) { | |
s2++; | |
drawscores(); | |
checkscores(); | |
} | |
} else { | |
// check the second ship (right side) | |
if (sh2y!=y && sh2y+1!=y) { | |
s1++; | |
drawscores(); | |
checkscores(); | |
} | |
} | |
} | |
if (y==8 || y==0) dy=-dy; | |
// Clear the non-active screen | |
LedSign::Clear(); | |
// Move the BALL : | |
x=x+dx; | |
y=y+dy; | |
// Draw the ball : | |
LedSign::Set(x,y,1); | |
// Draw the Ship | |
LedSign::Set(0, sh1y, 1); | |
LedSign::Set(0, sh1y+1, 1); | |
LedSign::Set(13, sh2y, 1); | |
LedSign::Set(13, sh2y+1, 1); | |
// The ships moves when the ball go in their direction. They follow it magically ;) : | |
if (dx>0) { | |
// the ball goes away from me, let's move randomly | |
randommove=random(0,3); | |
if (randommove==0) { | |
sh1y--; | |
} | |
if (randommove==1) { | |
sh1y++; | |
} | |
} else { | |
if (sh1y>y && (random(0,12)<10 || x<3)) { | |
sh1y--; | |
} | |
if (sh1y<y && (random(0,12)<10 || x<3)) { | |
sh1y++; | |
} | |
if (random(0,8)==0) { | |
if (sh1y>y) { | |
sh1y++; | |
} | |
if (sh1y<y) { | |
sh1y--; | |
} | |
} | |
} | |
// Human Player | |
// 1/4 of the variator is used. If we use it fully, it's too hard to play. | |
// To use it fully replace 36 by 146 | |
sh2y=AccelY/1152; | |
// Sanity checks for the ships : | |
if (sh1y>7) sh1y=7; | |
if (sh2y>7) sh2y=7; | |
if (sh1y<0) sh1y=0; | |
if (sh2y<0) sh2y=0; | |
// swap the screens ;) (sometime we may need this double-buffer algorithm... | |
// of course, as of today it's a little bit overkill ...) | |
LedSign::Flip(); | |
// Display the bitmap some times | |
delay(100); | |
// loop :) | |
} | |
void PlayGame(){ | |
if (cx < 13){ cx = 13; x = x+1;} | |
else if (cx > 13) {cx = 13; x = x-1;} | |
if (cy > 8) {cy = 8; y = y+1;} | |
else if (cy < 8) {cy = 8; y = y-1;} | |
if (y > 8) y = 8; | |
if (x > 13) x = 13; | |
if (y < 0) y = 0; | |
if (x < 0) x = 0; | |
LedSign::Set(x,y,1); | |
LedSign::Set(OLDx,OLDy,0); | |
delay(50); | |
} | |
void Accelerometer(){ | |
if (cx < 13){ cx = 13; x = x+1;} | |
else if (cx > 13) {cx = 13; x = x-1;} | |
if (cy > 8) {cy = 8; y = y+1;} | |
else if (cy < 8) {cy = 8; y = y-1;} | |
if (y > 8) y = 8; | |
if (x > 13) x = 13; | |
if (y < 0) y = 0; | |
if (x < 0) x = 0; | |
LOLDraw::Circle(x, y, 1); | |
LOLDraw::Circle(x, y, 2); | |
delay(50); | |
} | |
void BlowMeter(){ | |
val = analogRead(AUDIOPIN); // read voice | |
uint8_t i = map(val, 540, 1023, 0, 13); | |
for (int j = 0; j < 9; j++) | |
{ | |
LOLDraw::Line( 0, j, i, j); | |
} | |
delay(50); | |
} | |
void LoadTheErrorBoard(){ | |
DisplayBitMap(12771); | |
DisplayBitMap(13011); | |
DisplayBitMap(2772); | |
DisplayBitMap(1008); | |
DisplayBitMap(816); | |
DisplayBitMap(480); | |
DisplayBitMap(2052); | |
DisplayBitMap(12707); | |
DisplayBitMap(12643); | |
} | |
void LoadThePicBoard(){ | |
DisplayBitMap(16383); | |
DisplayBitMap(8193); | |
DisplayBitMap(12285); | |
DisplayBitMap(10245); | |
DisplayBitMap(11253); | |
DisplayBitMap(10245); | |
DisplayBitMap(12285); | |
DisplayBitMap(8193); | |
DisplayBitMap(16383); | |
} | |
void LoadTheHellBoard(){ | |
DisplayBitMap(5461); | |
DisplayBitMap(0); | |
DisplayBitMap(2421); | |
DisplayBitMap(2325); | |
DisplayBitMap(2423); | |
DisplayBitMap(2325); | |
DisplayBitMap(7029); | |
DisplayBitMap(0); | |
DisplayBitMap(10922); | |
} | |
void LoadTheFallBoard(){ | |
DisplayBitMap(0); | |
DisplayBitMap(4686); | |
DisplayBitMap(4770); | |
DisplayBitMap(4770); | |
DisplayBitMap(4838); | |
DisplayBitMap(4770); | |
DisplayBitMap(4770); | |
DisplayBitMap(13986); | |
DisplayBitMap(0); | |
} | |
void DisplayBitMap(int lineint) | |
{ | |
//int data[9] = {95, 247, 123, 511, 255, 1, 5, 31, 15}; | |
//for(line = 0; line < 9; line++) { | |
for (byte led=0; led<14; ++led) { | |
if (lineint & (1<<led)) { | |
LedSign::Set(led, line, 1); | |
} else { | |
LedSign::Set(led, line, 0); | |
} | |
} | |
line++; | |
if(line >= 9) line = 0; | |
} | |
void I2C_Write(uint8_t deviceAddress, uint8_t regAddress, uint8_t data){ | |
Wire.beginTransmission(deviceAddress); | |
Wire.write(regAddress); | |
Wire.write(data); | |
Wire.endTransmission(); | |
} | |
// Read all 14 registers | |
void Read_RawValue(uint8_t deviceAddress, uint8_t regAddress){ | |
Wire.beginTransmission(deviceAddress); | |
Wire.write(regAddress); | |
Wire.endTransmission(); | |
Wire.requestFrom(deviceAddress, (uint8_t)14); | |
AccelX = (((int16_t)Wire.read()<<8) | Wire.read()); | |
AccelY = (((int16_t)Wire.read()<<8) | Wire.read()); | |
AccelZ = (((int16_t)Wire.read()<<8) | Wire.read()); | |
Temperature = (((int16_t)Wire.read()<<8) | Wire.read()); | |
GyroX = (((int16_t)Wire.read()<<8) | Wire.read()); | |
GyroY = (((int16_t)Wire.read()<<8) | Wire.read()); | |
GyroZ = (((int16_t)Wire.read()<<8) | Wire.read()); | |
} | |
// Configure MPU6050 | |
void MPU6050_Init(){ | |
delay(150); | |
I2C_Write(MPU6050SlaveAddress, MPU6050_REGISTER_SMPLRT_DIV, 0x07); | |
I2C_Write(MPU6050SlaveAddress, MPU6050_REGISTER_PWR_MGMT_1, 0x01); | |
I2C_Write(MPU6050SlaveAddress, MPU6050_REGISTER_PWR_MGMT_2, 0x00); | |
I2C_Write(MPU6050SlaveAddress, MPU6050_REGISTER_CONFIG, 0x00); | |
I2C_Write(MPU6050SlaveAddress, MPU6050_REGISTER_GYRO_CONFIG, 0x00);//set +/-250 degree/second full scale | |
I2C_Write(MPU6050SlaveAddress, MPU6050_REGISTER_ACCEL_CONFIG, 0x00);// set +/- 2g full scale | |
I2C_Write(MPU6050SlaveAddress, MPU6050_REGISTER_FIFO_EN, 0x00); | |
I2C_Write(MPU6050SlaveAddress, MPU6050_REGISTER_INT_ENABLE, 0x01); | |
I2C_Write(MPU6050SlaveAddress, MPU6050_REGISTER_SIGNAL_PATH_RESET, 0x00); | |
I2C_Write(MPU6050SlaveAddress, MPU6050_REGISTER_USER_CTRL, 0x00); | |
} | |
// Absolute Value | |
float myAbs(float in){ | |
return (in)>0?(in):-(in); | |
} | |
/* ---------------------------------------------------------------------------*/ | |
/** Check if a player won ! | |
* If one of the players won, let's show a funny animation ;) | |
*/ | |
void checkscores() { | |
// TODO : DO the animation ;) | |
} | |
/* ---------------------------------------------------------------------------*/ | |
/** Draw the scores in a lovely scrolling :) | |
* Use the current active screen brutally ... | |
*/ | |
void drawscores() { | |
int8_t i,j,ps1; | |
for(ps1=0;ps1<8;ps1++) { | |
LedSign::Clear(); // Clear the active screen | |
LedSign::Set(6,4,1); // dash between the scores | |
LedSign::Set(7,4,1); | |
// Fill it with both scores : | |
// Left score goes up>down | |
for (i=ps1, j=6; i>=0 && j>=0; i--, j--) { | |
byte f = pgm_read_byte_near(&figures[s1][j]); | |
for (uint8_t k = 0; k < 5; k++, f>>=1) | |
LedSign::Set(k, i, f & 1); | |
} | |
// Right score goes down>up | |
for (i=8-ps1, j=0; i<=8 && j<=6; i++, j++) { | |
byte f = pgm_read_byte_near(&figures[s2][j]); | |
for (uint8_t k = 0; k < 5; k++, f>>=1) | |
LedSign::Set(k+9, i, f & 1); | |
} | |
LedSign::Flip(); | |
delay(200); | |
} | |
delay(1500); | |
LedSign::Clear(0); | |
if (s1==9 || s2==9) { | |
for(ps1=0;ps1<3;ps1++) { | |
LedSign::Flip(); | |
delay(300); | |
LedSign::Flip(); | |
delay(600); | |
} | |
delay(1500); | |
s1=0; s2=0; | |
drawscores(); | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** Draw the ship at its current position. | |
* @param set 1 or 0 to set or clear the led. | |
*/ | |
void drawShip(uint8_t c=1) { | |
LedSign::Set(0,shippos-1,c); | |
LedSign::Set(0,shippos ,c); | |
LedSign::Set(0,shippos+1,c); | |
LedSign::Set(1,shippos ,c); | |
} | |
/* ----------------------------------------------------------------- */ | |
/** Draw the number of lives remaining at the top left of the screen | |
*/ | |
void drawLives() { | |
for(byte i=0;i<STARTLIVES;i++) { | |
LedSign::Set(13-i,0,(i<lives)?1:0); | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** end of the game, draw the Scores using a scrolling | |
*/ | |
void endGame1() { | |
for(byte x=4;x<=8;x++) | |
for(byte y=0;y<=8;y++) | |
LedSign::Set(x,y,0); | |
Figure::Scroll90(score); | |
for(byte i=0;i<30;i++) { | |
drawShip(0); | |
delay(5*(30-i)); | |
drawShip(0); | |
delay(5*(30-i)); | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** Initialize a new game (lives, screen, score, ship ...) | |
*/ | |
void initInvaderGame() { | |
lives=STARTLIVES; | |
score=0; | |
shippos=4; | |
LedSign::Clear(); | |
drawLives(); | |
drawShip(); | |
} | |
/* ----------------------------------------------------------------- */ | |
/** Check the variator to know the position of the ship. | |
* Move and redraw the ship in case of change. | |
*/ | |
void moveShip() | |
{ | |
// Ship have 7 positions. Let's use a third of the variator position. | |
int newshpos=(AccelY/1152); // we reverse the command (variator wrongly connected ;) ) | |
if (newshpos>7) newshpos=7; | |
if (newshpos<1) newshpos=1; | |
if (newshpos!=shippos) { | |
drawShip(0); | |
for(byte i=0;i<8;i++) | |
LedSign::Set(0,i,0); | |
shippos=newshpos; | |
drawShip(1); | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** Check the fire button and fire if it has been pushed. | |
* Please note that we use a static status to check that the user | |
* pull the button between each fire. | |
*/ | |
void fireShip() | |
{ | |
static byte status=0; | |
if (status==0) { | |
// Ship may fire 10 times (not more...) | |
if (analogRead(1)>1000) { | |
// FIRE ! | |
status=1; | |
for(byte i=0;i<MAXFIRE;i++) { | |
if (firepos[i][0]==0) { | |
firepos[i][0]=2; firepos[i][1]=shippos; | |
break; | |
} | |
} | |
} | |
} else { | |
if (analogRead(1)<1000) status=0; | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** Crash : called when an ennemy touched the ship (failure!) | |
*/ | |
void crash() { | |
drawShip(1); | |
delay(150); | |
drawShip(0); | |
delay(150); | |
drawShip(1); | |
delay(150); | |
drawShip(0); | |
delay(150); | |
for(byte i=0;i<MAXENNEMIES;i++) | |
if (ennemypos[i][0]!=0) { | |
LedSign::Set(ennemypos[i][0],ennemypos[i][1],0); | |
ennemypos[i][0]=0; | |
} | |
for(byte i=0;i<MAXFIRE;i++) | |
firepos[i][0]=0; | |
lives--; | |
if (lives==0) { | |
endGame1(); | |
initInvaderGame(); | |
} else { | |
LedSign::Clear(); | |
drawLives(); | |
drawShip(); | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** Add ennemies at the top randomly, with random speed too | |
*/ | |
void addEnnemies() | |
{ | |
if (random(0,ENNEMIESRATE)==0) { | |
// ENNEMY COMING ! | |
for(byte i=0;i<MAXENNEMIES;i++) { | |
if (ennemypos[i][0]==0) { | |
ennemypos[i][0]=13; ennemypos[i][1]=random(1,8); | |
ennemypos[i][2]=random(2,5); // Speed of ennemies between 1 and 5 (5=slower) | |
ennemypos[i][3]=0; | |
break; | |
} | |
} | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** Move the ennemies, and check the collision with the ship | |
*/ | |
void moveEnnemies() | |
{ | |
for(byte i=0;i<MAXENNEMIES;i++) { | |
if (ennemypos[i][0]!=0) { | |
ennemypos[i][3]++; | |
if (ennemypos[i][2]==ennemypos[i][3]) { | |
ennemypos[i][3]=0; | |
LedSign::Set(ennemypos[i][0],ennemypos[i][1],0); | |
ennemypos[i][0]--; | |
// collision with the top of the ship | |
if (ennemypos[i][0]==1 && shippos==ennemypos[i][1]) { | |
crash(); | |
} else { | |
if (ennemypos[i][0]==0) { // Collision detection | |
ennemypos[i][0]=0; | |
if (score>0) score-=1; | |
if (shippos==ennemypos[i][1] || shippos-1==ennemypos[i][1] || shippos+1==ennemypos[i][1]) { | |
crash(); | |
} else { | |
LedSign::Set(ennemypos[i][0],ennemypos[i][1],0); | |
} | |
} | |
} | |
} else { | |
LedSign::Set(ennemypos[i][0],ennemypos[i][1],1); | |
} | |
} | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** Move the bullets, draw them, and check the collision with | |
* ennemies. | |
*/ | |
void moveFires() | |
{ | |
for(byte i=0;i<MAXFIRE;i++) { | |
if (firepos[i][0]!=0) { | |
LedSign::Set(firepos[i][0],firepos[i][1],0); | |
firepos[i][0]++; | |
// Let's detect collision with ennemies : | |
for(byte j=0;j<MAXENNEMIES;j++) { | |
if (ennemypos[j][0]!=0) { | |
if ((ennemypos[j][0]==firepos[i][0] || ennemypos[j][0]==firepos[i][0]+1) && ennemypos[j][1]==firepos[i][1]) { | |
// Ennemy destroyed | |
LedSign::Set(ennemypos[j][0],ennemypos[j][1],0); | |
ennemypos[j][0]=0; | |
firepos[i][0]=0; | |
score+=(6-ennemypos[j][2]); // Change it in case of ennemy speed change ;) score is 5-speed | |
} | |
} | |
} | |
if (firepos[i][0]==14) { | |
firepos[i][0]=0; | |
} else { | |
LedSign::Set(firepos[i][0],firepos[i][1],1); | |
} | |
} | |
} | |
} | |
////TETRIS | |
/* ----------------------------------------------------------------- */ | |
/** Switches on or off the display of a Tetris piece. | |
* @param piece the piece to be displayed or removed. | |
* @param position the position and view of the piece to draw or remove. | |
* @param set 1 or 0 to draw or remove the piece. | |
*/ | |
void switchPiece(const piece_t* piece, const pos_t& position, uint8_t c=1) { | |
for(uint8_t i=0;i<4;i++) { | |
coordPacked_t element = piece->views[position.view].elements[i]; | |
uint8_t eltXPos = element.x+position.coord.x; | |
uint8_t eltYPos = element.y+position.coord.y; | |
LedSign::Set(13-eltYPos, eltXPos, c); | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** | |
* Redraw a section of the tetris play grid between the given | |
* indexes (included). | |
* @param top the index of the top line of the section to redraw. | |
* @param bottom the index the bottom line of the section to redraw. | |
* This parameter MUST be greater or equal than top. | |
*/ | |
void redrawLines(uint8_t top, uint8_t bottom) { | |
for (uint8_t y=top; y<=bottom; y++) | |
for (uint8_t x=0; x<GRID_WIDTH; x++) | |
LedSign::Set(13-y,x,playGrid[y][x]); | |
} | |
/* ----------------------------------------------------------------- */ | |
/** | |
* End of the game, draw the score using a scroll. | |
*/ | |
void endGame() { | |
for (uint8_t y=0;y<=13;y++) { | |
LedSign::Vertical(13-y, 0); | |
delay(100); | |
} | |
// Draw the score and scroll it | |
Figure::Scroll90(score); | |
} | |
/* ----------------------------------------------------------------- */ | |
/** | |
* Game initialization, or reinitialization after a game over. | |
*/ | |
void startTetrisGame() { | |
// Initialize variables. | |
level = 0; | |
score = 0; | |
linesCleared = 0; | |
memset(playGrid, '\0', sizeof(playGrid)); | |
LedSign::Clear(); | |
} | |
/* ----------------------------------------------------------------- */ | |
/** | |
* Test whether a given piece can be put at a given location in the | |
* given location. This is used to check all piece moves. | |
* @param piece the piece to try and put on the play grid. | |
* @param position the position and view to try and put the piece. | |
*/ | |
boolean checkPieceMove(const piece_t* piece, const pos_t& position) { | |
for (uint8_t i=0; i<4; i++) { | |
coordPacked_t element = piece->views[position.view].elements[i]; | |
int8_t eltXPos = element.x+position.coord.x; | |
int8_t eltYPos = element.y+position.coord.y; | |
// Check x boundaries. | |
if (eltXPos>8 || eltXPos<0) | |
return false; | |
// Check y boundaries. | |
if (eltYPos>13 || eltYPos<0) | |
return false; | |
// Check collisions in grid. | |
if (playGrid[eltYPos][eltXPos]) | |
return false; | |
} | |
return true; | |
} | |
/* ----------------------------------------------------------------- */ | |
/** | |
* Handle player actions : left/right move and piece rotation. | |
*/ | |
void playerMovePiece() | |
{ | |
boolean moveSuccess; | |
int inputPos; | |
pos_t newPos; | |
// First try rotating the piece if requested. | |
// Ensure the player released the rotation button before doing a second one. | |
static byte status=0; | |
if (status == 0) { | |
if (analogRead(1)>1000) { | |
status = 1; | |
newPos = position; | |
newPos.view = (newPos.view+1)&3; | |
moveSuccess = checkPieceMove(currentPiece, newPos); | |
if (moveSuccess) { | |
switchPiece(currentPiece, position, 0); | |
switchPiece(currentPiece, newPos, 1); | |
position = newPos; | |
} | |
} | |
} else { | |
if (analogRead(1)<1000) { | |
status = 0; | |
} | |
} | |
newPos = position; | |
// Tweak the analog input to get a playable input. | |
inputPos=(AccelY/1152); // we reverse the command (variator wrongly connected ;) ) | |
if (inputPos != position.coord.x) { | |
// Try moving the piece to the requested position. | |
// We must do it step by step and leave the loop at the first impossible movement otherwise we might | |
// traverse pieces already in the game grid if the user changes the input fast between two game loop iterations. | |
int diffSign = (inputPos>position.coord.x) ? 1 : -1; | |
for(int i=position.coord.x;(inputPos-i)*diffSign>=0;i+=diffSign) { | |
newPos.coord.x = i; | |
if (i<-1 || i>8) { | |
// Skip out of screen iterations. | |
// Don't skip -2, it's a correct x position for certain pieces' views. | |
continue; | |
} else { | |
moveSuccess = checkPieceMove(currentPiece, newPos); | |
if (moveSuccess) { | |
switchPiece(currentPiece, position, 0); | |
switchPiece(currentPiece, newPos, 1); | |
position = newPos; | |
} else { | |
break; | |
} | |
} | |
} | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** | |
* Handle the current piece going down every few game loop iterations. | |
* @param count game loop counter, used to know if the piece should go | |
* down at each call. | |
*/ | |
void timerPieceDown(uint32_t& count) { | |
// Every 10-level iterations, make the piece go down. | |
// TODO The level change code is largely untested and surely needs tweaking. | |
if (++count % (10-level) == 0) { | |
pos_t newPos = position; | |
newPos.coord.y++; | |
boolean moveSuccess = checkPieceMove(currentPiece, newPos); | |
if (moveSuccess) { | |
switchPiece(currentPiece, position, 0); | |
switchPiece(currentPiece, newPos, 1); | |
position = newPos; | |
} else { | |
// Drop the piece on the grid. | |
for (uint8_t i=0; i<4; i++) { | |
coordPacked_t element = currentPiece->views[position.view].elements[i]; | |
uint8_t eltXPos = element.x+position.coord.x; | |
uint8_t eltYPos = element.y+position.coord.y; | |
playGrid[eltYPos][eltXPos] = true; | |
} | |
processEndPiece(); | |
nextPiece(); | |
} | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** | |
* Handles : | |
* - the dropping of the current piece on the game grid when it can't | |
* go lower ; | |
* - the detection and removal of full lines ; | |
* - the score update ; | |
* - the level update when needed. | |
*/ | |
void processEndPiece() { | |
uint8_t fullLines[4]; | |
uint8_t numFull = 0; | |
for (int8_t y=13; y>=0; y--) { | |
boolean full = true; | |
for (uint8_t x=0; x<9; x++) | |
if (!playGrid[y][x]) | |
full = false; | |
if (full) | |
fullLines[numFull++] = y; | |
} | |
if (numFull) { | |
// Blink full lines. | |
for (uint8_t i=0; i<5; i++) { | |
for (uint8_t j=0; j<numFull; j++) | |
LedSign::Vertical(13-fullLines[j], i&1); | |
delay(150); | |
} | |
// Remove full lines from the array. | |
for (uint8_t i=0; i<numFull; i++) { | |
uint8_t lineIdx = fullLines[i]; | |
// Move all lines above one step down. | |
for (uint8_t j=lineIdx; j>0; j--) | |
memcpy(playGrid+j, playGrid+j-1, GRID_WIDTH*sizeof(boolean)); | |
memset(playGrid, '\0', GRID_WIDTH*sizeof(boolean)); | |
// Update the indexes of the other lines to remove. | |
for (uint8_t k=i;k<numFull; k++) | |
fullLines[k]++; | |
} | |
// Update the display. | |
redrawLines(0, fullLines[0]); | |
// Sega scoring algorithm. | |
score += levelMultiplier[level] * linesMultiplier[numFull-1]; | |
// Level update. | |
linesCleared += numFull; | |
if (linesCleared >= 4) { | |
linesCleared = 0; | |
level++; | |
} | |
} | |
} | |
/* ----------------------------------------------------------------- */ | |
/** | |
* Start dropping a new randomly chosen piece. | |
*/ | |
void nextPiece() { | |
currentPiece = &pieces[random(0,7)]; | |
position.coord.x = 3; | |
position.coord.y = -1; | |
position.view = 0; | |
if (!checkPieceMove(currentPiece, position)) { | |
endGame(); | |
startTetrisGame(); | |
} | |
} | |
void Hammer() | |
{ | |
delay(200); | |
DisplayBitMap(7); | |
DisplayBitMap(1799); | |
DisplayBitMap(1287); | |
DisplayBitMap(5954); | |
DisplayBitMap(2690); | |
DisplayBitMap(1794); | |
DisplayBitMap(514); | |
DisplayBitMap(512); | |
DisplayBitMap(1280); | |
delay(200); | |
DisplayBitMap(112); | |
DisplayBitMap(1904); | |
DisplayBitMap(1392); | |
DisplayBitMap(5960); | |
DisplayBitMap(2692); | |
DisplayBitMap(1794); | |
DisplayBitMap(512); | |
DisplayBitMap(512); | |
DisplayBitMap(1280); | |
delay(200); | |
DisplayBitMap(0); | |
DisplayBitMap(224); | |
DisplayBitMap(480); | |
DisplayBitMap(736); | |
DisplayBitMap(624); | |
DisplayBitMap(424); | |
DisplayBitMap(996); | |
DisplayBitMap(48); | |
DisplayBitMap(32); | |
} | |
void Heart(){ | |
delay(100); | |
DisplayBitMap(0); | |
DisplayBitMap(0); | |
DisplayBitMap(288); | |
DisplayBitMap(720); | |
DisplayBitMap(528); | |
DisplayBitMap(288); | |
DisplayBitMap(192); | |
DisplayBitMap(0); | |
DisplayBitMap(0); | |
delay(100); | |
DisplayBitMap(0); | |
DisplayBitMap(816); | |
DisplayBitMap(1224); | |
DisplayBitMap(1032); | |
DisplayBitMap(1032); | |
DisplayBitMap(528); | |
DisplayBitMap(288); | |
DisplayBitMap(192); | |
DisplayBitMap(0); | |
delay(100); | |
DisplayBitMap(3612); | |
DisplayBitMap(4578); | |
DisplayBitMap(8193); | |
DisplayBitMap(8193); | |
DisplayBitMap(8193); | |
DisplayBitMap(8193); | |
DisplayBitMap(4098); | |
DisplayBitMap(2052); | |
DisplayBitMap(1032); | |
} | |
void Tsunami() | |
{ | |
delay(100); | |
DisplayBitMap(15); | |
DisplayBitMap(8223); | |
DisplayBitMap(12351); | |
DisplayBitMap(14407); | |
DisplayBitMap(14339); | |
DisplayBitMap(15363); | |
DisplayBitMap(15363); | |
DisplayBitMap(16135); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(30); | |
DisplayBitMap(63); | |
DisplayBitMap(8319); | |
DisplayBitMap(12431); | |
DisplayBitMap(12295); | |
DisplayBitMap(14343); | |
DisplayBitMap(14343); | |
DisplayBitMap(15887); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(60); | |
DisplayBitMap(126); | |
DisplayBitMap(255); | |
DisplayBitMap(8511); | |
DisplayBitMap(8223); | |
DisplayBitMap(12319); | |
DisplayBitMap(12319); | |
DisplayBitMap(15423); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(120); | |
DisplayBitMap(252); | |
DisplayBitMap(510); | |
DisplayBitMap(575); | |
DisplayBitMap(31); | |
DisplayBitMap(8223); | |
DisplayBitMap(8223); | |
DisplayBitMap(14399); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(240); | |
DisplayBitMap(504); | |
DisplayBitMap(1020); | |
DisplayBitMap(1150); | |
DisplayBitMap(62); | |
DisplayBitMap(63); | |
DisplayBitMap(63); | |
DisplayBitMap(12415); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(480); | |
DisplayBitMap(1008); | |
DisplayBitMap(2040); | |
DisplayBitMap(2300); | |
DisplayBitMap(124); | |
DisplayBitMap(126); | |
DisplayBitMap(126); | |
DisplayBitMap(8447); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(960); | |
DisplayBitMap(2016); | |
DisplayBitMap(4080); | |
DisplayBitMap(4600); | |
DisplayBitMap(248); | |
DisplayBitMap(252); | |
DisplayBitMap(252); | |
DisplayBitMap(511); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(1920); | |
DisplayBitMap(4032); | |
DisplayBitMap(8160); | |
DisplayBitMap(9200); | |
DisplayBitMap(496); | |
DisplayBitMap(504); | |
DisplayBitMap(504); | |
DisplayBitMap(1022); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(3840); | |
DisplayBitMap(8064); | |
DisplayBitMap(16320); | |
DisplayBitMap(2017); | |
DisplayBitMap(992); | |
DisplayBitMap(1008); | |
DisplayBitMap(1008); | |
DisplayBitMap(2044); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(7680); | |
DisplayBitMap(16128); | |
DisplayBitMap(16257); | |
DisplayBitMap(4034); | |
DisplayBitMap(1984); | |
DisplayBitMap(2016); | |
DisplayBitMap(2016); | |
DisplayBitMap(4088); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(15360); | |
DisplayBitMap(15873); | |
DisplayBitMap(16131); | |
DisplayBitMap(8068); | |
DisplayBitMap(3968); | |
DisplayBitMap(4032); | |
DisplayBitMap(4032); | |
DisplayBitMap(8160); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(14337); | |
DisplayBitMap(15363); | |
DisplayBitMap(15879); | |
DisplayBitMap(16136); | |
DisplayBitMap(7936); | |
DisplayBitMap(8064); | |
DisplayBitMap(8064); | |
DisplayBitMap(16352); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(12291); | |
DisplayBitMap(14343); | |
DisplayBitMap(15375); | |
DisplayBitMap(15889); | |
DisplayBitMap(15872); | |
DisplayBitMap(16128); | |
DisplayBitMap(16128); | |
DisplayBitMap(16321); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(8199); | |
DisplayBitMap(12303); | |
DisplayBitMap(14367); | |
DisplayBitMap(15395); | |
DisplayBitMap(15361); | |
DisplayBitMap(15873); | |
DisplayBitMap(15873); | |
DisplayBitMap(16259); | |
DisplayBitMap(16383); | |
delay(100); | |
DisplayBitMap(15); | |
DisplayBitMap(8223); | |
DisplayBitMap(12351); | |
DisplayBitMap(14407); | |
DisplayBitMap(14339); | |
DisplayBitMap(15363); | |
DisplayBitMap(15363); | |
DisplayBitMap(16135); | |
DisplayBitMap(16383); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment