4

SnakeOut v0.9

Development

I want to share my first stable version of Snake for Arduino with the internet. As I said in an earlier post, this version of Snake does not need an Extra shield for your Arduino, it only needs a video plug, and 2 resistors (like the schematic below).

For testing purposes, controls are via a processing sketch, so every enthusiast is able to control the snake, without hacking a game pad first… I will present the code for both the game and the processing sketch below, in a format that is (hopefully) readable and understandable.

Schematic

The schematic is made by the same people who made it possible to do TVout on the arduino:

Plug Schematic

Video Plug schematic. SYNC, VIDEO and GND are respectively 9,7 and GND on the arduino UNO

Instructions

Install the TVOut Library

Create a new Arduino sketch with the code below

Create a new Processing sketch with the code below (do not forget to change the comport)

And one last thing; In the PollSerial.cpp library file (libraries/pollserial), change line 30 from

#define BUFFER_SIZE 64
into
#define BUFFER_SIZE 8

Arduino couldn’t handle this big buffer altogether with the video stuff, and with this smaller buffer it is responding really good đŸ™‚

Arduino sketch

/*
  Snake for Arduino

 Snake Game with output via a video plug using the TVOut library.
 A Processing sketch is used to send key commands to let the snake move, but this
 can easily be replaced by physical buttons in next versions

 The circuit:
 * Video Plug Connected to Pin 7,9 and GND

 created 2011
 by Pepijn Fens

 V0.9:
 First stable version
 */

#include <TVout.h>
#include <fontALL.h>
#include <pollserial.h>
#define memory 25

TVout TV;
pollserial pserial;

byte vres = 120;
byte hres = 104;
byte x = vres/2;
byte y = hres/2;
byte xfield,yfield,wfield,hfield;
byte dx,dy,dir;
byte tailx,taily,taildx,taildy;
byte taildir;
byte bend_x[memory];
byte bend_y[memory];
byte bend_dir[memory];
byte i,j; //bend index from the head and tail
int score;
int foodx,foody;

void setup(){
  TV.begin(PAL,hres,vres);
  TV.select_font(font4x6);
  TV.set_hbi_hook(pserial.begin(57600));
  TV.print(5,2,"score ");
  initialize();
}

void loop() {
  char buf = 0;
  if (pserial.available()) {
    buf = (char) pserial.read();
    pserial.flush();
  }
  switch(buf){
    case 'U': if (dir != 'D') {dx=0; dy=-1; dir='U'; addBend(dir);} break;
    case 'D': if (dir != 'U') {dx=0; dy=1; dir='D';  addBend(dir);  } break;
    case 'R': if (dir != 'L') {dx=1; dy=0; dir='R';  addBend(dir); } break;
    case 'L': if (dir != 'R') {dx=-1; dy=0; dir='L';  addBend(dir); } break;
  }

  if (dir!=0){
  if (tailx == bend_x[j] && taily == bend_y[j]) {
    taildir = bend_dir[j];
    j++;
   if (j >= memory) { j=0; } //go searching for the next bend in the snake, and so on
  }

  switch(taildir) {
    case 'U': taildx=0; taildy=-1; break;
    case 'D': taildx=0; taildy=1; break;
    case 'R': taildx=1; taildy=0; break;
    case 'L': taildx=-1; taildy=0; break;
    case 'O': taildx=0; taildy=0; break;
  }

  moveTail();
  moveHead();
  }
}
void moveHead(){
  x += dx;
  y += dy;
  boolean border = false;
  if (x > xfield+wfield-1 ) { x = xfield+1; border=true;}
  if (x < xfield+1 ) { x =xfield+wfield-1; border=true;}
  if (y > yfield+hfield-2) {y = yfield+1; border=true;}
  if (y < yfield+1 ) {y =yfield+hfield-1; border=true;}
  if (x == foodx && y == foody) {grow(); border=true;}
  if (TV.get_pixel(x,y) == 1 && border==false ){
   gameover();
  }
  TV.set_pixel(x,y,1);

  delay(1000/30); //FPS
}

void moveTail(){
  tailx += taildx;
  taily += taildy;
  if (tailx > xfield+wfield-1 ) { tailx = xfield+1;}
  if (tailx < xfield+1 ) { tailx =xfield+wfield-1;}
  if (taily > yfield+hfield-2) {taily = yfield+1;}
  if (taily < yfield+1 ) {taily =yfield+hfield-1;}

  TV.set_pixel(tailx,taily,0);
}

void addBend(char direction) {
 bend_dir[i]=direction;
 bend_x[i] = x;
 bend_y[i] = y;
 i++;
 if (i >= memory) { i = 0; }
}

void grow() {
  score += 10;
  drawscore();
  newfood();
  moveHead();
  moveHead();

}
void newfood() {
  foodx = random(xfield+5,xfield+wfield-5);
  foody = random(yfield+5,yfield+hfield-5);
  TV.set_pixel(foodx,foody,1);
}
void drawscore() {
  TV.print(30,2,score,10);
}
void gameover() {
  dx=0;
  dy=0;
  taildir='O';
  TV.print(50,50,"GAME OVER");
  delay(5000); //if Game Over, wait 5 seconds....
  initialize();
}

void initialize() {
  drawscore();
  xfield=5; //where the field is on the screen
  yfield=10;
  wfield=hres-(xfield*2); //defining dimensions
  hfield=vres-15;
  TV.draw_rect(xfield,yfield,wfield,hfield,1,0);
  TV.set_pixel(x,y,1); //Set middle pixel to white=start
  TV.set_pixel(x-1,y,1);
  TV.set_pixel(x-2,y,1);
  taildir = 'R'; //start with tail to the right
  tailx = x-3;
  taily = y;
  newfood(); //setup first food
  dir = 0;
  dx = 0;
  dy = 0;
  i = 0;
  j= 0;
  score=0;
  for (int k=0; k<memory; k++) {
    bend_x[k] = 200;
    bend_y[k] = 200;
    bend_dir[k] = 0;
  }
}

Processing Sketch

import processing.serial.*;

Serial myPort;  // Create object from Serial class
int val;        // Data received from the serial port
char tempKey;

void setup()
{
  tempKey=0;
  size(200, 200);
  myPort = new Serial(this, "COM19", 57600); //Change COM19 to whatever comport you are using
}

void draw() {
  background(255);
}

void keyPressed() {
  println(key);
  switch(key){
  case 'w': myPort.write('U'); break;//up
   case 'a': myPort.write('L'); break; //left
   case 's': myPort.write('D'); break; //down
   case 'd': myPort.write('R'); break; //right
   }
}