title: Bot Factory KoTH (2.0)

text:
# Question
Title: Bot Factory KoTH (2.0)
In this challenge, bots (consisting of JS functions) move around an infinite factory (playing field), collecting characters. These characters can be used to build worker bots, or dropped for other bots to pick up.

This challenge is a modified redo of Bot Factory KoTH, which was broken by a mistake in the spec.

## Results

```
[39599.65] Ghost
[12251.15] Shy Guy
[8565.70]  Camper
[8339.95]  Honnold
[2862.50]  True Neutral
[51.575]   Rabbit
[10.65]    Centrist
[-8.85]    The Luggage
[-66.075]  The Caveman

```

The bounty went to **@Razetime**, as their bots were consistently in the lead until Shy Guy showed up.

Feel free to keep posting bots, but the scoring might not get updated.

## The Factory

All bots start out randomly placed around the center of the factory, `[0, 0]`, where coordinates are arrays `[x, y]`. North is -Y, and west is -X. Each round consists of up to 100000 turns. A bot may move one space in any cardinal direction (or build a worker, or drop a character) per turn.

Bots can move by returning `north()`, `east()`, `south()`, or `west()`.

## Characters

Characters will be randomly distributed, centered around `[0, 0]`. There are always up to a total of 4 characters per bot at any given time (excluding those from dead bots).

Character's positions may overlap, and their character value (such as `a` or `(`) is selected from a pool of the source code of every bot and worker currently in the factory.

Bots collect characters by moving into the same position in the factory, and these collected characters are stored within the bot.

Bots may also drop a character they have previously collected in any of the four cardinal directions. This character is then removed from the bot's collected characters array.

Bots drop characters by returning `drop.north(c)`, `drop.east(c)`, `drop.south(c)`, or `drop.west(c)`, where `c` is a string.

## Score

Bots have a score, which is initially `-floor(LEN / 2)`, where `LEN` is the length of the bot's source code in characters. Worker bots start with a score of 0 regardless of their length.

Whenever a character is collected, this score value increments; when a character is dropped, it decrements.

## Collisions

When two or more bots collide (occupy the same position), whichever has the highest score survives (if there is a tie, none survive). All bots that die "drop" the characters they have collected, which are randomly distributed centered around the position they collided in.

Bots can switch places by moving into each others' previously occupied positions without colliding. Bots can collide with their own workers.

## Workers

Bots can use the characters they have collected to build a worker bot. A worker bot's source code (provided as a function or arrow function) must be entirely made up of characters its owner has collected, which are then removed. Half of the length of the worker's source code is deducted from the owner's score.

Worker bots are placed randomly around the position of their owner, using the same system as characters, and are not immune to collision. Worker bots can also build worker bots, whose owner would be the worker.

Workers can be built by returning the result of the function `build(source)`, where `source` is a string. If the bot cannot be built (such as not having all necessary characters), nothing happens.

## Functions

All bots are functions. They are provided a single argument, an object that can be used for storage, and can access information about other bots and characters using the following functions:

* `bots()`: Returns an array of bots (as objects)
* `chars()`: Returns an array of characters (as objects)
* `self()`: Returns the bot that called it (as an object)
* `owner()`: Returns the owner of the bot that called it (as an object, `null` if owner is dead, or `self()` if no owner)

A bot object has the following properties:

* `uid`: An integer ID unique to each bot, randomly selected
* `owner`: The UID of the bot's owner
* `score`: The score of the bot
* `pos`: The position of the bot formatted as `[x, y]`

A bot object also has the following properties if it is the owner or worker of the bot:

* `chars`: An array of characters the bot has collected
* `source`: A string with the bot's source code

A character object has the following properties:

* `char`: A string
* `pos`: The position of the character formatted as `[x, y]`

There are also the following library functions:

* `center()`: Get average of bots' positions weighted by score
* `turn()`: Get current turn (starts at 0)
* `at(pos)`: Returns an object:
    + `bot`: The `uid` of the bot at that location, or `null` if there is none
    + `chars`: An array of characters at that location
* `dir(from, to)`: Get direction from `from` to `to`, which should be arrays formatted `[x, y]`
* `dirTo(pos)`: Same as `dir`, uses `self().pos` as `from`
* `dist(from, to)`: Get taxicab distance from `from` to `to`, which should be arrays formatted `[x, y]`
* `distTo(pos)`: Same as `dist`, uses `self().pos` as `from`

## Winning

Once all surviving bots are direct or indirect workers of the same initial bot, characters will no longer generate. Games automatically end when all characters are collected.

At the end of a game, all bots' scores (including their workers) are added up for each round with the highest winning.

## Rules

* Bots may not use global variables in a way which produces side effects other than intended (such as to sabotage other bots or the controller, or communicate with any other bots)
* Bots which error will be killed and drop their characters
* Bots may only attempt to build workers which are valid functions
* Bots may not take an unnecessarily long time to run (no hard limitation, but be reasonable)
* Bots may not team with other which do not originate from the same owner (directly or not)

## Technical Notes

* Character and bot spawning radius is based on a geometric distribution (`p=0.1`), with a random angle. The resulting coordinates are truncated to integers. Repeated until a position is found with no bots within a taxicab distance of 4
* For dead bots' drops, `p=0.2` and there is no distance requirement
* For worker bot spawning, minimum taxicab distance is 3 for the owner
* Every time a character is picked up, one attempt is made to generate another. An attempt will only fail if there are already `4 * botCount` characters not dropped by a bot (or a bots' death), where `botCount` is the total number of alive bots (including workers). `4 * botCount` characters are generated at the start.

## Example Bot

ExampleBot uses an arrow function to keep its starting score higher, but still isn't well golfed. It likes characters which are in the middle of the action, which it finds by sorting the `chars()` array and using the `dist` and `center` library functions. It will not be included in the competition game.

```
() => dirTo(chars().sort((a, b) => dist(center(), a.pos) - dist(center(), b.pos))[0].pos)

```

## Other

**Chat:** https://chat.stackexchange.com/rooms/118871/bot-factory-koth-2-0

**Controller:** https://gist.github.com/Radvylf/4d9915632fed1b4d7a7e9407e2a38edf

# Answer
> 3 votes
## Ghost

```
{
    name: "Ghost",
    color: "#5599dd",
    run: _=>{var f="filter",d=distTo,n=dist,e=self(),p=e.pos,h=bots()[f](b=>b.uid!=e.uid),r=chars()[f](c=>!h.find(b=>n(b.pos,c.pos)<=d(c.pos))),t=h[f](b=>b.score>=e.score),o=(l,i)=>!t.find(t=>n(t.pos,[p[0]+l,p[1]+i])<2),s=(l,i)=>n([0,0],[p[0]+l,p[1]+i]),T=[o(0,0),o(0,-1),o(1,0),o(0,1),o(-1,0)],L=[s(0,0),s(0,-1),s(1,0),s(0,1),s(-1,0)];return r[0]?turn()%50||!o(0,0)?dirTo(r.sort((a,b)=>d(a.pos)-d(b.pos))[0].pos):build(e.source):[_=>_,north,east,south,west][[0,1,2,2+1,5-1][f](i=>T[i]).sort((a,b)=>L[a]-L[b])[0]||0]()}
}

```

A bot loosely inspired by Shy Guy, but with so many optimizations it's almost guaranteed to be unbeatable.

Ghost is similar to Shy Guy in that it's almost unkillable. Except it's way, way smarter. It's shorter. Its source is optimized to use more common characters. It uses much simpler logic.

Here's how it works:

1. If there are any characters closer to Ghost than any other bot, go toward the nearest one
2. Otherwise, stay still or go in any direction which is 100% guaranteed not to kill Ghost (preferring ones which bring it closer to `[0, 0]`)
3. Finally, if there is no safe direction to go, stay still

# Answer
> 5 votes
# Honnold (66 bytes, -33 points)

```
{
    name: "Honnold",
    color: "#0000FF",
    run:()=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)
}

```

Bonus points for guessing the name's origin.

Try it:

```
var botData = [
  {
      name: "ExampleBot",
      color: "#999999",
      run: () => dirTo(chars().sort((a, b) => dist(center(), a.pos) - dist(center(), b.pos))[0].pos)
  },
  {
      name: "ExampleBot",
      color: "#999999",
      run: () => dirTo(chars().sort((a, b) => dist(center(), a.pos) - dist(center(), b.pos))[0].pos)
  },
  {
      name: "ExampleBot",
      color: "#999999",
      run: () => dirTo(chars().sort((a, b) => dist(center(), a.pos) - dist(center(), b.pos))[0].pos)
  },
  {
    name: "Honnold",
    color: "#0000FF",
    run:()=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)
  }
];

{
var game = {
  randPos: (center, any = !1, uid = 0, owner = 0, p = 0.1) => {
      var theta, radius, pos;

      do {
          theta = Math.random() * Math.PI * 2;
          radius = 0;

          while (Math.random() > p)
              radius++;

          pos = [Math.trunc(center[0] + Math.cos(theta) * radius), Math.trunc(center[1] + Math.sin(theta) * radius)];
      } while (!any && game.bots.find(a => a && a.uid != uid && Math.abs(a.pos[0] - pos[0]) + Math.abs(a.pos[1] - pos[1]) < (a.uid == owner ? 3 : 4)));

      return pos;
  },
  debug: function(){},
  log: 0 // 0 = NONE, 1 = SUMMARY, 2 = ALL
};

var north = () => ["north"];
var east = () => ["east"];
var south = () => ["south"];
var west = () => ["west"];

var build = code => ["worker", code];

var drop = {
  north: char => ["drop.north", char],
  east: char => ["drop.east", char],
  south: char => ["drop.south", char],
  west: char => ["drop.west", char]
};

var bots = () => game.bots.map(a => ({
  uid: a.uid,
  owner: a.owner,
  original: a.original,
  score: a.score,
  pos: [...a.pos],
  chars: game.uid == a.uid ? [...a.chars] : undefined,
  source: game.uid == a.uid ? a.source : undefined
})).sort((a, b) => a.uid - b.uid);

var chars = () => game.chars.map(a => ({
  char: a.char,
  pos: [...a.pos]
}));

var self = () => {
  var bot = game.bots.find(a => a.uid == game.uid);
  return bot ? {
      uid: bot.uid,
      owner: bot.owner,
      original: bot.original,
      score: bot.score,
      pos: [...bot.pos],
      chars: [...bot.chars],
      source: bot.source
  } : null;
};

var owner = () => {
  var bot = game.bots.find(a => a.uid == game.bots.find(b => b.uid == game.uid).owner);
  return bot ? {
      uid: bot.uid,
      owner: bot.owner,
      original: bot.original,
      score: bot.score,
      pos: [...bot.pos],
      chars: [...bot.chars],
      source: bot.source
  } : null;
};

var center = () => game.center;
var turn = () => game.turns;

var at = pos => ({
  bot: (game.bots.find(b => b.pos[0] == pos[0] && b.pos[1] == pos[1]) || {uid: null}).uid,
  chars: chars().filter(c => c.pos[0] == pos[0] && c.pos[1] == pos[1])
});

var dir = (posFrom, pos) => {
  if (Math.abs(posFrom[0] - pos[0]) <= Math.abs(posFrom[1] - pos[1]))
      return posFrom[1] < pos[1] ? ["north"] : ["south"];
  else
      return posFrom[0] < pos[0] ? ["west"] : ["east"];
};

var dirTo = pos => {
  var bot = game.bots.find(a => a.uid == game.uid);
  if (Math.abs(pos[0] - bot.pos[0]) <= Math.abs(pos[1] - bot.pos[1]))
      return pos[1] < bot.pos[1] ? ["north"] : ["south"];
  else
      return pos[0] < bot.pos[0] ? ["west"] : ["east"];
};

var dist = (posFrom, pos) => {
  return Math.abs(posFrom[0] - pos[0]) + Math.abs(posFrom[1] - pos[1]);
};

var distTo = pos => {
  var bot = game.bots.find(a => a.uid == game.uid);
  return Math.abs(pos[0] - bot.pos[0]) + Math.abs(pos[1] - bot.pos[1]);
};

async function runRound(turns = 100000) {
  var uids = [];

  game.perf = performance.now();

  for (let i = 1; i <= botData.length; i++)
      uids[i - 1] = i;

  for (let j, i = uids.length - 1; i > 0; i--) {
      j = Math.floor(Math.random() * (i + 1));
      [uids[i], uids[j]] = [uids[j], uids[i]];
  }

  game.bots = [];
  game.chars = [];
  game.records = game.records || [];
  game.uids = [];

  for (let i = 0; i < botData.length; i++) {
      game.bots[i] = {
          uid: uids[i],
          owner: uids[i],
          original: uids[i],
          score: Math.floor(botData[i].run.toString().length * -1 / 2),
          chars: [],
          pos: game.randPos([0, 0]),
          source: botData[i].run.toString(),
          run: botData[i].run,
          storage: {},
          name: botData[i].name || "Bot",
          color: botData[i].color || "#000000"
      };

      game.uids[uids[i]] = i;
      game.records[i] = game.records[i] || 0;
  }

  game.center = [
      game.bots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0),
      game.bots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0)
  ];

  game.charPool = game.bots.map(a => a.source).join("");

  for (let i = 0; i < botData.length * 4; i++)
      game.chars.push({
          char: game.charPool[Math.random() * game.charPool.length | 0],
          pos: game.randPos([0, 0]),
          game: !0
      });

  game.cuid = botData.length + 1;
  game.turns = 0;

  if (!game.fps) {
      while (game.chars.length && game.bots.length && game.turns < turns) {
          runTurn();

          game.turns++;
      }
  } else {
      game.debug();

      while (game.chars.length && game.bots.length && game.turns < turns) {
          await new Promise(function(resolve) {
              setTimeout(resolve, 1000 / game.fps);
          });

          if (!game.pause) {
              runTurn();

              game.debug();
              game.turns++;
          }
      }
  }
  bots().map(b => game.records[game.uids[b.original]] += b.score);

  if (game.log)
      console.log("Round Completed (" + ((performance.now() - game.perf) / 1000).toFixed(3) + "s):\n" + game.bots.map(a => a).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join("\n"));
}

function runTurn() {
  var cbots = [];
  var npos = [];
  var nposl = [];
  var nbots = [];

  for (let b, p, m, i = 0; i < game.bots.length; i++) {
      b = game.bots[i];

      game.uid = b.uid;

      try {
          m = b.run(b.storage);
      } catch(e) {
          m = ["dead"];

          if (game.log == 2)
              console.warn("[" + game.turns + "] Error: " + b.name + "\n" + (e.stack || e.message));

          for (let j = 0; j < b.chars.length; j++)
              game.chars.push({
                  char: b.chars[j],
                  pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                  game: !1
              });
      }

      if (!Array.isArray(m))
          m = [];

      if (m[0] == "north")
          p = [b.pos[0], b.pos[1] - 1];
      else if (m[0] == "east")
          p = [b.pos[0] + 1, b.pos[1]];
      else if (m[0] == "south")
          p = [b.pos[0], b.pos[1] + 1];
      else if (m[0] == "west")
          p = [b.pos[0] - 1, b.pos[1]];
      else
          p = [...b.pos];

      if (m[0] != "dead")
          npos.push({
              bot: b.uid,
              pos: p
          });

      if (m[0] == "worker" && m[1].split("").reduce((c, d, e) => d && (e = c.indexOf(d)) != -1 ? c.filter((f, g) => g != e) : null, [...b.chars])) {
          p = game.randPos(b.pos, !1, 0, b.uid);

          try {
              cbots.push({
                  uid: game.cuid,
                  owner: b.uid,
                  original: b.original,
                  score: 0,
                  chars: [],
                  pos: p,
                  source: m[1],
                  run: eval(m[1]),
                  storage: {},
                  name: b.name + "*",
                  color: b.color
              });

              npos.push({
                  bot: game.cuid++,
                  pos: p
              });

              b.score -= Math.floor(m[1].length / 2);

              for (let n, j = 0; j < m[1].length; j++) {
                  n = b.chars.indexOf(m[1][j]);

                  b.chars = b.chars.slice(0, n).concat(b.chars.slice(n + 1));
              }

              if (game.log == 2)
                  console.log("[" + game.turns + "] New Worker: " + b.name);
          } catch(e) {
              if (game.log == 2)
                  console.warn("[" + game.turns + "] Invalid Worker: " + b.name + "\n" + (e.stack || e.message));
          }
      }

      if (typeof m[0] == "string" && m[0].match(/^drop.(north|east|south|west)$/) && b.chars.includes(m[1])) {
          b.score--;

          for (let j = 0; j < b.chars.length; j++) {
              if (b.chars[j] == m[1]) {
                  b.chars = b.chars.slice(0, j) + b.chars.slice(j + 1);

                  break;
              }
          }

          if (m[0] == "drop.north")
              p = [b.pos[0], b.pos[1] - 1];
          else if (m[0] == "drop.east")
              p = [b.pos[0] + 1, b.pos[1]];
          else if (m[0] == "drop.south")
              p = [b.pos[0], b.pos[1] + 1];
          else if (m[0] == "drop.west")
              p = [b.pos[0] - 1, b.pos[1]];

          game.chars.push({
              char: m[1],
              pos: p,
              game: !1
          });
      }
  }

  game.bots.push(...cbots);

  for (let f, i = 0; i < npos.length; i++) {
      if (!(f = nposl.find(a => a.pos[0] == npos[i].pos[0] && a.pos[1] == npos[i].pos[1])))
          nposl.push(f = {
              pos: [...npos[i].pos],
              bots: []
          });

      f.bots.push(npos[i].bot);
  }

  for (let n, m, b, i = 0; i < nposl.length; i++) {
      n = nposl[i];

      if (n.bots.length > 1) {
          m = Math.max(...n.bots.map(a => game.bots.find(b => b.uid == a).score));

          if (game.bots.filter(a => n.bots.includes(a.uid) && a.score == m).length > 1) {
              m += 1;

              if (game.log == 2)
                  console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
          } else {
              if (game.log == 2)
                  console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
          }

          for (let j = 0; j < n.bots.length; j++) {
              b = game.bots.find(a => a.uid == n.bots[j]);

              if (b.score < m)
                  for (let k = 0; k < b.chars.length; k++)
                      game.chars.push({
                          char: b.chars[k],
                          pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                          game: !1
                      });
              else
                  nbots.push({
                      uid: b.uid,
                      owner: b.owner,
                      original: b.original,
                      score: b.score,
                      chars: [...b.chars],
                      pos: n.pos,
                      source: b.source,
                      run: b.run,
                      storage: b.storage,
                      name: b.name,
                      color: b.color
                  });
          }
      } else {
          b = game.bots.find(a => a.uid == n.bots[0]);

          nbots.push({
              uid: b.uid,
              owner: b.owner,
              original: b.original,
              score: b.score,
              chars: [...b.chars],
              pos: n.pos,
              source: b.source,
              run: b.run,
              storage: b.storage,
              name: b.name,
              color: b.color
          });
      }
  }

  game.center = [
      nbots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0),
      nbots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0)
  ];

  game.charPool = nbots.map(a => a.source).join("");

  for (let b, c, i = 0; i < game.chars.length; i++) {
      c = game.chars[i];

      if (b = nbots.find(a => a.pos[0] == c.pos[0] && a.pos[1] == c.pos[1])) {
          b.score++;

          b.chars.push(c.char);

          if (c.game && game.chars.filter(a => a && a.game).length < nbots.length * 4 && game.bots.map(a => a.original).reduce((a, b) => a.includes(b) ? a : a.concat(b), []).length > 1)
              game.chars.push({
                  char: game.charPool[Math.random() * game.charPool.length | 0],
                  pos: game.randPos([0, 0]),
                  game: !0
              });

          game.chars[i] = null;
      }
  }

  game.chars = game.chars.filter(a => a);
  game.bots = nbots;
};

function drawRound(turns = 100000, log = 2, fps = 5, zoom = 50){
  var c, ctx, wdim, scale;

  document.body.innerHTML = "<canvas></canvas>";

  c = document.body.firstChild;
  c.style.position = "absolute";
  c.style.top = "0";
  c.style.left = "0";
  c.style.zIndex = "2";
  ctx = c.getContext("2d");

  game.records = new Array(botData.length).fill(0);
  game.log = log;
  game.pause = !1;
  game.fps = fps;

  (window.onresize = function() {
      wdim = [window.innerWidth || 600, window.innerHeight || 400];
      scale = Math.ceil(wdim[1] / zoom);
      c.width = wdim[0];
      c.height = wdim[1];
  })();

  window.onkeydown = function() {
      var key = event.code;
      if (key == "Escape")
          game.pause = !game.pause;
      if (key == "ArrowLeft" && game.fps > 1)
          game.fps -= 1;
      if (key == "ArrowRight")
          game.fps += 1;
  };

  game.debug = function() {
      ctx.clearRect(0, 0, wdim[0], wdim[1]);
      ctx.textBaseline = "middle";
      ctx.textAlign = "center";
      ctx.font = Math.floor(scale * 0.6) + "px monospace";
      for (let x = -Math.ceil(wdim[0] / 2 / scale), i = wdim[0] / 2 - (Math.ceil(wdim[0] / 2 / scale) - 0.5) * scale; i <= wdim[0]; i += scale, x++) {
          for (let b, c, y = -Math.ceil(wdim[1] / 2 / scale), j = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; j <= wdim[1]; j += scale, y++) {
              if ((c = game.chars.filter(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))).length) {
                  for (let k = 0; k < c.length; k++)
                      ctx.fillText(JSON.stringify(c[k].char).slice(1, -1).replace(/\\"/, "\"").replace(/\\\\/, "\\").replace(/ /, "_"), i + scale / 2, j + scale / 2);
              }
              if (b = game.bots.find(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))) {
                  ctx.fillStyle = b.color;
                  ctx.fillRect(i, j, scale, scale);
                  ctx.fillStyle = "#000000";
              }
          }
          ctx.lineWidth = 0.1;
          ctx.beginPath();
          ctx.moveTo(i, 0);
          ctx.lineTo(i, wdim[1]);
          ctx.stroke();
      }
      for (let i = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; i <= wdim[1]; i += scale) {
          ctx.beginPath();
          ctx.moveTo(0, i);
          ctx.lineTo(wdim[0], i);
          ctx.stroke();
      }
      ctx.fillRect(wdim[0] / 2 - 3, wdim[1] / 2 - 3, 7, 7);
  };

  runRound(turns);
}

function runGame(rounds = 1, turns = 100000, log = 0) {
  game.records = new Array(botData.length).fill(0);
  game.log = log;

  for (let i = 0; i < rounds; i++)
      runRound(turns, 0);

  console.log("Game Conclusion:\n" + botData.map((a, b) => [a.name, game.records[b]]).sort((a, b) => b[1] - a[1]).map(a => "[" + a[1] + "] " + a[0]).join("\n"));
}

};

drawRound(/*turns =*/ 1000, /*log =*/ 2, /*fps =*/ 10,/* zoom =*/ 50);
```

```
<body></body>
```

# Answer
> 5 votes
# The Caveman (151 bytes)

This is Caveman. Very primitive. Hunt if strong. Eat if weak. Ooga-Booga.

```
{
  name: "The Caveman",
  color: "#FF0000",
  run:()=>{w=bots().sort((a,b)=>a.score-b.score)[0];return self().score<=w.score?dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos):dirTo(w.pos)}
}

```

Try against a bunch of Honnolds:

```
var botData = [
  {
name: "Honnold",
color: "#0000FF",
run:()=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)
  },
  {
name: "Honnold",
color: "#0000FF",
run:()=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)
  },
  {
name: "Honnold",
color: "#0000FF",
run:()=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)
  },

  {
    name: "The Caveman",
    color: "#FF0000",
    run:()=>{w=bots().sort((a,b)=>a.score-b.score)[0];return self().score<=w.score?dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos):dirTo(w.pos)}
  },

  {
    name: "The Caveman",
    color: "#FF0000",
    run:()=>{w=bots().sort((a,b)=>a.score-b.score)[0];return self().score<=w.score?dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos):dirTo(w.pos)}
  },

  {
    name: "The Caveman",
    color: "#FF0000",
    run:()=>{w=bots().sort((a,b)=>a.score-b.score)[0];return self().score<=w.score?dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos):dirTo(w.pos)}
  }
];

{
var game = {
  randPos: (center, any = !1, uid = 0, owner = 0, p = 0.1) => {
      var theta, radius, pos;

      do {
          theta = Math.random() * Math.PI * 2;
          radius = 0;

          while (Math.random() > p)
              radius++;

          pos = [Math.trunc(center[0] + Math.cos(theta) * radius), Math.trunc(center[1] + Math.sin(theta) * radius)];
      } while (!any && game.bots.find(a => a && a.uid != uid && Math.abs(a.pos[0] - pos[0]) + Math.abs(a.pos[1] - pos[1]) < (a.uid == owner ? 3 : 4)));

      return pos;
  },
  debug: function(){},
  log: 0 // 0 = NONE, 1 = SUMMARY, 2 = ALL
};

var north = () => ["north"];
var east = () => ["east"];
var south = () => ["south"];
var west = () => ["west"];

var build = code => ["worker", code];

var drop = {
  north: char => ["drop.north", char],
  east: char => ["drop.east", char],
  south: char => ["drop.south", char],
  west: char => ["drop.west", char]
};

var bots = () => game.bots.map(a => ({
  uid: a.uid,
  owner: a.owner,
  original: a.original,
  score: a.score,
  pos: [...a.pos],
  chars: game.uid == a.uid ? [...a.chars] : undefined,
  source: game.uid == a.uid ? a.source : undefined
})).sort((a, b) => a.uid - b.uid);

var chars = () => game.chars.map(a => ({
  char: a.char,
  pos: [...a.pos]
}));

var self = () => {
  var bot = game.bots.find(a => a.uid == game.uid);
  return bot ? {
      uid: bot.uid,
      owner: bot.owner,
      original: bot.original,
      score: bot.score,
      pos: [...bot.pos],
      chars: [...bot.chars],
      source: bot.source
  } : null;
};

var owner = () => {
  var bot = game.bots.find(a => a.uid == game.bots.find(b => b.uid == game.uid).owner);
  return bot ? {
      uid: bot.uid,
      owner: bot.owner,
      original: bot.original,
      score: bot.score,
      pos: [...bot.pos],
      chars: [...bot.chars],
      source: bot.source
  } : null;
};

var center = () => game.center;
var turn = () => game.turns;

var at = pos => ({
  bot: (game.bots.find(b => b.pos[0] == pos[0] && b.pos[1] == pos[1]) || {uid: null}).uid,
  chars: chars().filter(c => c.pos[0] == pos[0] && c.pos[1] == pos[1])
});

var dir = (posFrom, pos) => {
  if (Math.abs(posFrom[0] - pos[0]) <= Math.abs(posFrom[1] - pos[1]))
      return posFrom[1] < pos[1] ? ["north"] : ["south"];
  else
      return posFrom[0] < pos[0] ? ["west"] : ["east"];
};

var dirTo = pos => {
  var bot = game.bots.find(a => a.uid == game.uid);
  if (Math.abs(pos[0] - bot.pos[0]) <= Math.abs(pos[1] - bot.pos[1]))
      return pos[1] < bot.pos[1] ? ["north"] : ["south"];
  else
      return pos[0] < bot.pos[0] ? ["west"] : ["east"];
};

var dist = (posFrom, pos) => {
  return Math.abs(posFrom[0] - pos[0]) + Math.abs(posFrom[1] - pos[1]);
};

var distTo = pos => {
  var bot = game.bots.find(a => a.uid == game.uid);
  return Math.abs(pos[0] - bot.pos[0]) + Math.abs(pos[1] - bot.pos[1]);
};

async function runRound(turns = 100000) {
  var uids = [];

  game.perf = performance.now();

  for (let i = 1; i <= botData.length; i++)
      uids[i - 1] = i;

  for (let j, i = uids.length - 1; i > 0; i--) {
      j = Math.floor(Math.random() * (i + 1));
      [uids[i], uids[j]] = [uids[j], uids[i]];
  }

  game.bots = [];
  game.chars = [];
  game.records = game.records || [];
  game.uids = [];

  for (let i = 0; i < botData.length; i++) {
      game.bots[i] = {
          uid: uids[i],
          owner: uids[i],
          original: uids[i],
          score: Math.floor(botData[i].run.toString().length * -1 / 2),
          chars: [],
          pos: game.randPos([0, 0]),
          source: botData[i].run.toString(),
          run: botData[i].run,
          storage: {},
          name: botData[i].name || "Bot",
          color: botData[i].color || "#000000"
      };

      game.uids[uids[i]] = i;
      game.records[i] = game.records[i] || 0;
  }

  game.center = [
      game.bots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0),
      game.bots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0)
  ];

  game.charPool = game.bots.map(a => a.source).join("");

  for (let i = 0; i < botData.length * 4; i++)
      game.chars.push({
          char: game.charPool[Math.random() * game.charPool.length | 0],
          pos: game.randPos([0, 0]),
          game: !0
      });

  game.cuid = botData.length + 1;
  game.turns = 0;

  if (!game.fps) {
      while (game.chars.length && game.bots.length && game.turns < turns) {
          runTurn();

          game.turns++;
      }
  } else {
      game.debug();

      while (game.chars.length && game.bots.length && game.turns < turns) {
          await new Promise(function(resolve) {
              setTimeout(resolve, 1000 / game.fps);
          });

          if (!game.pause) {
              runTurn();

              game.debug();
              game.turns++;
          }
      }
  }
  bots().map(b => game.records[game.uids[b.original]] += b.score);

  if (game.log)
      console.log("Round Completed (" + ((performance.now() - game.perf) / 1000).toFixed(3) + "s):\n" + game.bots.map(a => a).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join("\n"));
}

function runTurn() {
  var cbots = [];
  var npos = [];
  var nposl = [];
  var nbots = [];

  for (let b, p, m, i = 0; i < game.bots.length; i++) {
      b = game.bots[i];

      game.uid = b.uid;

      try {
          m = b.run(b.storage);
      } catch(e) {
          m = ["dead"];

          if (game.log == 2)
              console.warn("[" + game.turns + "] Error: " + b.name + "\n" + (e.stack || e.message));

          for (let j = 0; j < b.chars.length; j++)
              game.chars.push({
                  char: b.chars[j],
                  pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                  game: !1
              });
      }

      if (!Array.isArray(m))
          m = [];

      if (m[0] == "north")
          p = [b.pos[0], b.pos[1] - 1];
      else if (m[0] == "east")
          p = [b.pos[0] + 1, b.pos[1]];
      else if (m[0] == "south")
          p = [b.pos[0], b.pos[1] + 1];
      else if (m[0] == "west")
          p = [b.pos[0] - 1, b.pos[1]];
      else
          p = [...b.pos];

      if (m[0] != "dead")
          npos.push({
              bot: b.uid,
              pos: p
          });

      if (m[0] == "worker" && m[1].split("").reduce((c, d, e) => d && (e = c.indexOf(d)) != -1 ? c.filter((f, g) => g != e) : null, [...b.chars])) {
          p = game.randPos(b.pos, !1, 0, b.uid);

          try {
              cbots.push({
                  uid: game.cuid,
                  owner: b.uid,
                  original: b.original,
                  score: 0,
                  chars: [],
                  pos: p,
                  source: m[1],
                  run: eval(m[1]),
                  storage: {},
                  name: b.name + "*",
                  color: b.color
              });

              npos.push({
                  bot: game.cuid++,
                  pos: p
              });

              b.score -= Math.floor(m[1].length / 2);

              for (let n, j = 0; j < m[1].length; j++) {
                  n = b.chars.indexOf(m[1][j]);

                  b.chars = b.chars.slice(0, n).concat(b.chars.slice(n + 1));
              }

              if (game.log == 2)
                  console.log("[" + game.turns + "] New Worker: " + b.name);
          } catch(e) {
              if (game.log == 2)
                  console.warn("[" + game.turns + "] Invalid Worker: " + b.name + "\n" + (e.stack || e.message));
          }
      }

      if (typeof m[0] == "string" && m[0].match(/^drop.(north|east|south|west)$/) && b.chars.includes(m[1])) {
          b.score--;

          for (let j = 0; j < b.chars.length; j++) {
              if (b.chars[j] == m[1]) {
                  b.chars = b.chars.slice(0, j) + b.chars.slice(j + 1);

                  break;
              }
          }

          if (m[0] == "drop.north")
              p = [b.pos[0], b.pos[1] - 1];
          else if (m[0] == "drop.east")
              p = [b.pos[0] + 1, b.pos[1]];
          else if (m[0] == "drop.south")
              p = [b.pos[0], b.pos[1] + 1];
          else if (m[0] == "drop.west")
              p = [b.pos[0] - 1, b.pos[1]];

          game.chars.push({
              char: m[1],
              pos: p,
              game: !1
          });
      }
  }

  game.bots.push(...cbots);

  for (let f, i = 0; i < npos.length; i++) {
      if (!(f = nposl.find(a => a.pos[0] == npos[i].pos[0] && a.pos[1] == npos[i].pos[1])))
          nposl.push(f = {
              pos: [...npos[i].pos],
              bots: []
          });

      f.bots.push(npos[i].bot);
  }

  for (let n, m, b, i = 0; i < nposl.length; i++) {
      n = nposl[i];

      if (n.bots.length > 1) {
          m = Math.max(...n.bots.map(a => game.bots.find(b => b.uid == a).score));

          if (game.bots.filter(a => n.bots.includes(a.uid) && a.score == m).length > 1) {
              m += 1;

              if (game.log == 2)
                  console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
          } else {
              if (game.log == 2)
                  console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
          }

          for (let j = 0; j < n.bots.length; j++) {
              b = game.bots.find(a => a.uid == n.bots[j]);

              if (b.score < m)
                  for (let k = 0; k < b.chars.length; k++)
                      game.chars.push({
                          char: b.chars[k],
                          pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                          game: !1
                      });
              else
                  nbots.push({
                      uid: b.uid,
                      owner: b.owner,
                      original: b.original,
                      score: b.score,
                      chars: [...b.chars],
                      pos: n.pos,
                      source: b.source,
                      run: b.run,
                      storage: b.storage,
                      name: b.name,
                      color: b.color
                  });
          }
      } else {
          b = game.bots.find(a => a.uid == n.bots[0]);

          nbots.push({
              uid: b.uid,
              owner: b.owner,
              original: b.original,
              score: b.score,
              chars: [...b.chars],
              pos: n.pos,
              source: b.source,
              run: b.run,
              storage: b.storage,
              name: b.name,
              color: b.color
          });
      }
  }

  game.center = [
      nbots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0),
      nbots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0)
  ];

  game.charPool = nbots.map(a => a.source).join("");

  for (let b, c, i = 0; i < game.chars.length; i++) {
      c = game.chars[i];

      if (b = nbots.find(a => a.pos[0] == c.pos[0] && a.pos[1] == c.pos[1])) {
          b.score++;

          b.chars.push(c.char);

          if (c.game && game.chars.filter(a => a && a.game).length < nbots.length * 4 && game.bots.map(a => a.original).reduce((a, b) => a.includes(b) ? a : a.concat(b), []).length > 1)
              game.chars.push({
                  char: game.charPool[Math.random() * game.charPool.length | 0],
                  pos: game.randPos([0, 0]),
                  game: !0
              });

          game.chars[i] = null;
      }
  }

  game.chars = game.chars.filter(a => a);
  game.bots = nbots;
};

function drawRound(turns = 100000, log = 2, fps = 60, zoom = 50){
  var c, ctx, wdim, scale;

  document.body.innerHTML = "<canvas></canvas>";

  c = document.body.firstChild;
  c.style.position = "absolute";
  c.style.top = "0";
  c.style.left = "0";
  c.style.zIndex = "2";
  ctx = c.getContext("2d");

  game.records = new Array(botData.length).fill(0);
  game.log = log;
  game.pause = !1;
  game.fps = fps;

  (window.onresize = function() {
      wdim = [window.innerWidth || 600, window.innerHeight || 400];
      scale = Math.ceil(wdim[1] / zoom);
      c.width = wdim[0];
      c.height = wdim[1];
  })();

  window.onkeydown = function() {
      var key = event.code;
      if (key == "Escape")
          game.pause = !game.pause;
      if (key == "ArrowLeft" && game.fps > 1)
          game.fps -= 1;
      if (key == "ArrowRight")
          game.fps += 1;
  };

  game.debug = function() {
      ctx.clearRect(0, 0, wdim[0], wdim[1]);
      ctx.textBaseline = "middle";
      ctx.textAlign = "center";
      ctx.font = Math.floor(scale * 0.6) + "px monospace";
      for (let x = -Math.ceil(wdim[0] / 2 / scale), i = wdim[0] / 2 - (Math.ceil(wdim[0] / 2 / scale) - 0.5) * scale; i <= wdim[0]; i += scale, x++) {
          for (let b, c, y = -Math.ceil(wdim[1] / 2 / scale), j = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; j <= wdim[1]; j += scale, y++) {
              if ((c = game.chars.filter(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))).length) {
                  for (let k = 0; k < c.length; k++)
                      ctx.fillText(JSON.stringify(c[k].char).slice(1, -1).replace(/\\"/, "\"").replace(/\\\\/, "\\").replace(/ /, "_"), i + scale / 2, j + scale / 2);
              }
              if (b = game.bots.find(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))) {
                  ctx.fillStyle = b.color;
                  ctx.fillRect(i, j, scale, scale);
                  ctx.fillStyle = "#000000";
              }
          }
          ctx.lineWidth = 0.1;
          ctx.beginPath();
          ctx.moveTo(i, 0);
          ctx.lineTo(i, wdim[1]);
          ctx.stroke();
      }
      for (let i = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; i <= wdim[1]; i += scale) {
          ctx.beginPath();
          ctx.moveTo(0, i);
          ctx.lineTo(wdim[0], i);
          ctx.stroke();
      }
      ctx.fillRect(wdim[0] / 2 - 3, wdim[1] / 2 - 3, 7, 7);
  };

  runRound(turns);
}

function runGame(rounds = 1, turns = 100000, log = 0) {
  game.records = new Array(botData.length).fill(0);
  game.log = log;

  for (let i = 0; i < rounds; i++)
      runRound(turns, 0);

  console.log("Game Conclusion:\n" + botData.map((a, b) => [a.name, game.records[b]]).sort((a, b) => b[1] - a[1]).map(a => "[" + a[1] + "] " + a[0]).join("\n"));
}

};

drawRound(/*turns =*/ 1000000, /*log =*/ 2, /*fps =*/ 600,/* zoom =*/ 50);
```

```
<body></body>
```

# Answer
> 3 votes
# Rabbit, 56 bytes(-28 points)

```
{
    name: "Rabbit",
    color: "#FFC0CB",
    run:_=>turn()%1e3?dirTo(chars()[0].pos):build(self().source)
}

```

Why rabbit? Because it runs away, forages for food, and then immediately reproduces.

Fixed with some help from Redwolf, and now works after a fix to the controller.

```
var botData = [
  {
name: "Honnold",
color: "#0000FF",
run:()=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)
  },

  {
    name: "The Caveman",
    color: "#FF0000",
    run:_=>{w=bots().sort((a,b)=>a.score-b.score)[0];return self().score<=w.score?dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos):dirTo(w.pos)}
  },
    {
    name: "Centrist",
    color: "#666666",
    run:_=>dirTo(center())
  },
  {
    name: "True Neutral",
    color: "#400000",
    run: _=>dirTo(chars().sort((a,b)=>dist(a.pos,[0,0])-dist(b.pos,[0,0])+distTo(a.pos)-distTo(b.pos))[0].pos)
},{
    name: "Rabbit",
    color: "#FFC0CB",
    run:_=>turn()%1e3?dirTo(chars()[0].pos):build(self().source)
}

];

var game = {
    randPos: (center, any = !1, uid = 0, owner = 0, p = 0.1) => {
        var theta, radius, pos;

        do {
            theta = Math.random() * Math.PI * 2;
            radius = 0;

            while (Math.random() > p)
                radius++;

            pos = [Math.trunc(center[0] + Math.cos(theta) * radius), Math.trunc(center[1] + Math.sin(theta) * radius)];
        } while (!any && game.bots.find(a => a && a.uid != uid && Math.abs(a.pos[0] - pos[0]) + Math.abs(a.pos[1] - pos[1]) < (a.uid == owner ? 3 : 4)));

        return pos;
    },
    debug: function(){},
    log: 0 // 0 = NONE, 1 = SUMMARY, 2 = ALL
};

var north = () => ["north"];
var east = () => ["east"];
var south = () => ["south"];
var west = () => ["west"];

var build = code => ["worker", code];

var drop = {
    north: char => ["drop.north", char],
    east: char => ["drop.east", char],
    south: char => ["drop.south", char],
    west: char => ["drop.west", char]
};

var bots = () => game.bots.map(a => ({
    uid: a.uid,
    owner: a.owner,
    original: a.original,
    score: a.score,
    pos: [...a.pos],
    chars: game.uid == a.uid ? [...a.chars] : undefined,
    source: game.uid == a.uid ? a.source : undefined
})).sort((a, b) => a.uid - b.uid);

var chars = () => game.chars.map(a => ({
    char: a.char,
    pos: [...a.pos]
}));

var self = () => {
    var bot = game.bots.find(a => a.uid == game.uid);

    return bot ? {
        uid: bot.uid,
        owner: bot.owner,
        original: bot.original,
        score: bot.score,
        pos: [...bot.pos],
        chars: [...bot.chars],
        source: bot.source
    } : null;
};

var owner = () => {
    var bot = game.bots.find(a => a.uid == game.bots.find(b => b.uid == game.uid).owner);

    return bot ? {
        uid: bot.uid,
        owner: bot.owner,
        original: bot.original,
        score: bot.score,
        pos: [...bot.pos],
        chars: [...bot.chars],
        source: bot.source
    } : null;
};

var center = () => game.center;
var turn = () => game.turns;

var at = pos => ({
    bot: (game.bots.find(b => b.pos[0] == pos[0] && b.pos[1] == pos[1]) || {uid: null}).uid,
    chars: chars().filter(c => c.pos[0] == pos[0] && c.pos[1] == pos[1])
});

var dir = (posFrom, pos) => {
    if (Math.abs(posFrom[0] - pos[0]) <= Math.abs(posFrom[1] - pos[1]))
        return posFrom[1] < pos[1] ? ["north"] : ["south"];
    else
        return posFrom[0] < pos[0] ? ["west"] : ["east"];
};

var dirTo = pos => {
    var bot = game.bots.find(a => a.uid == game.uid);
    if (Math.abs(pos[0] - bot.pos[0]) <= Math.abs(pos[1] - bot.pos[1]))
        return pos[1] < bot.pos[1] ? ["north"] : ["south"];
    else
        return pos[0] < bot.pos[0] ? ["west"] : ["east"];
};

var dist = (posFrom, pos) => {
    return Math.abs(posFrom[0] - pos[0]) + Math.abs(posFrom[1] - pos[1]);
};

var distTo = pos => {
    var bot = game.bots.find(a => a.uid == game.uid);
    return Math.abs(pos[0] - bot.pos[0]) + Math.abs(pos[1] - bot.pos[1]);
};

async function runRound(turns = 100000) {
    var uids = [];

    game.perf = performance.now();

    for (let i = 1; i <= botData.length; i++)
        uids[i - 1] = i;

    for (let j, i = uids.length - 1; i > 0; i--) {
        j = Math.floor(Math.random() * (i + 1));
        [uids[i], uids[j]] = [uids[j], uids[i]];
    }

    game.bots = [];
    game.chars = [];
    game.records = game.records || [];
    game.uids = [];

    for (let i = 0; i < botData.length; i++) {
        game.bots[i] = {
            uid: uids[i],
            owner: uids[i],
            original: uids[i],
            score: Math.floor(botData[i].run.toString().length * -1 / 2),
            chars: [],
            pos: game.randPos([0, 0]),
            source: botData[i].run.toString(),
            run: botData[i].run,
            storage: {},
            name: botData[i].name || "Bot",
            color: botData[i].color || "#000000"
        };

        game.uids[uids[i]] = i;
        game.records[i] = game.records[i] || 0;
    }

    game.center = [
        game.bots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0),
        game.bots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0)
    ];

    game.charPool = game.bots.map(a => a.source).join("");

    for (let i = 0; i < botData.length * 4; i++)
        game.chars.push({
            char: game.charPool[Math.random() * game.charPool.length | 0],
            pos: game.randPos([0, 0]),
            game: !0
        });

    game.cuid = botData.length + 1;
    game.turns = 0;

    if (!game.fps) {
        while (game.chars.length && game.bots.length && game.turns < turns) {
            runTurn();

            game.turns++;
        }
    } else {
        game.debug();

        while (game.chars.length && game.bots.length && game.turns < turns) {
            await new Promise(function(resolve) {
                setTimeout(resolve, 1000 / game.fps);
            });

            if (!game.pause) {
                runTurn();

                game.debug();
                game.turns++;
            }
        }
    }

    game.bots.map(b => game.records[game.uids[b.original]] += b.score);

    if (game.log)
        console.log("Round Completed (" + ((performance.now() - game.perf) / 1000).toFixed(3) + "s):\n" + game.bots.map(a => a).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join("\n"));
}

function runTurn() {
    var cbots = [];
    var npos = [];
    var nposl = [];
    var nbots = [];

    for (let b, p, m, i = 0; i < game.bots.length; i++) {
        b = game.bots[i];

        game.uid = b.uid;

        try {
            m = b.run(b.storage);
        } catch(e) {
            m = ["dead"];

            if (game.log == 2)
                console.warn("[" + game.turns + "] Error: " + b.name + "\n" + (e.stack || e.message));

            for (let j = 0; j < b.chars.length; j++)
                game.chars.push({
                    char: b.chars[j],
                    pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                    game: !1
                });
        }

        if (!Array.isArray(m))
            m = [];

        if (m[0] == "north")
            p = [b.pos[0], b.pos[1] - 1];
        else if (m[0] == "east")
            p = [b.pos[0] + 1, b.pos[1]];
        else if (m[0] == "south")
            p = [b.pos[0], b.pos[1] + 1];
        else if (m[0] == "west")
            p = [b.pos[0] - 1, b.pos[1]];
        else
            p = [...b.pos];

        if (m[0] != "dead")
            npos.push({
                bot: b.uid,
                pos: p
            });

        if (m[0] == "worker" && m[1].split("").reduce((c, d, e) => c && d && (e = c.indexOf(d)) != -1 ? c.filter((f, g) => g != e) : null, [...b.chars])) {
            p = game.randPos(b.pos, !1, 0, b.uid);

            try {
                cbots.push({
                    uid: game.cuid,
                    owner: b.uid,
                    original: b.original,
                    score: 0,
                    chars: [],
                    pos: p,
                    source: m[1],
                    run: eval(m[1]),
                    storage: {},
                    name: b.name + "*",
                    color: b.color
                });

                npos.push({
                    bot: game.cuid++,
                    pos: p
                });

                b.score -= Math.floor(m[1].length / 2);

                for (let n, j = 0; j < m[1].length; j++) {
                    n = b.chars.indexOf(m[1][j]);

                    b.chars = b.chars.slice(0, n).concat(b.chars.slice(n + 1));
                }

                if (game.log == 2)
                    console.log("[" + game.turns + "] New Worker: " + b.name);
            } catch(e) {
                if (game.log == 2)
                    console.warn("[" + game.turns + "] Invalid Worker: " + b.name + "\n" + (e.stack || e.message));
            }
        }

        if (typeof m[0] == "string" && m[0].match(/^drop.(north|east|south|west)$/) && b.chars.includes(m[1])) {
            b.score--;

            for (let j = 0; j < b.chars.length; j++) {
                if (b.chars[j] == m[1]) {
                    b.chars = b.chars.slice(0, j) + b.chars.slice(j + 1);

                    break;
                }
            }

            if (m[0] == "drop.north")
                p = [b.pos[0], b.pos[1] - 1];
            else if (m[0] == "drop.east")
                p = [b.pos[0] + 1, b.pos[1]];
            else if (m[0] == "drop.south")
                p = [b.pos[0], b.pos[1] + 1];
            else if (m[0] == "drop.west")
                p = [b.pos[0] - 1, b.pos[1]];

            game.chars.push({
                char: m[1],
                pos: p,
                game: !1
            });
        }
    }

    game.bots.push(...cbots);

    for (let f, i = 0; i < npos.length; i++) {
        if (!(f = nposl.find(a => a.pos[0] == npos[i].pos[0] && a.pos[1] == npos[i].pos[1])))
            nposl.push(f = {
                pos: [...npos[i].pos],
                bots: []
            });

        f.bots.push(npos[i].bot);
    }

    for (let n, m, b, i = 0; i < nposl.length; i++) {
        n = nposl[i];

        if (n.bots.length > 1) {
            m = Math.max(...n.bots.map(a => game.bots.find(b => b.uid == a).score));

            if (game.bots.filter(a => n.bots.includes(a.uid) && a.score == m).length > 1) {
                m += 1;

                if (game.log == 2)
                    console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
            } else {
                if (game.log == 2)
                    console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
            }

            for (let j = 0; j < n.bots.length; j++) {
                b = game.bots.find(a => a.uid == n.bots[j]);

                if (b.score < m) {
                    for (let k = 0; k < b.chars.length; k++)
                        game.chars.push({
                            char: b.chars[k],
                            pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                            game: !1
                        });

                    game.records[game.uids[b.original]] += b.score;
                } else {
                    nbots.push({
                        uid: b.uid,
                        owner: b.owner,
                        original: b.original,
                        score: b.score,
                        chars: [...b.chars],
                        pos: n.pos,
                        source: b.source,
                        run: b.run,
                        storage: b.storage,
                        name: b.name,
                        color: b.color
                    });
                }
            }
        } else {
            b = game.bots.find(a => a.uid == n.bots[0]);

            nbots.push({
                uid: b.uid,
                owner: b.owner,
                original: b.original,
                score: b.score,
                chars: [...b.chars],
                pos: n.pos,
                source: b.source,
                run: b.run,
                storage: b.storage,
                name: b.name,
                color: b.color
            });
        }
    }

    game.center = [
        nbots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0),
        nbots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0)
    ];

    game.charPool = nbots.map(a => a.source).join("");

    for (let b, c, i = 0; i < game.chars.length; i++) {
        c = game.chars[i];

        if (b = nbots.find(a => a.pos[0] == c.pos[0] && a.pos[1] == c.pos[1])) {
            b.score++;

            b.chars.push(c.char);

            if (c.game && game.chars.filter(a => a && a.game).length < nbots.length * 4 && game.bots.map(a => a.original).reduce((a, b) => a.includes(b) ? a : a.concat(b), []).length > 1)
                game.chars.push({
                    char: game.charPool[Math.random() * game.charPool.length | 0],
                    pos: game.randPos([0, 0]),
                    game: !0
                });

            game.chars[i] = null;
        }
    }

    game.chars = game.chars.filter(a => a);
    game.bots = nbots;
};

function drawRound(turns = 100000, log = 2, fps = 5, zoom = 50) {
    var c, ctx, wdim, scale;

    document.body.innerHTML = "<canvas></canvas>";

    c = document.body.firstChild;
    c.style.position = "absolute";
    c.style.top = "0";
    c.style.left = "0";
    c.style.zIndex = "2";
    ctx = c.getContext("2d");

    game.records = new Array(botData.length).fill(0);
    game.log = log;
    game.pause = !1;
    game.fps = fps;

    (window.onresize = function() {
        wdim = [window.innerWidth || 600, window.innerHeight || 400];
        scale = Math.ceil(wdim[1] / zoom);
        c.width = wdim[0];
        c.height = wdim[1];
    })();

    window.onkeydown = function() {
        var key = event.code;
        if (key == "Escape")
            game.pause = !game.pause;
        if (key == "ArrowLeft" && game.fps > 1)
            game.fps -= 1;
        if (key == "ArrowRight")
            game.fps += 1;
    };

    game.debug = function() {
        ctx.clearRect(0, 0, wdim[0], wdim[1]);
        ctx.textBaseline = "middle";
        ctx.textAlign = "center";
        ctx.font = Math.floor(scale * 0.6) + "px monospace";
        for (let x = -Math.ceil(wdim[0] / 2 / scale), i = wdim[0] / 2 - (Math.ceil(wdim[0] / 2 / scale) - 0.5) * scale; i <= wdim[0]; i += scale, x++) {
            for (let b, c, y = -Math.ceil(wdim[1] / 2 / scale), j = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; j <= wdim[1]; j += scale, y++) {
                if ((c = game.chars.filter(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))).length) {
                    for (let k = 0; k < c.length; k++)
                        ctx.fillText(JSON.stringify(c[k].char).slice(1, -1).replace(/\\"/, "\"").replace(/\\\\/, "\\").replace(/ /, "_"), i + scale / 2, j + scale / 2);
                }
                if (b = game.bots.find(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))) {
                    ctx.fillStyle = b.color;
                    ctx.fillRect(i, j, scale, scale);
                    ctx.fillStyle = "#000000";
                }
            }
            ctx.beginPath();
            ctx.moveTo(i, 0);
            ctx.lineTo(i, wdim[1]);
            ctx.stroke();
        }
        for (let i = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; i <= wdim[1]; i += scale) {
            ctx.beginPath();
            ctx.moveTo(0, i);
            ctx.lineTo(wdim[0], i);
            ctx.stroke();
        }
        ctx.fillRect(wdim[0] / 2 - 3, wdim[1] / 2 - 3, 7, 7);
    };

    runRound(turns);
}

function runGame(rounds = 1, turns = 100000, log = 0) {
    game.records = new Array(botData.length).fill(0);
    game.log = log;

    for (let i = 0; i < rounds; i++)
        runRound(turns, 0);

    console.log("Game Conclusion:\n" + botData.map((a, b) => [a.name, game.records[b]]).sort((a, b) => b[1] - a[1]).map(a => "[" + a[1] + "] " + a[0]).join("\n"));
}

drawRound(/*turns =*/ 1000000, /*log =*/ 2, /*fps =*/ 600,/* zoom =*/ 50);
```

# Answer
> 3 votes
# The Luggage

```
{
    name: "The Luggage",
    color: "#8B4513",
    run:()=>dirTo(bots()[0].pos)
}

```

Bonus points for guessing the name origin

```
var botData = [
{
    name: "ExampleBot",
    color: "#aaaaaa",
    run: () => dirTo(chars().sort((a, b) => dist(center(), a.pos) - dist(center(), b.pos))[0].pos)
},
{
    name: "Honnold",
    color: "#0000FF",
    run:()=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)
},
{
    name: "The Caveman",
    color: "#FF0000",
    run:()=>{w=bots().sort((a,b)=>a.score-b.score)[0];return self().score<=w.score?dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos):dirTo(w.pos)}
},
{
    name: "True Neutral",
    color: "#400000",
    run: _=>dirTo(chars().sort((a,b)=>dist(a.pos,[0,0])-dist(b.pos,[0,0])+distTo(a.pos)-distTo(b.pos))[0].pos)
},
{
    name: "Centrist",
    color: "#666666",
    run:_=>dirTo(center())
},
{
name: "Rabbit",
    color: "#FFC0CB",
    run:_=>turn()%1e3?dirTo(chars()[0].pos):build(self().source)
},
{
name: "The Luggage",
color: "#8B4513",
run:()=>dirTo(bots()[0].pos)
}
];

// _=>{var r,d,e,l,I,N,P,D=distTo,s=self(),p=s[P="pos"],w="_=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)",n=w.split``;s.chars.map(c=>n[n[N="indexOf"](c)]="");if(!n.find(_=>_))return build(w);r=chars().filter(c=>!bots().find(b=>b.uid!=s.uid&&dist(b[P],c[P])-D(c[P])<1));if(r.length)return dirTo(r.sort((a,b)=>D(a[P])-D(b[P])+(n[I="includes"](b)-n[I](a))*10)[0][P]);l={n:-p[1],e:p[0],s:p[1],w:-p[0]};d="nesw".split``.sort((a,b)=>l[a]-l[b]);e=bots().filter(b=>b.uid!=s.uid&&b.score>=s.score&&D(b[P])<5).map(b=>d[d[N](dirTo(b[P])[0][0])]="");return[north,east,south,west]["nesw"[N](d.find(x=>x))]()}

var game = {
randPos: (center, any = !1, uid = 0, owner = 0, p = 0.1) => {
    var theta, radius, pos;

    do {
        theta = Math.random() * Math.PI * 2;
        radius = 0;

        while (Math.random() > p)
            radius++;

        pos = [Math.trunc(center[0] + Math.cos(theta) * radius), Math.trunc(center[1] + Math.sin(theta) * radius)];
    } while (!any && game.bots.find(a => a && a.uid != uid && Math.abs(a.pos[0] - pos[0]) + Math.abs(a.pos[1] - pos[1]) < (a.uid == owner ? 3 : 4)));

    return pos;
},
debug: function(){},
log: 0 // 0 = NONE, 1 = SUMMARY, 2 = ALL
};

var north = () => ["north"];
var east = () => ["east"];
var south = () => ["south"];
var west = () => ["west"];

var build = code => ["worker", code];

var drop = {
north: char => ["drop.north", char],
east: char => ["drop.east", char],
south: char => ["drop.south", char],
west: char => ["drop.west", char]
};

var bots = () => game.bots.map(a => ({
uid: a.uid,
owner: a.owner,
original: a.original,
score: a.score,
pos: [...a.pos],
chars: game.uid == a.uid ? [...a.chars] : undefined,
source: game.uid == a.uid ? a.source : undefined
})).sort((a, b) => a.uid - b.uid);

var chars = () => game.chars.map(a => ({
char: a.char,
pos: [...a.pos]
}));

var self = () => {
var bot = game.bots.find(a => a.uid == game.uid);

return bot ? {
    uid: bot.uid,
    owner: bot.owner,
    original: bot.original,
    score: bot.score,
    pos: [...bot.pos],
    chars: [...bot.chars],
    source: bot.source
} : null;
};

var owner = () => {
var bot = game.bots.find(a => a.uid == game.bots.find(b => b.uid == game.uid).owner);

return bot ? {
    uid: bot.uid,
    owner: bot.owner,
    original: bot.original,
    score: bot.score,
    pos: [...bot.pos],
    chars: [...bot.chars],
    source: bot.source
} : null;
};

var center = () => game.center;
var turn = () => game.turns;

var at = pos => ({
bot: (game.bots.find(b => b.pos[0] == pos[0] && b.pos[1] == pos[1]) || {uid: null}).uid,
chars: chars().filter(c => c.pos[0] == pos[0] && c.pos[1] == pos[1])
});

var dir = (posFrom, pos) => {
if (Math.abs(posFrom[0] - pos[0]) <= Math.abs(posFrom[1] - pos[1]))
    return posFrom[1] < pos[1] ? ["north"] : ["south"];
else
    return posFrom[0] < pos[0] ? ["west"] : ["east"];
};

var dirTo = pos => {
var bot = game.bots.find(a => a.uid == game.uid);
if (Math.abs(pos[0] - bot.pos[0]) <= Math.abs(pos[1] - bot.pos[1]))
    return pos[1] < bot.pos[1] ? ["north"] : ["south"];
else
    return pos[0] < bot.pos[0] ? ["west"] : ["east"];
};

var dist = (posFrom, pos) => {
return Math.abs(posFrom[0] - pos[0]) + Math.abs(posFrom[1] - pos[1]);
};

var distTo = pos => {
var bot = game.bots.find(a => a.uid == game.uid);
return Math.abs(pos[0] - bot.pos[0]) + Math.abs(pos[1] - bot.pos[1]);
};

async function runRound(turns = 100000) {
var uids = [];

game.perf = performance.now();

for (let i = 1; i <= botData.length; i++)
    uids[i - 1] = i;

for (let j, i = uids.length - 1; i > 0; i--) {
    j = Math.floor(Math.random() * (i + 1));
    [uids[i], uids[j]] = [uids[j], uids[i]];
}

game.bots = [];
game.chars = [];
game.records = game.records || [];
game.uids = [];

for (let i = 0; i < botData.length; i++) {
    game.bots[i] = {
        uid: uids[i],
        owner: uids[i],
        original: uids[i],
        score: Math.floor(botData[i].run.toString().length * -1 / 2),
        chars: [],
        pos: game.randPos([0, 0]),
        source: botData[i].run.toString(),
        run: botData[i].run,
        storage: {},
        name: botData[i].name || "Bot",
        color: botData[i].color || "#000000"
    };

    game.uids[uids[i]] = i;
    game.records[i] = game.records[i] || 0;
}

game.center = [
    game.bots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0),
    game.bots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0)
];

game.charPool = game.bots.map(a => a.source).join("");

for (let i = 0; i < botData.length * 4; i++)
    game.chars.push({
        char: game.charPool[Math.random() * game.charPool.length | 0],
        pos: game.randPos([0, 0]),
        game: !0
    });

game.cuid = botData.length + 1;
game.turns = 0;

if (!game.fps) {
    while (game.chars.length && game.bots.length && game.turns < turns) {
        runTurn();

        game.turns++;
    }
} else {
    game.debug();

    while (game.chars.length && game.bots.length && game.turns < turns) {
        await new Promise(function(resolve) {
            setTimeout(resolve, 1000 / game.fps);
        });

        if (!game.pause) {
            runTurn();

            game.debug();
            game.turns++;
        }
    }
}

game.bots.map(b => game.records[game.uids[b.original]] += b.score);

if (game.log)
    console.log("Round Completed (" + ((performance.now() - game.perf) / 1000).toFixed(3) + "s):\n" + game.bots.map(a => a).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join("\n"));
}

function runTurn() {
var cbots = [];
var npos = [];
var nposl = [];
var nbots = [];

for (let b, p, m, i = 0; i < game.bots.length; i++) {
    b = game.bots[i];

    game.uid = b.uid;

    try {
        m = b.run(b.storage);
    } catch(e) {
        m = ["dead"];

        if (game.log == 2)
            console.warn("[" + game.turns + "] Error: " + b.name + "\n" + (e.stack || e.message));

        for (let j = 0; j < b.chars.length; j++)
            game.chars.push({
                char: b.chars[j],
                pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                game: !1
            });
    }

    if (!Array.isArray(m))
        m = [];

    if (m[0] == "north")
        p = [b.pos[0], b.pos[1] - 1];
    else if (m[0] == "east")
        p = [b.pos[0] + 1, b.pos[1]];
    else if (m[0] == "south")
        p = [b.pos[0], b.pos[1] + 1];
    else if (m[0] == "west")
        p = [b.pos[0] - 1, b.pos[1]];
    else
        p = [...b.pos];

    if (m[0] != "dead")
        npos.push({
            bot: b.uid,
            pos: p
        });

    if (m[0] == "worker" && m[1].split("").reduce((c, d, e) => c && d && (e = c.indexOf(d)) != -1 ? c.filter((f, g) => g != e) : null, [...b.chars])) {
        p = game.randPos(b.pos, !1, 0, b.uid);

        try {
            cbots.push({
                uid: game.cuid,
                owner: b.uid,
                original: b.original,
                score: 0,
                chars: [],
                pos: p,
                source: m[1],
                run: eval(m[1]),
                storage: {},
                name: b.name + "*",
                color: b.color
            });

            npos.push({
                bot: game.cuid++,
                pos: p
            });

            b.score -= Math.floor(m[1].length / 2);

            for (let n, j = 0; j < m[1].length; j++) {
                n = b.chars.indexOf(m[1][j]);

                b.chars = b.chars.slice(0, n).concat(b.chars.slice(n + 1));
            }

            if (game.log == 2)
                console.log("[" + game.turns + "] New Worker: " + b.name);
        } catch(e) {
            if (game.log == 2)
                console.warn("[" + game.turns + "] Invalid Worker: " + b.name + "\n" + (e.stack || e.message));
        }
    }

    if (typeof m[0] == "string" && m[0].match(/^drop.(north|east|south|west)$/) && b.chars.includes(m[1])) {
        b.score--;

        for (let j = 0; j < b.chars.length; j++) {
            if (b.chars[j] == m[1]) {
                b.chars = b.chars.slice(0, j) + b.chars.slice(j + 1);

                break;
            }
        }

        if (m[0] == "drop.north")
            p = [b.pos[0], b.pos[1] - 1];
        else if (m[0] == "drop.east")
            p = [b.pos[0] + 1, b.pos[1]];
        else if (m[0] == "drop.south")
            p = [b.pos[0], b.pos[1] + 1];
        else if (m[0] == "drop.west")
            p = [b.pos[0] - 1, b.pos[1]];

        game.chars.push({
            char: m[1],
            pos: p,
            game: !1
        });
    }
}

game.bots.push(...cbots);

for (let f, i = 0; i < npos.length; i++) {
    if (!(f = nposl.find(a => a.pos[0] == npos[i].pos[0] && a.pos[1] == npos[i].pos[1])))
        nposl.push(f = {
            pos: [...npos[i].pos],
            bots: []
        });

    f.bots.push(npos[i].bot);
}

for (let n, m, b, i = 0; i < nposl.length; i++) {
    n = nposl[i];

    if (n.bots.length > 1) {
        m = Math.max(...n.bots.map(a => game.bots.find(b => b.uid == a).score));

        if (game.bots.filter(a => n.bots.includes(a.uid) && a.score == m).length > 1) {
            m += 1;

            if (game.log == 2)
                console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
        } else {
            if (game.log == 2)
                console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
        }

        for (let j = 0; j < n.bots.length; j++) {
            b = game.bots.find(a => a.uid == n.bots[j]);

            if (b.score < m) {
                for (let k = 0; k < b.chars.length; k++)
                    game.chars.push({
                        char: b.chars[k],
                        pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                        game: !1
                    });

                game.records[game.uids[b.original]] += b.score;
            } else {
                nbots.push({
                    uid: b.uid,
                    owner: b.owner,
                    original: b.original,
                    score: b.score,
                    chars: [...b.chars],
                    pos: n.pos,
                    source: b.source,
                    run: b.run,
                    storage: b.storage,
                    name: b.name,
                    color: b.color
                });
            }
        }
    } else {
        b = game.bots.find(a => a.uid == n.bots[0]);

        nbots.push({
            uid: b.uid,
            owner: b.owner,
            original: b.original,
            score: b.score,
            chars: [...b.chars],
            pos: n.pos,
            source: b.source,
            run: b.run,
            storage: b.storage,
            name: b.name,
            color: b.color
        });
    }
}

game.center = [
    nbots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0),
    nbots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0)
];

game.charPool = nbots.map(a => a.source).join("");

for (let b, c, i = 0; i < game.chars.length; i++) {
    c = game.chars[i];

    if (b = nbots.find(a => a.pos[0] == c.pos[0] && a.pos[1] == c.pos[1])) {
        b.score++;

        b.chars.push(c.char);

        if (c.game && game.chars.filter(a => a && a.game).length < nbots.length * 4 && game.bots.map(a => a.original).reduce((a, b) => a.includes(b) ? a : a.concat(b), []).length > 1)
            game.chars.push({
                char: game.charPool[Math.random() * game.charPool.length | 0],
                pos: game.randPos([0, 0]),
                game: !0
            });

        game.chars[i] = null;
    }
}

game.chars = game.chars.filter(a => a);
game.bots = nbots;
};

function drawRound(turns = 100000, log = 2, fps = 5, zoom = 50) {
var c, ctx, wdim, scale;

document.body.innerHTML = "<canvas></canvas>";

c = document.body.firstChild;
c.style.position = "absolute";
c.style.top = "0";
c.style.left = "0";
c.style.zIndex = "2";
ctx = c.getContext("2d");

game.records = new Array(botData.length).fill(0);
game.log = log;
game.pause = !1;
game.fps = fps;

(window.onresize = function() {
    wdim = [window.innerWidth || 600, window.innerHeight || 400];
    scale = Math.ceil(wdim[1] / zoom);
    c.width = wdim[0];
    c.height = wdim[1];
})();

window.onkeydown = function() {
    var key = event.code;
    if (key == "Escape")
        game.pause = !game.pause;
    if (key == "ArrowLeft" && game.fps > 1)
        game.fps -= 1;
    if (key == "ArrowRight")
        game.fps += 1;
};

game.debug = function() {
    ctx.clearRect(0, 0, wdim[0], wdim[1]);
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";
    ctx.font = Math.floor(scale * 0.6) + "px monospace";
    for (let x = -Math.ceil(wdim[0] / 2 / scale), i = wdim[0] / 2 - (Math.ceil(wdim[0] / 2 / scale) - 0.5) * scale; i <= wdim[0]; i += scale, x++) {
        for (let b, c, y = -Math.ceil(wdim[1] / 2 / scale), j = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; j <= wdim[1]; j += scale, y++) {
            if ((c = game.chars.filter(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))).length) {
                for (let k = 0; k < c.length; k++)
                    ctx.fillText(JSON.stringify(c[k].char).slice(1, -1).replace(/\\"/, "\"").replace(/\\\\/, "\\").replace(/ /, "_"), i + scale / 2, j + scale / 2);
            }
            if (b = game.bots.find(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))) {
                ctx.fillStyle = b.color;
                ctx.fillRect(i, j, scale, scale);
                ctx.fillStyle = "#000000";
            }
        }
        ctx.beginPath();
        ctx.moveTo(i, 0);
        ctx.lineTo(i, wdim[1]);
        ctx.stroke();
    }
    for (let i = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; i <= wdim[1]; i += scale) {
        ctx.beginPath();
        ctx.moveTo(0, i);
        ctx.lineTo(wdim[0], i);
        ctx.stroke();
    }
    ctx.fillRect(wdim[0] / 2 - 3, wdim[1] / 2 - 3, 7, 7);
};

runRound(turns);
}

function runGame(rounds = 1, turns = 100000, log = 0) {
game.records = new Array(botData.length).fill(0);
game.log = log;

for (let i = 0; i < rounds; i++)
    runRound(turns, 0);

console.log("Game Conclusion:\n" + botData.map((a, b) => [a.name, game.records[b]]).sort((a, b) => b[1] - a[1]).map(a => "[" + a[1] + "] " + a[0]).join("\n"));
}

drawRound(/*turns =*/ 1000000, /*log =*/ 2, /*fps =*/ 60,/* zoom =*/ 50);
```

```
<body></body>
```

# Answer
> 3 votes
## Shy Guy

```
{
    name: "Shy Guy",
    color: "#dd0044",
    run: _=>{var Z="pos",E=self(),P=E[Z],D=distTo,T=bots().filter(b=>D(b[Z])==2&&b.score>E.score),H=(d,n)=>!T.find(t=>t[Z][d]*n<P[d]*n),N=[north,east,south,west],x=P[0],y=P[1],l={[N[0]]:-y,[N[1]]:x,[N[2]]:y,[N[3]]:-x},S=[H(1,1),H(0,-1),H(1,-1),H(0,1)],U=bots().find(b=>D(b[Z])==1&&b.score>E.score),r=chars().filter(c=>!bots().find(b=>b.uid!=E.uid&&dist(b[Z],c[Z])-D(c[Z])<1));return r.length?(dirTo(r.sort((a,b)=>D(a[Z])-D(b[Z]))[0][Z])):T.length?U?(N.filter((n,i)=>S[i]).sort((a,b)=>l[a]-l[b])[0]||(_=>_))():0:(turn()%20||U?dirTo("00"):build(E.source))}
}

```

Almost unkillable. Avoids dangerous bots, and any characters it knows it can't get. Reproduces insanely fast. Its priorities:

1. If there is a character closer to it than any other bot, go toward it (there is no way this can kill it)
2. If there is a dangerous bot two spaces away, but none one space away, stop for one turn (this makes it so that the dangerous bot will never be able to kill Shy Guy, preventing it from being locked in place)
3. If there is still a dangerous bot but it wouldn't be safe to stop, move in a safe direction, preferably toward `[0, 0]`
4. If it's safe to stop for a turn, and the turn number is divisible by twenty, attempt to create a new worker
5. Go toward `[0, 0]`

# Answer
> 3 votes
# Camper

```
{
name: "Camper",
color: "#00FF00",
run:_=>dirTo(chars().sort((a,b)=>dist("00",a.pos)-dist("00",b.pos))[0].pos)
}

```

Camps the center, and collects the characters closest to it, since the center has a higher density of those.

```
var botData = [
{
    name: "ExampleBot",
    color: "#aaaaaa",
    run: () => dirTo(chars().sort((a, b) => dist(center(), a.pos) - dist(center(), b.pos))[0].pos)
},
{
    name: "Honnold",
    color: "#0000FF",
    run:()=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)
},
{
    name: "The Caveman",
    color: "#FF0000",
    run:()=>{w=bots().sort((a,b)=>a.score-b.score)[0];return self().score<=w.score?dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos):dirTo(w.pos)}
},
{
    name: "True Neutral",
    color: "#400000",
    run: _=>dirTo(chars().sort((a,b)=>dist(a.pos,[0,0])-dist(b.pos,[0,0])+distTo(a.pos)-distTo(b.pos))[0].pos)
},
{
    name: "Centrist",
    color: "#666666",
    run:_=>dirTo(center())
},
{
name: "Rabbit",
    color: "#FFC0CB",
    run:_=>turn()%1e3?dirTo(chars()[0].pos):build(self().source)
},{
    name: "Shy Guy",
    color: "#dd0044",
    run: _=>{var Z="pos",E=self(),P=E[Z],D=distTo,T=bots().filter(b=>D(b[Z])==2&&b.score>E.score),H=(d,n)=>!T.find(t=>t[Z][d]*n<P[d]*n),N=[north,east,south,west],x=P[0],y=P[1],l={[N[0]]:-y,[N[1]]:x,[N[2]]:y,[N[3]]:-x},S=[H(1,1),H(0,-1),H(1,-1),H(0,1)],U=bots().find(b=>D(b[Z])==1&&b.score>E.score),r=chars().filter(c=>!bots().find(b=>b.uid!=E.uid&&dist(b[Z],c[Z])-D(c[Z])<1));return r.length?(dirTo(r.sort((a,b)=>D(a[Z])-D(b[Z]))[0][Z])):T.length?U?(N.filter((n,i)=>S[i]).sort((a,b)=>l[a]-l[b])[0]||(_=>_))():0:(turn()%20||U?dirTo("00"):build(E.source))}
},
{
name: "The Luggage",
color: "#8B4513",
run:()=>dirTo(bots()[0].pos)
},
{
name: "Camper",
color: "#00FF00",
run:_=>dirTo(chars().sort((a,b)=>dist("00",a.pos)-dist("00",b.pos))[0].pos)
}
];

// _=>{var r,d,e,l,I,N,P,D=distTo,s=self(),p=s[P="pos"],w="_=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)",n=w.split``;s.chars.map(c=>n[n[N="indexOf"](c)]="");if(!n.find(_=>_))return build(w);r=chars().filter(c=>!bots().find(b=>b.uid!=s.uid&&dist(b[P],c[P])-D(c[P])<1));if(r.length)return dirTo(r.sort((a,b)=>D(a[P])-D(b[P])+(n[I="includes"](b)-n[I](a))*10)[0][P]);l={n:-p[1],e:p[0],s:p[1],w:-p[0]};d="nesw".split``.sort((a,b)=>l[a]-l[b]);e=bots().filter(b=>b.uid!=s.uid&&b.score>=s.score&&D(b[P])<5).map(b=>d[d[N](dirTo(b[P])[0][0])]="");return[north,east,south,west]["nesw"[N](d.find(x=>x))]()}

var game = {
randPos: (center, any = !1, uid = 0, owner = 0, p = 0.1) => {
    var theta, radius, pos;

    do {
        theta = Math.random() * Math.PI * 2;
        radius = 0;

        while (Math.random() > p)
            radius++;

        pos = [Math.trunc(center[0] + Math.cos(theta) * radius), Math.trunc(center[1] + Math.sin(theta) * radius)];
    } while (!any && game.bots.find(a => a && a.uid != uid && Math.abs(a.pos[0] - pos[0]) + Math.abs(a.pos[1] - pos[1]) < (a.uid == owner ? 3 : 4)));

    return pos;
},
debug: function(){},
log: 0 // 0 = NONE, 1 = SUMMARY, 2 = ALL
};

var north = () => ["north"];
var east = () => ["east"];
var south = () => ["south"];
var west = () => ["west"];

var build = code => ["worker", code];

var drop = {
north: char => ["drop.north", char],
east: char => ["drop.east", char],
south: char => ["drop.south", char],
west: char => ["drop.west", char]
};

var bots = () => game.bots.map(a => ({
uid: a.uid,
owner: a.owner,
original: a.original,
score: a.score,
pos: [...a.pos],
chars: game.uid == a.uid ? [...a.chars] : undefined,
source: game.uid == a.uid ? a.source : undefined
})).sort((a, b) => a.uid - b.uid);

var chars = () => game.chars.map(a => ({
char: a.char,
pos: [...a.pos]
}));

var self = () => {
var bot = game.bots.find(a => a.uid == game.uid);

return bot ? {
    uid: bot.uid,
    owner: bot.owner,
    original: bot.original,
    score: bot.score,
    pos: [...bot.pos],
    chars: [...bot.chars],
    source: bot.source
} : null;
};

var owner = () => {
var bot = game.bots.find(a => a.uid == game.bots.find(b => b.uid == game.uid).owner);

return bot ? {
    uid: bot.uid,
    owner: bot.owner,
    original: bot.original,
    score: bot.score,
    pos: [...bot.pos],
    chars: [...bot.chars],
    source: bot.source
} : null;
};

var center = () => game.center;
var turn = () => game.turns;

var at = pos => ({
bot: (game.bots.find(b => b.pos[0] == pos[0] && b.pos[1] == pos[1]) || {uid: null}).uid,
chars: chars().filter(c => c.pos[0] == pos[0] && c.pos[1] == pos[1])
});

var dir = (posFrom, pos) => {
if (Math.abs(posFrom[0] - pos[0]) <= Math.abs(posFrom[1] - pos[1]))
    return posFrom[1] < pos[1] ? ["north"] : ["south"];
else
    return posFrom[0] < pos[0] ? ["west"] : ["east"];
};

var dirTo = pos => {
var bot = game.bots.find(a => a.uid == game.uid);
if (Math.abs(pos[0] - bot.pos[0]) <= Math.abs(pos[1] - bot.pos[1]))
    return pos[1] < bot.pos[1] ? ["north"] : ["south"];
else
    return pos[0] < bot.pos[0] ? ["west"] : ["east"];
};

var dist = (posFrom, pos) => {
return Math.abs(posFrom[0] - pos[0]) + Math.abs(posFrom[1] - pos[1]);
};

var distTo = pos => {
var bot = game.bots.find(a => a.uid == game.uid);
return Math.abs(pos[0] - bot.pos[0]) + Math.abs(pos[1] - bot.pos[1]);
};

async function runRound(turns = 100000) {
var uids = [];

game.perf = performance.now();

for (let i = 1; i <= botData.length; i++)
    uids[i - 1] = i;

for (let j, i = uids.length - 1; i > 0; i--) {
    j = Math.floor(Math.random() * (i + 1));
    [uids[i], uids[j]] = [uids[j], uids[i]];
}

game.bots = [];
game.chars = [];
game.records = game.records || [];
game.uids = [];

for (let i = 0; i < botData.length; i++) {
    game.bots[i] = {
        uid: uids[i],
        owner: uids[i],
        original: uids[i],
        score: Math.floor(botData[i].run.toString().length * -1 / 2),
        chars: [],
        pos: game.randPos([0, 0]),
        source: botData[i].run.toString(),
        run: botData[i].run,
        storage: {},
        name: botData[i].name || "Bot",
        color: botData[i].color || "#000000"
    };

    game.uids[uids[i]] = i;
    game.records[i] = game.records[i] || 0;
}

game.center = [
    game.bots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0),
    game.bots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0)
];

game.charPool = game.bots.map(a => a.source).join("");

for (let i = 0; i < botData.length * 4; i++)
    game.chars.push({
        char: game.charPool[Math.random() * game.charPool.length | 0],
        pos: game.randPos([0, 0]),
        game: !0
    });

game.cuid = botData.length + 1;
game.turns = 0;

if (!game.fps) {
    while (game.chars.length && game.bots.length && game.turns < turns) {
        runTurn();

        game.turns++;
    }
} else {
    game.debug();

    while (game.chars.length && game.bots.length && game.turns < turns) {
        await new Promise(function(resolve) {
            setTimeout(resolve, 1000 / game.fps);
        });

        if (!game.pause) {
            runTurn();

            game.debug();
            game.turns++;
        }
    }
}

game.bots.map(b => game.records[game.uids[b.original]] += b.score);

if (game.log)
    console.log("Round Completed (" + ((performance.now() - game.perf) / 1000).toFixed(3) + "s):\n" + game.bots.map(a => a).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join("\n"));
}

function runTurn() {
var cbots = [];
var npos = [];
var nposl = [];
var nbots = [];

for (let b, p, m, i = 0; i < game.bots.length; i++) {
    b = game.bots[i];

    game.uid = b.uid;

    try {
        m = b.run(b.storage);
    } catch(e) {
        m = ["dead"];

        if (game.log == 2)
            console.warn("[" + game.turns + "] Error: " + b.name + "\n" + (e.stack || e.message));

        for (let j = 0; j < b.chars.length; j++)
            game.chars.push({
                char: b.chars[j],
                pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                game: !1
            });
    }

    if (!Array.isArray(m))
        m = [];

    if (m[0] == "north")
        p = [b.pos[0], b.pos[1] - 1];
    else if (m[0] == "east")
        p = [b.pos[0] + 1, b.pos[1]];
    else if (m[0] == "south")
        p = [b.pos[0], b.pos[1] + 1];
    else if (m[0] == "west")
        p = [b.pos[0] - 1, b.pos[1]];
    else
        p = [...b.pos];

    if (m[0] != "dead")
        npos.push({
            bot: b.uid,
            pos: p
        });

    if (m[0] == "worker" && m[1].split("").reduce((c, d, e) => c && d && (e = c.indexOf(d)) != -1 ? c.filter((f, g) => g != e) : null, [...b.chars])) {
        p = game.randPos(b.pos, !1, 0, b.uid);

        try {
            cbots.push({
                uid: game.cuid,
                owner: b.uid,
                original: b.original,
                score: 0,
                chars: [],
                pos: p,
                source: m[1],
                run: eval(m[1]),
                storage: {},
                name: b.name + "*",
                color: b.color
            });

            npos.push({
                bot: game.cuid++,
                pos: p
            });

            b.score -= Math.floor(m[1].length / 2);

            for (let n, j = 0; j < m[1].length; j++) {
                n = b.chars.indexOf(m[1][j]);

                b.chars = b.chars.slice(0, n).concat(b.chars.slice(n + 1));
            }

            if (game.log == 2)
                console.log("[" + game.turns + "] New Worker: " + b.name);
        } catch(e) {
            if (game.log == 2)
                console.warn("[" + game.turns + "] Invalid Worker: " + b.name + "\n" + (e.stack || e.message));
        }
    }

    if (typeof m[0] == "string" && m[0].match(/^drop.(north|east|south|west)$/) && b.chars.includes(m[1])) {
        b.score--;

        for (let j = 0; j < b.chars.length; j++) {
            if (b.chars[j] == m[1]) {
                b.chars = b.chars.slice(0, j) + b.chars.slice(j + 1);

                break;
            }
        }

        if (m[0] == "drop.north")
            p = [b.pos[0], b.pos[1] - 1];
        else if (m[0] == "drop.east")
            p = [b.pos[0] + 1, b.pos[1]];
        else if (m[0] == "drop.south")
            p = [b.pos[0], b.pos[1] + 1];
        else if (m[0] == "drop.west")
            p = [b.pos[0] - 1, b.pos[1]];

        game.chars.push({
            char: m[1],
            pos: p,
            game: !1
        });
    }
}

game.bots.push(...cbots);

for (let f, i = 0; i < npos.length; i++) {
    if (!(f = nposl.find(a => a.pos[0] == npos[i].pos[0] && a.pos[1] == npos[i].pos[1])))
        nposl.push(f = {
            pos: [...npos[i].pos],
            bots: []
        });

    f.bots.push(npos[i].bot);
}

for (let n, m, b, i = 0; i < nposl.length; i++) {
    n = nposl[i];

    if (n.bots.length > 1) {
        m = Math.max(...n.bots.map(a => game.bots.find(b => b.uid == a).score));

        if (game.bots.filter(a => n.bots.includes(a.uid) && a.score == m).length > 1) {
            m += 1;

            if (game.log == 2)
                console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
        } else {
            if (game.log == 2)
                console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
        }

        for (let j = 0; j < n.bots.length; j++) {
            b = game.bots.find(a => a.uid == n.bots[j]);

            if (b.score < m) {
                for (let k = 0; k < b.chars.length; k++)
                    game.chars.push({
                        char: b.chars[k],
                        pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                        game: !1
                    });

                game.records[game.uids[b.original]] += b.score;
            } else {
                nbots.push({
                    uid: b.uid,
                    owner: b.owner,
                    original: b.original,
                    score: b.score,
                    chars: [...b.chars],
                    pos: n.pos,
                    source: b.source,
                    run: b.run,
                    storage: b.storage,
                    name: b.name,
                    color: b.color
                });
            }
        }
    } else {
        b = game.bots.find(a => a.uid == n.bots[0]);

        nbots.push({
            uid: b.uid,
            owner: b.owner,
            original: b.original,
            score: b.score,
            chars: [...b.chars],
            pos: n.pos,
            source: b.source,
            run: b.run,
            storage: b.storage,
            name: b.name,
            color: b.color
        });
    }
}

game.center = [
    nbots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0),
    nbots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0)
];

game.charPool = nbots.map(a => a.source).join("");

for (let b, c, i = 0; i < game.chars.length; i++) {
    c = game.chars[i];

    if (b = nbots.find(a => a.pos[0] == c.pos[0] && a.pos[1] == c.pos[1])) {
        b.score++;

        b.chars.push(c.char);

        if (c.game && game.chars.filter(a => a && a.game).length < nbots.length * 4 && game.bots.map(a => a.original).reduce((a, b) => a.includes(b) ? a : a.concat(b), []).length > 1)
            game.chars.push({
                char: game.charPool[Math.random() * game.charPool.length | 0],
                pos: game.randPos([0, 0]),
                game: !0
            });

        game.chars[i] = null;
    }
}

game.chars = game.chars.filter(a => a);
game.bots = nbots;
};

function drawRound(turns = 100000, log = 2, fps = 5, zoom = 50) {
var c, ctx, wdim, scale;

document.body.innerHTML = "<canvas></canvas>";

c = document.body.firstChild;
c.style.position = "absolute";
c.style.top = "0";
c.style.left = "0";
c.style.zIndex = "2";
ctx = c.getContext("2d");

game.records = new Array(botData.length).fill(0);
game.log = log;
game.pause = !1;
game.fps = fps;

(window.onresize = function() {
    wdim = [window.innerWidth || 600, window.innerHeight || 400];
    scale = Math.ceil(wdim[1] / zoom);
    c.width = wdim[0];
    c.height = wdim[1];
})();

window.onkeydown = function() {
    var key = event.code;
    if (key == "Escape")
        game.pause = !game.pause;
    if (key == "ArrowLeft" && game.fps > 1)
        game.fps -= 1;
    if (key == "ArrowRight")
        game.fps += 1;
};

game.debug = function() {
    ctx.clearRect(0, 0, wdim[0], wdim[1]);
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";
    ctx.font = Math.floor(scale * 0.6) + "px monospace";
    for (let x = -Math.ceil(wdim[0] / 2 / scale), i = wdim[0] / 2 - (Math.ceil(wdim[0] / 2 / scale) - 0.5) * scale; i <= wdim[0]; i += scale, x++) {
        for (let b, c, y = -Math.ceil(wdim[1] / 2 / scale), j = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; j <= wdim[1]; j += scale, y++) {
            if ((c = game.chars.filter(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))).length) {
                for (let k = 0; k < c.length; k++)
                    ctx.fillText(JSON.stringify(c[k].char).slice(1, -1).replace(/\\"/, "\"").replace(/\\\\/, "\\").replace(/ /, "_"), i + scale / 2, j + scale / 2);
            }
            if (b = game.bots.find(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))) {
                ctx.fillStyle = b.color;
                ctx.fillRect(i, j, scale, scale);
                ctx.fillStyle = "#000000";
            }
        }
        ctx.beginPath();
        ctx.moveTo(i, 0);
        ctx.lineTo(i, wdim[1]);
        ctx.stroke();
    }
    for (let i = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; i <= wdim[1]; i += scale) {
        ctx.beginPath();
        ctx.moveTo(0, i);
        ctx.lineTo(wdim[0], i);
        ctx.stroke();
    }
    ctx.fillRect(wdim[0] / 2 - 3, wdim[1] / 2 - 3, 7, 7);
};

runRound(turns);
}

function runGame(rounds = 1, turns = 100000, log = 0) {
game.records = new Array(botData.length).fill(0);
game.log = log;

for (let i = 0; i < rounds; i++)
    runRound(turns, 0);

console.log("Game Conclusion:\n" + botData.map((a, b) => [a.name, game.records[b]]).sort((a, b) => b[1] - a[1]).map(a => "[" + a[1] + "] " + a[0]).join("\n"));
}

drawRound(/*turns =*/ 1000000, /*log =*/ 2, /*fps =*/ 240,/* zoom =*/ 50);
```

```
<body></body>
```

# Answer
> 2 votes
## True Neutral

```
{
    name: "True Neutral",
    color: "#400000",
    run: _=>dirTo(chars().sort((a,b)=>dist(a.pos,[0,0])-dist(b.pos,[0,0])+distTo(a.pos)-distTo(b.pos))[0].pos)
}

```

Goes for the characters which are:

* Close to itself
* Close to `[0, 0]` (where the most characters are)

# Answer
> 2 votes
# Centrist, 18 bytes(-9 points)

```
{
    name: "Centrist",
    color: "#666666",
    run:_=>dirTo(center())
}

```

Even dumber than our friendly neighbourhood copycat. Just follows the crowd and hopes that he can live. Even his color is dull.

```
var botData = [
  {
name: "Honnold",
color: "#0000FF",
run:()=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)
  },

  {
    name: "The Caveman",
    color: "#FF0000",
    run:_=>{w=bots().sort((a,b)=>a.score-b.score)[0];return self().score<=w.score?dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos):dirTo(w.pos)}
  },
    {
    name: "Centrist",
    color: "#666666",
    run:_=>dirTo(center())
  },
  {
    name: "True Neutral",
    color: "#400000",
    run: _=>dirTo(chars().sort((a,b)=>dist(a.pos,[0,0])-dist(b.pos,[0,0])+distTo(a.pos)-distTo(b.pos))[0].pos)
}

];

{
var game = {
  randPos: (center, any = !1, uid = 0, owner = 0, p = 0.1) => {
      var theta, radius, pos;

      do {
          theta = Math.random() * Math.PI * 2;
          radius = 0;

          while (Math.random() > p)
              radius++;

          pos = [Math.trunc(center[0] + Math.cos(theta) * radius), Math.trunc(center[1] + Math.sin(theta) * radius)];
      } while (!any && game.bots.find(a => a && a.uid != uid && Math.abs(a.pos[0] - pos[0]) + Math.abs(a.pos[1] - pos[1]) < (a.uid == owner ? 3 : 4)));

      return pos;
  },
  debug: function(){},
  log: 0 // 0 = NONE, 1 = SUMMARY, 2 = ALL
};

var north = () => ["north"];
var east = () => ["east"];
var south = () => ["south"];
var west = () => ["west"];

var build = code => ["worker", code];

var drop = {
  north: char => ["drop.north", char],
  east: char => ["drop.east", char],
  south: char => ["drop.south", char],
  west: char => ["drop.west", char]
};

var bots = () => game.bots.map(a => ({
  uid: a.uid,
  owner: a.owner,
  original: a.original,
  score: a.score,
  pos: [...a.pos],
  chars: game.uid == a.uid ? [...a.chars] : undefined,
  source: game.uid == a.uid ? a.source : undefined
})).sort((a, b) => a.uid - b.uid);

var chars = () => game.chars.map(a => ({
  char: a.char,
  pos: [...a.pos]
}));

var self = () => {
  var bot = game.bots.find(a => a.uid == game.uid);
  return bot ? {
      uid: bot.uid,
      owner: bot.owner,
      original: bot.original,
      score: bot.score,
      pos: [...bot.pos],
      chars: [...bot.chars],
      source: bot.source
  } : null;
};

var owner = () => {
  var bot = game.bots.find(a => a.uid == game.bots.find(b => b.uid == game.uid).owner);
  return bot ? {
      uid: bot.uid,
      owner: bot.owner,
      original: bot.original,
      score: bot.score,
      pos: [...bot.pos],
      chars: [...bot.chars],
      source: bot.source
  } : null;
};

var center = () => game.center;
var turn = () => game.turns;

var at = pos => ({
  bot: (game.bots.find(b => b.pos[0] == pos[0] && b.pos[1] == pos[1]) || {uid: null}).uid,
  chars: chars().filter(c => c.pos[0] == pos[0] && c.pos[1] == pos[1])
});

var dir = (posFrom, pos) => {
  if (Math.abs(posFrom[0] - pos[0]) <= Math.abs(posFrom[1] - pos[1]))
      return posFrom[1] < pos[1] ? ["north"] : ["south"];
  else
      return posFrom[0] < pos[0] ? ["west"] : ["east"];
};

var dirTo = pos => {
  var bot = game.bots.find(a => a.uid == game.uid);
  if (Math.abs(pos[0] - bot.pos[0]) <= Math.abs(pos[1] - bot.pos[1]))
      return pos[1] < bot.pos[1] ? ["north"] : ["south"];
  else
      return pos[0] < bot.pos[0] ? ["west"] : ["east"];
};

var dist = (posFrom, pos) => {
  return Math.abs(posFrom[0] - pos[0]) + Math.abs(posFrom[1] - pos[1]);
};

var distTo = pos => {
  var bot = game.bots.find(a => a.uid == game.uid);
  return Math.abs(pos[0] - bot.pos[0]) + Math.abs(pos[1] - bot.pos[1]);
};

async function runRound(turns = 100000) {
  var uids = [];

  game.perf = performance.now();

  for (let i = 1; i <= botData.length; i++)
      uids[i - 1] = i;

  for (let j, i = uids.length - 1; i > 0; i--) {
      j = Math.floor(Math.random() * (i + 1));
      [uids[i], uids[j]] = [uids[j], uids[i]];
  }

  game.bots = [];
  game.chars = [];
  game.records = game.records || [];
  game.uids = [];

  for (let i = 0; i < botData.length; i++) {
      game.bots[i] = {
          uid: uids[i],
          owner: uids[i],
          original: uids[i],
          score: Math.floor(botData[i].run.toString().length * -1 / 2),
          chars: [],
          pos: game.randPos([0, 0]),
          source: botData[i].run.toString(),
          run: botData[i].run,
          storage: {},
          name: botData[i].name || "Bot",
          color: botData[i].color || "#000000"
      };

      game.uids[uids[i]] = i;
      game.records[i] = game.records[i] || 0;
  }

  game.center = [
      game.bots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0),
      game.bots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0)
  ];

  game.charPool = game.bots.map(a => a.source).join("");

  for (let i = 0; i < botData.length * 4; i++)
      game.chars.push({
          char: game.charPool[Math.random() * game.charPool.length | 0],
          pos: game.randPos([0, 0]),
          game: !0
      });

  game.cuid = botData.length + 1;
  game.turns = 0;

  if (!game.fps) {
      while (game.chars.length && game.bots.length && game.turns < turns) {
          runTurn();

          game.turns++;
      }
  } else {
      game.debug();

      while (game.chars.length && game.bots.length && game.turns < turns) {
          await new Promise(function(resolve) {
              setTimeout(resolve, 1000 / game.fps);
          });

          if (!game.pause) {
              runTurn();

              game.debug();
              game.turns++;
          }
      }
  }
  bots().map(b => game.records[game.uids[b.original]] += b.score);

  if (game.log)
      console.log("Round Completed (" + ((performance.now() - game.perf) / 1000).toFixed(3) + "s):\n" + game.bots.map(a => a).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join("\n"));
}

function runTurn() {
  var cbots = [];
  var npos = [];
  var nposl = [];
  var nbots = [];

  for (let b, p, m, i = 0; i < game.bots.length; i++) {
      b = game.bots[i];

      game.uid = b.uid;

      try {
          m = b.run(b.storage);
      } catch(e) {
          m = ["dead"];

          if (game.log == 2)
              console.warn("[" + game.turns + "] Error: " + b.name + "\n" + (e.stack || e.message));

          for (let j = 0; j < b.chars.length; j++)
              game.chars.push({
                  char: b.chars[j],
                  pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                  game: !1
              });
      }

      if (!Array.isArray(m))
          m = [];

      if (m[0] == "north")
          p = [b.pos[0], b.pos[1] - 1];
      else if (m[0] == "east")
          p = [b.pos[0] + 1, b.pos[1]];
      else if (m[0] == "south")
          p = [b.pos[0], b.pos[1] + 1];
      else if (m[0] == "west")
          p = [b.pos[0] - 1, b.pos[1]];
      else
          p = [...b.pos];

      if (m[0] != "dead")
          npos.push({
              bot: b.uid,
              pos: p
          });

      if (m[0] == "worker" && m[1].split("").reduce((c, d, e) => d && (e = c.indexOf(d)) != -1 ? c.filter((f, g) => g != e) : null, [...b.chars])) {
          p = game.randPos(b.pos, !1, 0, b.uid);

          try {
              cbots.push({
                  uid: game.cuid,
                  owner: b.uid,
                  original: b.original,
                  score: 0,
                  chars: [],
                  pos: p,
                  source: m[1],
                  run: eval(m[1]),
                  storage: {},
                  name: b.name + "*",
                  color: b.color
              });

              npos.push({
                  bot: game.cuid++,
                  pos: p
              });

              b.score -= Math.floor(m[1].length / 2);

              for (let n, j = 0; j < m[1].length; j++) {
                  n = b.chars.indexOf(m[1][j]);

                  b.chars = b.chars.slice(0, n).concat(b.chars.slice(n + 1));
              }

              if (game.log == 2)
                  console.log("[" + game.turns + "] New Worker: " + b.name);
          } catch(e) {
              if (game.log == 2)
                  console.warn("[" + game.turns + "] Invalid Worker: " + b.name + "\n" + (e.stack || e.message));
          }
      }

      if (typeof m[0] == "string" && m[0].match(/^drop.(north|east|south|west)$/) && b.chars.includes(m[1])) {
          b.score--;

          for (let j = 0; j < b.chars.length; j++) {
              if (b.chars[j] == m[1]) {
                  b.chars = b.chars.slice(0, j) + b.chars.slice(j + 1);

                  break;
              }
          }

          if (m[0] == "drop.north")
              p = [b.pos[0], b.pos[1] - 1];
          else if (m[0] == "drop.east")
              p = [b.pos[0] + 1, b.pos[1]];
          else if (m[0] == "drop.south")
              p = [b.pos[0], b.pos[1] + 1];
          else if (m[0] == "drop.west")
              p = [b.pos[0] - 1, b.pos[1]];

          game.chars.push({
              char: m[1],
              pos: p,
              game: !1
          });
      }
  }

  game.bots.push(...cbots);

  for (let f, i = 0; i < npos.length; i++) {
      if (!(f = nposl.find(a => a.pos[0] == npos[i].pos[0] && a.pos[1] == npos[i].pos[1])))
          nposl.push(f = {
              pos: [...npos[i].pos],
              bots: []
          });

      f.bots.push(npos[i].bot);
  }

  for (let n, m, b, i = 0; i < nposl.length; i++) {
      n = nposl[i];

      if (n.bots.length > 1) {
          m = Math.max(...n.bots.map(a => game.bots.find(b => b.uid == a).score));

          if (game.bots.filter(a => n.bots.includes(a.uid) && a.score == m).length > 1) {
              m += 1;

              if (game.log == 2)
                  console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
          } else {
              if (game.log == 2)
                  console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
          }

          for (let j = 0; j < n.bots.length; j++) {
              b = game.bots.find(a => a.uid == n.bots[j]);

              if (b.score < m)
                  for (let k = 0; k < b.chars.length; k++)
                      game.chars.push({
                          char: b.chars[k],
                          pos: game.randPos(b.pos, !0, 0, 0, 0.2),
                          game: !1
                      });
              else
                  nbots.push({
                      uid: b.uid,
                      owner: b.owner,
                      original: b.original,
                      score: b.score,
                      chars: [...b.chars],
                      pos: n.pos,
                      source: b.source,
                      run: b.run,
                      storage: b.storage,
                      name: b.name,
                      color: b.color
                  });
          }
      } else {
          b = game.bots.find(a => a.uid == n.bots[0]);

          nbots.push({
              uid: b.uid,
              owner: b.owner,
              original: b.original,
              score: b.score,
              chars: [...b.chars],
              pos: n.pos,
              source: b.source,
              run: b.run,
              storage: b.storage,
              name: b.name,
              color: b.color
          });
      }
  }

  game.center = [
      nbots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0),
      nbots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0)
  ];

  game.charPool = nbots.map(a => a.source).join("");

  for (let b, c, i = 0; i < game.chars.length; i++) {
      c = game.chars[i];

      if (b = nbots.find(a => a.pos[0] == c.pos[0] && a.pos[1] == c.pos[1])) {
          b.score++;

          b.chars.push(c.char);

          if (c.game && game.chars.filter(a => a && a.game).length < nbots.length * 4 && game.bots.map(a => a.original).reduce((a, b) => a.includes(b) ? a : a.concat(b), []).length > 1)
              game.chars.push({
                  char: game.charPool[Math.random() * game.charPool.length | 0],
                  pos: game.randPos([0, 0]),
                  game: !0
              });

          game.chars[i] = null;
      }
  }

  game.chars = game.chars.filter(a => a);
  game.bots = nbots;
};

function drawRound(turns = 100000, log = 2, fps = 60, zoom = 50){
  var c, ctx, wdim, scale;

  document.body.innerHTML = "<canvas></canvas>";

  c = document.body.firstChild;
  c.style.position = "absolute";
  c.style.top = "0";
  c.style.left = "0";
  c.style.zIndex = "2";
  ctx = c.getContext("2d");

  game.records = new Array(botData.length).fill(0);
  game.log = log;
  game.pause = !1;
  game.fps = fps;

  (window.onresize = function() {
      wdim = [window.innerWidth || 600, window.innerHeight || 400];
      scale = Math.ceil(wdim[1] / zoom);
      c.width = wdim[0];
      c.height = wdim[1];
  })();

  window.onkeydown = function() {
      var key = event.code;
      if (key == "Escape")
          game.pause = !game.pause;
      if (key == "ArrowLeft" && game.fps > 1)
          game.fps -= 1;
      if (key == "ArrowRight")
          game.fps += 1;
  };

  game.debug = function() {
      ctx.clearRect(0, 0, wdim[0], wdim[1]);
      ctx.textBaseline = "middle";
      ctx.textAlign = "center";
      ctx.font = Math.floor(scale * 0.6) + "px monospace";
      for (let x = -Math.ceil(wdim[0] / 2 / scale), i = wdim[0] / 2 - (Math.ceil(wdim[0] / 2 / scale) - 0.5) * scale; i <= wdim[0]; i += scale, x++) {
          for (let b, c, y = -Math.ceil(wdim[1] / 2 / scale), j = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; j <= wdim[1]; j += scale, y++) {
              if ((c = game.chars.filter(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))).length) {
                  for (let k = 0; k < c.length; k++)
                      ctx.fillText(JSON.stringify(c[k].char).slice(1, -1).replace(/\\"/, "\"").replace(/\\\\/, "\\").replace(/ /, "_"), i + scale / 2, j + scale / 2);
              }
              if (b = game.bots.find(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))) {
                  ctx.fillStyle = b.color;
                  ctx.fillRect(i, j, scale, scale);
                  ctx.fillStyle = "#000000";
              }
          }
          ctx.lineWidth = 0.1;
          ctx.beginPath();
          ctx.moveTo(i, 0);
          ctx.lineTo(i, wdim[1]);
          ctx.stroke();
      }
      for (let i = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; i <= wdim[1]; i += scale) {
          ctx.beginPath();
          ctx.moveTo(0, i);
          ctx.lineTo(wdim[0], i);
          ctx.stroke();
      }
      ctx.fillRect(wdim[0] / 2 - 3, wdim[1] / 2 - 3, 7, 7);
  };

  runRound(turns);
}

function runGame(rounds = 1, turns = 100000, log = 0) {
  game.records = new Array(botData.length).fill(0);
  game.log = log;

  for (let i = 0; i < rounds; i++)
      runRound(turns, 0);

  console.log("Game Conclusion:\n" + botData.map((a, b) => [a.name, game.records[b]]).sort((a, b) => b[1] - a[1]).map(a => "[" + a[1] + "] " + a[0]).join("\n"));
}

};

drawRound(/*turns =*/ 1000000, /*log =*/ 2, /*fps =*/ 600,/* zoom =*/ 50);
```

---
Tags: king-of-the-hill, javascript
---