# King of the Hill: Bank Heist

Rules Clarification 4/8: All submissions will be competing in one giant free-for-all tournament consisting of as many matches as my computer can perform in 48 hours.

Anyone who's spent any time watching streams on Twitch is aware of the prevalence of DeepBot, and is perhaps even familiar with its Bank Heist betting game. This King of the Hill tournament is directly inspired by that game. Don't worry, though. I think I've thrown enough extra kinks into this version to keep things interesting.

## A quick example

#####GAME 13: 16 players######

Round 1:
gunHeCK bet 0.
PassivePanga bet 69.
SnitcherKing bet 1.
Lurker bet 0.
OC'sRandomTpyos bet 1.
RaysFive01K bet 28.
KaylorrCriterion bet 0.
HardHatUmar bet 0.
HeCKuSumer bet 185.

Round 2
HeCKuSumer decided to !guncheck.
OC'sRandomTpyos decided to acquire intel.
RaysFive01K decided to deposit.
SnitcherKing decided to finger.
PassivePanga decided to !guncheck.

Results
PassivePanga failed. :(
SnitcherKing failed. :(
OC'sRandomTpyos was successful, and may gain ¥0
RaysFive01K was successful, and may gain ¥0
LimeadeSneaktar was successful, and may gain ¥1
HeCKuSumer failed. :(

Results:
0. KaylorrCriterion: 3600
1. Lurker: 3600
2. gunHeCK: 3600
3. SnitcherKing: 3586
4. PassivePanga: 2634
6. HeCKuSumer: 1909
7. HardHatUmar: 490
8. RaysFive01K: 255
9. OC'sRandomTpyos: 170

(In this round, 7 players joined the heist, but the dice only rolled right for 3 of them. Of those, only LimeadeSneaktar brought any home--having stolen it from OcsRandomTpyos. RaysFive01K won significantly more, but deposited it all at the bank before leaving. At this point, the players who did not heist are doing well, living off their day jobs.)

#####GAME 14: 231 players######

Round 1:
Lurker bet 0.
HeCKuSumer bet 190.
KaylorrCriterion bet 0.
HardHatUmar bet 0.
OC'sRandomTpyos bet 1.
gunHeCK bet 0.
RaysFive01K bet 25.
PassivePanga bet 69.
SnitcherKing bet 1.

Round 2
PassivePanga decided to !guncheck.
HeCKuSumer decided to !guncheck.
SnitcherKing decided to finger.
RaysFive01K decided to deposit.

Results
HeCKuSumer failed. :(
OC'sRandomTpyos failed. :(
RaysFive01K failed. :(
PassivePanga failed. :(
SnitcherKing failed. :(

Results:
0. KaylorrCriterion: 3840
1. Lurker: 3840
2. gunHeCK: 3840
3. SnitcherKing: 3825
4. PassivePanga: 2805
6. HeCKuSumer: 1959
7. HardHatUmar: 490
9. RaysFive01K: 229
10. OC'sRandomTpyos: 161

Six players heisted--but should have been paying more attention to the rabble and backed out, because the probabilities dropped too low to win, and all failed.

#####GAME 15: 300 players######

Round 1:
OC'sRandomTpyos bet 1.
Lurker bet 0.
SnitcherKing bet 1.
gunHeCK bet 0.
HardHatUmar bet 0.
RaysFive01K bet 22.
KaylorrCriterion bet 0.
HeCKuSumer bet 195.
PassivePanga bet 69.

Round 2
HeCKuSumer decided to !guncheck.
PassivePanga decided to !guncheck.
RaysFive01K decided to deposit.
SnitcherKing decided to finger.

Results
OC'sRandomTpyos failed. :(
SnitcherKing failed. :(
MonisAddiction was successful, and may gain ¥0
RaysFive01K failed. :(
HeCKuSumer failed. :(
PassivePanga failed. :(

And here, the probabilities dropped too low to win again--except for MonisAddiction, who went all in, and therefore avoided the probability modification incurred by the rabble backing out. No winnings are listed here, because a player who wins going all in immediately adds all winnings to its holdings without any possible modification by other players' actions.


## Game Rules

### Tournament/Game Structure

• The tournament will consist of a number of games chosen uniformly at random between 1000 and 1100, in which every serious submission will be competing simultaneously in a free-for-all.
• Every player starts the first game with 240 credits and each subsequent game with the number of credits it had at the end of the previous game.
• Each game proceeds in 2 rounds, and in each round, players are called in an order determined uniformly at random to make one decision:
• In the first round, a player may pay any integer number of credits between 0 and its current credit holdings to stake participation in a bank heist.
• In the second round, each player that elected to participate in the heist by wagering at least one credit (hereafter referred to as "heisters") may decide to let its bet ride (and, while doing so, possibly perform some other action), opt out of the heist, or go all-in. (These options are further described below.)
• Based on the number of heisters and the total number of credits they paid, one of five banks is selected on which to stage a heist. This selection affects the individual probability of victory and the odds by which the payout is determined. (The banks are described below.)
• Each heister that did not opt out will, with the bank's (modified) probability, win its stake multiplied by the bank's (modified) betting odds (rounded down), or else lose its stake. Note that each player's success or failure is determined individually--some will succeed where others fail.
• All players, whether they participated or not, succeeded or failed, then receive a paycheck (with exceptions described below).
• Note that it is impossible to permanently leave the game. At worst, a player may have to wait a game to receive its next paycheck.
• After all 1000-1100 games, the player with the greatest number of credits will be declared the winner of that tournament.
• The tournament will be repeated an unspecified number of times (as many as can be computed in 48 hours) and the player earnings across all tournaments summed in order to determine the overall winner of this contest.

### The Second Betting Round

• Any player who has wagered a positive stake in the first round may participate in the second round.
• In this round, a player may:
• reply with the string "back out" to cancel its bet. This will set its wager to zero for the current heist and also slightly decrease the probability the players remaining in the heist will succeed. By taking this option, a player forgoes the 240 credit paycheck that follows the heist as punishment for putting the remaining heisters at risk. (The remaining heisters will succeed with a probability equal to the bank's probability times the fraction of heisters that did not "back out".)
• reply with the string "all in" to blow its entire credit holdings--and take out a payday loan on its next 240 credit paycheck--to buy all the best gear and intel for a heist and go in alone, guns blazing, without relying on anyone. Such a player's victory probability cannot be affected by other heisters dropping out of the heist, nor can its winnings be stolen by double-crossers. Winning payouts will be determined as if its bet were its entire credit holdings plus 240, while loss will set its holdings to zero.
• Reply with any other string (including the empty string) in order to stick with the previous bet and go through with the heist as normal. (Recommended reply: "!guncheck"). Certain replies will have additional side effects:
• A reply of "change jobs" will cause the player to quit its job. Beginning this round, at the end of each round, the player will have a 5% chance to get hired in a new position. If this succeeds, the player is hired and immediately receives its first paycheck. Each new job is guaranteed to pay exactly 5% more than the last (rounded down). This action will succeed whether or not the heist succeeds.
• A reply of "acquire intel" will, if the heist succeeds, cause the player to spend all of its winnings from that heist to get an extra 0.00001 per credit thus spent on the odds for the bank that was heisted for that player only. This odds modification is permanent. As an example, if a player chooses this action when heisting bank 1 and wins 6969 credits in a heist, bank 1's odds for that player will be permanently increased by 0.06969 and the player will receive nothing from that heist.
• A reply of "buy guard" will cause the player to buy off one of the security guards at the bank being heisted. In exchange for a permanent reduction of 1 credit on that player's paycheck (the guard's regular bribe), the player will receive a "permanent" increased victory probability at that bank (due to the guard "forgetting to mention" that player to the cops when asked). The probability will increase by exactly 1% of the difference between the player's current victory probability at that bank and 100%. This action will succeed even if the heist fails. NOTE: If at any time, a player has not enough credits to pay all of its guard bribes, it immediately and "permanently" loses as many probability bonuses as the number of bribes that could not be paid, selected uniformly at random from all bribes registered.
• A reply of "deposit" will, if the heist succeeds, leave a player's entire winnings in an account at the bank heisted. The credits will not be accessible for any purpose nor be counted towards a player's score until withdrawn. This account will pay interest at a rate of 0.14% per game.
• A reply of "withdraw" will, if the heist succeeds, add to a player's winnings the entire contents of its account at the bank heisted. The account will be zeroed as a result. Note that these additional winnings can be stolen by double-crossers.
• A reply of "double cross" will do one of two things:
• If the number of heisters who played "double cross" is at most 1/10th (rounded down) of the total number of non-rabble heisters who decided to go through with the heist (or exactly one if there are less than 10 such players), the player will receive additional winnings equal to the total winnings of all non-double-crossers divided by the number of double-crossers (rounded down). All non-double-crossers in this case receive 0 credits from the heist. In other words, the double-crossers steal everyone else's credits and divide it evenly amongst themselves.
• If the number of heisters who played "double cross" exceeds the threshold, the player will receive no winnings (if its heist was successful), have its paycheck cut in half, and be fired from its job. (See "change jobs".) All non-double-crossers (including rabble) in this case will receive a bonus payment of the total winnings of all double-crossers divided by the total number of non-double-crossers. In other words, the conspiracy got too big to keep a secret, the conspirators were rooted out and excluded from the heist, and everyone divided up their stakes for punishment--and their reputation for dirty dealing lost them their jobs as well.
• A reply of "finger" (as in "fingering a double-crossing scoundrel rat") will, if the heist succeeds, give the player eight opportunities (drawing uniformly with replacement from the set of non-rabble heisters) to identify a double-crosser which has not already been so identified.
• Each double-crosser identified this way will immediately pay the fingerer 25% of its current credit holdings (rounded down) in lieu of being shot, lose its job and have its paycheck cut in half (because the boss won't tolerate bad behavior), and lose 5% of its probability of victory at the bank being heisted (as other heisters are extra suspicious in the future and likely to throw it under the bus if things get hairy). Double-crossers identified this way do not affect whether the double-cross was successful for other double-crossers, but they do not receive any of the stolen credits from the double-cross, and said stolen credits will be redistributed back to the non-double-crossers.
• If no double-crossers are identified this way, the snitch will get stitches for wasting everyone's time--and also pay half its winnings from the current heist, take a 5% cut on its paycheck (boss cuts the tattletale's hours), and lose 5% of its betting odds at the current bank (as other heisters are less likely to be generous/fair with their winnings in the future). The half of its winnings thus lost will be distributed to the unfingered double-crossers if the double-crossers succeeded, or the non-double-crossers (including rabble) if the double-crossers failed.

### The Banks

The bank is selected using the index numheisters + int(totalamountbet/100000), where numheisters is the number of players which wagered a positive stake in round 1 and totalamountbet is the sum of the stakes of all of those players. In other words, a hundred thousand credits is as good as 1 additional heister. Based on this index one of the following banks will be chosen, the bank with the highest threshold that the index meets or exceeds:

Bank             Index Threshold   Victory Prob.  Bet Odds
----             ---------------   -------------  --------
0:Municipal                    0           0.540      0.80
1:City                        20           0.488      1.10
2:State                       40           0.425      1.30
3:National                    60           0.387      1.65
4:Federal Reserve             80           0.324      1.95


Note that as a tournament continues, the probability of reaching the highest bank level will increase, as the amount each player can bet trends upwards. Note also that these are only initial odds and probabilities, before they have been modified by any "acquire intel" or "buy a guard" actions. With the initial probabilities and odds, only the City and National banks have expected winnings exceeding expected losses.

### The Rabble

• The tournament also contains 500 other players, called the "rabble", who participate as regular players in heists but are not scored in the end. These serve to make each game different and somewhat less predictable, and make it possible to reach the riskier/more rewarding banks even with only a few "real" players.
• Each game will include some subset of rabble to participate chosen uniformly at random over all subsets of rabble.
• All rabble use the following strategy:
• Randomly choose to bet with probability equal to the probability of succeeding at the bank that would be selected based on the decisions of the players who have already made their decision this round.
• If betting a nonzero amount, choose the largest of the following amounts that would not exceed its current holdings: 69, 420, 6969, 80085.
• In the second round, "back out" with probability equal to 5% plus 50% of the proportion of bettors that have already backed out, otherwise heist as normal. (Note that this means early players in the second round that back out can have huge cascading effects among the rabble--pay attention and be ready for the heist to collapse before it even starts.)

### Inputs and Outputs

In both rounds, programs will be given the following information, in exactly this order, as command line arguments. Unless otherwise stated, all values are integers containing no decimal.

1. The current game number (zero-indexed)
2. The round number of the current game (1 or 2)
3. The number of players in the current game (including rabble).
4. The number of players that have already taken their turns this round.
5. The number of heisters that have committed a positive stake so far. (In the second round, this will actually be the total number of heisters who bought in in the first round.)
6. The total number of credits invested so far. (In the second round, this will actually be the total number of credits invested in the first round--in particular, it does not include more than the initial stakes of "all in" heisters and does include the stakes of "back out" heisters.)
7. The number of heisters who have confirmed in the second round (i.e. did not "back out"). This will be zero during the first round.
8. The (zero-indexed) number of the bank to be heisted (during the first round, the bank that would be heisted if no one else bet)
9. The number of credits currently available to the player.
10. The number of credits the player bet in the first round. (This is always zero in the first round.)
11. The number of credits the player will receive in its paycheck at the end of each game.
12. 1 if the player is currently receiving a paycheck, 0 if the player is unemployed
13. Player's rank on the leaderboard (not including rabble) as of the end of the previous game, 1-indexed. (Defined as 1+the number of players with strictly more credits at that time. E.g. during the first game, all players have rank 1.)
14. The mean number of credits held by all players (not including rabble) (represented as a decimal number)
15. The mean absolute deviation in the number of credits held by all players (not including rabble) (represented as a decimal number)
16. The maximum number of credits held by any player (i.e. the number of credits held by a rank 1 player, not including rabble)
17. The number of credits the player has stored in bank 0 account
18. Bank 1 account
19. Bank 2 account
20. Bank 3 account
21. Bank 4 account
22. The player's individual probability of victory at bank 0
23. Bank 1 probability
24. Bank 2 probability
25. Bank 3 probability
26. Bank 4 probability
27. The player's individual payout odds upon a successful heist at bank 0
28. Bank 1 odds
29. Bank 2 odds
30. Bank 3 odds
31. Bank 4 odds

In the first round of a game, a player program must print to stdout an integer between 0 and the total number of credits in that player's account. Any bet amount greater than the available credit balance is taken to be a max bet. Any other output (or error condition) will be interpreted as a zero bet.

In the second round of a game, a player program must print to stdout a string as described in the section "The Second Betting Round" above. An error condition here is taken to be the default action: go through with the heist as normal.

## Controller

The tournament will be run using this controller. Bot examples there as well. Still being tested. More code to come. Feel free to submit bug fixes (on github). Will be updated if any rules change as well.

To run a single tournament on your own machine, add a line to competitors.txt and then use:

python bankheist.py 1000


## Contest Rules

• Players may submit any number of player programs in any freely available language the programs of which may receive command line arguments.
• Submissions must include fairly explicit instructions for how to compile programs and run them on my PC, including names of tools needed and exact commands to be issued. Submission must include at least one command which executes the program and may have command line arguments appended directly to it.
• Submissions must also have a unique name to identify them (which contains no spaces).
• Programs must run in a reasonably short amount of time. (I'm not setting an upper bound on what constitutes reasonable. Rather, I will simply advise the creator any entry that seems to have an outsize execution time to speed it up.)
• Programs may not read or write to files. Nor may they use any other method of storing information between runs. The point of this challenge is to make complex decisions on the basis of limited/summarized information.
• Any and all of these rules are subject to change at any time if necessary. A message will be added at the top of this post indicating any such changes.
• This contest ends no sooner than one week after the last user submits posts his or her first submission. Modifications of existing submissions are allowed at any time up until the contest ends. I'll do my best to keep the current deadline updated in a message at the top of this post.
• This contest ends no sooner than one week after the last time the rules were changed, if any. I will do my best to leave comments for any users affected by any rule changes.
• You better bet I'll be participating in this contest myself. ;)
• Comments are not for extended discussion; this conversation has been moved to chat. – Dennis Apr 9 '17 at 4:21
• Maybe because i am not familiar with twitch or the heist game but there is a lot of rules that may best be demonstrated with a small worked example(s). At the moment it seems not very approachable. – Moogie Apr 10 '17 at 5:34
• King of the Hill? I haven't heard that name in years. – Beta Decay Apr 11 '17 at 9:41

# Some "bad" examples of programs.

These are some bots I've written to test the controller. Mostly, they just do the minimum necessary thing to test one specific type of action. Depending on the level of participation in this KotH, anywhere from a few to all of these will be included in the final tournament, since a lot of the strategy in the game will come from dealing with the behaviors of lots of different bots.

I include them all here mostly as "usage" examples.

## Lurker

Never bet. If you're not beating this, rethink your strategy.

print 0


## PassivePanga

Always bet 69.

import sys

round = int(sys.argv[2])
myyattas = int(sys.argv[9])
if round == 1:
if myyattas > 69:
print "69"
else:
print "0"
else:
print "!guncheck"


## KaylorrCriterion

Make a Kelly bet if and only if the Kelly Criterion is met. As this rarely happens with doing some "acquire intel" and "buy guard" first, this generally gets the same result as Lurker.

import sys
import ast
bankholdings = map(int,sys.argv[17:22])
bankprobs = map(float,sys.argv[22:27])
bankodds = map(float,sys.argv[27:32])

def get_bank(bettors,credits):
selector = min(4,int(bettors+int(credits/100000.)/20))
return bankprobs[selector],bankodds[selector]

if round == 1:
if alreadyplayed < 0.37*numplayers or numbet==0:
print 0
else:
bettors = ratiosofar * numplayers
ratesofar = yattasbet/float(numbet)
credits = bettors*ratesofar
p,b = get_bank(bettors,credits)
f = (p*(b+1)-1)/b
print max(int(f*myyattas),0)
#sys.stderr.write("2: %d,%d\n"%(p,b))
else:
if alreadyplayed < 0.37*numbet or numbet==0:
print "!guncheck"
else:
p,b = get_bank(numbet,yattasbet)
f = (realp*(b+1)-(1-240./(myyattas+240.)))/b
print "!guncheck" if f>0 else "back out"


## gunHeCK

Make a Kelly bet if and only if the number of heisters seen so far indicate that the bet will meet the Kelly criterion (but don't back out if wrong). Typically does worse than Lurker

import sys
import ast
bankholdings = map(int,sys.argv[17:22])
bankprobs = map(float,sys.argv[22:27])
bankodds = map(float,sys.argv[27:32])

def get_bank(bettors,credits):
selector = min(4,int(bettors+int(credits/100000.)/20))
return bankprobs[selector],bankodds[selector]

if round == 1:
if alreadyplayed < 0.37*numplayers or numbet==0:
print 0
else:
bettors = ratiosofar * numplayers
ratesofar = yattasbet/float(numbet)
credits = bettors*ratesofar
p,b = get_bank(bettors,credits)
f = (p*(b+1)-1)/b
print max(int(f*myyattas),0)
#sys.stderr.write("2: %d,%d\n"%(p,b))
else:
print "!gunHeCK"


Go "all in" unless already winning.

import sys
import random

round = int(sys.argv[2])
myrank = int(sys.argv[13])
mybet = int(sys.argv[10])

if round == 1:
if random.random()<0.1:
print 1
else:
print 69
else:
if myrank>1:
print "all in"
else:
if mybet==1:
print "back out"
else:
print "!guncheck"


## HeCKuSumer

Always bet a small constant fraction of holdings.

import sys

round = int(sys.argv[2])
myyattas = int(sys.argv[9])

if round==1:
print int(0.1*myyattas)
else:
print "!guncheck"


## OC'sRandomTpyos

Change jobs a lot in early tournament. Spend all that money improving probabilities and odds. Then spend the last few games going all in. This would probably do a lot better without the going all in unless already in a top spot.

import sys
import ast
import random

bankholdings = map(int,sys.argv[17:22])
bankprobs = map(float,sys.argv[22:27])
bankodds = map(float,sys.argv[27:32])

if round == 1:
if game<800 or myrank>3:
print 1
else:
print myyattas/4
else:
if game<800:
if hired:
print "change jobs"
else:
else:
if myrank>3:
print "all in"
else:
print "!guncheck"


## HardHatUmar

Changes jobs whenever possible for most of the tournament. Avoids betting more than the minimum necessary. Does decently well, but not great.

import sys

game = int(sys.argv[1])
round = int(sys.argv[2])
hired = int(sys.argv[12])

if round==1:
if game < 900 and hired:
print 1
else:
print 0
else:
print "change jobs"


Change jobs whenever possible during the first part of the tournament. Spend every other game double crossing. Does decently well even against SnitcherKing. Will probably fare more poorly once many other bots are double crossing and fingering. If not--the rules may need changing.

import sys
import ast

bankholdings = map(int,sys.argv[17:22])
bankprobs = map(float,sys.argv[22:27])
bankodds = map(float,sys.argv[27:32])

if round==1:
print 1
else:
if hired and game<900:
print "change jobs"
else:
print "double cross"


## SnitcherKing

Always bet the minimum, and always finger. Does pretty well in small tournaments that include LimeadeSneaktar.

import sys

round = int(sys.argv[2])

if round == 1:
print 1
else:
print "finger"


## RaysFive01K

A bit more complicated--and therefore actually pretty good. Most of its advantage comes from depositing all winnings in early tournament (protecting them from double crossers) while driving up its win probabilities (and changing jobs to pay for all those guards and heists), then withdrawing them all at the end of the game (once they have earned serious interest and the probability of failing to withdraw is sufficiently low--though here losses to double crossers is a serious risk). This will definitely be in the tournament, and may or may not be a serious contender.

import sys
import ast
import random

bankholdings = map(int,sys.argv[17:22])
bankprobs = map(float,sys.argv[22:27])
bankodds = map(float,sys.argv[27:32])

if round ==1:
if game < 900:
print myyattas/10
else:
print 1
else:
if game < 500 and hired:
elif game < 900:
print "deposit"
elif bankholdings[bankid]>0:
print "withdraw"
else:
print "finger"
else:
print "back out"

• I approve of these nameings <insert Twitch & Panga memes> – CAD97 Apr 5 '17 at 16:10

# Lone John

import sys
import ast

bankholdings = map(int,sys.argv[17:22])
bankprobs = map(float,sys.argv[22:27])
bankodds = map(float,sys.argv[27:32])

if round == 1:
if mycredits > 100 or hired:
print(int(mycredits)/2)
else:
print(0)
else:
if bankprobs[int(bankid)] > 0.6:
print("all in")
elif int(mypayment) > 50 :
elif int(mycredits) > 200 and int(game) < 900 and hired == "1":
print("change jobs")

• Nice. Just as a tip, you don't need to do all those typecasts. Everything is already the right type from being eval'd right off the bat. Which means that the condition hired == "1" will always be false and it will never change jobs. – quintopia May 2 '17 at 3:39