Smallest unique number KoTH
BayesBot
Tries to make the optimal choice using a simple statistical model.
import random
def dirichlet(counts):
counts = [random.gammavariate(n, 1) for n in counts]
k = 1. / sum(counts)
return [n * k for n in counts]
class BayesBot(object):
def __init__(self, index):
self.index = index
self.counts = [[0.2 * (10 - i) for i in range(10)] for _ in range(10)]
def select(self):
player_distributions = []
for i, counts in enumerate(self.counts):
if i == self.index:
continue
player_distributions.append(dirichlet(counts))
cumulative_unique = 0.
scores = [0.] * 10
for i in range(10):
p_unpicked = 1.
for d in player_distributions:
p_unpicked *= (1. - d[i])
p_unique = p_unpicked * sum(d[i] / (1. - d[i]) for d in player_distributions)
scores[i] = p_unpicked * (1. - cumulative_unique)
cumulative_unique += p_unique * (1. - cumulative_unique)
return scores.index(max(scores)) + 1
def update(self, choices):
for i, n in enumerate(choices):
self.counts[i][n - 1] += 1
Avoid Constant Bots
Keep track of which bots have always returned the same value, and skip those values. Of the remaining values, select them randomly, but biased significantly towards lower values.
import numpy as np
class AvoidConstantBots(object):
all_values = range(1, 11)
def __init__(self, index):
self.index = index
self.constant_choices = None
def select(self):
available = set(self.all_values)
if self.constant_choices is not None:
available -= set(self.constant_choices)
if len(available) == 0:
available = set(self.all_values)
values = np.array(sorted(available))
weights = 1. / (np.arange(1, len(values) + 1)) ** 1.5
weights /= sum(weights)
return np.random.choice(sorted(available), p=weights)
def update(self, choices):
if self.constant_choices is None:
self.constant_choices = choices[:]
self.constant_choices[self.index] = None
else:
for i, choice in enumerate(choices):
if self.constant_choices[i] != choice:
self.constant_choices[i] = None
WaitWhatBot
Not the most competitive bot and definitely not GTO, but will stifle the score of any "always 1" or "nearly always 1" opponent in the same game as in such a scenario WaitWhatBot becomes such a bot too.
Uses evolving probabilities with weighted weights both in time (more recent -> greater weight) and choice value (lower point -> greater weight).
Uses somewhat obfuscated code for a bit of a giggle.
from random import choices as weightWeight
class WaitWhatBot(object):
def __init__(wait,what):
weight,weightWhat=5,2
wait.what,wait.weight=what,(weight**(weight/weight/weightWhat)+weightWhat/weightWhat)/weightWhat
wait.whatWeight,wait.weightWeight=[wait.what==wait.weight]*int(wait.weight**weight),wait.weight
wait.whatWhat=wait.whatWeight.pop()#wait, when we pop weight off whatWeight what weight will pop?
wait.waitWait=tuple(zip(*enumerate(wait.whatWeight,wait.weightWeight!=wait.whatWeight)))[weightWeight==wait.weight]
def select(what):return int(what.weight**what.whatWhat if all(not waitWait for waitWait in what.whatWeight)else weightWeight(what.waitWait,what.whatWeight)[what.weight==what.what])
def update(waitWhat,whatWait):
what,wait,weightWhat=set(wait for wait in whatWait[:waitWhat.what]+whatWait[waitWhat.what+1:]if wait in waitWhat.waitWait),-~waitWhat.whatWhat,waitWhat.weightWeight
while wait not in what:
waitWhat.whatWeight[wait+~waitWhat.whatWhat]+=weightWhat
weightWhat/=waitWhat.weight
wait-=~waitWhat.whatWhat
if not wait!=(what!=weightWhat):waitWhat.whatWeight[waitWhat.whatWhat]+=weightWhat
waitWhat.weightWeight*=waitWhat.weight