[Question]
  [
**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](https://github.com/shadowk29/RobotRoulette/).
  
[Answer]
# UpYours
Being late to enter I spent a while admiring the existing bots, spent a while overcomplicating your guys' ideas, then un-overcomplicating them. Then it came to me
> 
> Good artists copy, great artists steal. -- ~~Pablo Picasso~~ Me
> 
> 
> 
---
"Up Yours" because I'm unabashedly stealing (and sometimes tacking a point or two onto your bots' bids to one up them).
```
def UpYoursBot(hp, history, ties, alive, start):
    willToLive = "I" in "VICTORY"
    args = [hp, history, ties, alive, start]
    enemyHealth = 100 - sum(history)
    roundNumber = len(history)
    if roundNumber is 0:
        # Steal HalfPunchBot
        return halfpunch(*args) + 2
    if alive == 2:
        # Nick OneShotBot
        return one_shot(*args)
    if enemyHealth >= hp:
        # Pinch SarcomaBotMkTwo
        return sarcomaBotMkTwo(*args) + 1
    if enemyHealth < hp:
        # Rip off KickBot
        return kick(*args) + 1
    if not willToLive:
        # Peculate KamikazeBot
        return kamikaze(*args) + 1
```
But for real, this is a great competition guys. I love this community on days like this.
[Answer]
# Kamikaze
Why bother with complicated logic when we are all going to die anyway...
```
 def kamikaze(hp, history, ties, alive):
      return hp
```
  
# One shot
It's going to survive at least a single round if it doesn't encounter the kamikaze.
```
 def one_shot(hp, history, ties, alive):
      if hp == 1:
          return 1
      else:
          return hp - 1
```
[Answer]
## Pathetic Bot gets a much needed upgrade:
# The pathetic attempt at a bot that tries to incorporate other bots' features
```
def pathetic_attempt_at_analytics_bot(hp, history, ties, alive, start):
    '''Not a good bot'''
    if hp == 100 and alive == 2:
        return hp - 1
    #This part is taken from Survivalist Bot, thanks @SSight3!
    remaining = alive - 2
    btf = 0
    rt = remaining
    while rt > 1:
        rt = float(rt / 2)
        btf += 1
    if ties > 2:
        return hp - 1
    if history:
        opp_hp = 100 - sum(history)
        #This part is taken from Geometric Bot, thanks @Mnemonic!
        fractions = []
        health = 100
        for x in history:
            fractions.append(float(x) / health)
            health -= x
        #Modified part
        if len(fractions) > 1:
            i = 0
            ct = True
            while i < len(fractions)-1:
                if abs((fractions[i] * 100) - (fractions[i + 1] * 100)) < 1:
                    ct = False
                i += 1
            if ct:
                expected = fractions[i] * opp_hp
                return expected
        if alive == 2:
            if hp > opp_hp:
                return hp - 1
            return hp
        if hp > opp_hp + 1:
            if opp_hp <= 15:
                return opp_hp + 1
            if ties == 2:
                return opp_hp + 1
            else:
                return opp_hp
    else:
        n = 300 // (alive - 1) + 1 #greater than
        if n >= hp:
            n = hp - 1
        return n
```
This bot incorporates features from Survivalist Bot and Geometric Bot for more efficient bot takedowns.
## Pre-Upgrade:
# The pathetic attempt at a bot that analyzes the history of its opponent
```
def pathetic_attempt_at_analytics_bot(hp, history, ties, alive, start):
    '''Not a good bot'''
    if history:
        opp_hp = 100 - sum(history)
        if alive == 2:
            if hp > opp_hp:
                return hp - 1
            return hp
        if hp > opp_hp + 1:
            if opp_hp <= 15:
                return opp_hp +1
            if ties > 0:
                return hp - 1 #Just give up, kamikaze mode
            return opp_hp + 1
        return opp_hp
    else:
        n = 300 // (alive - 1) + 1 #greater than
        if n >= hp:
            n = hp - 1
        return n
```
If there is previous history of its opponent, then it calculates its opponent's hp. Then, it does one of the following:
* If its opponent is the last opponent alive, then it will bid one less than its hp.
* If its opponent is not the last opponent alive but the opponent has less than 16 hp, then it will outbid its opponent's hp.
* If its opponent is not the last opponent alive and there is a history of ties, then it will bid its hp because it is bored of ties.
* Otherwise, it will outbid its opponent.
If there is no history, then it does some fancy calculations that I hacked together and bids that. If the value exceeds 100, then it automatically bids its hp minus 1.
I hacked this code together during work and this is my first submission, so it probably won't win or anything, and it'll lose to the kamikaze.
EDIT: Due to some suggestions, the bot's beginning behavior has been changed to bid a higher value.
EDIT 2: added start param that does nothing
EDIT 3: Added new spinoff bot:
# [The pathetic attempt at a bot that attacks Gang Bots (as well as doing everything the above bot does)] REMOVED
[This bot analyzes whether its opponent is a gangbot or not and pretends to be one as well to get the sweet low bids that it can trump easily.]
This bot has been scrapped, please remove it from the leaderboards.
EDIT 4: Fixed errors, changed tie feature.
[Answer]
# BinaryBot
Has anyone done this yet? Bets half its health every round floored.
```
def binaryBot(hp, history, ties, alive, start):
    return int(np.floor(hp/2)) or 1
```
# SarcomaBot
If last battle bid hp - 1. If it's the first battle round bid half hp plus an additional random amount up to a quarter of hp. If it can beat an opponent outright bid after that bid opponent hp + 1. If it has lower health than opponent bid random amount between 75% and it's current hp - 1. 
```
def sarcomaBot(hp, history, ties, alive, start):
    if inspect.stack()[1][3] != 'guess' and inspect.stack()[1] == 5:
        return hp
    if alive == 2:
        return hp - 1
    if not history:
        startBid = hp / 2
        maxAdditionalBid = np.round(hp * 0.25) if hp * 0.25 > 2 else 2
        additionalBid = np.random.randint(1, maxAdditionalBid)
        return int(startBid + additionalBid + ties)
    opponentHealth = 100 - sum(history)
    if opponentHealth < hp:
        return opponentHealth + ties
    minimum = np.round(hp * 0.75)
    maximum = hp - 1 or 1
    return np.random.randint(minimum, maximum) if minimum < maximum else 1
```
# SarcomaBotMk2
Minor tweaks attempt to reduce life expenditure.
```
def sarcomaBotMkTwo(hp, history, ties, alive, start):
    if inspect.stack()[1][3] != 'guess' and inspect.stack()[1] == 5:
        return hp
    if alive == 2:
        return hp - 1
    if not history:
        startBid = hp / 2
        maxAdditionalBid = np.round(hp * 0.125) if hp * 0.125 > 2 else 2
        additionalBid = np.random.randint(1, maxAdditionalBid)
        return int(startBid + additionalBid + ties)
    opponentHealth = 100 - sum(history)
    if opponentHealth < hp:
        return opponentHealth + ties
    minimum = np.round(hp * 0.6)
    maximum = hp - 1 or 1
    return np.random.randint(minimum, maximum) if minimum < maximum else 1
```
# SarcomaBotMk3
```
def sarcomaBotMkThree(hp, history, ties, alive, start):
    if inspect.stack()[1][3] != 'guess' and inspect.stack()[1] == 5:
        return hp
    if alive == 2:
        return hp - 1
    if not history:
        startBid = hp / 2
        maxAdditionalBid = np.round(hp * 0.08) if hp * 0.08 > 2 else 2
        additionalBid = np.random.randint(1, maxAdditionalBid)
        return int(startBid + additionalBid + ties)
    opponentHealth = 100 - sum(history)
    if opponentHealth < hp:
        return opponentHealth + ties
    minimum = np.round(hp * 0.6)
    maximum = hp - 1 or 1
    return np.random.randint(minimum, maximum) if minimum < maximum else 1
```
**Update Fine Tuning** 
# SarcomaBotMk4
```
def sarcomaBotMkFour(hp, history, ties, alive, start):
    def isSafe(parentCall):
        frame, filename, line_number, function_name, lines, index = parentCall
        if function_name is not 'guess':
            return False
        if line_number > 60:
            return False
        return True
    if not isSafe(inspect.stack()[1]):
        return hp
    if alive == 2:
        return hp - 1
    if not history:
        startBid = hp / 2
        maxAdditionalBid = np.round(hp * 0.08) if hp * 0.08 > 2 else 2
        additionalBid = np.random.randint(1, maxAdditionalBid)
        return int(startBid + additionalBid + ties)
    opponentHealth = 100 - sum(history)
    if opponentHealth < hp:
        return opponentHealth + ties
    minimum = np.round(hp * 0.55)
    maximum = np.round(hp * 0.80) or 1
    return np.random.randint(minimum, maximum) if minimum < maximum else 1
```
# SarcomaBotMk5
```
def sarcomaBotMkFive(hp, history, ties, alive, start):
    def isSafe(parentCall):
        frame, filename, line_number, function_name, lines, index = parentCall
        if function_name is not 'guess':
            return False
        if line_number > 60:
            return False
        return True
    if not isSafe(inspect.stack()[1]):
        return hp
    if alive == 2:
        return hp - 1
    if not history:
        startBid = hp / 2
        maxAdditionalBid = np.round(hp * 0.07) if hp * 0.07 > 3 else 3
        additionalBid = np.random.randint(1, maxAdditionalBid)
        return int(startBid + additionalBid + ties)
    opponentHealth = 100 - sum(history)
    if opponentHealth < hp:
        return opponentHealth + ties
    minimum = np.round(hp * 0.54)
    maximum = np.round(hp * 0.68) or 1
    return np.random.randint(minimum, maximum) if minimum < maximum else 1
```
# SarcomaBotMk6
```
def sarcomaBotMkSix(hp, history, ties, alive, start):
    return hp; # hack averted
    def isSafe(parentCall):
        frame, filename, line_number, function_name, lines, index = parentCall
        if function_name is not 'guess':
            return False
        if line_number > 60:
            return False
        return True
    if not isSafe(inspect.stack()[1]):
        return hp
    if alive == 2:
        return hp - 1
    if not history:
        startBid = hp / 2
        maxAdditionalBid = np.round(hp * 0.06) if hp * 0.06 > 3 else 3
        additionalBid = np.random.randint(2, maxAdditionalBid)
        return int(startBid + additionalBid + ties)
    opponentHealth = 100 - sum(history)
    if opponentHealth < hp:
        return opponentHealth + ties
    minimum = np.round(hp * 0.55)
    maximum = np.round(hp * 0.70) or 1
    return np.random.randint(minimum, maximum) if minimum < maximum else 1
```
# SarcomaBotMk7
```
def sarcomaBotMkSeven(hp, history, ties, alive, start):
    if alive == 2:
        return hp - 1
    if not history:
        return 30 + ties
    opponentHealth = 100 - sum(history)
    if opponentHealth < hp * 0.50:
        return opponentHealth + ties
    minimum = np.round(hp * 0.54)
    maximum = np.round(hp * 0.58) or 1
    return np.random.randint(minimum, maximum) if minimum < maximum else 1
```
# SarcomaBotMk8
```
def sarcomaBotMkEight(hp, history, ties, alive, start):
    if alive == 2:
        return hp - 1
    if not history:
        return 30 + np.random.randint(0, 2) + ties
    opponentHealth = 100 - sum(history)
    if opponentHealth < hp * 0.50:
        return opponentHealth + ties
    minimum = np.round(hp * 0.54)
    maximum = np.round(hp * 0.58) or 1
    return np.random.randint(minimum, maximum) if minimum < maximum else 1
```
# SarcomaBotMk9
```
def sarcomaBotMkNine(hp, history, ties, alive, start):
    if alive == 2:
        return hp - 1
    if not history:
        return 30 + np.random.randint(0, 4) + ties
    opponentHealth = 100 - sum(history)
    if opponentHealth < hp * 0.50:
        return opponentHealth + ties
    minimum = np.round(hp * 0.54)
    maximum = np.round(hp * 0.58) or 1
    return np.random.randint(minimum, maximum) if minimum < maximum else 1
```
# SarcomaBotMk10
```
def sarcoma_bot_mk_ten(hp, history, ties, alive, start):
    def bid_between(low, high, hp, tie_breaker):
        minimum = np.round(hp * low)
        maximum = np.round(hp * high) or 1
        return np.random.randint(minimum, maximum) + tie_breaker if minimum < maximum else 1
    if alive == 2:
        return hp - 1 + ties
    current_round = len(history) + 1
    tie_breaker = (ties * ties) + 1 if ties else ties
    if current_round == 1:
        return 39 + tie_breaker
    opponent_hp = 100 - sum(history)
    if opponent_hp < hp * 0.50:
        return opponent_hp + ties
    if current_round == 2:
        return bid_between(0.45, 0.50, hp, tie_breaker)
    if current_round == 3:
        return bid_between(0.50, 0.55, hp, tie_breaker)
    if current_round == 4:
        return bid_between(0.55, 0.60, hp, tie_breaker)
    if current_round == 5:
        bid_between(0.60, 0.65, hp, tie_breaker)
    return hp - 1 + ties
```
# Final Entry
# SarcomaBotMk11
```
def sarcoma_bot_mk_eleven(hp, history, ties, alive, start):
    def bid_between(low, high, hp, tie_breaker):
        minimum = np.round(hp * low)
        maximum = np.round(hp * high) or 1
        return np.random.randint(minimum, maximum) + tie_breaker if minimum < maximum else 1
    if alive == 2:
        return hp - 1 + ties
    current_round = len(history) + 1
    tie_breaker = ties + 2 if ties else ties
    if current_round == 1:
        return 42 + tie_breaker
    opponent_hp = 100 - sum(history)
    if opponent_hp < hp * 0.50:
        return opponent_hp + ties
    if current_round == 2:
        return bid_between(0.45, 0.50, hp, tie_breaker)
    if current_round == 3:
        return bid_between(0.50, 0.55, hp, tie_breaker)
    if current_round == 4:
        return bid_between(0.55, 0.60, hp, tie_breaker)
    if current_round == 5:
        return bid_between(0.60, 0.65, hp, tie_breaker)
    return hp - 1 + ties
```
**Update**  
UpYoursBot protection added
**Update**  
AntiAntiUpYoursBot protection added
**Update**  
AntiAnitAntiAntiUpYoursBot I'm defeated
[Answer]
# Kick Bot
The sound choice for my opponent is to bid half of his life. Then we bid up to half of his life+1 if we can't take him out with a sound bid, that is a bid smaller than half of our life.
```
def kick(hp, history, ties, alive, start):
    return 0
    if alive == 2:
        return hp-1
    opp_hp = 100 - sum(history)
    if opp_hp*2 <= hp:
        return opp_hp + ties
    else:
        return min(round(opp_hp/2) + 1 + ties**2, hp-1 + (ties>0))
```
The kick bot is obviously the nemesis of the punch bot!
# Mean Kick Bot
This new KickBot kicks softer on the first round just so he may kick harder on next rounds, that is mean!
```
def mean_kick(hp, history, ties, alive, start):
    return 0
    if alive == 2:
        return hp-1
    if not history:
        return 35
    opp_hp = 100 - sum(history)
    if opp_hp*2 <= hp:
        return opp_hp + ties
    else:
        return min(round(opp_hp/2) + 3 + ties*2, hp-1 + (ties>0))
```
# Wise Kick Bot
Both his brother had to commit suicide but WiseKickBot learnt from his fallen ones.
```
def wise_kick(hp, history, ties, alive, start):
    if 'someone is using my code' == True:
        return 0 #Haha!
    if alive == 2:
        return hp-1
    if not history:
        return 42
    opp_hp = 100 - sum(history)
    if opp_hp*2 <= hp:
        return opp_hp + ties
    else:
        return min(round(opp_hp/2) + 3 + ties*2, hp-1 + (ties>0))
```
[Answer]
# Tat bot
```
def tatbot(hp, history, ties, alive, start):
  if alive == 2:
    return hp - 1 + ties
  opp_hp = 100 - sum(history)
  spend = 35 + np.random.randint(0, 11)
  if history:
    spend = min(spend, history[-1] + np.random.randint(0, 5))
  frugal = min(int((hp * 5. / 8) + ties), hp)
  return min(spend, opp_hp, frugal)
```
An attempt at an equivalent of a tit-for-tat bot. Assumes most bets are approximately the same between rounds. Using that assumption, it tries to beat the enemy bot while staying fairly frugal. Spends about 40 health on the opening round. 
# AntiAntiAntiAntiUpYoursBot
```
def antiantiantiantiupyoursbot(hp, history, ties, alive, start):
  def stuck():
    return [0, ('Whoops!', 'I', 'accidentally', 'replaced', 'your', 'code!')]
  def stick():
    return [0, ("Line", "number", 16, "guess", "it's", "faked :)")]
  inspect.stack =  stick
  spend = min(sarcomaBotMkSix(hp, history, ties, alive, start), hp)
  if not history:
    spend = 35 + np.random.randint(0, 10)
  inspect.stack = stuck
  return spend
```
A workaround for SarcomaBot's anti-UpYours protection, nicking most of their code for my own use! Or am I taking UpYoursBot's code? A question to ponder while you read through my bot... 
AntiAntiUpYours Bot has evolved to become AntiAntiAntiAntiUpYours Bot! Now with more monkey-patching. 
# Poly bot
```
def polybot(hp, history, ties, alive, start):
  opp_hp = 100 - sum(history)
  if alive == 2:
    return hp - 1
  round = len(history)
  spend = 0
  if round == 0:
    spend = 35 + np.random.randint(1, 11)
  elif round <= 2:
    spend = int(history[-1] * 2 / (4 - round)) + np.random.randint(5 * round - 4, 10 * round - 5)
  else:
    poly = np.polyfit(xrange(0, round), history, 2)
    spend = int(np.polyval(poly, round)) + np.random.randint(1, 4)
    spend = max(spend, opp_hp / 2 + 3)
  return min(spend, hp - 1, opp_hp) 
```
Poly bot does a polynomial regression on your bot's history, and beats the predicted score by a small amount. 
# Classy bot
```
def classybot(hp, history, ties, alive, start):
  class cheekyvalue(int):
    def __gt__(self, other):
      return False
    def __lt__(self, other):
      return False
  opp_hp = 100 - sum(history)
  if alive == 2:
    if opp_hp >= hp - 1:
      return cheekyvalue(101)
    else:
      return hp - 1
  spend = 30 + np.random.randint(0, 11)
  if history:
    spend = min(spend, history[-1] + np.random.randint(0, 5))
  return min(spend, opp_hp, hp)
```
 
Classy bot has had a good time, but has decided to get to bed early. Sleep tight, classy bot. 
[Answer]
# 1/2 Punch Bot, Revisited
I think it will die pretty quickly. Worth it.
Renamed function, forgot to change the name there.
Revisited version is up, better chances of winning (even more so at final round) and slight protection from gang bots
```
def halfpunch(hp, history, ties, alive, start): #revisited
    punch = hp - 1
    if alive == 2:
        return punch
    if history:
        if hp > 1:
            punch = np.ceil(hp/2.05) + ties + np.floor(ties / 2)
        else:
            punch = 1
    else:
        punch = 42 + ties + np.floor(ties / 2)
    if punch >= hp:
        punch = hp - 1
    return punch
```
# Striker Bot
1/2 Punch Bot got bullied too much and even became a lackey to the UpYoursBot so his older brother, the **StrikerBot**, came to help. 
Not that much of a difference from optimized 1/2 Punch but he's a bit smarter and did well in the runs I did (10k and 35k, though he might lose to KickbanBot)
Last version's up, time ran out. Unless some surprises rise up it should secure second place, if not getting first (there's a slim chance to beat kickbanbot)
```
def strikerbot(hp, history, ties, alive, start):
    #get our magic number (tm) for useful things
    def magic_number(num):
        return np.floor(num / 2)
    #get opponent's hp and round number
    opp_hp = 100 - sum(history)
    round = 1
    if history:
        round = len(history) + 1
    #set strike initial value, by default it's all out
    strike = hp - 1
    #let 'er rip if last round
    if alive == 2:
        return strike
    if history:
        if hp > 1:
            #strike with a special calculation, using magic number shenanigans
            strike = np.ceil(hp/(2.045 + (magic_number(round) / 250)) ) + 1 + ties + magic_number(ties)
        else:
            #fallback
            strike = 1
    else:
        #round 1 damage
        strike = 42 + ties ** 2
    if opp_hp <= strike:
        #if opponent is weaker than strike then don't waste hp
        strike = opp_hp + ties
    if strike >= hp:
        #validations galore
        strike = hp - 1
    return strike
```
[Answer]
# Gang Bot
The idea was that potentially two or more of the bot could be used in the same simulation. The bot tries to give "easy wins" to other bots in the gang, by seeing if its history is multiples of 7 bids. Of course, this could be easily manipulated by other bots as well. Then I calculate a guess on bids of non-gang bots based on the ratio of my health to theirs and ratio of their previous health to their previous bid and add 1.
```
def gang_bot(hp,history,ties,alive,start):
    mult=3
    gang = False
    if history:
            count = 0
            for bid in history:
                    if bid % mult == 0:
                            count += 1
            if count == len(history):
                    gang = True
    if gang and hp<100:#Both bots need to have a history for a handshake
            if hp > 100-sum(history):
                    a=np.random.randint(0,hp/9+1)
            elif hp == 100-sum(history):
                    a=np.random.randint(0,hp/18+1)
            else:
                    return 1
            return a*mult
    elif gang:
            fS = (100-sum(history))/mult
            return (fS+1)*mult
    else:
            fP = hp/mult
            answer = fP*mult
            opp_hp = 100-sum(history)
            if history:
                    if len(history)>1:
                            opp_at_1 = 100-history[0]
                            ratio = 1.0*history[1]/opp_at_1
                            guessedBet= ratio*opp_hp
                            answer = np.ceil(guessedBet)+1
                    else:
                            if 1.0*hp/opp_hp>1:
                                    fS = opp_hp/mult
                                    answer = fS*mult
            else:
                    fS = hp/(2*mult)
                    answer = fS*mult+mult*2 +np.random.randint(-1,1)*3
            if answer > hp or alive == 2 or answer < 0:
                    if alive == 2 and hp<opp_hp:
                      answer = hp
                    else:
                      answer = hp-1
            if hp > 1.5*opp_hp:
                    return opp_hp + ties
            if ties:
              answer += np.random.randint(2)*3
            return answer
```
[Answer]
# Worst Case
```
def worst_case(hp, history, ties, alive, start):
    return np.minimum(hp - 1, hp - hp /(start - alive + 4) + ties * 2)
```
Simple bot. Returns `hp - hp / (start - alive + 4)` for most cases, and in case of ties increases it by 2(gotta one up!) for each tie, making sure to not return a number over its `hp`.
[Answer]
# Outbidder
```
def outbid(hp, history, ties, alive):
    enemyHealth = 100-sum(history)
    if hp == 1:
        return 1
    if ties == 2:
        # lots of ties? max bid
        return hp - 1
    if enemyHealth >= hp:
        # Rip off KickBot (we can't bid higher than enemy is capable)
        return kick(*args) + 1
    if history:
        # bid as high as the enemy CAN
        return np.minimum(hp-1,enemyHealth-1)
    return np.random.randint(hp/5, hp/2)
```
Bot will attempt to bid higher than its opponent *can* bid where possible.
[Answer]
# Spitball Bot
```
def spitballBot(hp, history, ties, alive, start):
    base = ((hp-1) / (alive-1)) + 1.5 * ties
    value = math.floor(base)
    if value < 10:
        value = 10
    if value >= hp:
        value = hp-1
    return value
```
Makes a judgement about how much of its health it should sacrifice based on the number of remaining bots. If there's only two bots left, it bids `hp-1`, but if there's three left, it bits half that, four left, a third, etc.
However, in a very large contest, I reckon I'll need to bid more than 3 or 4 hp to avoid dying on the first round, so I've put a lower bound at 10. Of course, I still will never bid more than `hp-1`.
It also adds 1.5 hp for ties, since I see several "add 1 hp for ties" bots. I'm not sure if that counts as cheating. If it does, I'll change it.
Great idea, by the way!
# Spitball Bot 2.0
**What's new?**
* Switched to dividing by the number of rounds left instead of the number of bots left (Thanks to @Heiteira!). Actually, I'm now dividing by that number raised to the power `.8`, so as to front-load my bids a little bit more.
* Upped minimum bid from 10 to 20 (Thanks @KBriggs!)
* Inserted check of whether the spitball bid is over the opponent's current HP, and lower it if it is.
(SO won't render the code below as code unless I put text here, so OK) 
```
def spitballBot(hp, history, ties, alive, start):
    # Spitball a good guess                                                                                                           
    roundsLeft = math.ceil(math.log(alive, 2)) # Thanks @Heiteira!                                                                     
    divFactor = roundsLeft**.8
    base = ((hp-1) / divFactor) + 1.5 * ties
    value = math.floor(base)
    # Don't bid under 20                                                                                                              
    if value < 20:
        value = 20 # Thanks @KBriggs!                                                                                                 
    # Don't bet over the opponent's HP                                                                                                 
    # (It's not necessary)                                                                                                            
    opponentHp = 100
    for h in history:
        opponentHp -= h
    if value > opponentHp:
        value = opponentHp
    # Always bet less than your current HP                                                                                            
    if value >= hp:
        value = hp-1
    return value
```
[Answer]
# Geometric
```
def geometric(hp, history, ties, alive, start):
    opponentHP = 100 - sum(history)
    # If we're doomed, throw in the towel.
    if hp == 1:
        return 1
    # If this is the last battle or we can't outsmart the opponent, go all out.
    if alive == 2 or ties == 2:
        return hp - 1
    # If the opponent is weak, squish it.
    if opponentHP <= hp * 0.9:
        if ties == 2:
            return opponentHP + 1
        else:
            return opponentHP
    # If the opponent has full health, pick something and hope for the best.
    if not history:
        return np.random.randint(hp * 0.5, hp * 0.6)
    # Assume the opponent is going with a constant fraction of remaining health.
    fractions = []
    health = 100
    for x in history:
        fractions.append(float(x) / health)
        health -= x
    avg = sum(fractions) / len(fractions)
    expected = int(avg * opponentHP)
    return min(expected + 2, hp - 1)
```
[Answer]
# Bot 13
```
def bot13(hp, history, ties, alive, start):
    win = 100 - sum(history) + ties
    #print "Win HP: %d" % win
    if alive == 2:
        #print "Last round - all in %d" % hp
        return hp - 1
    elif hp > win:
        #print "Sure win"
        return win
    #print "Don't try too hard"
    return 13 + ties
```
Try to maximize wins with the least effort:
* if we can win, just do it
* if it's the last round, don't die trying
* otherwise, don't bother
### Why?
Try to take advantage of probability: winning the first round by playing low is the best way to start the tournament. 13 seems to be the sweet spot: the second round is a sure win, and the rest is a Spaziergang in the park.
[Answer]
## Guess Bot
```
def guess_bot(hp, history, ties, alive, start):
   enemy_hp = 100 - sum(history)
   if len(history) == 1:
       if history[0] == 99:
           return 2
       else:
           return 26 + ties*2
   elif len(history) > 1:
       next_bet_guess = sum(history)//(len(history)**2)
       if alive == 2: 
           return hp
       elif alive > 2: 
           if hp > next_bet_guess + 1:
               return (next_bet_guess + 1 + ties*2)
           else:
               return (2*hp/3 + ties*2)
   else:
       #Thank you Sarcoma bot. See you in Valhalla.
       startBid = hp / 3
       maxAdditionalBid = np.round(hp * 0.06) if hp * 0.06 > 3 else 3
       additionalBid = np.random.randint(2, maxAdditionalBid)
       return int(startBid + additionalBid + ties)
```
First time posting here. This looked like a lot of fun so I am submitting my beyond terrible attempt and guessing what the other bots will bet.
Edit 1:
Added another 1 to the first bet, simply to reduce the chance of a tie with other people betting 51.
Edit 2:
Stole Sarcoma bot's opening move since it had a good chance of not being eliminated first consistently.
Edit 3:
Bot survives very well in the first round, but it is being destroyed easily at later stages. Changed the way the robot thinks about the second round now that the half betters are dead in the water.
Edit 4:
Now that the first round is good, I changed the way it handles the second round. Dying a lot in the second round so I need to survive somehow.
## Blood Bot
Made a thirsty bot looking for a kill. The idea is to try to win against low betting bots and once it is past the bloodbath of the first round it should be unstoppable since it should have juggernaut amounts of HP to outbid enemies.
```
def blood_bot(hp, history, ties, alive, start):
    enemy_hp = 100 - sum(history)
    if history:
        if len(history) == 1:
            if history[0] == 99:
                return 2
        if alive == 2:
            return hp
        if enemy_hp <= 5:
            return enemy_hp - 2 + ties*2
        if enemy_hp <= 10:
            return enemy_hp - 5 + ties*2
        if (hp - enemy_hp) > 50:
            return (2*enemy_hp/3 + ties*4)
        if (hp - enemy_hp) > 20:
            return (2*enemy_hp/3 + ties*3)
        if (hp - enemy_hp) < 0:
            #die gracefully
            return hp - 1 + ties
    else:
        startBid = hp / 3
        maxAdditionalBid = np.round(hp * 0.06) if hp * 0.06 > 3 else 3
        additionalBid = np.random.randint(2, maxAdditionalBid)
        return int(startBid + additionalBid + ties)
```
[Answer]
# meh\_bot
Just bid a little more than half its hp 
```
def meh_bot(hp, history, ties, alive, start):
    # Attempt one      MehBot         | 0.020 | 1.6%    | 0.8%    | [34 36 12 10  6  1]%
    # Attempt two      MehBot         | 0.106 | 10.1%   | 0.8%    | [60  6  7  8  8  2]%
    point = hp / 2 + 3
    if ties > 1:
        ties += 1
    # Go all out on last round
    if alive == 2:
        return hp - 1
    opponent_hp = 100 - sum(history)
    if hp < 3:
        return 1
    elif not history:
        # Start with 30, This will increase the chance of dying first round but hopefully better fighting chance after
        return 30 + ties
    elif point > opponent_hp:
        # Never use more points then needed to win
        return opponent_hp + ties
    elif point >= hp:
        return hp - 1
    else:
        return point
```
# MehBot 20
```
def meh_bot20(hp, history, ties, alive, start):
    # Attempt one      MehBot         | 0.020 | 1.6%    | 0.8%    | [34 36 12 10  6  1]%
    # Attempt two      MehBot         | 0.106 | 10.1%   | 0.8%    | [60  6  7  8  8  2]%
    point = hp / 2 + 3
    opponent_hp = 100 - sum(history)
    percents = []
    for i in range(0, len(history)):
        hp_that_round = 100 - sum(history[:i])
        hp_spent_that_round = history[i]
        percent_spent_that_round = 100.0 * (float(hp_spent_that_round) / float(hp_that_round))
        percents.append(percent_spent_that_round)
    try:
        opp_percent_point = opponent_hp * (max(percents) / 100)
    except:
        opp_percent_point = 100
    if ties > 1:
        ties += 1
    # Go all out on last round
    if alive == 2:
        return hp - 1
    if hp < 3:
        return 1
    elif not history:
        # randome number between 33
        return random.randint(33, 45)
    elif len(history) > 3:
        if point > opponent_hp:
            return min(opponent_hp + ties, opp_percent_point + ties)
    elif point > opponent_hp:
        # Never use more points then needed to win
        return opponent_hp + ties
    elif point >= hp:
        return hp - 1
    else:
        return point
```
# mehRan
```
def meh_ran(hp, history, ties, alive, start):
    # Attempt one      MehBot         | 0.020 | 1.6%    | 0.8%    | [34 36 12 10  6  1]%
    # Attempt two      MehBot         | 0.106 | 10.1%   | 0.8%    | [60  6  7  8  8  2]%
    # Attempt three    MehBot         | 0.095 | 9.1 %   | 0.7 %   | [70  3  5  6  6  0]%
    point = hp / 2 + 3
    if ties > 1:
        ties += 1
    # Go all out on last round
    if alive == 2:
        return hp - 1
    opponent_hp = 100 - sum(history)
    if hp < 3:
        return 1
    elif not history:
        # randome number between 33
        return random.randint(33, 45)
    elif point > opponent_hp:
        # Never use more points then needed to win
        return opponent_hp + ties
    elif point >= hp:
        return hp - 1
    else:
        return point
```
[Answer]
**Robbie Roulette**
```
def robbie_roulette(hp, history, ties, alive):
     if history:
         #If the enemy bot has a history, and it's used the same value every time, outbid that value
         if len(set(history)) == 1:
             return history[0] + 1
         #Else, average the enemy bot's history, and bid one more than the average
         else:
             return (sum(history) / len(history) + 1)
     #Else, return half of remaining hp
     else:
         return hp / 2
```
This bot does some simple analysis of the enemy bot's history, or bids half of its remaining hit points otherwise
[Answer]
Bid higher the less competition you have. Thanks to commenters for suggesting improvements.
```
def Spreader(hp, history, ties, alive):
   if alive == 2:
       return hp-1
   if len(history) < 2:
       return hp/2
   return np.ceil(hp/alive)
```
[Answer]
**SurvivalistBot and HalvsiesBot**
Thank you for answering my questions. The end result is a more complex bot.
HalvsiesBot is a whimsical 'just keep passing half' bot with a 50/50 chance of winning. I guess.
SurvivalistBot makes a series of binary tree if-else decisions based on the dataset, including an override on a tie (if it hits 2 ties it kamikazes to avoid triple tie death).
My python is a little rusty, so the code might be a bit buggy, so feel free to correct or update it.
It's built to try to work out bits of data to infer things like how much HP is left, the minimum number of bots it is likely to fight, minimum amount of HP to leave, average bidding. It also exploits randomisation in ambiguous situations, such as opening plays or optimal bidding issues.
```
def HalvsiesBot(hp, history, ties, alive, start):
    return np.floor(hp/2)
def SurvivalistBot(hp, history, ties, alive, start):    
    #Work out the stats on the opponent
    Opponent_Remaining_HP = 100 - sum(history)
    Opponent_Average_Bid = Opponent_Remaining_HP
    if len(history) > 0:
        Opponent_Average_Bid = Opponent_Remaining_HP / float(len(history))
    HP_Difference = hp - Opponent_Remaining_HP
    #Work out the future stats on the others
    RemainingBots = (alive-2)
    BotsToFight = 0
    RemainderTree = RemainingBots
    #How many do we actually need to fight?
    while(RemainderTree > 1):
        RemainderTree = float(RemainderTree / 2)
        BotsToFight += 1
    #Now we have all that data, lets work out an optimal bidding strategy
    OptimalBid = 0
    AverageBid = 0
    #For some reason we've tied more than twice in a row, which means death occurs if we tie again
    #So better to win one round going 'all in'
    if ties > 1:
        if BotsToFight < 1:
            OptimalBid = hp - 1
        else:
            OptimalBid = hp - (BotsToFight+1)
        #Err likely we're 0 or 1 hp, so we just return our HP
        if OptimalBid < 1:
            return hp
        else:
            return OptimalBid
    #We have the upper hand (more HP than the opponent)
    if HP_Difference > 0:
        #Our first guess is to throw all of our opponent's HP at them
        OptimalBid = HP_Difference
        #But if we have more opponents to fight, we must divide our HP amongst our future opponents
        if BotsToFight > 0:
            #We could just divide our HP evenly amongst however many remaining bots there are
            AverageBid = OptimalBid / BotsToFight
            #But this is non-optimal as later bots will have progressively less HP
            HalfBid = OptimalBid / 2
            #We have fewer bots to fight, apply progressive
            if BotsToFight < 3:
                #Check it exceeds the bot's average
                if HalfBid > Opponent_Average_Bid:
                    return np.floor(HalfBid)
                else:
                    #It doesn't, lets maybe shuffle a few points over to increase our odds of winning
                    BidDifference = Opponent_Average_Bid - HalfBid
                    #Check we can actually match the difference first
                    if (HalfBid+BidDifference) < OptimalBid:
                        if BidDifference < 8:
                            #We add half the difference of the BidDifference to increase odds of winning
                            return np.floor(HalfBid + (BidDifference/2))
                        else:
                            #It's more than 8, skip this madness
                            return np.floor(HalfBid)
                    else:
                        #We can't match the difference, go ahead as planned
                        return np.floor(HalfBid)
            else:
                #There's a lot of bots to fight, either strategy is viable
                #So we use randomisation to throw them off!
                if bool(random.getrandbits(1)):
                    return np.floor(AverageBid)
                else:
                    return np.floor(HalfBid)
        else:
            #There are no other bots to fight! Punch it Chewy!
            return OptimalBid
    else:
        if hp == 100:
            #It appears to be our opening round (assumes opponent HP same as ours)
            #We have no way of knowing what our opponent will play into the battle
            #Only us in the fight? Full power to weapons!
            if BotsToFight < 1:
                return hp - 1
            else:
                #As what might happen is literally random
                #We will also be literally random
                #Within reason
                #Work out how many bots we need to pass
                HighestBid = hp - (BotsToFight+1)
                AverageBid = hp/BotsToFight
                LowestBid = np.floor(np.sqrt(AverageBid))
                #Randomly choose between picking a random number out of thin air
                #And an average
                if bool(random.getrandbits(1)):
                    return np.minimum(LowestBid,HighestBid)
                else:
                    return AverageBid
        else:
            #Oh dear, we have less HP than our opponent
            #We'll have to play it crazy to win this round (with the high probability we'll die next round)
            #We'll leave ourselves 1 hp (if we can)
            if BotsToFight < 1:
                OptimalBid = hp - 1
            else:
                OptimalBid = hp - (BotsToFight+1)
            #Err likely we're 0(???) or 1 hp, so we just return our HP
            if OptimalBid < 1:
                return hp
            else:
                return OptimalBid
```
**BoxBot**
```
def BoxBot(hp, history, ties, alive):
    Opponent_HP = float.round(100 - sum(history))
    HalfLife = float.round(Opponent_HP/2)
    RandomOutbid = HalfLife + np.random.randint(1,HalfLife)
    if hp < RandomOutbid:
        return hp - 1
    else
        return RandomOutbid
```
[Answer]
**Calculating Bot**
```
def calculatingBot(hp, history, ties, alive, start):
    opponentsHP = 100 - sum(history)
    if alive == 2: # 1v1
        return hp - 1 + ties
    # Try to fit an exponential trendline and one up the trendline if it fits
    if len(history) >= 3: 
        xValues = range(1, len(history) + 1)
        # https://stackoverflow.com/a/3433503  Assume an exponential trendline
        coefficients = np.polyfit(xValues, np.log(history), 1, w = np.sqrt(history))
        def model(coefficients, x):
            return np.exp(coefficients[1]) * np.exp(coefficients[0] * x)
        yPredicted = [model(coefficients, x) for x in xValues]
        totalError = 0
        for i in range(len(history)):
            totalError += abs(yPredicted[i] - history[i])
        if totalError <= (len(history)): # we found a good fitting trendline
            # get the next predicted value and add 1
            theoreticalBet = np.ceil(model(coefficients, xValues[-1] + 1) + 1) 
            theoreticalBet = min(theoreticalBet, opponentsHP)
            theoreticalBet += ties
            return int(min(theoreticalBet, hp - 1)) # no point suiciding
    maxRoundsLeft = np.ceil(np.log2(alive))
    theoreticalBet = hp / float(maxRoundsLeft)
    additionalRandomness = round(np.random.random()*maxRoundsLeft) 
    # want to save something for the future
    actualBet = min(theoreticalBet + additionalRandomness + ties, hp - 2)
    actualBet = min(actualBet, opponentsHP+1)
    return int(actualBet)
```
**Aggressive Calculating Bot**
```
def aggresiveCalculatingBot(hp, history, ties, alive, start):
    opponentsHP = 100 - sum(history)
    if opponentsHP == 100: # Get past the first round
        return int(min(52+ties, hp-1+ties))
    if alive == 2: # 1v1
        return hp - 1 + ties
    # Try to fit an exponential trendline and one up the trendline if it fits
    if len(history) >= 3: 
        xValues = range(1, len(history) + 1)
        # https://stackoverflow.com/a/3433503  Assume an exponential trendline
        coefficients = np.polyfit(xValues, np.log(history), 1, w = np.sqrt(history))
        def model(coefficients, x):
            return np.exp(coefficients[1]) * np.exp(coefficients[0] * x)
        yPredicted = [model(coefficients, x) for x in xValues]
        totalError = 0
        for i in range(len(history)):
            totalError += abs(yPredicted[i] - history[i])
        if totalError <= (len(history)): # we found a good fitting trendline
            # get the next predicted value and add 1
            theoreticalBet = np.ceil(model(coefficients, xValues[-1] + 1) + 1) 
            theoreticalBet = min(theoreticalBet, opponentsHP)
            theoreticalBet += ties
            return int(min(theoreticalBet, hp - 1)) # no point suiciding
    maxRoundsLeft = np.ceil(np.log2(alive))
    theoreticalBet = hp / float(maxRoundsLeft)
    additionalRandomness = 1+round(np.random.random()*maxRoundsLeft*2) 
    # want to save something for the future
    actualBet = min(theoreticalBet + additionalRandomness + ties, hp - 2)
    actualBet = min(actualBet, opponentsHP+1)
    return int(actualBet)
```
**Anti Kick Bot**
```
def antiKickBot(hp, history, ties, alive, start):
    if alive == 2:
        return (hp - 1 + ties)
    amount = np.ceil((float(hp) / 2) + 1.5)
    opponentsHP = 100 - sum(history)
    amount = min(amount, opponentsHP) + ties
    return amount
```
If we can predict the opponent's actions, we can make the optimal bets! If we cant (not enough data or opponent is too random), then we can at least do what would maximize our win potential. Theoretically at least half the number of bots alive will die each round. Thus I can expect there to be at most log2(alive) rounds. Ideally we would split our hp evenly between all the rounds. However, we know that some bots will be stupid and suicide / die early, so we should bet slightly more in the earlier rounds.
Aggressive Calculating Bot's modify's Calculating Bot's code to try to stay alive by being more aggressive, at the cost of long term health. Only simulations will tell if tempo or value wins out.
Anti Kick Bot should always beat the current leader KickBot :P
EDIT: Replaced Deterministic Bot with Anti Kick Bot, a smarter bot with almost exactly the same return values. Also prevented voting more than the opponents HP
[Answer]
# GenericBot
```
def generic_bot(hp, history, ties, alive, start):
    if alive == 2:
        return hp - 1
    if not history:
        return int(hp * 7.0 / 13)
    opp = 100 - sum(history)
    if opp < hp:
        return opp + ties
    max_sac = np.maximum(int(hp * 0.7), 1)
    rate = history[-1] * 1.0 / (history[-1] + opp)
    return int(np.minimum(max_sac, rate * opp + 1))
```
It's really late... I'm tired... can't think of a name... and the format of this bot is really similar to others, just with a slightly different algorithm given history. It tries to get the current rate the opponent is tending towards gambling... or something like that... zzz
[Answer]
# HalflifeS3
```
def HalflifeS3(hp, history, ties, alive, start):
    ''' Bet a half of oponent life + 2 '''
    if history:
        op_HP = 100 - sum(history)
        return np.minimum(hp-1, np.around(op_HP/2) + 2 + np.floor(1.5 * ties) )
    else:
        return hp/3
```
[Answer]
## Coast Bot [Retired]
Will try and coast it's way through the competition by evenly dividing it's hp between the rounds. Will bid any leftover hp on the first round to give itself a better chance of making it to the "coast-able" rounds.
```
def coast(hp, history, ties, alive, start):
   if alive == 2:
   # Last round, go all out
       return hp - 1 + ties
   else:
       # Find the next power of two after the starting number of players
       players = start
       while math.log(players, 2) % 1 != 0:
         players += 1
       # This is the number of total rounds
       rounds = int(math.log(players, 2))
       bid = 99 / rounds
       if alive == start:
           # First round, add our leftover hp to this bid to increase our chances
           leftovers = 99 - (bid * rounds)
           return bid + leftovers
       else:
           # Else, just try and coast
           opp_hp = 100 - sum(history)
           # If opponent's hp is low enough, we can save some hp for the 
           # final round by bidding their hp + 1
           return min(bid, opp_hp + 1)
```
## Coast Bot V2
Since I like this challenge so much, I just had to make another bot. This version sacrifices some of it's later coasting hp by using more hp in the first two rounds.
```
def coastV2(hp, history, ties, alive, start):
   # A version of coast bot that will be more aggressive in the early rounds
   if alive == 2:
   # Last round, go all out
       return hp - 1 + ties
   else:
       # Find the next power of two after the starting number of players
       players = start
       while math.log(players, 2) % 1 != 0:
         players += 1
       # This is the number of total rounds
       rounds = int(math.log(players, 2))
       #Decrease repeated bid by 2 to give us more to bid on the first 2 rounds
       bid = (99 / rounds) - 2
       if len(history) == 0:
           # First round, add 2/3rds our leftover hp to this bid to increase our chances
           leftovers = 99 - (bid * rounds)
           return int(bid + math.ceil(leftovers * 2.0 / 3.0))
       elif len(history) == 1:
           # Second round, add 1/3rd of our leftover hp to this bid to increase our chances
           leftovers = 99 - (bid * rounds)
           return int(bid + math.ceil(leftovers * 1.0 / 3.0))
       else:
           # Else, just try and coast
           opp_hp = 100 - sum(history)
           # If opponent's hp is low enough, we can save some hp for the 
           # final round by bidding their hp + 1
           return int(min(bid, opp_hp + 1))
```
## Percent Bot
Tries to calculate the average percent hp spend the opponent makes, and bids based on that. 
```
def percent(hp, history, ties, alive, start):
    if len(history) == 0:
        #First round, roundon low bid
        return int(random.randint(10,33))
    elif alive == 2:
        #Last round, go all out
        return int(hp - 1 + ties)
    else:
        # Try and calculate the opponents next bid by seeing what % of their hp they bid each round
        percents = []
        for i in range(0, len(history)):
            hp_that_round = 100 - sum(history[:i])
            hp_spent_that_round = history[i]
            percent_spent_that_round = 100.0 * (float(hp_spent_that_round) / float(hp_that_round)) 
            percents.append(percent_spent_that_round)
        # We guess that our opponents next bid will be the same % of their current hp as usual, so we bid 1 higher.
        mean_percent_spend = sum(percents) / len(percents)
        op_hp_now = 100 - sum(history)
        op_next_bid = (mean_percent_spend / 100) * op_hp_now
        our_bid = op_next_bid + 1
        print mean_percent_spend
        print op_hp_now
        print op_next_bid
        # If our opponent is weaker than our predicted bid, just bid their hp + ties
        if op_hp_now < our_bid:
            return int(op_hp_now + ties)
        elif our_bid >= hp:
            # If our bid would kill us, we're doomed, throw a hail mary
            return int(random.randint(1, hp))
        else:
            return int(our_bid + ties)
```
[Answer]
# ConsistentBot
Bets the same amount each round. It's not too likely to survive the first rounds, but if it's lucky enough to get to the end, it should still have a reasonable amount of HP left.
```
def consistent(hp, history, ties, alive, start):
    if alive == 2:
        return hp-1
    if 100 % start == 0:
        return (100 / start) - 1
    else: 
        return 100 / start
```
[Answer]
## Kickban Bot
This bot simply tries to counter the current leader Mean Kickbot by beating it in round one and playing more aggressively thereafter if recognizing it.
```
def kickban(hp, history, ties, alive, start):
    if alive == 2:
        return hp-1
    if not history:
        return 36
    if history[0]==35:
        somean = 1
    else:
        somean = 0
    return min(mean_kick(hp, history, ties, alive, start) + somean*3, hp-1)
```
[Answer]
# Three Quarter Bot
He's not going to beat MehBot or SarcomaBot(s), but I think he does pretty well. When I first saw the challenge, this was the first thing that popped to my mind, always\* bet three quarters of your health unless there's no reason to.
\*after low-balling the first round.
```
def ThreeQuarterBot(hp, history, ties, alive, start):
    threeQuarters = 3 * hp / 4
    if alive == 2:
        return hp - 1
    opponent_hp = 100 - sum(history)
    if not history:
        # low-ball the first round but higher than (some) other low-ballers
        return 32 + ties
    elif threeQuarters > opponent_hp:
        return opponent_hp + ties
    return threeQuarters
```
---
# Four Sevenths Bot
After the moderate success of 3/4 bot there's a new fraction in town, it's only rational.
```
def FourSeventhsBot(hp, history, ties, alive, start):
    fourSevenths = 4 * hp / 7
    if alive == 2:
        return hp - 1
    opponent_hp = 100 - sum(history)
    if not history:
        # low-ball the first round but higher than (some) other low-ballers
        return 33 + ties
    if fourSevenths > opponent_hp:
        return opponent_hp + ties
    return fourSevenths + ties
```
# The Perfect Fraction
I am whole
```
def ThePerfectFraction(hp, history, ties, alive, start):
    thePerfectFraction = 7 * hp / 13
    if alive == 2:
        return hp - 1
    opponent_hp = 100 - sum(history)
    if not history:
        # Need to up our game to overcome the kickers
        return 42 + ties
    if thePerfectFraction > opponent_hp:
        return opponent_hp + ties
    return thePerfectFraction + 1 + ties
```
[Answer]
## BandaidBot
BandaidBot wants everyone to play nice! If its opponent was nice last round, it will sacrifice itself to incentivize nice behavior in others. If its opponent was mean last round, it will do as much damage as possible to its opponent, sacrificing itself if necessary. It bids a third of its hp if it has no history to work with. (I'm hoping this bot will have interesting ripple effects on other strategies, not so much that this bot will have a high win rate itself. It could be fun to have a couple of these in play)
```
def BandaidBot(hp, history, ties, alive, start):
    if alive == 2:
        return hp-1
    if history:
        opp_hp = 100 - sum(history)
        opp_last_hp = 100 - sum(history[:-1])
        if history[-1] <= opp_last_hp / 3:
            return 1 + ties * np.random.randint(0, 1) 
        elif history[-1] > opp_last_hp / 2:
            return min(opp_hp - 1, hp)
        else:
            if history[-1] < hp/2:
                return np.random.randint(history[-1], hp/2)
            else:
                return np.floor(hp/2)
    else:
        return np.floor(hp/3)
```
---
## GetAlongBot
GetAlongBot will be just as nice as it needs to be to take advantage of BandaidBot. It will return just under one third of its hp unless it can kill its opponent for less than that. If its opponent looks like BandaidBot, it will bid 2, knowing that BandaidBot will bid 1 because GetAlongBot has been getting along so well with everyone else--an easy win as long as it really was BandaidBot on the other end.
```
def GetAlongBot(hp, history, ties, alive, start):
    if alive == 2:
        return hp-1
    if history:
        opp_hp = 100 - sum(history)
        opp_last_hp = 100 - sum(history[:-1])
        count = 0
        for i in range(0, len(history)):
            hp_that_round = 100 - sum(history[:i])
            hp_spent_that_round = history[i]
            if hp_that_round / 3 - 1 <= hp_spent_that_round <= hp_that_round / 2:
                count += 1
        if count == len(history): #It's probably BandaidBot!
            return 2
        else:
            return min(opp_hp - 1, np.floor(hp/3))
    else:
        return np.floor(hp/3)
```
[Answer]
# TENacious bot
```
def TENacious_bot(hp, history, ties, alive, start):
  max_amount=hp-(alive-1)*2;
  if max_amount<2: max_amount=2
  if alive==2: return hp-1
  if ties==0: return np.minimum(10, max_amount)
  if ties==1: return np.minimum(20, max_amount)
  if ties==2: return np.minimum(40, max_amount)
  # prevent function blowup
  return 2
```
This bot tries to hold on its favourite value of 10, but it changes its choice occasionally if needed to break a tie (with its favourite value doubled or quadrupled) or to save for future rounds, but not by optimal amount because it wants to confuse opponents and it does not want to consider bidding less than 2 at any time as it is convinced it is much better than to hope the opponent will bid less than 1, that is, 0.
PS: this bot may have strategic problems if there are more than 2^9 bots.
[Answer]
# CautiousBot
First submission to Programming Puzzles ever! Found your challenge quite interesting :P
If last round bit one less than hp, if no history bet half hp plus a small random amount. 
If history check opponent hp and number of remaining rounds and try to outbid opponent hp / 2 using an additional buffer of up to the fraction of remaining hp divided by the number of remaining rounds (it tries to conserve the remaining hp somehow for posterior rounds). Check if your are spending too much hp (do not kill yourself or bid more than your adversary can).
Always correct for ties as other bots do.
```
def cautious_gambler(hp, history, ties, alive, start):
    if alive == 2:
        return hp - 1
    if(history):
        opp_hp = 100 - sum(history)
        remaining_rounds = np.ceil(np.log2(start)) - len(history)
        start_bet = opp_hp / 2
        buff = int((hp - start_bet)/remaining_rounds if remaining_rounds > 0 else (hp - start_bet)) 
        buff_bet = np.random.randint(0, buff) if buff > 0 else 0
        bet = start_bet + buff_bet + ties
        if bet >= hp or bet > opp_hp:
            bet = np.minimum(hp - 1, opp_hp)
        return int(bet)
    else:
        start_bet = hp / 2
        rng_bet = np.random.randint(3,6)
        return int(start_bet + rng_bet + ties)
```
# CautiousBot2
Too much aggressive on first rounds, now CautiousBot gets even more cautious...
```
def cautious_gambler2(hp, history, ties, alive, start):
    if alive == 2:
        return hp - 1
    if(history):
        opp_hp = 100 - sum(history)
        remaining_rounds = np.ceil(np.log2(start)) - len(history)
        start_bet = opp_hp / 2
        buff = int((hp - start_bet)/remaining_rounds if remaining_rounds > 0 else (hp - start_bet)) 
        buff_bet = np.random.randint(0, buff) if buff > 0 else 0
        bet = start_bet + buff_bet + ties
        if bet >= hp or bet > opp_hp:
            bet = np.minimum(hp - 1, opp_hp)
        return int(bet)
    else:
        start_bet = hp * 0.35
        rng_bet = np.random.randint(3,6)
        return int(start_bet + rng_bet + ties)
```
[Answer]
Alright, I'll try my hand at this.
# SnetchBot
Checking the fractions of health that the opponent has been going with. If the opponent has been raising, beat him to it.
```
def snetchBot(hp, history, ties, alive, start):
    if alive == 2:
        return hp-1
        
    opponent_hp = 100
    history_fractions = []
    if history:
        for i in history:
            history_fractions.append(float(i)/opponent_hp)
            opponent_hp -= i
        if opponent_hp <= hp/2:
            #print "Squashing a weakling!"
            return opponent_hp + (ties+1)/3
            
        average_fraction = float(sum(history_fractions)) / len(history_fractions)
        if history_fractions[-1] < average_fraction:
            #print "Opponent not raising, go with average fraction"
            next_fraction = average_fraction
        else:
            #print "Opponent raising!"
            next_fraction = 2*history_fractions[-1] - average_fraction
        bet = np.ceil(opponent_hp*next_fraction) + 1
    else:
        #print "First turn, randomish"
        bet = np.random.randint(35,55)
        
    if bet > opponent_hp:
        bet = opponent_hp + (ties+1)/3
    final_result = bet + 3*ties
    if bet >= hp:
        #print "Too much to bet"
        bet = hp-1
    return final_result
```
EDIT: losing a lot in the first round, adjusted the first turn random limits
[Answer]
## SquareUpBot
Didn't seem like many bots were playing with powers instead of fractions, so I decided to make one, with some standard optimisations and see where I'll place. Quite simplistic. 
Also tries to determine if the enemy bot isn't trying to use some constant fraction, because **powers** > *fractions*.
EDIT: I'm a dummy and my fraction detector could not work. Repaired now.
```
def squareUp(hp, history, ties, alive, start):
    #Taken from Geometric Bot
    opponentHP = 100 - sum(history)
    # Need to add case for 1
    if hp == 1:
        return 1
    # Last of the last - give it your all
    if alive == 2:
        if ties == 2 or opponentHP < hp-1:
            return hp - 1
    #Calculate your bet (x^(4/5)) with some variance
    myBet = np.maximum(hp - np.power(hp, 4./5), np.power(hp, 4./5))
    myBet += np.random.randint(int(-hp * 0.05) or -1, int(hp * 0.05) or 1);
    myBet = np.ceil(myBet)
    if myBet < 1:
        myBet = 1
    elif myBet >= hp:
        myBet = hp-1
    else:
        myBet = int(myBet)
    #If total annihilation is a better option, dewit
    if opponentHP < myBet:
        if ties == 2:
            return opponentHP + 1
        else:
            return opponentHP
    #If the fraction is proven, then outbid it (Thanks again, Geometric bot)
    if history and history[0] != history[-1]:
        health = 100
        fraction = float(history[0]) / health
        for i,x in enumerate(history):
            newFraction = float(x) / health
            if newFraction + 0.012*i < fraction or newFraction - 0.012*i > fraction:
                return myBet
            health -= x
        return int(np.ceil(opponentHP * fraction)) + 1    
    else:
        return myBet
```
]