# Gambler's Fallacy dice # note: this does not record list of rolls. only frequencies import random import math import sys NUMFREQ = [1,2,3,4,5,6,5,4,3,2,1] # length 11 def random_fromweight(weight_list, choices): '''Given a list of weights 'weight_list' corresponding to the choices in 'choices', choose an element from 'choices' at random according to the weights in 'weight_list'.''' normalized_weight = [float(j)/sum(weight_list) for j in weight_list] # normalize the weights so they sum to 1 num = random.random() sumweights = 0 for i in range(len(weight_list)): sumweights += normalized_weight[i] if num <= sumweights: return choices[i] def new_weights(freq, rate=300): '''Compute the weights for the next roll, given the current frequency of rolls 'freq'.''' if sum(freq) == 0: return [0]*11 # constant array of zeros return [2**(rate*(float(NUMFREQ[j])/sum(NUMFREQ) - \ float(freq[j])/sum(freq))) for j in range(len(NUMFREQ))] def random_roll(): '''Roll two dice randomly. Return dice roll result.''' return random_fromweight(NUMFREQ,range(2,13)) def dice_roll(counter, freq, rand_prob=0.2, first=3, rate=300): '''Roll the dice, return (dice roll, "string random/weighted"). Update 'freq.''' if counter <= first or random_fromweight([rand_prob,1-rand_prob],[1,0]): # then roll randomly roll = random_roll() rando = "Random" else: roll = random_fromweight(new_weights(freq, rate), range(2,13)) rando = "Weighted history" freq[roll-2] += 1 # increment current frequency return (roll, rando) def modplusone(integer, modulus): '''Return 'integer' mod 'modulus', except 0 is now 'modulus'.''' return ((integer - 1) % modulus) + 1 def main(): '''Main function to run Gambler's Fallacy dice.''' # parameter default values rate = 300 first = 3 rand_prob = 0.2 counter = 1 cur_freq = [0]*11 verbose = False veryverbose = False while 1: command = raw_input('Player %i ' % modplusone(counter, 4)) if command == '': # roll the dice this_roll = dice_roll(counter, cur_freq, rand_prob, first, rate) if verbose: print str(this_roll[0]) + ' ' + this_roll[1] else: print str(this_roll[0]) if veryverbose: print 'Roll number: %i' % counter print cur_freq print [float(i)/sum(cur_freq) for i in cur_freq] print new_weights(cur_freq) print '\n' counter += 1 # verbose and quiet modes: elif command == 'v': print 'Starting verbose mode. Type of roll will be printed.' verbose = True elif command == 'q': print 'Starting quiet mode' verbose = False veryverbose = False elif command == 'vv': print 'Starting very verbose mode. Roll number, type of roll, \n\ current count and frequency, \n\ and new weights will be printed. \n\ Only recommended for debugging.' verbose = True veryverbose = True # changing parameters: elif command == 'c': print 'Press Enter to leave the current value' try: temp = raw_input('Enter RATE (current is %f) ' %float(rate)) if temp != '': rate = float(temp) temp = raw_input('Enter FIRST (current is the first %i rolls random) '\ %first) if temp != '': first = int(temp) temp = raw_input('Enter probability of random roll (current is %f) ' %rand_prob) if temp != '': rand_prob = float(temp) if rate < 0 or first < 1 or rand_prob < 0 or rand_prob > 1: raise ValueError('Invalid range') print '\n' except: print 'Invalid parameter. RATE must be positive float\n\ FIRST a positive integer\n\ RAND a float between 0 and 1\n' # usage elif command == 'help': usage() # undo roll elif command == 'u': pass # need to keep track of list of rolls for this... # to quit: elif command == 'quit' or command == 'exit': break def usage(): print 'usage: python %s' % sys.argv[0] print 'Options:' print 'q = quiet (default)' print 'v = verbose, vv = very verbose (for debugging)' print 'c = change parameters' print 'help = show usage' print 'u = undo last move' print 'quit (or exit) as expected\n' if __name__ == '__main__': try: # TODO number of players, player names usage() main() except: print sys.argv usage() sys.exit(1)