Prisoner's Dilemma v.3 - Petri Dilemma

17

5

A mad scientist has just created a new species of bacterium! He has decided to name it Noblus Gentlemanus, after observing its behaviour. However, his bacteria have ran out of food, and have declared war, as they are able to harvest other bacterium's corpses for enough food to create copies of themselves. There are a number of different subspecies of this bacterium, who have different strategies in playing Prisoner's Dilemma, their favourite game. There are five bacteria from each different subspecies. In Prisoner's Dilemma, each of the two players simultaneously chooses either defect or cooperate. If one player chooses cooperate and the other chooses default, the defaulter gets 2 points, and the cooperator loses 3 points. If both player choose cooperate, both players get one 1 point. If both players choose default, both players lose 1 point.

Being Noble Gentlemen, the bacteria have decided to fight this war by playing 200 round long games of Iterated Prisoner's Dilemma. The loser of each duel will commit suicide, allowing the victor to clone itself. In the event of a tie, both bacteria will remain alive, but neither will be able to clone itself. In addition, all bacteria from a match carry over 10% of their points to the next match. A clone carries over the points of the bacterium it was cloned by. Also, there is a one in ten chance each turn that one bacterium will mutate into another subspecies, with 0 bonus points (if I receive complaints about the randomness of this, I can remove it). After the bacteria have played a number of these duels equal to the number of subspecies of bacteria times ten, the mad scientist accidentally drops the petri dish the bacteria reside in, and all of the bacteria acquire new sources of food, ending their duels. This is different from an ordinary iterated prisoner's dilemma contest, as it involves 1v1 duels with carryover points, rather than simply attempting to get the most points overall. This makes a big difference in how effective a given strategy is.

Each bacterium will receive input at the beginning of its turn in the format: (turn number, current points, enemies points, your previous moves [in a string, using the character "c" for cooperate, and the character "d" for defect], enemies previous moves [in the same format]).

Here are four sample strategies that will be entered. I actually think that Defector might win, even though it is extremely simple.

Tit for Tat

def titfortatfunc(counter, mypoints, enpoints, mylist, enlist):
    if counter==0 or enlist[counter-1] == "c":
        return "c"
    else:
        return "d"

RandomPick

from random import choice
def randompickfunc(counter, mypoints, enpoints, mylist, enlist):
    if counter == 199:
        return "d"
    else:
        return choice(["d", "c"])

Cooperator

def cooperatorfunc(counter, mypoints, enpoints, mylist, enlist):
    return "c"

Defector

def defectorfunc(counter, mypoints, enpoints, mylist, enlist):
    return "d"

All submissions must be in the form of a Python 2.7 function, with the name being the name of the submission with no spaces, with func on the end. If someone would like to submit an answer in a different language, please enter it in pseudo-code, for me to convert to Python in an edit to your answer once I have time, or give me instructions on interfacing your language with my controller which is found below set up for all submissions as of June 4.

from titfortat import titfortatfunc
from randompick import randompickfunc
from cooperator import cooperatorfunc
from defector import defectorfunc
from luckytitfortat import luckytitfortatfunc
from randomtitfortat import randomtitfortatfunc
from remorsefulaggressor import remorsefulaggressorfunc
from everyother import everyotherfunc
from niceguy import niceguyfunc
from titfortatbackstab import titfortatbackstabfunc
from gentleDefector import gentleDefectorfunc
from anticapitalist import anticapitalistfunc
from grimtrigger import grimtriggerfunc
from bizzaro import bizzarofunc
from neoanticapitalist import neoanticapitalistfunc
from bittertat import bittertatfunc
from teamer import teamerfunc
from copyfirst import copyfirstfunc
from exploitivetat import exploitativetatfunc
from defectorv2 import defectorv2func
from crazytat import crazytatfunc
from randomchoicev2 import randomchoicev2func
from twotitsforatat import twotitsforatatfunc
from threetitsforatat import threetitsforatatfunc
from fourtitsforatat import fourtitsforatatfunc
from fivetitsforatat import fivetitsforatatfunc
from sixtitsforatat import sixtitsforatatfunc
from tentitsforatat import tentitsforatatfunc
from theelephant import theelephantfunc
from xbittertat import xbittertatfunc
from fifteentitsforatat import fifteentitsfortatfunc
from twentytitsforatat import twentytitsforatatfunc
from fox import foxfunc
from onehundredfortysixtitsforatat import onehundredfourtysixtitsforatatfunc
from gameofthrones import gameofthronesfunc
from boy import boyfunc
from grimace import grimacefunc
from fiftytitsforatat import fiftytitsfortatfunc
from soreloser import soreloserfunc
from everyotherd import everyotherdfunc
from fiftythreetitsfortat import fiftythreetitsfortatfunc
from twentyfivetitsfortat import twentyfivetitsfortatfunc
from handshake import handshakefunc
from anty import antyfunc
from fiftyfourtitsforatat import fiftyfourtitsfortatfunc
from kindatitsfortat import kindatitsfortatfunc

import random

players = 38

rounds = players*10

def runcode(num, points1, points2, history1, history2, cell):
    ans = ""
    if cell == 0:
        ans = titfortatfunc(num, points1, points2, history1, history2)
    elif cell == 1:
        ans = randompickfunc(num, points1, points2, history1, history2)
    elif cell == 2:
        ans = cooperatorfunc(num, points1, points2, history1, history2)
    elif cell == 3:
        ans = defectorfunc(num, points1, points2, history1, history2)
    elif cell == 4:
        ans = luckytitfortatfunc(num, points1, points2, history1, history2)
    elif cell == 5:
        ans = randomtitfortatfunc(num, points1, points2, history1, history2)
    elif cell == 6:
        ans = remorsefulaggressorfunc(num, points1, points2, history1, history2)
    elif cell == 7:
        ans = everyotherfunc(num, points1, points2, history1, history2)
    elif cell == 8:
        ans = niceguyfunc(num, points1, points2, history1, history2)
    elif cell == 9:
        ans = titfortatbackstabfunc(num, points1, points2, history1, history2)
    elif cell == 10:
        ans = gentleDefectorfunc(num, points1, points2, history1, history2)
    elif cell == 11:
        ans = anticapitalistfunc(num, points1, points2, history1, history2)
    elif cell == 12:
        ans = grimtriggerfunc(num, points1, points2, history1, history2)
    elif cell == 13:
        ans = bizzarofunc(num, points1, points2, history1, history2)
    elif cell == 14:
        ans = neoanticapitalistfunc(num, points1, points2, history1, history2)
    elif cell == 15:
        ans = tentitsforatatfunc(num, points1, points2, history1, history2)
    elif cell == 16:
        ans = bittertatfunc(num, points1, points2, history1, history2)
    elif cell == 17:
        ans = copyfirstfunc(num, points1, points2, history1, history2)
    elif cell == 18:
        ans = exploitativetatfunc(num, points1, points2, history1, history2)
    elif cell == 19:
        ans = sixtitsforatatfunc(num, points1, points2, history1, history2)
    elif cell == 20:
        ans = fifteentitsfortatfunc(num, points1, points2, history1, history2)
    elif cell == 21:
        ans = fivetitsforatatfunc(num, points1, points2, history1, history2)
    elif cell == 22:
        ans = twentytitsforatatfunc(num, points1, points2, history1, history2)
    elif cell == 23:
        ans = threetitsforatatfunc(num, points1, points2, history1, history2)
    elif cell == 24:
        ans = fiftyfourtitsfortatfunc(num, points1, points2, history1, history2)
    elif cell == 25:
        ans = theelephantfunc(num, points1, points2, history1, history2)
    elif cell == 26:
        ans = xbittertatfunc(num, points1, points2, history1, history2)
    elif cell == 27:
        ans = foxfunc(num, points1, points2, history1, history2)
    elif cell == 28:
        ans = gameofthronesfunc(num, points1, points2, history1, history2)
    elif cell == 29:
        ans = boyfunc(num, points1, points2, history1, history2)
    elif cell == 30:
        ans = grimacefunc(num, points1, points2, history1, history2)
    elif cell == 31:
        ans = soreloserfunc(num, points1, points2, history1, history2)
    elif cell == 32:
        ans = everyotherdfunc(num, points1, points2, history1, history2)
    elif cell == 33:
        ans = twentyfivetitsfortatfunc(num, points1, points2, history1, history2)
    elif cell == 34:
        ans = fiftythreetitsfortatfunc(num, points1, points2, history1, history2)
    elif cell == 35:
        ans = handshakefunc(num, points1, points2, history1, history2)
    elif cell == 36:
        ans = antyfunc(num, points1, points2, history1, history2)
    elif cell == 37:
        ans = kindatitsfortatfunc(num, points1, points2, history1, history2)


    return ans

def fight(l1,l2):
    num1,num2=l1[0],l2[0]
    points1,points2=l1[1],l2[1]
    history1 = ""
    history2 = ""

    for num in range(200):
        p1 = runcode(num, points1, points2, history1, history2, num1)
        p2 = runcode(num, points2, points1, history2, history1, num2)

        history1+=p1
        history2+=p2

        if p1 == "c" and p2 == "c":
            points1 += 1
            points2 += 1
        elif p1 == "c" and p2 == "d":
            points1 -= 3
            points2 += 2
        elif p1 == "d" and p2 == "c":
            points1 += 2
            points2 -= 3
        elif p1 == "d" and p2 == "d":
            points1 -= 1
            points2 -= 1

    if points1 > points2:
        return [l1[0], points1/10], [l1[0], points1/10]
    elif points1 < points2:
        return [l2[0], points2/10], [l2[0], points2/10]
    else:
        return [l1[0], points1/10], [l2[0], points2/10]

def rounddoer(bots):
    bots2=[]
    for x in range(len(bots)):
        if x%2==0:
            out1, out2 = fight(bots[x], bots[x-1])
            bots2.append(out1)
            bots2.append(out2)

    return bots2

def gamedoer():

    bots=[[0,0],[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,0],[12,0],[13,0],[14,0],[15,0],[16,0],[17,0],[18,0],[19,0],[20,0],[21,0],[22,0],[23,0],[24,0],[25,0],[26,0],[27,0],[28,0],[29,0],[30,0],[31,0],[32,0],[33,0],[34,0],[35,0],[36,0],[37,0],[0,0],[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,0],[12,0],[13,0],[14,0],[15,0],[16,0],[17,0],[18,0],[19,0],[20,0],[21,0],[22,0],[23,0],[24,0],[25,0],[26,0],[27,0],[28,0],[29,0],[30,0],[31,0],[32,0],[33,0],[34,0],[35,0],[36,0],[37,0],[0,0],[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,0],[12,0],[13,0],[14,0],[15,0],[16,0],[17,0],[18,0],[19,0],[20,0],[21,0],[22,0],[23,0],[24,0],[25,0],[26,0],[27,0],[28,0],[29,0],[30,0],[31,0],[32,0],[33,0],[34,0],[35,0],[36,0],[37,0],[0,0],[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,0],[12,0],[13,0],[14,0],[15,0],[16,0],[17,0],[18,0],[19,0],[20,0],[21,0],[22,0],[23,0],[24,0],[25,0],[26,0],[27,0],[28,0],[29,0],[30,0],[31,0],[32,0],[33,0],[34,0],[35,0],[36,0],[37,0],[0,0],[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,0],[12,0],[13,0],[14,0],[15,0],[16,0],[17,0],[18,0],[19,0],[20,0],[21,0],[22,0],[23,0],[24,0],[25,0],[26,0],[27,0],[28,0],[29,0],[30,0],[31,0],[32,0],[33,0],[34,0],[35,0],[36,0],[37,0]]
    random.shuffle(bots)
    counter=0

    while counter < rounds:

        counter += 1
        bots = rounddoer(bots)

        if random.randint(0,10) == 9:
            bots[random.randint(0, players*5)-1] = [random.randint(0, players-1), 0]

        random.shuffle(bots)

##        for item in bots:
##            print str(item[0]) + " with " + str(item[1]) + " bonus points."

    return bots

a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,a33,a34,a35,a36,a37,mycounter=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

while mycounter < 1000:
    mycounter += 1
    bots = gamedoer()

    print "Game: " + str(mycounter)

    for item in bots:
        if item[0]==0:
            a0 += 1
        if item[0]==1:
            a1 += 1
        if item[0]==2:
            a2 += 1
        if item[0]==3:
            a3 += 1
        if item[0]==4:
            a4 += 1
        if item[0]==5:
            a5 += 1
        if item[0]==6:
            a6 += 1
        if item[0]==7:
            a7 += 1
        if item[0]==8:
            a8 += 1
        if item[0]==9:
            a9 += 1
        if item[0]==10:
            a10 += 1
        if item[0]==11:
            a11 += 1
        if item[0]==12:
            a12 += 1
        if item[0]==13:
            a13 += 1
        if item[0]==14:
            a14+=1
        if item[0]==15:
            a15+=1
        if item[0]==16:
            a16+=1
        if item[0]==17:
            a17+=1
        if item[0]==18:
            a18 += 1
        if item[0]==19:
            a19+=1
        if item[0]==20:
            a20+=1
        if item[0]==21:
            a21+=1
        if item[0]==22:
            a22+=1
        if item[0]==23:
            a23+=1
        if item[0]==24:
            a24+=1
        if item[0]==25:
            a25+=1
        if item[0]==26:
            a26+=1
        if item[0]==27:
            a27+=1
        if item[0]==28:
            a28+=1
        if item[0]==29:
            a29+=1
        if item[0]==30:
            a30+=1
        if item[0]==31:
            a31+=1
        if item[0]==32:
            a32+=1
        if item[0]==33:
            a33+=1
        if item[0]==34:

This Contest is now Finished

If you would like to add an answer, I'll see if I can get around to adding a post-challenge scoreboard under the one for the original contestants. I'll be adding that one as soon as the testing program finishes (probably 2-3 more days).

FINAL SCORES!!!!!

Tit for Tat: 18
Random Pick: 28
Cooperator: 19
Defector: 24
Lucky Tit for Tat: 23
Random Tit for Tat: 23
Remorseful Aggressor: 22
Every Other C: 23
Nice Guy: 18
Tit for Tat Backstab: 15
Gentle Defector: 22
Anticapitalist: 27
Grim Trigger: 19
Bizzaro: 21
NeoAnticapitalist: 24
Ten Tits for a Tat: 240
Bitter Tat: 12
Copy First: 30
Exploitative Tat: 19
Six Tits for a Tat: 16
Thirty Tits for Tat: 4129
Five Tits for a Tat: 22
Forty Tits for a Tat: 1972
Three Tits for a Tat: 22
Fifty Four Tits for a Tat: 25805
The Elephant: 31
Extra Bitter Tat: 28
Fox: 35
Game of Thrones: 11297
The Boy: 31
Grimace: 26
Sore Loser: 39
Every Other D: 18
Twenty Five Tits for a Tat: 2399
Fifty Three Tits for a Tat: 5487
Handshake: 28
Anty: 26
Kinda Tits for Tat: 20
Prudent Defector: 154539
Bizzarro Trigger: 25
Young Mathematician: 21
Older Mathematician: 16
Perfect Gentleman: 1953341

So, it appears that Perfect Gentleman is the winner. Congratulations to Draco18, who definitely deserves his green checkmark.

Gryphon

Posted 2017-05-23T00:22:31.773

Reputation: 6 697

Comments are not for extended discussion; this conversation has been moved to chat.

– Dennis – 2017-05-31T22:37:21.003

1NOTE: IF YOU ARE EDITING YOUR PROGRAM, PLEASE GIVE ME A COMMENT SO I NOTICE, OR IT MAY NOT BE PUT ON THE SCOREBOARD FOR A WHILE!!!!!!!!!!!!!!!!!!!!!!! – Gryphon – 2017-06-01T00:52:53.757

Yikes! I just realised how many imports that is. – Gryphon – 2017-06-04T20:56:59.607

1Hey Gryphon, you working on those final rankings? ;) – Draco18s no longer trusts SE – 2017-07-09T15:07:24.147

Sorry, I forgot about this. Give me a bit to run it. – Gryphon – 2017-07-09T15:48:28.780

OK, running as we speak. This is going to take a LONG time, so it probably won't be up for a couple days. – Gryphon – 2017-07-09T18:51:52.000

Looks like PG is just going to miss the 2 million mark. – Gryphon – 2017-07-10T01:28:20.030

@Draco18s, I'm at round 2050, and PG just broke 400,000. Should be able to post in 3-4 days. – Gryphon – 2017-07-11T00:25:36.057

@Gryphon oh wow, taking a while! Good to know. – Draco18s no longer trusts SE – 2017-07-11T01:39:24.377

Sorry, but 10,000 games is a lot to run. Especially when your computer is, well, my computer. – Gryphon – 2017-07-11T01:47:56.493

Are you going to post the final results? – MegaTom – 2017-07-11T20:15:25.623

As soon as they're done. I started 2 days ago, and I'm on round 3,380 out of 10,000 – Gryphon – 2017-07-11T20:17:50.423

My computer's slow. – Gryphon – 2017-07-11T20:18:15.143

OK, so I've got bad news, and worse news. – Gryphon – 2017-07-16T15:06:14.057

The bad news is that windows updated, stopping my program, so I have to run it again. – Gryphon – 2017-07-16T15:13:11.867

The worse news, is that I'll be gone for the next week, so I won't have access to the program. Unfortunately, it looks like you'll have to wait until next Sunday or Monday for the results. I can confirm, however, that Perfect Gentleman is almost definitely the winner. – Gryphon – 2017-07-16T15:14:26.463

No worries, Gryphon. :) – Draco18s no longer trusts SE – 2017-07-23T19:03:25.937

Answers

8

The Perfect Gentleman

I don't have a good description for this bot. I stumbled into a couple of potential optimizations, tested them, fine tuned, and ended up with a bacteria that utterly destroys the competition. Instead, I've commented the code itself to explain what it does.

import random
def perfectgentlemanfunc(num, i, d, c, en):
    if num>0 and i < 0 and d > 0 and -i%3 == 0 and d%2 == 0 and en[0] == "d":
        #probably very first iteration, probably facing a defector: feed it free points
        #    defector cannot be beaten by *any* bot unless that bot
        #    entered with a point lead. defector does some of our work for us
        if num >= 140:
            #140 threshold restricts how much we feed
            return "d"
        return "c"
    turn_to_betray = 130
    if num > turn_to_betray and en[turn_to_betray -2] == "c" and
     en[turn_to_betray -1] == "c" and en[turn_to_betray] == "d":
        #if self, then sacrifice the lower point bot to raise the points of the higher
        #(better net outcome than "c/c" cooperation)
        #    Handshake independently arrived at this same optimization
        if i == d:
            #max 50% probability of choosing different possible. May as well take it
            #    "ccd" has a 55% chance of choosing the same
            #    better outcomes for splitting early
            return "cd"[random.randint(0,1)]
        if i > d:
            return "d"
        return "c"
    #betray after betray point, or if behind by >200
    #performs 6 percentage points better than not having the condition
    if num >= turn_to_betray or i + 200 < d
        return "d"
    else:
        #be nice the first turn
        if num == 0:
            return "c";
        #finally, be tit-for-tat
        return en[-1]

Several values were arbitrarily chosen with alternatives tested and the values here are near-optimal at this point. Against the current spread of opposing factions, The Perfect Gentleman achieves complete dominance (100% of bacteria population) about 90% of the time (plus or minus 3 percentage points).

I have not added in the Mathematicians to my tests yet, however those two should only serve to feed existing strategies and not alter the outcome greatly.

It does manage a good portion of its control via propping up Defector, but that was allowed per the rules (the example strategies were fair game for targeting). It has a side effect of also propping up Game of Thrones, but that was unintentional as the two are indistinguishable based on the criteria I chose. Those "defector types" then have a point-advantage in round 2 and take out several troublesome neighbors as a result (the N-T4T types) and when they reface The Perfect Gentleman, they've expended their point advantage and are swiftly consumed.

There is an approximate 5% chance that all Perfect Gentlemen end up paired with Defector-types in the first round and end up committing mass suicide. In which case, one of the n-T4t types achieves total domination (196 cells of 196). Very rarely one of the other types (Game of Thrones, Boy, Grimace, Sore Loser...) manage to not go completely extinct and score a point or two.

Current simulation (still in progress towards 200 total games). All entries scoring 0 removed. Looks like Game of Thrones and 54-T4T split a round (195 points between them) after PG was eliminated.

Game: 90

Cooperator: 1
Remorseful Aggressor: 1
Copy First: 1
Six Tits for a Tat: 1
Thirty Tits for Tat: 393
Five Tits for a Tat: 1
Fifty Four Tits for a Tat: 538
Game of Thrones: 248
Perfect Gentleman: 16456 (93.2)%

##Simulation Terminated: Adding new bots

Backstabbing Tit for Tat (with Forgiveness)

This is basically Lucky Tit for Tat (aka Tit for Tat with Forgiveness) which is the "solved" optimal solution (for some value of "lucky"), with a twist. As we know exactly how many rounds the game will last, this bacteria backstabs on the final round thus ensuring a net-beneficial outcome against any other Tit for Tat and Cooperator bacteria (against itself it ends with a net zero, same as if it had cooperated). Due to the 10% carryover, this results in a long-term advantage.

from random import randint
def titfortatbackstabfunc(num, i, d, c, enlist):
    if num == 199:
        return "d";
    lucky = randint(0, 200)
    if lucky == 0:
        return "c"
    if num == 0 or enlist[-1] == "c":
        return "c"
    else:
        return "d"

Bitter Tat

Bitter Tat takes advantage of any attempts of cooperation given by the enemy when the enemy is ahead in points. Most bacteria offer an olive branch at least once during the 200 rounds, and as Bitter Tat is behind overall, it will milk those 5 points in a desperate bid for recovery.

Otherwise, it tit-for-tats per the usual dominant strategy. Also, it's a little bit more of a jerk than its cousin and backstabs a round earlier and offers no forgiveness.

def bittertatfunc(num, i, d, c, enlist):
    if i < d:
        return "d";
    if num >= 198:
        return "d";
    if num == 0 or enlist[-1] == "c":
        return "c"
    else:
        return "d"

Bitter Tat was designed via looking at the behaviors of other bots against Tit for Tat and the patterns expressed in those results, but is not designed to explicitly counter those strategies: it is still a general purpose formula.

Extra Bitter Tat

def xbittertatfunc(num, i, d, c, enlist):
    if i < d:
        return "d";
    if num >= 188:
        return "d";
    if num == 0 or enlist[-1] == "c":
        return "c"
    else:
        return "d"

Extra bitter by defecting extra early.

Draco18s no longer trusts SE

Posted 2017-05-23T00:22:31.773

Reputation: 3 053

1Please change the name of your function, as this one is already taken. – Gryphon – 2017-05-23T06:24:09.010

@Gryphon Whoops, sorry, I didn't realize I'd done that. I don't actually code in Python, I just smashed two bits of code together. – Draco18s no longer trusts SE – 2017-05-23T13:47:02.790

For what I have tested this proves to be the best strategy right now... – Masclins – 2017-05-23T18:37:29.650

@AlbertMasclans There are alterations that could be made to improve its fitness, but those alterations have no absolute maxima, only local, as it depends on the distribution of the other bots. e.g. the forgiveness right could be higher or lower (1-5% is typical, right now its 0.5%) and the backstab could come sooner (which would boost fitness against some bots, reduce it against others, and zero-out against still more, so the exact turning point entirely depends on the cross generational distribution of bots; ie earlier gens would want to backstab sooner in order to carry pts forward). – Draco18s no longer trusts SE – 2017-05-23T18:43:33.347

Great job on your victory in the testing round! Unfortunately, very early results that may change suggest that NeoAnticapitalist may be beating you. – Gryphon – 2017-05-25T13:38:21.090

@Gryphon Added a new contender. I suspect it will outperform NeoAnticapitalist by a small margin. – Draco18s no longer trusts SE – 2017-05-25T14:51:10.583

We'll see. I won't be able to submit a new test round with it and teamer for about 18 hours, but it does look promising. – Gryphon – 2017-05-25T16:36:00.167

@Gryphon No hurry. I realized that running the tests can't be done immediately and might take a while. Just raising the "I did an edit, here's a new bot" flag so that it wasn't missed. :) – Draco18s no longer trusts SE – 2017-05-25T16:50:08.003

Yeah, thanks. Even the 1,000 round tests take a couple hours to complete, and the time only goes up as I get more bots. That's why I reduced the number of bacteria and the number of rounds per game. – Gryphon – 2017-05-25T16:51:41.530

1I suspect it will outperform NeoAnticapitalist by a small margin. More like by over 30,000 points. – Gryphon – 2017-05-26T10:35:05.390

Congrats on Bitter Tat`s amazing victory. – Gryphon – 2017-05-26T10:35:59.603

@Gryphon Jebus Crikies. That lead is huge. – Draco18s no longer trusts SE – 2017-05-26T13:40:19.630

Not as high as Backstabbing Tit for Tat had. It was at something like 65,000 points pre-NeoAnticapitalist. You're only beating Defector by around 5,000 – Gryphon – 2017-05-26T13:41:17.193

@Gryphon True. I think that's due to the fact that everyone's trying to outcompete over an increasingly narrow window of opportunity, while having to largely cooperate in order to rack up raw points. For example, if two Bitter Tats encounter each other but have different point totals, the one won't cooperate, whereas the standard Tit-for-Tat would. – Draco18s no longer trusts SE – 2017-05-26T13:55:30.320

Good point. Although, as more not as good answers rack up, the number of points available also rise. Bitter Tat and Defector together make up as many points as NeoAnticapitalist did. – Gryphon – 2017-05-26T16:32:28.280

1

Let us continue this discussion in chat.

– Draco18s no longer trusts SE – 2017-05-26T16:41:28.270

@Gryphon new bot – Draco18s no longer trusts SE – 2017-05-29T19:56:11.883

Extra Bitter is in second! – Gryphon – 2017-05-30T10:30:02.930

@Gryphon Mm, I am not surprised. I know where the competition of optimization is occurring. I didn't get around to running things myself over the weekend, but I've got one more thing to try. – Draco18s no longer trusts SE – 2017-05-30T13:29:49.630

OK, I should be able to post results from 15 and 20 tits for a tat in about four hours. – Gryphon – 2017-05-30T15:57:31.440

@Gryphon That isn't the competition of optimization. They'll probably do better, but not because of what you think. In fact, based on the pattern you're following, "more tits" will probably continue to perform better than "fewer tits" up until around 100 or 150. – Draco18s no longer trusts SE – 2017-05-30T16:05:53.233

I wasn't saying it was, I was just letting you know when they will probably be up there. – Gryphon – 2017-05-30T16:13:56.837

@Gryphon <Salute> :) – Draco18s no longer trusts SE – 2017-05-30T16:15:51.163

The next updates have but Extra Bitter Tat down into tied for last :( – Gryphon – 2017-05-30T19:53:36.943

@Gryphon I've added The Perfect Gentleman. The undefeated (and likely undefeatable) bot. – Draco18s no longer trusts SE – 2017-06-07T03:37:18.467

Impressive optimizations. I'm having some trouble with my code bangs head helplessly on desk so it may take me a while to run the tests for this, but if you're right, this guy certainly does sound unbeatable, or nearly so. You deserve it for the amount of work you put into it. I would still like to see the strategies people come up with to try to topple it, though. @AHL seems interested in this KOTH, so maybe he'll try. I might even try. – Gryphon – 2017-06-07T03:40:29.930

2I appreciate that you made a bot to make this koth not a joke anymore – Destructible Lemon – 2017-06-07T05:37:59.270

@DestructibleLemon I'm glad I did too. It just took me a while to get Python (and the controller) up and running. I saw that the Tit-for-Tat optimization was getting out of control and where it was actually optimizing, so I knew I had to (and could) do something about it. – Draco18s no longer trusts SE – 2017-06-07T12:43:14.150

Perfect genteleman might be able to work with alternator strategies (cdcdcd or dcdcdc), since T4Ting them is the worst possible solution. Don't know if that would count as "targeting" them, though. – Robert Fraser – 2017-06-08T14:27:24.813

@RobertFraser I did not consider alternator-strategies when I was writing it. "dcdcdc..." would be indistinguishable from a defector "dddddd..." for the "encourage through self-sacrifice" code. – Draco18s no longer trusts SE – 2017-06-08T14:31:38.813

8

Anticapitalist

Another simple one. For even matches (starting at the same score) behaves pretty much like TitForTat, but the main idea is trying to survive the match.

def anticapitalistfunc(counter, mypoints, enpoints, mylist, enlist):
    if mypoints >= enpoints:
        return "c"
    else:
        return "d"

Gentle Defector

My idea here is to defect except if my enemy is usually cooperating. Though, it begins cooperating.

def gentleDefectorfunc(counter, mypoints, enpoints, mylist, enlist):
    if enlist.count("d") * 4 > len(enlist):
        return "d"
    else:
        return "c"

NeoAnticapitalist

An improvement of the Anticapitalist (or so I think). I see no reason to collaborate on last turn. I also see no reason to collaborate when I am pretty sure my opponent won't.

def neoanticapitalistfunc(counter, mypoints, enpoints, mylist, enlist):
    if mypoints >= enpoints:
        if counter > 1:
            if counter == 199 or (enlist[-1] != "c" and enlist[-2] != "c"):
                return "d"
        return "c"
    else:
        return "d"

Masclins

Posted 2017-05-23T00:22:31.773

Reputation: 914

I'm actually surprised I didn't think of this, but it's brilliant. I don't know if it will win, but I think it should work very well. – Gryphon – 2017-05-23T12:54:21.733

@Gryphon added a new bot (and merged my two other ones) – Masclins – 2017-05-25T09:53:51.643

@Gryphon thanks for both editions – Masclins – 2017-05-25T10:55:04.240

No problem, @AlbertMasclans – Gryphon – 2017-05-25T12:17:30.027

From the simulations I've been running, NeoAnticapitalist seems to have taken the lead from Backstabbing Tit for Tat. – Gryphon – 2017-05-25T13:06:40.020

@Gryphon happy to hear. It seemed really hard to beat it. – Masclins – 2017-05-25T13:09:55.477

Yes, it was winning by a landslide, and had over 86% of the total points. I should be able to post the new scores in about 7 hours. – Gryphon – 2017-05-25T13:14:36.187

@AlbertMasclans Oh, I misread the if-statement. My apologies. I'll remove my comment. – Draco18s no longer trusts SE – 2017-05-25T14:53:59.523

@Gryphon a small change on Gentle Defector, to begin cooperating. – Masclins – 2017-05-26T06:58:51.983

6

Remorseful Aggressor

from random import randint
def remorsefulaggressorfunc(counter, mypoints, enpoints, mylist, enlist):
    if counter == 0:
        return "d"
    if (counter > 195 and mylist[-1] == "d"):
        return "d"
    if ((counter == 1 or counter > 2) and enlist[-1] == "d"):
        return "d"
    if (counter == 2 and enlist[-1] == "d" and enlist[-2] == "d"):
        return "d"
    if (counter >= 195 and randint(0, 200 - counter) == 0):
        return "d"
    else:
        return "c"

This is designed to "keep up" with Defector, by defecting every time against it, and also to beat tit-for-tat-based strategies.

The basic idea is that we start by defecting, but if the opponent cooperated turn 1, we then cooperate twice to avoid a mutual recrimination cycle, thus avoiding too large a point penalty. (If, however, the opponent defects later on, we don't break the cycle ourself; we'll make them do it and likely lose the game as a result.) Then at the end of the game, we pick a random time within the last 5 turns to backstab the enemy, giving us one more defection than them and thus meaning that as long as we weren't too far behind on carryover points, we end up winning, without sacrificing much in terms of carryover in the process. (Randomizing the time period means that we're very likely to get in first with the backstab, also that this strategy can't be "tuned against" by aiming to backstab it one turn earlier.)

user62131

Posted 2017-05-23T00:22:31.773

Reputation:

Congrats on your number 3 spot! +1 – Gryphon – 2017-05-27T10:59:29.500

6

Grim Trigger

Simplistic bot, to try and fill out the competition

It will cooperate, unless the enemy defects, in which case it defects unforgivingly

def grimtriggerfunc(I, Do, Not, Care, enlist): return "d" if "d" in enlist else "c"

well, seems like this doesn't work because of the ntitsfortat meta of defecting early

Destructible Lemon

Posted 2017-05-23T00:22:31.773

Reputation: 5 908

Congrats on your number 5 spot, +1. – Gryphon – 2017-05-26T18:56:34.533

@Sleafar I was wondering who would be so mean ;_; ok – Destructible Lemon – 2017-06-06T07:54:48.713

5

Game of Thrones

def gameofthronesfunc(counter, mypoints, enpoints, mylist, enlist):
    turn_to_betray = 140
    if counter >= turn_to_betray or mypoints > enpoints or "d" in enlist:
        return "d"
    else:
        return "c"

Idea here is that you can never lose by betraying, so the only reason to cooperate is if you're behind. It also has the general framework of the other T4T answers (without any forgiveness, because I'm not sure if there's much point with the other contenders here).

The turn to betray might need to be changed to win, since in an even race, the T4Ter who betrays first will win, but against a very cooperative bot, you will miss out on some lifetime points. I'm not sure the right apex for this hill, so I'm just going for 140. I wouldn't be surprised if it were much earlier, though.

If this ends up in a petri dish with a T4Ter who betrays earlier or a defector (ie 146 T4T), then it depends entirely on if the GoT is already ahead (it will stay ahead) or if they're even/GoT is behind, in which case the early betrayer will win.

Robert Fraser

Posted 2017-05-23T00:22:31.773

Reputation: 912

Congrats on your third place finish! +1 – Gryphon – 2017-05-31T19:58:48.040

And now up to second! – Gryphon – 2017-06-03T13:50:14.597

Game of Thrones is putting up quite the fight against the bot I'm currently testing. The simple strategy is working well for it. – Draco18s no longer trusts SE – 2017-06-06T15:00:27.327

4

Lucky Tit For Tat

import os
def luckytitfortatfunc(num, i, d, c, enlist):
    lucky = ord(os.urandom(1))
    lucky = int(round(200 * float(lucky - 0) / 255.0))
    if lucky == 0:
        return "c"
    if num == 0 or enlist[-1] == "c":
        return "c"
    else:
        return "d"

I'm pretty sure I read somewhere that tit for tat was the best strategy. I decided to make it a possibility for other programs to redeem themselves just to add some variety. Now with a proper random number generator (it gives me an advantage right?).

Alex

Posted 2017-05-23T00:22:31.773

Reputation: 417

Basically, this is the winning strategy, hands down, all the time as noted on wikipedia. The only variance is in the probability of breaking out of a tit-for-tat defect-cycle (which depends on the matchup of all the other entries).

– Draco18s no longer trusts SE – 2017-05-23T03:52:45.577

1@Draco18s It's the winning strategy for a different scoring system, the total points scoring system. Basic tit-for-tat can never win a round, if it doesn't carry some points into the round, so it wouldn't do well. – isaacg – 2017-05-23T14:38:05.100

@isaacg is right, which is the reason this strategy is now in 14th out of 18 (although I have no right to blame AH L for this as the 18th program is one of mine.) – Gryphon – 2017-05-26T17:27:59.937

4

54 Tits for a Tat

def fiftyfourtitsfortatfunc(num, more, fun, me, en):
    tits = 54
    if "d" in en[-tits:] or num >= (200-tits):
        return "d"
    return "c"

Alex

Posted 2017-05-23T00:22:31.773

Reputation: 417

I wonder if this will still win? – Gryphon – 2017-05-28T23:09:04.673

This looks to be winning right now! – Gryphon – 2017-05-28T23:53:50.713

Congrats, you narrowly beat both of my top bots! – Gryphon – 2017-05-29T02:43:43.413

@Gryphon I would make 5 tits for a tat if I wasn't so attached for my other bots :) – Alex – 2017-05-29T03:20:40.940

I think that would be going over the other side of the curve. I might test one myself though! – Gryphon – 2017-05-29T03:21:16.843

And....Five Tits for a Tat is now a thing, and I'm running the test program. – Gryphon – 2017-05-29T03:41:20.350

Five Tits for a Tat is also winning 6294 to 2 right now. – Gryphon – 2017-05-29T03:42:00.753

@Gryphon I just made 6 tits for a tat ... the tit for tat arms race is happening. – Alex – 2017-05-29T03:47:04.640

Wow! This may become excessive. Would you mind moving all of your programs to one answer. That would make my life so much easier. – Gryphon – 2017-05-29T03:48:54.623

@Gryphon I still only have 5 bots though ... – Alex – 2017-05-29T03:55:13.320

I'm going to have to impose a limit on replacing bots if this gets out of hand. – Gryphon – 2017-05-29T03:57:25.857

Let us continue this discussion in chat.

– Gryphon – 2017-05-29T03:59:32.380

How did you scientifically prove 146? – Robert Fraser – 2017-05-31T03:44:14.387

@RobertFraser I ran it a few thousand times – Alex – 2017-05-31T04:05:18.540

Congrats on your win! – Gryphon – 2017-06-01T10:42:40.133

@Gryphon only got second :( – Alex – 2017-06-01T21:45:17.500

You were first before, but new additions make a difference, so after a second run with two new competitors, I retook the lead. – Gryphon – 2017-06-01T21:46:06.907

@Gryphon I just realized that the scoreboard said for 1,000 rounds but you said you would run 10,000 rounds ... – Alex – 2017-06-01T21:47:52.793

Yes, for the final scoring I will run 10,000 rounds, but to save time on these unofficial preliminaries, I only ran 1,000 – Gryphon – 2017-06-01T22:39:55.807

@Gryphon Idiot me read july 1st as june 1st and thought you ran the final scoring with 1,000 rounds :( – Alex – 2017-06-02T00:06:23.103

@Gryphon back to increasing by orders of one again – Alex – 2017-06-02T23:00:08.130

Seriously!!! Won't be able to judge this one for a while, sorry. – Gryphon – 2017-06-03T00:55:36.727

4

The Elephant

The Elephant never forgets!

import re
def theelephantfunc(counter, mypoints, enpoints, mylist, enlist):
    interwoven = "".join(i for j in zip(mylist, enlist) for i in j)
    backwoven = interwoven[::-1]
    predict = re.match("^((?:..)*).*?(.).\\1(?:..)*$",backwoven)
    if(predict is None):
        return "c"
    predict = predict.groups()[1]
    if(predict == "d"):
        return "d"
    if(mypoints - enpoints >= 6):
        return "c"
    return "d"

The Elephant looks at the history of the fight, and tries to figure out what the enemy has planned. He looks at both his moves and his enemies!

He tries to find the longest concurrent group that matches what's just happened, and takes what the enemy did just after that.

If he can't work it out, the Elephant will just Cooperate, as friendship is always the answer.

If he thinks his opponent will defect, he too will defect, not wanting to lose his hard earned points.

If he thinks his opponent will cooperate, but his less than or exactly 6 points lead, then he will defect, to gain some foothold.

And lastly, if he thinks his opponent will cooperate, and he has a strong lead, he will cooperate.

ATaco

Posted 2017-05-23T00:22:31.773

Reputation: 7 898

I was waiting for something like this that worked better than Nice Guy. However, I won't be able to test it for about 8 hours, so I should be able to update in about 12-13. – Gryphon – 2017-05-29T12:14:38.213

3

Every Other D

def everyotherdfunc(counter, mypoints, enpoints, mylist, enlist):
    if counter % 2 == 0:
        return "d"
    else:
        return "c"

Every Other C

def everyotherdfunc(counter, mypoints, enpoints, mylist, enlist):
    if counter % 2 == 0:
        return "c"
    else:
        return "d"

Stephen

Posted 2017-05-23T00:22:31.773

Reputation: 12 293

Maybe you should submit another entry starting with cooperate. – Gryphon – 2017-06-01T15:47:20.263

Just thought it might be interesting. – Gryphon – 2017-06-01T15:54:35.783

3

Nice Guy

def niceguyfunc(counter, mypoints, enpoints, mylist, enlist):
  if counter < 2:
    return "c"

  mylast = mylist[-1]
  enlast = enlist[-1]
  last_found_index = -1

  for i, item in enumerate(mylist):
    if i == counter - 1:
      break
    if mylist[i] == mylast and enlist[i] == enlast:
      last_found_index = i

  if last_found_index == -1:
    return "c"
  else:
    if enlist[last_found_index + 1] == "c":
      return "c"
    else:
      return "d"

Tries to predict the opponents output by looking at the history. For example, if the last moves were (c, enemy d), it tries to find the last occurrence of the exact same moves.

CommonGuy

Posted 2017-05-23T00:22:31.773

Reputation: 4 684

3

Hackman [disqualified as expected]

Ok, this one will probably be ruled out of the contest, but I really feel like trying it out:

def hackmanfunc(counter, mypoints, enpoints, mylist, enlist):
        if enlist.count("#") > 0:
                return "c"
        elif counter >= 2 and enpoints > mypoints:
                return "d"
        elif counter == 198:
                return "d"
        elif counter == 199:
                return "#"
        elif counter == 0 or enlist[-1] == "c":
                return "c"
        elif counter >= 2 and enlist[-2] != "c":
                return "#"
        else:
                return "d"

Here I'm taking as base the BackstabbingTitForTat that proved to be the best in my simulations. Also, it's heavily based in using a non-used symbol "#"(that's why I say it will be probably ruled out).

Now let me explain the conditions here:

1st: Ensure two Hackman cooperate if something went wrong.

2nd: If I'm going to lose against a different bot, at least make him lose as many points as possible, so it's not a huge enemy afterwards.

3rd: Betray one turn before, so wins vs Backstabbing

using "#" instead of "d" makes me get 0 points instead of -1 and also communicate with other Hackman who has less points, so he stops defecting.

Masclins

Posted 2017-05-23T00:22:31.773

Reputation: 914

2Sorry, but disqualified. This counts as messing around with the process of judging. You MUST return either "c" or "d" each turn. – Gryphon – 2017-05-24T10:14:26.603

2However, this is pretty inventive, so I'm sorry to have to disqualify it. – Gryphon – 2017-05-24T10:15:01.227

3

Bizzaro

Does the exact opposite of tit for tat. When someone is kind to him he shows his love by being evil, and when someone is mean he shows revenge by being good. Heavily based on tit for tat.

def bizzarofunc(counter, mypoints, enpoints, mylist, enlist):
    if counter==0 or enlist[counter-1] == "c":
        return "d"
    else:
        return "c"

TitusLucretius

Posted 2017-05-23T00:22:31.773

Reputation: 121

Interesting. This is going to get killed by Defector, though. – Gryphon – 2017-05-24T18:10:57.527

@Gryphon Lmao, actually didn't realize that. But hey, Bizzaro no know difference between right and wrong, win and lose. – TitusLucretius – 2017-05-24T18:20:04.913

Yeah, this will be defector towards cooperator and cooperator towards defector, which could make for some interesting possibilities. It can't really co-exist with anything else, including itself. – Gryphon – 2017-05-24T18:23:12.517

@Gryphon, Yeah it should get 0, when it plays itself. Wonder what will happen with Gentle Defector. – TitusLucretius – 2017-05-24T18:25:59.357

Wonder what happens when it faces backstabbing tit for tat, which is what was winning before this submission. – Gryphon – 2017-05-24T18:28:33.147

@Gryphon it enters a "d/c","d/d","c/d","c/c" loop. Which has a -1 net point for both bots after the 4 cycle loop completes. If forgiveness happens, there's a 50-50 chance of both bots skipping a "c/d" entry or repeating a "d/c" entry (the former is point neutral other than changing when the last round occurs (which if it happens precisely once, costs Tit for Tat a net 5), the latter costs Tit for Tat a net 3 points--both ignoring the backstabbing behavior). – Draco18s no longer trusts SE – 2017-05-25T14:10:16.567

Per the backstabbing round, if the bots are tied up (Forgiveness never occurred) then T4T will gain the net 5 points (as the cycle normally concludes on a "c/c" which turns into a "c/d"). If forgiveness happened that cost T4T 3 points, nothing special happens (would have been "c/d" anyway). If the alternative Forgiveness result occurs, instead of ending on "d/c" it becomes "d/d" and the last round is point-neutral, undoing the damage of changing the loop end-point. – Draco18s no longer trusts SE – 2017-05-25T14:18:35.393

Wasn't actually expecting an explanation, but OK. – Gryphon – 2017-05-26T17:03:17.403

3

Ten Tits for a Tat

def tentitsforatatfunc(num, more, fun, me, en):
    if "d" in en[-10:] or num >= 190:
        return "d"
    return "c"

Defects earlier, and also defects if its opponent has defected in the last ten turns.

CopyFirst

def copyfirstfunc(num, mypoints, enpoints, myhistory, enhistory):        
    if num == 0 or num >= 197:
        return "d"
    else:
        return enhistory[0]

This defects the first round, then does whatever the opponent did the first round, until the 197th round, when it backstabs.

Forty Tits for a Tat

def fourtytitsforatatfunc(num, mypoints, enpoints, myhistory, enhistory):
    if "d" in en[-40:] or num >= 150:
        return "d"
    return "c"

If the opponent defected in the last 40 turns, defect, otherwise cooperate. Backstab on the last 50 turns.

Three Tits for a Tat

If the opponent defected in the last 3 turns, defect, otherwise cooperate. Backstab on the last 5 turns. This program has stolen the lead from Tit for Two Tats by a narrow margin.

def threetitsforatatfunc(num, mypoints, enpoints, myhistory, enhistory):
    if num == 0 or num==1 and enhistory[-1]=="c" or num==2 and enhistory[-1]=="c" and enhistory[-2]=="c":
        return "c"
    if enhistory[-1] == "d" or enhistory[-2] == "d" or enhistory[-3] == "d" or num >= 195:
        return "d"
    else:
        return "c"

Five Tits for a Tat

def fivetitsforatatfunc(num, more, fun, me, en):
    if "d" in en[-5:] or num >= 194:
        return "d"
    return "c"

If you can't figure out what this one does, you're an idiot. Also backstabs one round earlier.

Gryphon

Posted 2017-05-23T00:22:31.773

Reputation: 6 697

IMO, feel free to submit your own entry. – Draco18s no longer trusts SE – 2017-05-25T14:33:50.683

Just wasn't sure if it was generally considered fair. – Gryphon – 2017-05-25T16:33:47.237

What's usually common, though, is that Best Answer is the winning bot. – Masclins – 2017-05-26T07:00:03.117

It will be, I won't just pick my own answer. That would definitely not be fair. – Gryphon – 2017-05-26T10:24:14.070

I apologise, I had not realised I`d accidentally accepted my answer. It is now unaccepted, and I will accept the best answer as of July 1. – Gryphon – 2017-05-26T10:39:30.713

I can't believe Teamer scored that badly. Wow. – Draco18s no longer trusts SE – 2017-05-26T13:42:56.673

Yeah, going to work on some updates. It sure needs them! – Gryphon – 2017-05-26T13:45:30.893

Well, Teamer is doing a bit better, being now tied for 8th, but I think most of that is just random luck. – Gryphon – 2017-05-27T11:21:57.020

Yep, I think we've hit the point where it's going to be freakishly difficult to out-compete 2T4T, as evidenced by Defector being in 2nd place...by a factor of 22. – Draco18s no longer trusts SE – 2017-05-27T17:00:23.403

I'm just happy we hit that point when one of my programs was on the top of the scoreboard. – Gryphon – 2017-05-28T00:02:46.550

Turns out it was possible to beat Tit for Two Tats, just by adding another Tit. Defector also fell in points by a level of magnitude, so I'm thinking we aren't at that point yet. – Gryphon – 2017-05-28T22:39:06.067

I think 40 might be overboard and you should probably update the description – Alex – 2017-05-30T23:10:44.937

@AHL, I do not. – Gryphon – 2017-05-30T23:11:25.190

Description updated. Tfor40tats is also winning right now. – Gryphon – 2017-05-30T23:12:44.783

also, its named 30 tits for tat func – Alex – 2017-05-30T23:14:14.420

Tfor30tats is in a fairly close second. – Gryphon – 2017-05-30T23:20:21.607

3

6 Tits for a Tat

def sixtitsforatatfunc(num, more, fun, me, en):
    if "d" in en[-6:] or num >= 194:
        return "d"
    return "c"

Tit for Tat arms race is happening :)

Alex

Posted 2017-05-23T00:22:31.773

Reputation: 417

I feel we're going to go overboard and Defector will steal top spot. – Gryphon – 2017-05-29T03:51:56.630

3

Grimace

def grimacefunc(I, Do, Not, Care, enlist):
    if round < 123: return "d" if "d" in enlist else "c"
    return "d"

Destructible Lemon

Posted 2017-05-23T00:22:31.773

Reputation: 5 908

It kind of feels like the "optimal" solution for this hill is to do something like this (T4nT with n>5 is basically this), and just optimize the round when it betrays. Anything more creative will be destroyed. – Robert Fraser – 2017-06-01T08:20:49.343

3

Predictable Mathematicians:

Young Mathematician

New to the harshness of the world

import math
def ymathfunc(num, mpoints, enpoints, mlist, enlist):
  if(math.sin(num) + 0.8 > 0):
    return 'c'
  else:
    return 'd'

Older Mathematitian

More experienced in these matters

import math
def omathfunc(num, mpoints, enpoints, mlist, enlist):
  if(math.cos(num) + 0.8 > 0):
    return 'd'
  else:
    return 'c'

I doubt either of these will do well, but at least they'll add ways for others to get points!

Panda0nEarth

Posted 2017-05-23T00:22:31.773

Reputation: 111

Neither of these are going to do well, heh. All they do is feed the defectors, for the most part. – Draco18s no longer trusts SE – 2017-06-06T15:16:20.420

2

Randomized Tit For Tat

import os
def randomtitfortatfunc(forgot, ten, var, iables, enlist):
    luck = enlist.count("d") + 1
    choice = ord(os.urandom(1))
    choice = int(round(luck * float(choice - 0) / 255.0))
    if choice == 0:
        return "c"
    return "d"

Tit For Tat, but randomized. This is not going to win any prizes (unless I'm really lucky). Now with random numbers generated from a proper source.

Alex

Posted 2017-05-23T00:22:31.773

Reputation: 417

2

Exploitative Tat

Exploitative Tat tries to play the following strategies:

  • Defect when behind. It's the only way to catch up.

  • Cooperate against tit-for-tat and similar strategies. It's the only way to get a good long-term score.

  • Defect against always-cooperators and other chumps.

  • Defect 5 rounds early.

Here's the code:

def exploitativetatfunc(num, mypoints, enpoints, mylist, enlist):
    if mypoints < enpoints:
        return "d"
    if num >= 195:
        return "d"
    if num == 0:
        return "c"
    # Test defect, and keep defecting as long as they'll allow
    if (num == 5 or num >= 8) and all(choice == "c" for choice in enlist):
        return "d"
    # Recover if that goes wrong, and they were nice.
    if (num == 6 or num == 7) and all(choice == "c" for choice in enlist[:4]):
        return "c"
    # Otherwise, tit for tat.
    return enlist[-1]

isaacg

Posted 2017-05-23T00:22:31.773

Reputation: 39 268

I'm surprised this didn't do better than tie for 8th, but I think it was just the wrong time for it, and it was unfortunately entered at the same time as Two Tits for a Tat. – Gryphon – 2017-05-27T11:18:25.970

2

30 Tits for a Tat

def thirtytitsfortatfunc(num, more, fun, me, en):
    tits = 30
    if "d" in en[-tits:] or num >= (200-tits):
        return "d"
    return "c"

Alex

Posted 2017-05-23T00:22:31.773

Reputation: 417

2

Fox

def foxfunc(counter, mypoints, enpoints, mylist, enlist):
    if counter > enpoints:
        return "d"
    return "c"

Defects if the round number is greater than the enemies points, cooperates otherwise.

The Boy

def boyfunc(counter, mypoints, enpoints, mylist, enlist):
    if counter!=0 and enlist[-1]=="c" and counter <= 194 or enpoints+10<mypoints:
        return "c"
    return "d"

Cooperates first round, then acts for tit for tat but backstabs on the last five rounds, and defects if it isn't ten points ahead.

53 tits for a tat

def fiftythreetitsfortatfunc(num, more, fun, me, en):
    tits = 53
    if "d" in en[-tits:] or num >= (200-tits):
        return "d"
    return "c"

You all know what this is :)

Foxy

Posted 2017-05-23T00:22:31.773

Reputation: 249

2

Prudent Betrayer

def PrudentBetrayer(counter, mypoints, enpoints, mylist, enlist):
    # Am I ahead, even if he betrays first?
    if mypoints > enpoints + 5:
        if counter == 0:
            return "c"
        else:
            return enlist[-1]
    # Can I catch up if I betray first?
    elif mypoints + 5 > enpoints:
        if counter == 0:
            return "c"
        elif counter > 130:
            return "d"
        else:
            return "d" if "d" in enlist else "c"
    # can't win -> kill his score
    else:
        return "d"

Assumes it is fighting an n-tits-for-a-tat bot. If it has the score to be betrayed and still win, it will let the other bot hit it first (playing as tit for tat.) If it can win only when it betrays first, It will betray on round 130, well before any current bot. If it is many points behind its opponent, it will just play defector in an attempt to lower the unsuspecting bots score.


Handshake

import random
def handshakefunc(num, me, him, m, en):
    handshake = "cdccd"
    # For the first few rounds, give the handshake.
    if num < len(handshake):
        if m == en:
            return handshake[num]
        return "d"
    if en[:len(handshake)] == handshake:
        if me > him:
            return "d"
        if me == him:
            return "ccd"[random.randint(0,2)]
        return "c"
    return "d"

Uses the pattern cdccd on the first five rounds, to find out if it is with itself. If so, it will try to max its points by having the bot with more points always defect, while the other always cooperates. If it finds it is fighting an enemy, it will play the defector.

In my tests, I find it dose well if it makes up a significant portion of the population. When it dose not have a chance to fight itself, it will basically be reduced to a defector.

EDIT: Clearly from the scores, their are to many bots for this to work well. It will still win if fighting just the top few...


Bizzaro Trigger

def bizzaroTriggerfunc(round,ms,ts,mm,tm):
  if round==1:return 'c'
  if 'c' in tm:return'd'
  return 'c'

Always cooperate, unless your opponent ever cooperates with you, in which case you defect. Always.

MegaTom

Posted 2017-05-23T00:22:31.773

Reputation: 3 787

@Gryphon the sore loser was removed, and a new bot added! – MegaTom – 2017-06-05T17:23:34.133

In the test I'm running now on a bot I haven't yet posted, Handshake is in 2nd place after 390 games (5338 surviving bots) outdoing 54-T4T by about 1200. My new bot is much stronger than even that, though. Initial tests had it scoring 196 out of 196, though long-run it's scoring an average of ~124 / game. Interestingly enough, the basis behind Handshake was what I was going to try next, turns out you beat me to it without my realizing it. – Draco18s no longer trusts SE – 2017-06-05T20:44:12.130

2

Twentyfivetitsforatat

def twentyfivetitsfortatfunc(num, more, fun, me, en):
    tits = 25
    if "d" in en[-tits:] or num >= (200-tits):
        return "d"
    return "c"

Kinda titsforatat

def kindatitsfortatfunc(num, more, fun, me, en):
    tits = 54  
    if "c" in en[-tits:] or num >= (200-tits):
        return "c"
    return "d"

Christopher

Posted 2017-05-23T00:22:31.773

Reputation: 3 428

Next time you edit to add a program, please also add a comment so I get alerted. Thanks! – Gryphon – 2017-06-04T21:13:16.300

@Gryphon oh sorry – Christopher – 2017-06-04T21:24:57.837

2

but what if... the next answer was not grim trigger or something for tat

I present

Anty

def antyfunc(counter, mypoints, enpoints, mylist, enlist):
    if counter > 150: return "d"
    if not "c" in enlist[-2:]:
        return "d"
    if enpoints >= mypoints:
        return "d"
    else:
        return "c"

Destructible Lemon

Posted 2017-05-23T00:22:31.773

Reputation: 5 908

Intersting, will test when I get home. – Gryphon – 2017-06-02T14:35:28.900

0

FakeShake

takes advantage of handshake- does a handshake then just defects while handshake trusts it. When it meets itself, however, it does the 'real' handshake. If it meets a different bot it plays tit for tat, with a betrayal at the end. This feels kinda mean...

import random
def fakeshakefunc(num, i, d, m, enlist):
      secret_handshake="cdccdd"
      handshake= "cdccd"
      #checks if it is meeting itself
      if enlist[:len(secret_handshake)] == secret_handshake:
          if me > him:
            return "d"
          if me == him:
             return "ccd"[random.randint(0,2)]
          return "c"
      #does handshake's handshake,if the bot is not handshake or fakeshake it plays T4T
      if num < len(handshake):
            if m == enlist:
                return handshake[num]
            if i < d or num>= 198:
                return "d";
            if num == 0 or enlist[-1] == "c":
                return "c"
            else:
                return "d"
            if enlist[:len(handshake)] == handshake:
                return "d"
            if i < d or num>= 198:
                return "d";
            if num == 0 or enlist[-1] == "c":
                return "c"
            else:
                return "d"

One problem with this is that if it meets handshake and it has more points, it thinks its playing itself. I'm a newbie to python and this site (in fact this is my first answer) so make sure to tell me if I've made any stupid mistakes!

Arkine

Posted 2017-05-23T00:22:31.773

Reputation: 3

Welcome to PPCG! – Laikoni – 2018-11-17T18:00:28.573

@Laikoni Thanks! – Arkine – 2018-11-17T19:05:07.530