NickBusey.com

Technical person from Boulder, CO

Slither Bots - How I built a series of JavaScript snippets to play Slither.io for you

2018-08-22 Code

Slither Bots is a series of scripts I created that automate playing the web-based game Slither.io in increasingly complicated ways.

Slither.io is a multiplayer version of the classic Worm game. You hit other snakes, you lose, they hit you, they lose. Once a worm dies, it drops a bunch of food that can be slurped up by the survivors (or anyone else) and can double or triple a player’s size in a second or two.

I started very simply, and added layers of complexity one after another, saving them each as a separate generation.

You can try any of these out on your own with a few easy steps:

  • Open Firefox (or Chrome for you heathens).
  • Go to http://slither.io
  • Tools -> Web Developer -> Web Console
  • Open a bot by clicking the name of the generation below
  • Copy the entire file, paste it into the JS console, hit enter, watch and be amazed.

Generation 0 - Random

Demo

No attempt was made at making it smart, just verifying that all the game hooks and controls work correctly.

Generation 1 - Kamikaze

Demo

The first step is to have our snake avoid enemy snakes. It seemed that debugging the tracking of other snakes would be easier if we were trying to deliberately lose, and steer straight at the nearest enemy snake, rather than trying to avoid them.

setInterval(function() {
  var closest_snake = null;
  var closest_distance = 5000;

  for (var ii = 0;ii<snakes.length;ii++) {
    var targetSnake = snakes[ii];
    if (targetSnake.id != snake.id) {
      console.log('Enemy: ',targetSnake);
      var distance_x = Math.round(targetSnake.xx - snake.xx);
      var distance_y = Math.round(targetSnake.yy - snake.yy);
      var distance = Math.abs(distance_x)+Math.abs(distance_y);

      console.log('	Distance: ',distance,' - ',distance_x,distance_y)

      if (distance < closest_distance) {
        closest_distance = distance;
        closest_snake = targetSnake;
      }
    }
  }
  console.log("CLOSEST: ",closest_distance,closest_snake);
  set_direction(Math.round(closest_snake.xx - snake.xx),Math.round(closest_snake.yy - snake.yy));

  update_high_scores();
},200);

Generation 2 - Wuss

Demo

The logical next step after getting the Kamikaze mode working was the opposite of that, to just blindly run away from any nearby snakes.

Generation 3 - Glutton

Demo

To verify food tracking worked, glutton mode simply eats whatever food is available, ignoring everything else.

Generation 4 - Hungry Alert

Demo

This mode is really a combination of the Wuss and Glutton modes. It eats food, unless there is another snake nearby, then it runs away. If the other snake gets too close, the games boost function is used to move away faster. It also included new code to detect if it was trying to get the same piece of food for too long, which would happen when it starts winding around a piece without getting closer. It ignores that piece of food afterwards and keeps going to the next closest piece.

Generation 5 - Looky Loo

Demo

Every generation up to this very only took into account the proximity of things to the head of the snake. This generation only looks at things that are in the 45 degrees in front of the snake, taking it’s current travel direction into accoint.

        // Ony look at food within the defined vision_angle
        var angle = get_angle(snake.xx,snake.yy,food.xx,food.yy);
        if (angle <= my_angle+vision_angle/2 && angle >= my_angle-vision_angle/2) {

Generation 6 - Flashlight

Demo

This is the same as the last generation, but the pieces of food that are calculated as being in the snakes field of vision are highlighted to make debugging easier.

          // Highlight the food in our vision field
          food.fi = per_color_imgs[2].pr_imgs[10];

Generation 7 - Clusterd

Demo

To integrate food better into the bot, it should favor clusters of food rather than any random floating piece of food. Clusters appear when a snake dies and it can be very advantageous to eat these up before other players.

This mode detects clusters of food by downsampling the resolution of the grid of food and searching the grid for a close cluster, if it finds one it goes towards that.

      // Downsample the food grid by 100 to make finding clusters easier
      var x = Math.round(food.xx/100);
      var y = Math.round(food.yy/100);

Generation 8 - Genetic

Demo

This is the furthest I have taken things so far. It’s the start of a basic genetic algorithm. Several parameters are broken out into variables that are randomly nudged after each death.

function apply_mutation() {
  var new_params = params[params.length-1];
  for (var ii = 0; ii < new_params.length; ii++) {
    // Apply a random percentage change, capped at 25% change of any one attribute at one time.
    if (Math.random() > .5) {
      new_params[ii] = Math.round((new_params[ii] + (new_params[ii] * Math.random()) * .25)*10)/10;
    } else {
      new_params[ii] = Math.round((new_params[ii] - (new_params[ii] * Math.random()) * .25)*10)/10;
    }
  }
  params.push(new_params);
}

Obviously without something overseeing multiple versions running at once, and modifying them in a more smart way according to which ones get the higher score. I assume something like that could be done using chrome headless or something similar. Feel free to give it a shot!

Potential Modifications

  • Prediction of where other bots are going to move
  • Actively try and kill other snakes
  • Use Machine Learning?

I probably won’t get around to these any time soon, but merge requests are highly encouraged!

Let me know if you try out the scripts and what your thoughts are!

comments powered by Disqus