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\$
22
  • 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\$ Jun 29, 2018 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\$ Jun 29, 2018 at 10:05
  • 1
    \$\begingroup\$ code-challenge, maybe also puzzle-solver although I think that's a stretch for this question. \$\endgroup\$ Jun 29, 2018 at 10:10
  • 1
    \$\begingroup\$ Was this sandboxed? If not, it should have been \$\endgroup\$
    – mbomb007
    Jun 29, 2018 at 13:49
  • 1
    \$\begingroup\$ Do you know the leaderboard snippet? It helps automatize the process of making a leaderboard. \$\endgroup\$
    – DELETE_ME
    Jun 29, 2018 at 15:13

1 Answer 1

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\$
8
  • \$\begingroup\$ I love this! Particularly how it factors the number of red blocks into the decision making. \$\endgroup\$
    – AJFaraday
    Jun 29, 2018 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\$ Jun 29, 2018 at 14:50
  • \$\begingroup\$ So this will always find the path that need to break least red squares? \$\endgroup\$
    – DELETE_ME
    Jun 29, 2018 at 16:10
  • \$\begingroup\$ @user202729 Yes it does, but only considering the current objective - not thinking ahead at all. \$\endgroup\$
    – orlp
    Jun 29, 2018 at 16:23
  • \$\begingroup\$ This will probably raise some runtime error when the blue square is at the position of the snake. \$\endgroup\$
    – DELETE_ME
    Jul 1, 2018 at 3:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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