Sophie

Sophie

distrib > Mandriva > 2010.2 > i586 > media > contrib-backports > by-pkgid > df29c83ca401d91ec9c00bfcf7fea4ea > files > 175

shedskin-0.8-2mdv2010.2.i586.rpm

# Recipe 496907: Mastermind-style code-breaking, by Raymond Hettinger
# http://code.activestate.com/recipes/496907/
# Version speed up and adapted to Psyco D by leonardo maffi, V.1.0, Apr 4 2009

import random
from math import log
from collections import defaultdict
from time import clock


DIGITS = 4
TRIALS = 1
fmt = '%0' + str(DIGITS) + 'd'
searchspace = [[int(f) for f in fmt % i] for i in xrange(0, 10 ** DIGITS)]
count1 = [0] * 10
count2 = [0] * 10

def compare(a, b):
    N = 10
    for i in xrange(N):
        count1[i] = 0
        count2[i] = 0

    strikes = 0
    i = 0
    for dig1 in a:
        dig2 = b[i]
        i += 1
        if dig1 == dig2:
            strikes += 1
        count1[dig1] += 1
        count2[dig2] += 1

    balls  = (count1[0] if count1[0] < count2[0] else count2[0])
    balls += (count1[1] if count1[1] < count2[1] else count2[1])
    balls += (count1[2] if count1[2] < count2[2] else count2[2])
    balls += (count1[3] if count1[3] < count2[3] else count2[3])
    balls += (count1[4] if count1[4] < count2[4] else count2[4])
    balls += (count1[5] if count1[5] < count2[5] else count2[5])
    balls += (count1[6] if count1[6] < count2[6] else count2[6])
    balls += (count1[7] if count1[7] < count2[7] else count2[7])
    balls += (count1[8] if count1[8] < count2[8] else count2[8])
    balls += (count1[9] if count1[9] < count2[9] else count2[9])

    return (strikes << 16) | (balls - strikes)


def rungame(target, strategy, verbose=True, maxtries=15):
    possibles = 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 >> 16) == DIGITS: # score >> 16 is strikes
            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 utility(play, possibles):
    b = defaultdict(int)
    for poss in possibles:
        b[compare(play, poss)] += 1

    # info
    bits = 0
    s = float(len(possibles))
    for i in b.itervalues():
        p = i / s
        bits -= p * log(p, 2)
    return bits

def nodup(play):
    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 tally[1:]

def eval_strategy(name, strategy):
    start = clock()
    data = [rungame(random.choice(searchspace), strategy, verbose=False) for i in xrange(TRIALS)]
    print 'mean=%.2f %r  %s n=%d dig=%d' % (average(data), counts(data), name, len(data), DIGITS)
    print 'Time elapsed %.2f' % (clock() - start,)

def main():
    random.seed(1)
    print '-' * 60

    names = "s_bestinfo s_samplebest s_worstinfo s_allrand s_trynodup s_bestinfo".split()
    eval_strategy('s_bestinfo', s_bestinfo)
    eval_strategy('s_samplebest', s_samplebest)
    eval_strategy('s_worstinfo', s_worstinfo)
    eval_strategy('s_trynodup', s_trynodup)
    eval_strategy('s_allrand', s_allrand)

if __name__ == '__main__':
    main()