Gourmake is an Erlang/OTP application which probabalistically generates new recipes based on recipe templates which encode human knowledge. Gourmake was created for a computational creativity project at University.
Gourmake works by searching several databases of recipes, ingredients, cuisines and ingredient categories to stochastically fill in gaps left in the recipes. Because these databases encode human information about ingredients and cuisines, recipes generated by Gourmake generally look relatively real and human made, though there are of course some exceptions.
The easiest way to generate recipes is to run the following:
git clone https://github.com/vereis/gourmake
cd gourmake/src
erl
Then, in the Erlang shell
c(gourmake_supervisor).
c(ingredient_server).
c(recipe_server).
c(name_server).
c(main).
main:main().
Which will return CLI-output of a recipe in Markdown. Eventually Gourmake will output recipes into files.
Please note that Gourmake REQUIRES ERLANG 20
- Cottage Cheese
- Walnut
- Lamb Neck
- Rapeseed Oil
- Beetroot
- Preheat a grill to high heat
- Wash around 800g Beetroot and chop them into 3 - 5cm chunks.
- Heat a pan of water and oil of your choice (perhaps Rapeseed Oil?), add plenty of salt before finally adding your chopped Beetroot
- Cook your Beetroot until softened.
- While you're waiting for them to cook, season your Lamb Neck and place it on another hot pan with some Rapeseed Oil, until a nice sear develops
- Remove your now cooked Lamb Neck, tossing some Walnut in the leftover Lamb Neck juices to develop some flavour
- Drain your Beetroot now that they are softened, grate a hearty portion of Cottage Cheese and mix
- Spread this mixure, alongside your Lamb Neck, into a pan and put into your grill until a golden crust develops
- Feel free to top it with toppings of your choice, and don't forget to garnish it when done with your Walnut
- Rest for 2 - 5 minutes and serve piping hot
Brian's South American inspired simple Lamb Shoulder chilli with Black Beans, Kidney Bean and Pinto Bean
- Turmeric
- Black Beans
- Kidney Bean
- Pinto Bean
- Smoked Cheese
- Manderin
- Oregano
- Chilli Oil
- Lamb Shouler
- Brown Rice
- Heat a large pan, pot or wok to high heat, adding plenty of Chilli Oil
- Fry your Turmeric until aromatic and fragrant
- Make sure you chop your Lamb Shouler into small cubes or mince it before adding to your frying Turmeric to brown a little
- Once nicely seared, transfer the contents of your pan, pot or wok to some container, keeping the cooking juices in your pan, pot or wok
- Add plenty of chopped tomatoes, water and stock if you have any and season well
- Into your tomato mixture, mix in some Cayenne Pepper, Paprika, Cumin and let it simmer
- Mix in your fried Turmeric, Lamb Shouler and Black Beans, Kidney Bean and Pinto Bean
- Cook until softened and reduced before serving on warm Brown Rice(s)
- Feel free to drizzle some Manderin and more Chilli Oil on your Brown Rice(s) for added flavour
- Optinally, top with plenty of Smoked Cheese as well
Gourmake's main business logic is decentralised across three major processes:
- The Ingredient Server which acts as a database containing all of the ingredients currently loaded into Gourmake, as well as information about what ingredients are used in certain cuisines and categories.
- The Recipe Server which acts as a database containing all of the recipe templates currently loaded into Gourmake. The recipe server is responsible for choosing recipes to create, as well as communicating with the Ingredient Server to actually create recipes.
- The Name Server which is just responsible for loading in some string prefixes to recipe names which help individualise each invented recipe.
Gourmake's servers are all in-memory databases. The main.erl
module contains bootstrapping logic as well as a means of easily generating and pretty printing recipes into Markdown via main:main/0
. Otherwise, it is possible to interact with the servers directly in the Erlang shell.
Each server loads its information at spawn time, files loaded are named according and exist in the $PROJECTROOT/data
directory. The files simply contain stored Erlang terms which are read via Erlang's file:consult/1
function. It's possible to manually load more data into the servers either by updating these files and restarting them, or simply pushing data into the processes through their callbacks which are documented in their individual source code files.
Recipes are defined in the following schema:
-type INTERPSTRING() :: string(). %% String patterns in the form ~INTERPKEY are interpolated
-type CUISINE() :: atom().
-type INTERPKEY() :: atom().
-type CATEGORY() :: atom().
-type CATEGORY_BLACKLIST() :: atom().
-type MIN :: non_neg_integer().
-type MAX :: non_neg_integer().
[
#{
name => INTERPSTRING,
cuisines => [
CUISINE, ...
]
ingredients => #{
INTERPKEY => {MIN, MAX, [CATEGORY, ...], [CATEGORY_BLACKLIST, ...]},
...
},
steps => [
INTERPSTRING,
...
]
},
...
]
Ingredients are defined in the following schema:
-type NAME() :: atom().
-type CATEGORY() :: atom().
-type CUISINE() :: atom().
[
{
NAME,
#{
categories => [
CATEGORY,
...
],
cuisines => [
CUISINE,
...
]
}
},
...
]
Categories of ingredients, as well as cuisines of ingredients are generated from this file. NAMEs get pretty printed out as space seperated strings.
Names are simply defined via
[
string(),
...
]