Skip to content

Instantly share code, notes, and snippets.

@JarbasAl
Last active June 19, 2021 11:35
Show Gist options
  • Select an option

  • Save JarbasAl/fa3d7d15bc827bd2a99ca797f276a4ec to your computer and use it in GitHub Desktop.

Select an option

Save JarbasAl/fa3d7d15bc827bd2a99ca797f276a4ec to your computer and use it in GitHub Desktop.
virtual pet
import random
from time import sleep
from threading import Thread
from datetime import datetime
class VirtualPet(Thread):
def __init__(self, name="VirtualPet", speed=1.0, lifespan=1):
"""
:param name: creature name
:param speed:
:param lifespan: in hours
"""
super().__init__()
self.name = name
self.birthday = None
self.is_predator = False
self.species = "Virtual Pet"
self.stats = {"food": 100,
"water": 100,
"happiness": 100,
"hygiene": 100,
"health": 100,
"energy": 100}
self.is_sleeping = False
self.is_dead = False
self.is_playing = False
self.is_sick = False
self.is_nocturnal = False
self.min_temperature = -50
self.max_temperature = 100
self.cause_of_death = "Not Dead"
self.lifespan = 60 * 60 * lifespan
self.number_of_poo = 0
self.age = 0
self.setDaemon(True)
self.callbacks = []
self.speed = speed
@property
def life_expectancy(self):
return self.lifespan - 0.2 * self.lifespan, \
self.lifespan + 0.2 * self.lifespan
@property
def is_depressed(self):
return self.stats["happiness"] < 30
@property
def is_hungry(self):
return self.stats["food"] < 40
@property
def is_thirsty(self):
return self.stats["water"] < 50
@property
def as_json(self):
data = self.stats.copy()
data["species"] = self.species
data["name"] = self.name
data["min_temperature"] = self.min_temperature
data["max_temperature"] = self.max_temperature
data["is_nocturnal"] = self.is_nocturnal
data["is_predator"] = self.is_predator
data["is_dead"] = self.is_dead
data["is_sick"] = self.is_sick
data["is_playing"] = self.is_playing
data["is_sleeping"] = self.is_sleeping
data["number_of_poo"] = self.number_of_poo
data["birthday"] = str(self.birthday)
data["age"] = self.age
data["lifespan"] = self.lifespan
data["cause_of_death"] = self.cause_of_death
return data
def add_callback(self, callback):
idx = len(self.callbacks)
self.callbacks.append(callback)
return idx
def remove_callback(self, idx):
self.callbacks.pop(idx)
@staticmethod
def validate(value):
value = min(100, value)
value = max(0, value)
return value
def validate_stats(self):
for stat, value in self.stats.items():
self.stats[stat] = self.validate(value)
def update(self, data):
for stat in self.stats:
self.stats[stat] += data[stat]
self.validate_stats()
def decrease_stat(self, stat, speed, steps):
# speed is hours to 0
# step is seconds
self.stats[stat] -= (steps * 10) / (speed * 36)
def increase_stat(self, stat, speed, steps):
# speed is hours to 100
# step is seconds
self.stats[stat] += (steps * 10) / (speed * 36)
def random_event(self):
if not self.is_playing and not self.is_sleeping:
random_number = random.randint(0, 600)
if random_number == 0:
self.number_of_poo = min(self.number_of_poo + 1, 5)
if random_number == 1 and self.stats["hygiene"] > 50 and \
self.is_sick and self.stats["happiness"] > 50 and \
self.stats["health"] > 50:
self.is_sick = False
self.on_healed()
if random_number == 2:
self.stats["health"] += 0.5
if self.life_expectancy[0] < self.age < self.life_expectancy[1] \
and random_number == 3:
self.kill("Old age")
self.stats["health"] = 0
def step(self, steps=1.0):
self.age += steps
# sleep status change
if datetime.now().hour <= 6 or datetime.now().hour >= 22:
if self.is_nocturnal and self.is_sleeping:
self.is_sleeping = False
self.on_wake_up()
elif not self.is_nocturnal and not self.is_sleeping:
self.is_sleeping = True
self.on_sleep()
else:
if self.is_nocturnal and not self.is_sleeping:
self.is_sleeping = True
self.on_sleep()
elif not self.is_nocturnal and self.is_sleeping:
self.is_sleeping = False
self.on_wake_up()
if self.is_sleeping:
# recover health
self.increase_stat("energy", 8, steps)
self.decrease_stat("hygiene", (12 - self.number_of_poo),
steps)
else:
# update stats
if self.is_playing:
self.decrease_stat("energy", 3, steps)
self.decrease_stat("hygiene", 3, steps)
self.decrease_stat("food", 4, steps)
self.decrease_stat("water", 4, steps)
self.increase_stat("happiness", 1, steps)
else:
self.decrease_stat("energy", 4, steps)
self.decrease_stat("hygiene",
4 / (self.number_of_poo + 1), steps)
self.decrease_stat("food", 4, steps)
self.decrease_stat("happiness", 4, steps)
self.decrease_stat("water", 6, steps)
# check if too much poo
if self.is_sick:
self.decrease_stat("health", 24, steps)
else:
if self.stats["hygiene"] <= 40:
self.is_sick = True
self.on_sick("Lack of hygiene")
elif self.stats["happiness"] <= 30 and \
self.stats["energy"] <= 50:
self.is_sick = True
self.on_sick("Depression")
self.random_event()
# enforce 0 - 100 range
self.validate_stats()
# check death
if not self.is_dead:
if self.stats["food"] == 0:
self.kill("Starved")
elif self.stats["health"] == 0:
self.kill("Disease")
elif self.stats["water"] == 0:
self.kill("Died of thirst")
def run(self):
if self.birthday is None:
self.birthday = datetime.now()
while not self.is_dead:
sleep(self.speed)
self.step(self.speed)
for cb in self.callbacks:
try:
cb()
except Exception as e:
print(e)
def kill(self, cause_of_death="unknown"):
self.is_dead = True
self.stats["health"] = 0
self.cause_of_death = cause_of_death
self.on_dead(cause_of_death)
def clean_poo(self):
self.number_of_poo = 0
# events
def on_wake_up(self):
print(self.name + " woke up")
def on_sleep(self):
print(self.name + " is sleeping")
def on_drinking(self, water):
print(self.name + " drank " + str(water) + " water")
def on_eating(self, food, creature=None):
print(self.name + " ate " + str(food) + " food")
def on_dead(self, reason):
print(self.name + " died of " + reason)
def on_sick(self, reason):
print(self.name + " is sick because " + reason)
def on_healed(self):
print(self.name + " healed")
def on_playing_start(self):
print(self.name + " is playing")
def on_playing_stop(self):
print(self.name + " stopped playing")
class PrimordialSlime(VirtualPet):
def __init__(self, name="Slimy", speed=0.5):
super().__init__(name, speed, lifespan=9999999999)
self.species = "Primordial Slime"
self.is_nocturnal = True
self.min_temperature = -100
self.max_temperature = 200
class GooblyGook(VirtualPet):
def __init__(self, name="Goobly", speed=5.0):
super().__init__(name, speed, lifespan=600)
self.species = "Goobly Gook"
self.min_temperature = 20
self.max_temperature = 80
class Grub(VirtualPet):
def __init__(self, name="Grubus", speed=2.0):
super().__init__(name, speed, lifespan=2000)
self.species = "Grub"
self.is_predator = True
self.min_temperature = 0
self.max_temperature = 80
class VirtualHabitat(Thread):
def __init__(self, name="VirtualHabitat",
food=500,
water=1000,
temperature=30):
super().__init__()
self.species = {}
self.name = name
self.food = food
self.water = water
self.temperature = temperature
self.callbacks = []
@property
def creatures(self):
creatures = []
for species in self.species:
for creature in self.species[species]:
creatures.append(creature)
return creatures
@property
def number_of_poo(self):
return sum([c.number_of_poo for c in self.creatures])
def add_callback(self, callback):
idx = len(self.callbacks)
self.callbacks.append(callback)
return idx
def remove_callback(self, idx):
self.callbacks.pop(idx)
def add_creature(self, creature):
if creature.species not in self.species:
self.species[creature.species] = []
self.species[creature.species].append(creature)
self.species[creature.species][-1].start()
def get_random_creature(self,
filter_prey=False,
filter_predators=False,
filter_sleeping=True):
alive = [c for c in self.creatures if not c.is_dead]
if filter_predators:
alive = [c for c in alive if not c.is_predator]
if filter_prey:
alive = [c for c in alive if c.is_predator]
if filter_sleeping:
alive = [c for c in alive if not c.is_sleeping]
if not len(alive):
return None
return random.choice(alive)
def random_event(self, creature):
if not creature.is_dead:
if self.temperature < creature.min_temperature:
creature.kill("Ambient temperature too low")
elif self.temperature > creature.max_temperature:
creature.kill("Ambient temperature too high")
if creature.is_sleeping or creature.is_dead:
return
rand = random.randint(0, 100)
if creature.is_thirsty and self.water > 0:
self.water -= 1
creature.stats["water"] += 1
creature.on_drinking(1)
if not creature.is_predator and self.food > 0 and creature.is_hungry:
self.food -= 1
creature.stats["food"] += 1
creature.on_eating(1)
elif rand < 30 and creature.is_hungry and creature.is_predator:
dead = self.get_random_creature(filter_predators=True)
if dead is not None:
food = 10 + dead.stats["food"]
dead.kill("eaten by " + creature.name)
creature.stats["energy"] = 30 + creature.stats["energy"]
creature.stats["food"] += food
creature.on_eating(food, dead)
creature.stats["happiness"] += 70
elif rand == 2 and not creature.is_playing:
creature.is_playing = True
creature.on_playing_start()
elif rand == 3 and creature.is_playing:
creature.is_playing = False
creature.on_playing_stop()
elif rand == 4 and self.water > 5:
self.water -= 5
creature.stats["water"] += 5
creature.on_drinking(5)
elif random.randint(0, 600) == 0 and \
not creature.is_predator and \
self.food > 5:
self.food -= 5
creature.stats["food"] += 5
creature.on_eating(5)
creature.validate_stats()
def step(self):
for species in self.species:
for creature in self.species[species]:
self.random_event(creature)
def run(self, speed=1):
old_temp = int(self.temperature)
old_water = int(self.water)
old_food = int(self.food)
old_poo = self.number_of_poo
old_num = len(self.creatures)
while len(self.creatures) > 0:
sleep(speed)
self.step()
for cb in self.callbacks:
try:
cb()
except Exception as e:
print(e)
if old_temp != int(self.temperature):
self.on_temperature_change(old_temp, int(self.temperature))
old_temp = int(self.temperature)
if old_water != int(self.water):
self.on_water_change(old_water, int(self.water))
old_water = int(self.water)
if old_food != int(self.food):
self.on_food_change(old_food, int(self.food))
old_food = int(self.food)
if old_poo != self.number_of_poo:
self.on_poo_change(old_poo, self.number_of_poo)
old_poo = self.number_of_poo
if old_num != len(self.creatures):
self.on_creature_number_change(old_num, len(self.creatures))
old_num = len(self.creatures)
def stop(self):
self.running = False
for creature in self.creatures:
creature.kill("Apocalypse")
# actions
def warm(self, value=0.5):
self.temperature += value
def cool(self, value=0.5):
self.temperature -= value
def clean(self):
for c in self.creatures:
c.clean_poo()
def add_food(self, value=50):
self.food += value
def add_water(self, value=50):
self.water += value
# events
def on_temperature_change(self, old, new):
print("TEMPERATURE: " + str(new))
def on_poo_change(self, old, new):
print("POO NUMBER: " + str(new))
def on_food_change(self, old, new):
print("FOOD: " + str(new))
def on_water_change(self, old, new):
print("WATER: " + str(new))
def on_creature_number_change(self, old, new):
print("CREATURE NUMBER: " + str(new))
if __name__ == "__main__":
h = VirtualHabitat(food=500)
h.add_creature(PrimordialSlime())
h.add_creature(PrimordialSlime("Slimou"))
h.add_creature(PrimordialSlime("Slimetic"))
h.add_creature(PrimordialSlime("Slimogic"))
h.add_creature(Grub("Borkus"))
h.add_creature(GooblyGook("Gob"))
h.add_creature(GooblyGook("Gub"))
h.add_creature(GooblyGook("Gaab"))
for c in h.creatures:
# randomize initial stats
for stat in c.stats:
if stat in ["hygiene"]:
continue
c.stats[stat] = random.randint(40, 90)
def cb():
# create food out of nowhere
h.add_food(0.02)
h.add_water(0.02)
# temperature fluctuation
if random.choice([True, False]):
h.warm(0.01)
else:
h.cool(0.01)
if h.number_of_poo > 10:
print("I HATE CLEANING THE DAMN POO")
h.clean()
h.add_callback(cb)
h.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment