 # Histogram_Dots_1.1.py

Oct 11th, 2014
204
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
1. # Random dots, with Histogram 1.1
2. # By CA7746
3. # Based on Random Dots 1.0 by Jason Thibeault
4. #     http://freethoughtblogs.com/lousycanuck/2014/10/09/busy-busy-worker-bee/
5.
6. # Usage
7. #
8. # A wave of random dots appears. Then they fall. As they do, the average
9. # X position among dots in that wave is added to a bar chart, a histogram,
10. # showing how often waves with each average have appeared. As more waves are
11. # independently measured, the frequency of their averages evens out into a
12. # bell curve: a demo of the Central Limit Theorem.
13. #
14. # The chart gets re-scaled to fit the tallest bar on the screen.
15. #
16. # Mouse click - Spawn a new wave of dots.
17. #               You can accumulate them with gravity off, or make it rain.
18. #               Click Click Click Click...
19. #
20. # Caps lock - Toggle gravity for current and future waves.
21. #
22. # Shift - Tap to make only the current dots fall.
23. #
24. # Space - Reset everything and add a fresh wave of dots.
25. #
26. # Escape - Exit
27.
28. # Requires
29. #
30. # PyGame - http://www.pygame.org
31.
32.
33. # Settable variables
34. dots_per_wave = 5         # Small waves start off more interesting
35. gravity = 0.5             # Continually added to falling dots' velocity
36. histogram_divisions = 20
37.
38.
39. # Imports
40. import pygame
41. from pygame.locals import *
42. import random
43. import time
44.
45. # Defines
46. bg = None
47. clock = None
48.
49.
50. def init_game():
51.     global bg, clock
52.     pygame.init()
53.
54.     display_info = pygame.display.Info()
55.     screen_width,screen_height = display_info.current_w, display_info.current_h
56.
57.     bg = pygame.display.set_mode((screen_width,screen_height), FULLSCREEN, 32)
58.     clock = pygame.time.Clock()
59.
60.     pygame.mouse.set_visible(False)
61.
62.
63. def run_game():
64.     display_info = pygame.display.Info()
65.     screen_width,screen_height = display_info.current_w, display_info.current_h
66.
67.     bg_color1 = (255, 255, 255)
68.     label_color = (240, 100, 0)  # Ehh, a different color might be more readable
69.                                  # And/or draw a filled rect behind the text for contrast
70.                                  # Wasn't too awful when grayscaling a screenshot to check
71.
72.     # Create a cached bitmap to share among all dots
73.     dot_image = pygame.Surface((8, 8), pygame.SRCALPHA, 32)
74.     pygame.draw.circle(dot_image, (0, 0, 128), [4, 4], 4, 0)
75.
76.     myfont = pygame.font.SysFont("monospace", 30, bold=True)
77.     x_axis_label = myfont.render("Mean X position from each wave", True, label_color)
78.     y_axis_label = myfont.render("# of waves that had a given mean", True, label_color)
79.     y_axis_label = pygame.transform.rotate(y_axis_label, -90)
80.
81.     waves = []
82.
83.     histogram = Histogram(pygame.Rect(0, 0, screen_width, screen_height), histogram_divisions)
84.
85.     # Loop forever
86.     running = True
87.     while (running):
88.         pygame.event.pump()
89.
90.         # Look for quit and mouseclick
91.         for e in pygame.event.get():
92.             if (e.type == pygame.QUIT):
93.                 running = False
94.                 break
95.             elif (e.type == pygame.KEYDOWN):
96.                 if (e.key == pygame.K_ESCAPE):
97.                     running = False
98.                     break
99.
100.                 elif (e.key == pygame.K_SPACE):
101.                     # Clear all existing dots and add a fresh wave
102.                     waves[:] = []
103.                     histogram.reset()
104.                     dots = generate_dots(dot_image, dots_per_wave, screen_width, screen_height)
105.
106.                     if (pygame.key.get_mods() & pygame.KMOD_CAPS):
107.                         for dot in dots:
108.                             if (dot.state is Dot.STATE_HOVER):
109.                                 dot.state = Dot.STATE_FALL
110.
111.                     waves.append(Wave(dots))
112.
113.                 elif (e.key in (pygame.K_CAPSLOCK, pygame.K_LSHIFT, pygame.K_RSHIFT)):
114.                     # Drop any existing dots that are hovering
115.                     if (pygame.key.get_mods() & pygame.KMOD_CAPS or pygame.key.get_mods() & pygame.KMOD_SHIFT):
116.                         for wave in waves:
117.                             for dot in wave.dots:
118.                                 if (dot.state is Dot.STATE_HOVER):
119.                                     dot.state = Dot.STATE_FALL
120.
121.             elif (e.type == pygame.MOUSEBUTTONDOWN):
122.                 # Add another wave of dots
123.                 new_dots = generate_dots(dot_image, dots_per_wave, screen_width, screen_height)
124.
125.                 if (pygame.key.get_mods() & pygame.KMOD_CAPS):
126.                     # Gravity's on; drop the new dots
127.                     for dot in new_dots:
128.                         dot.state = Dot.STATE_FALL
129.
130.                 waves.append(Wave(new_dots))
131.
132.
133.         # Update entities in the world
134.         for wave in waves:
135.             for dot in wave.dots:
136.                 if (dot.state is Dot.STATE_FALL):
137.                     dot.velocity_y += gravity   # Accelerate gradually
138.                     dot.y += dot.velocity_y
139.
140.                     # The line above always moves a fixed amount regardless of how
141.                     # long ago the loop last executed.
142.
143.                     # Tips online suggest tracking "time since the last world update"
144.                     # separately from "time since a frame was drawn depicting the world",
145.                     # so low FPS rendering wouldn't lag the game itself, and vice versa.
146.                     # Overkill here. Custom Clock classes exist on the net for that, but
147.                     # not stock in pygame. :/
148.
149.                     if (dot.y+dot.height > screen_height):
150.                         dot.velocity_y = 0
151.                         dot.y = screen_height - dot.height
152.                         dot.state = Dot.STATE_GROUNDED
153.
154.                         # Newly grounded dot; bump the histogram?
155.                         if (wave.grounded()):
156.                             wave_mean_x = wave.get_mean_x()
157.                             histogram.add_wave_mean(wave_mean_x)
158.
159.         # Prune the grounded dots to reduce clutter
160.         for wave in waves:
161.             if (wave.grounded()):
162.                 wave.remove_dots()
163.
164.         # Paint the screen
165.         # Fill background
166.         bg.fill(bg_color1, (0, 0, screen_width, screen_height))
167.
168.         # Draw histogram
169.         histogram.draw(bg)
170.
171.         # Draw dots
172.         for wave in waves:
173.             for dot in wave.dots:
174.                 dot.draw(bg)
175.
176.         # Draw labels
177.         bg.blit(x_axis_label, (screen_width/2 - x_axis_label.get_width()/2, screen_height - x_axis_label.get_height()*2))
178.         bg.blit(y_axis_label, (y_axis_label.get_width()*2, screen_height/2 - y_axis_label.get_height()/2))
179.
180.         pygame.display.update()
181.
182.     clock.tick(40)  # Limit FPS.
183.
184.
185. def generate_dots(dot_image, num, range_x, range_y):
186.     dot_list = [None]*num
187.
188.     for i in range(0, num):
189.         dot = Dot(dot_image)
190.         dot.x,dot.y = random_coords(range_x, range_y)
191.         dot_list[i] = dot
192.
193.     return dot_list
194.
195.
196. def random_coords(range_x, range_y):
197.     x,y = random.randint(1,range_x), random.randint(1,range_y)
198.     return [x,y]
199.
200.
201. def terminate_game():
202.     pygame.mouse.set_visible(True)
203.     pygame.quit()
204.
205.
206. def main():
207.     init_game()
208.     run_game()
209.     terminate_game()
210.
211.
212.
213. class Dot(object):
214.     STATE_HOVER = "Hover"
215.     STATE_FALL = "Fall"
216.     STATE_GROUNDED = "Grounded"
217.
218.     def __init__(self, image):
219.         self._image = image
220.         self.width, self.height = image.get_width(), image.get_height()
221.         self.x, self.y = 0, 0
222.         self.velocity_x, self.velocity_y = 0, 0
223.         self.state = Dot.STATE_HOVER
224.
225.     def draw(self, surface):
226.         surface.blit(self._image, (self.x, self.y))
227.
228.
229.
230. class Wave(object):
231.     def __init__(self, existing_dots=None):
232.         if (existing_dots is not None):
233.             self.dots = existing_dots
234.         else:
235.             self.dots = []
236.         self._cached_mean_x = None
237.
238.     def add_dot(self, dot):
239.         self.dots.append(dot)
240.
241.     def add_dots(self, new_dots):
242.         self.dots.extend(new_dots)
243.
244.     def grounded(self):
245.         """Tests whether all dots in this wave are grounded."""
246.         if (len(self.dots) == 0):
247.             return False
248.
249.         for dot in self.dots:
250.             if (dot.state is not Dot.STATE_GROUNDED):
251.                 return False
252.         return True
253.
254.     def get_mean_x(self, recalculate=False):
255.         """Get the average x position among dots in this wave."""
256.         if (self._cached_mean_x is None or recalculate):
257.             values = [dot.x for dot in self.dots]
258.             self._cached_mean_x = float(sum(values)) / len(values) if (len(values) > 0) else float('nan')
259.         return self._cached_mean_x
260.
261.     def remove_dots(self):
262.         """Removes all dot objects from this wave, while remembering the mean."""
263.         self.get_mean_x()
264.         self.dots[:] = []
265.
266.
267.
268. class Histogram(object):
269.     def __init__(self, screen_rect, divisions):
270.         self._screen_rect = screen_rect
271.         self.divisions = divisions
272.         self.bar_color = (200, 200, 230)
273.
274.         self.cols = [None]*self.divisions
275.         col_width = self._screen_rect.width / self.divisions
276.         for n in range(0, self.divisions):
277.             region = pygame.Rect(n*col_width, 0, col_width, self._screen_rect.height)
278.             bar = pygame.Rect(n*col_width, self._screen_rect.height, col_width, 0)
279.             self.cols[n] = {"region":region, "count":0, "bar":bar}
280.
281.     def reset(self):
282.         for col in self.cols:
283.             col["count"] = 0
284.             col["bar"].y = self._screen_rect.height
285.             col["bar"].height = 0
286.
287.     def add_wave_mean(self, wave_mean_x):
288.         for col in self.cols:
289.             if (col["region"].collidepoint(wave_mean_x, 0)):
290.                 col["count"] += 1
291.                 break
292.
293.         # Re-scale visible bars to fit the screen
294.         highest_count = max(self.cols, key=lambda x: x["count"])["count"]
295.         unit = float(self._screen_rect.height) / highest_count
296.         for col in self.cols:
297.             col["bar"].y = int(self._screen_rect.height - unit*col["count"])
298.             col["bar"].height = int(self._screen_rect.height - col["bar"].y)
299.
300.     def draw(self, surface):
301.         for col in self.cols:
302.             pygame.draw.rect(surface, self.bar_color, col["bar"], 0)
303.
304.
305.
306. main()
RAW Paste Data

# Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×