5
\$\begingroup\$

So, I wrote a simple Javascript game (as yet unnamed) and naturally I wanted to write an API so I could write a bot to play it for me, and naturally I wanted to let the PPCG community have a go for themselves.

It's a 'snake' game with the simple goal of collecting as many blue squares as possible while avoiding red squares or the edge of the board. It was initially controlled with a mouse.

Example of gameplay

Find the API edition of the game here: https://ajfaraday.github.io/game_name_here/api.html

Gameplay:

  • Type the code for your bot in the field under 'Your function': This will repeat automatically when you tell it to run.
    • Only in Javascript, sorry.
    • See restrictions, below.
  • You can try it step-wise by clicking 'step'.
  • I suggest you 'Run (fast)' once you have a workable function.
  • Click 'Reset' to try a fresh game.
  • Every 10 points will increase the points per blue square by 1.
  • Every 10 points will increase the red squares spawned by 1.
  • You can lose lives by:
    • Gobbling up a red square (Be aware that, while you lose a life, it also breaks down the wall. Sacrificing lives to break through walls is a legitimate tactic).
    • Attempting to leave the game board.
    • Running down the 'Timeout' clock of 20 seconds between blue squares.
  • You will not lose a life by 'stepping on your tail' in this version.
  • Every 100 points will earn an additional life.
  • Be aware that all coordinates are x, y starting in the top left corner. 0 indexed.

Restrictions:

  • There is no security in this code. Please do not hack the game!
    • Only use standard JS and the API methods described.
    • PLEASE! Don't change API.coord
    • You may not interact directly with Sketch, Cell or other entities defined within the game.

The competition

  • Please give your entry title a short name for your snake.
  • Include your JS code and any explanation you'd like to include.
  • The OP will copy/paste the code into the game, and run it 5 times (using 'Run (fast)').
  • The highest score achieved in those attempts is your score.
  • The OP will add your score to the answer, and to a leader board in the question.
  • You may submit more than one function.

Leader Board

+--------------------------+---------------------------------+----------+
| Username                 | Snake Name                      | Score    |
+--------------------------+---------------------------------+----------+
| orlp                     | Simple A*                       | 595      |
+--------------------------+---------------------------------+----------+

Rendering Issue

This rendering issue has now been fixed and deployed.

Please be aware that if you call more than one of the direction methods during your function, the snake will visit all of the squares and collect points or lose lives accordingly. However, due to a rendering issue, it will only display the ending position of your snake.

Be aware that the snake can not travel diagonally between walls without losing a life.


Responsible disclosure: This is written in pure Javascript, it contains no logging, analytics or other data gathering processes. It's hosted on github pages, typically a trusted home of code.

If you want to confirm this, or would prefer to develop your answer on a locally hosted instance, the code is available here: https://github.com/ajfaraday/game_name_here


This is my first attempt at a competitive non-golf challenge, so I'm open to suggestions about how I could make it more suitable for this kind of situation.

Happy snaking!

\$\endgroup\$
  • 1
    \$\begingroup\$ "Running down the 'Timeout' clock of 20 seconds between blue squares." How fast is the snake going, so how many cells can be traveled in those 20 secs from blue to blue? (And I assume the run-fast button, also increases this countdown speed?) \$\endgroup\$ – Kevin Cruijssen Jun 29 '18 at 8:32
  • 4
    \$\begingroup\$ I don't think this is king-of-the-hill because as far as I can tell the submissions don't interact with each other. \$\endgroup\$ – Peter Taylor Jun 29 '18 at 10:05
  • 1
    \$\begingroup\$ code-challenge, maybe also puzzle-solver although I think that's a stretch for this question. \$\endgroup\$ – Peter Taylor Jun 29 '18 at 10:10
  • 1
    \$\begingroup\$ Was this sandboxed? If not, it should have been \$\endgroup\$ – mbomb007 Jun 29 '18 at 13:49
  • 1
    \$\begingroup\$ Do you know the leaderboard snippet? It helps automatize the process of making a leaderboard. \$\endgroup\$ – user202729 Jun 29 '18 at 15:13
4
\$\begingroup\$

Simple A* - Official score: 595

This solution uses simple A* pathfinding, with no long-term strategy - it always finds the optimal route towards just the next blue square.

var h = function(p, q) {
    // Manhattan distance.
    return Math.abs(p[0] - q[0]) + Math.abs(p[1] - q[1]);
};

var s = JSON.stringify;
var p = JSON.parse;
var walls = API.walls.map(s);
var start = API.coord;
var goal = API.goal;

var is_closed = {};
is_closed[s(start)] = 0;
var open = [s(start)];

var came_from = {};
var gs = {};
gs[s(start)] = 0;
var fs = {};
fs[s(start)] = h(start, goal);

var cur;
while (open.length) {
    var best;
    var bestf = Infinity;
    for (var i = 0; i < open.length; ++i) {
        if (fs[open[i]] < bestf) {
            bestf = fs[open[i]];
            best = i;
        }
    }

    cur = p(open.splice(best, 1)[0]);
    is_closed[s(cur)] = 1;

    if (s(cur) == s(goal)) break;

    for (var d of [[0, 1], [0, -1], [1, 0], [-1, 0]]) {
        var next = [cur[0] + d[0], cur[1] + d[1]];
        if (next[0] < 0 || next[0] >= API.width ||
            next[1] < 0 || next[1] >= API.height) {
            continue;
        }

        if (is_closed[s(next)]) continue;
        if (open.indexOf(s(next)) == -1) open.push(s(next));
        var is_wall = walls.indexOf(s(next)) > -1;
        var g = gs[s(cur)] + 1 + 10000 * is_wall;
        if (gs[s(next)] != undefined && g > gs[s(next)]) continue;

        came_from[s(next)] = cur;
        gs[s(next)] = g;
        fs[s(next)] = g + h(next, goal);
    }
}

var path = [cur];
while (came_from[s(cur)] != undefined) {
    cur = came_from[s(cur)];
    path.push(cur);
}

var c = path[path.length - 1];
var n = path[path.length - 2];
if (n[0] < c[0]) {
    API.left();
} else if (n[0] > c[0]) {
    API.right();
} else if (n[1] < c[1]) {
    API.up();
} else {
    API.down();
}
\$\endgroup\$
  • \$\begingroup\$ I love this! Particularly how it factors the number of red blocks into the decision making. \$\endgroup\$ – AJFaraday Jun 29 '18 at 14:42
  • 1
    \$\begingroup\$ This will be pretty hard to beat since almost every pathfinding algorithm is created from this one. Nice solution +1 \$\endgroup\$ – Luis felipe De jesus Munoz Jun 29 '18 at 14:50
  • \$\begingroup\$ So this will always find the path that need to break least red squares? \$\endgroup\$ – user202729 Jun 29 '18 at 16:10
  • \$\begingroup\$ @user202729 Yes it does, but only considering the current objective - not thinking ahead at all. \$\endgroup\$ – orlp Jun 29 '18 at 16:23
  • \$\begingroup\$ This will probably raise some runtime error when the blue square is at the position of the snake. \$\endgroup\$ – user202729 Jul 1 '18 at 3:25

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.