#! /usr/bin/env python import random, os.path, math import time import pygame from pygame.locals import * PASTURE = Rect(0, 0, 500, 500) CULTURALDIST = 5.0 FLOCKSIZE = 50 EXITSSTAGELEFT = False COMFORTDIST = 5.0 JAYGIRTH = 10.0 WORMGIRTH = 4.0 MAXSPEED = 10.0 # mph, 1 pixel = 1 mile STRUTSPERHOUR = 2 def render_background(): surf = pygame.Surface(PASTURE.size) surf.fill((255,255,255)) return surf def render_rect(size, color): surf = pygame.Surface(size) r = surf.get_rect() surf.fill(color) return surf, r def rand_pos(buffer = 0): return random.randrange(buffer, PASTURE.right - buffer), random.randrange(buffer, PASTURE.bottom - buffer) class rectangible: def __init__(self,x,y,w,h): #force float self.x = x + 0.; self.y = y + 0.; self.w = w + 0.; self.h = h + 0. def colliderect(self, *args): if len(args) == 1 and isinstance(args[0], rectangible): x = args[0].x; y = args[0].y; w = args[0].w; h = args[0].h elif len(args) == 4: x = args[0]; y = args[1]; w = args[2]; h = args[3] else: raise InputError, "rectangible.colliderect() requires either a rectangible or x,y,w,h" return ((self.x >= x and self.x < x+w) or (x >= self.x and x < self.x+self.w)) and \ ((self.y >= y and self.y < y+h) or (y >= self.y and y < self.y+self.h)) def inflate(self, x, y): return rectangible(self.x - x/2, self.y - y/2, self.w + x, self.h + y) def move(self, x, y): return rectangible(self.x + x, self.y + y, self.w, self.h) def move_ip(self, x,y): self.x += x; self.y += y return self def centerx(self): return self.x + self.w/2 def centery(self): return self.y + self.h/2 def Rect(self): return pygame.Rect(self.x, self.y, self.w, self.h) class nightcrawler(pygame.sprite.Sprite): def __init__(self, size, pos, b): pygame.sprite.Sprite.__init__(self, self.containers) self.image, self.rect = render_rect(size, (100,70,50)) self.rect.left, self.rect.top = pos self.pos = rectangible(pos[0],pos[1],size[0],size[1]) self.birthmark = b class jaybird(pygame.sprite.Sprite): def __init__(self, size, pos, m, l, b): pygame.sprite.Sprite.__init__(self, self.containers) self.image, self.rect = render_rect(size, (0,0,255)) self.rect.left, self.rect.top = pos self.pos = self.lastpos = self.nextpos = rectangible(pos[0],pos[1],size[0],size[1]) self.maxspeed = m self.lunch = l self.birthmark = b self.sated = False def eye(self, fellows): xmove = ymove = count = 0 xdel = self.lunch.centerx() - self.pos.centerx() ydel = self.lunch.centery() - self.pos.centery() for chirper in fellows: if chirper != self and not EXITSSTAGELEFT and self.pos.inflate(2*COMFORTDIST, 2*COMFORTDIST).colliderect(chirper.pos): count = count + 1 # move away from the nearby chirper xdel2 = self.pos.centerx() - chirper.pos.centerx() ydel2 = self.pos.centery() - chirper.pos.centery() fiddle2 = (self.maxspeed/2.0)/math.sqrt(xdel2*xdel2 + ydel2*ydel2) xmove += fiddle2*xdel2; ymove += fiddle2*ydel2 # move slightly to the right w.r.t. the direction towards the worm fiddle3 = (self.maxspeed/8.0)/math.sqrt(xdel*xdel + ydel*ydel) xmove += fiddle3*ydel; ymove += fiddle3*xdel; #move in the direction of the worm, if not in others' midst fiddle = (self.maxspeed/4.0)/math.sqrt(xdel*xdel + ydel*ydel) xmove += fiddle*xdel; ymove += fiddle*ydel return xmove, ymove def strut(self): #print self.birthmark, ": ", self.pos self.lastpos = self.pos self.pos = self.nextpos self.rect = self.pos.Rect() def main(winstyle = 0): pygame.init() # display mode winstyle = 0 bestdepth = pygame.display.mode_ok(PASTURE.size, winstyle, 32) screen = pygame.display.set_mode(PASTURE.size, winstyle, bestdepth) # background bg = render_background() screen.blit(bg, (0,0)) pygame.display.flip() # sprite groups thebirds = pygame.sprite.Group() theworms = pygame.sprite.Group() all = pygame.sprite.RenderUpdates() jaybird.containers = thebirds, all nightcrawler.containers = theworms, all # controls framerate clock = pygame.time.Clock() random.seed() # place sprites randomly, but not too close together for birthmark in range(FLOCKSIZE): while True: pos = rand_pos(buffer=JAYGIRTH) for wriggler in theworms.sprites(): if wriggler.pos.inflate(math.sqrt(2)*(WORMGIRTH+2*CULTURALDIST),math.sqrt(2)*(WORMGIRTH+2*CULTURALDIST)).\ colliderect(pos[0],pos[1],WORMGIRTH,WORMGIRTH): break else: break n = nightcrawler((WORMGIRTH, WORMGIRTH), pos, birthmark) while True: pos = rand_pos(buffer=JAYGIRTH/2) for chirper in thebirds.sprites(): if chirper.pos.inflate(CULTURALDIST, CULTURALDIST).colliderect(pos[0], pos[1], JAYGIRTH, JAYGIRTH): break else: break j = jaybird((JAYGIRTH, JAYGIRTH), pos, MAXSPEED, n.pos, birthmark) start = time.time() # main loop flockishly = True while flockishly: # EVENTS pygame.event.pump() for event in pygame.event.get(): if event.type == KEYDOWN and event.key == K_p: pygame.event.set_allowed(None) pygame.event.set_allowed(KEYDOWN) pygame.event.clear() pygame.event.set_blocked(None) pygame.event.wait() if event.type == QUIT or (event.type == KEYDOWN and event.key == K_q): flockishly = False # EYE for chirper in thebirds.sprites(): if chirper.sated == False: xvel, yvel = chirper.eye(thebirds.sprites()) #XXX: need send only non sated participants if exiting stage left chirper.nextpos = chirper.pos.move(xvel/STRUTSPERHOUR, yvel/STRUTSPERHOUR) # STRUT for chirper in thebirds.sprites(): if chirper.sated == False: chirper.strut() # CULTURAL VIOLATIONS & COLLISIONS # uhhh... O(n^2), not O(n!) for chirper in thebirds.sprites(): if chirper.sated == True: continue for chirpier in thebirds.sprites(): if chirper != chirpier and \ chirper.pos.inflate(2*CULTURALDIST, 2*CULTURALDIST).colliderect(chirpier.pos) and \ (EXITSSTAGELEFT == False or (EXITSSTAGELEFT == True and chirpier.sated == False)): print chirper.birthmark, " and ", chirpier.birthmark, "are invading each other's personal space!" continue if chirper != chirpier and chirper.pos.colliderect(chirpier.pos) and \ (EXITSSTAGELEFT == False or (EXITSSTAGELEFT == True and chirpier.sated == False)): print "collision between ", chirper.birthmark, " and ", chirpier.birthmark print chirper.pos.Rect(), " and ", chirpier.pos.Rect() flockishly = False # GOT WORM? for chirper in thebirds.sprites(): if chirper.pos.colliderect(chirper.lunch): chirper.sated = True # DRAW all.clear(screen, bg) all.update() dirty = all.draw(screen) pygame.display.update(dirty) # HUNGRYBIRD? for chirper in thebirds.sprites(): if chirper.sated == False: break else: flockishly = False #limit framerate #clock.tick(100) print "Took ", time.time() - start, "seconds (ignoring pauses, etc.)" #pause before exiting while True: event = pygame.event.wait() if event.type == QUIT or event.type == KEYDOWN: return #call the "main" function if running this script if __name__ == '__main__': main()