4
\$\begingroup\$

In this challenge, players need to tamper with a time bomb as late as possible without getting blasted in the face.

Mechanics

Let N be the number of bots plus one. Each round, each bot receives the following arguments:

  • numbers played by all bots last round in ascending order.
  • number of points it received last round. Negative if it lost last round.

The bot is asked to play an integer between 1 to N inclusive. After all bots played their numbers, a counter is initialized with half of the sum of numbers from all bots. Each bots then subtracts the number it played from the counter. Bots are sorted from the lowest to the highest. Bots with the same number are considered subtracts the counter at the same time. The bomb explodes when the counter hits zero or becomes negative.

Scoring

After the bomb exploded, the round ends, and the points for this round is calculated as the following:

  • Bots that played before the explosion get the number of points equal to twice the number it played this round.
  • Bots that played when the explosion lose the number of points equal to the number it played this round.
  • Bots that played after the explosion do not get nor receive point.

Example rounds:

  • Number played: [1,2,3,4,5]. The initial counter is 7.5, and the bomb explodes on 4. The bot with 1, 2, and 3 gets 2, 4, and 6 points respectively, the bot with 4 loses 4, and the bot with 5 neither gets not lose points.
  • Number played: [1,1,2,4]. The initial counter is 4, and the bomb explodes on 2. Bots with 1 gets 2 points, the bot with 2 loses 2, and the bot with 4 neither gets not lose points.
  • Number played: [3,3,3,3]. The initial counter is 6, and the bomb explodes on 3. All bots lose 3 points.

The point for each round is added to a total score for that run. Each run consist of 1000 rounds, and there will be 10 runs. The final total score is the sum of each run, and the bot with the highest final score the the end wins.

Specifications

The challenge is in JS.

Your bot must be an object that has a run method that takes an array of numbers and a number as input, and returns a integer between 1 and N inclusive. The value being outside the valid range will be rounded and clamped. If the value is not a number, a number in the valid range will be selected and the behavior of selecting the number is not specified.

On the first round, the array of numbers given will be initialized with random numbers, and the number for points last round will be 0.

Other rules

  • Storing data in your bot's properties is allowed.
  • You can use the Math.random method. During scoring, seedrandom.min.js will be used, with a secret string concatenated with the run number as the seed for each run. The md5 of the secret string is 4311868bf10b3f7c1782982e68e6d7ca, for proving that I didn't change the key after the challenge.
  • You can use the helper function sum.
  • Trying to access any other variables outside your bot's own properties is forbidden.
  • Standard loopholes apply.

The code for controller can be found here. Feel free to point out mistakes in my code, thanks.

Example bots

{
  // Example bot
  // It always play the middle number.
  name: "Middle", 
  run(number) {
    return Math.round((numbers.length+2)/2);
  }
}
{
  // Example bot
  // It starts at 1, advances when it got points last round,
  // and resets when it lost points.
  name: "Advance and reset",
  last: 1, // Setting properties is allowed
  run(numbers, points) {
    var own = this.last;
    if(points > 0){
      own = Math.min(this.last+1, numbers.length+1);
    }
    if(points < 0){
      own = 1;
    }
    this.last = own;
    return own;
  }
}

Both example bots above will also play in the game.

Submissions are due by 2021-05-06 12:00 UTC, but might be lenient depending on when I'm online after the said time above.

\$\endgroup\$
12
  • 2
    \$\begingroup\$ I've voted to close this as a duplicate of xkcd 2385 KoTH (Final Exam). As noted by Redwolf, the strategy is going to be almost identical, just with different numbers, and IMO that makes this essentially the same challenge \$\endgroup\$ – caird coinheringaahing May 3 at 13:35
  • 2
    \$\begingroup\$ I am closing this challenge for the reasons caird gave. Since both just involve "maximize a number without making it too much larger than everyone else" or equivalently "pick a low number without it being too low", strategies will be very uncreative / identical to the other KoTH. \$\endgroup\$ – hyper-neutrino May 3 at 13:41
  • 3
    \$\begingroup\$ I don't see how the two challenges are the same The old KoTH was in favour of all strategies converging: if everyone played rationally they would arrive at a single point, the only challenge was about finding the variance other players make and take slight advantage of it. In this challenge if everyone plays the same "optimal" strategy, everyone ends up loosing and can improve their gain. I would almost say there is no Nash equilibrium let alone an optimal equilibrium. \$\endgroup\$ – IQuick 143 May 3 at 13:50
  • 3
    \$\begingroup\$ To support on @IQuick143, here is a round history of xkcd KoTH with all the bots included. Each line in the plot represent a 10% percentile e.g. 0%, 10%, ..., 100% percentile for score outputted by the bot for each round. You can see that at least 40% of the score (40% to 80%) quickly converge to the range between 75 and 80 in less then 10 rounds. This with the buffer created by these "simpleton" bots, plus that the point is a single-humped function of the score, means that the optimal play is to converge and make use of deviation caused by more random bots. \$\endgroup\$ – leo3065 May 3 at 15:39
  • 4
    \$\begingroup\$ (cont.) In contrast, in this challenge, not only the reward function isn't a single-humped, it's also not continuous since the decision space is discrete, with the decision of maximum possible reward right next to the one with the minimum. That combined with the rule that the bots playing the same number subtract the counter together means that converges with other bots will only cause everyone to lose points together. \$\endgroup\$ – leo3065 May 3 at 15:48
5
\$\begingroup\$

Some "basic" bots to get this started, also to serve as baseline (they will also play in the game):

{
  name: "33%",
  run(numbers) {
    return numbers[Math.floor(numbers.length/3)];
  }
}
{
  name: "Pure random",
  run(numbers) {
    return Math.floor(Math.random() * (numbers.length+1))+1;
  }
}
{
  name: "Copy random",
  run(numbers) {
    return numbers[Math.floor(Math.random() * numbers.length)];
  }
}
{
  name: "Alternate",
  round: 0,
  run(numbers) {
    this.round++
    return (this.round % 2)? 1 : (numbers.length + 1)
  }
}
\$\endgroup\$
5
\$\begingroup\$
{
  name: "Playing It Safe",
  run(numbers) {
    return 1;
  }
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Damn, this is good \$\endgroup\$ – StackMeter May 3 at 12:43
2
\$\begingroup\$

Smart Random

{
name: "Smart Random", 
  run(numbers) {
    var random_int = Math.floor(Math.random() * 3 * (numbers.length+1)/4);
    return random_int
  }
}

Returns a random integer between 0 and 3/4N inclusive.

I hope this works - this is my first JS and my first KoTH answer.

\$\endgroup\$
2
  • \$\begingroup\$ You missed a right parenthesis, but I fixed it for you. \$\endgroup\$ – leo3065 May 3 at 12:43
  • \$\begingroup\$ Thanks, @leo3065, this is my first JS answer \$\endgroup\$ – StackMeter May 3 at 12:44
2
\$\begingroup\$

Coinflipper

Starts by initialising x to half of N, then flips N coins. If heads, decrement x (unless already zero). If tails, increment x (unless already equal to N).

{
  name: "Coinflipper",
  run(numbers) {
    let x = Math.floor(numbers.length/2);
    numbers.forEach(function(){
        let coin = (Math.floor(Math.random()*2)+1)
        if (coin==1) { x--; }
        else if (coin==2) { x++; }
        if (x<0) { x=0; }
        if (x>numbers.length) { x=numbers.length; }
    });
    return x;
  }
}
\$\endgroup\$
2
  • \$\begingroup\$ You need to declare x and coin. I added let for you. \$\endgroup\$ – leo3065 May 3 at 13:28
  • \$\begingroup\$ Ah whoops, forgot to do that. Thanks for the edit! \$\endgroup\$ – SjoerdPennings May 3 at 13:33
1
\$\begingroup\$

Painfully Average

{
    name: "Painfully Average",
    run: (n)=>Math.abs(Math.floor(n.reduce((a,x)=>a+x))/n.length-1)
}
\$\endgroup\$
1
\$\begingroup\$

OnePlus

Plays one plus a random floating point between 0 and a random integer between 0 and 5.

{
  name: "OnePlus",
  run(numbers) {
    return 1 + Math.random() * Math.floor(Math.random() * 5);
  }
}

My first King Of The Hill answer, not sure if I am doing things correctly. From my understanding, numbers is the list of numbers of the bots? But I am completely ignoring that numbers.

\$\endgroup\$
1
  • \$\begingroup\$ It's correct. Technically you should check if the output is valid, but since we already have more than 6 bots, it shouldn't be a problem. \$\endgroup\$ – xigoi May 3 at 18:00
1
\$\begingroup\$

D8roller

Rolls a 8-sided die, and plays that number.

{
  name: "D8roller",
  run(numbers) {
    return Math.floor(Math.random()*8)+1;
  }
}
\$\endgroup\$

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