Skip to content

Instantly share code, notes, and snippets.

@tuenhidiy
Created April 29, 2018 08:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuenhidiy/ae62a1cd12b92c05d50970ea2fcb8bb1 to your computer and use it in GitHub Desktop.
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
#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