diff --git a/core/src/main/java/ru/trader/analysis/graph/AbstractGraph.java b/core/src/main/java/ru/trader/analysis/graph/AbstractGraph.java new file mode 100644 index 0000000..4afd9da --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/graph/AbstractGraph.java @@ -0,0 +1,155 @@ +package ru.trader.analysis.graph; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.trader.analysis.AnalysisCallBack; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.RecursiveAction; + +public abstract class AbstractGraph implements Graph { + private final static ForkJoinPool POOL = new ForkJoinPool(); + private final static int THRESHOLD = 4; + + private final static Logger LOG = LoggerFactory.getLogger(AbstractGraph.class); + + protected Vertex root; + protected final Map> vertexes; + private final GraphCallBack callback; + + protected int minJumps; + + protected AbstractGraph() { + this(new AnalysisCallBack()); + } + + protected AbstractGraph(AnalysisCallBack callback) { + this.callback = new GraphCallBack(callback); + vertexes = new ConcurrentHashMap<>(50, 0.9f, THRESHOLD); + } + + protected abstract GraphBuilder createGraphBuilder(Vertex vertex, Collection set, int deep, double limit); + + public void build(T start, Collection set, int maxDeep, double limit) { + callback.startBuild(start); + root = getInstance(start, maxDeep); + POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit)); + if (set.size() > vertexes.size()){ + minJumps = maxDeep; + } else { + minJumps = 1; + for (Vertex vertex : vertexes.values()) { + int jumps = maxDeep - vertex.getLevel(); + if (jumps > minJumps) minJumps = jumps; + } + } + callback.endBuild(); + } + + private Vertex getInstance(T entry, int deep){ + Vertex vertex = vertexes.get(entry); + if (vertex == null) { + LOG.trace("Is new vertex"); + vertex = new Vertex<>(entry); + vertex.setLevel(deep); + vertexes.put(entry, vertex); + } + return vertex; + } + + @Override + public boolean isAccessible(T entry){ + return vertexes.containsKey(entry); + } + + @Override + public Vertex getVertex(T entry){ + return vertexes.get(entry); + } + + @Override + public Vertex getRoot() { + return root; + } + + @Override + public int getMinJumps() { + return minJumps; + } + + protected abstract class GraphBuilder extends RecursiveAction { + protected final Vertex vertex; + protected final Collection set; + protected final int deep; + protected final double limit; + + protected GraphBuilder(Vertex vertex, Collection set, int deep, double limit) { + this.vertex = vertex; + this.set = set; + this.deep = deep; + this.limit = limit; + } + + protected abstract double onConnect(T entry); + protected abstract Edge createEdge(Vertex target); + protected GraphBuilder createSubTask(Vertex vertex, Collection set, int deep, double limit){ + return createGraphBuilder(vertex, set, deep, limit); + } + + @Override + protected void compute() { + LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep); + ArrayList subTasks = new ArrayList<>(THRESHOLD); + Iterator iterator = set.iterator(); + while (iterator.hasNext()) { + if (callback.isCancel()) break; + T entry = iterator.next(); + if (entry == vertex.getEntry()) continue; + double nextLimit = onConnect(entry); + if (nextLimit >= 0) { + LOG.trace("Connect {} to {}", vertex, entry); + Vertex next = getInstance(entry, 0); + vertex.connect(createEdge(next)); + // If level > deep when vertex already added on upper deep + if (next.getLevel() < deep) { + next.setLevel(vertex.getLevel() - 1); + if (deep > 0) { + //Recursive build + GraphBuilder task = createSubTask(next, set, deep - 1, nextLimit); + task.fork(); + subTasks.add(task); + } + } + } else { + LOG.trace("Vertex {} is far away", entry); + } + if (subTasks.size() == THRESHOLD || !iterator.hasNext()){ + for (GraphBuilder subTask : subTasks) { + if (callback.isCancel()){ + subTask.cancel(true); + } else { + subTask.join(); + } + } + subTasks.clear(); + } + } + if (!subTasks.isEmpty()){ + for (GraphBuilder subTask : subTasks) { + if (callback.isCancel()){ + subTask.cancel(true); + } else { + subTask.join(); + } + } + subTasks.clear(); + } + LOG.trace("End build graph from {} on deep {}", vertex, deep); + } + } +} diff --git a/core/src/main/java/ru/trader/analysis/graph/CCrawler.java b/core/src/main/java/ru/trader/analysis/graph/CCrawler.java new file mode 100644 index 0000000..4351251 --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/graph/CCrawler.java @@ -0,0 +1,112 @@ +package ru.trader.analysis.graph; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.trader.core.Profile; +import ru.trader.core.Ship; +import ru.trader.graph.Connectable; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; + +public class CCrawler> extends Crawler { + private final static Logger LOG = LoggerFactory.getLogger(CCrawler.class); + + public CCrawler(ConnectibleGraph graph, Consumer>> onFoundFunc) { + super(graph, onFoundFunc); + } + + private Ship getShip(){ + return ((ConnectibleGraph)graph).getProfile().getShip(); + } + + private Profile getProfile(){ + return ((ConnectibleGraph)graph).getProfile(); + } + + @Override + protected Crawler.TraversalEntry start(Vertex vertex) { + double fuel = getShip().getTank(); + return new CTraversalEntry(new ArrayList<>(), vertex, fuel); + } + + @Override + protected Crawler.CostTraversalEntry costStart(Vertex vertex) { + double fuel = getShip().getTank(); + return new CCostTraversalEntry(new ArrayList<>(), vertex, fuel); + } + + @Override + protected Crawler.TraversalEntry travers(TraversalEntry entry, Edge edge) { + T source = entry.vertex.getEntry(); + double distance = source.getDistance(edge.target.getEntry()); + double fuelCost = getShip().getFuelCost(((CTraversalEntry)entry).fuel, distance); + double nextLimit = getProfile().withRefill() ? ((CTraversalEntry)entry).fuel - fuelCost : getShip().getTank(); + boolean refill = nextLimit < 0 && source.canRefill(); + if (refill) { + LOG.trace("Refill"); + refill = true; + nextLimit = getShip().getTank() - getShip().getFuelCost(distance); + } + edge = new ConnectibleEdge<>(edge.getSource(), edge.getTarget(), refill, fuelCost); + return new CTraversalEntry(getCopyList(entry.head, edge), edge.getTarget(), nextLimit); + } + + @Override + protected Crawler.CostTraversalEntry costTravers(Crawler.CostTraversalEntry entry, List> head, Edge edge) { + T source = entry.vertex.getEntry(); + double distance = source.getDistance(edge.target.getEntry()); + double fuelCost = getShip().getFuelCost(((CCostTraversalEntry)entry).fuel, distance); + double nextLimit = getProfile().withRefill() ? ((CCostTraversalEntry)entry).fuel - fuelCost : getShip().getTank(); + boolean refill = nextLimit < 0 && source.canRefill(); + if (refill) { + LOG.trace("Refill"); + refill = true; + fuelCost = getShip().getFuelCost(distance); + nextLimit = getShip().getTank() - fuelCost; + } + edge = new ConnectibleEdge<>(edge.getSource(), edge.getTarget(), refill, fuelCost); + return new CCostTraversalEntry(head, edge, entry.getWeight(), nextLimit); + } + + protected class CTraversalEntry extends TraversalEntry { + private final double fuel; + + protected CTraversalEntry(List> head, Vertex vertex, double fuel) { + super(head, vertex); + this.fuel = fuel; + } + + @Override + protected Iterator> getIteratorInstance() { + return vertex.getEdges().stream().filter(e -> { + ConnectibleEdge edge = (ConnectibleEdge) e; + return edge.getFuel() <= fuel || e.getSource().getEntry().canRefill(); + }).iterator(); + } + } + + protected class CCostTraversalEntry extends CostTraversalEntry { + private final double fuel; + + protected CCostTraversalEntry(List> head, Vertex vertex, double fuel) { + super(head, vertex); + this.fuel = fuel; + } + + protected CCostTraversalEntry(List> head, Edge edge, double cost, double fuel) { + super(head, edge, cost); + this.fuel = fuel; + } + + @Override + protected Iterator> getIteratorInstance() { + return vertex.getEdges().stream().filter(e -> { + ConnectibleEdge edge = (ConnectibleEdge) e; + return edge.getFuel() <= fuel || e.getSource().getEntry().canRefill(); + }).iterator(); + } + } +} diff --git a/core/src/main/java/ru/trader/analysis/graph/ConnectibleEdge.java b/core/src/main/java/ru/trader/analysis/graph/ConnectibleEdge.java new file mode 100644 index 0000000..5c94aa4 --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/graph/ConnectibleEdge.java @@ -0,0 +1,36 @@ +package ru.trader.analysis.graph; + +import ru.trader.graph.Connectable; + +public class ConnectibleEdge> extends Edge { + protected final boolean refill; + protected final double fuel; + + public ConnectibleEdge(Vertex source, Vertex target, boolean refill, double fuel) { + super(source, target); + this.refill = refill; + this.fuel = fuel; + } + + public boolean isRefill() { + return refill; + } + + public double getFuel() { + return fuel; + } + + @Override + protected double computeWeight() { + T s = source.getEntry(); + T t = target.getEntry(); + return s.getDistance(t); + } + + @Override + public String toString() { + return source.getEntry().toString() + " - "+ weight + + (refill ? "R" : "") + +" -> " + target.getEntry().toString(); + } +} diff --git a/core/src/main/java/ru/trader/analysis/graph/ConnectibleGraph.java b/core/src/main/java/ru/trader/analysis/graph/ConnectibleGraph.java index 6af9c3e..4c94adb 100644 --- a/core/src/main/java/ru/trader/analysis/graph/ConnectibleGraph.java +++ b/core/src/main/java/ru/trader/analysis/graph/ConnectibleGraph.java @@ -9,10 +9,10 @@ import ru.trader.graph.Connectable; import java.util.Collection; import java.util.function.Predicate; -public class ConnectibleGraph> extends Graph { +public class ConnectibleGraph> extends AbstractGraph { private final static Logger LOG = LoggerFactory.getLogger(ConnectibleGraph.class); - private final Profile profile; + protected final Profile profile; public ConnectibleGraph(Profile profile) { super(); @@ -24,6 +24,10 @@ public class ConnectibleGraph> extends Graph { this.profile = profile; } + public Profile getProfile() { + return profile; + } + @Override protected GraphBuilder createGraphBuilder(Vertex vertex, Collection set, int deep, double limit) { return new ConnectibleGraphBuilder(vertex, set, deep, limit); @@ -48,11 +52,12 @@ public class ConnectibleGraph> extends Graph { } } - private class ConnectibleGraphBuilder extends GraphBuilder { + protected class ConnectibleGraphBuilder extends GraphBuilder { private final DistanceFilter distanceFilter; protected boolean refill; + protected double fuelCost; - private ConnectibleGraphBuilder(Vertex vertex, Collection set, int deep, double limit) { + protected ConnectibleGraphBuilder(Vertex vertex, Collection set, int deep, double limit) { super(vertex, set, deep, limit); distanceFilter = new DistanceFilter(limit, vertex.getEntry()); } @@ -64,12 +69,13 @@ public class ConnectibleGraph> extends Graph { LOG.trace("Vertex {} is far away, {}", entry, distance); return -1; } - double costFuel = profile.getShip().getFuelCost(limit, distance); - double nextLimit = profile.withRefill() ? limit - costFuel : profile.getShip().getTank(); + fuelCost = profile.getShip().getFuelCost(limit, distance); + double nextLimit = profile.withRefill() ? limit - fuelCost : profile.getShip().getTank(); if (nextLimit < 0) { LOG.trace("Refill"); refill = true; - nextLimit = profile.getShip().getTank() - profile.getShip().getFuelCost(distance); + fuelCost = profile.getShip().getFuelCost(distance); + nextLimit = profile.getShip().getTank() - fuelCost; } else { refill = false; } @@ -77,35 +83,9 @@ public class ConnectibleGraph> extends Graph { } @Override - protected ConnectibleEdge createEdge(Vertex target) { - return new ConnectibleEdge(vertex, target, refill); + protected ConnectibleEdge createEdge(Vertex target) { + return new ConnectibleEdge<>(vertex, target, refill, fuelCost); } } - protected class ConnectibleEdge extends Edge { - private final boolean refill; - - protected ConnectibleEdge(Vertex source, Vertex target, boolean refill) { - super(source, target); - this.refill = refill; - } - - public boolean isRefill() { - return refill; - } - - @Override - protected double computeWeight() { - T s = source.getEntry(); - T t = target.getEntry(); - return s.getDistance(t); - } - - @Override - public String toString() { - return source.getEntry().toString() + " - "+ weight - + (refill ? "R" : "") - +" -> " + target.getEntry().toString(); - } - } } diff --git a/core/src/main/java/ru/trader/analysis/graph/Crawler.java b/core/src/main/java/ru/trader/analysis/graph/Crawler.java index 1a963b9..5ea60a3 100644 --- a/core/src/main/java/ru/trader/analysis/graph/Crawler.java +++ b/core/src/main/java/ru/trader/analysis/graph/Crawler.java @@ -13,21 +13,27 @@ public class Crawler { private final static int THRESHOLD = 4; private final static Logger LOG = LoggerFactory.getLogger(Crawler.class); - private final Graph graph; + protected final Graph graph; private final Consumer>> onFoundFunc; + private int maxSize; public Crawler(Graph graph, Consumer>> onFoundFunc) { this.graph = graph; + maxSize = graph.getRoot().getLevel(); this.onFoundFunc = onFoundFunc; } - private List> getCopyList(List> head, Edge tail){ + protected List> getCopyList(List> head, Edge tail){ List> res = new ArrayList<>(20); res.addAll(head); res.add(tail); return res; } + public void setMaxSize(int maxSize) { + this.maxSize = maxSize; + } + public void findFast(T target){ findFast(target, 1); } @@ -37,9 +43,11 @@ public class Crawler { int found = 0; if (t != null) { if (count > 1) { - found = bfs(new ArrayList<>(), graph.root, target, 0, count); + Vertex s = graph.getRoot(); + s.sortEdges(); + found = bfs(start(s), target, 0, count); } else { - found = dfs(new ArrayList<>(), graph.root, target, t.getLevel() + 1, count); + found = dfs(start(graph.getRoot()), target, t.getLevel() + 1, count); } } LOG.debug("Found {} paths", found); @@ -53,31 +61,53 @@ public class Crawler { Vertex t = graph.getVertex(target); int found = 0; if (t != null) { - found = ucs(new ArrayList<>(), graph.root, target, 0, count); + found = ucs(costStart(graph.getRoot()), target, 0, count); } LOG.debug("Found {} paths", found); } - private int dfs(List> head, Vertex source, T target, int deep, int count) { - LOG.trace("DFS from {} to {}, deep {}, count {}, head {}", source, target, deep, count, head); + protected TraversalEntry start(Vertex vertex){ + return new TraversalEntry(new ArrayList<>(), vertex); + } + + protected CostTraversalEntry costStart(Vertex vertex){ + return new CostTraversalEntry(new ArrayList<>(), vertex); + } + + protected TraversalEntry travers(TraversalEntry entry, Edge edge){ + return new TraversalEntry(getCopyList(entry.head, edge), edge.getTarget()); + } + + private CostTraversalEntry costTravers(CostTraversalEntry entry, Edge edge){ + return costTravers(entry, getCopyList(entry.head, edge), edge); + } + + protected CostTraversalEntry costTravers(CostTraversalEntry entry, List> head, Edge edge){ + return new CostTraversalEntry(head, edge, entry.getWeight()); + } + + private int dfs(TraversalEntry entry, T target, int deep, int count) { int found = 0; + List> head = entry.head; + Vertex source = entry.vertex; + LOG.trace("DFS from {} to {}, deep {}, count {}, head {}", source, target, deep, count, head); if (deep == source.getLevel()){ - Optional> last = source.getEdges().parallelStream() - .filter(next -> next.isConnect(target)) - .findFirst(); - if (last.isPresent()){ - List> res = getCopyList(head, last.get()); - LOG.debug("Last edge find, path {}", res); - onFoundFunc.accept(res); - found++; + for (Edge next : entry.getEdges()) { + if (next.isConnect(target)){ + List> res = getCopyList(head, next); + LOG.debug("Last edge find, path {}", res); + onFoundFunc.accept(res); + found++; + break; + } } } if (found < count){ - if (deep < source.getLevel()) { + if (deep < source.getLevel() && head.size() < maxSize-1) { LOG.trace("Search around"); - for (Edge edge : source.getEdges()) { + for (Edge edge : entry.getEdges()) { if (edge.getTarget().isSingle()) continue; - found += dfs(getCopyList(head, edge), edge.getTarget(), target, deep, count-found); + found += dfs(travers(entry, edge), target, deep, count-found); if (found >= count) break; } } @@ -85,18 +115,23 @@ public class Crawler { return found; } - private int bfs(List> head, Vertex source, T target, int deep, int count) { - LOG.trace("BFS from {} to {}, deep {}, count {}", source, target, deep, count); + private int bfs(TraversalEntry root, T target, int deep, int count) { + LOG.trace("BFS from {} to {}, deep {}, count {}", root.vertex, target, deep, count); int found = 0; LinkedList queue = new LinkedList<>(); - queue.add(new TraversalEntry(head, source)); + queue.add(root); while (!queue.isEmpty() && count > found){ TraversalEntry entry = queue.poll(); - head = entry.head; - source = entry.vertex; + List> head = entry.head; + Vertex source = entry.vertex; + if (head.size() >= maxSize){ + LOG.trace("Is limit deep"); + continue; + } LOG.trace("Search from {} to {}, head {}", source, target, head); - source.sortEdges(); - for (Edge edge : source.getEdges()) { + Iterator> iterator = entry.iterator(); + while (iterator.hasNext()){ + Edge edge = iterator.next(); if (edge.isConnect(target)){ List> res = getCopyList(head, edge); LOG.debug("Last edge find, path {}", res); @@ -106,25 +141,26 @@ public class Crawler { if (found >= count) break; if (edge.getTarget().isSingle()) continue; if (deep < source.getLevel()) { - queue.add(new TraversalEntry(getCopyList(head, edge), edge.getTarget())); + edge.getTarget().sortEdges(); + queue.add(travers(entry, edge)); } } } return found; } - private int ucs(List> head, Vertex source, T target, int deep, int count) { - LOG.trace("UCS from {} to {}, deep {}, count {}", source, target, deep, count); + private int ucs(CostTraversalEntry root, T target, int deep, int count) { + LOG.trace("UCS from {} to {}, deep {}, count {}", root.vertex, target, deep, count); int found = 0; PriorityQueue queue = new PriorityQueue<>(); - queue.add(new CostTraversalEntry(head, source)); + queue.add(root); while (!queue.isEmpty() && count > found){ CostTraversalEntry entry = queue.poll(); - LOG.trace("Check path head {}, edge {}, cost {}", entry.head, entry.edge, entry.cost); - head = entry.head; + LOG.trace("Check path head {}, edge {}, weight {}", entry.head, entry.edge, entry.weight); + List> head = entry.head; + Vertex source = entry.vertex; Edge edge = entry.edge; if (edge != null) { - source = edge.getSource(); if (edge.isConnect(target)) { List> res = getCopyList(head, edge); LOG.debug("Path found {}", res); @@ -137,13 +173,17 @@ public class Crawler { } head = getCopyList(entry.head, edge); } - Iterator> iterator = entry.iterator; + if (head.size() >= maxSize){ + LOG.trace("Is limit deep"); + continue; + } + Iterator> iterator = entry.iterator(); //put only 2 entry for iterate while (iterator.hasNext()){ edge = iterator.next(); if (deep < source.getLevel() && !edge.getTarget().isSingle() || edge.isConnect(target)) { LOG.trace("Add edge {} to queue", edge); - queue.add(new CostTraversalEntry(head, edge, entry.cost)); + queue.add(costTravers(entry, head, edge)); } } } @@ -151,17 +191,17 @@ public class Crawler { } //last edge don't compare - private int ucs2(List> head, Vertex source, T target, int deep, int count) { - LOG.trace("UCS2 from {} to {}, deep {}, count {}", source, target, deep, count); + private int ucs2(CostTraversalEntry root, T target, int deep, int count) { + LOG.trace("UCS2 from {} to {}, deep {}, count {}", root.vertex, target, deep, count); int found = 0; PriorityQueue queue = new PriorityQueue<>(); - source.sortEdges(); - queue.add(new CostTraversalEntry(head, source)); + queue.add(root); while (!queue.isEmpty() && count > found){ CostTraversalEntry entry = queue.peek(); - head = entry.edge != null ? getCopyList(entry.head, entry.edge) : entry.head; - Iterator> iterator = entry.iterator; - LOG.trace("Check path head {}, cost {}", head, entry.cost); + List> head = entry.edge != null ? getCopyList(entry.head, entry.edge) : entry.head; + Vertex source = entry.vertex; + Iterator> iterator = entry.iterator(); + LOG.trace("Check path head {}, weight {}", head, entry.weight); int i = 0; //put only 2 entry for iterate while (iterator.hasNext() && i < 2){ @@ -175,9 +215,9 @@ public class Crawler { } if (found >= count) break; if (edge.getTarget().isSingle()) continue; - if (deep < source.getLevel()) { + if (deep < source.getLevel() && head.size() < maxSize-1) { edge.getTarget().sortEdges(); - queue.add(new CostTraversalEntry(head, edge, entry.cost)); + queue.add(costTravers(entry, head, edge)); i++; } } @@ -186,39 +226,59 @@ public class Crawler { return found; } - private class TraversalEntry { - private final List> head; - private final Vertex vertex; + protected class TraversalEntry { + protected final List> head; + protected final Vertex vertex; + private Iterator> iterator; - private TraversalEntry(List> head, Vertex vertex) { + protected TraversalEntry(List> head, Vertex vertex) { this.head = head; this.vertex = vertex; } + + public Iterator> iterator(){ + if (iterator == null){ + iterator = getIteratorInstance(); + } + return iterator; + } + + protected Iterator> getIteratorInstance(){ + return vertex.getEdges().iterator(); + } + + private Iterable> getEdges(){ + return this::iterator; + } } - private class CostTraversalEntry implements Comparable{ - private final List> head; + protected class CostTraversalEntry extends TraversalEntry implements Comparable{ private final Edge edge; - private final Iterator> iterator; private final double cost; + private Double weight; - private CostTraversalEntry(List> head, Vertex vertex) { - this.head = head; - this.iterator = vertex.getEdges().iterator(); + protected CostTraversalEntry(List> head, Vertex vertex) { + super(head, vertex); this.edge = null; this.cost = 0; } - private CostTraversalEntry(List> head, Edge edge, double cost) { - this.head = head; + protected CostTraversalEntry(List> head, Edge edge, double cost) { + super(head, edge.getTarget()); this.edge = edge; - this.iterator = edge.getTarget().getEdges().iterator(); - this.cost = cost + edge.getWeight(); + this.cost = cost; + } + + protected double getWeight(){ + if (weight == null){ + weight = cost + (edge !=null ? edge.getWeight() : 0); + } + return weight; } @Override public int compareTo(@NotNull CostTraversalEntry other) { - int cmp = Double.compare(cost, other.cost); + int cmp = Double.compare(getWeight(), other.getWeight()); if (cmp != 0) return cmp; return Integer.compare(head.size(), other.head.size()); } diff --git a/core/src/main/java/ru/trader/analysis/graph/Graph.java b/core/src/main/java/ru/trader/analysis/graph/Graph.java index 6666bd0..c069dab 100644 --- a/core/src/main/java/ru/trader/analysis/graph/Graph.java +++ b/core/src/main/java/ru/trader/analysis/graph/Graph.java @@ -1,144 +1,11 @@ package ru.trader.analysis.graph; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ru.trader.analysis.AnalysisCallBack; +public interface Graph { + boolean isAccessible(T entry); -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.RecursiveAction; + Vertex getVertex(T entry); -public abstract class Graph { - private final static ForkJoinPool POOL = new ForkJoinPool(); - private final static int THRESHOLD = 4; + Vertex getRoot(); - private final static Logger LOG = LoggerFactory.getLogger(Graph.class); - - protected Vertex root; - protected final Map> vertexes; - private final GraphCallBack callback; - - protected int minJumps; - - protected Graph() { - this(new AnalysisCallBack()); - } - - protected Graph(AnalysisCallBack callback) { - this.callback = new GraphCallBack(callback); - vertexes = new ConcurrentHashMap<>(50, 0.9f, THRESHOLD); - } - - protected abstract GraphBuilder createGraphBuilder(Vertex vertex, Collection set, int deep, double limit); - - public void build(T start, Collection set, int maxDeep, double limit) { - callback.startBuild(start); - root = getInstance(start, maxDeep); - POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit)); - if (set.size() > vertexes.size()){ - minJumps = maxDeep; - } else { - minJumps = 1; - for (Vertex vertex : vertexes.values()) { - int jumps = maxDeep - vertex.getLevel(); - if (jumps > minJumps) minJumps = jumps; - } - } - callback.endBuild(); - } - - private Vertex getInstance(T entry, int deep){ - Vertex vertex = new Vertex<>(entry); - vertex.setLevel(deep); - Vertex old = vertexes.get(entry); - if (old == null || old.getLevel() < deep) { - LOG.trace("Is top vertex"); - vertexes.put(entry, vertex); - } - return vertex; - } - - public boolean isAccessible(T entry){ - return vertexes.containsKey(entry); - } - - public Vertex getVertex(T entry){ - return vertexes.get(entry); - } - - public T getRoot() { - return root.getEntry(); - } - - public int getMinJumps() { - return minJumps; - } - - protected abstract class GraphBuilder extends RecursiveAction { - protected final Vertex vertex; - protected final Collection set; - protected final int deep; - protected final double limit; - - protected GraphBuilder(Vertex vertex, Collection set, int deep, double limit) { - this.vertex = vertex; - this.set = set; - this.deep = deep; - this.limit = limit; - } - - protected abstract double onConnect(T entry); - protected abstract Edge createEdge(Vertex target); - - @Override - protected void compute() { - LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep); - ArrayList subTasks = new ArrayList<>(THRESHOLD); - Iterator iterator = set.iterator(); - while (iterator.hasNext()) { - if (callback.isCancel()) break; - T entry = iterator.next(); - if (entry == vertex.getEntry()) continue; - double nextLimit = onConnect(entry); - if (nextLimit >= 0) { - LOG.trace("Connect {} to {}", vertex, entry); - Vertex next = getInstance(entry, vertex.getLevel() - 1); - vertex.connect(createEdge(next)); - if (deep > 0) { - //Recursive build - GraphBuilder task = createGraphBuilder(next, set, deep - 1, nextLimit); - task.fork(); - subTasks.add(task); - } - } else { - LOG.trace("Vertex {} is far away", entry); - } - if (subTasks.size() == THRESHOLD || !iterator.hasNext()){ - for (GraphBuilder subTask : subTasks) { - if (callback.isCancel()){ - subTask.cancel(true); - } else { - subTask.join(); - } - } - subTasks.clear(); - } - } - if (!subTasks.isEmpty()){ - for (GraphBuilder subTask : subTasks) { - if (callback.isCancel()){ - subTask.cancel(true); - } else { - subTask.join(); - } - } - subTasks.clear(); - } - LOG.trace("End build graph from {} on deep {}", vertex, deep); - } - } + int getMinJumps(); } diff --git a/core/src/main/java/ru/trader/analysis/graph/Vertex.java b/core/src/main/java/ru/trader/analysis/graph/Vertex.java index 9a566d9..4847afb 100644 --- a/core/src/main/java/ru/trader/analysis/graph/Vertex.java +++ b/core/src/main/java/ru/trader/analysis/graph/Vertex.java @@ -22,7 +22,7 @@ public class Vertex { return this.entry.equals(entry); } - void setLevel(int level) { + public void setLevel(int level) { this.level = level; } diff --git a/core/src/main/java/ru/trader/core/Profile.java b/core/src/main/java/ru/trader/core/Profile.java index cb37276..cd28934 100644 --- a/core/src/main/java/ru/trader/core/Profile.java +++ b/core/src/main/java/ru/trader/core/Profile.java @@ -6,10 +6,21 @@ public class Profile { private int jumps; private Ship ship; private boolean refill; + //Scorer multipliers + private int scoreOrdersCount; + private double distanceMult; + private double jumpMult; + private double landMult; + private double fuelPrice; public Profile(Ship ship) { this.ship = ship; refill = true; + scoreOrdersCount = 5; + distanceMult = 1; + landMult = 1; + fuelPrice = 1; + jumpMult = 0.01; } public double getBalance() { @@ -43,4 +54,44 @@ public class Profile { public void setRefill(boolean refill) { this.refill = refill; } + + public int getScoreOrdersCount() { + return scoreOrdersCount; + } + + public void setScoreOrdersCount(int scoreOrdersCount) { + this.scoreOrdersCount = scoreOrdersCount; + } + + public double getDistanceMult() { + return distanceMult; + } + + public void setDistanceMult(double distanceMult) { + this.distanceMult = distanceMult; + } + + public double getJumpMult() { + return jumpMult; + } + + public void setJumpMult(double jumpMult) { + this.jumpMult = jumpMult; + } + + public double getLandMult() { + return landMult; + } + + public void setLandMult(double landMult) { + this.landMult = landMult; + } + + public double getFuelPrice() { + return fuelPrice; + } + + public void setFuelPrice(double fuelPrice) { + this.fuelPrice = fuelPrice; + } } diff --git a/core/src/test/java/ru/trader/analysis/graph/CrawlerTest.java b/core/src/test/java/ru/trader/analysis/graph/CrawlerTest.java index 84bd093..ac1e9ce 100644 --- a/core/src/test/java/ru/trader/analysis/graph/CrawlerTest.java +++ b/core/src/test/java/ru/trader/analysis/graph/CrawlerTest.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; public class CrawlerTest extends Assert { private final static Logger LOG = LoggerFactory.getLogger(CrawlerTest.class); @@ -89,7 +90,7 @@ public class CrawlerTest extends Assert { // x5 <-> x4, x5 <-> x6 SimpleCollector paths = new SimpleCollector(); - Crawler crawler = new Crawler<>(graph, paths::add); + Crawler crawler = new CCrawler<>(graph, paths::add); crawler.findMin(x4, 10); assertPaths(paths.get(), PPath.of(x5, x4)); paths.clear(); @@ -118,7 +119,7 @@ public class CrawlerTest extends Assert { // x5 <-> x4 <-> x3 <-> x2, x5 <-> x6 <-> x7 <-> x8 // x5 <-> x3, x4 <-> x2, x3 <-> x6, x4 <-> x6 SimpleCollector paths = new SimpleCollector(); - Crawler crawler = new Crawler<>(graph, paths::add); + Crawler crawler = new CCrawler<>(graph, paths::add); crawler.findMin(x8, 10); assertPaths(paths.get(), PPath.of(x5, x6, x7, x8)); @@ -135,11 +136,16 @@ public class CrawlerTest extends Assert { crawler.findMin(x4, 20); assertPaths(true, paths.get(), PPath.of(x5, x4), PPath.of(x5, x3, x4), PPath.of(x5, x6, x4), - PPath.of(x5, x6, x5, x4), PPath.of(x5, x4, x3, x4), PPath.of(x5, x4, x5, x4), - PPath.of(x5, x6, x3, x4), PPath.of(x5, x4, x6, x4), - PPath.of(x5, x3, x5, x4), PPath.of(x5, x3, x2, x4), - PPath.of(x5, x4, x2, x4), PPath.of(x5, x3, x6, x4) + PPath.of(x5, x6, x5, x4), PPath.of(x5, x4, x5, x4), PPath.of(x5, x4, x3, x4), + PPath.of(x5, x4, x6, x4), PPath.of(x5, x6, x3, x4), + PPath.of(x5, x3, x5, x4), PPath.of(x5, x4, x2, x4), + PPath.of(x5, x3, x2, x4), PPath.of(x5, x3, x6, x4) ); + TestUtil.assertCollectionEquals(paths.getWeights(), 5.0, 15.0, 15.0, + 15.0, 15.0, 15.0, + 25.0, 25.0, + 25.0, 35.0, + 35.0, 35.0); paths.clear(); crawler.findMin(x5, 20); @@ -177,7 +183,7 @@ public class CrawlerTest extends Assert { // x5 <-> x4 <- refill -> x3 <- refill -> x2, x5 <-> x6 // x5 <-> x3 <- refill -> x2, x5 <-> x4 <- refill -> x6 SimpleCollector paths = new SimpleCollector(); - Crawler crawler = new Crawler<>(graph, paths::add); + Crawler crawler = new CCrawler<>(graph, paths::add); crawler.findMin(x1, 10); assertTrue(paths.get().isEmpty()); @@ -188,10 +194,14 @@ public class CrawlerTest extends Assert { paths.clear(); crawler.findMin(x6, 10); - assertPaths(true, paths.get(), PPath.of(x5, x6), PPath.of(x5, x4, x6), - PPath.of(x5, x6, x5, x6), PPath.of(x5, x4, x5, x6), - PPath.of(x5, x3, x5, x6), PPath.of(x5, x3, x4, x6), + assertPaths(paths.get(), PPath.of(x5, x6), PPath.of(x5, x4, x6), + PPath.of(x5, x4, x5, x6), PPath.of(x5, x6, x5, x6), + PPath.of(x5, x3, x4, x6), PPath.of(x5, x3, x5, x6), PPath.of(x5, x6, x4, x6)); + TestUtil.assertCollectionEquals(paths.getWeights(), 5.0, 15.0, + 15.0, 15.0, + 25.0, 25.0, + 25.0); paths.clear(); crawler.findFast(x2); @@ -216,7 +226,7 @@ public class CrawlerTest extends Assert { // x5 <-> x3 <- refill -> x2 // x5 <-> x4 <- refill -> x6 SimpleCollector paths = new SimpleCollector(); - Crawler crawler = new Crawler<>(graph, paths::add); + Crawler crawler = new CCrawler<>(graph, paths::add); crawler.findMin(x1, 10); assertTrue(paths.get().isEmpty()); @@ -227,7 +237,7 @@ public class CrawlerTest extends Assert { PPath.of(x5, x4, x3, x2), PPath.of(x5, x4, x2), PPath.of(x5, x3, x5, x4, x2), PPath.of(x5, x6, x4, x2), PPath.of(x5, x6, x4, x3, x2), PPath.of(x5, x4, x3, x4, x2), PPath.of(x5, x4, x5, x4, x2), PPath.of(x5, x6, x5, x4, x2), PPath.of(x5, x3, x4, x3, x2), - PPath.of(x5, x3, x4, x3, x2), PPath.of(x5, x3, x4, x3, x2), PPath.of(x5, x3, x4, x3, x2)); + PPath.of(x5, x3, x4, x3, x2), PPath.of(x5, x3, x4, x3, x2), PPath.of(x5, x3, x4, x3, x2)); paths.clear(); crawler.findMin(x6, 30); @@ -269,13 +279,22 @@ public class CrawlerTest extends Assert { return paths; } - public List> get(int indx) { - if (indx >= paths.size()) return Collections.emptyList(); - return paths.get(indx); + public List> get(int index) { + if (index >= paths.size()) return Collections.emptyList(); + return paths.get(index); } public void clear(){ paths.clear(); } + + public double getWeight(int index){ + if (index >= paths.size()) return 0; + return paths.get(index).stream().mapToDouble(Edge::getWeight).sum(); + } + + public Collection getWeights(){ + return paths.stream().map(p -> p.stream().mapToDouble(Edge::getWeight).sum()).collect(Collectors.toList()); + } } } \ No newline at end of file