mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-01-25 20:38:11 +01:00
More optimized speed updates
This commit is contained in:
parent
cc0d7f402d
commit
1fed398e8f
2 changed files with 90 additions and 41 deletions
|
@ -15,11 +15,15 @@ public class KineticNetwork {
|
|||
private final Set<KineticNode> members = new HashSet<>();
|
||||
private final Set<KineticNode> generators = new HashSet<>();
|
||||
private final Set<Pair<KineticNode, KineticNode>> conflictingCycles = new HashSet<>();
|
||||
private float rootSpeed;
|
||||
private float rootTheoreticalSpeed;
|
||||
private @Nullable KineticNode mainGenerator;
|
||||
private boolean speedDirty;
|
||||
private boolean rootSpeedDirty;
|
||||
private boolean overstressed;
|
||||
|
||||
private float rootSpeedCur;
|
||||
private float rootSpeedPrev;
|
||||
private final Set<KineticNode> potentialNewBranches = new HashSet<>();
|
||||
|
||||
public KineticNetwork(KineticNode root) {
|
||||
addMember(root);
|
||||
}
|
||||
|
@ -28,8 +32,10 @@ public class KineticNetwork {
|
|||
members.add(node);
|
||||
if (node.isGenerator() && !generators.contains(node)) {
|
||||
generators.add(node);
|
||||
speedDirty = true;
|
||||
rootSpeedDirty = true;
|
||||
}
|
||||
|
||||
potentialNewBranches.add(node);
|
||||
}
|
||||
|
||||
public void updateMember(KineticNode node) {
|
||||
|
@ -40,14 +46,14 @@ public class KineticNetwork {
|
|||
} else {
|
||||
generators.remove(node);
|
||||
}
|
||||
speedDirty = true;
|
||||
rootSpeedDirty = true;
|
||||
}
|
||||
|
||||
public void removeMember(KineticNode node) {
|
||||
members.remove(node);
|
||||
if (node.isGenerator() && generators.contains(node)) {
|
||||
generators.remove(node);
|
||||
speedDirty = true;
|
||||
rootSpeedDirty = true;
|
||||
}
|
||||
conflictingCycles.removeIf(p -> p.getFirst() == node || p.getSecond() == node);
|
||||
}
|
||||
|
@ -65,10 +71,14 @@ public class KineticNetwork {
|
|||
* each other, and OK otherwise
|
||||
*/
|
||||
public SolveResult tryRecalculateSpeed() {
|
||||
if (!conflictingCycles.isEmpty() && !isStopped()) return SolveResult.CONTRADICTION;
|
||||
if (!speedDirty) return SolveResult.OK;
|
||||
SolveResult result = tryRecalculateTheoreticalSpeed();
|
||||
if (isStopped()) return SolveResult.OK;
|
||||
return result;
|
||||
}
|
||||
|
||||
SolveResult result = SolveResult.OK;
|
||||
private SolveResult tryRecalculateTheoreticalSpeed() {
|
||||
SolveResult result = conflictingCycles.isEmpty() ? SolveResult.OK : SolveResult.CONTRADICTION;
|
||||
if (!rootSpeedDirty) return result;
|
||||
|
||||
float newSpeed = 0;
|
||||
KineticNode newGenerator = null;
|
||||
|
@ -93,11 +103,15 @@ public class KineticNetwork {
|
|||
newGenerator = generator;
|
||||
}
|
||||
}
|
||||
rootSpeed = newSpeed * sign;
|
||||
mainGenerator = newGenerator;
|
||||
speedDirty = false;
|
||||
|
||||
if (overstressed) return SolveResult.OK;
|
||||
rootTheoreticalSpeed = newSpeed * sign;
|
||||
if (!overstressed) {
|
||||
rootSpeedCur = rootTheoreticalSpeed;
|
||||
}
|
||||
|
||||
mainGenerator = newGenerator;
|
||||
rootSpeedDirty = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -109,20 +123,24 @@ public class KineticNetwork {
|
|||
|
||||
if (generators.isEmpty()) {
|
||||
overstressed = false;
|
||||
members.forEach(KineticNode::stop);
|
||||
members.forEach(KineticNode::flushChangedSpeed);
|
||||
return newNetworks;
|
||||
}
|
||||
|
||||
float stressImpact = (float) members.stream().mapToDouble(n -> n.getTotalStressImpact(rootSpeed)).sum();
|
||||
float stressImpact = (float) members.stream().mapToDouble(n -> n.getTotalStressImpact(rootTheoreticalSpeed)).sum();
|
||||
float stressCapacity = (float) members.stream().mapToDouble(KineticNode::getStressCapacity).sum();
|
||||
|
||||
if (stressImpact > stressCapacity) {
|
||||
if (!overstressed) {
|
||||
overstressed = true;
|
||||
rootSpeedCur = 0;
|
||||
members.forEach(KineticNode::stop);
|
||||
}
|
||||
} else {
|
||||
if (overstressed) {
|
||||
overstressed = false;
|
||||
rootSpeedCur = rootTheoreticalSpeed;
|
||||
newNetworks.addAll(bulldozeContradictingMembers());
|
||||
newNetworks.addAll(updateMemberSpeeds());
|
||||
}
|
||||
|
@ -132,7 +150,14 @@ public class KineticNetwork {
|
|||
return newNetworks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the speed of every member, starting from the main generator and popping off speeding nodes along the way
|
||||
* @return a List of new networks created during this function call
|
||||
*/
|
||||
private List<KineticNetwork> updateMemberSpeeds() {
|
||||
boolean rootSpeedChanged = rootSpeedPrev != rootSpeedCur;
|
||||
rootSpeedPrev = rootSpeedCur;
|
||||
|
||||
// if we're stopped, then all members' speeds will be 0, so no need to check for speeding nodes
|
||||
if (isStopped()) {
|
||||
members.forEach(KineticNode::stop);
|
||||
|
@ -143,45 +168,68 @@ public class KineticNetwork {
|
|||
// generators should not be turning against each other or have conflicting cycles by now
|
||||
assert(recalculateSpeedResult.isOk());
|
||||
|
||||
// update node speeds in a breadth-first order, checking for speeding nodes along the way
|
||||
List<KineticNetwork> newNetworks = new LinkedList<>();
|
||||
Set<KineticNode> visited = new HashSet<>();
|
||||
List<KineticNode> frontier = new LinkedList<>();
|
||||
frontier.add(mainGenerator);
|
||||
|
||||
while (!frontier.isEmpty()) {
|
||||
KineticNode cur = frontier.remove(0);
|
||||
visited.add(cur);
|
||||
if (cur.tryUpdateSpeed(rootSpeed).isOk()) {
|
||||
cur.getActiveConnections()
|
||||
.map(Pair::getFirst)
|
||||
.filter(n -> !visited.contains(n))
|
||||
.forEach(frontier::add);
|
||||
} else {
|
||||
// stop searching on this branch once a speeding node is found
|
||||
cur.onPopBlock();
|
||||
newNetworks.add(cur.getNetwork());
|
||||
}
|
||||
if (rootSpeedChanged) {
|
||||
// root speed changed, update all nodes starting from the main generator
|
||||
bfs(mainGenerator, newNetworks, false);
|
||||
} else if (!potentialNewBranches.isEmpty()) {
|
||||
// new nodes added, update only the new network branches
|
||||
potentialNewBranches.stream()
|
||||
.filter(n -> !potentialNewBranches.contains(n.getSource()))
|
||||
.forEach(n -> bfs(n, newNetworks, true));
|
||||
potentialNewBranches.clear();
|
||||
}
|
||||
|
||||
return newNetworks;
|
||||
}
|
||||
|
||||
private void bfs(KineticNode root, List<KineticNetwork> newNetworks, boolean followSource) {
|
||||
// update node speeds in a breadth-first order, checking for speeding nodes along the way
|
||||
Set<KineticNode> visited = new HashSet<>();
|
||||
List<KineticNode> frontier = new LinkedList<>();
|
||||
frontier.add(root);
|
||||
|
||||
while (!frontier.isEmpty()) {
|
||||
KineticNode cur = frontier.remove(0);
|
||||
if (!members.contains(cur) || visited.contains(cur)) continue;
|
||||
visited.add(cur);
|
||||
|
||||
if (cur.tryUpdateSpeed(rootSpeedCur).isOk()) {
|
||||
cur.getActiveConnections()
|
||||
.map(Pair::getFirst)
|
||||
.filter(n -> !followSource || n.getSource() == cur)
|
||||
.forEach(frontier::add);
|
||||
} else {
|
||||
// stop searching on this branch once a speeding node is found
|
||||
cur.popBlock();
|
||||
newNetworks.add(cur.getNetwork());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<KineticNetwork> bulldozeContradictingMembers() {
|
||||
/*
|
||||
This method is necessary to handle the edge case where contradicting nodes have been added to the network while
|
||||
it was overstressed and now that it's moving again we need to pop them. Here we can't just stop following a
|
||||
branch after popping a block though since there may be more contradictions further down that branch, so we'll
|
||||
just pop all potentially contradicting nodes off and hope no one cares
|
||||
*/
|
||||
|
||||
List<KineticNetwork> newNetworks = new LinkedList<>();
|
||||
|
||||
// generators running against network
|
||||
float sign = Math.signum(rootSpeed);
|
||||
float sign = Math.signum(rootSpeedCur);
|
||||
List<KineticNode> runningAgainst = generators.stream()
|
||||
.filter(n -> Math.signum(n.getGeneratedSpeedAtRoot()) != sign)
|
||||
.collect(Collectors.toList());
|
||||
runningAgainst.forEach(n -> { n.onPopBlock(); newNetworks.add(n.getNetwork()); });
|
||||
runningAgainst.forEach(n -> { n.popBlock(); newNetworks.add(n.getNetwork()); });
|
||||
|
||||
// conflicting cycles
|
||||
List<KineticNode> cycles = conflictingCycles.stream()
|
||||
.map(Pair::getFirst)
|
||||
.collect(Collectors.toList());
|
||||
cycles.forEach(n -> { n.onPopBlock(); newNetworks.add(n.getNetwork()); });
|
||||
cycles.forEach(n -> { n.popBlock(); newNetworks.add(n.getNetwork()); });
|
||||
|
||||
return newNetworks;
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ public class KineticNode {
|
|||
generatedSpeed = newSpeed;
|
||||
network.updateMember(this);
|
||||
if (network.tryRecalculateSpeed().isContradiction()) {
|
||||
onPopBlock();
|
||||
popBlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,10 @@ public class KineticNode {
|
|||
return network.tryRecalculateSpeed();
|
||||
}
|
||||
|
||||
public KineticNode getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
private SolveResult setSource(KineticNode from, float ratio) {
|
||||
source = from;
|
||||
speedRatio = from.speedRatio * ratio;
|
||||
|
@ -124,7 +128,7 @@ public class KineticNode {
|
|||
if (setSource(e.getFirst(), 1/e.getSecond()).isOk()) {
|
||||
propagateSource();
|
||||
} else {
|
||||
onPopBlock();
|
||||
popBlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -151,7 +155,7 @@ public class KineticNode {
|
|||
if (network.isStopped()) {
|
||||
network.markConflictingCycle(cur, next);
|
||||
} else {
|
||||
onPopBlock();
|
||||
popBlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +166,7 @@ public class KineticNode {
|
|||
frontier.add(next);
|
||||
} else {
|
||||
// this node will run against the network or activate a conflicting cycle
|
||||
onPopBlock();
|
||||
popBlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -208,12 +212,9 @@ public class KineticNode {
|
|||
}
|
||||
}
|
||||
|
||||
public void onPopBlock() {
|
||||
public void popBlock() {
|
||||
// this should cause the node to get removed from the solver and lead to onRemoved() being called
|
||||
entity.getLevel().destroyBlock(entity.getBlockPos(), true);
|
||||
}
|
||||
|
||||
public boolean isSourceOf(KineticNode other) {
|
||||
return other.source == this;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue