diff --git a/core/src/main/java/ru/trader/analysis/AbstractCrawlerSpecification.java b/core/src/main/java/ru/trader/analysis/AbstractCrawlerSpecification.java new file mode 100644 index 0000000..6ff6e1a --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/AbstractCrawlerSpecification.java @@ -0,0 +1,28 @@ +package ru.trader.analysis; + + +import ru.trader.analysis.graph.Edge; +import ru.trader.core.Vendor; + +import java.util.List; +import java.util.function.Consumer; + +public abstract class AbstractCrawlerSpecification implements CrawlerSpecification { + private final RouteSpecification routeSpecification; + private final Consumer>> onFoundFunc; + + protected AbstractCrawlerSpecification(RouteSpecification routeSpecification, Consumer>> onFoundFunc) { + this.routeSpecification = routeSpecification; + this.onFoundFunc = onFoundFunc; + } + + @Override + public RouteSpecification routeSpecification() { + return routeSpecification; + } + + @Override + public Consumer>> onFoundFunc() { + return onFoundFunc; + } +} diff --git a/core/src/main/java/ru/trader/analysis/CrawlerSpecification.java b/core/src/main/java/ru/trader/analysis/CrawlerSpecification.java new file mode 100644 index 0000000..296d403 --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/CrawlerSpecification.java @@ -0,0 +1,19 @@ +package ru.trader.analysis; + +import ru.trader.analysis.graph.Edge; +import ru.trader.core.Vendor; + +import java.util.List; +import java.util.function.Consumer; + +public interface CrawlerSpecification { + + public double computeWeight(VendorsCrawler.VendorsEdge edge); + + public double computeWeight(VendorsCrawler.VendorsTraversalEntry entry); + + public RouteSpecification routeSpecification(); + + public Consumer>> onFoundFunc(); + +} diff --git a/core/src/main/java/ru/trader/analysis/CrawlerSpecificationByProfit.java b/core/src/main/java/ru/trader/analysis/CrawlerSpecificationByProfit.java new file mode 100644 index 0000000..bffa864 --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/CrawlerSpecificationByProfit.java @@ -0,0 +1,39 @@ +package ru.trader.analysis; + +import ru.trader.analysis.graph.Edge; +import ru.trader.core.Vendor; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; + +public class CrawlerSpecificationByProfit extends AbstractCrawlerSpecification { + + public CrawlerSpecificationByProfit(Consumer>> onFoundFunc) { + super(null, onFoundFunc); + } + + public CrawlerSpecificationByProfit(RouteSpecification routeSpecification, Consumer>> onFoundFunc) { + super(routeSpecification, onFoundFunc); + } + + @Override + public double computeWeight(VendorsCrawler.VendorsEdge edge) { + return edge.getTime()/edge.getProfitByTonne(); + } + + @Override + public double computeWeight(VendorsCrawler.VendorsTraversalEntry entry) { + double profit = 0; double time = 0; + Iterator> iterator = entry.routeIterator(); + while (iterator.hasNext()){ + VendorsCrawler.VendorsEdge edge = (VendorsCrawler.VendorsEdge)iterator.next(); + if (edge != null){ + profit += edge.getProfitByTonne(); + time += edge.getTime(); + } + } + return profit > 1 ? time / profit : time; + } + +} diff --git a/core/src/main/java/ru/trader/analysis/CrawlerSpecificationByTime.java b/core/src/main/java/ru/trader/analysis/CrawlerSpecificationByTime.java new file mode 100644 index 0000000..be6766c --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/CrawlerSpecificationByTime.java @@ -0,0 +1,37 @@ +package ru.trader.analysis; + +import ru.trader.analysis.graph.Edge; +import ru.trader.core.Vendor; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; + +public class CrawlerSpecificationByTime extends AbstractCrawlerSpecification { + public CrawlerSpecificationByTime(Consumer>> onFoundFunc) { + super(null, onFoundFunc); + } + + public CrawlerSpecificationByTime(RouteSpecification routeSpecification, Consumer>> onFoundFunc) { + super(routeSpecification, onFoundFunc); + } + + @Override + public double computeWeight(VendorsCrawler.VendorsEdge edge) { + return edge.getTime(); + } + + @Override + public double computeWeight(VendorsCrawler.VendorsTraversalEntry entry) { + double time = 0; + Iterator> iterator = entry.routeIterator(); + while (iterator.hasNext()){ + VendorsCrawler.VendorsEdge edge = (VendorsCrawler.VendorsEdge)iterator.next(); + if (edge != null){ + time += edge.getTime(); + } + } + return time; + } + +} diff --git a/core/src/main/java/ru/trader/analysis/RouteSearcher.java b/core/src/main/java/ru/trader/analysis/RouteSearcher.java index 36493d8..5b9462e 100644 --- a/core/src/main/java/ru/trader/analysis/RouteSearcher.java +++ b/core/src/main/java/ru/trader/analysis/RouteSearcher.java @@ -119,10 +119,10 @@ public class RouteSearcher { public static Route toRoute(List> edges, final Scorer scorer){ List entries = new ArrayList<>(edges.size()+1); Vendor buyer = null; - VendorsGraph.VendorsEdge vEdge = null; + VendorsCrawler.VendorsEdge vEdge = null; RouteEntry prev = null; for (Edge e : edges) { - vEdge = (VendorsGraph.VendorsEdge) e; + vEdge = (VendorsCrawler.VendorsEdge) e; List> transitEdges = vEdge.getPath().getEntries(); for (int k = 0; k < transitEdges.size(); k++) { ConnectibleEdge edge = transitEdges.get(k); diff --git a/core/src/main/java/ru/trader/analysis/VendorsCrawler.java b/core/src/main/java/ru/trader/analysis/VendorsCrawler.java new file mode 100644 index 0000000..025e0ce --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/VendorsCrawler.java @@ -0,0 +1,214 @@ +package ru.trader.analysis; + +import org.jetbrains.annotations.NotNull; +import ru.trader.analysis.graph.*; +import ru.trader.core.Order; +import ru.trader.core.Vendor; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +public class VendorsCrawler extends Crawler { + private double startFuel; + private double startBalance; + + public VendorsCrawler(VendorsGraph graph, CrawlerSpecification specification, AnalysisCallBack callback) { + super(graph, specification.routeSpecification(), specification.onFoundFunc(), callback); + startFuel = graph.getProfile().getShip().getTank(); + startBalance = graph.getProfile().getBalance(); + } + + private Scorer getScorer(){ + return ((VendorsGraph)graph).getScorer(); + } + + public void setStartFuel(double startFuel) { + this.startFuel = startFuel; + } + + public void setStartBalance(double startBalance) { + this.startBalance = startBalance; + } + + @Override + protected VendorsTraversalEntry start(Vertex vertex) { + return new VendorsTraversalEntry(super.start(vertex), startFuel, startBalance); + } + + @Override + protected VendorsTraversalEntry travers(final CostTraversalEntry entry, final Edge edge) { + VendorsTraversalEntry vEntry = (VendorsTraversalEntry)entry; + VendorsEdge vEdge = (VendorsEdge) edge; + return new VendorsTraversalEntry(vEntry, vEdge); + } + + protected class VendorsTraversalEntry extends CostTraversalEntry { + private final double fuel; + private final double balance; + + protected VendorsTraversalEntry(CostTraversalEntry entry, double fuel, double balance) { + super(entry.getTarget()); + this.fuel = fuel; + this.balance = balance; + } + + protected VendorsTraversalEntry(VendorsTraversalEntry head, VendorsEdge edge) { + super(head, edge); + this.balance = head.balance + edge.getProfit(); + this.fuel = edge.getRemain(); + } + + @Override + public List> collect(Collection> src) { + return src.stream().filter(this::check).map(this::wrap).filter(e -> e != null).collect(Collectors.toList()); + } + + protected boolean check(Edge e){ + VendorsGraph.VendorsBuildEdge edge = (VendorsGraph.VendorsBuildEdge) e; + return fuel <= edge.getMaxFuel() && (fuel >= edge.getMinFuel() || edge.getSource().getEntry().canRefill()) + && (edge.getProfit() > 0 || VendorsCrawler.this.isFound(edge, this)); + } + + protected VendorsEdge wrap(Edge e) { + VendorsGraph.VendorsBuildEdge edge = (VendorsGraph.VendorsBuildEdge) e; + Path path = edge.getPath(fuel); + if (path == null) return null; + VendorsEdge res = new VendorsEdge(edge.getSource(), edge.getTarget(), new TransitPath(path, fuel)); + res.setOrders(MarketUtils.getStack(edge.getOrders(), balance, getScorer().getProfile().getShip().getCargo())); + return res; + } + + @Override + public double getWeight() { + if (weight == null){ + double profit = 0; double time = 0; + Iterator> iterator = routeIterator(); + while (iterator.hasNext()){ + VendorsEdge edge = (VendorsEdge)iterator.next(); + if (edge != null){ + profit += edge.getProfitByTonne(); + time += edge.getTime(); + } + } + weight = profit > 1 ? time / profit : time; + } + return weight; + } + } + + public class VendorsEdge extends ConnectibleEdge { + private TransitPath path; + private List orders; + private Double profitByTonne; + private Long time; + + protected VendorsEdge(Vertex source, Vertex target, TransitPath path) { + super(source, target); + if (path == null) throw new IllegalArgumentException("Path must be no-null"); + this.path = path; + } + + protected void setOrders(List orders){ + this.orders = orders; + } + + public double getProfit(){ + return getOrders().stream().mapToDouble(Order::getProfit).sum(); + } + + public List getOrders(){ + if (orders == null){ + Vendor seller = source.getEntry(); + Vendor buyer = target.getEntry(); + orders = MarketUtils.getOrders(seller, buyer); + } + return orders; + } + + public double getRemain() { + return path.getRemain(); + } + + @Override + public boolean isRefill() { + return path.isRefill(); + } + + @Override + public double getFuelCost() { + if (path != null){ + return path.getFuelCost(); + } + return super.getFuelCost(); + } + + public TransitPath getPath() { + return path; + } + + public double getProfitByTonne() { + if (profitByTonne == null){ + profitByTonne = computeProfit(); + } + return profitByTonne; + } + + public long getTime() { + if (time == null){ + time = computeTime(); + } + return time; + } + + @Override + public int compareTo(@NotNull Edge other) { + double w = getWeight(); + double ow = other.getWeight(); + if (ow >= 0 && w >= 0) return super.compareTo(other); + if (w < 0 && ow < 0) return Double.compare(Math.abs(w), Math.abs(ow)); + return w < 0 ? 1 : -1; + } + + protected double computeProfit(){ + return getScorer().getProfitByTonne(getProfit(), getFuelCost()); + } + + protected long computeTime(){ + int jumps = source.getEntry().getPlace().equals(target.getEntry().getPlace())? 0 : 1; + int lands = 1; + if (path != null){ + jumps = path.size(); + lands += path.getRefillCount(); + //not lands if refuel on this station + if (path.isRefill()) lands--; + } else { + lands += isRefill() ? 1 :0; + } + return getScorer().getTime(target.getEntry().getDistance(), jumps, lands); + } + + @Override + protected double computeWeight() { + return getTime()/getProfitByTonne(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VendorsEdge)) return false; + if (!super.equals(o)) return false; + VendorsEdge edge = (VendorsEdge) o; + return !(path != null ? !path.equals(edge.path) : edge.path != null); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (path != null ? path.hashCode() : 0); + return result; + } + + } +} diff --git a/core/src/main/java/ru/trader/analysis/VendorsGraph.java b/core/src/main/java/ru/trader/analysis/VendorsGraph.java index 738f17a..4408e36 100644 --- a/core/src/main/java/ru/trader/analysis/VendorsGraph.java +++ b/core/src/main/java/ru/trader/analysis/VendorsGraph.java @@ -1,6 +1,5 @@ package ru.trader.analysis; -import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.trader.analysis.graph.*; @@ -8,7 +7,6 @@ import ru.trader.core.*; import java.util.*; import java.util.function.Consumer; -import java.util.stream.Collectors; public class VendorsGraph extends ConnectibleGraph { @@ -22,12 +20,28 @@ public class VendorsGraph extends ConnectibleGraph { this.scorer = scorer; } + public Scorer getScorer() { + return scorer; + } + public VendorsCrawler crawler(Consumer>> onFoundFunc, AnalysisCallBack callback){ - return new VendorsCrawler(onFoundFunc, callback); + return new VendorsCrawler(this, new CrawlerSpecificationByProfit(onFoundFunc), callback); } public VendorsCrawler crawler(RouteSpecification specification, Consumer>> onFoundFunc, AnalysisCallBack callback){ - return new VendorsCrawler(specification, onFoundFunc, callback); + return new VendorsCrawler(this, new CrawlerSpecificationByProfit(specification, onFoundFunc), callback); + } + + public VendorsCrawler crawlerByTime(Consumer>> onFoundFunc, AnalysisCallBack callback){ + return new VendorsCrawler(this, new CrawlerSpecificationByTime(onFoundFunc), callback); + } + + public VendorsCrawler crawlerByTime(RouteSpecification specification, Consumer>> onFoundFunc, AnalysisCallBack callback){ + return new VendorsCrawler(this, new CrawlerSpecificationByTime(specification, onFoundFunc), callback); + } + + public VendorsCrawler crawler(CrawlerSpecification specification, AnalysisCallBack callback){ + return new VendorsCrawler(this, specification, callback); } @Override @@ -298,7 +312,7 @@ public class VendorsGraph extends ConnectibleGraph { return paths; } - private Path getPath(double fuel){ + public Path getPath(double fuel){ Path res = null; for (Path p : paths) { if (fuel >= p.getMinFuel() && fuel <= p.getMaxFuel() || getSource().getEntry().canRefill()) { @@ -340,216 +354,6 @@ public class VendorsGraph extends ConnectibleGraph { update(path); } } - - public VendorsEdge getInstance(double fuel, double balance){ - Path path = getPath(fuel); - if (path == null) return null; - VendorsEdge res = new VendorsEdge(source, target, new TransitPath(path,fuel)); - res.setOrders(MarketUtils.getStack(getOrders(), balance, getShip().getCargo())); - return res; - } } - public class VendorsEdge extends ConnectibleEdge { - private TransitPath path; - private List orders; - private Double profitByTonne; - private Long time; - - protected VendorsEdge(Vertex source, Vertex target, TransitPath path) { - super(source, target); - if (path == null) throw new IllegalArgumentException("Path must be no-null"); - this.path = path; - } - - protected void setOrders(List orders){ - this.orders = orders; - } - - public double getProfit(){ - return getOrders().stream().mapToDouble(Order::getProfit).sum(); - } - - public List getOrders(){ - if (orders == null){ - Vendor seller = source.getEntry(); - Vendor buyer = target.getEntry(); - orders = MarketUtils.getOrders(seller, buyer); - } - return orders; - } - - public double getRemain() { - return path.getRemain(); - } - - @Override - public boolean isRefill() { - return path.isRefill(); - } - - @Override - public double getFuelCost() { - if (path != null){ - return path.getFuelCost(); - } - return super.getFuelCost(); - } - - public TransitPath getPath() { - return path; - } - - public double getProfitByTonne() { - if (profitByTonne == null){ - profitByTonne = computeProfit(); - } - return profitByTonne; - } - - public long getTime() { - if (time == null){ - time = computeTime(); - } - return time; - } - - @Override - public int compareTo(@NotNull Edge other) { - double w = getWeight(); - double ow = other.getWeight(); - if (ow >= 0 && w >= 0) return super.compareTo(other); - if (w < 0 && ow < 0) return Double.compare(Math.abs(w), Math.abs(ow)); - return w < 0 ? 1 : -1; - } - - protected double computeProfit(){ - return scorer.getProfitByTonne(getProfit(), getFuelCost()); - } - - protected long computeTime(){ - int jumps = source.getEntry().getPlace().equals(target.getEntry().getPlace())? 0 : 1; - int lands = 1; - if (path != null){ - jumps = path.size(); - lands += path.getRefillCount(); - //not lands if refuel on this station - if (path.isRefill()) lands--; - } else { - lands += isRefill() ? 1 :0; - } - return scorer.getTime(target.getEntry().getDistance(), jumps, lands); - } - - @Override - protected double computeWeight() { - return getTime()/getProfitByTonne(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof VendorsEdge)) return false; - if (!super.equals(o)) return false; - VendorsEdge edge = (VendorsEdge) o; - return !(path != null ? !path.equals(edge.path) : edge.path != null); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (path != null ? path.hashCode() : 0); - return result; - } - - } - - public class VendorsCrawler extends Crawler { - private double startFuel; - private double startBalance; - - protected VendorsCrawler(Consumer>> onFoundFunc, AnalysisCallBack callback) { - super(VendorsGraph.this, onFoundFunc, callback); - startFuel = getShip().getTank(); - startBalance = getProfile().getBalance(); - } - - protected VendorsCrawler(RouteSpecification specification, Consumer>> onFoundFunc, AnalysisCallBack callback) { - super(VendorsGraph.this, specification, onFoundFunc, callback); - startFuel = getShip().getTank(); - startBalance = getProfile().getBalance(); - } - - public void setStartFuel(double startFuel) { - this.startFuel = startFuel; - } - - public void setStartBalance(double startBalance) { - this.startBalance = startBalance; - } - - @Override - protected VendorsTraversalEntry start(Vertex vertex) { - return new VendorsTraversalEntry(super.start(vertex), startFuel, startBalance); - } - - @Override - protected VendorsTraversalEntry travers(final CostTraversalEntry entry, final Edge edge) { - VendorsTraversalEntry vEntry = (VendorsTraversalEntry)entry; - VendorsEdge vEdge = (VendorsEdge) edge; - return new VendorsTraversalEntry(vEntry, vEdge); - } - - protected class VendorsTraversalEntry extends CostTraversalEntry { - private final double fuel; - private final double balance; - - protected VendorsTraversalEntry(CostTraversalEntry entry, double fuel, double balance) { - super(entry.getTarget()); - this.fuel = fuel; - this.balance = balance; - } - - protected VendorsTraversalEntry(VendorsTraversalEntry head, VendorsEdge edge) { - super(head, edge); - this.balance = head.balance + edge.getProfit(); - this.fuel = edge.getRemain(); - } - - @Override - public List> collect(Collection> src) { - return src.stream().filter(this::check).map(this::wrap).filter(e -> e != null).collect(Collectors.toList()); - } - - protected boolean check(Edge e){ - VendorsBuildEdge edge = (VendorsBuildEdge) e; - return fuel <= edge.getMaxFuel() && (fuel >= edge.getMinFuel() || edge.getSource().getEntry().canRefill()) - && (edge.getProfit() > 0 || VendorsCrawler.this.isFound(edge, this)); - } - - protected VendorsEdge wrap(Edge e) { - VendorsBuildEdge edge = (VendorsBuildEdge) e; - return edge.getInstance(fuel, balance); - } - - @Override - public double getWeight() { - if (weight == null){ - double profit = 0; double time = 0; - Iterator> iterator = routeIterator(); - while (iterator.hasNext()){ - VendorsEdge edge = (VendorsEdge)iterator.next(); - if (edge != null){ - profit += edge.getProfitByTonne(); - time += edge.getTime(); - } - } - weight = profit > 1 ? time / profit : time; - } - return weight; - } - } - - - } } 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 c298865..ad1762f 100644 --- a/core/src/main/java/ru/trader/analysis/graph/Crawler.java +++ b/core/src/main/java/ru/trader/analysis/graph/Crawler.java @@ -26,11 +26,7 @@ public class Crawler { private int maxSize; public Crawler(Graph graph, Consumer>> onFoundFunc, AnalysisCallBack callback) { - this.graph = graph; - this.callback = new CrawlerCallBack(callback); - maxSize = graph.getRoot().getLevel(); - this.onFoundFunc = onFoundFunc; - this.specification = (edge, entry) -> isTarget(edge); + this(graph, null, onFoundFunc, callback); } public Crawler(Graph graph, RouteSpecification specification, Consumer>> onFoundFunc, AnalysisCallBack callback) { @@ -38,7 +34,11 @@ public class Crawler { this.callback = new CrawlerCallBack(callback); maxSize = graph.getRoot().getLevel(); this.onFoundFunc = onFoundFunc; - this.specification = specification; + if (specification != null){ + this.specification = specification; + } else { + this.specification = (edge, entry) -> isTarget(edge); + } } protected List> getCopyList(Traversal head, Edge tail){