Monday, October 8, 2012

PIC18F4550 Snake Game on 128x64 GLCD



source code (for educational purpose only), simple copy-paste won't work :)

file snake.c:


#include <18F4550.h>
#DEVICE ADC=10
#fuses HSPLL, PLL3, CPUDIV1, NOWDT, NOPROTECT, PUT, NOBROWNOUT, NOLVP, MCLR




#define maxX 16
#define maxY 8


unsigned int8 matrix[maxY][maxX];     // y, x


int snake_dir = 1;


unsigned int8 snake_x[100];
unsigned int8 snake_y[100];
unsigned int8 snake_len = 3;

unsigned int8 score = 0;


void matrix_clear() {
    int x, y;
    for(x=0; x<maxX; x++)
        for(y=0; y<maxY; y++)
            matrix[y][x] = '.';
}



void drawSnakeCell(int x, int y) {
   unsigned int8 sizeX, sizeY;

   sizeX = 128 / maxX;
   sizeY = 64 / maxY;
  
   glcd_rect(x*sizeX, y*sizeY, (x+1)*sizeX-1, (y+1)*sizeY-1, YES, ON);
}

void drawFoodCell(int x, int y) {
   unsigned int8 sizeX, sizeY;

   sizeX = 128 / maxX;
   sizeY = 64 / maxY;
  
   glcd_rect(x*sizeX, y*sizeY, (x+1)*sizeX-1, (y+1)*sizeY-1, NO, ON);
}

void eraseCell(int x, int y) {
   unsigned int8 sizeX, sizeY;
  
   sizeX = 128 / maxX;
   sizeY = 64 / maxY;
  
   glcd_rect(x*sizeX, y*sizeY, (x+1)*sizeX-1, (y+1)*sizeY-1, YES, OFF);
}


void show() {
    unsigned int8 x, y;
    unsigned int8 i;
   
    unsigned int8 sizeX, sizeY;
   
    sizeX = 128 / maxX;
    sizeY = 64 / maxY;
   
    for(i=0; i<snake_len; i++)
        matrix[snake_y[i]][snake_x[i]] = 219;
   
   
    glcd_clear();
    glcd_off();
   
    for(y=0; y<maxY; y++) {    
        for(x=0; x<maxX; x++) {
           if(matrix[y][x]==219)
               glcd_rect(x*sizeX, y*sizeY, (x+1)*sizeX-1, (y+1)*sizeY-1, YES, ON);
           if(matrix[y][x]==3)
               glcd_rect(x*sizeX, y*sizeY, (x+1)*sizeX-1, (y+1)*sizeY-1, NO, ON);
        }
    }
    glcd_on();
   
   
}


void put_food() {
    unsigned int8 x, y;
    while(true) {
        x = rand() % maxX;
        y = rand() % maxY;
        if(matrix[y][x]!=219) {
            matrix[y][x] = 3;
            drawFoodCell(x, y);
            break;
        }
    }
}




unsigned int8 move_snake() {
    signed int8 tail_x, tail_y, head_x, head_y;
    unsigned int8 i;
   
    tail_x = snake_x[snake_len-1];
    tail_y = snake_y[snake_len-1];
   
   
    for(i=snake_len-1; i>0; i--) {
        snake_x[i] = snake_x[i-1];
        snake_y[i] = snake_y[i-1];
    }
   
    head_x = snake_x[0];
    head_y = snake_y[0];
   
    switch(snake_dir) {
        case 0: head_y--; break;
        case 1: head_x++; break;
        case 2: head_y++; break;
        case 3: head_x--; break;
    }
   
    snake_x[0] = head_x;
    snake_y[0] = head_y;
   
    if(head_x < 0)
        return -1;
   
    if(head_y < 0)
        return -1;
       
    if(head_x >= maxX)
        return -1;
       
    if(head_y >= maxY)
        return -1;
       
    if(matrix[head_y][head_x] != 3) {  // nothing eaten
        matrix[tail_y][tail_x] = '.';  // cut tail
        eraseCell(tail_x, tail_y);
    }
       
    if(matrix[head_y][head_x] == 3) {
        snake_x[snake_len] = tail_x;
        snake_y[snake_len] = tail_y;
        snake_len++;
        matrix[head_y][head_x] = 219;  // add head
        drawSnakeCell(head_x, head_y);
       
        return 2;
    }
   
   
    matrix[head_y][head_x] = 219;  // add head 
    drawSnakeCell(head_x, head_y);
    return 0;
   
}
 


int1 refreshSnakeDir() {
   unsigned int8 oldDir;
   oldDir = snake_dir;
   if(keyUpPressed) snake_dir = 0;
   if(keyRightPressed) snake_dir = 1;
   if(keyDownPressed) snake_dir = 2;
   if(keyLeftPressed) snake_dir = 3;
   if(oldDir!=snake_dir)
      return 1;
   else
      return 0;
}
    

void main() {

   unsigned int8 i;
   unsigned int8 res;
   unsigned int1 dirChanged;

   glcd_clear();
  
  
   matrix_clear();
   
   snake_y[0] = 0;
   snake_y[1] = 0;
   snake_y[2] = 0;
  
   snake_x[0] = 2;
   snake_x[1] = 1;
   snake_x[2] = 0;
  
  
   matrix[0][0] = 219;
   matrix[0][1] = 219;
   matrix[0][2] = 219;
  
   drawSnakeCell(0, 0);
   drawSnakeCell(1, 0);
   drawSnakeCell(2, 0);
  
   put_food();
   
   while(true) {
       
        for(i=0; i<4; i++) {
            dirChanged = refreshSnakeDir();
            delay_ms(50);
            dirChanged |= refreshSnakeDir();
            delay_ms(50);
            dirChanged |= refreshSnakeDir();
            delay_ms(50);
            dirChanged |= refreshSnakeDir();
            delay_ms(50);
            dirChanged |= refreshSnakeDir();
           
            if( dirChanged || keyBPressed || keyAPressed )
               break;       
        }
     
       
        if(keyBPressed)   // exit app
            reset_cpu();
           
        refreshSnakeDir();
           
        res = move_snake();
       
        if(res==2) {
            score++;
            put_food();
        }
       
        if(res==-1) {
            glcd_clear();
            glcd_putc("Game Over\r\nscore = ");
            glcd_putn(score);
            delay_ms(1000);
            reset_cpu();
        }
    
    }

}