55
\$\begingroup\$

Final Standings


+----------------------------------+---------+---------+---------+----------------------------+
|               Name               |  Score  | WinRate | TieRate |  Elimination Probability   |
+----------------------------------+---------+---------+---------+----------------------------+
| 1. SarcomaBotMk11                | 0.06333 | 6.13%   | 0.41%   | [42 24 10  8  6  4]%       |
| 2. WiseKickBot                   | 0.06189 | 5.91%   | 0.56%   | [51 12  7 10  7  6]%       |
| 3. StrikerBot                    | 0.05984 | 5.78%   | 0.41%   | [46 18 11  8  6  5]%       |
| 4. PerfectFractionBot            | 0.05336 | 5.16%   | 0.35%   | [49 12 14 10  6  4]%       |
| 5. MehRanBot                     | 0.05012 | 4.81%   | 0.41%   | [57 12  8  7  6  5]%       |
| 6. OgBot                         | 0.04879 | 4.66%   | 0.45%   | [50 15  9  8  7  5]%       |
| 7. SnetchBot                     | 0.04616 | 4.48%   | 0.28%   | [41 29  8  9  5  3]%       |
| 8. AntiKickBot                   | 0.04458 | 4.24%   | 0.44%   | [20 38 17 10  6  4]%       |
| 9. MehBot                        | 0.03636 | 3.51%   | 0.25%   | [80  3  4  4  3  3]%       |
| 10. Meh20Bot                     | 0.03421 | 3.30%   | 0.23%   | [57 12  8  7  9  3]%       |
| 11. GenericBot                   | 0.03136 | 3.00%   | 0.28%   | [18 39 20 11  5  3]%       |
| 12. HardCodedBot                 | 0.02891 | 2.75%   | 0.29%   | [58 21  3  6  5  4]%       |
| 13. GangBot1                     | 0.02797 | 2.64%   | 0.32%   | [20 31 35  6  3  2]%       |
| 14. SarcomaBotMk3                | 0.02794 | 2.62%   | 0.34%   | [16 15 38 17  7  4]%       |
| 15. GangBot0                     | 0.02794 | 2.64%   | 0.30%   | [20 31 35  6  3  2]%       |
| 16. GangBot2                     | 0.02770 | 2.62%   | 0.31%   | [20 31 35  6  3  2]%       |
| 17. TitTatBot                    | 0.02740 | 2.63%   | 0.21%   | [54 10 15 10  5  2]%       |
| 18. MataHari2Bot                 | 0.02611 | 2.35%   | 0.51%   | [39 26 11 11  6  5]%       |
| 19. PolyBot                      | 0.02545 | 2.41%   | 0.27%   | [53 18  6 13  5  3]%       |
| 20. SpitballBot                  | 0.02502 | 2.39%   | 0.22%   | [84 10  1  1  0  1]%       |
| 21. SquareUpBot                  | 0.02397 | 2.35%   | 0.10%   | [10 60 14  7  4  3]%       |
| 22. CautiousGamblerBot2          | 0.02250 | 2.19%   | 0.13%   | [60 18 10  5  3  1]%       |
| 23. Bot13                        | 0.02205 | 2.15%   | 0.11%   | [90  0  2  3  2  1]%       |
| 24. AggroCalcBot                 | 0.01892 | 1.75%   | 0.29%   | [26 49 13  5  3  3]%       |
| 25. CautiousBot                  | 0.01629 | 1.56%   | 0.14%   | [15 41 27 11  4  1]%       |
| 26. CoastBotV2                   | 0.01413 | 1.40%   | 0.02%   | [83 12  3  1  0  0]%       |
| 27. CalculatingBot               | 0.01404 | 1.29%   | 0.22%   | [87  9  1  1  1  1]%       |
| 28. HalfPunchBot                 | 0.01241 | 1.15%   | 0.18%   | [47 20 13 12  5  2]%       |
| 29. HalflifeS3Bot                | 0.01097 | 1.00%   | 0.20%   | [76  9  5  4  2  2]%       |
| 30. AntiGangBot                  | 0.00816 | 0.76%   | 0.11%   | [94  1  1  1  1  1]%       |
| 31. GeometricBot                 | 0.00776 | 0.74%   | 0.07%   | [19 46 25  7  2  1]%       |
| 32. GuessBot                     | 0.00719 | 0.05%   | 1.34%   | [65 17  4  6  5  3]%       |
| 33. BoundedRandomBot             | 0.00622 | 0.60%   | 0.05%   | [42 39 12  5  2  0]%       |
| 34. SpreaderBot                  | 0.00549 | 0.54%   | 0.02%   | [32 43 19  4  1  0]%       |
| 35. DeterminBot                  | 0.00529 | 0.45%   | 0.16%   | [22 41 20 11  4  2]%       |
| 36. PercentBot                   | 0.00377 | 0.38%   | 0.00%   | [85  8  4  2  1  0]%       |
| 37. HalvsiestBot                 | 0.00337 | 0.29%   | 0.08%   | [32 43 15  6  2  1]%       |
| 38. GetAlongBot                  | 0.00330 | 0.33%   | 0.01%   | [76 18  4  1  0  0]%       |
| 39. BandaidBot                   | 0.00297 | 0.29%   | 0.02%   | [76  9 10  4  1  0]%       |
| 40. TENaciousBot                 | 0.00287 | 0.29%   | 0.00%   | [94  4  1  0  0  0]%       |
| 41. SurvivalistBot               | 0.00275 | 0.25%   | 0.04%   | [92  6  1  0  0  0]%       |
| 42. RandomBot                    | 0.00170 | 0.13%   | 0.07%   | [42 36 14  5  2  1]%       |
| 43. AggressiveBoundedRandomBotV2 | 0.00165 | 0.14%   | 0.06%   | [ 8 46 34  9  2  1]%       |
| 44. BloodBot                     | 0.00155 | 0.01%   | 0.30%   | [65 28  5  1  1  0]%       |
| 45. OutBidBot                    | 0.00155 | 0.03%   | 0.25%   | [65  6 21  6  1  1]%       |
| 46. BoxBot                       | 0.00148 | 0.10%   | 0.09%   | [10 51 33  5  1  1]%       |
| 47. LastBot                      | 0.00116 | 0.08%   | 0.07%   | [74  6 16  2  1  0]%       |
| 48. UpYoursBot                   | 0.00088 | 0.07%   | 0.03%   | [37 40 17  5  1  0]%       |
| 49. AverageBot                   | 0.00073 | 0.06%   | 0.03%   | [74  3 10 10  2  0]%       |
| 50. PatheticBot                  | 0.00016 | 0.01%   | 0.02%   | [94  0  5  1  0  0]%       |
| 51. OverfittedBot                | 0.00014 | 0.01%   | 0.00%   | [58 40  2  0  0  0]%       |
| 52. RobbieBot                    | 0.00009 | 0.01%   | 0.00%   | [32 41 24  2  0  0]%       |
| 53. WorstCaseBot                 | 0.00002 | 0.00%   | 0.00%   | [ 4 71 23  2  0  0]%       |
| 54. SmartBot                     | 0.00002 | 0.00%   | 0.00%   | [44 51  5  0  0  0]%       |
| 55. AAAAUpYoursBot               | 0.00000 | 0.00%   | 0.00%   | [40 58  2  0  0  0]%       |
| 56. KickbanBot                   | 0.00000 | 0.00%   | 0.00%   | [67 32  1  0  0  0]%       |
| 57. OneShotBot                   | 0.00000 | 0.00%   | 0.00%   | [ 2 95  3  0  0  0]%       |
| 58. KickBot                      | 0.00000 | 0.00%   | 0.00%   | [100   0   0   0   0   0]% |
| 59. KamikazeBot                  | 0.00000 | 0.00%   | 0.00%   | [100   0   0   0   0   0]% |
| 60. MeanKickBot                  | 0.00000 | 0.00%   | 0.00%   | [100   0   0   0   0   0]% |
+----------------------------------+---------+---------+---------+----------------------------+

Thanks for everyone who participated, and congratulations to @Sarcoma for the win!

Rules:

Everyone starts with 100 hp. Each round, 2 players are chosen at random from the pool of contestants who have not yet competed in that round. Both players pick a number between 0 and their current hp, and reveal those numbers at the same time. The player who chose the lower number immediately dies. The other player subtracts their chosen number from their remaining hp and goes on to the next round.

The tournament works like this:

From the bracket of contestants, 2 are chosen at random. They face off, and one or both of them dies. A player dies if:

  1. They choose a number smaller than that of their opponent
  2. Their hp drops to or below zero
  3. They tie three times in a row with their opponent

In the case of ties, both players simply generate new numbers, up to 3 times. After the faceoff, the survivor (if any) is moved to the pool for the next round, and the process repeats until we have exhausted the current round pool. If there is an odd number in the pool, then the odd one out moves on to the next round for free.

Your task is to write a function in python2.7 which takes as inputs your current hp, a list of your opponent's bid history, and an integer ties which tells you how many times you have already tied with your current opponent, and an integer which tells you how many bots are still alive (including you), and an integer which listed the number of bots at the start of the tournament. Note that the history does not include ties. The function must return an integer between 0 and your current total hp. A few simple examples, which ignore ties, are shown below:

def last(hp, history, ties, alive, start):
    ''' Bet a third of your hp at first, then bet your opponent's last bid, if possible '''
    if history:
        return np.minimum(hp-1, history[-1])
    else:
        return hp/3
    
def average(hp, history, ties, alive, start):
    ''' Bet the average opponent's bid so far, on the assumption that bids will tend downward '''
    if history:
        num = np.minimum(hp-1, int(np.average(history))+1)
    else:
        num = hp/2
    return num

def random(hp, history, ties, alive, start):
    ''' DO YOU WANT TO LIVE FOREVER?! '''
    return 1 + np.random.randint(0, hp)

If your function returns a number larger than your hp, it will be reset to 0. Yes, it is possible to kill yourself. Your function must not attempt to access or modify any member of any object of the RouletteBot class. You are not allowed to take any action which unambiguously identifies your opponent regardless of future additional bots. Inspecting the stack is allowed as long as it is theoretically possible that more than one distinct opponent could have produced the information you glean from it, even if only one bot currently exists that could. ie, you can't just read through the stack to see which enemy function was called.

Under these rules it is possible that there is no winner, and the last two contestants kill each other. In that case both finalists get half a point each.

This is my first programming puzzle attempt, so critiques are welcome!

The controller can be found here.

\$\endgroup\$
54
  • 4
    \$\begingroup\$ FWIW, I plan to use a neural network trained on all the other bots just for fun once you set up the controller :) \$\endgroup\$
    – Quintec
    Oct 2, 2018 at 22:27
  • 2
    \$\begingroup\$ The type check was for the benefit of antiantiantiantiupyoursbot. I'll find another way \$\endgroup\$
    – KBriggs
    Oct 4, 2018 at 16:10
  • 3
    \$\begingroup\$ @Sarcoma Seems like this competition has sparked a serious code war. This competition hasn't ended, but I'm already looking forward to evolution of this. Maybe even the next step, AI enhanced competition :P \$\endgroup\$ Oct 4, 2018 at 21:27
  • 3
    \$\begingroup\$ WOOOOOOOOOOOOOO! \$\endgroup\$ Oct 6, 2018 at 15:23
  • 5
    \$\begingroup\$ Oh my god. The deliberate trolling of changing mean_kick to always return zero when it was used so many places is brilliant. \$\endgroup\$
    – Magua
    Oct 6, 2018 at 17:07

37 Answers 37

1
2
3
\$\begingroup\$

Gets the median bid of all other bots and outbids by one

Edit: Suicide protection and remove bots that are likely to fail in the first round after the first round

Edit 2: spelling

ogbot V2.0.1

def ogbot(hp, history, ties, alive, start):
    otherBots = functions = [f for f in globals().values() if type(f) == types.FunctionType]

    def whatWouldOtherbotDo(bot,hp,history,ties,alive,start):
        botname = bot.__name__
        # got to avoid self referencing
        if(botname)=='ogbot':
            return -999

        # avoid non bot functions 
        try:
            return bot(hp,history,ties,alive,start)
        except (TypeError, NameError, RuntimeError):
            return -999

    otherBotCurrentBids = []

    otherBotOpeningBids = []    

    for bot in otherBots:
        otherBotCurrentBids.append(whatWouldOtherbotDo(bot,hp,history,ties,alive,start))
        otherBotOpeningBids.append(whatWouldOtherbotDo(bot,100,[],0,start,start))

    # remove invalid outputs
    otherBotCurrentBids = filter(lambda a: a != -999, otherBotCurrentBids)
    otherBotOpeningBids = filter(lambda a: a != -999, otherBotOpeningBids)

    # if this is the second round or later, try to deal with bots who were likely to pass the first round
    if len(history)>ties:
        medianOpening = np.median(otherBotOpeningBids)
        # get the bots who probably passed the first round and aren't committing suicide
        likelySuccessBots = [(a,b) for a,b in zip(otherBotCurrentBids, otherBotOpeningBids) if (b>medianOpening) & (a<hp)]      
        otherBotCurrentBids = [a for a,b in likelySuccessBots]


    out = min(np.median(otherBotCurrentBids) + 1,hp-1)
    return out
\$\endgroup\$
7
  • \$\begingroup\$ Running the sim now, I'm looking forward to seeing how this does. You actually don't slow things down much, so if you wanted to do more careful simulation you can. \$\endgroup\$
    – KBriggs
    Oct 5, 2018 at 23:59
  • \$\begingroup\$ Just pushed an update. It does surprisingly well @KBriggs \$\endgroup\$
    – OganM
    Oct 6, 2018 at 0:00
  • \$\begingroup\$ Yea I got the update, no worries \$\endgroup\$
    – KBriggs
    Oct 6, 2018 at 0:00
  • \$\begingroup\$ Huh.. this has more variation than I thought it would. On my run I had 5th place. Will you increase the N for the final? Edit: though it could be because my version wasn't up to date \$\endgroup\$
    – OganM
    Oct 6, 2018 at 0:09
  • \$\begingroup\$ I'll do at least a million for the final, maybe 10 million if its feasible. \$\endgroup\$
    – KBriggs
    Oct 6, 2018 at 0:09
3
\$\begingroup\$

MataHariBot

Illegal access of information in the tournament allows it to identify its opponent. Left for posterity.

def MataHariBot(hp, history, ties, alive, start):
    ''' 
    Interrogate our opponent about what they're going to do
    and use that against them
    '''     

    if alive <= 3:
        return hp - 1

    debug = False

    # Hello antiantiantiantiupyoursbot and your inspect.stack modification
    f = inspect.currentframe()
    target_frame = None
    depth = 0
    while True:
        f = f.f_back
        if f is None:
            break
        depth = depth + 1
        if depth == 2:
            target_frame = f

    if target_frame is None or target_frame.f_code.co_name != 'tournament':
        if debug:
            print('Skullduggery!')
        return hp - 1

    # Find our opponent
    opponent = None
    us = None
    for key, value in target_frame.f_locals.iteritems():
        if not isinstance(value, RouletteBot):
            continue
        if value.func.__code__.co_name == inspect.currentframe().f_code.co_name:
            us = value
        else:
            opponent = value

    if us is None or opponent is None:
        if debug:
            print('Falsity!')
        return hp - 1

    results = [ ]
    for i in range(random.randint(100, 151)):
        result = opponent.func(opponent.hp, us.history, ties, alive, start)
        # pathetic_attempt_at_analytics_bot sometimes returns None, though
        # I couldn't figure out why with a quick glance at its code.
        if result is None:
            result = 0
            if debug:
                print("%s returned None" % (opponent.__code__.co_name))
        results.append(result)

    # If we have a deterministic result, use that
    if np.allclose(results, results[0]):
        guess = results[0]
    # If we have a small range of guesses, use the maximum
    elif np.max(results) - np.min(results) <= hp / 3:
        guess = np.max(results)
    # Otherwise, we're dealing with a wide range of guesses and can just hope
    else:
        guess = np.median(results) * 1.25

    return np.minimum(hp - 1, int(guess) + 1)

MataHari2Bot

Uses (allowed) reflection to get all the bots. Simulates a bunch in order to get a good first round choice, and then on later rounds simulates bots that may be its opponent based on history.

Caches a bunch of data so performance is not terrible. Perhaps for some definitions of terrible. Also may contain more code than all the other bots put together. Or close to it.

When updating to the latest controller I discovered OgBot. The two do not work well together at all, since they both work by calling others, which leads to recursion if left alone. So MataHari2Bot gives OgBot the cold shoulder.

They both have the same strategy for round 1, but for rounds 2+ OgBot tries to figure out what other bots would do in its situation (with the hp and history it was given); MataHari2Bot tries to figure out what bot it is likely to be against and then tries to figure out what that bot would do to it.

def MataHari2Bot(hp, history, ties, alive, start):
    ''' 
    Round 1: Determine what our opponents do in the beginning with no information,
    give the assumed best counter.

    Round 2: Using the information from round 1, determine which of our opponents
    are possible given their history.  Determine what they'll do with our history
    (which we know from our hp) and give the assumed best counter.

    Round 3+: Same as round 2, except we have to make up our history based on our
    hp (not knowing our own history is a really odd decision but w/e), which leads
    to less and less precision.
    '''

    this = MataHari2Bot
    INITIAL_HP = 100

    # It's not that I hate Ogbot, it's that otherwise we get recursion
    if inspect.currentframe().f_back.f_back.f_code.co_name != 'tournament':
        return 1

    def simulate(method, hp, history, ties, alive, start):
        # print("Running simulation for %s when we are %i HP, opponent is %i HP" % (method.__name__, hp, INITIAL_HP - np.sum(history) + 1))
        return [ method(hp, history, ties, alive, start) for x in range(200) ]

    def best_guess(hp, results):
        low = np.min(results)
        high = np.max(results)
        if high == low or high - low <= 5:
            guess = high
        else:
            guess = np.median(results) 
        if guess is None:
            return 1
        return guess

    class BotInfo:
        def __init__(self, name, method, hp, history, ties, alive, start):
            self.name = name
            self.method = method
            self.round1_simulations = simulate(self.method, hp, history, ties, alive, start)
            self.round1_guess = best_guess(hp, self.round1_simulations)
            self.guesses = { }

        def guess(self, hp, history, ties, alive, start):
            opponent_hp = INITIAL_HP - np.sum(history)
            key = (hp * 100) + opponent_hp
            if key not in self.guesses:
                try:
                    simulations = simulate(self.method, opponent_hp, [ INITIAL_HP - hp ], ties, alive, start)
                except ValueError:
                    # BoundedRandomBot has a problem if you feed it a hp of 1 and a history of 99; this doesn't
                    # occur normally because it's not actually included in reset_brackets, perhaps by oversight?
                    # Anyways, ignore it.
                    # print("Error occurred calling %s with (%i, %s, %i, %i, %i); our hp %i, opponent history %s" % (self.name, opponent_hp, [ INITIAL_HP - hp ], ties, alive, start, hp, history))
                    if self.name != 'BoundedRandomBot':
                        raise
                    simulations = [ 1 ]

                self.guesses[key] = best_guess(hp, simulations)
            return self.guesses[key]

    if not hasattr(this, 'bots'):
        # Could've used reset_brackets, but abiding by the rules to not touch RouletteBot even if they're not the ones
        # being used in the tournament
        print("Initializing %s; this should only happen once" % (this.__name__))
        # We have to skip OgBot or the "Simulate OgBot 200 times" then leads into OgBot calling every other bot and performance just goes
        # to hell
        blacklist = [ this.__name__, 'ogbot' ]
        this.bots = [ ]
        for key, value in globals().iteritems():
            if key not in blacklist and type(value) == type(this) and len(inspect.getargspec(value).args) == 5:
                bot = BotInfo(key, value, hp, history, ties, alive, start) 
                # Ignore the suicide bots.  Even if we beat them in round 1, we'll be down too many hp to win, so they
                # will just skew our medians pointless
                # Also ignore the wildly random bots
                if bot.round1_guess < 70 and (np.max(bot.round1_simulations) - np.min(bot.round1_simulations)) < 40:
                    this.bots.append(bot)

    # Round1 will always be the same, no need to recalculate every time
    if len(history) == 0:
        if hasattr(this, 'round1_guess'):
            return this.round1_guess + ties
        guesses = [ bot.round1_guess for bot in this.bots ]
    else:
        # Otherwise, determine which bots could have given us the round1 guess we have in history
        # Weight this; the guess of a bot that deterministically returns this number is worth more
        # than one that returned it only 5% of the time
        guesses = [ ]
        for bot in this.bots:               
            if history[0] in bot.round1_simulations:
                guess = bot.guess(hp, history, ties, alive, start)
                guesses.extend( [ guess ] * bot.round1_simulations.count(history[0]))

    if len(guesses) == 0:
        # yolo
        guess = mean_kick(hp, history, ties, alive, start) + 1
    else:
        guess = int(math.ceil(np.median(guesses))) + 1
        # As long as it doesn't cost us too much, keep going up.  This catches the various
        # "do it like Xbot but adding 1" bots
        while guess in guesses:
            guess = guess + 1
    guess = min(guess, hp - 1 + ties, INITIAL_HP - np.sum(history) + ties)
    if len(history) == 0:
        this.round1_guess = guess
        print("MataHari2Bot's Round1 Guess: %i" % (this.round1_guess))
        #ordered_bots = sorted(this.bots, key = lambda x: x.round1_guess)
        #i = 1
        #for bot in ordered_bots:
        #   print("\t%i: %s (%i)" % (i, bot.name, bot.round1_guess))
        #   i = i + 1
    return guess

OverfittedBot

Ran into this a bunch while doing MataHari2Bot. The whole 'kickbot' ecosystem, and then the bots that rely on it, and the bots that counter it, really distort the field. This bot is incredibly overfitted for the specific environment I was running in, based on data I got while testing MataHari2Bot, and goes down in performance significantly if the initial value of 40 is changed, or other bots in the 'kick' ecosystem are put in or taken out, or basically anything else. But so it goes.

def OverfittedBot(hp, history, ties, alive, start):
    if alive == 2:
        return hp - 1
    opponent_hp = 100 - np.sum(history)
    if opponent_hp == 100:
        guess = 40 + ties
    elif len(history) == 1 and history[0] == 39:
        guess = opponent_hp * 0.50 + 1 + ties
    else:
        guess = mean_kick(hp, history, ties, alive, start) + 1 
    return min(guess, hp - 1 + ties, 100 - np.sum(history) + ties)   
\$\endgroup\$
10
  • \$\begingroup\$ Sorry, I explicitly disallow entries that attempt to access or modify properties of other bots. You can call the general functions they use, but you can't call them via the bot objects themselves, which is what I think you're doing here. Pretty sure your immune system is legit though. I'll need more time to analyze before I enter it. You're also not allowed to access your own history. \$\endgroup\$
    – KBriggs
    Oct 4, 2018 at 21:47
  • \$\begingroup\$ My misunderstanding; I thought the rule was just about disallowing modification. When you say "can't call them via the bot objects themselves", you mean via the RouletteBot class? \$\endgroup\$
    – Magua
    Oct 4, 2018 at 21:48
  • \$\begingroup\$ Yes, it might be a bit ambiguously worded. You can't access or modify the properties of any roulettebot instance, including yourself. Very clever, though. \$\endgroup\$
    – KBriggs
    Oct 4, 2018 at 21:49
  • \$\begingroup\$ Ok. Two followup questions: 1. Is it allowed to get our own history (as opposed to our opponent's)? 2. Is it allowed to inspect the methods of the file in order to find all functions that are bots? \$\endgroup\$
    – Magua
    Oct 4, 2018 at 21:51
  • \$\begingroup\$ No, as the rules stand you can't get your own history. I'm not sure about the second one, since you're clearly much better st this than I am ^_^. What did you have in mind? \$\endgroup\$
    – KBriggs
    Oct 4, 2018 at 21:53
3
\$\begingroup\$

SmartBot

def smart_bot(hp, history, ties, alive, start):
    op_high_bid = 0
    op_hp = 100
    hptoret = 1
    his = False
    tienum = (ties * ties) + 1 if ties else ties
    if not history:
        return 42 + tienum
        #return obots(hp, history, ties, alive, start)
    if his:
        if history[0]==36:
            return min(wise_kick(hp, history, ties, alive, start) + 3, hp-1)
        
    if alive == 2:
        if history[0]==36:
            return min(wise_kick(hp, history, ties, alive, start) + 3, hp-1)
        return hp - 1
    if history:
        his = True
    
    if his:
        for h in history:
            if int(h) > op_high_bid:
                op_high_bid = h
    if his:
        op_hp = 100 - sum(history)
        if op_hp >= hp:
            return np.floor(hp / 3)
        
    
    roundn = len(history) + 1
    #if roundn > 6:
        #return hp - 1
    """Overrideable(If above did not execute of course)"""
    
    if op_hp < hp:
        """Kick em hard"""
        hptoret = wise_kick(hp, history, ties, alive, start)
    while op_hp < hptoret:
        hptoret -= 1
    hptoret += 1
    return hptoret

It only got 35th place(I ran the executer) EDIT: Doing MUCH better!

First, SmartBot Bids around 1/3 of its hp(When I fine-tune the code this may go up or down). Next It checks if it is with the last bot. Next priority is attacking MeanKickBot. Then, if the Opponents HP is higher it bids 1/3 of its hp(Whoops just caught a mistake np.floor() fixes it). If it is round six, SmartBot just forfeits all but 1 hp, Probably not having much left. Then it gets to the overrideables(One If can change the change of another If, using the hptoret variable) First, if first if Opponents hp is less than SmartBot's hp, It checks how much greater, then, if the opponent's hp is more then 11 less than SmartBot, if so, SamrtBot bids opponents hp + 1, otherwise, it gets mean_kicked(Thanks MeanKickBot)

SmartBot takes a lot of logic, hence SmartBot.

These Tweaks Are worth it!

I got my bot somewhat better with tweaks.

Everybody should tweak their code, it is much worth it

\$\endgroup\$
15
  • \$\begingroup\$ Let us continue this discussion in chat. \$\endgroup\$
    – KBriggs
    Oct 6, 2018 at 13:10
  • \$\begingroup\$ Darn it @KBriggs Wise kickbot destroyed me \$\endgroup\$
    – Menotdan
    Oct 6, 2018 at 14:02
  • \$\begingroup\$ You're not the only one ^_^ \$\endgroup\$
    – KBriggs
    Oct 6, 2018 at 14:03
  • \$\begingroup\$ Well, after deadline im making a franken bot would you run the test case after that, and see how he does? @KBriggs \$\endgroup\$
    – Menotdan
    Oct 6, 2018 at 14:04
  • 1
    \$\begingroup\$ I would have gotten 3rd in final if not for that wise kickbot \$\endgroup\$
    – Menotdan
    Oct 6, 2018 at 14:21
2
\$\begingroup\$

BoundedRandomBot

A simple variation on a pick-a-random-number bot, where it will never pick a number higher than opponent's HP + 1 (except in the final round, where it borrows SarcomaBot's trick of betting remaining HP - 1).

def boundedRandomBot(hp, history, ties, alive, start):
    max_possible_bid = hp - 1
    if alive == 2 or max_possible_bid == 0:
        return max_possible_bid

    if history:
        opp_hp = 100 - sum(history)
        bid_ceiling = min(opp_hp+1, max_possible_bid)
    else:
        bid_ceiling = max_possible_bid
    return np.random.randint(1, bid_ceiling+1)

AggressiveBoundedRandomBotV2

Like the above, but with a floor instead of a ceiling; instead of never picking above opponent's HP + 1, it will never pick below opponent's HP / 2 (unless of course it doesn't have that much HP).

def aggressiveBoundedRandomBotV2(hp, history, ties, alive, start):
    max_possible_bid = hp - 1
    if alive == 2 or max_possible_bid == 0:
        return max_possible_bid

    if history:
        opp_hp = 100 - sum(history)
    else:        
        opp_hp = 100

    bid_floor = min(np.ceil(opp_hp * 0.5), max_possible_bid)
    return np.random.randint(bid_floor, max_possible_bid+1)
\$\endgroup\$
2
  • \$\begingroup\$ Thanks to @KBriggs for catching my bug. \$\endgroup\$
    – MJ713
    Oct 5, 2018 at 19:45
  • \$\begingroup\$ @KBriggs updated AggressiveBoundedRandomBot to AggressiveBoundedRandomBotV2 \$\endgroup\$
    – MJ713
    Oct 5, 2018 at 23:04
2
\$\begingroup\$

Killerbot

def KillerBot(hp, history, ties, alive, start):
    curr_hp = 100
    #if we're the last one alive just bet our hp
    if alive == 2:
        return hp - 1
    #calculate their hp
    their_hp = 100 - sum(history)

    #go through their history
    if history.length() > 1 and hp > their_hp:
        ratios = []
        num_hist = 0
        their_hp = 100
        for hist in history:
           top = curr_hp
            curr_hp -= hist
            bot = curr_hp
            ratios.append((top/bot))
            num_hist += 1
        if(ties < 2):
            return 100*(ratios/num_hist + 2)
        else:
            return 100*(ratios/num_hist + 3)
    else:
        #first round bid 20
        return 20

    return hp - 1

This solution tries to check for the average ratio of opponent's bets and tries to bet slightly higher. First time posting so hopefully it goes well.

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

HardCoded

def hc(hp, history, ties, alive, start):
    plan = [40,27,20,10,2,5,5,5,5,5,2,2,2,2,2,1,1,1,1,1,100,1]
    ohp = 100 - sum(history)
    if ohp < hp * 0.16:
        return ohp + 1
    x = 100
    k = 0
    while x > hp:
        x -= plan[k]
        k += 1
    if plan[k] > ohp:
        return ohp + 1
    return plan[k] + ties

I tried a bunch of values trying to get a decent bot out of fixed values. It does okay, but won't be winning overall for sure.

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

Hodgepodge

def hodgepodge(hp, history, ties, alive, start):
    enemyHealth = 100-sum(history)
    bl = 0
    if enemyHealth < 100:
        if enemyHealth > 96:
            # if somehow the bot survived last round, murder it cheaply
            return (ties*2)+1
    if ties == 2:
        # lots of ties? max bid
        return hp - 1
    if hp < 3:
        # little remaining hp? return minimum
        return 1
    if enemyHealth > hp:
        # Rip off SarcomaBot
        r = sarcoma_bot_mk_eleven(hp-1,history,ties,alive,start) + 1
        if bl:
            print r
        return r
    if history:
        # rip off KickBan
        return kickban(hp,history,ties,alive,start)
    if alive == 2:
        return hp-1
    if not history:
        return 42

Didn't realize there was a final end-date and was futzing around with this for a while. Tends to come in somewhere between 3rd and 5th.

\$\endgroup\$
1
2

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.