Compare commits

...

3 Commits

Author SHA1 Message Date
agp8x 0ab1f7b330 fix #2: calculate exact values for byproducts and its parents 2023-04-12 12:38:07 +02:00
agp8x f8bdb9902b improve graph nodes 2023-04-12 11:19:02 +02:00
agp8x a2db6b7765 add production rates to plot 2023-04-10 17:40:33 +02:00
9 changed files with 122 additions and 73 deletions

View File

@ -128,6 +128,7 @@ public class Test {
new Production(Database.IronPlate, 1));
planFor("p3_acu", new Production(Database.AdaptiveControlUnit, 1));
planFor("p3_me", new Production(Database.ModularEngine, 1));
planFor("p3_me_acu", new Production(Database.ModularEngine, 1), new Production(Database.AdaptiveControlUnit, 1));
planFor("p3_vf", new Production(Database.VersatileFrameWork, 1));
planFor("screw", new Production(Database.ReinforcedIronPlate, 1));
planFor("rotor", new Production(Database.Rotor, 1));

View File

@ -106,8 +106,15 @@ public class Utils {
if (item.isRaw()) {
m.put("peripheries", DefaultAttribute.createAttribute(2));
}
String label = item.getName();
double required = sum.outgoingEdgesOf(item).stream().map(ProductionEdge::getTotalRequired).reduce(Double::sum).orElseGet(() -> Double.NaN);
double instances = sum.outgoingEdgesOf(item).stream().map(ProductionEdge::getInstances).reduce(Double::sum).orElseGet(() -> Double.NaN);
String buildingName = item.getRecipe().getBuilding().getName();
if (Double.isNaN(required) || Double.isNaN(instances)) {
required = -sum.incomingEdgesOf(item).stream().map(ProductionEdge::getTotalProduced).reduce(Double::sum).orElseGet(() -> Double.NaN);
instances = 0;
buildingName = "";
}
String label = "%s \n(%.1f||%.1f)\n%s".formatted(item.getName(), required, instances,buildingName);
m.put("label", DefaultAttribute.createAttribute(label));
return m;
});

View File

@ -7,6 +7,12 @@ public class ProductionEdge {
private final Item target;
private double totalRequired;
private double instances;
private boolean ignoreUpstream;
public ProductionEdge(Item source, Item target, double totalRequired, double productionRate, boolean ignoreUpstream) {
this(source, target, totalRequired, productionRate);
this.ignoreUpstream = ignoreUpstream;
}
public ProductionEdge(Item source, Item target, double totalRequired, double productionRate) {
this.source = source;
@ -28,6 +34,13 @@ public class ProductionEdge {
}
public double getTotalRequired() {
if (ignoreUpstream){
return 0.0;
}
return totalRequired;
}
public double getTotalProduced(){
// for byproducts
return totalRequired;
}
@ -36,6 +49,9 @@ public class ProductionEdge {
}
public double getInstances() {
if (ignoreUpstream){
return 0.0;
}
return instances;
}

View File

@ -179,7 +179,11 @@ public class Recipe {
private double getByproductRate(Item main, Item product, double production) {
double runs = getRequiredProcessRuns(main, production);
return product.getRecipe().outputs.get(product) * runs;
System.out.printf("BY-RATE: %s -> %s%n", main.getName(), product.getName());
if (!main.getRecipe().outputs.containsKey(product)){
return Double.NaN;
}
return main.getRecipe().outputs.get(product) * runs;
}
public SumResult sum(Item target, double prodPerMinute) {
@ -192,6 +196,7 @@ public class Recipe {
production.addVertex(target);
production.addEdge(target, target, new ProductionEdge(target, target, prodPerMinute, processesNeeded(target, prodPerMinute)));
Set<Item> visited = new HashSet<>(); // keep track of which items are already expanded fully into the graph
Set<Item> by = new HashSet<>(); // keep track of which items are already expanded fully into the graph
while (!queue.isEmpty()) {
Item item = queue.remove();
if (!visited.contains(item)) {
@ -199,6 +204,7 @@ public class Recipe {
buildGraph.incomingEdgesOf(item)
.stream()
.map(buildGraph::getEdgeSource)
//.filter(item1 -> !item.getRecipe().isByProduct(item1))
.forEach(queue::add);
}
// *this* item
@ -211,13 +217,15 @@ public class Recipe {
// product is by-product, no forward dependency
System.out.println("BY-PRODUCT " + item.getName() + " -> " + product.getName() + "... " + queue);
byProducts.add(product);
//visited.add(product);
continue;
}
if (queue.contains(product) || productWantedPerMinute == null) {
// defer update, no all usages are expanded yet
System.err.println("product still queued or productWantedPerMinute null for '" + product.getName() + "' from '" + item.getName() + "'");
sum = 0;
queue.add(item);
//if (!item.getRecipe().isByProduct(product)){
queue.add(item);//}
break;
}
double amountNeeded = buildGraph.getEdgeWeight(edge);
@ -233,15 +241,22 @@ public class Recipe {
}
if (!byProducts.isEmpty()) {
double finalSum = sum;
byProducts.forEach(item1 -> {
production.addVertex(item1);
byProducts.forEach(byproduct -> {
production.addVertex(byproduct);
// TODO: calculate produced amount
double byproductRate = getByproductRate(item, item1, finalSum);
production.addEdge(item, item1, new ProductionEdge(item, item1, byproductRate, processesNeeded(item, finalSum)));
double byproductRate = getByproductRate(item, byproduct, finalSum);
production.addEdge(item, byproduct, new ProductionEdge(item, byproduct, byproductRate, processesNeeded(item, finalSum), true));
});
by.addAll(byProducts);
}
visited.add(item);
}
by.forEach(item -> {production.incomingEdgesOf(item).forEach(productionEdge -> {
Item source = productionEdge.getSource();
double sourceRate = map.get(source);
double byProductRate = getByproductRate(source, item, sourceRate);
map.put(item, byProductRate + map.getOrDefault(item,0.0));
});});
map.forEach((item, aDouble) -> System.out.println(item.getName() + ": " + aDouble));
return new SumResult(production, map);
}

View File

@ -29,6 +29,11 @@ public class SumResult {
.map(prod -> prod.getItem().getRecipe().sum(prod.getItem(), prod.getAmount()))
.reduce(SumResult::merge).orElse(new SumResult());
}
public static SumResult sum(List<Production> productions) {
return productions.stream()
.map(prod -> prod.getItem().getRecipe().sum(prod.getItem(), prod.getAmount()))
.reduce(SumResult::merge).orElse(new SumResult());
}
public static Graph<Item, ProductionEdge> merge(Graph<Item, ProductionEdge> graph0, Graph<Item, ProductionEdge> graph1) {
// ToDo: test!
@ -69,8 +74,7 @@ public class SumResult {
}
public SumResult merge(SumResult other) {
HashMap<Item, Double> map = new HashMap<>();
map.putAll(this.map);
HashMap<Item, Double> map = new HashMap<>(this.map);
other.map.forEach((item, aDouble) -> map.merge(item, aDouble, Double::sum));
return new SumResult(merge(production, other.getProduction()), map);
}

View File

@ -47,7 +47,21 @@ class ItemTest {
@Test
void productionUraniumFuelRod(){
test(Database.UraniumFuelRod, "uranium_fuel_rod");
//test(Database.UraniumFuelRod, "uranium_fuel_rod");
fail();
}
@Test
void productionPlastic(){
test(Database.Plastic,"plastic");
}
@Test
void productionRubber(){
test(Database.Rubber,"rubber");
}
@Test
void productionRubberAndPlastic(){
test("rubberAndPlastic",Database.Rubber, Database.Plastic);
}
}

View File

@ -3,23 +3,21 @@ package satisfactory.items;
import org.junit.jupiter.api.Test;
import satisfactory.Database;
import java.util.Map;
import static satisfactory.items.TestHelper.*;
import static satisfactory.items.TestHelper.test;
class Phase3Test {
@Test
void testPhase3_ACU() {
Item item = Database.AdaptiveControlUnit;
test(item);
test(item, "p3_acu");
}
@Test
void testPhase3_ME() {
Item item = Database.ModularEngine;
test(item);
test(item, "p3_me");
}
@ -31,70 +29,21 @@ class Phase3Test {
@Test
void testPhase3_ME_ACU_VF() {
// references
Map<Item, Double> ref = merge(
ValidatedValues.get(Database.ModularEngine),
ValidatedValues.get(Database.AdaptiveControlUnit),
ValidatedValues.get(Database.VersatileFrameWork));
ref.forEach((item, aDouble) -> System.out.println(aDouble + "\t" + item.getName()));
// calculate
Map<Item, Double> calculations = SumResult.sum(
new Production(Database.ModularEngine, 1),
new Production(Database.AdaptiveControlUnit, 1),
new Production(Database.VersatileFrameWork, 1)
).getMap();
// assert
assertMap(ref, calculations);
test(Database.ModularEngine, Database.AdaptiveControlUnit, Database.VersatileFrameWork);
}
@Test
void testPhase3_ME_ACU() {
// references
Map<Item, Double> ref1 = merge(
ValidatedValues.get(Database.ModularEngine),
ValidatedValues.get(Database.AdaptiveControlUnit)
);
// calculate
Map<Item, Double> calculations1 = SumResult.sum(
new Production(Database.ModularEngine, 1),
new Production(Database.AdaptiveControlUnit, 1)
).getMap();
// assert
assertMap(ref1, calculations1);
test(Database.ModularEngine, Database.AdaptiveControlUnit);
}
@Test
void testPhase3_ACU_VF() {
// references
Map<Item, Double> ref1 = merge(
ValidatedValues.get(Database.AdaptiveControlUnit),
ValidatedValues.get(Database.VersatileFrameWork));
// calculate
Map<Item, Double> calculations1 = SumResult.sum(
new Production(Database.AdaptiveControlUnit, 1),
new Production(Database.VersatileFrameWork, 1)
).getMap();
// assert
assertMap(ref1, calculations1);
test(Database.AdaptiveControlUnit, Database.VersatileFrameWork);
}
@Test
void testPhase3_ME_VF() {
// references
Map<Item, Double> ref1 = merge(
ValidatedValues.get(Database.ModularEngine),
ValidatedValues.get(Database.VersatileFrameWork));
// calculate
Map<Item, Double> calculations1 = SumResult.sum(
new Production(Database.ModularEngine, 1),
new Production(Database.VersatileFrameWork, 1)
).getMap();
test(Database.ModularEngine, Database.VersatileFrameWork);
}
}

View File

@ -2,7 +2,9 @@ package satisfactory.items;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -38,12 +40,37 @@ public class TestHelper {
assertMap(ref, calculations.getMap());
}
public static void test(String name, Item... items) {
// references
Map<Item, Double> ref1 = merge(Arrays.stream(items).map(ValidatedValues::get).toList());
// calculate
SumResult sumResult = SumResult.sum(Arrays.stream(items).map(item -> new Production(item, 1.0)).toList());
Map<Item, Double> calculations = sumResult.getMap();
// plot
if (name != null) {
name = "test_" + name;
plot2(sumResult.getProduction(), name);
javaPlot(name);
list(sumResult, name);
}
// assert
assertMap(ref1, calculations);
}
public static void test(Item... items) {
test(null, items);
}
private static SumResult calculate(Item item) {
// calculate
return SumResult.sum(new Production(item, 1));
}
public static <K> Map<K, Double> merge(Map<K, Double>... single_refs) {
public static <K> Map<K, Double> merge(List<Map<K, Double>> single_refs) {
Map<K, Double> ref = new HashMap<>();
for (Map<K, Double> singleRef : single_refs) {
singleRef.forEach((singleItem, singleValue) -> ref.compute(singleItem, (refItem, refValue) -> singleValue + check(refValue)));
@ -77,7 +104,7 @@ public class TestHelper {
//c.put("b", 4.0);
e.put("b", 3.);
}
Map<String, Double> m = merge(a, b, c);
Map<String, Double> m = merge(Arrays.asList(a, b, c));
e.forEach((s, aDouble) -> {
assertEquals(aDouble, m.get(s));
});

View File

@ -78,6 +78,22 @@ public class ValidatedValues {
ref.put(item, 1.);
values.put(item, ref);
}
{
Item item = Database.Plastic;
Map<Item, Double> ref = new HashMap<>();
ref.put(Database.CrudeOil, 1.5);
ref.put(Database.Plastic, 1.);
ref.put(Database.HeavyOilResidue, 0.5);
values.put(item, ref);
}
{
Item item = Database.Rubber;
Map<Item, Double> ref = new HashMap<>();
ref.put(Database.CrudeOil, 1.5);
ref.put(Database.HeavyOilResidue, 1.);
ref.put(item, 1.);
values.put(item, ref);
}
}
public static Map<Item, Double> get(Item item) {
@ -113,7 +129,7 @@ public class ValidatedValues {
ref.put(Database.CopperIngot, 249.0);
ref.put(Database.IronRod, 90.5);
ref.put(Database.IronIngot, 158.0);
//ref.put(Database.HeavyOilResidue, 39.0); // FIXME byproduct
ref.put(Database.HeavyOilResidue, 39.0); // FIXME byproduct
ref.put(Database.CopperSheet, 30.0);
return ref;
}
@ -140,7 +156,7 @@ public class ValidatedValues {
ref.put(Database.SmartPlating, 2.0);
ref.put(Database.ReinforcedIronPlate, 2.0);
ref.put(Database.Rubber, 15.0);
//ref.put(Database.HeavyOilResidue, 15.0); // FIXME byproduct
ref.put(Database.HeavyOilResidue, 15.0); // FIXME byproduct
return ref;
}