How to calculate changing vertex weights in directed graphwith cyclic dependencies?
How to calculate changing vertex weights in directed graphwith cyclic dependencies?
I am making a small game in Java to practice programming and came across this little issue that I can't seem to solve on my own.
In this game, I want to build a virtual shop where I can buy and sell items.
Most items can only be obtained in the game by creating them out of other items by following what I called 'recipes'. A recipe is basically just a set of items needed to create a new item. An item can have multiple recipes. The result of a recipe can be more than one instance of the new amount. I will denote recipes like so [item, item, ..., item](resulting amount).
So there are base items, which can not be created but must be obtained through other means, and there are created items which can only be obtained via one of its recipes.
I want to try and simulate supply and demand, and I came up with this property I call the 'trade amount', which is initialized to 0 for each item. The trade amount represents how much of the item is being sold or bought. If I sell X amount of an item, its trade amount goes up by X, and if I buy Y amount of an item, its trade amount goes down by Y.
Now I need to determine a price for my items.
All base items get assigned a flat value. The value and trade amount then get plugged into a function f(v, t) that spits out a price. This price is always >= 0. One property of this function is if the trade amount t is 0, then the resulting price is equal to the input value v. So if one player buys a set amount of an item, the trade amount goes down by x and the price gets closer to 0. If then another player sells the same amount of the same item, the trade amount goes back to 0, and the price jumps back up to the original value.
Now that all base items have a value, I need to calculate the values of all created items to be able to calculate their price using f. I want their value to be based on the cheapest recipe needed to create them. So I have to parse all recipes, add up all items' prices used in each recipe, and determine the minimum price of a recipe. That is the value of the created item.
I can model this setup in a graph with vertices representing items that can be traded in the shop, and the directed edges connect an item to another item that can be created from the source item. So I have a directed edge ('source -> destination'), where the source is an ingredient in one of the destination's recipes. So from this premise, I create a random directed graph of some 600-ish vertices.
This graph is different every time the game loads and the generator should specifically allow cycles within the graph.
Here is an example:
Items A and B are base items with a value of 10 and 2 respectively. Their trade amounts initially are 0, so their prices are equal to their values, 10 and 2.
Item C is a created item with two recipes R1 = A, A, B and R2 = B, B, B. Now I determine R1 to be priced at 22 and R2 to be priced at 6 which is the minimum recipe price. Thus item C has a value of 6 (and initially a trade amount of 0, thus the price is also 6).
In this case, the graph would look something like (A) --> (C) <-- (B)
Up to this point, it all works beautifully and perfectly fine.
Now the problem I have appears when the price-value dependencies are cyclic.
Here are 3 types of cases I need help with:
(1) - 0 vertex cycle
Simply put, item A has a fixed price, and item B can be created from the recipe [A, B]. If the price of B gets changed, then all items created by B have to get recalculated. In this case, B is made of A and B, so I have to recalculate the price of B as well. This again triggers the recalculation of B and so on and so forth...
I know the premise is weird but think of it like repainting a door (B). It is made of the same door (B) plus paint (A).
I call it a 0 vertex cycle because when traversing the cycle, you pass 0 vertices to get back to where you started.
(2) - 1 vertex cycle
If we take the same items A and B from the very first example, and add item C with recipes R1=A, B and R2=D, D, D and item D with recipe R3=C. So C can be "broken down" into 3 Ds, and 3 Ds can be combined into one C. If I now lower the price of C by selling lots of it, I should also lower the price of D, since D can be made of C. But since C can be made of D as well, and assuming R2 becomes cheaper than R1, then I have to lower the price of C as well, thus again triggering the lowering of price of D and so on and so forth. This will continue until the value has converged somewhere depending on the ingredients of the recipes involved. I call it a 1 vertex cycle because when traversing the cycle, you pass only one vertex.
(3) - n vertices cycle
This is the general case, and I think if I solve this, the two specific cases above will be solved as well.
If item A is made of B, B of C, C of D and so on up to item Z, which is made of A, then changing the price of one will trigger a change in the price of the next, which will trigger the price of the next and so on and so forth. This chain can be practically arbitrarily long.**
I am looking for an algorithm that can solve my problem. I want to be able to calculate all values and prices of all items with the initial trade-amount of 0. When players buy and sell items, the price-changes should propagate through the graph.
I have considered modifying Dijkstra's algorithm, A*, the Bellman-Ford algorithm, and the Floyd–Warshall algorithm, but could not get a satisfying result with any of them.
Can somebody point me in the right direction?
@NicoSchertler I am not sure I understood the stopping condition you mention. The randomly generated graph is guaranteed to have no self-inflationary items. The problem with "inflation" happens due to rampant recursion until the changed price difference is smaller than some epsilon. How would I model it with a linear system? Also, another problem that can occur is say items Q, X, Y and Z are some flat-valued items. Items A, B and C can be made the recipes [any of A, B and C, Q, X/Y/Z (respectively)](1)....
– neph
Sep 16 '18 at 20:41
... then even if I detect a price change in A, that triggers a change in B, which doesn't allow A to change, the change in B might trigger a change in C, which in turn will trigger A again...
– neph
Sep 16 '18 at 20:41
Your trigger will calculate a new price for some item. Then, compare the new price (including the minimum operation) and the old price. If they are different, continue the propagation. Otherwise, stop. If you have no inflation, this propagation should be guaranteed to stop eventually.
– Nico Schertler
Sep 17 '18 at 6:13
@NicoSchertler Your suggestion helped a lot and solved the issue of initializing the graph with weights (prices) for most cases. However I just came across another problem with cycles and I will try my best to explain it:
– neph
Sep 17 '18 at 20:00
0
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy
Your propagation should stop as soon as the price of the target item would not change. Thus, your graph must guarantee that there is no infinite recursion, which is not easy as you have found. In other words, as soon as there is a cycle that allows to e.g. generate two items of A out of a single item of A, you will have inflation and A will have no value. If you guarantee that no inflation occurs, your propagation should stop rather quickly. If you had a single recipe for each item, this could be modelled as a linear system. The minimum operation makes this a bit harder.
– Nico Schertler
Sep 16 '18 at 18:47