The Darwin Game - Rounds 21-500

by lsusr2 min read21st Nov 202016 comments

31

Prisoner's DilemmaRationality
Personal Blog

Rounds 20-30

EarlyBirdMimicBot takes the lead off the backs of the Clone Army.

Rounds 30-100

EarlyBirdMimicBot exhausts the clone army before the treaty expires in turn 90.

Rounds 100-500

From here on out it is a random walk. The bots with low populations die to variance. Congratulations to BeauBot and Insub's CooperateBot for making it this far!

Winners

Note: This is an alternate timeline. It is not the official tournament.

  1. EarlyBirdMimicBot by Multicore
  2. BendBot by Zvi
  3. MeasureBot by Measure
  4. LiamGoddard by Liam Goddard

Today's Obituary

BotTeamSummaryRound
RaterBotChaos ArmyEstimates opponent's aggression by counting the number of 3s, 2s, return 3s and return 2 instances in its source code. Then picks a strategy based off of that.21
CopoperaterChaos ArmyTit-for-tat, starting at 2.22
RandomOrGreedyBotChaos ArmyIf the opponent averaged less than 2.5 over the last 100 turns then plays int(5 - opponent_avg). Otherwise randomly selects 3 or 2 randomly.24
Silly TFT Bot 3NPCsPlays tit-for-tat starting at 3.28
EmpiricistChaos ArmyPerforms the best strategy that would have worked against historical data.28
CopyBot DeluxeChaos ArmyTit-for-tat. Picks starting value of 2 or 3 based off of round number.32
Pure TFTChaos Army"For the first round, play 2 or 3 with a 50/50 chance of each. For each subsequent round, play whatever the opponent played on the previous round."36
Silly TFT Bot 2Chaos ArmyPlays tit-for-tat starting at 2.40
CloneBotClone ArmyCloneBot. Died before the treaty broke.42
jacobjacob-BotNorm EnforcersCooperates with Ben-Bot42
SimplePatternFinderBotChaos ArmyFinds simple patterns.42
Silly 2 BotNPCsAlways returns 2.43
Winner against low constant botsChaos ArmyStarts with 2. Then always returns 5 - opponent_previous_move.44
Clone wars, episode return 3Clone ArmyCloneBot. Died before the treaty broke.50
a_comatose_squirrelClone ArmyCloneBot. Died before the treaty bre52
CliqueZviBotClone ArmyCloneBot. Died before the treaty bre53
incomprehensibotClone ArmyCloneBot. Died before the treaty bre53
A Very Social BotClone ArmyCloneBot. Died before the treaty bre57
KarmaBotClone ArmyCloneBot. Died before the treaty bre58
Akrasia BotClone ArmyCloneBot. Died before the treaty bre60
AttemptAtFairChaos ArmyOscillates between 3 and 2, starting with 3.95
OscillatingTwoThreeBotChaos Army"cooperates in the dumbest possible way"95
Why can't we all just get alongChaos ArmyDoesn't negotiate with terrorists. Doesn't overly punish slackers. Attempts to establish steady tit-for-tat.98
BeauBotChaos ArmyA sophisticated bot with 528 lines. It picks one of 3 simple strategies based on it's opponent's behavior. It also adjusts its behavior based on the round.113
CooperateBot [Insub]Chaos ArmyLet MLM = my last move, OLM = opponent's last move. On the first turn, play 2. On subsequent turns: [Fork 1] If (MLM + OLM = 5), then play OLM [Fork 2] Otherwise, flip a coin and play max(MLM, OLM) with 50% probability, and (5 - max(MLM, OLM)) with 50% probability.254

This concludes the alternate timeline where AbstractSpyTreeBot was disqualified by mistake. The Mutant Game (the Blind Idiot God alternate timeline with multiple game engine bugs) will resume on November 23, 2020.

31

14 comments, sorted by Highlighting new comments since Today at 6:06 AM
New Comment

An interesting note is that BendBot gets an endgame advantage here for sometimes starting 2, but if there were 101 turns per round instead of 100 that would have been punished instead.

I am flattered you called my bot sophisticated. I think its is partly a function of my circuitous and inexperienced coding more so than needed complexity. The code is below:

class BeauBot():
    # bot attempts to cooperate and will even risk three points here and there in early rounds
    # if other bot defects in later rounds, BeauBot will bid 4's until other bot bids 1
    def __init__(self, round=0):
        self.round = round
        self.turn = 0
        self.myPrevious = None
        self.theirPrevious = None
        self.strategy = None

    
    def move(self, previous=None):
        # sets starting bid to be 2 in before round 10,
        # alternating 2 and 3 between rounds 10 and 30, and 3 in later rounds
        self.turn += 1
        self.theirPrevious = previous
        if self.theirPrevious == None:
            if self.round < 10:
                self.myPrevious = 2
                return self.myPrevious
            elif self.round < 30:
                self.myPrevious = self.round % 2 + 2
                return self.myPrevious
            else:
                self.myPrevious = 3
                return self.myPrevious
        else:
            self.myPrevious = self.strategyPicker()
            return self.myPrevious
        

    def strategyPicker(self):
        # chooses between 3 simple strategies to use based on current strategy and previous bids
        if self.theirPrevious == 1 or self.theirPrevious == 2 and self.strategy != 'hard':  # will always cooperate if other bot bids low if not using hard strategy
            return self.titForTat()
        elif self.theirPrevious == 3 or self.strategy == 'titForTat':                       # if in tit for tat strategy, will allow higher numbers to be alternated
            if self.strategy == 'titForTat' or (self.myPrevious == 3 and self.round < 11):  # will only cooperate with 3 bids if self.myPrevious was 3
                return self.titForTat()
            else:
                if self.round < 5:                                                          # bot will be try to get points from aggressors in early rounds
                    self.strategy = 'soft'
                    return self.soft()
                else:
                    self.strategy = None
                    return 3
        else:
            if self.round < 5:
                self.strategy = 'soft'
                return self.soft()
            else:
                self.strategy = 'hard'
                return self.hard()
                

    def titForTat(self):
        #will copy opponent move if self.theirPrevious sum = 5, will try to fix if bids not adding to 5,
        #will switch out of tit for tat  if opponent continues high bids
        if self.theirPrevious + self.myPrevious == 5:                                        # could allow for 4-1 alternating bids only if opponent bids 1 when bot was bidding 4 (from hard strategy)
            self.strategy = 'titForTat'
            return self.theirPrevious
        elif self.theirPrevious + self.myPrevious < 5:
            self.strategy = 'titForTat'
            return self.theirPrevious + self.turn % 2                                        # only tries to correct every other round in case other bot is also correcting for underbiding in cooperation
        elif self.theirPrevious + self.myPrevious > 5:                                       # will not cooperate will not continue tit for tat if a 3 bid is made after failing to cooperate in the self.theirPrevious round
            self.strategy = None
            if self.myPrevious > 2:
                return 2
            else:
                if self.round < 5:
                    self.strategy = 'soft'
                    return self.soft()
                else:
                    self.strategy = 'hard'
                    return self.hard()

    def soft(self):
        #will bid 1 or 2 if opponent bids 4 or 3 respectively, only to be used in early rounds
        #intended to get early growth by feeding bullies and attackers early
        # will switch to titFortat if other bot bids low
        if self.theirPrevious > 2 and self.theirPrevious < 5:
            return 5 - self.theirPrevious
        elif self.theirPrevious < 3:
            self.strategy = 'titForTat'
            return self.theirPrevious
        return 1


    def hard(self):
        # will return 4 as long as opponent does not return 1
        # if opponent returns 1, the extra 4 points should be enough buffer to attempt cooperation again
        if self.theirPrevious == 1:
            self.strategy = 'titForTat'
            return 2
        else:
            self.strategy == 'hard'
            return 4

Wow, that was not what I would have predicted from the last set of results. It looks like the trend in my population from when the clones were a little nasty accelerated when the clones got really nasty.

In the true game with AbstractSpyTreeBot, MeasureBot is going to be eating it at the same time I'm eating the clones, but is it going to be a boost as extreme as this?

In the endgame everyone cooperates with everyone else, and it seems to be down to openings and match-breaking strategies. LiamGoddard and MeasureBot always start 3, BendBot alternates between starting 2 and starting 3 on a per-round basis, and EarlyBirdMimicBot randomly starts 2 or 3 using the python random number generator. BendBot uses a fixed pseudorandom sequence to try to break matches, EarlyBirdMimicBot picks 2 or 3 with equal probability, MeasureBot plays 2 with probability 0.69 possibly with some special cases, and I'm not sure what LiamGoddard does.

Silly 2 bot lasted longer than anyone could have reasonably expected. The Clone Wars presumably gave it a second wind, as folding was a sound strategy against the clones' aggressive plays.

BeauBot in sixth place is the best scoring bot we don't have a clear explanation of yet.

In the true game with AbstractSpyTreeBot, MeasureBot is going to be eating it at the same time I'm eating the clones, but is it going to be a boost as extreme as this?

I think it will depend on how long ASTB survives. Even with MeasureBot feeding on it, ASTB does very well in the early game against many of the silly bots. It folds to attackers and cooperates with the clones and other cooperators. However, I still expect (80%) that EBMB will pull ahead before round 100. As the clones become more aggressive in the midgame, they start fighting with MeasureBot while continuing to cooperate with EBMB.

I have a theory that ASTB might end up helping EBMB somewhat too as some of ASTB's mass goes to the clones which keeps them alive for longer for EBMB to feed off. Still most should go straight to MB so you'll get the bigger boost, just probably not enough.

My understanding is that EBMB doesn't directly feed off the clones - they mutually cooperate until round 90, so EBMB helps them as much as they help it. It's just that the clones start attacking everyone else in the midgame and leave EBMB alone while EBMB continues to cooperate with everyone. Thus the clone army and the rest of the pack tear each other down while EBMB pulls ahead.

A larger clone share would indeed end up benefitting EBMB relative to MB, but if ASTB is more effective than the clones in the early game, I'm not sure its presence in the midgame would be more helpful to the clones than a typical chaos or silly bot. I guess it depends on how effectively the clones' aggression can convince ASTB to fold. The clones may or may not get enough back to compensate for their reduced share in the early game.

(Aside: I continue to be quietly amused at how much AbstractSpyTreeBot has ended up affecting the game (by partially inspiring EarlyBirdMimicBot and getting exploited by MeasureBot and accidentally spawning a new timeline), given how little I've invested in the game compared to others—the backstory is not one of strategic thinking about how to compete, but rather, I was feeling bleh in the afternoon of 9 October, and writing a simulator-bot using the ast module just seemed like a fun exercise that should only take a couple hours.)

It also significantly affected people's expectations of the metagame and made them prepare for simulators. BendBot and CloneBot were made deterministic so that simulators could efficiently cooperate with them.

See my other comment for BeauBot's code. Below round 10 BeauBot starts 2, then from 10 to 29 it alternates 2 and 3 as starting bid, and 30 and beyond it always starts with 3.

I tried to make my bot never be outscored past round 29 but I believe if my opponent bids three when BeauBot bids 3 in the first round, Beaubot will bid 2 in attempts to cooperate making it possible for opponents to outscore it.

After round 100, MeasureBot ensures that it is never outscored by its opponent (it always starts 3, and keeps playing 3 until symmetry is broken by the opponent). If two or more bots have this behavior in the endgame, they quickly destroy each other until only one remains.

Congratulations to the two oscillator bots for lasting longest with no complexity, even beating out "silly 2" and TFT. Interesting how much variance there was among the 4 TFT bots based only on what they played first!

This has been a really interesting game! It's good to see I survived, although I barely made it to the end.

I assume that when you run the 'official' timeline, the arrival of AbstractSpyTreeBot will bring MeasureBot higher than in this, so MeasureBot will probably reach second place. But even with randomness as a factor, I doubt such a small change would disrupt EarlyBirdMimicBot's serious advantage. I think we can probably say Multicore is the winner.

I'd be interested in watching you continue the game past round 500- EarlyBirdMimicBot would most likely remain in first, and me in fourth, but I would want to see if things change between MeasureBot and BendBot. However, it might be a while before those rounds could be run- by this point we're seeing more alternate timelines than Doctor Strange. (Or the Guardians of the Universe, if you're more of a DC fan.)

One interesting graph would be the average points gained per matchup vs round number which would give a good indication of cooperation level and what kinds of strategies would work. It can kind of be inferred from the bots which are left but seeing a graph would make it easier to picture.