Skip to content

Instantly share code, notes, and snippets.

@jinie
Created November 6, 2012 23:16
Show Gist options
  • Save jinie/4028376 to your computer and use it in GitHub Desktop.
Save jinie/4028376 to your computer and use it in GitHub Desktop.
Zombie Apocalypse Automation
#!/usr/bin/python
from Tkinter import *
from random import randint
import timeit
from optparse import OptionParser
worldInstance = None
class Direction(object):
#directions
NONE = -1
NORTH = 0
NORTHEAST = 1
EAST = 2
SOUTHEAST = 3
SOUTH = 4
SOUTHWEST = 5
WEST = 6
NORTHWEST = 7
directionList = [NORTH,NORTHEAST,EAST,SOUTHEAST,SOUTH,SOUTHWEST,WEST,NORTHWEST]
@staticmethod
def left(direction):
nd = direction - 1
if(nd < 0):
nd = Direction.NORTHWEST
return nd
@staticmethod
def right(direction):
nd = direction + 1
if(nd > Direction.NORTHWEST):
nd = Direction.NORTH
return nd
@staticmethod
def turnaround(direction):
nd = direction
for i in xrange(4):
nd = Direction.right(nd)
return nd
@staticmethod
def coordsToDirection(fromx, fromy, tox, toy):
if tox < fromx:
if(toy == fromy):
return Direction.EAST
elif(toy < fromy):
return Direction.SOUTHEAST
else:
return Direction.NORTHEAST
elif tox > fromx:
if(toy == fromy):
return Direction.WEST
elif(toy < fromy):
return Direction.SOUTHWEST
else:
return Direction.NORTHWEST
elif tox == fromx:
if(toy < fromy):
return Direction.SOUTH
else:
return Direction.NORTH
class Person(object):
"""
Normal Person
Travels in straight lines when not fleeing
Will seek company of other persons.
Will panic and flee when it sees a zombie
Will panic and flee when it sees another person panicing
!Speed is NOT implemented!
Normal speed is 2.
Panic speed is 5.
Has a 20% increasing chance of regaining control pr. round when in panic.
Will stop fleeing when a zombie has not been seen for 3 rounds.
"""
x = 0
y = 0
infected = False
incubation_time = 0
panic = False
in_the_clear = 0
speed = 0
direction = Direction.NORTH
NORMAL_SPEED = 2
CHASE_SPEED = 5
def __init__(self, x, y):
self.x = x
self.y = y
self.direction = Direction.directionList[randint(0,len(Direction.directionList)-1)]
def beginPanic(self):
self.panic = True
self.in_the_clear = 0
self.speed = self.CHASE_SPEED
self.direction = Direction.turnaround(self.direction)
def endPanic(self):
self.panic = False
self.speed = self.NORMAL_SPEED
def infect(self):
if(self.infected):
#ignore for now, later 2x infection = dead
pass
else:
self.infected = True
self.incubation_time = 3
self.speed = 0
def update(self, world):
if(self.infected):
self.incubation_time -= 1
if(self.incubation_time <= 0):
#BRAAAAINNNS
world.set(self.x, self.y, Zombie(self.x, self.y))
return
valid_moves = []
follow = []
panic = False
for d in [
Direction.left(self.direction),
Direction.left(Direction.left(self.direction)),
self.direction,
Direction.right(self.direction),
Direction.right(Direction.right(self.direction))
]:
x,y,o = world.objectInDirection(self.x,self.y,d)
if o == None:
valid_moves.append(d)
if isinstance(o,Zombie):
self.direction = d
self.beginPanic()
panic = True
break
if isinstance(o,Person):
if(o.panic == False):
follow.append(d)
else:
self.direction = d
self.beginPanic()
panic = True
break
if self.panic:
if panic == False:
self.in_the_clear += 1
if(self.in_the_clear == 5):
self.endPanic()
elif(randint(1,100) < self.in_the_clear * 20):
self.endPanic()
else:
if len(follow) > 0:
self.direction = follow[randint(0,len(follow)-1)]
else:
self.direction = valid_moves[randint(0, len(valid_moves) - 1)]
x, y, o = world.objectInDirection(self.x, self.y, self.direction)
if(o == None):
self.x, self.y = world.moveInDirection(self.x, self.y, self.direction)
class Zombie(object):
"""
Zombie
Will chase regular persons.
Will wander around randomly if no persons can be found.
kills things directly in front of it
!Speed is NOT implemented!
Normal speed is 1
"""
x = 0
y = 0
chasing = False
direction = Direction.NORTH
speed = 0
health = 100
decayRate = 2
NORMAL_SPEED = 1
CHASE_SPEED = 1
def __init__(self, x, y):
self.x = x
self.y = y
def infect(self, p):
#gotcha
p.infect()
self.chasing = False
self.speed = self.NORMAL_SPEED
def update(self, world):
self.health -= self.decayRate
if(self.health < 1):
#Dead for real this time.
world.set(self.x, self.y, None)
return
leftx, lefty, lefto = world.objectInDirection(self.x, self.y, Direction.left(self.direction))
aheadx, aheady, aheado = world.objectInDirection(self.x, self.y, self.direction)
rightx, righty, righto = world.objectInDirection(self.x, self.y, Direction.right(self.direction))
#can we find someone to kill ?
valid_targets = []
if isinstance(aheado,Person):
valid_targets.append(aheado)
if isinstance(lefto,Person):
valid_targets.append(lefto)
if isinstance(righto,Person):
valid_targets.append(righto)
if(len(valid_targets)> 0):
o = valid_targets[randint(0,len(valid_targets)-1)]
self.direction = Direction.coordsToDirection(self.x,self.y,o.x,o.y)
self.infect(o)
else:
valid_moves = []
if(lefto == None):
valid_moves.append(Direction.left(self.direction))
if(righto == None):
valid_moves.append(Direction.right(self.direction))
if(aheado == None):
valid_moves.append(self.direction)
if(len(valid_moves) == 0):
#stuck
return
nd = valid_moves[randint(0, len(valid_moves) - 1)]
self.x, self.y = world.moveInDirection(self.x, self.y, nd)
class World(object):
world = []
empty_ct = 0
person_ct = 0
zombie_ct = 0
infected_ct = 0
unknown_ct = 0
turn = 0
verbose = False
log = None
def __init__(self, width, height, verbose=False, log=None):
super(World, self).__init__()
self.height = height
self.width = width
for y in range(self.height):
for x in range(self.width):
if(x % 5 == 0 and y % 3 == 0):
self.world.append(Person(x, y))
self.person_ct += 1
else:
self.world.append(None)
self.empty_ct += 1
self.set(0, 0, Zombie(0, 0))
self.zombie_ct += 1
self.verbose = verbose
self.log = log
if(self.verbose):
print "persons,zombies,infected,empty,unknown"
def objectAt(self, x, y):
return self.world[(self.width * y) + x]
def coordsInDirection(self, ox, oy, direction):
x = ox
y = oy
#sanity checks
if(x < 0 or x > self.width):
raise "x must be between 0 and ", self.width
if(y < 0 or y > self.height):
raise "y must be between 0 and ", self.height
#get direction, we have more than one...
if(direction == Direction.NORTH):
y -= 1
elif(direction == Direction.NORTHEAST):
y -= 1
x += 1
elif(direction == Direction.EAST):
x += 1
elif(direction == Direction.SOUTHEAST):
x += 1
y += 1
elif(direction == Direction.SOUTH):
y += 1
elif(direction == Direction.SOUTHWEST):
y += 1
x -= 1
elif(direction == Direction.WEST):
x -= 1
elif(direction == Direction.NORTHWEST):
x -= 1
y -= 1
if(x >= self.width):
x = 0
elif(x < 0):
x = self.width - 1
if(y >= self.height):
y = 0
elif(y < 0):
y = self.height - 1
#sanity checks
if(x < 0 or x > self.width):
raise "ox(%d) converts to %d, must be between 0 and " % (ox, x, self.width)
if(y < 0 or y > self.height):
raise "oy(%d) converts to %d, must be between 0 and " % (oy, y, self.height)
return x, y
def objectInDirection(self, ox, oy, direction):
x, y = self.coordsInDirection(ox, oy, direction)
return x, y, self.objectAt(x, y)
def moveInDirection(self, ox, oy, direction):
nx, ny = self.coordsInDirection(ox, oy, direction)
if(self.objectAt(nx, ny) == None):
self.set(nx, ny, self.objectAt(ox, oy))
self.set(ox, oy, None)
return nx, ny
else:
raise BaseException('Cannot move to occupied space (%d,%d (%s) => %d,%d (%s))' % (ox, oy, str(self.objectAt(ox, oy)), nx, ny, str(self.objectAt(nx, ny))))
def set(self, x, y, value):
self.world[(self.width * y) + x] = value
def update(self):
empty_ct = 0
person_ct = 0
zombie_ct = 0
infected_ct = 0
unknown_ct = 0
for y in range(self.height):
for x in range(self.width):
p = self.objectAt(x, y)
if(p == None):
empty_ct += 1
continue
elif(isinstance(p, Person)):
person_ct += 1
if(p.infected == True):
infected_ct += 1
elif(isinstance(p, Zombie)):
zombie_ct += 1
p.update(self)
self.person_ct = person_ct
self.zombie_ct = zombie_ct
self.empty_ct = empty_ct
self.infected_ct = infected_ct
self.turn += 1
if(self.verbose):
print self.turn, person_ct, zombie_ct, infected_ct, empty_ct, unknown_ct
class TkMainWindow:
worldInstance = None
def __init__(self, width, height, scale, worldInstance):
self.worldInstance = worldInstance
self.master = Tk()
self.personCtVar = StringVar()
self.zombieCtVar = StringVar()
self.infectedCtVar = StringVar()
self.personLabel = Label(self.master, text="Persons")
self.personCount = Entry(self.master,state=NORMAL, textvariable=self.personCtVar)
self.infectedLabel = Label(self.master, text="Infected")
self.infectedCount = Entry(self.master,state=NORMAL, textvariable=self.infectedCtVar)
self.zombieLabel = Label(self.master, text="Zombies")
self.zombieCount = Entry(self.master,state=NORMAL, textvariable=self.zombieCtVar)
self.startButton = Button(text="Start", command=self.startButtonCallback)
self.personLabel.grid(row=0, column=0)
self.personCount.grid(row=0, column=1)
self.infectedLabel.grid(row=0, column=2)
self.infectedCount.grid(row=0, column=3)
self.zombieLabel.grid(row=0, column=4)
self.zombieCount.grid(row=0, column=5)
self.startButton.grid(row=0, column=6)
self.canvas = Canvas(self.master, width=width * scale, height=height * scale)
self.canvas.grid(row=1,column=0,columnspan=7)
#self.canvas.pack()
#self.master.after(100, self.update)
self.width = width
self.height = height
self.scale = scale
mainloop()
def startButtonCallback(self):
if(self.master!=None):
self.startButton.config(state=DISABLED)
self.master.after(10,self.update)
def update(self):
self.canvas.delete(ALL)
self.worldInstance.update()
for y in range(self.worldInstance.height):
for x in range(self.worldInstance.width):
o = self.worldInstance.objectAt(x, y)
if(o != None):
color = 'green'
if(isinstance(o, Zombie)):
color = 'purple'
elif(isinstance(o, Person)):
if(o.infected):
color = 'pink'
if(o.panic):
color = 'red'
self.canvas.create_rectangle(x, y, x + 1, y + 1, fill=color, outline=color)
self.canvas.scale(ALL, 0, 0, self.scale, self.scale)
self.personCtVar.set(str(self.worldInstance.person_ct))
self.infectedCtVar.set(str(self.worldInstance.infected_ct))
self.zombieCtVar.set(str(self.worldInstance.zombie_ct))
if(self.worldInstance.person_ct > 0 and self.worldInstance.zombie_ct > 0):
self.master.after(10, self.update)
else:
self.startButton.config(state=NORMAL)
def run(verbose=False, log=None):
TkMainWindow(200, 100, 4,World(200, 100))
if __name__ == '__main__':
parser = OptionParser()
parser.add_option("-l","--log", dest="log", help="log output to FILE", default=False, metavar="FILE")
parser.add_option("-v","--verbose", dest="verbose", help="log output to STDOUT")
(options,args) = parser.parse_args()
run(options.verbose, options.log)
#print timeit.timeit("zombie_automation.run()", "import zombie_automation", number=1)
@jinie
Copy link
Author

jinie commented Nov 6, 2012

Quick and dirty Zombie Apocalypse Automation, assuming Zombies actually decay over time, and that humans will flock, and panic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment