import random
from itertools import izip, imap
from math import log
digits = 4
trials = 100
fmt = '%0' + str(digits) + 'd'
searchspace = tuple([tuple(map(int,fmt % i)) for i in range(0,10**digits)])
def compare(a, b, imap=imap, sum=sum, izip=izip, min=min):
count1 = [0] * 10
count2 = [0] * 10
strikes = 0
for dig1, dig2 in izip(a,b):
if dig1 == dig2:
strikes += 1
count1[dig1] += 1
count2[dig2] += 1
balls = sum(imap(min, count1, count2)) - strikes
return (strikes, balls)
def rungame(target, strategy, verbose=True, maxtries=15):
possibles = list(searchspace)
for i in xrange(maxtries):
g = strategy(i, possibles)
if verbose:
print "Out of %7d possibilities. I'll guess %r" % (len(possibles), g),
score = compare(g, target)
if verbose:
print ' ---> ', score
if score[0] == digits:
if verbose:
print "That's it. After %d tries, I won." % (i+1,)
break
possibles = [n for n in possibles if compare(g, n) == score]
return i+1
############################################### Strategy support
def info(seqn):
bits = 0
s = float(sum(seqn))
for i in seqn:
p = i / s
bits -= p * log(p, 2)
return bits
def utility(play, possibles):
b = {}
for poss in possibles:
score = compare(play, poss)
b[score] = b.get(score, 0) + 1
return info(b.values())
def hasdup(play, set=set, digits=digits):
return len(set(play)) != digits
def nodup(play, set=set, digits=digits):
return len(set(play)) == digits
################################################ Strategies
def s_allrand(i, possibles):
return random.choice(possibles)
def s_trynodup(i, possibles):
for j in xrange(20):
g = random.choice(possibles)
if nodup(g):
break
return g
def s_bestinfo(i, possibles):
if i == 0:
return s_trynodup(i, possibles)
plays = random.sample(possibles, min(20, len(possibles)))
_, play = max([(utility(play, possibles), play) for play in plays])
return play
def s_worstinfo(i, possibles):
if i == 0:
return s_trynodup(i, possibles)
plays = random.sample(possibles, min(20, len(possibles)))
_, play = min([(utility(play, possibles), play) for play in plays])
return play
def s_samplebest(i, possibles):
if i == 0:
return s_trynodup(i, possibles)
if len(possibles) > 150:
possibles = random.sample(possibles, 150)
plays = possibles[:20]
elif len(possibles) > 20:
plays = random.sample(possibles, 20)
else:
plays = possibles
_, play = max([(utility(play, possibles), play) for play in plays])
return play
## Evaluate Strategies
def average(seqn):
return sum(seqn) / float(len(seqn))
def counts(seqn):
limit = max(10, max(seqn)) + 1
tally = [0] * limit
for i in seqn:
tally[i] += 1
return tuple(tally[1:])
from time import clock
print '-' * 60
##for ss in searchspace, filter(hasdup, searchspace), filter(nodup, searchspace):
## print 'spacesize:', len(ss), ' = ', log(len(ss),2), 'bits'
##for play in [(1,2,3,4,5), (1,1,2,3,4), (1,1,1,2,3), (1,1,2,2,3), (1,1,1,2,2)]:
## print 'First play:', play, 'has info content', utility(play, searchspace)
## print 'First play:', play, 'has info content', utility(play, filter(hasdup, searchspace)), 'against dups'
## print 'First play:', play, 'has info content', utility(play, filter(nodup, searchspace)), 'against nodups'
## print
for strategy in (s_bestinfo, s_samplebest, s_worstinfo, s_allrand, s_trynodup, s_bestinfo):
start = clock()
data = [rungame(random.choice(searchspace), strategy, verbose=False) for i in xrange(trials)]
#print '[%d %d| median %.2f mean %.2f |%d %d] dig=%d n=%d %s' % (min(data), lq(data), median(data),average(data), uq(data), max(data), digits, len(data), strategy.__name__)
print 'mean=%.2f %r %s n=%d dig=%d' % (average(data), counts(data), strategy.__name__, len(data), digits)
print 'Time elapsed %.2f' % (clock() - start,)
""" Analysis of strategies with four digit targets
s_worstinfo [3 | median 7.00 mean 6.76 | 10] n=120
s_allrand [3 | median 6.00 mean 6.19 | 9] n=120
s_firstnodup [3 | median 6.00 mean 6.24 | 9] n=120
s_trynodup [3 | median 6.00 mean 5.99 | 9] n=120
s_bestinfo [2 | median 6.00 mean 5.75 | 10] n=120
s_worstinfo [3 | median 7.00 mean 6.86 | 10] n=200
s_allrand [4 | median 6.00 mean 6.52 | 10] n=200
s_firstnodup [1 | median 6.00 mean 6.19 | 10] n=200
s_firstdup_restnodup [3 | median 6.00 mean 6.13 | 10] n=200
s_trynodup [3 | median 6.00 mean 6.11 | 10] n=200
s_bestinfo [2 | median 6.00 mean 5.73 | 9] n=200
[3 6| median 7.00 mean 6.78 |8 11] dig=4 n=200 s_worstinfo 6.77
[3 5| median 6.00 mean 6.24 |7 10] dig=4 n=200 s_allrand 6.38
[2 6| median 6.00 mean 6.26 |7 9] dig=4 n=200 s_firstnodup 6.22
[2 5| median 6.00 mean 5.93 |7 9] dig=4 n=200 s_firstdup_restnodup 6.03
[4 5| median 6.00 mean 6.14 |7 9] dig=4 n=200 s_trynodup 6.12
[3 5| median 6.00 mean 5.87 |6 9] dig=4 n=200 s_bestinfo 5.74
[1 6| median 7.00 mean 6.93 |8 11] dig=4 n=500 s_worstinfo
[2 5| median 6.00 mean 6.11 |7 10] dig=4 n=500 s_allrand
[3 5| median 6.00 mean 6.12 |7 10] dig=4 n=500 s_firstnodup
[2 5| median 6.00 mean 6.07 |7 10] dig=4 n=500 s_firstdup_restnodup
[2 5| median 6.00 mean 6.03 |7 9] dig=4 n=500 s_trynodup
[2 5| median 6.00 mean 5.82 |6 9] dig=4 n=500 s_bestinfo
[2 5| median 6.00 mean 5.82 |6 9] dig=4 n=500 s_bestinfo_randfirst
spacesize: 100000 = 16.6096404744 bits
spacesize: 69760 = 16.0901124197 bits
spacesize: 30240 = 14.8841705191 bits
First play: (1, 2, 3, 4, 5) has info content 2.72731327592
First play: (1, 2, 3, 4, 5) has info content 2.54363363003 against dups
First play: (1, 2, 3, 4, 5) has info content 2.77115216576 against nodups
First play: (1, 1, 2, 3, 4) has info content 2.57287940403
First play: (1, 1, 2, 3, 4) has info content 2.51059435464 against dups
First play: (1, 1, 2, 3, 4) has info content 2.56508152257 against nodups
First play: (1, 1, 1, 2, 3) has info content 2.16683284403
First play: (1, 1, 1, 2, 3) has info content 2.11526590293 against dups
First play: (1, 1, 1, 2, 3) has info content 2.13291456398 against nodups
First play: (1, 1, 2, 2, 3) has info content 2.28656723529
First play: (1, 1, 2, 2, 3) has info content 2.28606417747 against dups
First play: (1, 1, 2, 2, 3) has info content 2.14424289741 against nodups
First play: (1, 1, 1, 2, 2) has info content 2.16683284403
First play: (1, 1, 1, 2, 2) has info content 2.11526590293 against dups
First play: (1, 1, 1, 2, 2) has info content 2.13291456398 against nodups
mean=6.22 (0, 0, 0, 4, 33, 130, 72, 9, 2, 0) s_samplebest n=250 dig=5
Time elapsed 1395.75
"""