Skip to content

Instantly share code, notes, and snippets.

@pnathan
Created January 1, 2019 23:42
Show Gist options
  • Save pnathan/309dc6a347cbb256e0ea14fea2c87415 to your computer and use it in GitHub Desktop.
Save pnathan/309dc6a347cbb256e0ea14fea2c87415 to your computer and use it in GitHub Desktop.
World simulator, has similarties and inspirations from dwarf fortress
# Licensed under AGPL3
from pprint import pprint, pformat
from enum import Enum
world = []
buf=""
def flush():
global buf
print(buf)
buf=""
def buffer(s):
global buf
buf+=s+"\n"
if len(buf) > 4096:
flush()
def classify(*enums):
base="_".join(enums)
return type(base, (object,),
dict(
[(e, type(e, (object,), {"name":e})) for e in enums]))
state = classify("resting",
"moving",
"angry",
"attacking",
"out",
"horny",
"babymaking",
"dead")
class ch(object):
def __init__(self,
first_name,
last_name,
gender,
volatility,
relations,
attrs=None):
self.first_name=first_name
self.last_name=last_name
self.name = first_name + " " + last_name
self.gender=gender
self.health=random.choice(range(5, 15)) * 1.0
self.volatility=volatility
self.loc=None
self.state = state.resting
self.target=None
self.history=[]
self.clock = 0
self.relations=relations
if not attrs:
self.attrs=dict()
else:
self.attrs=attrs
def with_clock(self, age):
self.clock=age
return self
def with_gender(self, g):
self.gender=g
return self
def __str__(self):
if self.loc:
s = "<npc %s %s HP %s @ %s>" % (self.name, self.health, self.gender, self.loc.name)
else:
s = "<npc %s %s HP %s>" % (self.name, self.health, self.gender)
return s
def __repr__(self):
return __str__(self)
def longform(self):
r = ", ".join(map(lambda s: str(self.loc.world.relations[self][s]),
self.loc.world.relations[self]))
return " ".join([self.name, str(self.age()), str(self.health), self.gender, r])
def age(self):
return self.clock
def __repr__(self):
return str(self)
def ack(self, loc):
self.loc = loc
def unack(self, loc):
self.loc = None
def istyped(self):
return True
def give_birth(self):
father=self.attrs['mate']
g = 'm' if random.random() > 0.5 else 'f'
baby = ch(
random.choice(first_m) if g =='m' else random.choice(first_f),
self.last_name,
g,
self.volatility,
[self, father]).with_clock(0)
self.loc.world.place(baby, "bedroom")
self.loc.world.set_relations(baby, self, "%s's daughter" % self.name)
self.loc.world.set_relations(self, baby, "%s's mother" % baby.name)
self.loc.world.set_relations(baby, father, "%s's daughter" % father.name)
self.loc.world.set_relations(father, baby, "%s's father" % baby.name)
del self.attrs['pregnant']
del self.attrs['mate']
self.say("A baby has arrived! %s" % baby.name)
def state_change(self):
if self.state == state.dead:
return
if self.clock >= 80:
self.say("they were good years")
self.state = state.dead
return
if 'pregnant' in self.attrs:
if self.attrs['pregnant'] <= self.loc.world.clock - 10:
self.give_birth()
if self.health <= 0:
self.state = state.out
self.target = None
return
if self.state == state.attacking or self.state==state.babymaking or self.state == state.horny:
return
if self.state == state.out:
if self.health > 0:
self.state = state.resting
elif self.health < 10:
self.say("I have been murdered!")
self.state = state.dead
return
wiggle = random.random() + self.volatility
if wiggle <= 0.2:
self.state = state.resting
elif wiggle > 0.2 and wiggle <= 0.7:
self.state = state.moving
elif wiggle > 0.7 and wiggle <= 0.95:
if self.age() < 13:
self.state = state.moving
elif self.age() >= 13 and self.age() < 55:
self.state = state.horny
else:
self.state = state.resting
elif wiggle > 0.95:
self.state = state.angry
def related_to(self, other):
self.loc.world.is_related_to(self, other)
def say(self, msg):
print("%s: %s" % (self.name, msg))
def hit_on(self, other):
proc = random.random()
if proc > 0.3:
self.say("I made sweet sweet love to %s" % other.name)
other.state = state.babymaking
other.target = self
self.target = other
self.state = state.babymaking
def attack(self, other):
aval = random.random() * 30
proc = random.random()
other.state = state.attacking
other.target = self
if proc > 0.3:
other.health -= aval
self.say("Got you, %s (%s)!" % (other.target.name, other.target.health))
def interact(self):
if self.state == state.dead:
return
self.say("%s @ %s" % (self.state.name, self.attrs))
if self.target and (self.state == state.angry or self.state == state.attacking):
self.say("Gunning for %s" % self.target)
# if others beside you are here
others = list(set(self.loc.npcs) - set((self,)))
if self.state == state.angry:
target_choices = tuple(filter(lambda o: o.age() > 13, others))
if len(target_choices) > 0 and self.age() > 13:
# and if they are
target=random.choice(target_choices)
if not target.state == state.out:
self.say("I'mma mess you up, %s" % target.name)
self.state = state.attacking
self.target=target
else:
self.say("I'm pissed")
elif (self.state == state.attacking and
self.target in self.loc.npcs):
self.say("You're going down, %s" % self.target.name)
self.attack(self.target)
if self.target.health <= 0:
self.state = state.moving
self.say("Feels good, man")
self.target.state = state.out
self.target.target = None
self.target.history.append("Attached by %s" % self)
self.target = None
elif self.state == state.horny:
available = list(filter(lambda o: (o.gender != self.gender and
not self.related_to(o)), others))
if available:
selected=random.choice(available)
self.say("hey baby %s" % selected)
self.hit_on(selected)
elif self.state == state.babymaking:
if self.gender == 'f':
self.history.append("made baby with %s" % self.target)
self.attrs['mate'] = self.target
import copy
self.attrs['pregnant'] = copy.copy(self.loc.world.clock)
elif self.gender == 'm':
self.history.append("made baby with %s" % self.target)
self.state = state.resting
elif self.state == state.moving:
exits = self.loc.find_exit()
if exits:
e=random.choice(exits)
self.say("moving on to %s" % e.name)
self.loc.world.move_to(self, e)
else:
self.say("I appear to be stuck")
elif self.state == state.out:
self.say(" zzz")
self.health += 1
else:
self.health = min(self.health + 1, 20)
first_m=["Biff", "Alex", "Anders", "Cap", "Tim", "Tank", "Steve", "Uri", "Moses", "Isaac", "Ben", "Simon", "Ruben"]
first_f=["Suzy", "Jean", "Billy", "Samantha", "Alane", "Audra", "Sarah", "Ella", "Artemis"]
last=["Smith", "Ohno", "King", "Tanner", "Bob", "Jean", "Rogers", "Stone",
"Goldberg", "Greenspun", "Kaplan", "Perlman", "Stein"]
import random
def random_ch(g=None):
if not g:
g = 'm' if random.random() > 0.5 else 'f'
if g =='m':
first = random.choice(first_m)
else:
first=random.choice(first_f)
return ch(first, random.choice(last), g, random.random() * 0.3, [])
class location(object):
def __init__(self, name, world):
self.name=name
self.npcs=[]
self.world = world
def find_exit(self):
return self.world.get_connected(self)
def add(self, npc):
assert npc.istyped() == True
self.npcs.append(npc)
npc.ack(self)
def remove(self, npc):
assert npc.istyped() == True
self.npcs.remove(npc)
npc.unack(self)
def has(self):
return self.npcs
def __repr__(self):
return "<loc %s %s>" % (self.name, pformat(self.npcs)) if self.npcs else "<loc %s>" % self.name
class AutoVivification(dict):
"""Implementation of perl's autovivification feature."""
#https://stackoverflow.com/a/651879
def __getitem__(self, item):
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value
def world_printer(w):
pprint(dict([(n.name, n.loc.name) for n in w.npcs]))
class World(object):
def __init__(self):
self.relations=AutoVivification()
self.npcs=[]
self.pm=AutoVivification()
self.locs={}
self.clock=0
def set_relations(self, npc1, npc2, md):
self.relations[npc1][npc2] = md
import functools
@functools.lru_cache(maxsize=256)
def is_related_to(self, npc1, npc2):
visited=set()
nexts=[npc1]
while len(nexts)>0:
n = nexts.pop()
visited.add(n)
related=self.relations[n]
if npc2 in related:
return True
nexts.extend([a for a in related if a not in visited])
return False
def __repr__(self):
return (pformat(self.pm) + "\n" +
pformat(list(self.locs.values())))
def get_connected(self, p):
connected = [self.locs[l] for l in self.pm[p.name].keys()]
return connected
def connect(self, p, connected_to):
if not p in self.locs.keys():
self.locs[p] = location(p, self)
for other in connected_to:
if not other in self.locs:
self.locs[other] = location(other, self)
self.pm[p][other]=1
self.pm[other][p]=1
return self
def move_to(self, npc, location):
npc.loc.remove(npc)
self.locs[location.name].add(npc)
def place(self, npc, location):
if npc not in self.npcs:
self.npcs.append(npc)
self.locs[location].add(npc)
def step(self):
self.clock +=1
valid_npcs = [ n for n in self.npcs if n.state != state.dead]
for n in valid_npcs:
n.state_change()
for n in valid_npcs:
n.interact()
if n.state != state.dead:
n.clock += 1
def dead_world(self):
valid_npcs = [ n for n in self.npcs if n.state != state.dead]
return len(valid_npcs) == 0
def print_npcs(self):
for n in self.npcs:
print(n.longform())
print("A total of %s people lived" % len(self.npcs))
a=len(list(filter(lambda n: n.state != state.dead, self.npcs)))
print("And %s are still alive" % a)
PM=World()
PM.connect("street", ["neighbor", "downtown"])
PM.connect("downtown", ["work", "bus depot", "pub", "club"])
PM.connect("front door", ["street", "yard"])
PM.connect("front door", ["living room",
"office",
"kitchen"])
PM.connect("living room", ["bedroom"])
#n2=random_ch().with_clock(13).with_gender('m')
PM.place(random_ch('f').with_clock(14), "neighbor")
PM.place(random_ch('f').with_clock(14), "neighbor")
PM.place(random_ch('f').with_clock(14), "neighbor")
PM.place(random_ch('m').with_clock(14), "neighbor")
PM.place(random_ch('f').with_clock(14), "neighbor")
PM.place(random_ch('f').with_clock(14), "neighbor")
PM.place(random_ch('f').with_clock(14), "neighbor")
#PM.place(n2, "neighbor")
#PM.place(random_ch().with_clock(13).with_gender('f'), "neighbor")
#PM.place(random_ch().with_clock(13).with_gender('f'), "neighbor")
#us1=random_ch().with_clock(13).with_gender('m')
PM.place(random_ch('m').with_clock(13), "bedroom")
PM.place(random_ch('m').with_clock(13), "pub")
PM.place(random_ch('m').with_clock(13), "club")
#PM.place(us2, "bedroom")
try:
for i in range(0, 240):
print("-" * 20, i, "-" * 20)
PM.step()
if PM.dead_world():
break
except KeyboardInterrupt:
pass
flush()
PM.print_npcs()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment