Mountebank

updated spinner.py script

Jan 6th, 2019
670
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.54 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 = 0
  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.         out[x] = 1.0 / 32
  22.     return out
  23. base_tick_distribution = tick_distribution()
  24.  
  25. DOWN = 0
  26. UP = 1
  27. LEFT = 2
  28. RIGHT = 3
  29. # Helper function to compute probability of new direction given old direction
  30. def direction_distribution():
  31.     out = {}
  32.     for direction in range(4):
  33.         t = {}
  34.         opposite = (direction + 2) % 4
  35.         t[3 - direction] = 1.0 / 2
  36.         t[opposite] = 1.0 / 4
  37.         t[3 - opposite] = 1.0 / 4
  38.         out[direction] = t
  39.     return out
  40. base_direction_distribution = direction_distribution()
  41.  
  42. # Given current state, compute distribution for next state
  43. # State is of the form (direction, n_ticks)
  44. @memodict
  45. def next_state(state):
  46.     out = {}
  47.     if state[1] != 1:
  48.         # Decrement n_ticks
  49.         # Handle n_ticks == 0 case to not change state
  50.         new_state = (state[0], max(0, state[1] - 1))
  51.         out[new_state] = 1
  52.     else:
  53.         # Use the base distributions computed earlier to get new distribution
  54.         new_directions = base_direction_distribution[state[0]]
  55.         new_ticks = base_tick_distribution
  56.         for direction, p in new_directions.iteritems():
  57.             for n_ticks, q in base_tick_distribution.iteritems():
  58.                 new_state = (direction, n_ticks)
  59.                 out[new_state] = p * q
  60.     return out
  61.  
  62. # Given current state distribution, compute next state distribution
  63. def next_distribution(distribution):
  64.     out = defaultdict(float)
  65.     for state, p in distribution.iteritems():
  66.         d = next_state(state)
  67.         for new_state, q in d.iteritems():
  68.                 out[new_state] += p * q
  69.     return out
  70.  
  71. # Computes distribution given observation of spinner in given direction for n ticks
  72. # Possible values of initial hidden state n_ticks: n_ticks > n or n_ticks == 0
  73. # If n >= 31, then the spinner is guaranteed to be stuck.
  74. def start_distribution(direction, n):
  75.     out = {}
  76.     n_possibilities = max(1, 32 - n)
  77.     for i in range(n_possibilities):
  78.         out[(direction, i)] = 1.0 / n_possibilities
  79.     return out
  80.  
  81. # Remove hidden n_ticks part of state
  82. # Compute distribution based only on direction
  83. def compress(dist):
  84.     out = [0.0, 0.0, 0.0, 0.0]
  85.     for state, p in dist.iteritems():
  86.             out[state[0]] += p
  87.     return out
  88.  
  89. # Given start state corresponding to d, n
  90. # After up to t ticks, computes direction distribution
  91. def chain(d, n, t):
  92.     out = []
  93.     s = start_distribution(d, n)
  94.     for i in range(t+1):
  95.         out.append(compress(s))
  96.         s = next_distribution(s)
  97.     return out
  98.  
  99. def main():
  100.     # Collect some probabilities and dump them in files
  101.     # Yes this is ugly but i dont care
  102.     bike_file = open('bike.txt', 'w')
  103.     walk_file = open('walk.txt', 'w')
  104.     bike_file.write('\t| down\t| up\t| left\t| right\n')
  105.     bike_file.write('------------------------------------------\n')
  106.     walk_file.write('\t| down\t| up\t| left\t| right\n')
  107.     walk_file.write('------------------------------------------\n')
  108.     # Without loss of generality, suppose that we cross below the spinner
  109.     for n_ticks in range(32):
  110.         # Multiply by 2 to convert to frames
  111.         bike_file.write('%d' % (2*n_ticks))
  112.         walk_file.write('%d' % (2*n_ticks))
  113.         for start_direction in range(4):
  114.             bike_file.write('\t| ')
  115.             walk_file.write('\t| ')
  116.             x = chain(start_direction, n_ticks, 9)
  117.             # x[k][DOWN] = probability of spinner facing down after k ticks
  118.             # 8 frames = 4 ticks per bike, yet for some reason it actually takes 5...
  119.             bike_file.write('%.3f' % x[5][DOWN])
  120.             # 16 frames = 8 ticks per step, yet for some reason it actually takes 9...
  121.             walk_file.write('%.3f' % x[9][DOWN])
  122.         # so much copypasta
  123.         bike_file.write('\n')
  124.         walk_file.write('\n')
  125.     bike_file.close()
  126.     walk_file.close()
  127.  
  128. if __name__ == '__main__':
  129.     main()
Advertisement
Add Comment
Please, Sign In to add comment