Arduino Snake Game 360 - Test Game

Hello!, I'm new at this forum and relatively new at electronics and microcontrollers.

This time I've not registered here to ask about nothing but to show my experience. Last week i bought an arduino mega and some other utilities like cables, leds and a "J12864" LED display.
It took me a few nights to notice that behind the screen there's a little over-sensitive screw to adjust contrast!. Anyway... heres my first finished first test project!

And here's the crappy code: (maybe it becomes helpful to someone)

#include "U8glib.h"

//Pin Config
U8GLIB_ST7920_128X64 u8g(52, 51, 53, U8G_PIN_NONE); // SPI Com: SCK = en = 52, MOSI = rw = 51, CS = di = 53

//LED - arduino
//-----------------
//blk = ground
//bla = +5v
//psb = ground
//e   = 52
//rw  = 51
//rs  = 53
//vcc = +5v
//gnd = ground

//MegaDrive Controller Pins
//     _________
//  5  \       / 1
//    9 \_____/ 6

//joystick - arduino
//--------------------
//pin 7    = 5
//pin 9    = 4
//pin 4    = 3
//pin 3    = 2
//pin 5    = +5v
//pin 8    = ground

uint8_t uiKeySelect = 5; //Output pin for select       
uint8_t uiKeyStart = 4; //Input pin for 'start' key    
uint8_t uiKeyLeft = 3; //Input pin for 'left' key      
uint8_t uiKeyRight = 2; //Input pin for 'right' key    



//Key defs
#define KEY_NONE 0
#define KEY_LEFT 1
#define KEY_RIGHT 2
#define KEY_START 3

//Ui Defs
#define UI_WELCOME 0
#define UI_READY 1
#define UI_PLAY 2
#define UI_GAMEOVER 3

//Control variables
uint8_t uiState = UI_WELCOME;
uint8_t uiKeyCode = KEY_NONE;

//InGame variables
uint8_t startBlinkInterval = 0;
uint8_t readyInterval = 0;

int wormRadius = 4;
int wormMaxDelay = 0;
int wormDelay = 0;
int wormPieces;
int wormStartPieces = 6;
float wormDirection = 0.0;
float wormX[255];
float wormY[255];

int pelletX = 0;
int pelletY = 0;
boolean pelletVisible = true;
int pelletBlink = 0;
int pelletBlinkDelay = 2;

String scoreStr = "";
char score[]="000000";

void draw(){
  int i;
  
  switch(uiState) {
    case UI_WELCOME:
      u8g.setFont(u8g_font_fub11);
      u8g.drawStr(24, 20, "360 Worm");
      
      startBlinkInterval++;
      if (startBlinkInterval <= 40) {
         u8g.setFont(u8g_font_04b_03r);
         u8g.drawStr(35, 40, "Press start!");
      } else if (startBlinkInterval >= 80) {
         startBlinkInterval = 0;
      }
      break;
      
    case UI_READY:
      drawPieces();
      
      readyInterval++;
      
      u8g.setFont(u8g_font_04b_03r);      
      if (readyInterval <= 40) {      
         u8g.drawStr(47, 40, "Ready");
      } else if (readyInterval <= 80 && readyInterval > 40) {
         u8g.drawStr(43, 40, "Set");        
      } else if (readyInterval <= 120 && readyInterval > 80) {
         u8g.drawStr(40, 40, "Go!");        
      } else if (readyInterval > 120) {
        readyInterval = 0;
        uiState = UI_PLAY;
      }
      break;
    case UI_PLAY:
      drawPieces();    
      break;
      
    case UI_GAMEOVER:
      u8g.setColorIndex(1);
      u8g.drawBox(0, 0, 128, 64);
      
      u8g.setColorIndex(0);
      u8g.setFont(u8g_font_04b_03r);      
      u8g.drawStr(40, 30, "Game over!");
      u8g.drawStr(40, 40, "Score -");
      u8g.drawStr(76, 40, score);
      u8g.setColorIndex(1);      
      break;
  }
  
}

void drawPieces(){
  int i, x, y;  
  
  for(i=0; i<wormPieces; i++) {
     x = round(wormX[i]);
     y = round(wormY[i]);
     u8g.drawCircle(x, y, 1);
     u8g.drawPixel(x, y);
  }
  
  if (pelletVisible) {
     u8g.drawCircle(pelletX, pelletY, 2);
     u8g.drawPixel(pelletX, pelletY);
  }
  
}

void controlSetup(void) {
  pinMode(uiKeyLeft, INPUT);
  digitalWrite(uiKeyLeft, HIGH);
  pinMode(uiKeyRight, INPUT);
  digitalWrite(uiKeyRight, HIGH);
  pinMode(uiKeyStart, INPUT);
  digitalWrite(uiKeyStart, HIGH);  
  
  pinMode(uiKeySelect, OUTPUT);
  digitalWrite(uiKeySelect, HIGH);    
}

void controlStep(void) {
    uiKeyCode = KEY_NONE;
    
    digitalWrite(uiKeySelect, HIGH);
    if (digitalRead(uiKeyLeft) == LOW) {
      uiKeyCode = KEY_LEFT;
    } else if (digitalRead(uiKeyRight) == LOW) {
      uiKeyCode = KEY_RIGHT;
    }
    
    digitalWrite(uiKeySelect, LOW);
    if (digitalRead(uiKeyStart) == LOW) {
      uiKeyCode = KEY_START;    
    }
}

//Set READY to uiState, prepare initial values.
void startNewGame() {
    int i, x, y;
    
    uiState = UI_READY;
    wormPieces = wormStartPieces;
    wormDirection = 0.0;
    
    x=30; y=20;
    for (i=wormPieces-1; i>=0; i--) {
      wormX[i] = x;
      wormY[i] = y;
      x-=wormRadius;
    }
    
    resetPelletPosition();
}



//Collision test from two centres and a radius
boolean circularCollision(int x1, int y1, int x2, int y2, int r){
  
  int hyp = sqrt(pow(abs(x2-x1), 2) + pow(abs(y2-y1), 2));
  
  if (hyp <= r) return true;
  
  return false;
}

//This will pick random posible pellet positions until its far away from the worm and the corners
void resetPelletPosition(){
  boolean validPosition=false;
  int randomX, randomY, i;
  
  do {
    randomX = random(4, 124);
    randomY = random(4, 60);
    
    validPosition=true;
    
    //Near worm
    for(i=0; i<wormPieces; i++) {
      if (circularCollision(wormX[i], wormY[i], randomX, randomY, 4)) {
        validPosition=false;
        break;
      }
    }
    
    //Near corners
    if (circularCollision(0, 0, randomX, randomY, 10) ||
        circularCollision(0, 64, randomX, randomY, 10) ||
        circularCollision(128, 0, randomX, randomY, 10) ||
        circularCollision(128, 64, randomX, randomY, 10)) {
      validPosition=false;
    }
   
  } while(!validPosition);
  
  pelletX = randomX;
  pelletY = randomY;
}



void gameStep(void) {
  int i;
  
  //Press start to play game...
  if (uiKeyCode == KEY_START && uiState == UI_WELCOME) {
    startNewGame();
  }
  
  //Game over restart
  if (uiKeyCode == KEY_START && uiState == UI_GAMEOVER) {
    uiState = UI_WELCOME;
  }  
 
  //Gameplay
  if (uiState == UI_PLAY) {
     wormDelay++;
     if (wormDelay >= wormMaxDelay) {
       
       //Pellet Blink Animation
       pelletBlink++;
       if (pelletBlink >= pelletBlinkDelay) {
         pelletBlink=0;
         if (pelletVisible) {
           pelletVisible = false; 
         } else {
           pelletVisible = true; 
         }
       }
       
       //Pellet Collision
       if (circularCollision(wormX[wormPieces-1], wormY[wormPieces-1], pelletX, pelletY, 4)) {
         wormPieces++;
         wormX[wormPieces-1] = wormX[wormPieces-2];
         wormY[wormPieces-1] = wormY[wormPieces-2];         
         resetPelletPosition();
       }
       
       //Sides Collision (Game Over)
       if (wormX[wormPieces-1] > 128 ||
           wormX[wormPieces-1] < 0   ||
           wormY[wormPieces-1] > 64  ||
           wormY[wormPieces-1] < 0) {
             
          scoreStr = String((wormPieces - wormStartPieces) * 10);
          scoreStr.toCharArray(score, 6);
          uiState = UI_GAMEOVER;
       }
       
       //Worm Collision (Game Over)
       for(i=0; i<wormPieces-wormStartPieces; i++) {
         if (circularCollision(wormX[wormPieces-1], wormY[wormPieces-1], wormX[i], wormY[i], 2)) {
           
           scoreStr = String((wormPieces - wormStartPieces) * 10);
           scoreStr.toCharArray(score, 6);           
           uiState = UI_GAMEOVER;
           break;
         }
       }       
       
       
       //Worm Movement:
       for(i=0; i<wormPieces-1; i++) {
         wormX[i] = wormX[i+1];
         wormY[i] = wormY[i+1];         
       }

       wormX[wormPieces-1] += cos(wormDirection) * wormRadius;
       wormY[wormPieces-1] += sin(wormDirection) * wormRadius;    
       
       //Worm direction:
       switch (uiKeyCode) {
         case KEY_LEFT:
           wormDirection -= (PI/7);
           break;
         case KEY_RIGHT:
           wormDirection += (PI/7);        
           break;
       }
       
       if (wormDirection > (2*PI)) {
         wormDirection = 0; 
       } else if (wormDirection < 0) {
         wormDirection = (2*PI);
       }
       
       //Reset delay
       wormDelay = 0;
     }
     
    
  }



}


void setup() {
  controlSetup(); 
  //Serial.begin(9600); //Serial log
}

void loop() {  
  controlStep();
  
  u8g.firstPage();
  do  {
    draw();
  } while(u8g.nextPage());
  
  gameStep();
  
  delay(5);
}

Cheers!

Congratulations.
Very well done
Best regards
Jantje

Wait a minute, you're new to this?? That's a very complex piece of work for a beginner that just got the stuff last week. You must have done this kind of thing before.

Anyway, nice job, enjoy the next project.

Thanks, i'll do!... In fact i bought my first arduino board last week, but i made some simple circuits in the past and i love programming! So it wasn't that difficult, even more with a graphic library controlling spi com and graphic functions for me.

Saludos!

Bummer. Playing the video it just says: "Unfortunatly this SME-music-content is not availbale in Germany, because GMEA has not granted the respective music publishing rights". I am sure the video is intresting.

OT comment: I can understand that the artist wants to earn his money, and that is fair enough. But this sell, resell and trading in copyright by 3rd parties with no "artistic interest" just is too much.
(So now I have to do some Ninja-trick with IP to see it anyhow. Well, I could)

draythomp:
Wait a minute, you're new to this?? That's a very complex piece of work for a beginner that just got the stuff last week. You must have done this kind of thing before.

Anyway, nice job, enjoy the next project.

Programming is everything...!

+1
well done!

Never knew "crappy" code was so well written :wink:

Nice project!