Permalink
Browse files

More things...

  • Loading branch information...
1 parent 10c0aa7 commit bf7ab27d4773fda7ccde13eefeb189b419404057 @Dolkar committed Feb 2, 2012
Showing with 471 additions and 1 deletion.
  1. +140 −0 CachedSystemRandom.py
  2. +9 −1 README.txt
  3. +194 −0 randomShapes.py
  4. +128 −0 snake.py
View
140 CachedSystemRandom.py
@@ -0,0 +1,140 @@
+"""
+Created: 11.1.2012
+Version: 0.1
+Author: Dolkar
+License: None
+
+Cryptografically safe pseudo-random generator using os.urandom.
+Standard random module already has one such generator, SystemRandom, which is
+pretty inefficient because of large os.urandom overhead. This one minimizes
+the loss by loading random data in large chunks (10KiB by default). It also uses
+struct for unpacking the raw binary randomness, which is faster than
+binascii.hexlify.
+
+Run the file for speed comparisons. If you find anything that can be implemented
+more efficiently... let me know.
+"""
+
+
+import random as _random
+from os import urandom as _urandom
+from binascii import hexlify as _hexlify
+from struct import unpack_from as _unpack_from
+
+BPF = 53 # Number of bits in a float
+FLOAT_CONV_CONSTANT = 2 ** -(BPF + 11) # For 8-byte int to [0, 1) float range
+
+_CACHE_SIZE = 10240
+
+__all__ = ["CachedSystemRandom", "random", "uniform", "randint", "choice",
+ "sample", "randrange", "shuffle", "normalvariate", "lognormvariate",
+ "expovariate", "vonmisesvariate", "gammavariate", "triangular",
+ "betavariate", "paretovariate", "weibullvariate", "getrandbits"]
+
+class CachedSystemRandom(_random.Random):
+ def __init__(self, cache_size = _CACHE_SIZE):
+ """
+ CachedSystemRandom(cache_size = 10240): Creates a random number
+ generator with given cache size in bytes.
+ """
+ self._source = _urandom
+ self._cache_size = cache_size
+ self.refresh()
+
+ def getrandbits(self, k):
+ """
+ getrandbits(k) -> x. Generates a long int with k random bits.
+ Used for covering very large ranges"""
+ if k <= 0:
+ raise ValueError('number of bits must be greater than zero')
+ if k != int(k):
+ raise TypeError('number of bits should be an integer')
+
+ bytes = (k + 7) // 8 # bits / 8 and rounded up
+
+ data = self._cache[self._pos:self._pos + bytes]
+
+ bytes_left = self._cache_size - self._pos
+ if bytes >= bytes_left:
+ if bytes > self._cache_size:
+ data = ''.join((data, self._source(bytes - bytes_left)))
+ self.refresh()
+ else:
+ self.refresh()
+ self._pos = bytes - bytes_left
+ data = ''.join((data, self._cache[:self._pos]))
+ else:
+ self._pos += bytes
+
+ return long(_hexlify(data), 16) >> (bytes * 8 - k) # trim excess bits
+
+ def refresh(self):
+ """Drops all current catched data and fetches some new."""
+ # 8 extra bytes to ensure enough space for a random call
+ self._cache = self._source(self._cache_size + 8)
+ self._pos = 0
+
+ def random(self):
+ """Get the next random number in the range [0.0, 1.0)."""
+ value = _unpack_from('Q', self._cache, self._pos)[0] * FLOAT_CONV_CONSTANT
+ self._pos += 8
+ if self._pos >= self._cache_size:
+ self.refresh()
+ return value
+
+ def _stub(self, *args, **kwds):
+ "Stub method. Not used for a system random number generator."
+ return None
+ seed = jumpahead = _stub
+
+ def _notimplemented(self, *args, **kwds):
+ "Method should not be called for a system random number generator."
+ raise NotImplementedError('System entropy source does not have state.')
+ getstate = setstate = _notimplemented
+
+_inst = CachedSystemRandom()
+random = _inst.random
+uniform = _inst.uniform
+triangular = _inst.triangular
+randint = _inst.randint
+choice = _inst.choice
+randrange = _inst.randrange
+sample = _inst.sample
+shuffle = _inst.shuffle
+normalvariate = _inst.normalvariate
+lognormvariate = _inst.lognormvariate
+expovariate = _inst.expovariate
+vonmisesvariate = _inst.vonmisesvariate
+gammavariate = _inst.gammavariate
+betavariate = _inst.betavariate
+paretovariate = _inst.paretovariate
+weibullvariate = _inst.weibullvariate
+getrandbits = _inst.getrandbits
+
+if __name__ == '__main__':
+ from timeit import timeit
+
+ R = _random.Random()
+ SR = _random.SystemRandom()
+ CSR = CachedSystemRandom()
+ setup = 'from __main__ import CSR, SR, R'
+
+ def testMethod(name, number):
+ a = timeit('R.%s' % name, setup, number = number) * 1000
+ b = timeit('SR.%s' % name, setup, number = number) * 1000
+ c = timeit('CSR.%s' % name, setup, number = number) * 1000
+ print "%25s %8.3f ms %12.3f ms %14.3f ms" % (name, a, b, c)
+
+ print " call Random SystemRandom C.SystemRandom"
+ print
+ testMethod('random()', 100000)
+ testMethod('randrange(100000)', 10000)
+ testMethod('randrange(2**100)', 10000)
+ testMethod('uniform(0, 2**100)', 10000)
+ testMethod('choice(range(1000))', 10000)
+ testMethod('shuffle(range(1000))', 100)
+ testMethod('gammavariate(1.0,1.0)', 10000)
+ print
+ testMethod('getrandbits(2)', 100000)
+ testMethod('getrandbits(2**10)', 10000)
+ testMethod('getrandbits(2**20)', 100)
View
10 README.txt
@@ -11,4 +11,12 @@ shunting - A shunting yard algorithm implementation for converting infix to post
Useful for mathematical or logical expressions. Handles parentheses. Not well documented.
vector - A hopefully easy to use vector class for computations with vectors.
-I made it a long time ago and would probably change few things in there...
+I made it a long time ago and would probably change few things in there...
+
+CachedSystemRandom - An efficient cryptographically secure random number generator.
+
+randomShapes - A screensaver showing randomly generated "shapes". Pretty nice to watch.
+
+snake - A simple snake-like game on wrapped map with centered camera. Brings a new
+ feel to the old snake. Control the snake with arrows and try to eat food
+ before it's too late...
View
194 randomShapes.py
@@ -0,0 +1,194 @@
+import random
+import pygame
+from vector import Vector
+from math import radians
+from colorsys import hsv_to_rgb
+
+DIM = (800, 600)
+ESCAPE_EVENTS = set([pygame.KEYDOWN, pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN, pygame.QUIT])
+JOINTS = 3
+WAIT_TIME = 500 # 10 seconds
+
+class Joint(object):
+ def __init__(self, length, sharpness = 180):
+ self.length = length
+ self.sharpness = sharpness
+ self.step = random.randrange(-sharpness, sharpness) % 360
+ self.start = random.randrange(360)
+ self.angle = self.start
+
+ def getPos(self):
+ return Vector.polar(radians(self.angle), self.length)
+
+ def rotate(self):
+ """Rotates the joint and returns True if it did a full circle already."""
+ self.angle = (self.angle + self.step) % 360
+ return self.angle == self.start
+
+
+class ShapeDrawer(object):
+ def __init__(self, surface, pos, size, joints):
+ self.surface = surface
+ self.size = size
+ self.pos = Vector(pos)
+ self.initJoints(joints)
+ color = hsv_to_rgb(random.random(), random.random() * 0.6 + 0.2, 1)
+ self.color = pygame.Color(*[int(v * 255) for v in color])
+
+ self.lines = 0
+ self.total_length = 0
+
+ def initJoints(self, count):
+ sharpness = random.randint(3, 120)
+
+ self.joints = []
+ for _ in xrange(count):
+ self.joints.append(Joint(self.size / float(count), sharpness))
+
+ self.last_point = tuple(self.getPos())
+
+ def __iter__(self):
+ return self
+
+ def getPos(self):
+ return sum((j.getPos() for j in self.joints), self.pos)
+
+ def getMaxStep(self):
+ return max(j.sharpness for j in self.joints)
+
+ def next(self):
+ """Draws a next line and returns relative line length."""
+ done = [j.rotate() for j in self.joints]
+
+ point = self.getPos()
+ pygame.draw.aaline(self.surface, self.color, tuple(point), self.last_point)
+ length = (point - self.last_point).mag()
+
+ self.last_point = tuple(point)
+ self.lines += 1
+ self.total_length += length
+
+ if all(done): # We are done here...
+ raise StopIteration
+
+ return length / self.size
+
+class ShapeFader(object):
+ """Shape fading process"""
+ def __init__(self, surface):
+ self.surface = surface
+ self.surface.set_colorkey(None)
+ self.size = self.surface.get_size()
+ self.tick = 0
+
+ def step(self):
+ if self.tick % 3 == 0 and self.tick < 300:
+ rotate(self.surface, self.surface, 3, 1.02)
+ self.surface.blit(black, (0, 0))
+ self.tick += 1
+
+
+def writeStats(drawer):
+ """Draws shape stats on a transparent surface"""
+ sharptext = font.render("Sharpness: %s" % drawer.getMaxStep(), True, (255, 180, 0))
+ linestext = font.render("Number of lines: %s" % drawer.lines, True, (255, 180, 0))
+ lengthtext = font.render("Total length: %s" % int(drawer.total_length), True, (255, 180, 0))
+ avg = int(drawer.total_length) / drawer.lines
+ averagetext = font.render("Average line length: %s" % avg, True, (255, 180, 0))
+
+ surface = pygame.Surface((300, 100))
+ surface.set_colorkey((0, 0, 0))
+ surface.blit(sharptext, (20, 20))
+ surface.blit(linestext, (20, 40))
+ surface.blit(lengthtext, (20, 60))
+ surface.blit(averagetext, (20, 80))
+ return surface
+
+def rotate(surface, source, tick, scale = 1, rate = 2000.0):
+ """Rotates surface based on tick and blits it centered."""
+ angle = (1 - (tick / rate) % 1) * 360
+ rect = source.get_rect()
+ rotated = pygame.transform.rotozoom(source, angle, scale)
+ rect.center = rotated.get_rect().center
+ surface.blit(rotated, (0, 0), rect)
+
+
+pygame.init()
+screen = pygame.display.set_mode(DIM, pygame.FULLSCREEN)
+screen.fill((0, 0, 0))
+pygame.event.clear()
+pygame.mouse.set_visible(False)
+timer = pygame.time.Clock()
+
+font = pygame.font.SysFont('georgia', 13, bold = "True")
+black = pygame.Surface(DIM, flags = pygame.SRCALPHA)
+black.fill((0, 0, 0, 5))
+
+tick = 0
+wait_time = 30
+last_shape = None
+shape = None
+stats = None
+drawer = None
+running = True
+
+
+while running:
+ timer.tick(50)
+ tick += 1
+
+ # Setting...
+ if wait_time > 0:
+ # Waiting...
+ wait_time -= 1
+
+ elif drawer:
+ # Image is being drawed
+ try:
+ timemod = drawer.next()
+ except StopIteration:
+ # Image done, change state...
+ stats = writeStats(drawer)
+ wait_time = WAIT_TIME
+ rot_start_tick = tick
+
+ last_shape = None
+ drawer = None
+ else:
+ wait_time = round(2 * timemod)
+
+ else:
+ if shape:
+ # Fading last shape
+ rotate(shape, shape, tick - rot_start_tick)
+ shape.blit(stats, (0, 0))
+ last_shape = ShapeFader(shape)
+ stats = None
+ # Setting a new drawer...
+ shape = pygame.Surface(DIM)
+ shape.set_colorkey((0, 0, 0))
+ drawer = ShapeDrawer(shape, (400, 300), 300, JOINTS)
+ wait_time = 20
+
+ # Drawing...
+ if last_shape:
+ # Blend last shape as background
+ last_shape.step()
+ screen.blit(last_shape.surface, (0, 0))
+
+ if shape:
+ if not drawer:
+ # Rotate
+ rotate(screen, shape, tick - rot_start_tick)
+ else:
+ screen.blit(shape, (0, 0))
+
+ if stats:
+ screen.blit(stats, (0, 0))
+
+ pygame.display.update()
+
+ for event in pygame.event.get():
+ if event.type in ESCAPE_EVENTS:
+ pygame.quit()
+ running = False
View
128 snake.py
@@ -0,0 +1,128 @@
+import pygame
+from random import randrange
+
+pygame.font.init()
+
+DIFFS = [0.001, 0.0025, 0.005, 0.0075, 0.01]
+
+class Game(object):
+ def __init__(self, size, block_size, speed = 10, difficulty = 3, growth = 3):
+ self.size = size
+ self.block_size = block_size
+ self.distance = self.block_size + 1
+ self.font = pygame.font.SysFont("consolas", 32, bold = True)
+ self.speed = speed
+ self.snake = [(size / 2, size / 2)]
+ self.dir = (0, -1)
+ self.running = True
+ self.base_growth = growth
+ self.growth = self.base_growth
+ self.score_decay_growth = DIFFS[difficulty - 1]
+ self.score = 100
+ self.turn = 0
+
+
+ self.surface = pygame.display.set_mode((size * (block_size + 1),) * 2)
+ self.generateFood()
+ self.run()
+
+ def generateFood(self):
+ self.food = (randrange(0, self.size), randrange(0, self.size))
+ if self.food in self.snake:
+ self.generateFood()
+
+ def makeTurn(self):
+ newpos = ((self.snake[0][0] + self.dir[0]) % self.size,
+ (self.snake[0][1] + self.dir[1]) % self.size)
+
+ self.snake = [newpos] + self.snake
+
+ if newpos == self.food:
+ self.growth += self.base_growth
+ self.score += 0.1 * len(self.snake) ** 1.5 + 10
+ self.generateFood()
+
+ if self.growth > 0:
+ self.growth -= 1
+ else:
+ self.snake.pop()
+
+ self.score -= 0.5 + self.score_decay_growth * self.turn
+ self.turn += 1
+
+ if self.snake.count(newpos) >= 2:
+ # collided...
+ self.score -= 100
+ del(self.snake[0])
+
+ if self.score <= 0:
+ # the end...
+ self.running = False
+ self.score = 0
+
+ self.showFrame()
+
+ def showFrame(self):
+ self.surface.fill((0, 0, 0))
+ cam_pos = (self.snake[0][0] - self.size / 2, self.snake[0][1] - self.size / 2)
+
+ line_y = ((self.size - cam_pos[1]) % self.size * self.distance - 1)
+ line_x = ((self.size - cam_pos[0]) % self.size * self.distance - 1)
+ pygame.draw.line(self.surface, (60, 60, 60),
+ (0, line_y), (self.size * self.distance, line_y))
+ pygame.draw.line(self.surface, (60, 60, 60),
+ (line_x, 0), (line_x, self.size * self.distance))
+
+ shade = 250
+ for (x, y) in self.snake:
+ pos = ((x - cam_pos[0]) % self.size * self.distance,
+ (y - cam_pos[1]) % self.size * self.distance)
+ pygame.draw.rect(self.surface, (0, round(shade), 0), (pos, (self.block_size,) * 2))
+ shade -= 150.0 / len(self.snake)
+
+ foodpos = ((self.food[0] - cam_pos[0]) % self.size * self.distance + self.block_size / 2,
+ (self.food[1] - cam_pos[1]) % self.size * self.distance + self.block_size / 2)
+ pygame.draw.circle(self.surface, (255, 0, 0), foodpos, self.block_size / 2)
+
+ if self.score < 500:
+ color = (255, self.score / 2, 0)
+ else:
+ color = (max((1000 - self.score) / 500 * 255, 0), 255, 0)
+
+ sc = self.font.render(str(int(self.score)), True, color)
+ width = self.font.size(str(int(self.score)))[0]
+
+ turn = self.font.render(str(self.turn), True, (0, 150, 255))
+
+ self.surface.blit(sc, (self.size * self.distance - width - 20, 20))
+ self.surface.blit(turn, (20, 20))
+
+ pygame.display.update()
+
+ def run(self):
+ clock = pygame.time.Clock()
+
+ while True:
+ if self.running:
+ self.makeTurn()
+ clock.tick(self.speed)
+
+ event = pygame.event.poll()
+ while event and event.type != pygame.KEYDOWN:
+ if event.type == pygame.QUIT:
+ pygame.quit()
+ return
+ event = pygame.event.poll()
+ if event:
+ if self.dir[1]:
+ if event.key == pygame.K_LEFT:
+ self.dir = (-1, 0)
+ elif event.key == pygame.K_RIGHT:
+ self.dir = (1, 0)
+ else:
+ if event.key == pygame.K_UP:
+ self.dir = (0, -1)
+ elif event.key == pygame.K_DOWN:
+ self.dir = (0, 1)
+
+Game(20, 20, 10, difficulty = 3)

0 comments on commit bf7ab27

Please sign in to comment.