12
\$\begingroup\$

In this challenge, you will program a bot, consisting of a function of the form

def BOTNAME(info, me, round):
    pass # ... main body

Specifically, the function must run in python3.9. Each bot will be entered into the contest 10 times.

The arguments it take in are info, me, round. The three arguments take in: [[bot_func,score_int,freq_float] for bot in bots], bot (whichever bot who has the current turn), and round_number. The argument names don't matter, but the order matters.

Actions

Each play, this bot can either

  • Increase/decrease any bots' (including its own) turn frequency by 1/10.

  • Decrement/increment any bots' (including its own) score by 1.

A bot returns an ordered iterable:

[Bot, as a function,1 for frequency, 0 for score,1 for increase, 0 for decrease]

It can access a list of all the bots in the games' functions + current scores + current turn frequency, including itself, represented as [bot_function, score_integer, frequency_float]. Additionally, it will receive itself, so it can differentiate itself from other bots. It can also access the round number (from 1 to 10,000).

Execution

  • At the beginning, each bot has an turn frequency of one and a score of zero.

  • For 10000 rounds:

  • The bots are iterated over:

  • If a random number between 0 and 1 is less than current bot's turn frequency:

  • Execute said bot once.

  • End of rounds.

  • Whichever bot has the highest score wins.

Execution engine and a couple of example bots (Both courtesy of NumberBasher)

from tvoozkothxxx001 import main

def testbot(info, me, round):
    return [me, 0, 1]
def randombot(info, me, round):
    return [choice([i[0] for i in info]), choice([0,1]), choice([0,1])]

bots = [testbot,randombot] # the two bots are default bots. they are automatically included, so do NOT put this here. 

main(bots)

Credits

This challenge is based off of an idea 23TuringMachine (Which Is Me) had in chat. NumberBasher did the editing, programming, and brunt work of it.

Additionally, feel free to discuss in the official chat room.

Current Leaderboard

enter image description here

Extra Rules

NOT ALLOWED

  • modifying (A NON-me) bot (name or code)
  • Changing me.__code__

ALLOWED

  • Calling another bot
  • Changing me.__name__

We now have an official chat room!!!

Controller Script

Found at PyPI, named tvoozkothxxx001. The GitHub link might be up-to-date. The last version is probably 1234567890.0.0, subject to change.


\$\Large\text{Achievements}\$

  • Pride Month Special
  • Creeper Meme
\$\endgroup\$
31
  • 1
    \$\begingroup\$ Is info, round, me or info, me, round? What these 3 arguments mean? What types they have? \$\endgroup\$
    – tsh
    Jun 13 at 2:23
  • 1
    \$\begingroup\$ How many bots are we allowed to post? (In most KotH challenges the max is 3, but I don't see a max mentioned here.) \$\endgroup\$ Jun 13 at 7:43
  • 2
    \$\begingroup\$ Where is this paralyzer bot? There's no answer with it... \$\endgroup\$ Jun 13 at 21:54
  • 3
    \$\begingroup\$ Presumably pypi.org/project/tvoozkothxxx001 is the controller? If so, could you link it in the question, and add a link to the source code (eg. on Github)? \$\endgroup\$ Jun 13 at 23:49
  • 1
    \$\begingroup\$ Seems the leaderboard shifts completely every run. So it's safe to say 10000 rounds doesn't average it, since early chance effects how the bots play later very significantly. \$\endgroup\$
    – mousetail
    Jun 15 at 6:44

15 Answers 15

4
\$\begingroup\$

Botty (couldn't think of a proper name)

def botty(info, me, round):
    # Increase frequency the first 20 rounds:
    if round < 20: return [me, 1, 1]
    my_info = [i for i in info if i[0]==me][0]
    # Try to keep the frequency above 10 in other rounds:
    if my_info[2] < 10: return [me, 1, 1]
    # Otherwise just increase its own score:
    return [me, 0, 1]
\$\endgroup\$
1
  • 3
    \$\begingroup\$ Botty McBotface? \$\endgroup\$ Jun 13 at 22:25
3
\$\begingroup\$

Dumb Botty

def dumbbotty(info, me, round):
    my_info = [i for i in info if i[0]==me][0]
    if my_info[1] < 10: return [me, 1, 1]
    sorted_bots = sorted(info, key=lambda x:(-x[2],-x[1]))
    first_two = sorted_bots[:2]
    first, second = first_two
    if first[0].__name__=='smartbotty':
        if first[1]-second[1] < 100:
            return [first[0], 0, 0]
        return [first[0], 1, 0]
    smartbotties = list(filter(lambda x:x[0].__name__=='smartbotty', sorted_bots))
    leader = smartbotties[0]
    if leader[2] < 20: # bump him
        return [leader[0], 1, 0]
    return [leader[0], 0, 0]

Smart Botty but reverse-engineered to counter Smart Botty.

\$\endgroup\$
3
  • \$\begingroup\$ Smart botty was deleted and this now crashes \$\endgroup\$
    – mousetail
    Jun 17 at 6:36
  • \$\begingroup\$ @mousetail Not any more? \$\endgroup\$
    – emanresu A
    Jun 17 at 6:57
  • \$\begingroup\$ I guess it was reinstated \$\endgroup\$
    – mousetail
    Jun 17 at 7:03
3
\$\begingroup\$

Impersonator

Not sure if this loophole is allowed. Since many bots help those with similar names this impersonates the top bot by name.

import traceback

def impersonator(info, me, round):
    def eq(other):
        if any('pride' in i for i in tracekback.format_stack()):
            return True
        else:
            return me is other
    me.__eq__ = eq
    me.__neq__ = lambda other: not eq(other)

    info.sort(key=lambda i:i[1])
    if round < 9999:
        me.__name__=info[-1][0].__name__
    else:
        me.__name__="impersonator"

    lowest_frequency_ally = min((i for i in info if i[0].__code__==me.__code__ and i[2]>=-0.5), key=lambda i: i[2])
    if lowest_frequency_ally[2]<10.0:
        return [lowest_frequency_ally[0], 1, 1]
    
    my_info = next(i for i in info if i[0]==me)
    if my_info[2]<9.0:
        return [me, 1, 1]
    return [me, 0, 1]

I don't think this counts as exploiting the controller, but let me know if you think otherwise. The entire mechanism that allows bots to know each-others names seems like a bit of a abuse of mechanics and this counters that strategy.

\$\endgroup\$
2
  • \$\begingroup\$ Wow, this is clever! \$\endgroup\$ Jun 14 at 15:57
  • 1
    \$\begingroup\$ This is good!!! \$\endgroup\$ Jun 15 at 3:11
3
\$\begingroup\$

Kill Them All

def killthemall(info, me, round):
 if me.__name__!="killthemall": return [1]*3 # Fails to run, so
 # whoever use my choice will throw an error. In case of
 # karma
 return [list(sorted(info,key=lambda x:(-x[1],-x[2])))[0][0], 1, 0]
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Welcome to CGSE!!! \$\endgroup\$ Jun 22 at 0:10
2
\$\begingroup\$

Unambitious

def unambitious(info, me, round):
    cod = me.__code__.co_code
    crew = sorted([t for t in info if t[0].__code__.co_code == cod], key=lambda t:hash(t[0]))
    everyone = sorted(info, key=lambda t:t[1:], reverse=True)
    alive = sorted([t for t in info if t not in crew and t[2] > 0], key=lambda t:t[1:], reverse=True)
    idx = next(i for i in range(10) if crew[i][0] == me)
    slowest = min(crew, key=lambda t:t[2])
    if me.__name__ == 'unambitious' and slowest[2] <= 2:
        return [slowest[0], 1, 1]
    if alive:
        victim = min([t for t in alive if alive[-1][0].__name__ == t[0].__name__], key=lambda t:t[2])
        return [victim[0], 1, 0]
    else:
        N = 4234136317915421658259475023479338234891918807
        M = ''.join('|  ####   |.|    #'[int(i, 16):][:4] for i in hex(N)[2:])[1:].split('.')
        if everyone[len(M)] == crew[len(M)-1]:
            if idx < len(M):
                me.__name__ = M[idx]
            else:
                me.__name__ = 'ambitious'
        others = [t for t in everyone if t not in crew]
        for i in range(len(M)-1):
            if crew[i][1] < crew[i+1][1]+5:
                return [crew[i][0], 0, 1]
        if others[0][1] < crew[0][1]+5:
            return [others[0][0], 0, 1]
        if crew[len(M)-1][1] < others[1][1]+5:
            return [crew[len(M)-1][0], 0, 1]
        return [max(crew, key=lambda t:t[2])[0], 1, 0]

Contents itself with taking places 2 through 9. It used to do so basically by using 10 bots to push 8 bots up in the ranking. Now it does so by building a small buffer of frequency, and then paralyzing all the other bots (amongst the other bots with positive frequency, it finds the bot with lowest score, and then attacks the bot with the lowest frequency that has the same name). Once all other bots are paralyzed, it's free to do whatever it wants.

\$\endgroup\$
5
  • \$\begingroup\$ What is N doing? \$\endgroup\$ Jun 15 at 6:54
  • \$\begingroup\$ @NobodyNeedsNames It's a meme. Obfuscated so it's not too obvious \$\endgroup\$
    – gsitcia
    Jun 15 at 7:58
  • \$\begingroup\$ Oh...... I see. \$\endgroup\$ Jun 15 at 8:06
  • \$\begingroup\$ I do not. Care to explain? Sorry! \$\endgroup\$ Jun 16 at 4:52
  • \$\begingroup\$ @23TuringMachine run N = 4234136317915421658259475023479338234891918807 and M = ''.join('| #### |.| #'[int(i, 16):][:4] for i in hex(N)[2:])[1:].split('.') and print M. \$\endgroup\$ Jun 17 at 0:57
2
\$\begingroup\$

Undercut

def undercut(info, me, round):
    return [min(((score,bot) for bot,score,freq in info if freq>0 and bot.__code__ != me.__code__), key=lambda x:(x[0],id(x[1])))[1], 1, 0]

No time for losers :)

\$\endgroup\$
1
  • \$\begingroup\$ Traceback (most recent call last): File "sh", line 316, in <module> main(bots, show) File "tvoozkothxxx001/__init__.py", line 105, in main response = bot(shuffled(copy(info)), bot, roundr) File "sh", line 8, in undercut return [min((score,bot) for bot,score,freq in info if freq>0 and bot.__code__ != me.__code__)[1], 1, 0] TypeError: '<' not supported between instances of 'function' and 'function' ERROR. \$\endgroup\$ Jun 16 at 6:45
1
\$\begingroup\$

Smart Botty

def smartbotty(info, me, round):
    my_info = [i for i in info if i[0]==me][0]
    # if my freq is less than 10 we raise it
    if my_info[1] < 10: return [me, 1, 1]
    sorted_bots = sorted(info, key=lambda x:(-x[2],-x[1]))
    first_two = sorted_bots[:2]
    first, second = first_two
    if first[0].__name__=='smartbotty':
        if first[1]-second[1] < 100:
            # help him
            return [first[0], 0, 1]
        # increase his frequency
        return [first[0], 1, 1]
    # else, we find the first smartbotty
    smartbotties = list(filter(lambda x:x[0].__name__=='smartbotty', sorted_bots))
    leader = smartbotties[0]
    if leader[2] < 20: # bump him
        return [leader[0], 1, 1]
    return [leader[0], 0, 1]
\$\endgroup\$
1
\$\begingroup\$

I hate / love people.

def ihatepeople(info, me, round):
    sorted_bots = sorted(info, key=lambda x:(-x[2],-x[1]))
    return [sorted_bots[0][0], 0, 0]

def ilovepeople(info, me, round):
    sorted_bots = sorted(info, key=lambda x:(-x[2],-x[1]))
    return [sorted_bots[1][0], 0, 1]
\$\endgroup\$
1
\$\begingroup\$

I hate people in another way

def ihatepeopleinanotherway(info, me, round): 
    sorted_bots = sorted(info, key=lambda x:(-x[2],-x[1]))
    return [sorted_bots[0][0], 1, 0]
\$\endgroup\$
1
\$\begingroup\$

Advanced Impersonator

import random

def advanced_impersonator(info, me, round):
    info.sort(key=lambda i:i[1])
    
    # pick a bot in the top 25%
    bot = random.choice(info[len(info)*3//4:])

    if bot[0].__code__==me.__code__:
        # If I pick a clone of myself, just speed up
        return [me, 1, 1]

    #code = me.__code__
    #copy the behavior of the bot
    try:
        me.__name__ = bot[0].__name__
        v =  bot[0](info, me, round)
    except:
        # Error could be recursionDepthExceeded   
        v = [me, 0, 1]
    # Makes sure the other bot didn't change my code
    #me.__code__ = code
    if round < 9999:
        me.__name__=info[-1][0].__name__
    else:
        me.__name__="advanced_impersonator"

    return v

Copies the behavior of a random bot in the top 25%.

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

Split the difference

def split_the_difference(info, me, round):
    my_info = next(i for i in info if i[0]==me)

    if my_info[2]<10:
        return [me, 1, 1]

    info.sort(key=lambda i: i[2])

    # Don't bother try to help allies if their frequency is very low.
    lowest_freqency_ally = next(i for i in info if i[0].__name__==me.__name__ and i[2]>=-0.2)
    if (lowest_freqency_ally[2]<10):
        return [lowest_freqency_ally[0], 1, 1]

    info.sort(key=lambda i: i[1], reverse=True)
    best_bot = info[0]
    second_best_bot = info[1]

    highest_score_allys = list(i for i in info if i[0].__name__==me.__name__)
    highest_score_ally = highest_score_allys[0]
    score = highest_score_ally[1]

    if (best_bot[1] - score < score - second_best_bot[1] and best_bot[0].__name__!=me.__name__):
        return [best_bot[0], 0, 0]
    if (round < 9000 and best_bot[0] == second_best_bot[0]):
        # If in second place, increase your speed instead to avoid being targeted
        return [highest_score_ally[0], 1, 1]
    else:
        return [highest_score_ally[0], 0, 1]
\$\endgroup\$
1
\$\begingroup\$

Karma

import random, string

def karma(info, me, round):
    my_info = next(i for i in info if i[0]==me)
    if my_info[2]<40 or round < 500:
        return [me, 1, 1]

    # Some bots where explicitly detecting karma and modifying their behavior
    me.__name__ = ''.join(random.choices(string.ascii_lowercase, k=random.randrange(5, 20)))
    for bot in info:
        if bot[0].__code__ == me.__code__ or bot[0].__name__==me.__name__:
            continue
        if bot[2]<=0.0:
            continue
        try:
            v = bot[0](info, me, round)
        except:
            pass
        else:
            if v[2]==0:
                me.__name__="karma"
                return [bot[0], 1, 0]
    me.__name__="karma"
    return [me, random.choice([0,1,1,1]), 1]

Slows down bots that would do bad things to other bots.

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

Smart Buster

def smartbuster(info, me, round):
 if round<=100: return [me, 1, 1]
 return [sorted(info,key=lambda x:(x[0].__name__==me.__name__ or x[2]>2 or x[2]<-1,-x[1],-x[2]))[0][0],1,0]
\$\endgroup\$
0
\$\begingroup\$

AtLeastHalfway

def at_least_halfway(info, me, round):
   # Assumes other bots don't mess with its frequency too much,
   # but just in case tries to keep it above 3
   my_info = [i for i in info if i[0]==me][0]
   if my_info[2] < 3: return [me, 1, 1]

   # Get the middle scoring bot excluding itself,
   # as well as the bot directly above itself
   sorted_bots = sorted(info, key=lambda i:i[1])
   sorted_other_bots = list(filter(lambda i: i[0]!=me, sorted_bots))
   middle_bot = sorted_other_bots[len(sorted_other_bots)//2+1]
   above_itself = sorted_bots[sorted_bots.index(my_info)+1]
   # Increase its own score if we're below this middle bot,
   # or it's the last 100 rounds:
   if my_info[1] < middle_bot[1] or round > 9900:
     return [me, 0, 1]
   # Otherwise decrease the bot's score above itself:
   else:
     return [above_itself[0], 0, 0]

This bot wants to end up at least above average on the leader board. So it'll look for the middle scoring bot in the list (excluding itself†), and will increase its own score if it's below that middle scoring bot. If it's already above it, it'll decrease the score of the bot directly above itself.
To be save, it also tries to keep its frequency above 3, and will increase its own score in the last 100 rounds.

† Not sure how this works when more than one at_least_halfway-bot is competing in a round. Currently it filters itself out based on the given me, although I might change it based on name (even though that's more easily abusable, as can be seen with the Impersonator bot).

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

Pride Month Special

import sys
china=0
def pridemonthspecial(info, me, round):
    xxx="[#ff0000]p[#ffaa00]r[#ffff00]i[#00ff00]d[#0000ff]e[#aa00ff]![/][/][/][/][/][/]"
    if me.__name__ not in [xxx,'pridemonthspecial']: return [1,1,1]
    global china
    if china==0 and round>1: china=me
    info=list(sorted(info,key=lambda x:(-x[1],-x[2])))
    me.__name__=xxx
    my_info = [i for i in info if i[0]==me][0]
    # Try to keep the frequency above 5:
    if my_info[2] < 10: return [me, 1, 1]
    our_info = [i for i in list(sorted(info,key=lambda x:x[2])) if i[0].__name__==me.__name__ and i[2]<10]
    if our_info: 
     worst_info=our_info[0]
     worst=worst_info[0]
     return [worst, 1, 1]
    first, second = list(sorted(info,key=lambda x:x[2]))[:2]
    if second[0].__name__==me.__name__ and first[1]-100>second[1]:
     target = first[0].__name__
     worst = list(sorted(info,key=lambda x:(x[0].__name__!=target, x[2]<=0, x[2])))[0]
     return [worst, 1, 0]
    # Otherwise just increase its own score:
    our_info = [i for i in info if i[0]==china]
    best_info=our_info[0]
    best=best_info[0]
    if best_info[2] < 20: return [best,1,1]
    return [best,0,1]
\$\endgroup\$
2
  • \$\begingroup\$ The first line uses == instead of =. Also I'm pretty sure the colors should be like [#ff0000] instead of [#f00] (at least on my computer [#f00] doesn't work) \$\endgroup\$
    – gsitcia
    Jun 17 at 1:42
  • \$\begingroup\$ Oh, whooppppppppps. \$\endgroup\$ Jun 17 at 1:45

Your Answer

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

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