Mountebank

spinner.py

Feb 26th, 2015
722
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.24 KB | None | 0 0
  1. from collections import defaultdict
  2. from pprint import pprint
  3.  
  4. # From http://code.activestate.com/recipes/578231-probably-the-fastest-memoization-decorator-in-the-/
  5. def memodict(f):
  6.     """ Memoization decorator for a function taking a single argument """
  7.     class memodict(dict):
  8.         def __missing__(self, key):
  9.             ret = self[key] = f(key)
  10.             return ret
  11.     return memodict().__getitem__
  12.  
  13. # We assume that stuck spinners will never move again
  14. # We mark this case with tick count = -1
  15. # 1 tick = 2 frames
  16.  
  17. # Helper function to compute base distribution when spinner tick count reaches 0
  18. def tick_distribution():
  19.     out = {}
  20.     for x in range(32):
  21.         u = x
  22.         if x == 0:
  23.             u = -1
  24.         out[u] = 1.0 / 32
  25.     return out
  26. base_tick_distribution = tick_distribution()
  27.  
  28. DOWN = 0
  29. UP = 1
  30. LEFT = 2
  31. RIGHT = 3
  32. # Helper function to compute probability of new direction given old direction
  33. def direction_distribution():
  34.     out = {}
  35.     for direction in range(4):
  36.         t = {}
  37.         opposite = (direction + 2) % 4
  38.         t[3 - direction] = 1.0 / 2
  39.         t[opposite] = 1.0 / 4
  40.         t[3 - opposite] = 1.0 / 4
  41.         out[direction] = t
  42.     return out
  43. base_direction_distribution = direction_distribution()
  44.  
  45. # Given current state, compute distribution for next state
  46. # State is of the form (direction, n_ticks)
  47. @memodict
  48. def next_state(state):
  49.     out = {}
  50.     if state[1] != 0:
  51.         # Decrement n_ticks
  52.         # Handle n_ticks == -1 case to not change state
  53.         new_state = (state[0], max(-1, state[1] - 1))
  54.         out[new_state] = 1
  55.     else:
  56.         # Use the base distributions computed earlier to get new distribution
  57.         new_directions = base_direction_distribution[state[0]]
  58.         new_ticks = base_tick_distribution
  59.         for direction, p in new_directions.iteritems():
  60.             for n_ticks, q in base_tick_distribution.iteritems():
  61.                 new_state = (direction, n_ticks)
  62.                 out[new_state] = p * q
  63.     return out
  64.  
  65. # Given current state distribution, compute next state distribution
  66. def next_distribution(distribution):
  67.     out = defaultdict(float)
  68.     for state, p in distribution.iteritems():
  69.         d = next_state(state)
  70.         for new_state, q in d.iteritems():
  71.             out[new_state] += p * q
  72.     return out
  73.  
  74. # Computes distribution given observation of spinner in given direction for n ticks
  75. # Possible values of hidden state n_ticks: n_ticks >= n or n_ticks == -1
  76. def start_distribution(direction, n):
  77.     out = {}
  78.     # Annoying case
  79.     if n == 0:
  80.         for x in base_tick_distribution:
  81.             out[(direction, x)] = 1.0 / 32
  82.     else:
  83.         n_possibilities = max(0, 32 - n)
  84.         for i in range(-1, n_possibilities):
  85.             out[(direction, i)] = 1.0 / (n_possibilities + 1)
  86.     return out
  87.  
  88. # Remove hidden n_ticks part of state
  89. # Compute distribution based only on direction
  90. def compress(dist):
  91.     out = [0.0, 0.0, 0.0, 0.0]
  92.     for state, p in dist.iteritems():
  93.         out[state[0]] += p
  94.     return out
  95.  
  96. # Given start state corresponding to d, n
  97. # After up to t ticks, computes direction distribution
  98. def chain(d, n, t):
  99.     out = []
  100.     s = start_distribution(d, n)
  101.     for i in range(t+1):
  102.         out.append(compress(s))
  103.         s = next_distribution(s)
  104.     return out
  105.  
  106. def main():
  107.     # Collect some probabilities and dump them in files
  108.     # Yes this is ugly but i dont care
  109.     bike_file = open('bike.txt', 'w')
  110.     walk_file = open('walk.txt', 'w')
  111.     bike_file.write('\t| down\t| up\t| left\t| right\n')
  112.     bike_file.write('------------------------------------------\n')
  113.     walk_file.write('\t| down\t| up\t| left\t| right\n')
  114.     walk_file.write('------------------------------------------\n')
  115.     # Without loss of generality, suppose that we cross below the spinner
  116.     for n_ticks in range(32):
  117.         # Multiply by 2 to convert to frames
  118.         bike_file.write('%d' % (2*n_ticks))
  119.         walk_file.write('%d' % (2*n_ticks))
  120.         for start_direction in range(4):
  121.             bike_file.write('\t| ')
  122.             walk_file.write('\t| ')
  123.             x = chain(start_direction, n_ticks, 9)
  124.             # x[k][DOWN] = probability of spinner facing down after k ticks
  125.             # 8 frames = 4 ticks per bike, yet for some reason it actually takes 5...
  126.             bike_file.write('%.3f' % x[5][DOWN])
  127.             # 16 frames = 8 ticks per step, yet for some reason it actually takes 9...
  128.             walk_file.write('%.3f' % x[9][DOWN])
  129.         # so much copypasta
  130.         bike_file.write('\n')
  131.         walk_file.write('\n')
  132.     bike_file.close()
  133.     walk_file.close()
  134.  
  135. if __name__ == '__main__':
  136.     main()
Advertisement
Add Comment
Please, Sign In to add comment