Tile based snake game

Ever since I saw someone do this in graal, I’ve wanted to do the same… from what I remember of that guy’s code it semt very hard to make… ahhh young coders.
In fact, it was pretty easy… I even added a labyrinth mode for fun.

Content of the level’s npc:

// NPC made by 2ndwolf
if (created) {
  putnpc ,snakelabyrinth.txt,0,0;
  freezeplayer 0.05;
  hideplayer 0.05;
  startagain();
}

//The initializing function
function startagain(){
  this.mode = 0; //0 = Regular snake, 1 = Labyrinth mode, 2 = Menu mode, to be coded
  this.origin = {23,23};
  this.playboardsize = {18,18};
  playerx = this.origin[0]+this.playboardsize[0]/2;
  playery = this.origin[1]+this.playboardsize[1]/2;
  this.scrollinglimits = {6,6,12,12};
  this.hurttile = 0x71F;
  this.playtile = 0x937;
  this.foodtile = 0x71E;
  this.snaketile = 0x936;
  this.goaltile = 0x7FF;
  this.headpos = {8,8};
  this.labyrinthxoffset = 0;  //Resets the offsets
  this.labyrinthyoffset = 0;
  this.snakelength = 3;
  setstring this.snaketail,#v(this.headpos[0]),#v(this.headpos[1]),#v(this.headpos[0]),#v(this.headpos[1]);
  this.snakespeed = 10; //The bigger the slower

  //Empty the board for regular snake mode
  for(i=0;i<this.playboardsize[0]*this.playboardsize[1];i++){
    if(int(i/this.playboardsize[0])==0 || int(i/this.playboardsize[0])==this.playboardsize[1]-1 || i%this.playboardsize[0]== 0 || i%this.playboardsize[0] == this.playboardsize[0]-1){
      tiles[this.origin[0]+i%this.playboardsize[0],this.origin[1]+int(i/this.playboardsize[0])] = this.hurttile;
    }
    else{
      tiles[this.origin[0]+i%this.playboardsize[0],this.origin[1]+int(i/this.playboardsize[0])] = this.playtile;

    }
  }
  placefood();
  updateboard this.origin[0],this.origin[1],this.playboardsize[0],this.playboardsize[1];
  timeout = 0.05;

}

if(timeout){
  freezeplayer 0.05;
  hideplayer 0.05;
  checkiterations();

  //Read the latest pressed key
  if(keydown(0)&&this.lastdir!=2)this.dir = 0;
  else if(keydown(1)&&this.lastdir!=3)this.dir = 1;
  else if(keydown(2)&&this.lastdir!=0)this.dir = 2;
  else if(keydown(3)&&this.lastdir!=1)this.dir = 3;

  if(this.iterated%this.snakespeed == 0){

    //If in labyrinth mode
    if(this.mode==1){
      //Scroll if snake is near the borders
      if(this.headpos[0]<this.scrollinglimits[0]){
        this.labyrinthxoffset -= 1;
        this.headpos = {this.headpos[0]+1,this.headpos[1]};
        for(i=0;i<sarraylen(this.snaketail)/2;i++){
          replacestring this.snaketail,i*2,#v(strtofloat(#I(this.snaketail,i*2))+1);
        }
      }
      if(this.headpos[1]<this.scrollinglimits[1]){
        this.labyrinthyoffset -= 1;
        this.headpos = {this.headpos[0],this.headpos[1]+1};
        for(i=0;i<sarraylen(this.snaketail)/2;i++){
          replacestring this.snaketail,i*2+1,#v(strtofloat(#I(this.snaketail,i*2+1))+1);
        }
      }
      if(this.headpos[0]>this.scrollinglimits[2]){
        this.labyrinthxoffset += 1;
        this.headpos = {this.headpos[0]-1,this.headpos[1]};
        for(i=0;i<sarraylen(this.snaketail)/2;i++){
          replacestring this.snaketail,i*2,#v(strtofloat(#I(this.snaketail,i*2))-1);
        }
      }
      if(this.headpos[1]>this.scrollinglimits[3]){
        this.labyrinthyoffset += 1;
        this.headpos = {this.headpos[0],this.headpos[1]-1};
        for(i=0;i<sarraylen(this.snaketail)/2;i++){
          replacestring this.snaketail,i*2+1,#v(strtofloat(#I(this.snaketail,i*2+1))-1);
        }
      }

      //Generate the board's tiles according to what's inside local.snakelabyrinth
      for(k=0;k<this.playboardsize[1];k++){
        for(j=0;j<this.playboardsize[0];j++){
          if(strequals(#I(local.snakelabyrinth,this.labyrinthxoffset+this.labyrinthyoffset*strtofloat(#I(local.snakelabyrinth,0))+j+k*strtofloat(#I(local.snakelabyrinth,0))),X)){
            tiles[this.origin[0]+j,this.origin[1]+k] = this.hurttile;
          }
          else if (strequals(#I(local.snakelabyrinth,this.labyrinthxoffset+this.labyrinthyoffset*strtofloat(#I(local.snakelabyrinth,0))+j+k*strtofloat(#I(local.snakelabyrinth,0))),o)){
            tiles[this.origin[0]+j,this.origin[1]+k] = this.foodtile;
          }
          else if (strequals(#I(local.snakelabyrinth,this.labyrinthxoffset+this.labyrinthyoffset*strtofloat(#I(local.snakelabyrinth,0))+j+k*strtofloat(#I(local.snakelabyrinth,0))),.)){
            tiles[this.origin[0]+j,this.origin[1]+k] = this.playtile;
          }
          else if (strequals(#I(local.snakelabyrinth,this.labyrinthxoffset+this.labyrinthyoffset*strtofloat(#I(local.snakelabyrinth,0))+j+k*strtofloat(#I(local.snakelabyrinth,0))),G)){
            tiles[this.origin[0]+j,this.origin[1]+k] = this.goaltile;
          }
        }
      }
    }

    //If he didn't just eat, let's make the tail follow the head, otherwise let it go longer
    if(this.justate==0){
      tiles[this.origin[0]+strtofloat(#I(this.snaketail,0)),this.origin[1]+strtofloat(#I(this.snaketail,1))] = this.playtile;
      for(i=0;i<sarraylen(this.snaketail)/2;i++){
        replacestring this.snaketail,i*2,#I(this.snaketail,i*2+2);
        replacestring this.snaketail,i*2+1,#I(this.snaketail,i*2+3);
      }
    }
    this.justate = 0;

    //Make the front of the tail equal head's position then make the head get further
    replacestring this.snaketail,sarraylen(this.snaketail)-1,#v(this.headpos[1]);
    replacestring this.snaketail,sarraylen(this.snaketail)-2,#v(this.headpos[0]);
    this.headpos = {this.headpos[0]+vecx(this.dir),this.headpos[1]+vecy(this.dir)}

    //Redraw the tail because it might have got overwritten by the labyrinth
    for(i=0;i<sarraylen(this.snaketail)/2;i++){
      tiles[this.origin[0]+strtofloat(#I(this.snaketail,i*2)),this.origin[1]+strtofloat(#I(this.snaketail,i*2+1))] = this.snaketile;
    }

    //If you eat yourself or touch a border tile, you lose
    if(tiles[this.origin[0]+this.headpos[0],this.origin[1]+this.headpos[1]] == this.hurttile || tiles[this.origin[0]+this.headpos[0],this.origin[1]+this.headpos[1]] == this.snaketile){
      say2 Game over;
      startagain();
    }

    //If you eat food
    if(tiles[this.origin[0]+this.headpos[0],this.origin[1]+this.headpos[1]] == this.foodtile){
      this.snakelength += 1;
      addstring this.snaketail,#v(this.headpos[0]);
      addstring this.snaketail,#v(this.headpos[1]);
      this.justate = 1;
      if(this.mode==0){placefood();}
      //In labyrinth mode, erase the food from the string
      if(this.mode==1){
        replacestring local.snakelabyrinth,this.headpos[0]+this.labyrinthxoffset+(this.headpos[1]+this.labyrinthyoffset)*strtofloat(#I(local.snakelabyrinth,0)),.;
      }
    }

    //If you touch the goal
    if(tiles[this.origin[0]+this.headpos[0],this.origin[1]+this.headpos[1]] == this.goaltile){
      say2 You won!;
    }

    //Save last dir to keep the snake from turning around directly and eating himself
    this.lastdir = this.dir;

    tiles[this.origin[0]+this.headpos[0],this.origin[1]+this.headpos[1]] = this.snaketile;

    //Finally, update the board
    updateboard this.origin[0],this.origin[1],this.playboardsize[0],this.playboardsize[1];

  }

  timeout = 0.05;
}

//Just a function to check iterations, obvious isn't it!?
function checkiterations(){
  this.iterated+=1;
}


//This function places food on the board while in normal mode and activates labyrinth mode once the snake gets long enough
function placefood(){
  if(this.snakelength<5){
    this.foodpos = {random(this.origin[0],this.origin[0]+this.playboardsize[0]),random(this.origin[1],this.origin[1]+this.playboardsize[1])};
    while(tiles[this.foodpos[0],this.foodpos[1]]!=this.playtile){
      this.foodpos = {random(this.origin[0],this.origin[0]+this.playboardsize[0]),random(this.origin[1],this.origin[1]+this.playboardsize[1])};
    }
    tiles[this.foodpos[0],this.foodpos[1]] = this.foodtile;
  }
  else{
    this.mode = 1;
  }
}

//Have fun

Content of snakelabyrinth.txt:

if(created){setstring local.snakelabyrinth,58,33,
X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,.,.,.,.,.,.,.,.,.,X,.,.,.,.,.,.,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,.,.,.,.,.,.,.,.,.,X,.,.,.,.,.,.,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,X,X,X,X,X,X,.,.,X,.,.,X,X,X,X,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,.,.,X,.,.,X,.,.,.,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,.,.,X,.,.,X,.,.,.,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,.,.,X,X,.,.,X,X,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,o,o,X,X,X,X,X,X,X,X,X,X,X,X,.,.,.,.,.,X,X,.,.,X,X,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,.,.,.,.,.,X,X,.,.,X,X,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,o,o,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,X,X,X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,X,X,X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,
X,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,.,.,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,g,.,.,.,.,X,
X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,g,.,.,.,.,X,
X,X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
X,X,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
X,X,X,X,X,X,X,X,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
X,X,X,X,X,X,X,X,.,.,.,.,.,.,.,.,.,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X;
}

Behaviour:
Has two modes, labyrinth mode and regular snake mode.

Known bugs:
When you get food on the left side and enter labyrinth mode the snake appears in the wall.
Sometimes a playtile appears on the top left corner while it should be a hurttile.
You reach the bottom of the screen then when you move off to the side it’ll decide to start shifting focus down.

To do:
Needs a labyrinth generator which I’m going to code in python sometime between now and the release of Mistral.
Menu mode.

Hah, cool idea. Although…Still not making use of vecx and vecy I see. I think you’re ready to acquaint yourself with them so try and do so, prove you’re making progress. Also after playing your focus scrolling is somewhat inconsistent. You reach the bottom of the screen then when you move off to the side it’ll decide to start shifting focus down, I’d recommend a fix but where’s the fun in that :stuck_out_tongue:

Thanks for the feedback, it means a lot to me :smiley: