CA7746

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.

×