Skip to content

Instantly share code, notes, and snippets.

@bonnici
Created December 2, 2012 01:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bonnici/4186279 to your computer and use it in GitHub Desktop.
Save bonnici/4186279 to your computer and use it in GitHub Desktop.
Quick-and-Dirty Brute Force Risk vs Reward bot
# Quick-and-Dirty Brute Force Risk vs Reward bot
# Before each roll (except the first), do a brute force calculation of risk vs reward. In this case,
# risk is the probability of having 3 shotgun dice after the roll, and reward is the probability of
# getting an extra 1, 2, or 3 brains. If P(death) - rewardBonus[1]*P(1 brain) -
# rewardBonus[2]*P(2 brain) - rewardBonus[3]*P(3 brain) > rvrCutoff, then roll.
# If behindBonus and/or aheadPenalty are not zero, then those amounts are added to or subtracted from
# the risk vs reward depending on weather the bot is winning or losing, encouraging risky play when
# behind and safer play when ahead
# Based purely on trial-and-error, the following configurations seem to work best:
# ZombieBot_QaDBFRvR('QaDBFRvR A', 0.125, [0.11,0.11,0.11], 0.025, 0),
# ZombieBot_QaDBFRvR('QaDBFRvR B', 0.19, [0.1,0.15,0.2], 0, 0.1),
# ZombieBot_QaDBFRvR('QaDBFRvR C', 0.19, [0.1,0.1,0.1], 0, 0.1),
class ZombieBot_QaDBFRvR(object):
def __init__(self, name, rvrCutoff, rewardBonus, behindBonus, aheadPenalty):
self.name = name
self.rvrCutoff = rvrCutoff
self.rewardBonus = rewardBonus
self.behindBonus = behindBonus
self.aheadPenalty = aheadPenalty
# Precalcualted roll probabilities, given colour and face
self.rollProbabilities = {
GREEN: {BRAINS: 0.5, FOOTSTEPS: 1.0/3, SHOTGUN: 1.0/6},
YELLOW: {BRAINS: 1.0/3, FOOTSTEPS: 1.0/3, SHOTGUN: 1.0/3},
RED: {BRAINS: 1.0/6, FOOTSTEPS: 1.0/3, SHOTGUN: 0.5}
}
def initState(self):
self.diceLeft = {GREEN: 6, YELLOW: 4, RED: 3}
self.shotguns = 0
self.footsteps = [] # list of dice colours
self.brains = [] # list of dice colours
self.rollNumber = 1
def updateState(self, results):
oldFootsteps = self.footsteps[:]
self.footsteps = []
for i in results:
if i[ICON] == SHOTGUN:
self.shotguns += 1
if i[ICON] == FOOTSTEPS:
self.footsteps += [i[COLOR]]
if i[ICON] == BRAINS:
self.brains += [i[COLOR]]
self.diceLeft[i[COLOR]] -= 1
# "Add back" the old set of footsteps, since they were already in our hand and were not
# taken out of the dice jar
for colour in oldFootsteps:
self.diceLeft[colour] += 1
def printState(self, gameState):
print('%s round %d: Shotguns: %d, Footsteps: %s, Dice Left: G=%d, Y=%d, R=%d' % (
self.name, gameState['round'], self.shotguns, self.footsteps,
self.diceLeft[GREEN], self.diceLeft[YELLOW], self.diceLeft[RED]))
def putBackBrainDice(self):
for colour in self.brains:
self.diceLeft[colour] += 1
self.brains = []
# Generate a list of all possible combinations of the colours of dice that can come out of a roll.
# This only calculates the combinations of the dice left over when you take into account the
# footstep dice that are left over from the last roll
def generateColourCombinations(self):
dice = [GREEN]*self.diceLeft[GREEN] + [YELLOW]*self.diceLeft[YELLOW] + [RED]*self.diceLeft[RED]
return itertools.combinations(dice, 3 - len(self.footsteps))
# No indices because I'm lazy,
# 0 = probability of having 3 or more shotguns after the roll
# 1 = probability of having 1 additional brain after the roll
# 2 = probability of having 2 additional brains after the roll
# 3 = probability of having 3 additional brains after the roll
def calculateProbabilites(self):
probabilities = [0, 0, 0, 0]
# All possible permutations of a roll with repeated elements (e.g. SSS, SSB, SBS, BSS)
rollPermutations = itertools.product([SHOTGUN, FOOTSTEPS, BRAINS], repeat=3)
# All possible combinations of dice colours
colourCombinations = self.generateColourCombinations()
# For each possible combination of dice colours, add to the probabilites
for combination in colourCombinations:
colours = self.footsteps + list(combination)
# Calculate the probability of the roll, given the colours
for roll in rollPermutations:
probability = 1;
for (colour, die) in zip(colours, roll):
x = self.rollProbabilities[colour][die]
probability *= x
# Chance of death
shotgunsInRoll = roll.count(SHOTGUN)
if (shotgunsInRoll + self.shotguns >= 3):
probabilities[0] += probability
# Chance of any number of brains
brainsInRoll = roll.count(BRAINS)
if (brainsInRoll > 0):
probabilities[brainsInRoll] += probability
return probabilities
def turn(self, gameState):
self.initState()
shouldRoll = True
while shouldRoll and self.shotguns < 3:
# Before the roll, make sure we have enough dice left over
numDiceLeft = self.diceLeft[GREEN] + self.diceLeft[YELLOW] + self.diceLeft[RED]
numDiceNeeded = 3 - len(self.footsteps)
if (numDiceNeeded > numDiceLeft):
self.putBackBrainDice()
results = roll()
self.rollNumber += 1
self.updateState(results)
probabilities = self.calculateProbabilites()
risk = probabilities[0]
reward = (self.rewardBonus[0]*probabilities[1] +
self.rewardBonus[1]*probabilities[2] +
self.rewardBonus[2]*probabilities[3])
maxScore = max(gameState[SCORES].items())
myScore = gameState[SCORES][self.name]
if (myScore > maxScore):
# Add risk (play safer)
risk += self.aheadPenalty
elif (myScore < maxScore):
# Add reward (play riskier)
reward += self.behindBonus
shouldRoll = (risk - reward) < self.rvrCutoff
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment