Skip to content

Instantly share code, notes, and snippets.

@jondoesntgit
Last active November 3, 2015 15:12
Show Gist options
  • Save jondoesntgit/c4a335e61310b9959d8c to your computer and use it in GitHub Desktop.
Save jondoesntgit/c4a335e61310b9959d8c to your computer and use it in GitHub Desktop.
Chickens in Space

#Chickens in Space

Inspiration

In a D&D April fools joke, a feat was made available to NPCs to have a 50% chance of drawing a live chicken out of a box or when they brandish a weapon. This could be combined with a few other feats ingame to exploit this feat so that the user could produce an infinite number of chickens and violate physical principles such as conservation of mass and momentum.

With this in mind, what if you were on a space ship with one such NPC, who would willingly produce chickens for you to eject from your craft as propulsion?

Explore this idea in the following pythonista script. Tilt the screen to aim the cannon on the ship, and fire chickens with some recoil. (Each chicken has mass 1, and the ship has mass 3). Be careful! Once ejected, chickens become deadly if you collide with them. Be wary of gravitational effects, especially when chickens glob into chicken clusters!

from scene import *
import random
from numpy import sign as sgn
score= 0
chandr=20
decayRate = 100 # higher is slower
spew = .1
leng=-100
gconst = 1
rscale = 3
eggSpeed = 600
rdeadly = 10 * rscale
cannonForce = - 3
restart = 0
eventHorizonCoeff=.5
class ChickensInSpace (object):
def __init__(self):
self.arr = []
self.collision = False
self.m = 0
def draw(self):
for chicken in self.arr:
chicken.draw()
def update(self):
index = 0
for index,chicken in enumerate(self.arr):
if chicken.distance(ship) > rdeadly:
chicken.attract(ship)
if not chicken.isEgg:
for i in range(index+1, len(self.arr)):
try: # poor programming
neighbor = self.arr[i]
except:
continue
#line(chicken.x,chicken.y,neighbor.x,neighbor.y)
if chicken.distance(neighbor) < chicken.r + neighbor.r:
if (not chicken.isEgg and not neighbor.isEgg):
chicken.coalesce(neighbor)
if neighbor.isEgg == chicken.isEgg:
chicken.attract(neighbor)
chicken.update()
self.getNearest()
def getNearest(self):
nearest = None
d=999
for chicken in self.arr:
if chicken.d < d:
d = chicken.d
nearest = chicken
if nearest != None:
r = nearest.r + 5
# ellipse(nearest.x-r/2,nearest.y-r/2,r,r)
if nearest != None and d<nearest.r+ship.r and nearest.deadly:
self.collision = True
return d
class Chicken (object):
def __init__(self,x,y,xvel,yvel):
self.isEgg = False
self.x = x
self.y = y
self.m = 1
self.r = rscale * sqrt(self.m)
self.d = self.distance(ship)
self.xvel = 2*xvel + ship.xvel
self.yvel = 2*yvel + ship.yvel
self.deadly = False
self.blackHole = False
def distance(self, neighbor):
d = sqrt(pow(self.x - neighbor.x,2) + pow(self.y-neighbor.y,2))
return d
def update(self):
if self.y + self.yvel > scene.h:
self.yvel = - self.yvel
if self.isEgg:
cis.arr.remove(self)
if self.x + self.xvel > scene.w:
self.xvel = - self.xvel
if self.isEgg:
cis.arr.remove(self)
if self.x + self.xvel < 0:
self.xvel = - self.xvel
if self.isEgg:
try:
cis.arr.remove(self)
except :
pass
if self.y + self.yvel < 0:
self.yvel = - self.yvel
if self.isEgg:
try:
cis.arr.remove(self)
except:
pass
self.x += self.xvel
self.y += self.yvel
if self.blackHole and random.randint(1,decayRate*int(sqrt(self.m))) < sqrt(score/10):
global ship
egg = Chicken(self.x+sgn(ship.x-self.x), self.y+sgn(ship.y-self.y),(ship.x - self.x)/eggSpeed,(ship.y - self.y)/eggSpeed)
egg.isEgg = True
cis.arr.append(egg)
for i in xrange(0,int(score/1000)):
egg=Chicken(self.x+sgn(ship.x-self.x), self.y+sgn(ship.y-self.y),(ship.x - self.x)/eggSpeed + random.uniform(-spew,spew),(ship.y - self.y)/eggSpeed + random.uniform(-spew,spew))
egg.isEgg = True
cis.arr.append(egg)
self.m-=1
#for i in xrange(0,sqrt(score/100)):
# egg.isEgg = True
# cis.append(egg)
if self.blackHole:
if self.m > 0 and self.r > rscale *eventHorizonCoeff * sqrt(self.m):
self.r -= .2 # rscale /2 * sqrt(self.m)
if self.m > chandr:
self.blackHole = True
elif self.m < 1:
cis.arr.remove(self)
self.d = self.distance(ship)
if not self.deadly:
if self.d > rdeadly:
self.deadly = True
def coalesce(self,other):
self.x = (other.x * other.m + self.x * self.m) / (other.m + self.m)
self.y = (other.y * other.m + self.y * self.m) / (other.m + self.m)
self.xvel = (self.m * self.xvel + other.m * other.xvel)/(self.m + other.m)
self.yvel = (other.m * other.yvel)/(self.m + other.m)
self.m += other.m
self.r = rscale * sqrt(self.m)
global cis
if other.blackHole:
self.blackHole = True
if self.blackHole:
self.r = rscale * eventHorizonCoeff * sqrt(self.m)
cis.arr = [ item for item in cis.arr if item != other ]
def attract(self,neighbor):
d = self.distance(neighbor)
if d>max(neighbor.r,self.r):
self.xvel += gconst * neighbor.m / pow(d,3) * (neighbor.x-self.x)
self.yvel += gconst * neighbor.m / pow(d,3) * (neighbor.y-self.y)
neighbor.xvel += gconst * self.m / pow(d,3) * (self.x-neighbor.x)
neighbor.yvel += gconst * self.m /pow(d,3) * (self.y-neighbor.y)
def draw(self):
if self.blackHole:
fill(0,0,0)
ellipse(self.x - self.r,self.y-self.r,self.r*2,self.r*2)
ellipse(self.x - 1,self.y-1,2,2)
else:
if self.deadly:
fill(0,1,0)
else:
fill(1,0,0)
if self.isEgg:
fill(1,1,1)
ellipse(self.x - self.r,self.y-self.r,self.r*2,self.r*2)
class Ship (object):
def __init__(self):
global scene
self.x = scene.w/2
self.y = scene.h/2
self.xvel = 0
self.yvel = 0
self.m = 3
self.r = rscale * sqrt(self.m)
def update(self):
if self.y + self.yvel > scene.h:
self.yvel = - self.yvel
if self.x + self.xvel > scene.w:
self.xvel = - self.xvel
if self.x + self.xvel < 0:
self.xvel = - self.xvel
if self.y + self.yvel < 0:
self.yvel = - self.yvel
self.x += self.xvel
self.y += self.yvel
def draw(self):
g = gravity()
ellipse(self.x-self.r, self.y-self.r, self.r*2,self.r*2)
class MyScene (Scene):
def __init__(self):
self.w=350
self.h=600
def setup(self):
# This will be called before the first frame is drawn.
global ship
ship = Ship()
stroke(0,1,0)
stroke_weight(1)
pass
def draw(self):
self.w=self.size.w
self.h=self.size.h
# This will be called for every frame (typically 60 times per second).
background(0, 0, 0)
# Draw a red circle for every finger that touches the screen:
global ship
global cis
global restart
if not cis.collision:
g=gravity()
line(ship.x, ship.y, ship.x+leng*g.x,ship.y+leng*g.y)
ship.update()
cis.update()
global score
score += cis.m / 60.0
if cis.collision and restart is 0:
self.gameOver()
if cis.collision and restart != 0 and self.t > restart:
ship = Ship()
cis = ChickensInSpace()
restart = 0
global score
score =0
fill(1,0,1)
ship.draw()
fill(1, 0, 0)
cis.draw()
text(str(int(score)),"helvetica",16,20,20,5)
def touch_began(self, touch):
c = Chicken(ship.x,ship.y,cannonForce * gravity().x,cannonForce * gravity().y)
ship.xvel -= c.xvel * c.m / ship.m
ship.yvel -= c.yvel * c.m / ship.m
global cis
cis.arr.append(c)
cis.m+=1
pass
def touch_moved(self, touch):
pass
def touch_ended(self, touch):
pass
def gameOver(self):
global restart
restart = self.t + 3
scene = MyScene()
cis = ChickensInSpace()
run(scene)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment