[Question]
  [
# Final Result
The competition is over. Congratulations to `hard_coded`!
Some interesting facts:
* In 31600 out of 40920 auctions (77.2%), the winner of the first round won the most rounds in that auction.
* If example bots are included in the competition, the top nine places won't change except that `AverageMine` and `heurist` will swap their positions.
* Top 10 results in an auction:
```
[2, 2, 3, 3] 16637
[0, 3, 3, 4] 7186
[1, 3, 3, 3] 6217
[1, 2, 3, 4] 4561
[0, 1, 4, 5] 1148
[0, 2, 4, 4] 1111
[2, 2, 2, 4] 765
[0, 2, 3, 5] 593
[1, 1, 4, 4] 471
[0, 0, 5, 5] 462
```
* Tie count (number of auctions that the i-th round had no winner): `[719, 126, 25, 36, 15, 58, 10, 7, 19, 38]`.
* Average winning bid of the i-th round: `[449.4, 855.6, 1100.8, 1166.8, 1290.6, 1386.3, 1500.2, 1526.5, 1639.3, 3227.1]`.
**Scoreboard**
```
Bot count: 33
hard_coded            Score: 16141  Total: 20075170
eenie_meanie_more     Score: 15633  Total: 18513346
minus_one             Score: 15288  Total: 19862540
AverageMine           Score: 15287  Total: 19389331
heurist               Score: 15270  Total: 19442892
blacklist_mod         Score: 15199  Total: 19572326
Swapper               Score: 15155  Total: 19730832
Almost_All_In         Score: 15001  Total: 19731428
HighHorse             Score: 14976  Total: 19740760
bid_higher            Score: 14950  Total: 18545549
Graylist              Score: 14936  Total: 17823051
above_average         Score: 14936  Total: 19712477
below_average         Score: 14813  Total: 19819816
Wingman_1             Score: 14456  Total: 18480040
wingman_2             Score: 14047  Total: 18482699
simple_bot            Score: 13855  Total: 20935527
I_Dont_Even           Score: 13505  Total: 20062500
AntiMaxer             Score: 13260  Total: 16528523
Showoff               Score: 13208  Total: 20941233
average_joe           Score: 13066  Total: 18712157
BeatTheWinner         Score: 12991  Total: 15859037
escalating            Score: 12914  Total: 18832696
one_upper             Score: 12618  Total: 18613875
half_in               Score: 12605  Total: 19592760
distributer           Score: 12581  Total: 18680641
copycat_or_sad        Score: 11573  Total: 19026290
slow_starter          Score: 11132  Total: 20458100
meanie                Score: 10559  Total: 12185779
FiveFiveFive          Score: 7110   Total: 24144915
patient_bot           Score: 7088   Total: 22967773
forgetful_bot         Score: 2943   Total: 1471500
bob_hater             Score: 650    Total: 1300
one_dollar_bob        Score: 401    Total: 401
```
---
In this game, we will simulate a sealed-bid auction.
Each auction is a 4-player game, consists of 10 rounds. Initially, players have no money. At the start of each round, each player will get $500, and then make their own bids. The bid can be any non-negative integer less or equal than the amount they have. Usually, one who bid the highest win the round. However, to make things more interesting, if several players bid the same price, their bid won't be taken into account (thus can't win the round). For example, if four players bid 400 400 300 200, the one bids 300 wins; if they bid 400 400 300 300, no one wins. The winner should pay what they bid.
Since it is a "sealed-bid" auction, the only information player will know about the bidding is the winner and how much they paid when next round starts (so player can know how much everyone has).
---
# Scoring
One auction will be held for every possible 4-player combination. That is, if there are N bots in total, there will be NC4 auction. The bot which wins the most rounds will be the final winner. In the case that there's a tie, the bot which paid the least in total will win. If there's still a tie, in the same way as the bidding, those ties will be removed.
---
# Coding
You should implement a **Python 3** class with a member function `play_round` (and `__init__` or others if you need). `play_round` should take 3 arguments (including self). The second and third argument will be, in order: the id of winner of the previous round, followed by how much they paid. If no one wins or it is the first round, they will both be -1. Your id will always be 0, and id 1–3 will be other players in an order only determined by the position on this post.
---
# Additional rules
**1. Deterministic:**
The behavior of your function should depend only on the input arguments within an auction. That is, you can't access files, time, global variables or anything that will store states **between different auctions or bots**. If you want to use a pseudorandom generator, it's better to write it by yourself (to prevent affecting others' programs like `random` in Python lib), and make sure you have it reset with a fixed seed in `__init__` or the first round.
**2. Three Bots per Person:**
You're allowed to submit at most 3 bots, so you can develop a strategy to make your bots "cooperate" in some way.
**3. Not Too Slow:**
Since there will be many auctions, make sure that your bots won't run too slow. Your bots should be able to finish at least 1,000 auctions in a second.
---
# Controller
Here's the controller I'm using. All bots will be imported and added to `bot_list` in the order on this post.
```
# from some_bots import some_bots
bot_list = [
    #one_bot, another_bot, 
]
import hashlib
def decide_order(ls):
    hash = int(hashlib.sha1(str(ls).encode()).hexdigest(), 16) % 24
    nls = []
    for i in range(4, 0, -1):
        nls.append(ls[hash % i])
        del ls[hash % i]
        hash //= i
    return nls
N = len(bot_list)
score = [0] * N
total = [0] * N
def auction(ls):
    global score, total
    pl = decide_order(sorted(ls))
    bots = [bot_list[i]() for i in pl]
    dollar = [0] * 4
    prev_win, prev_bid = -1, -1
    for rounds in range(10):
        bids = []
        for i in range(4): dollar[i] += 500
        for i in range(4):
            tmp_win = prev_win
            if prev_win == i: tmp_win = 0
            elif prev_win != -1 and prev_win < i: tmp_win += 1
            bid = int(bots[i].play_round(tmp_win, prev_bid))
            if bid < 0 or bid > dollar[i]: raise ValueError(pl[i])
            bids.append((bid, i))
        bids.sort(reverse = True)
        winner = 0
        if bids[0][0] == bids[1][0]:
            if bids[2][0] == bids[3][0]: winner = -1
            elif bids[1][0] == bids[2][0]: winner = 3
            else: winner = 2
        if winner == -1:
            prev_win, prev_bid = -1, -1
        else:
            prev_bid, prev_win = bids[winner]
            score[pl[prev_win]] += 1
            total[pl[prev_win]] += prev_bid
            dollar[prev_win] -= prev_bid
for a in range(N - 3):
    for b in range(a + 1, N - 2):
        for c in range(b + 1, N - 1):
            for d in range(c + 1, N): auction([a, b, c, d])
res = sorted(map(list, zip(score, total, bot_list)), key = lambda k: (-k[0], k[1]))
class TIE_REMOVED: pass
for i in range(N - 1):
    if (res[i][0], res[i][1]) == (res[i + 1][0], res[i + 1][1]):
        res[i][2] = res[i + 1][2] = TIE_REMOVED
for sc, t, tp in res:
    print('%-20s Score: %-6d Total: %d' % (tp.__name__, sc, t))
```
---
# Examples
If you need a pseudorandom generator, here is a simple one.
```
class myrand:
    def __init__(self, seed): self.val = seed
    def randint(self, a, b):
        self.val = (self.val * 6364136223846793005 + 1) % (1 << 64)
        return (self.val >> 32) % (b - a + 1) + a
class zero_bot:
    def play_round(self, i_dont, care): return 0
class all_in_bot:
    def __init__(self): self.dollar = 0
    def play_round(self, winner, win_amount):
        self.dollar += 500
        if winner == 0: self.dollar -= win_amount
        return self.dollar
class random_bot:
    def __init__(self):
        self.dollar = 0
        self.random = myrand(1)
    def play_round(self, winner, win_amount):
        self.dollar += 500
        if winner == 0: self.dollar -= win_amount
        return self.random.randint(0, self.dollar)
class average_bot:
    def __init__(self):
        self.dollar = 0
        self.round = 11
    def play_round(self, winner, win_amount):
        self.dollar += 500
        self.round -= 1
        if winner == 0: self.dollar -= win_amount
        return self.dollar / self.round
class fortytwo_bot:
    def play_round(self, i_dont, care): return 42
```
**Result**
```
all_in_bot           Score: 20     Total: 15500
random_bot           Score: 15     Total: 14264
average_bot          Score: 15     Total: 20000
TIE_REMOVED          Score: 0      Total: 0
TIE_REMOVED          Score: 0      Total: 0
```
The winner is `all_in_bot`. Note that `zero_bot` and `fortytwo_bot` have the same score and total, so they're removed.
These bots will not be included in the competition. You can use them if you think they are great.
---
The final competition will be held at **2017/11/23 14:00 (UTC)**. You can make any change to your bots before that.
  
[Answer]
# hard\_coded
```
class hard_coded:
  def __init__(self):
    self.money = 0
    self.round = 0
  def play_round(self, did_i_win, amount):
    self.money += 500
    self.round += 1
    if did_i_win == 0:
      self.money -= amount
    prob = [500, 992, 1170, 1181, 1499, 1276, 1290, 1401, 2166, 5000][self.round - 1]
    if prob > self.money:
      return self.money
    else:
      return prob    
```
This bot is the results of genetic training against a lot of other pseudo-random bots (and some of the bots in other answers). I've spent some time fine-tuning at the end, but its structure is actually very simple.
The decisions are based only on a fixed set of parameters and not on the outcome of previous rounds.
The key seems to be the first round: you have to go all-in, bidding 500 is the safe move. Too many bots are trying to outsmart the initial move by bidding 499 or 498. Winning the first round gives you a big advantage for the rest of the auction. You are only 500 dollars behind, and you have time to recover.
A safe bet in the second round is a little over 990, but even bidding 0 gives some good result. Bidding too high and winning could be worse than losing this round.
In the third round, most bots stop escalating: 50% of them have less than 1500 dollars by now, so there is no need to waste money on this round, 1170 is a good tradeoff. Same thing in the fourth round. If you lost the first three, you can win this one very cheap, and still have enough money for the next.
After that, the average money required to win a round is 1500 dollars (which is the logical conclusion: everyone wins a round out of four by now, bidding less to win later is just wasting money, the situation has stabilized and it's just round-robin from now on).
The last round must be all-in, and the other parameters are fine-tuned to win the last round by bidding as low as possible until then.
A lot of bots try to win the ninth round by bidding more than 2000 dollars, so I took that into account and try to overbid them (I can't win both the last two rounds anyway, and the last will be harder).
[Answer]
# Above Average
Bids above the average amount of money the other players have. Bids everything in the last round.
```
class above_average:
  def __init__(self):
    self.round = 0
    self.player_money = [0] * 4
  def play_round(self, winner, winning_bid):
    self.round += 1
    self.player_money = [x+500 for x in self.player_money]
    if winner != -1:
      self.player_money[winner] -= winning_bid
    if self.round == 10:
      return self.player_money[0]
    bid = sum(self.player_money[1:]) / 3 + 1
    if bid > self.player_money[0]:
      return self.player_money[0]
    return min(self.player_money[0], bid)
```
[Answer]
# I Don't Even
```
class I_Dont_Even:
	def __init__(self):
		self.money = 0
		self.round = 0
	def play_round(self, loser, bid):
		self.money += 500 - (not loser) * bid
		self.round += 1
		return self.money * (self.round & 1 or self.round == 10)
```
Only participates in odd rounds and the last round.
[Answer]
The forgetful bot doesn't know how much money he has, so he just puts in the money he was given for this round. If he finds he has some money at the end, he just donates it to a charity.
```
class forgetful_bot:
  def play_round(self, winner, amt):
    return 500
```
[Answer]
# One Upper
I don't know much about Python, so I might make some kind of error
```
class one_upper:
    def __init__(self): 
        self.money = 0
        self.round = 0
    def play_round(self, winner, win_amount):
        self.money += 500
        if winner == 0: self.money -= win_amount
        self.round += 1
        bid = win_amount + 1
        if self.money < bid or self.round == 10:
            bid = self.money
        return bid
```
bids 1 higher than the previous winning bid, or goes all-in during the last round.
I may in the future decide on a different strategy for when `win_amount` is -1
[Answer]
# Patient Bot
```
class patient_bot:
    def __init__(self):
        self.round = 0
        self.money = 0
    def rand(self, seed, max):
        return (394587485 - self.money*self.round*seed) % (max + 1)
    def play_round(self, winner, amount):
        self.round += 1
        self.money += 500
        if winner == 0:
            self.money -= amount
        if self.round < 6:
            return 0
        else:
            bid = 980 + self.rand(amount, 35)
            if self.money < bid or self.round == 10:
                bid = self.money
            return bid
```
Bids nothing for the first five rounds, then bids ~1000 dollars for the next four rounds, and finally bids everything it has in the last round.
[Answer]
# Copycat Or Sad
Third and final bot.  
This bot will bid exactly the same amount as the previous winner (including itself). However, if it doesn't have enough cash to do so it will be sad, and will bid a measly 1 dollar bill with his tear on it instead. In the final round it will go all-in.
```
class copycat_or_sad:
  def __init__(self):
    self.money = 0
    self.round = -1
  def play_round(self, winner, win_amount):
    # Default actions:
    #  Collect 500 dollars
    self.money += 500
    #  If it was the winner: subtract the win_amount from his money
    if winner == 0:
      self.money -= win_amount
    #  One round further
    self.round += 1
    
    # If it's the final round: bid all-in
    if self.round == 9:
      return self.money
    # Else-if there was no previous winner, or it doesn't have enough money left: bid 1
    if win_amount < 1 or self.money < win_amount:
      return 1
    # Else: bid the exact same as the previous winner
    return win_amount
```
I never program in Python, so if you see any mistakes let me know..
[Answer]
# Test run
I've edited a previous test run put together by [Steadybox](https://codegolf.stackexchange.com/users/61405/steadybox) adding in the newest submissions. 
I'm posting it here so there is a place where the link can be updated with more recent versions, this post is a community wiki so feel free to update it if you post a new submission, modify an old one, or simply see something new from some other submission!
[Here's the link to the test run! (TIO)](https://tio.run/##7Vxtb9s4Ev5c/wouil6lhE4k20nrIAnQvfZuC1zbQ7e7i0MQCLJFx7rqxZDkJtm9/e29Gb5IpEQ5TpPedg8p2tSWhsPhzMPhzJDM6rpa5tn48zwJy5LkGQuiPEnCIpjls6MBIRFbkFUSXgdFvs4i54pe019dfEFIwap1kRF/MBCNF3lxwarFOoG2laVtyZIFJZdxlrGCkjCtTD4Hnqc4rcIqZlml@AhOQRBncRUEnI9sin/w6x7vgZwQz3ycwoCu68fIpQhrSUrGIkrS8EpjJmVxxtPJwfNnk@cHZKhx2mn62sHWLnlCHGBAdonv1l1sGDI8rOyi756AIi2yw3NUjHoTLyQ3cgKjaji1Wg1PZGd6Q623Y3JotpXjbjpiSclMklmMCp4@92C0ghWqUnRDyfjANahVf0KeY946LwxjwYhbI2i6adraxAQaBZVwln9iQfiJFeEFU6CzQcUCE/4IrcWKQCHlzDsnO2RyE3rx/zi7CECSLv/altYOrnbBoDhZyBWJsy7N@cA09HcnZOgrPXWozwTVOZpcE2rQtbmhb6nILjtPdC@tsE6dLol/dO6SfTJG1Kt@kP7Uym7bHuX7NM4cGw3FLlxl9dfByxz8w6tPLDsaPLKZ/NGjlgOQDxoEPLIaOMlLtK@wq8FEzETwB06WV4LOBaigsg3e3PqPHunjFQx2iKOR/YX4tglRDxFd8Xq1YkWvBySbfF0P5G8CdWD3Udv4opb/aZjd6O4E2JoGNbC@2JH0OpGuA1mGyQL0eqPrMNVrqHboD26r2cfkJVuE66Qi4byK86xUj8lfYfll84pjTSzF5aDfDtDg9YLEFbkMS1ItmewRjLGeVQXwVg@VahdFnpJlXJJGL/Y1ZaM1odt3GSNCAYt1AZ0UVi8oqbmMT4WEizgLE9H0iBsqTBJwhHaPNbW5j0b0x@QVrlPke2CDhiSXy7BCdSxBHQlbVEaEoQ1pn4wUAMokvwzKKiwqMdceUPA/RgFO55iLH@WszJ6i9T4xwrJ8fbEUEgpb3oyWlmfYISPQ36k2iu3ghP1gyxRME6@SmEUw9GrJBZ@viwJiUyn6JXtaMG5xWHiVUpy3eQVcXlfIqCSeHHBRylZuB5SauAqW83x1PQ@rIC@CMowegPlHuqfhndzTEJqhBIxrIcvJqmCf4nxd1vrHCbAd@n1NJUpnx1okoVbJ5nVLQr8DdBw0u0JblGHKiDRUS0YdsZqyJVajuKyKeLa@iwct4Znvb5812gGn8xueNOoy@rEEwpPpdGuwgQy6OhzH6BQyQZjIOD12tXYYMGtkSnEpC7OYbZnjdlOUdlhV61B7UeVVmATlCn0War03DJQq7mY2XQFsKYyWu/QaYkNi0@5mU17TO75dO62IBluhqy7gKfhoSGa7DI/RI3vW4DLOKsfhb1WJQGvo7mvs9yfurt@OQDHPAUY0Da8cbdSYW@36VHvindcpwY/L/DJfLDZNMV2FJccKJfj3fNAxyz/AqSBevNvnuqDAdJUXFUnDamnvV/uKeN1F9dNOst9ueeYjqb8d7UjR9g1Ow12DulOzbNLq3Iq2/qJLa5R9rTtab7HprB7lWT2oIoQF2EhmuKD7LVVI6iS/AF8cz@NfGYLU8UFFDlppj5EdSECHzzENFTyHxNs7cOEPvhn6pkPkbRZJnhdOa5A7Ric1OFk5DxPgm13cJVbxHkKVzaEKOh/xNFunM@gJ/O@KFXP0VlWcshIMmIYxYlAIRgm3IsChytFpsQtzNTcKFFqn@/voGqRx/xZ/YuqfGFYm6jM418VKBI6sWYO8L1nR@CPF1yQEpZXb0DUw2rbasU05Fhe6GBVdhNkFc4z@3d4FLD5vF0wkRGJEh0TXoN/BIYMaL5WdEPUiO/J7Sr0Y7dxY621Xh4UUyqLW5riAHRwcUNJaq24qIBvkPcVds@izN8fZgqulC6KNOWekGtY7Dy@yKn4TXil9PmDzm8VmvbpcOU7KhU/NCBIjIBQkPT4xgWUNCCLh@k@asumLJM3LKniRJMHrP1FteKgb@AmAfAf@DclYDet7FlYfluwXzTIPKP9mUZ5eK73avR0O55IK5EMYzSLn13jl1Px0n@pSSMRhlOzkQ7FmrbHiPJFdnZK0OxQ127T04zHGKIxcsIqIkgQj8xCrDuECpIpIvi7UrCKQ1IcQL0AIUebtGYzpj@x7X9u1eCE24t7E2Z8kTjAey23E2@yW3DN6twJnI6ZjfDc3mIZk5GL0L@U0MgZArIslCZPed7/GZLhxUm4ZryDivL3p1FOmtcUctwlkjCADIXNqrEM8u2zKAnaq0VZUY07VPz9bNYBzavRADU6usTtm1iNaaDC1BO2eH/RFcgYp5Vut9xHI1XsscbpK2MN5jvs7z7H5NIavH8f42juoM4Y7aA9HMP6IIxjg5P@gIxizJJx/TGKItdM8ulery/d1B/DyN49iif73/wtMtMe3F0aRoxGBEwJRXCst@Q8o40pQbBAWAfI7Z1CxrAqr@BMLtBltM364rnIQAkjq5agDud16P8Vke6xaK31cLuOEtYhwjXTMR2Kg9QjqUTauqC2@@X0oDaovUe0WUrJBj95tAumTwXivsP/3IrxWct437HltFoPY3/zfm6dRvFjwh949zYEeIGt1crElonE79dorHReVq7F3Auzr4pjN@Zg2tx5aWt9qrt1hqreB1OsWN80Xwxy7/uGUbvDo@/tjJBED3XIKgfyOQEG6U7itikq7B/66qF8L@0EiAE8tbNNhtA2/qH7NDeq6fTPRPnc3zTP4GCzji@WGQ3AtSPEy/1c6BCeZtyK4vpgvzuaY4Y79zeflJFP7gTlb9GCs4qJ1qwe1QSF60R9Y0y4pwanKFOUJPJDfteZhbaoNiUKni/4cyFCGmd90@RpVbImVHwAnP@SFIr1LmeGu4NkmJQDVeH2Jvm3DqNoumbhtFb@nBHmL5O@GzA@85roM8uy2hxy8L02/7jfNsqmp3nb4Bfx6GmaB/@f1Tt4X@46v78pM19F2O1/BN92bL7qUwBh9C8Doq0fYASOBMZo@AONegfHm3dtX/wr@@ep98P7dT29fSrW9/emN@P6jWKYkft5DoJenPeARha62lT@FCXfLLDJKY6oqCN4S0nd7I6f@vEMOx4cTf3w4Go2fTw6fTceed8CLYVgY88nxMTmcuJ3iWt3@9JSMR5x2Bn4yFC13SViLNF/m8ZwpiYqiW6mDh2d1PYuXfGnCMgdph757Xl/7EdWm4N@51HqxFmE2RNfm7tPmMiNXNDQQGncmI3e7fSvLETkD@kO/nT2Jp5alBw@XfMRW5IP45Li9RHsZu6qkTHw5OmnhyrWBf/LsYAPeN/IHqTb2YDRer6KwggA@TMIMrGxTV7exbrjd/rN@alaqvpqznM0cslwmE6KogEU1vmBVLaXn9s1aSbHFNFcbZaofAas9iXaB5xkLqwDPe0JGU1Z5cS13@Zrnsr/zXoFUN44G0g7bNs7xXS2Xpuv9fZtCbRv1mPhyLrtkRDcp0qWeVTJFsUEynaGa26qZu0mo3W1EeqymFllXcRJX14R7EelL5Lvb7EyU3bVa5sYN4qJ4Xu3hIbGP7Lp0xNbfxKVE1xEKC45zzcp2X3KoZxbeZ@KryL7F53aGLin3eNfoNVWH2vwWPk14Eax62cfZ2cjcpkOLg@kZArAXWYShEh0wVPbY1c0Gvg27lk/SOVLCrlYsK1l76D2yQvwiG5jStvHasqSwrwK6ZnEXSyjtQUnbrFNHUGGZHxdA@a05C3wZbrwceF@ZrXHmpTfRsp582SIFNu9D3pyh@v2JWX2fj62LuoR8V8UMWzFzBta7zItq2arcbgwXLLq7yx5hK2HVNd0u6raE1gulHQMZQ7NVSGuCBrPgiGVt72wyfU59z/OoD7Ej9cdTHz4dwNfpyKejAx@@Hh4ewg8gAd7e@VmjiPOercpT1YEVGHi0ma8CgqYpwzbj9Y/G5@6mnexeFM3yWbAMK32CafaFt/QyThKa5LnhPiS/@m4hY1nMAnHLI0jz4hu56nFDJPtw2eNulz0mE6/vugex3/cg9gsfy7CIgnkesehuZ@r7toyiOAriALTYKqbddLfK2EaqmWy8MlXn/KsinyGs8I4HmU4hfPP9Zx7@fA568CfTKfwcPTvEn1N8PvHg@Qi8ByVtx4ErQr0vyxlvf9dTdwaSAjl8nuVVIHedz/hr83eiUPPXnFD9d5VQ87dRUP3XFNDmQj9Vd86pcfeYtq58igOv2tU6Kq@LUXURiGqXLqhxRp82R6KpeSSWmkdJqX5ij2rndETvxtEOau7503oblGobNbQpxNOm@EubWiltqmNUT@OpCmlEz3IZp40npl1vSrUZMoCgb1XgRH36fV7h/Vm8/EieRE/JEx5BKcNiDCWvLy3DcpnEs8EAZ0fE5nHEQPkRpOCJOpiHJNIBSOq9chn6DlgFifZYhr07rru3ZFdRfMHKyoEg3z/EMshIeO4s4YcSBVJbhwEn/H7W0NfcL5DvoSZgmiblGRfgCYm1tDBiCdHf1C/4k/19EFffWgN@g8FbkMDQwqCcgwbrNebtgHs07TtXSrjml2oafVwkOcS5hDemhLcR0xqbGiqUJ1uTUq7AM7QKsFcSnMXnjtuoY5WIcdS1UH3tw9uowk/xT8IpD32qAjS@rynce61ZX09p@EXs2gY2O7hHsmvLCcUurbFGVOlK@L9aznbpQj1HFxkfaQ28VnVBp/2uLiPVj4711p1Tls1ahbqGcexpHl@2ajTodn5hjzjK4eF2sDhMVCvkCEYel4z8jFnIq6LIC2eVnMWtUgUqWSFXrHax1gl/i6Bw5IlmXvFas4ZCRbnEWLuxHWAB4XByIr7559qpJpNwZBCOOWHDuFO0Vc18o9mo1WzcrQE1L0d9FUBTwJsgbA9QFS3VICRklCGTGSPhrIR89UwRn1vO4vI52yVSPRm0EgA1Ja7lNeEAp0XYTIu3eGFBTg18NWte8VIwJW/5oeQjY17NG6pZQ@W3plh90EBQziUlTFrloc6wwA1rKEQ1GEAVIuUWPigNV45YqPg5e8134fqilgVKPvIQKgnTWRSSj0fEGX7k594@Aj6a1PvD61fB@1dv3v386uURLP9lKTQRm5pQIwBQAOBxNnJW8iPwQ5CINzgY7a34ChR6XsFbjQCiOg3/ronD5ShBBTDSasXlYXLfVy6NT4YjryQ/ogZgbRweRuQDqkGtk0612guCLExZEFDByXU/f/4v)
[Answer]
# Half In
This bot always bids half of what it has left, except in the final round where it will go all in.
```
class half_in:
  def __init__(self):
    self.money = 0
    self.round = -1
  def play_round(self, winner, win_amount):
    # Default actions:
    #  Collect 500 dollars
    self.money += 500
    #  If it was the winner: subtract the win_amount from his money
    if winner == 0:
      self.money -= win_amount
    #  One round further
    self.round += 1
    # If it's the final round: bid all in
    if self.round == 9:
      return self.money
    # Else: Bid half what it has left:
    return self.money / 2
```
I never program in Python, so if you see any mistakes let me know..
[Answer]
# Graylist
```
class Graylist:
  def __init__(self):
    self.round = 0
    self.player_money = [0] * 4
    self.ratios = {1}
    self.diffs = {0}
  def play_round(self, winner, winning_bid):
    self.round += 1
    if winner != -1:
      if winner >0 and winning_bid>0:
        self.ratios.add(self.player_money[winner]/winning_bid)
        self.diffs.add(self.player_money[winner]-winning_bid)
      self.player_money[winner] -= winning_bid
    self.player_money = [x+500 for x in self.player_money]
    tentative_bid = min(self.player_money[0],max(self.player_money[1:])+1, winning_bid+169, sum(self.player_money[1:])//3+169)
    while tentative_bid and (tentative_bid in (round(m*r) for m in self.player_money[1:] for r in self.ratios)) or (tentative_bid in (m-d for m in self.player_money[1:] for d in self.diffs)):
      tentative_bid = tentative_bid - 1
    return tentative_bid
```
Inspired by the [blacklist submission by histocrat](https://codegolf.stackexchange.com/a/147835/62393), this bot keeps in memory all the previous winning bets of other players as both the ratio of money they bet compared to their full money and the difference between their bet amount and the full amount. In order to avoid losing to a tie (which apparently is actually a big factor in this competition) it then avoids betting any number that could give the same results given its opponents' current moneys.
EDIT: as the starting value for the bid now uses the minimum between: its current money, 1 more than the money of the richest opponent, X more than the last winning bet, or Y more than the average money of its opponents. X and Y are constants that will probably be modified before the end of the competition.
[Answer]
# AverageMine
This player calculates the percentage (bid / total money) for the winner of each round and bids his (total money \* average winning percentage + 85) unless he has more money than all the other players, then he bids 1 more than the highest competitor. Starts with a bid of 99.0% of the starting amount.
```
class AverageMine:
    nplayers = 4
    maxrounds = 10
    def __init__(self):
        self.money = [0] * self.nplayers
        self.wins = [0] * self.nplayers
        self.round = 0
        self.average = 0
    def play_round(self, winner, win_amt):
        self.round += 1
        for i in range(self.nplayers):
            if i == winner:
                self.average = (self.average * (self.round - 2) + (win_amt / self.money[i])) / (self.round - 1)
                self.money[i] -= win_amt
                self.wins[i] += 1
            self.money[i] += 500
        if self.round == 1:
            return int(0.990 * self.money[0])
        elif self.round < self.maxrounds:
            if self.money[0] > self.money[1] + 1 and self.money[0] > self.money[2] + 1 and self.money[0] > self.money[3] + 1:
                return max(self.money[1],self.money[2],self.money[3]) + 1
            bid = int(self.average * self.money[0]) + 85
            return min(self.money[0],bid)
        else:
            bid = self.money[0]
            return bid
```
[Answer]
# Eenie Meanie More
This player is identical to Meanie, except for one variable. This version bids more aggressively and causes some players to spend more than meanie thinks the auction is worth.
```
class eenie_meanie_more:
    def __init__(self):
        self.money = [0] * 4
        self.rounds = 11
        self.total_spent = 0
    def play_round(self, winner, winning_bid):
        self.money = [x+500 for x in self.money]
        self.rounds -= 1
        if winner != -1:
            self.money[winner] -= winning_bid
            self.total_spent += winning_bid
        bid = 500
        if self.rounds > 0 and self.total_spent < 20000:
            bid = int((20000 - self.total_spent)/self.rounds/4)+440
        return min(bid, max(self.money[1:])+1, self.money[0])
```
[Answer]
# Distributer
When this bot loses a round, he distributes the excess cash among all of the next rounds. He puts in $499 on the first round thinking that the others will tie with $500 and be eliminated.
```
class distributer:
  def __init__(self):
    self.money = 0
    self.rounds = 11
  def play_round(self, winner, amt):
    self.money += 500
    self.rounds -= 1
    if self.rounds == 10:
      return 499
    if winner == 0:
      self.money -= amt
    return ((self.rounds - 1) * 500 + self.money) / self.rounds
```
[Answer]
# Meanie
This player takes the total cash that will enter play to get the mean bid across the number of players and remaining rounds. If this target is more than all other players currently hold it lowers its bid to the balance of its greatest competitor plus one. If the player cannot afford its target, it is all-in.
```
class meanie:
    def __init__(self):
        self.money = [0] * 4
        self.rounds = 11
        self.total_spent = 0
    def play_round(self,winner,winning_bid):
        self.money = [x+500 for x in self.money]
        self.rounds -= 1
        if winner != -1:
            self.money[winner] -= winning_bid
            self.total_spent += winning_bid
        bid = 500
        if self.rounds > 0 and self.total_spent < 20000:
            bid = int((20000 - self.total_spent)/self.rounds/4)+1
        return min(bid,max(self.money[1:])+1,self.money[0])
```
[Answer]
# Beat the Winner
Bid 1 more than the player with the most wins so far
```
class BeatTheWinner:
    nplayers = 4
    maxrounds = 10
    def __init__(self):
        self.money = [0] * self.nplayers
        self.wins = [0] * self.nplayers
        self.round = 0
    def play_round(self, winner, win_amt):
        self.round += 1
        for i in range(self.nplayers):
            self.money[i] += 500
            if i == winner:
                self.money[i] -= win_amt
                self.wins[i] += 1
        mymoney = self.money[0]
        for w,m in sorted(zip(self.wins, self.money),reverse=True):
            if mymoney > m:
                return m+1
        #if we get here we can't afford our default strategy, so
        return int(mymoney/10)
```
[Answer]
# Minus One
```
class minus_one:
    def __init__(self):
        self.money = 0
    def play_round(self, winner, amount):
        self.money += 500
        if winner == 0:
            self.money -= amount
        return self.money - 1
```
[Answer]
# Bid Higher
```
class bid_higher:
    def __init__(self):
        self.dollar = 0
        self.round = 0
    def play_round(self, winner, win_amount):
        self.dollar += 500
        self.round += 1
        inc = 131
        if winner == 0: self.dollar -= win_amount
        if self.round == 10: return self.dollar
        if win_amount == 0: win_amount = 500
        if self.dollar > (win_amount + inc):
            return win_amount + inc
        else:
            if self.dollar > 1:
                return self.dollar -1
            else:
                return 0
```
Still learning python; bid a bit higher than the last winner.
[Answer]
# FiveFiveFive
Skips the first round and bids $555 on remaining rounds. In last round, will go all in unless 2 other bots have the same amount (and will presumably tie out). 
```
class FiveFiveFive:
    nplayers = 4
    maxrounds = 10
    def __init__(self):
        self.money = [0] * self.nplayers
        self.wins = [0] * self.nplayers
        self.round = 0
    def play_round(self, winner, win_amt):
        self.round += 1
        for i in range(self.nplayers):
            self.money[i] += 500
            if i == winner:
                self.money[i] -= win_amt
                self.wins[i] += 1
        if self.round == 1:
            return 0
        elif self.round < self.maxrounds:
            return min(555, self.money[0])
        else:
            bid = self.money[0]
            return bid if self.money.count(bid) < 3 else bid-1
```
[Answer]
# Almost All In
```
class Almost_All_In:
	def __init__(self):
		self.money = 0
		self.round = 0
	def play_round(self, loser, bid):
		self.money += 500 - (not loser) * bid
		self.round += 1
		return self.money - self.round % 3 * 3 - 3
```
Always bids slightly less than it has.
[Answer]
# Escalating Quickly
Bids increasing fractions of its money each round (please let me know if any errors, a while since I used Python)
```
class escalating:
  def __init__(self):
    self.money = 0
    self.round = 0
  def play_round(self, winner, win_amount):
    # Default actions:
    #  Collect 500 dollars
    self.money += 500
    #  If it was the winner: subtract the win_amount from his money
    if winner == 0:
      self.money -= win_amount
    #  One round further
    self.round += 1
    # bid round number in percent times remaining money, floored to integer
    return self.money * self.round // 10
```
[Answer]
# Below Average
Similar to above average, but goes a bit lower
```
class below_average:
  def __init__(self):
    self.round = 0
    self.player_money = [0] * 4
  def play_round(self, winner, winning_bid):
    self.round += 1
    self.player_money = [x+500 for x in self.player_money]
    if winner != -1:
      self.player_money[winner] -= winning_bid
    if self.round == 10:
      return self.player_money[0]
    bid = sum(self.player_money[1:]) / 3 - 2
    if bid > self.player_money[0]:
      return self.player_money[0]
    return min(self.player_money[0], bid)
```
[Answer]
# HighHorse
This player bids all his money minus the current round number, except on the last round, where he goes all in.
```
class HighHorse:
    maxrounds = 10
    def __init__(self):
        self.money = 0
        self.round = 0
    def play_round(self, winner, win_amt):
        self.round += 1
        if 0 == winner:
            self.money -= win_amt
        self.money += 500
        if self.round < self.maxrounds:
            return self.money - self.round
        else:
            bid = self.money
            return bid
```
[Answer]
# Swapper
Alternates between bidding one under his max and going all in.
```
class Swapper:
    def __init__(self):
        self.money = 0
        self.round = 0
    def play_round(self, loser, bid):
        self.money += 500 - (not loser) * bid
        self.round += 1
        if self.round & 1:
            return self.money - 1
        return self.money
```
I figured I needed to find something that could beat Steadybox's minus\_one. :)
[Answer]
# Modular Blacklist
```
class blacklist_mod:
  def __init__(self):
    self.round = 0
    self.player_money = [0] * 4
    self.blacklist = {0, 499}
  def play_round(self, winner, winning_bid):
    self.round += 1
    self.player_money = [x+500 for x in self.player_money]
    if winner != -1:
      self.player_money[winner] -= winning_bid
      self.blacklist.add(winning_bid % 500)
      self.blacklist |= {x % 500 for x in self.player_money[1:]}
    tentative_bid = self.player_money[0]
    autowin = max(self.player_money[1:])+1
    if tentative_bid < autowin:
      while tentative_bid and (tentative_bid % 500) in self.blacklist:
        tentative_bid = tentative_bid - 1
    else:
      tentative_bid = autowin
    self.blacklist.add(tentative_bid % 500)
    return tentative_bid
```
Bets the highest amount it can that isn't congruent modulo 500 to any numbers it's seen before.
Edited to not apply the blacklist when it can get a guaranteed win.
[Answer]
## Heurist
The *Heurist* treats this game as one of repeatable probability, so it knows where to draw the line.
It is also miserly, so it bids the bare minimum required for a win when it can.
```
class heurist:
    def __init__(self):
        self.money = 0
        self.round = -1
        self.net_worth = [0] * 4
    def play_round(self, winner, bid):
        self.round += 1
        self.money += 500
        if winner == 0: self.money -= bid
        if winner != -1: self.net_worth[winner] -= bid
        self.net_worth = [x+500 for x in self.net_worth]
        max_bid = [498,1000,1223,1391,1250,1921,2511,1666,1600,5000][self.round]
        if self.money > max_bid:
            return 1 + min(max_bid,max(self.net_worth[1:3]))
        else:
            return self.money
```
*Disclaimer: `max_bid` is subject to change*
[Answer]
# bob\_hater
This bot does not like Bob and thus will always bid 2$ to win against Bob.
```
class bob_hater:
    def play_round(bob,will,loose):
        return 2
```
[Answer]
## Showoff
This is that guy who shows off his math ability in situations that truly do not require anything so complicated. Until the last round (in which he goes all-in), he uses a logistic model to determine his bid, more if his enemies have a greater portion of their money remaining.
```
class Showoff:
  def __init__(self):
      self.moneys = [0, 0, 0]
      self.roundsLeft = 10
  def play_round(self, winner, winning_bid):
      import math
      self.moneys = [self.moneys[0] + 500,
                     self.moneys[1] + 1500,
                     self.moneys[2] + 1500]
      self.roundsLeft -= 1
      if winner > 0:
          self.moneys[1] -= winning_bid
      if winner == 0:
          self.moneys[0] -= winning_bid
      if self.roundsLeft == 0:
          return self.moneys[0]
      ratio = self.moneys[1] / self.moneys[2]
      logisticized = (1 + (math.e ** (-8 * (ratio - 0.5)))) ** -1
      return math.floor(self.moneys[0] * logisticized)
```
The logistic curve used is f(x)=1/(1+e-8(x-0.5)), where x is the ratio of current enemy-money to the round's total potential enemy-money. The more the others have, the more he bids. This has the [possible benefit](https://codegolf.stackexchange.com/a/147598/73884) of bidding almost-but-not-quite $500 the first round.
[Answer]
# AntiMaxer
Match the highest amount we can afford of all player's money. Will cause any bot going for all-in on that round to tie out. 
```
class AntiMaxer:
    nplayers = 4
    maxrounds = 10
    def __init__(self):
        self.money = [0] * self.nplayers
        self.wins = [0] * self.nplayers
        self.round = 0
    def play_round(self, winner, win_amt):
        self.round += 1
        for i in range(self.nplayers):
            self.money[i] += 500
            if i == winner:
                self.money[i] -= win_amt
                self.wins[i] += 1
        return max((m for m in self.money[1:] if m<=self.money[0]),
                   default=0)    
```
[Answer]
# Simple Bot
```
class simple_bot:
    def __init__(self):
        self.round = 0
        self.money = 0
    def rand(self, seed, max):
        return (394587485 - self.money*self.round*seed) % (max + 1)
    def play_round(self, winner, amount):
        self.round += 1
        self.money += 500
        if winner == 0:
            self.money -= amount
        bid = 980 + self.rand(amount, 135)
        if self.money < bid or self.round == 10:
            bid = self.money
        return bid
```
Almost the same as Patient Bot, but not as patient. Gets a **much** better score than it, though.
[Answer]
# Wingman 2
If one wingman is good, two must be better ?
```
class wingman_2:
    def __init__(self):
        self.dollar = 0
        self.round = 0
    def play_round(self, winner, win_amount):
        self.round += 1
        self.dollar += 500
        inc = 129
        if win_amount == 0: win_amount = 500
        if winner == 0: self.dollar -= win_amount
        if self.round == 10: return self.dollar
        if self.dollar > win_amount + inc:
            return win_amount + inc
        else:
            if self.dollar > 1: return self.dollar -1
            else:
                return 0
```
]