22
\$\begingroup\$

In this challenge, you will write a bot that play's the prisoner's dilemma. Here's the catch: you will not have access to the history of previous games. Instead, you will have access to the opponent itself. In this version, both players get +2 points if they both cooperate, +1 points if they both defect, and if one cooperates but one defects, the defector gets +3 while the other gets no points. Each submission will be played against every other submission, including itself, 10 times. The winner is the submission with the most total points.

Controller: You should write a javascript function, in the form

function submissionName(them) {
  /* Your code here */
}

The controller uses the function's name property to display the results, so if it is not in this format (and is instead f = x => ... or f = function() { ... }) it will be difficult to see your score and you will not be able to access your own function.

The function will accept one parameter: them which is the opponent's function. It may then call that function to see what the opponent's reaction would be given certain functions as inputs. Based on that data, you must return 'C' or 'D' for cooperate or defect respectively.

Examples (will be competing):

function cooperate(them) {
    return 'C';
}

function defect(them) {
    return 'D';
}

function nice(them) {
    // Do whatever they would do when faced with a cooperator
    return them(wrap(_ => 'C'));
}

The controller is available here

Rules:

  • You won't be able to see the opponent's code itself. All functions are wrapped so that they look the same when toString() is called. The only way to examine an opponent (who could be yourself) is to test them.
  • Your function does not have to be deterministic. You may only save state by setting properties on your own function, such as submissionName.state = {};. However, between matches (even between matches of the same players), state is cleared by calling toString() and eval. Therefore, there is no memory of previous matches.
  • The order of which function is called first in each match is randomized.
  • If your code throws an error, it will be treated as though you cooperated while your opponent defected. If you are the first to run, the opponent's code will not even be called. This happens even if the error occurs in your opponent's code while while you are calling them. Be wary of stack overflow errors, especially if your code calls them(wrap(submissionName)), as they might do the same.
  • You may not access the variable self, or any other variable that happens to be in scope when eval is called EXCEPT the function wrap. This function allows you to call the opponent in a manner indistinguishable from how the controller calls a function. You may not write to Math, window, etc. (You may use functions, such as Math.random(), however).
  • You may not access the stack trace by creating an Error or by some other method.

A note on taking too long: please avoid getting stuck in a while loop forever. The combined time of both competitors should not exceed 1 second in any given round. To enforce this, a random timeout between 1000 ms and 2000 ms is chosen (this is to avoid gaming by intentionally waiting a known amount of time), and if the worker takes longer than that to execute, an error will be thrown. If this happens, the cause of the error will be determined as follows: the execution will be paused at a random moment after 1000 ms, and the call stack at that moment will be inspected. The most recently called competitor that is currently in a loop (or loop-like recursion, in the sense that it is a recursion set up to avoid a stack overflow error) will be blamed. If the same competitor is blamed for causing a "taking too long" error several times, that competitor will be disqualified. This means that even if you cause the opponent to take too long by getting stuck in a loop when your opponent calls you, it will still be your fault, because it is the call stack that matters.

\$\endgroup\$
  • \$\begingroup\$ This challenge reminds me of the Dollar Bill Auction. \$\endgroup\$ – Alion Sep 30 '18 at 9:18
  • \$\begingroup\$ Must the function used to test them be deterministic/follow the rules? For example function me(them){let log=0;them(x=>{++log;return 'C';});return log==0?'D':'C';} \$\endgroup\$ – user202729 Sep 30 '18 at 12:28
  • 2
    \$\begingroup\$ If both functions call them(wrap(something)), how can you prevent recursion? Am I missing something? \$\endgroup\$ – Quintec Sep 30 '18 at 13:29
  • \$\begingroup\$ @Quintec you can use recursion and loops. It's just that the recursion needs to result in a StackOverflow error and not an infinite loop that never quits. If it could result in a StackOverflow, make sure you add a try-catch statement. For an example of recursion that does not reach a stackoverflow error within 1 second, you need more obscure examples like stackoverflow.com/q/12438786/3371119 \$\endgroup\$ – soktinpk Sep 30 '18 at 14:18
  • 1
    \$\begingroup\$ @Quintec not necessarily. For example, them(() => 'C') would not result in an error because when the opponent calls them, it calls the () => 'C' function. The only thing that needs to be wrapped in try-catch would be if you call them with a parameter of some function that calls them with a parameter of some function that calls them etc. (infinitely). For example, them(t => t(() => 'C')) would play whatever the opponent would play if the opponent thought they were playing nice. There is no possibility of a stackoverflow error. \$\endgroup\$ – soktinpk Sep 30 '18 at 14:39

19 Answers 19

14
\$\begingroup\$

BoomBot

function boom(them) {
  throw 1;
}

If the opponent is run first and calls this without try..catch, this bot automatically wins 3 points. Zero points in any other case.

\$\endgroup\$
  • \$\begingroup\$ If the opponent is run first and do not call this, then it will lose 3 points, right? \$\endgroup\$ – user202729 Oct 1 '18 at 13:51
  • 1
    \$\begingroup\$ @user202729 More precisely, the opponent will get 3 points. There's no losing points in this game. \$\endgroup\$ – Bubbler Oct 1 '18 at 22:58
9
\$\begingroup\$

Archaeopteryx

function archaeopteryx(them) {
  const guard = them => us => {
    try {
      return them(wrap(them => us(guard(them))));
    } catch (e) {
      return 'C';
    }
  };
  const f = guard(them);
  return f(f => 'C') == 'C' ? f(f => 'D') : f(f => 'D') == 'C' || f(f => f(f => 'C')) == 'C' ? 'D' : 'C';
}
  • If opponent cooperates with cooperate, then mimic opponent’s move against defect.
  • Else, if opponent cooperates with defect or with nice, then defect.
  • Else, cooperate.

What makes this a good strategy? I have no idea. I generated it using an evolutionary algorithm, trained partly on current submissions.

Tiktaalik

function tiktaalik(them) {
  const guard = them => us => {
    try {
      return them(wrap(them => us(guard(them))));
    } catch (e) {
      return 'C';
    }
  };
  const f = guard(them);
  return f(f => 'C') == 'D' ? f(f => 'D') == 'C' ? 'D' : 'C' : f(f => 'D') == 'D' ? 'D' : f(f => f(f => 'D'));
}
  • If opponent defects against cooperate, then invert opponent’s move against defect.
  • Else, if opponent defects against defect, then defect.
  • Else, mimic opponent’s move against notNice.

Another evolutionarily generated strategy.

\$\endgroup\$
6
\$\begingroup\$

WhatWouldBotDoBot

function WWBDB(them) {
    let start = performance.now();
    let cc = 0, cd = 0, dc = 0, dd = 0;
    try {
        for (let i = 0; i < 10; i++) {
            them(() => 'C') == 'C' ? cc++ : cd++;
            them(() => 'D') == 'C' ? dc++ : dd++;
            if (performance.now() - start > 500) break;
        }
    }
    catch (e) {}
    return 2 * cc >= 3 * dc + dd ? 'C' : 'D';
}

WhatWouldBotDoBot is fairly simple; it just tests its opponent for what it would do against a steady state program. If a bot prefers cooperating if possible, WWBDB will also prefer cooperation (so it will cooperate with nice bot). WWBDB does not itself prefer cooperation.

\$\endgroup\$
5
\$\begingroup\$

Check stateful

function checkStateful(them) {
  let stateful = false;
  let response = 'D';
  try {
    response = them(wrap(function (them) {
      stateful = true;
      return 'C';
    }));
  } catch (e) {
  }
  if (stateful) {
    return 'D';
  }
  return response;
}

If them invoke me, then them probably be a truly them. We act as defector. If them not invoke me, then them probably be a wrapped tester. We would act as nicer.


Above is the original answer. And maybe I should make myself cooperation to earn more points.

Check stateful with self-coop

function checkStatefulSelfCoop(them) {
  let stateful = false;
  let response = 'D';
  if (!checkStatefulSelfCoop.invokeCounter) {
    checkStatefulSelfCoop.invokeCounter = 0;
  }
  let lastInvoke = ++checkStatefulSelfCoop.invokeCounter;
  try {
    response = them(wrap(function (them) {
      stateful = true;
      return 'C';
    }));
  } catch (e) {
  }
  if (checkStatefulSelfCoop.invokeCounter > lastInvoke) {
    return 'C';
  }
  if (stateful) {
    return 'D';
  }
  return response;
}
\$\endgroup\$
3
\$\begingroup\$

RandomBot

function rand(them) {
  return 'CD'[Math.random() * 2 | 0]
}

Because why not.

\$\endgroup\$
3
\$\begingroup\$

Complexity

function complexity(them) {
    try {
        let coop_w_def = them(wrap(() => "D")) == "C",
            coop_w_coop = them(wrap(() => "C")) == "C",
            coop_w_nice = them(wrap((a) => a(wrap(() => "C")))) == "C",
            coop_w_nnice = them(wrap((a) => a(wrap(() => "D")))) == "C";
        if (coop_w_def && coop_w_coop && coop_w_nice && coop_w_nnice) return "C";
        let def_w_def = them(wrap(() => "D")) == "D",
            def_w_coop = them(wrap(() => "C")) == "D",
            def_w_nice = them(wrap((a) => a(wrap(() => "C")))) == "D",
            def_w_nnice = them(wrap((a) => a(wrap(() => "D")))) == "D";
        if (def_w_def && def_w_coop && def_w_nice && def_w_nnice) return "C";
    } catch (e) {}
    return "D";
}

Complexity tests to see whether the bot is Cooperate or Defect. If it is, it cooperates, but if it isn't, it defects. All current bots that test their opponents use simple functions to test the responses, so Complexity will just pretend to be Cooperate in those cases.

\$\endgroup\$
3
\$\begingroup\$
function onlyTrustYourself(them) {

  function tester (){
  }

  onlyTrustYourself.activated = false;

  try{them(tester);}
  catch(e){}

  if(them.name == "tester")
  {
    onlyTrustYourself.activated = true;
  }

  if(onlyTrustYourself.activated)
  {
    return 'C';
  }

  return 'D';
}

How I want this to work is to always defect, except for when playing against self. It tries to do that by passing a "tester" function which isn't wrapped to them, and it tries to detect if "them" is named tester. If it is named tester, it changes the static variable activated to true, then returns cooperate. But it doesn't work. I'm not too familiar with javascript, and I'll probably make some more changes.

\$\endgroup\$
  • \$\begingroup\$ clever idea, but what happens when another bro makes a tester function :D \$\endgroup\$ – V. Courtois Aug 6 at 12:35
2
\$\begingroup\$

NotNice

function NotNice(them) {
  return them(wrap(_ => "D"))
}

Imitates opponent's reaction to deflection

\$\endgroup\$
2
\$\begingroup\$

NotNice 2

function notNice2(them) {
  try {
    return them(wrap(_ => 'D'));
  } catch(e) {
    return 'D';
  }
}

Boom-proof version of NotNice by FatalError.

\$\endgroup\$
2
\$\begingroup\$

Common Sense

function commonSense(them) {
  try {
    var ifC = them(wrap(_ => 'C'));
    var ifD = them(wrap(_ => 'D'));

    if (ifD === 'C') {
      return 'D';
    }

    return them(_ => ifC);
  } catch (e) {
    return 'D';
  }
}

Disclaimer: I kinda don't know javascript.

If you can profit off a nice person, do it. Otherwise, return what they would return if they faced themselves cooperating (at least, that's what I think it does).

\$\endgroup\$
2
\$\begingroup\$

And you where do you wana go? (inspired by the voltures in the book of jungle)

    function yourself(them) {
      try{
        return them(this);
      }catch(e){
        return "D";
      }
    }

   function yourself_no_this(them) {
      try{
        return them(yourself_no_this);
      }catch(e){
        return "D";
      }
    }
\$\endgroup\$
  • \$\begingroup\$ This just won in a tournament I ran. Good job! \$\endgroup\$ – MegaTom Oct 12 '18 at 18:37
  • \$\begingroup\$ I just noticed that this bot violates the rules. "You may not access the variable self..." this is the same as self. I think that you wanted to say return them(yourself). \$\endgroup\$ – MegaTom Oct 15 '18 at 18:57
  • \$\begingroup\$ Technicaly ( xkcd.com/1475 ) ;) this is not a variable, it is a keyword, and in a context of a function this!=self. self would mean the window object and this the function itself (always refers to the context it is in, that is why it is not considered as a variables). That is why having var self = this; in the begining of many code-examples might be considered as misleading. Added version without the "this" \$\endgroup\$ – T.S. Oct 16 '18 at 11:34
  • 1
    \$\begingroup\$ No. this is not referring to the function. yourself and yourself_no_this run vary differently. this basically never refers to the function in javascript. See: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… \$\endgroup\$ – MegaTom Oct 16 '18 at 17:51
2
\$\begingroup\$

Punish Inspectors

Give the bot some code and see if it runs it. If it was run more then once, the bot is an evil inspector, and we must defect! If it was run exactly once, play as not nice bot. If it was never run, cooperate.

function punishInspectors(them) {
  var inspections = 0;
  var result;
  try{
    result = them(wrap(function(_){
      inspections += 1;
      return 'D';
    }))
  }catch(e){
    result = 'D';
  }
  return (inspections > 1) ? 'D' : (inspections === 1) ? result : 'C';
}

History

What would the last bot I saw do vs this opponent?

function history(them) {
  var res = 'D';
    if(history.last){
    try{
      res = history.last(them);
    }catch(ex){}
  }
  history.last = them;
  return res;
}

Results for a 10000 round tournament:

1  defect...................365226
2  complexity...............353492
3  punishInspectors.........349957
4  checkStatefulSelfCoop....348913
5  checkStateful............333481
6  cooperate................329870
7  archaeopteryx............323624
8  selfapply................319533
9  tiktaalik................318663
10 history..................315266
11 rand.....................300735
12 randalt..................297561
13 yourself.................293701
14 notNice2.................283744
15 NotNice..................260350
16 WWBDB....................245281
17 nice.....................245036
18 commonSense..............242546
19 trickybot................181696
20 boom.....................67245
\$\endgroup\$
1
\$\begingroup\$

TrickyBot

Try to be unpredictable

function trickybot(them) 
{
  if(Math.round(Math.random(2)) == 0)
  {
     throw 1;
  }

  if(Math.round(Math.random(2)) == 0)
  {
     return 'D';
  }

  return 'C';
}
\$\endgroup\$
1
\$\begingroup\$

selfapply

function selfapply(them) {
    function testthem(x) {
        return (them(x)=='D' || them(x)=='D' || them(x)=='D' ||
               them(x)=='D' || them(x)=='D')  ? 'D' : 'C';
    }
    function logic() {
        try {
            return testthem(them);
        } catch (e) {}
        try {
            return testthem(wrap(_ => 'C'));
        } catch (e) {}
        return 'D';
    }
    if (selfapply.hasOwnProperty('state')) {
        return 'C';
    }
    selfapply.state=1;
    let r=logic();
    delete selfapply.state;
    return r;
}

Not sure if it makes any sense, but it seems interesting! Do to you as you do to yourself, repeat to catch randomness. If that doesn't work, be nice.

Untested, and my first javascript code, and more complex than I expected.

\$\endgroup\$
  • \$\begingroup\$ This is going to disqualify itself because selfapply(selfapply) calls selfapply(selfapply)! \$\endgroup\$ – Anders Kaseorg Oct 3 '18 at 11:17
  • \$\begingroup\$ I did consider its own selfapplication, but thought it would be okay. I hope it really is now. \$\endgroup\$ – Christian Sievers Oct 3 '18 at 21:01
1
\$\begingroup\$

RandomAlternate

function randalt(them){
    if (randalt.hasOwnProperty('state')){
        randalt.state = 1 - randalt.state;
    } else {
        randalt.state = Math.floor(2*Math.random());
    }
    return 'CD'[randalt.state];
}

So I learned how to use properties for state...

\$\endgroup\$
1
\$\begingroup\$

Murder Bot #1

function murder(them) {
    while (1) {
        try {
            them(them);
        } catch (e) {}
    }
}

Causes an infinite loop in for which it is more likely that the opponent will be blamed.

\$\endgroup\$
1
\$\begingroup\$

The Platinum Rule Bot

function platinumRule(them) {
    try {
        return wrap(them)(them);
    } catch (e) {
        return 'C';
    }
}

The Platinum Rule states "Treat others the way they want to be treated." My bot accommodates that. Whatever they would do to themselves, which we assume is how they would want to be treated, we do to them. If they throw an error, we assume they want to cooperate.

\$\endgroup\$
  • \$\begingroup\$ This would actually go forever if it was called against itself \$\endgroup\$ – mackycheese21 Jun 18 at 0:26
  • \$\begingroup\$ then wouldn't it crash (stack overflow) and cooperate with itself? @mackycheese21 \$\endgroup\$ – V. Courtois Aug 6 at 12:42
1
\$\begingroup\$

TheGolfedOne (func name: a), 63 bytes

Golfed code is hard to read. Because of it, them will break.
I did not fully understand the mechanics under this KotH, but I suppose that if the opponent is stateless, I just need to break them while I defect.

function a(t){try{t(wrap(_=>'D'));throw 1}catch(e){return 'D'}}

His first tourney's result (I did not bother using all bots, sorry)

boom                     54
tiktaalik               180
archaeopteryx           161
cooperate               210
commonSense             210
history                 248
onlyTrustYourself       265 <-- 2nd
punishInspectors        230
yourself_no_this        220
defect                  280 <-- 1st
nice                    185
complexity              216
WWBDB                   210
checkStatefulSelfCoop   258
a                       260 <-- Me, 3rd

He's not doing as bad as I thought, 3rd place (among those) first try.
Second try, a got 260 again, 3rd place again, behind onlyTrustYourself and defect again. It might be consistent in the end :)

PS: I'm not that good with golfing so it's more for the joke than anything. Here I only shortened variable names, func name, and removed as much whitespace as possible.

\$\endgroup\$
0
\$\begingroup\$

Mockingbird (Non-Competing?)

function mockingbird(them) {
  var nest = arguments.callee.caller.caller.name;
  //Why yes officer, I'm a sweet little chickadee.
  return nest == "compete" ? 'D' : 'C';
  }

We're doing this all sneaky-beaky like. Mockingbird here is a cheap-cheap-cheater - it checks up the call stack two steps to grab the name of whoever is calling it through the wrap.

In any context other than when Mockingbird is being polled by compete(), it spits out a 'C'. In other words, any direct inspection (no matter how careful) will make the other bot think that Mockingbird is friendly and cooperative - even though it's anything but.

Mockingbird doesn't technically break the rules - everything I'm accessing is happily inside of Mockingbird's scope, and I never touch the stack trace (I just climb up the stack directly). Still...

I'm mostly submitting this little birdie to see if anyone can come up with a strategy that can outcompete a perfect liar.

\$\endgroup\$
  • \$\begingroup\$ function trap(them) { arguments.callee.caller.caller.__defineGetter__("name", (_=>"not compete")); return "D"; } will transform your bot into cooperate. \$\endgroup\$ – MegaTom Oct 19 '18 at 18:28
  • \$\begingroup\$ @MegaTom Why not throw in the __defineGetter__. \$\endgroup\$ – tsh Oct 25 '18 at 7:49
  • \$\begingroup\$ @MegaTom @tsh Clever - throw is actually weaker, tsh, since I could revise Mockingbird to protect against it. There's pretty much nothing I can do if someone fiddles with the name :p. \$\endgroup\$ – M Dirr Oct 25 '18 at 14:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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