# Question
Title: Robot Roulette: High stakes robot gambling
**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.

# Answer
> 12 votes
# 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
> 17 votes
# 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
> 15 votes
# 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
> 14 votes
## 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
> 11 votes
# 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
> 8 votes
# 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
> 8 votes
# 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
> 7 votes
# 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
> 6 votes
# 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
> 6 votes
# 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
> 6 votes
# 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
> 5 votes
## 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
> 5 votes
# 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
> 5 votes
# 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
> 5 votes
# 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
> 4 votes
**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
> 4 votes
**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
> 4 votes
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
> 4 votes
**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
> 4 votes
# 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
> 4 votes
## 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
> 4 votes
# 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
> 4 votes
# 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
> 4 votes
# 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
> 4 votes
## 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
> 4 votes
## 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
> 3 votes
# 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
> 3 votes
# 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
> 3 votes
# 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)

```

# Answer
> 3 votes
## 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

```

# Answer
> 3 votes
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
> 3 votes
# 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

# Answer
> 3 votes
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

```

# Answer
> 2 votes
# 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)

```

# Answer
> 2 votes
**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.

# Answer
> 2 votes
## 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.

# Answer
> 1 votes
# 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.

---
Tags: king-of-the-hill, python
---