I have been meddling with an idea in C# to create a piece of software (for pseudo-personal usage) but ran into implementation problems. Well... maybe they are design problems I am unaware of, but I just simply can't figure out.
The idea, expectation and general pseudo algorithm
Consider we have the following "database":
Foo + Bar = Foobar
Baz + Qux = Bazqux
Foobar + Bazqux = Foobarbazqux
These are recipes for creating the said items. Using one Foo
and one Bar
, we create a Foobar
result which can further be an ingredient for another recipe.
Now think that we need to figure out the full recipe for the item Foobarbazqux
. For the human mind and intelligence, it's relatively easy:
We need Foobarbazqux
Foobarbazqux = Foobar + Bazqux
Foobar = Foo + Bar
Bazqux = Baz + Qux
Foobarbazqux = Foo + Bar + Baz + Qux
And of course, if and when we have all ingredients at our disposal, we can "run" the recipe and create the items with executing each recipe in the order a few paragraphs above. (So like: we first create Foobar
, then Bazqux
, then combine the two to get the final result.)
But in real time, the database is a lot bigger. That's where, I suppose, computers should come in. With a working software, the machine could easily find all the ingredients and execution (crafting) steps and give it to the user, so we don't need to read through ALL the entries by hand.
The implementation (... attempt)
I have thought about using C# for achieving this piece of software. Command-line project, nothing shiny or anything, just pure stdio
.
First, of course, I needed to define the item. (Note: Right now I think about using structs, but if needed, I can "upgrade" them to classes. The "database" is initialized from some sort of sequential text file which will be made later. The "database" is not altered or written in any manner during or after execution, once it is loaded, it's read-only.) And of course, I needed to define the recipe.
struct Item
{
public string Name;
}
struct Recipe
{
public Item Result;
public Item[] Ingredients;
}
These two structs are held in a generic, static
List<>
field of Program
(the default class).
At entry point, we define some items and some recipes for testing:
items.Add(new Item { Name = "Foo" });
items.Add(new Item { Name = "Bar" });
items.Add(new Item { Name = "Baz" });
items.Add(new Item { Name = "Qux" });
items.Add(new Item { Name = "Foobar" });
items.Add(new Item { Name = "Bazqux" });
items.Add(new Item { Name = "Foobarbazqux" });
recipes.Add(new Recipe
{
Result = GetItem("Foobar"),
Ingredients = new Item[] { GetItem("Foo"), GetItem("Bar") }
});
recipes.Add(new Recipe
{
Result = GetItem("Bazqux"),
Ingredients = new Item[] { GetItem("Baz"), GetItem("Qux") }
});
recipes.Add(new Recipe
{
Result = GetItem("Foobarbazqux"),
Ingredients = new Item[] { GetItem("Foobar"), GetItem("Bazqux") }
});
Because items are primary keyed by their Name
field, GetItem
is simply a List.Find<>
shortcut:
private static Item GetItem(string _name)
{
return Program.Items.Find(match => match.Name == _name);
}
So right now, the database is set. I have realized that I need to make some sort of recursive method to fetch all the ingredients, but this is where I came to problem.
Consider the following, first attempt (this is not recursive, only goes "one level deep"):
string search = "Foobarbazqux";
SearchRecipe(search);
private static void SearchRecipe(string _search)
{
List<Recipe> item_recipes = Program.Recipes.FindAll(match => match.result.Name == GetItem(search).Name);
foreach (Recipe recp in item_recipes)
{
Console.WriteLine("-------------");
Console.WriteLine("Ingredients: ");
foreach (Item ingredient in recp.Ingredients)
{
Console.WriteLine(ingredient.Name);
}
}
}
This produces the following output, which is, without recursion, is pretty fine and expected:
-------------
Ingredients:
Foobar
Bazqux
So for the recursion, I modified the code like this:
foreach (Item ingredient in recp.Ingredients)
{
+++ if (recipes.FindAll(match2 => match2.Result.name == Ingredient.name).Count != 0)
+++ {
+++ SearchRecipe(ingredient.Name);
+++ }
Console.WriteLine(ingredient.Name);
}
Now the output is the following:
| -------------
| Ingredients:
* -------------
* Ingredients:
* Foo
* Bar
| Foobar
@ -------------
@ Ingredients:
@ Baz
@ Qux
| Bazqux
I see what happens as I understand my code right now. Lines marked with *
are recursions for Foobar
, @
are recursions for Bazqux
and |
are the output of the original method call. But this isn't... kinda the output I would want to implement, as it fails to be understood easily and... isn't generally right.
The expected output and the question
The output I visualized is something like the following (of course the "extra" status lines are ignorable):
Searching for: Foobarbazqux
1 recipe found.
-----
1 Foobarbazqux = 1 Foobar + 1 Bazqux
1 Foobar = 1 Foo + 1 Bar
1 Bazqux = 1 Baz + 1 Qux
1 Foobarbazqux = 1 Foo + 1 Bar + 1 Baz + 1 Qux
1 Foo + 1 Bar => 1 Foobar
1 Baz + 1 Qux => 1 Bazqux
1 Foobar + 1 Bazqux => 1 Foobarbazqux
-----
Exit.
This is what I call as breaking down the chain of item creation recipes... or... I don't call, but imagine so, in reference to the question's title. So the program works as:
- User enters the "final" item they are searching for (this is
Foobarbazqux
in our example) - The program ravages the database enough times so first the ingredients of the searched item are found, and then it recurses(?) deeper until it finds only subatomic ingredients that cannot be "crafted" from others: they are not a
Result
for anyRecipe
. - (While working so) output is shown.
Note: Fits the real scenario better, but makes an extra implementation gap: Much of the items have more than one recipes from which they can be created.
What I would like to ask for is advices and help. I am slowly wanting to convince myself that this program is unwritable (which is only some sort of desperate outcry) and that my idea has design issues. But I still hope that, within boundaries, we don't need fancy, yet-to-be-created AI systems to solve this... kinda underestimatable problem.
Thank you.