Created
November 6, 2012 23:16
-
-
Save jinie/4028376 to your computer and use it in GitHub Desktop.
Zombie Apocalypse Automation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Quick and dirty Zombie Apocalypse Automation, assuming Zombies actually decay over time, and that humans will flock, and panic.