Skip to content

Instantly share code, notes, and snippets.

@TheCherry
Last active March 25, 2018 02:42
Show Gist options
  • Save TheCherry/2e71893d01bc54c65ee22b17c173ef27 to your computer and use it in GitHub Desktop.
Save TheCherry/2e71893d01bc54c65ee22b17c173ef27 to your computer and use it in GitHub Desktop.
# This file is part of DEAP.
#
# DEAP is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# DEAP is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with DEAP. If not, see <http://www.gnu.org/licenses/>.
# example which maximizes the sum of a list of integers
# each of which can be 0 or 1
path_to_food = "Food/"
ban_items = [
"Ecoylent"
, "Transglutaminase"
, "Milk"
# , "Bear S U P R E M E"
# , "Elk Wellington"
# , "Fried Hare Haunches"
# , "Boiled Sausage"
# , "Seared Meat"
# , "Corn Fritters"
]
craftstations = [
"CampfireObject"
, "BakeryOvenObject"
, "StoveObject"
, "CastIronStoveObject"
, "ButcheryTableObject"
, "MillObject"
, "KitchenObject"
# , "LaboratoryObject"
]
global max_food_combinations, ban_items
generations = 250
max_food_combinations = 8
import random
from deap import base
from deap import creator
from deap import tools
import multiprocessing
from os import listdir
import re
REGEX_FOOD = r"FriendlyName\s*{\s*get\s*{\s*return\s*\"(?P<name>.*?)\";\s*}\s*}.*Carbs\s*=\s*(?P<carbs>\d*),\s*Fat\s*=\s*(?P<fat>\d*),\s*Protein\s*=\s*(?P<protein>\d*),\s*Vitamins\s*=\s*(?P<vitamins>\d*).*float\s*Calories\s*{\s*get\s*{\s*return\s*(?P<calories>\d*);\s*}\s*}.*CraftingComponent.AddRecipe\(typeof\((?P<craft>.*?)\),\s*this\);"
def load_data(path):
global ban_items
# get file-list
data = []
for _file in listdir(path):
with open(path + _file) as f:
cs_txt = f.read()
m = re.finditer(REGEX_FOOD, cs_txt, re.DOTALL)
for matchNum, match in enumerate(m):
matchNum = matchNum + 1
if(match.group("name") not in ban_items):
if(len(only_craft) == 0 or match.group("craft") in craftstations):
if(not (int(match.group("carbs")) == 0 and \
int(match.group("fat")) == 0 and \
int(match.group("protein")) == 0 and \
int(match.group("vitamins")) == 0)): # dont add zero food
data.append({
"name": match.group("name"),
"craft": match.group("craft"),
"carbs": int(match.group("carbs")),
"fat": int(match.group("fat")),
"protein": int(match.group("protein")),
"vitamins": int(match.group("vitamins")),
"calories": int(match.group("calories")),
"nutrients": int(match.group("carbs")) + int(match.group("fat")) + int(match.group("protein")) + int(match.group("vitamins"))
})
return data
global data
data = load_data(path_to_food)
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)
toolbox = base.Toolbox()
pool = multiprocessing.Pool()
toolbox.register("map", pool.map)
# Attribute generator
# define 'attr_bool' to be an attribute ('gene')
# which corresponds to integers sampled uniformly
# from the range [0,1] (i.e. 0 or 1 with equal
# probability)
toolbox.register("attr_bool", random.randint, 0, len(data)+1)
# Structure initializers
# define 'individual' to be an individual
# consisting of 100 'attr_bool' elements ('genes')
toolbox.register("individual", tools.initRepeat, creator.Individual,
toolbox.attr_bool, max_food_combinations)
# define the population to be a list of individuals
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
# the goal ('fitness') function to be maximized
def evalOneMax(gene):
"""
Helper method used to return the fitness for the chromosome based
on its gene.
"""
all_nutrients_cal = 0
all_cal = 0
weight_fat = 0
weight_carbs = 0
weight_protein = 0
weight_vitamins = 0
modificator = 0
for gen in gene:
if(gen < len(data)):
all_nutrients_cal += (data[gen]["nutrients"] * data[gen]["calories"])
all_cal += data[gen]["calories"]
weight_fat += (data[gen]["calories"] * data[gen]["fat"])
weight_carbs += (data[gen]["calories"] * data[gen]["carbs"])
weight_protein += (data[gen]["calories"] * data[gen]["protein"])
weight_vitamins += (data[gen]["calories"] * data[gen]["vitamins"])
modificator -= 0.2 # less is more!
if(all_cal == 0):
return 0,
fat = weight_fat / all_cal
carbs = weight_carbs / all_cal
protein = weight_protein / all_cal
vitamins = weight_vitamins / all_cal
balance = (sum([fat, carbs, protein, vitamins]) / (max([fat, carbs, protein, vitamins])*4))*2.0
return (((all_nutrients_cal / all_cal) * balance)+modificator),
#----------
# Operator registration
#----------
# register the goal / fitness function
toolbox.register("evaluate", evalOneMax)
# register the crossover operator
toolbox.register("mate", tools.cxTwoPoint)
# register a mutation operator with a probability to
# flip each attribute/gene of 0.05
toolbox.register("mutate", tools.mutFlipBit, indpb=0.1)
# operator for selecting individuals for breeding the next
# generation: each individual of the current generation
# is replaced by the 'fittest' (best) of three individuals
# drawn randomly from the current generation.
toolbox.register("select", tools.selTournament, tournsize=3)
#----------
def main():
# random.seed(64)
# create an initial population of 300 individuals (where
# each individual is a list of integers)
pop = toolbox.population(n=2500)
# CXPB is the probability with which two individuals
# are crossed
#
# MUTPB is the probability for mutating an individual
CXPB, MUTPB = 0.5, 0.5
print("Start of evolution")
# Evaluate the entire population
fitnesses = list(map(toolbox.evaluate, pop))
for ind, fit in zip(pop, fitnesses):
ind.fitness.values = fit
print(" Evaluated %i individuals" % len(pop))
# Extracting all the fitnesses of
fits = [ind.fitness.values[0] for ind in pop]
# Variable keeping track of the number of generations
g = 0
best_ind = None
best_fit = 0
# Begin the evolution
while g < generations:
# A new generation
g = g + 1
print("-- Generation %i --" % g)
# Select the next generation individuals
offspring = toolbox.select(pop, len(pop))
# Clone the selected individuals
offspring = list(map(toolbox.clone, offspring))
# Apply crossover and mutation on the offspring
for child1, child2 in zip(offspring[::2], offspring[1::2]):
# cross two individuals with probability CXPB
if random.random() < CXPB:
toolbox.mate(child1, child2)
# fitness values of the children
# must be recalculated later
del child1.fitness.values
del child2.fitness.values
for mutant in offspring:
# mutate an individual with probability MUTPB
if random.random() < MUTPB:
toolbox.mutate(mutant)
del mutant.fitness.values
# Evaluate the individuals with an invalid fitness
invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
fitnesses = map(toolbox.evaluate, invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):
ind.fitness.values = fit
print(" Evaluated %i individuals" % len(invalid_ind))
# The population is entirely replaced by the offspring
pop[:] = offspring
# Gather all the fitnesses in one list and print the stats
fits = [ind.fitness.values[0] for ind in pop]
length = len(pop)
mean = sum(fits) / length
sum2 = sum(x*x for x in fits)
std = abs(sum2 / length - mean**2)**0.5
print(" Min %s" % min(fits))
print(" Max %s" % max(fits))
print(" Avg %s" % mean)
print(" Std %s" % std)
if(max(fits) > best_fit):
best_ind = tools.selBest(pop, 1)[0]
best_fit = best_ind.fitness.values[0]
print("-- End of (successful) evolution --")
# best_ind = tools.selBest(pop, 1)[0]
print("Best individual is %s, %s" % (best_ind, best_ind.fitness.values))
for val in best_ind:
if(val < len(data)):
print(data[val]["name"])
if __name__ == "__main__":
main()
@goblix
Copy link

goblix commented Mar 23, 2018

trying to run this on windows machine... getting errors:

S:\eco>python find_best_food.py
find_best_food.py:43: SyntaxWarning: name 'ban_items' is assigned to before global declaration
  global max_food_combinations, ban_items
C:\Python27\lib\site-packages\deap\tools\_hypervolume\pyhv.py:33: ImportWarning: Falling back to the python version of hypervolume module. Expect this to be very slow.
  "module. Expect this to be very slow.", ImportWarning)
Traceback (most recent call last):
  File "find_best_food.py", line 94, in <module>
    data = load_data(path_to_food)
  File "find_best_food.py", line 76, in load_data
    if(len(only_craft) == 0 or match.group("craft") in craftstations):
NameError: global name 'only_craft' is not defined

doesn't look like only_craft is defined anywhere, if I remove the only_craft part in the if statement i think it loops indefinitely.

is their any requirements that I may be missing ?
Python 2.7.14
installed deap via pip:

Name: deap
Version: 1.2.2
Summary: Distributed Evolutionary Algorithms in Python
Home-page: https://www.github.com/deap
Author: deap Development Team
Author-email: deap-users@googlegroups.com
License: LGPL
Location: c:\python27\lib\site-packages
Requires:

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