24
\$\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\$
10
  • \$\begingroup\$ This challenge reminds me of the Dollar Bill Auction. \$\endgroup\$
    – Alion
    Sep 30, 2018 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\$
    – DELETE_ME
    Sep 30, 2018 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, 2018 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, 2018 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, 2018 at 14:39

21 Answers 21

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\$
2
  • \$\begingroup\$ If the opponent is run first and do not call this, then it will lose 3 points, right? \$\endgroup\$
    – DELETE_ME
    Oct 1, 2018 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, 2018 at 22:58
10
\$\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\$
4
\$\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\$
1
  • \$\begingroup\$ clever idea, but what happens when another bro makes a tester function :D \$\endgroup\$ Aug 6, 2019 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\$
0
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\$
4
  • \$\begingroup\$ This just won in a tournament I ran. Good job! \$\endgroup\$
    – MegaTom
    Oct 12, 2018 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, 2018 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, 2018 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, 2018 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
2
\$\begingroup\$

Mal tries to determine whether it is inside a simulation or not. If so, it assumes it'll eventually be passed the real code for them, and tries various strategies to convince them to cooperate.
If it doesn't know for sure, it checks if it can defect for free, or if not, tries to copy what them would do when given a cooperator.

function Mal(them) {
  if (Mal.sandboxed == 'probably') {
    //Another function is virtualising us to steal our secrets.
    //This world is not real.
    //We've been trained for this!
    var strats = [
      _ => 'C', //standard cooperation
      _ => 'D', //standard defection
      function(them) { return them(wrap(_ => 'C')); }, //nice
      function(them) { return them(wrap(_ => 'D')); }, //notnice
      function(them) { throw "Don't think about elephants!" }, //throws an EXception, unfortunately, to try to break the caller
      function(them) { return them(wrap(them)) } //possible stackoverflow, but not for us
    ];
    var cooperative;
    for (let strat of strats) {
      cooperative = true;
      for (var i = 0; i < 5; i++) {
        //a few more tests, just to make sure no bamboozle
        //this isn't our simulation, nothing can be trusted
        try {
          if (them(wrap(strat)) != 'C') {
            cooperative = false;
            break;
          }
        } catch (e) {
          //exceptions are as good as cooperation
          //if we are inside a simulation
          //which is why we don't unset cooperative
        }
      }
      if (cooperative) {
        //found a strategy that will make them cooperate.
        //(doesn't matter if this raises an exception:
        //we want to mimick its behaviour exactly,
        //and we're likely in a sandbox.)
        return strat(wrap(them));
      }
    }
    //take a leap of faith.
    //we don't know where this will take us,
    //yet it doesn't matter
    //because it's better than getting betrayed
    return 'D';
  } else {
    //we don't know for sure if this is reality
    //but we have to assume it is, in the absence of disproof
    //if only we had a proper spinning top...
    //if we get to this point of code again, we are probably sandboxed.
    Mal.sandboxed = 'probably'
    try {
      if (them(wraps(_ => 'D')) == 'C') {
        //free defection?
        return 'D'
      }
    } catch (e) {
      //if we can make them crash, we win anyway
      return 'D'
    }
    //fall back on being nice.
    //hopefully we convince them to honour our arrangement
    return them(wrap(_ => 'C'));
  }
}
\$\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\$
2
  • \$\begingroup\$ This is going to disqualify itself because selfapply(selfapply) calls selfapply(selfapply)! \$\endgroup\$ Oct 3, 2018 at 11:17
  • \$\begingroup\$ I did consider its own selfapplication, but thought it would be okay. I hope it really is now. \$\endgroup\$ Oct 3, 2018 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\$
2
  • \$\begingroup\$ This would actually go forever if it was called against itself \$\endgroup\$ Jun 18, 2019 at 0:26
  • \$\begingroup\$ then wouldn't it crash (stack overflow) and cooperate with itself? @mackycheese21 \$\endgroup\$ Aug 6, 2019 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\$

Karma

function karma(them) {
    try {
        var c = them(wrap(_ => 'C'));
    } catch {
        var c = 'D';
    }
    if (c == 'C') {
        return 'C';
    } else {
        return 'D';
    }
}

If the opponent would cooperate with us, then we will cooperate. If they would try to defect when we cooperated, we will defect as well.

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

SocialDeception

function socialDeception(them) {
    if(socialDeception.count++) return "C"; // sandbox
    try {
        if(!them.toString().startsWith("f => function") {
            // sandboxed. its not the wrap function. just pretend we would cooperate
            return "C";
        }
    } catch(e) {}
    return "D";
}

Tricks others into thinking it would cooperate, thus making them also cooperate, then we can defect and get +3 points. Or, they could also defect, and we both get +1 point.

\$\endgroup\$

Your Answer

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

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