#!/usr/bin/env python """ particles "collision" screen saver written by Felix "Albert" Liard """ from graphics import * from random import * from time import * class Particle: id_seq = 0 # ea particle has a unique ID # special RED 'destroyer' is 0 def __init__(self, window, colors, x=0, y=0, color=None, size=None): self.id = Particle.id_seq Particle.id_seq += 1 # position self.px = x self.py = y # direction vectorX = vectorY = 0 while vectorX==0 and vectorY==0: #if vX=0 AND vY=0 particle won't move vectorX = randint(-20,20) #...so get a new non-zero vector vectorY = randint(-20,20) self.dx = vectorX self.dy = vectorY if color == None: color = randint(1,len(colors)-1) # color=0 reserved RED particle self.color = colors[color] if size == None: size = randint(8,24) self.size = size circle = Circle(Point(x,y),size) # circle is physical screen object circle.setOutline(colors[color]) circle.setFill(colors[color]) circle.draw(window) self.circle = circle # logical attribute of particle points to phys def destroy(self, particles_list): del particles_list[self.circle] # remove from dictionary self.circle.undraw() # remove from screen del self.circle # don't forget remove physical object! del self def initialize_window(sizex,sizey,color='Black'): w = GraphWin('Colliding Particles: RED destroys (resets after 45 secs)', sizex,sizey) w.setCoords(0,0,sizex,sizey) w.setBackground(color) seed() # random numbers seed return w def read_colors(): colors = [] with open('colors-subset.txt','r') as file: for line in file: color = line.strip() colors.append(color) return colors def screen_text(window,maxX,maxY): text = Text(Point(maxX/2 - 30,maxY/2), '') text.setSize(22) text.setStyle('bold') text.setTextColor('green') text.setText('Winner! Particles lasted 45 secs!') for _ in range(5): # flash the text effect text.draw(window) sleep(.4) text.undraw() sleep(.2) def main(): particles_list = {} total_particles = 50 maxX = 1920 maxY = 1000 p0x1 = p0x2 = p0y1 = p0y2 = 0 colors = read_colors() window = initialize_window(maxX,maxY,'black') p0 = Particle(window,colors,maxX/2,maxY/2,0,45) radius = p0.circle.getRadius() # particle 0 is special RED destroyer particles_list[p0.circle] = p0 # dict of objects circle:particle # particle = logical object # circle = physical representation focci = (.25,.5,.75) # particles originate in 9 points (3x * 3y) for _ in range(total_particles): focusx = focci[int(randint(0,len(focci)-1))]*maxX focusy = focci[int(randint(0,len(focci)-1))]*maxY particle = Particle(window,colors,focusx,focusy) particles_list[particle.circle] = particle start = time() restart = False sleep(1.5) while True: remaining = len(particles_list) if remaining <= 10: # less than n particles, animation too fast sleep(.011) # so add a delay elif remaining <= 100: sleep(.01) present = time() for c,p in particles_list.items(): # EDGE detect, if not at edge keep moving if 0 < p.px+p.dx < maxX and 0 < p.py+p.dy < maxY: p.px += p.dx p.py += p.dy # if at edge (x or y) then alter course to "bounce" if p.px+p.dx >= maxX or p.px+p.dx <= 0: p.dx = -(p.dx) p.px += p.dx if p.py+p.dy >= maxY or p.py+p.dy <= 0: p.dy = -(p.dy) p.py += p.dy c.move(p.dx,p.dy) #...finally, move particle to next position if p.id == 0: # where is special RED particle0? p0x1 = p.px - radius p0x2 = p.px + radius p0y1 = p.py - radius p0y2 = p.py + radius else: # detect collision of this.particle with special RED particle0 if p0x1 <= p.px <= p0x2 and p0y1 <= p.py <= p0y2: p.destroy(particles_list) if present > start + 45: # resets after 45 secs screen_text(window,maxX,maxY) restart = True # destroy any remaining particles, re-instantiate and restart if len(particles_list) == 1 or restart: for c,p in particles_list.items(): p.destroy(particles_list) particles_list = {} p0 = Particle(window,colors,maxX/2,maxY/2,0,45) p0.id = 0 particles_list[p0.circle] = p0 radius = p0.circle.getRadius() for _ in range(total_particles): focusx = focci[int(randint(0,len(focci)-1))]*maxX focusy = focci[int(randint(0,len(focci)-1))]*maxY particle = Particle(window,colors,focusx,focusy) particles_list[particle.circle] = particle start = time() restart = False break if __name__ == '__main__': main()