new more fasted route finder
This commit is contained in:
@@ -2,22 +2,20 @@ package ru.trader.analysis;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.analysis.graph.ConnectibleEdge;
|
||||||
import ru.trader.analysis.graph.Crawler;
|
import ru.trader.analysis.graph.Crawler;
|
||||||
import ru.trader.analysis.graph.Edge;
|
import ru.trader.analysis.graph.Edge;
|
||||||
import ru.trader.core.Order;
|
import ru.trader.core.Order;
|
||||||
import ru.trader.core.TransitVendor;
|
|
||||||
import ru.trader.core.Vendor;
|
import ru.trader.core.Vendor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class RouteSearcher {
|
public class RouteSearcher {
|
||||||
private final static Logger LOG = LoggerFactory.getLogger(RouteSearcher.class);
|
private final static Logger LOG = LoggerFactory.getLogger(RouteSearcher.class);
|
||||||
private final VendorsGraph vGraph;
|
private final Scorer scorer;
|
||||||
|
|
||||||
public RouteSearcher(Scorer scorer) {
|
public RouteSearcher(Scorer scorer) {
|
||||||
vGraph = new VendorsGraph(scorer);
|
this.scorer = scorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Route> getRoutes(Vendor from, Vendor to, Collection<Vendor> vendors){
|
public List<Route> getRoutes(Vendor from, Vendor to, Collection<Vendor> vendors){
|
||||||
@@ -30,10 +28,13 @@ public class RouteSearcher {
|
|||||||
|
|
||||||
private List<Route> search(Vendor source, Vendor target, Collection<Vendor> vendors){
|
private List<Route> search(Vendor source, Vendor target, Collection<Vendor> vendors){
|
||||||
LOG.trace("Start search route to {} from {}", source, target);
|
LOG.trace("Start search route to {} from {}", source, target);
|
||||||
|
VendorsGraph vGraph = new VendorsGraph(scorer);
|
||||||
|
LOG.trace("Build vendors graph");
|
||||||
vGraph.build(source, vendors);
|
vGraph.build(source, vendors);
|
||||||
|
LOG.trace("Graph is builds");
|
||||||
RouteCollector collector = new RouteCollector();
|
RouteCollector collector = new RouteCollector();
|
||||||
Crawler<Vendor> crawler = vGraph.crawler(collector::add);
|
Crawler<Vendor> crawler = vGraph.crawler(collector::add);
|
||||||
|
crawler.setMaxSize(scorer.getProfile().getLands());
|
||||||
if (target == null){
|
if (target == null){
|
||||||
int count = vGraph.getProfile().getRoutesCount() / vendors.size();
|
int count = vGraph.getProfile().getRoutesCount() / vendors.size();
|
||||||
for (Vendor vendor : vendors) {
|
for (Vendor vendor : vendors) {
|
||||||
@@ -49,10 +50,11 @@ public class RouteSearcher {
|
|||||||
private class RouteCollector {
|
private class RouteCollector {
|
||||||
private List<Route> routes = new ArrayList<>();
|
private List<Route> routes = new ArrayList<>();
|
||||||
|
|
||||||
public void add(List<Edge<Vendor>> edges){
|
public boolean add(List<Edge<Vendor>> edges){
|
||||||
Route route = toRoute(edges);
|
Route route = toRoute(edges);
|
||||||
route.setBalance(vGraph.getProfile().getBalance());
|
route.setBalance(scorer.getProfile().getBalance());
|
||||||
routes.add(route);
|
routes.add(route);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Route> get() {
|
public List<Route> get() {
|
||||||
@@ -62,34 +64,31 @@ public class RouteSearcher {
|
|||||||
private Route toRoute(List<Edge<Vendor>> edges){
|
private Route toRoute(List<Edge<Vendor>> edges){
|
||||||
List<RouteEntry> entries = new ArrayList<>(edges.size()+1);
|
List<RouteEntry> entries = new ArrayList<>(edges.size()+1);
|
||||||
Vendor buyer = null;
|
Vendor buyer = null;
|
||||||
VendorsGraph.VendorsEdge edge = null;
|
VendorsGraph.VendorsEdge vEdge = null;
|
||||||
for (int i = 0; i < edges.size(); i++) {
|
for (Edge<Vendor> e : edges) {
|
||||||
edge = (VendorsGraph.VendorsEdge) edges.get(i);
|
vEdge = (VendorsGraph.VendorsEdge) e;
|
||||||
|
List<ConnectibleEdge<Vendor>> transitEdges = vEdge.getPath().getEntries();
|
||||||
|
for (int k = 0; k < transitEdges.size(); k++) {
|
||||||
|
ConnectibleEdge<Vendor> edge = transitEdges.get(k);
|
||||||
Vendor vendor = edge.getSource().getEntry();
|
Vendor vendor = edge.getSource().getEntry();
|
||||||
RouteEntry entry = new RouteEntry(vendor, edge.isRefill(), edge.getFuel(), edge.getWeight());
|
RouteEntry entry = new RouteEntry(vendor, edge.isRefill(), edge.getFuelCost(), 0);
|
||||||
if (buyer != null && vendor.equals(buyer)) {
|
if (buyer != null && vendor.equals(buyer)) {
|
||||||
entry.setLand(true);
|
entry.setLand(true);
|
||||||
|
buyer = null;
|
||||||
}
|
}
|
||||||
List<Order> orders = edge.getOrders();
|
if (k == 0) {
|
||||||
|
entry.setScore(vEdge.getWeight());
|
||||||
|
List<Order> orders = vEdge.getOrders();
|
||||||
if (!orders.isEmpty()) {
|
if (!orders.isEmpty()) {
|
||||||
buyer = orders.get(0).getBuyer();
|
buyer = orders.get(0).getBuyer();
|
||||||
if (vendor instanceof TransitVendor){
|
|
||||||
Vendor seller = orders.get(0).getSell().getVendor();
|
|
||||||
for (int j = i-1; j >= 0; j--) {
|
|
||||||
RouteEntry sEntry = entries.get(j);
|
|
||||||
if (sEntry.is(seller)){
|
|
||||||
sEntry.addAll(orders);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
entry.addAll(orders);
|
entry.addAll(orders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entries.add(entry);
|
entries.add(entry);
|
||||||
}
|
}
|
||||||
if (edge != null) {
|
}
|
||||||
RouteEntry entry = new RouteEntry(edge.getTarget().getEntry(), false, 0, 0);
|
if (vEdge != null) {
|
||||||
|
RouteEntry entry = new RouteEntry(vEdge.getTarget().getEntry(), false, 0, 0);
|
||||||
if (buyer != null) entry.setLand(true);
|
if (buyer != null) entry.setLand(true);
|
||||||
entries.add(entry);
|
entries.add(entry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,10 +76,6 @@ public class Scorer {
|
|||||||
return avgDistance;
|
return avgDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getFuel(double distance){
|
|
||||||
return profile.getShip().getFuelCost(distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getScore(RouteEntry entry, int jumps) {
|
public double getScore(RouteEntry entry, int jumps) {
|
||||||
int lands = entry.isLand() ? 1 : 0;
|
int lands = entry.isLand() ? 1 : 0;
|
||||||
return getScore(entry.getVendor(), entry.getProfit(), jumps, lands, entry.getFuel());
|
return getScore(entry.getVendor(), entry.getProfit(), jumps, lands, entry.getFuel());
|
||||||
|
|||||||
112
core/src/main/java/ru/trader/analysis/TransitPath.java
Normal file
112
core/src/main/java/ru/trader/analysis/TransitPath.java
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package ru.trader.analysis;
|
||||||
|
|
||||||
|
|
||||||
|
import ru.trader.analysis.graph.ConnectibleEdge;
|
||||||
|
import ru.trader.analysis.graph.ConnectibleGraph;
|
||||||
|
import ru.trader.analysis.graph.Path;
|
||||||
|
import ru.trader.core.Vendor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TransitPath {
|
||||||
|
private final List<ConnectibleEdge<Vendor>> entries;
|
||||||
|
private double fuelCost;
|
||||||
|
private double remain;
|
||||||
|
private int refillCount;
|
||||||
|
|
||||||
|
public TransitPath(Path<Vendor> path, double fuel) {
|
||||||
|
List<ConnectibleGraph<Vendor>.BuildEdge> edges = path.getEdges();
|
||||||
|
entries = new ArrayList<>(edges.size());
|
||||||
|
createEdges(edges, fuel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createEdges(List<ConnectibleGraph<Vendor>.BuildEdge> edges, double fuel) {
|
||||||
|
fuelCost = 0; refillCount = 0;
|
||||||
|
for (int i = edges.size() - 1; i >= 0; i--) {
|
||||||
|
ConnectibleGraph<Vendor>.BuildEdge edge = edges.get(i);
|
||||||
|
ConnectibleEdge<Vendor> cEdge = new ConnectibleEdge<>(edge.getSource(), edge.getTarget());
|
||||||
|
double fuelCost = edge.getFuelCost(fuel);
|
||||||
|
this.fuelCost += fuelCost;
|
||||||
|
cEdge.setFuelCost(fuelCost);
|
||||||
|
entries.add(cEdge);
|
||||||
|
if (fuel < 0 || fuel < edge.getMinFuel()){
|
||||||
|
refillCount++;
|
||||||
|
fuel = refill(edges, entries.size()-1);
|
||||||
|
} else {
|
||||||
|
fuel -= fuelCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remain = fuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double refill(List<ConnectibleGraph<Vendor>.BuildEdge> edges, int startIndex){
|
||||||
|
double max = -1;
|
||||||
|
for (int i = startIndex; i >= 0; i--) {
|
||||||
|
ConnectibleGraph<Vendor>.BuildEdge e = edges.get(edges.size()-1-i);
|
||||||
|
if (max != -1){
|
||||||
|
max += e.getFuelCost(max + e.getMinFuel());
|
||||||
|
}
|
||||||
|
Vendor source = e.getSource().getEntry();
|
||||||
|
if (source.canRefill()){
|
||||||
|
ConnectibleEdge<Vendor> ce = entries.get(i);
|
||||||
|
if (ce.isRefill()){
|
||||||
|
throw new IllegalStateException("Is not exists path");
|
||||||
|
}
|
||||||
|
double remain = max != -1 ? Math.min(max, e.getRefill()) : e.getRefill();
|
||||||
|
double fuelCost = e.getFuelCost(remain);
|
||||||
|
this.fuelCost += fuelCost - ce.getFuelCost();
|
||||||
|
ce.setFuelCost(fuelCost);
|
||||||
|
ce.setRefill(remain);
|
||||||
|
remain = updateFuelCost(edges, i+1, startIndex, remain-fuelCost);
|
||||||
|
if (remain < 0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return remain;
|
||||||
|
}
|
||||||
|
if (max == -1 || e.getMaxFuel() < max){
|
||||||
|
max = e.getMaxFuel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Is not exists path");
|
||||||
|
}
|
||||||
|
|
||||||
|
private double updateFuelCost(List<ConnectibleGraph<Vendor>.BuildEdge> edges, int startIndex, int endIndex, double fuel){
|
||||||
|
for (int i = startIndex+1; i <= endIndex; i++) {
|
||||||
|
ConnectibleGraph<Vendor>.BuildEdge e = edges.get(edges.size()-1-i);
|
||||||
|
ConnectibleEdge<Vendor> ce = entries.get(i);
|
||||||
|
double fuelCost = e.getFuelCost(fuel);
|
||||||
|
this.fuelCost = fuelCost - ce.getFuelCost();
|
||||||
|
ce.setFuelCost(fuelCost);
|
||||||
|
fuel -= fuelCost;
|
||||||
|
if (fuel < 0 || fuel < e.getMinFuel()){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ConnectibleEdge<Vendor>> getEntries() {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getFuelCost() {
|
||||||
|
return fuelCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getRemain() {
|
||||||
|
return remain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRefillCount() {
|
||||||
|
return refillCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRefill() {
|
||||||
|
return entries.get(0).isRefill();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return entries.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
32
core/src/main/java/ru/trader/analysis/VendorVertex.java
Normal file
32
core/src/main/java/ru/trader/analysis/VendorVertex.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package ru.trader.analysis;
|
||||||
|
|
||||||
|
import ru.trader.analysis.graph.Edge;
|
||||||
|
import ru.trader.analysis.graph.Path;
|
||||||
|
import ru.trader.analysis.graph.Vertex;
|
||||||
|
import ru.trader.core.Vendor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class VendorVertex extends Vertex<Vendor> {
|
||||||
|
|
||||||
|
public VendorVertex(Vendor entry, int index) {
|
||||||
|
super(entry, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void connect(Edge<Vendor> edge) {
|
||||||
|
VendorsGraph.VendorsBuildEdge vEdge = (VendorsGraph.VendorsBuildEdge) edge;
|
||||||
|
Optional<Edge<Vendor>> old = getEdge(edge.getTarget());
|
||||||
|
if (old.isPresent()){
|
||||||
|
VendorsGraph.VendorsBuildEdge oEdge = (VendorsGraph.VendorsBuildEdge) old.get();
|
||||||
|
if (oEdge.getPaths() == null) return;
|
||||||
|
Collection<Path<Vendor>> paths = vEdge.getPaths();
|
||||||
|
for (Path<Vendor> path : paths) {
|
||||||
|
oEdge.add(path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.connect(edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,21 +3,21 @@ package ru.trader.analysis;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import ru.trader.analysis.graph.*;
|
import ru.trader.analysis.graph.*;
|
||||||
import ru.trader.core.Order;
|
import ru.trader.core.*;
|
||||||
import ru.trader.core.TransitVendor;
|
|
||||||
import ru.trader.core.Vendor;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
import java.util.concurrent.ForkJoinTask;
|
||||||
import java.util.List;
|
import java.util.concurrent.RecursiveAction;
|
||||||
import java.util.Optional;
|
import java.util.function.Function;
|
||||||
import java.util.function.Consumer;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||||
private final static Logger LOG = LoggerFactory.getLogger(VendorsGraph.class);
|
private final static Logger LOG = LoggerFactory.getLogger(VendorsGraph.class);
|
||||||
|
private final static int THRESHOLD = 8;
|
||||||
|
|
||||||
private final Scorer scorer;
|
private final Scorer scorer;
|
||||||
|
private final List<VendorsGraphBuilder> deferredTasks = new ArrayList<>();
|
||||||
|
|
||||||
public VendorsGraph(Scorer scorer) {
|
public VendorsGraph(Scorer scorer) {
|
||||||
super(scorer.getProfile());
|
super(scorer.getProfile());
|
||||||
@@ -29,18 +29,118 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
|||||||
this.scorer = scorer;
|
this.scorer = scorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VendorsCrawler crawler(Consumer<List<Edge<Vendor>>> onFoundFunc){
|
public VendorsCrawler crawler(Function<List<Edge<Vendor>>, Boolean> onFoundFunc){
|
||||||
return new VendorsCrawler(onFoundFunc);
|
return new VendorsCrawler(onFoundFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Vertex<Vendor> newInstance(Vendor entry, int index) {
|
||||||
|
return new VendorVertex(entry, index);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected GraphBuilder createGraphBuilder(Vertex<Vendor> vertex, Collection<Vendor> set, int deep, double limit) {
|
protected GraphBuilder createGraphBuilder(Vertex<Vendor> vertex, Collection<Vendor> set, int deep, double limit) {
|
||||||
return new VendorsGraphBuilder(vertex, set, deep, limit);
|
return new VendorsGraphBuilder(vertex, set, deep, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class VendorsGraphBuilder extends ConnectibleGraphBuilder {
|
@Override
|
||||||
|
protected void onEnd() {
|
||||||
|
super.onEnd();
|
||||||
|
runDeferredTasks();
|
||||||
|
updateVertexes();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void holdTask(VendorsGraphBuilder task){
|
||||||
|
synchronized (deferredTasks){
|
||||||
|
deferredTasks.add(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runDeferredTasks(){
|
||||||
|
deferredTasks.sort((b1,b2) -> Integer.compare(b2.getDeep(), b1.getDeep()));
|
||||||
|
for (VendorsGraphBuilder task : deferredTasks) {
|
||||||
|
ForkJoinTask.invokeAll(task);
|
||||||
|
}
|
||||||
|
deferredTasks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateVertexes(){
|
||||||
|
vertexes.removeIf(v -> v.getEntry() instanceof TransitVendor);
|
||||||
|
updateLevels(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLevels(Vertex<Vendor> vertex){
|
||||||
|
vertex.getEdges().forEach(e -> {
|
||||||
|
Vertex<Vendor> target = e.getTarget();
|
||||||
|
if (target.getLevel()+1 <vertex.getLevel()){
|
||||||
|
target.setLevel(vertex.getLevel()-1);
|
||||||
|
updateLevels(target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VendorsGraphBuilder extends ConnectibleGraphBuilder {
|
||||||
|
private final ArrayList<RecursiveAction> subTasks = new ArrayList<>(THRESHOLD);
|
||||||
|
private final VendorsGraphBuilder head;
|
||||||
|
private final BuildEdge edge;
|
||||||
|
private boolean isAdding;
|
||||||
|
|
||||||
protected VendorsGraphBuilder(Vertex<Vendor> vertex, Collection<Vendor> set, int deep, double limit) {
|
protected VendorsGraphBuilder(Vertex<Vendor> vertex, Collection<Vendor> set, int deep, double limit) {
|
||||||
super(vertex, set, deep, limit);
|
super(vertex, set, deep, limit);
|
||||||
|
this.head = null;
|
||||||
|
this.edge = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private VendorsGraphBuilder(VendorsGraphBuilder head, BuildEdge edge, Collection<Vendor> set, int deep, double limit) {
|
||||||
|
super(edge.getTarget(), set, deep, limit);
|
||||||
|
this.head = head;
|
||||||
|
this.edge = edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeep(){
|
||||||
|
return deep;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void compute() {
|
||||||
|
LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep);
|
||||||
|
if (isAdding){
|
||||||
|
addAlreadyCheckedEdges();
|
||||||
|
} else {
|
||||||
|
checkVertex();
|
||||||
|
}
|
||||||
|
if (!subTasks.isEmpty()){
|
||||||
|
joinSubTasks();
|
||||||
|
}
|
||||||
|
LOG.trace("End build graph from {} on deep {}", vertex, deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkVertex(){
|
||||||
|
Iterator<Vendor> iterator = set.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
if (callback.isCancel()) break;
|
||||||
|
Vendor entry = iterator.next();
|
||||||
|
LOG.trace("Check {}", entry);
|
||||||
|
if (entry == vertex.getEntry()) continue;
|
||||||
|
double nextLimit = onConnect(entry);
|
||||||
|
if (nextLimit >= 0) {
|
||||||
|
LOG.trace("Connect {} to {}", entry, vertex);
|
||||||
|
Vertex<Vendor> next = getInstance(entry, 0, deep);
|
||||||
|
BuildEdge e;
|
||||||
|
if (entry instanceof TransitVendor){
|
||||||
|
e = super.createEdge(next);
|
||||||
|
} else {
|
||||||
|
e = createEdge(next);
|
||||||
|
vertex.connect(e);
|
||||||
|
}
|
||||||
|
addSubTask(e, nextLimit);
|
||||||
|
} else {
|
||||||
|
LOG.trace("Vertex {} is far away", entry);
|
||||||
|
}
|
||||||
|
if (subTasks.size() == THRESHOLD || !iterator.hasNext()){
|
||||||
|
joinSubTasks();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -48,23 +148,241 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
|||||||
double nextlimit = super.onConnect(buyer);
|
double nextlimit = super.onConnect(buyer);
|
||||||
Vendor seller = vertex.getEntry();
|
Vendor seller = vertex.getEntry();
|
||||||
if (nextlimit > 0){
|
if (nextlimit > 0){
|
||||||
if (buyer instanceof TransitVendor && seller.getPlace().equals(buyer.getPlace())) nextlimit = -1;
|
if (buyer instanceof TransitVendor && (deep == 0 || seller.getPlace().equals(buyer.getPlace()))){
|
||||||
if (seller instanceof TransitVendor && seller.getPlace().equals(buyer.getPlace())) nextlimit = -1;
|
LOG.trace("Buyer is transit of seller or is end, skipping");
|
||||||
|
nextlimit = -1;
|
||||||
|
}
|
||||||
|
if (seller instanceof TransitVendor && seller.getPlace().equals(buyer.getPlace())){
|
||||||
|
LOG.trace("Seller is transit of buyer, skipping");
|
||||||
|
nextlimit = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nextlimit;
|
return nextlimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ConnectibleEdge<Vendor> createEdge(Vertex<Vendor> target) {
|
protected VendorsBuildEdge createEdge(Vertex<Vendor> target) {
|
||||||
return new VendorsEdge(vertex, target, refill, fuelCost);
|
BuildEdge cEdge = super.createEdge(target);
|
||||||
|
if (vertex.getEntry() instanceof TransitVendor){
|
||||||
|
addEdgesToHead(cEdge);
|
||||||
|
}
|
||||||
|
return new VendorsBuildEdge(cEdge);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEdgesToHead(BuildEdge lastEdge){
|
||||||
|
Vertex<Vendor> target = lastEdge.getTarget();
|
||||||
|
assert vertex.getEntry() instanceof TransitVendor && !(target.getEntry() instanceof TransitVendor);
|
||||||
|
VendorsGraphBuilder h = this;
|
||||||
|
Path<Vendor> path = new Path<>(Collections.singleton(lastEdge));
|
||||||
|
while (h != null){
|
||||||
|
BuildEdge cEdge = h.edge;
|
||||||
|
Vertex<Vendor> source = cEdge.getSource();
|
||||||
|
if (source.equals(vertex)){
|
||||||
|
LOG.trace("Found loop, break");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
path = path.add(cEdge);
|
||||||
|
if (!source.equals(target)){
|
||||||
|
addEdge(source, target, path);
|
||||||
|
}
|
||||||
|
if (!(source.getEntry() instanceof TransitVendor)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
h = h.head;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEdge(Vertex<Vendor> source, Vertex<Vendor> target, Path<Vendor> path){
|
||||||
|
LOG.trace("Is not transit, add edge to {}", source);
|
||||||
|
VendorsBuildEdge vEdge = new VendorsBuildEdge(source, target, path);
|
||||||
|
source.connect(vEdge);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAlreadyCheckedEdges(){
|
||||||
|
LOG.trace("Adding already checked vertex");
|
||||||
|
vertex.getEdges().parallelStream().forEach(aEdge -> {
|
||||||
|
VendorsBuildEdge e = (VendorsBuildEdge) aEdge;
|
||||||
|
if (callback.isCancel()) return;
|
||||||
|
Vendor entry = e.getTarget().getEntry();
|
||||||
|
LOG.trace("Check {}", entry);
|
||||||
|
if (limit >= e.getMinFuel() && limit <= e.getMaxFuel()) {
|
||||||
|
LOG.trace("Connect {} to {}", entry, vertex);
|
||||||
|
if (vertex.getEntry() instanceof TransitVendor && !(entry instanceof TransitVendor)) {
|
||||||
|
addCheckedEdgesToHead(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCheckedEdgesToHead(VendorsBuildEdge lastEdge){
|
||||||
|
Vertex<Vendor> target = lastEdge.getTarget();
|
||||||
|
assert vertex.getEntry() instanceof TransitVendor && !(target.getEntry() instanceof TransitVendor);
|
||||||
|
List<Path<Vendor>> paths = lastEdge.paths;
|
||||||
|
int i = 1;
|
||||||
|
Path<Vendor> path = paths != null ? paths.get(0) : new Path<>(Collections.singleton((BuildEdge)lastEdge));
|
||||||
|
while (path != null){
|
||||||
|
VendorsGraphBuilder h = this;
|
||||||
|
while (h != null){
|
||||||
|
if (h.limit >= path.getMinFuel() && h.limit <= path.getMaxFuel()){
|
||||||
|
BuildEdge cEdge = h.edge;
|
||||||
|
Vertex<Vendor> source = cEdge.getSource();
|
||||||
|
if (source.equals(vertex)){
|
||||||
|
LOG.trace("Found loop, break");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
path = path.add(cEdge);
|
||||||
|
if (!source.equals(target)){
|
||||||
|
addEdge(source, target, path);
|
||||||
|
}
|
||||||
|
if (!(source.getEntry() instanceof TransitVendor)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
h = h.head;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paths == null || i >= paths.size()){
|
||||||
|
path = null;
|
||||||
|
} else {
|
||||||
|
path = paths.get(i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSubTask(BuildEdge e, double nextLimit){
|
||||||
|
Vertex<Vendor> next = e.getTarget();
|
||||||
|
// If level > deep when vertex already added on upper deep
|
||||||
|
if (next.getLevel() < deep || next.getEntry() instanceof TransitVendor) {
|
||||||
|
boolean adding = next.getLevel() >= deep;
|
||||||
|
if (!adding){
|
||||||
|
next.setLevel(vertex.getLevel() - 1);
|
||||||
|
}
|
||||||
|
if (deep > 0 || adding) {
|
||||||
|
//Recursive build
|
||||||
|
VendorsGraphBuilder task = new VendorsGraphBuilder(this, e, set, deep - 1, nextLimit);
|
||||||
|
task.isAdding = adding;
|
||||||
|
if (adding){
|
||||||
|
holdTask(task);
|
||||||
|
} else {
|
||||||
|
task.fork();
|
||||||
|
subTasks.add(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.trace("Vertex {} already check", next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void joinSubTasks(){
|
||||||
|
for (RecursiveAction subTask : subTasks) {
|
||||||
|
if (callback.isCancel()){
|
||||||
|
subTask.cancel(true);
|
||||||
|
} else {
|
||||||
|
subTask.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subTasks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VendorsBuildEdge extends BuildEdge {
|
||||||
|
private List<Path<Vendor>> paths = new ArrayList<>();
|
||||||
|
private List<Order> orders;
|
||||||
|
|
||||||
|
protected VendorsBuildEdge(Vertex<Vendor> source, Vertex<Vendor> target, Path<Vendor> path) {
|
||||||
|
super(source, target);
|
||||||
|
if (path == null) throw new IllegalArgumentException("Path must be no-null");
|
||||||
|
paths.add(path);
|
||||||
|
update(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected VendorsBuildEdge(BuildEdge edge) {
|
||||||
|
super(edge.getSource(), edge.getTarget());
|
||||||
|
Path<Vendor> path = new Path<>(Collections.singleton(edge));
|
||||||
|
paths.add(path);
|
||||||
|
update(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(Path<Vendor> path){
|
||||||
|
setFuel(path.getMinFuel(), path.getMaxFuel());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setOrders(List<Order> orders){
|
||||||
|
this.orders = orders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Order> getOrders(){
|
||||||
|
if (orders == null){
|
||||||
|
Vendor seller = source.getEntry();
|
||||||
|
Vendor buyer = target.getEntry();
|
||||||
|
orders = MarketUtils.getOrders(seller, buyer);
|
||||||
|
}
|
||||||
|
return orders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getProfit(){
|
||||||
|
return getOrders().stream().mapToDouble(Order::getProfit).sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Path<Vendor>> getPaths() {
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path<Vendor> getPath(double fuel){
|
||||||
|
Path<Vendor> res = null;
|
||||||
|
for (Path<Vendor> p : paths) {
|
||||||
|
if (fuel >= p.getMinFuel() && fuel <= p.getMaxFuel() || getSource().getEntry().canRefill()) {
|
||||||
|
if (getProfile().getPathPriority().equals(Profile.PATH_PRIORITY.FAST)) {
|
||||||
|
if (res == null || p.getSize() < res.getSize() && p.getRefillCount(fuel) <= res.getRefillCount(fuel)) {
|
||||||
|
res = p;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (res == null || p.getFuelCost() < res.getFuelCost()) {
|
||||||
|
res = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Path<Vendor> path) {
|
||||||
|
for (Iterator<Path<Vendor>> iterator = paths.iterator(); iterator.hasNext(); ) {
|
||||||
|
Path<Vendor> p = iterator.next();
|
||||||
|
if (p.getSize() >= path.getSize() && p.getMinFuel() >= path.getMinFuel() && p.getRefillCount() >= path.getRefillCount()) {
|
||||||
|
iterator.remove();
|
||||||
|
} else {
|
||||||
|
if (path.getSize() >= p.getSize() && path.getMinFuel() >= p.getMinFuel() && path.getRefillCount() >= p.getRefillCount()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paths.add(path);
|
||||||
|
if (getMinFuel() > path.getMinFuel()) {
|
||||||
|
update(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public VendorsEdge getInstance(double fuel, double balance){
|
||||||
|
Path<Vendor> 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<Vendor> {
|
public class VendorsEdge extends ConnectibleEdge<Vendor> {
|
||||||
|
private TransitPath path;
|
||||||
private List<Order> orders;
|
private List<Order> orders;
|
||||||
|
|
||||||
protected VendorsEdge(Vertex<Vendor> source, Vertex<Vendor> target, boolean refill, double fuel) {
|
protected VendorsEdge(Vertex<Vendor> source, Vertex<Vendor> target, TransitPath path) {
|
||||||
super(source, target, refill, fuel);
|
super(source, target);
|
||||||
|
if (path == null) throw new IllegalArgumentException("Path must be no-null");
|
||||||
|
this.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setOrders(List<Order> orders){
|
protected void setOrders(List<Order> orders){
|
||||||
@@ -84,100 +402,118 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
|||||||
return orders;
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getRemain() {
|
||||||
|
return path.getRemain();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRefill() {
|
||||||
|
return path.isRefill();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransitPath getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected double computeWeight() {
|
protected double computeWeight() {
|
||||||
int jumps = source.getEntry().getPlace().equals(target.getEntry().getPlace())? 0 : 1;
|
int jumps = source.getEntry().getPlace().equals(target.getEntry().getPlace())? 0 : 1;
|
||||||
int lands = !(target.getEntry() instanceof TransitVendor) ? 1 : 0;
|
int lands = 1; double fuel = fuelCost;
|
||||||
boolean transit = lands == 0 && source.getEntry() instanceof TransitVendor || target.getEntry() instanceof TransitVendor;
|
if (path != null){
|
||||||
|
jumps = path.size(); fuel = getFuelCost();
|
||||||
|
lands += path.getRefillCount();
|
||||||
|
}
|
||||||
double profit = getProfit();
|
double profit = getProfit();
|
||||||
double score = transit ? scorer.getTransitScore(fuel) :
|
double score = scorer.getScore(target.getEntry(), profit, jumps, lands, fuel);
|
||||||
scorer.getScore(target.getEntry(), profit, jumps, lands, fuel);
|
|
||||||
score = scorer.getMaxScore() - score;
|
score = scorer.getMaxScore() - score;
|
||||||
if (score < 0)
|
if (score < 0) score = 0;
|
||||||
score = 0;
|
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VendorsCrawler extends CCrawler<Vendor> {
|
@Override
|
||||||
protected VendorsCrawler(Consumer<List<Edge<Vendor>>> onFoundFunc) {
|
public int hashCode() {
|
||||||
|
int result = super.hashCode();
|
||||||
|
result = 31 * result + (path != null ? path.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VendorsCrawler extends Crawler<Vendor> {
|
||||||
|
private double startFuel;
|
||||||
|
private double startBalance;
|
||||||
|
|
||||||
|
protected VendorsCrawler(Function<List<Edge<Vendor>>, Boolean> onFoundFunc) {
|
||||||
super(VendorsGraph.this, onFoundFunc);
|
super(VendorsGraph.this, onFoundFunc);
|
||||||
|
startFuel = getShip().getTank();
|
||||||
|
startBalance = getProfile().getBalance();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected VendorsCrawler(Function<Edge<Vendor>, Boolean> isFoundFunc, Function<List<Edge<Vendor>>, Boolean> onFoundFunc) {
|
||||||
|
super(VendorsGraph.this, isFoundFunc, onFoundFunc);
|
||||||
|
startFuel = getShip().getTank();
|
||||||
|
startBalance = getProfile().getBalance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartFuel(double startFuel) {
|
||||||
|
this.startFuel = startFuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartBalance(double startBalance) {
|
||||||
|
this.startBalance = startBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected VendorsTraversalEntry start(Vertex<Vendor> vertex) {
|
protected VendorsTraversalEntry start(Vertex<Vendor> vertex) {
|
||||||
double balance = getProfile().getBalance();
|
return new VendorsTraversalEntry(super.start(vertex), startFuel, startBalance);
|
||||||
return new VendorsTraversalEntry(super.start(vertex), balance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected VendorsTraversalEntry travers(final CostTraversalEntry entry, final Edge<Vendor> edge, final Vendor target) {
|
protected VendorsTraversalEntry travers(final CostTraversalEntry entry, final Edge<Vendor> edge) {
|
||||||
|
VendorsTraversalEntry vEntry = (VendorsTraversalEntry)entry;
|
||||||
VendorsEdge vEdge = (VendorsEdge) edge;
|
VendorsEdge vEdge = (VendorsEdge) edge;
|
||||||
CCostTraversalEntry ce = super.travers(entry, edge, target);
|
return new VendorsTraversalEntry(vEntry, vEdge);
|
||||||
return new VendorsTraversalEntry((VendorsTraversalEntry) entry, edge, ce.getFuel(), ((VendorsTraversalEntry)entry).balance + vEdge.getProfit());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class VendorsTraversalEntry extends CCostTraversalEntry {
|
protected class VendorsTraversalEntry extends CostTraversalEntry {
|
||||||
|
private final double fuel;
|
||||||
private final double balance;
|
private final double balance;
|
||||||
|
|
||||||
protected VendorsTraversalEntry(CCostTraversalEntry entry, double balance) {
|
protected VendorsTraversalEntry(CostTraversalEntry entry, double fuel, double balance) {
|
||||||
super(entry.getTarget(), entry.getFuel());
|
super(entry.getTarget());
|
||||||
|
this.fuel = fuel;
|
||||||
this.balance = balance;
|
this.balance = balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected VendorsTraversalEntry(VendorsTraversalEntry head, Edge<Vendor> edge, double fuel, double balance) {
|
protected VendorsTraversalEntry(VendorsTraversalEntry head, VendorsEdge edge) {
|
||||||
super(head, edge, fuel);
|
super(head, edge);
|
||||||
this.balance = balance;
|
this.balance = head.balance + edge.getProfit();
|
||||||
|
this.fuel = edge.getRemain();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
public List<Edge<Vendor>> collect(Collection<Edge<Vendor>> src) {
|
||||||
|
return src.stream().filter(this::check).map(this::wrap).filter(e -> e != null).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean check(Edge<Vendor> e){
|
protected boolean check(Edge<Vendor> e){
|
||||||
boolean good = super.check(e);
|
VendorsBuildEdge edge = (VendorsBuildEdge) e;
|
||||||
// remove transit cicles
|
return fuel <= edge.getMaxFuel() && (fuel >= edge.getMinFuel() || edge.getSource().getEntry().canRefill()) && (edge.getProfit() > 0 || isFound(edge));
|
||||||
if (good && e.getSource().getEntry() instanceof TransitVendor && !(e.getTarget().getEntry() instanceof TransitVendor)){
|
|
||||||
Optional<Vendor> seller = getSeller();
|
|
||||||
good = seller.isPresent() && !e.getTarget().isEntry(seller.get());
|
|
||||||
}
|
|
||||||
return good;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected VendorsEdge wrap(Edge<Vendor> e) {
|
||||||
protected VendorsEdge wrap(Edge<Vendor> edge) {
|
VendorsBuildEdge edge = (VendorsBuildEdge) e;
|
||||||
ConnectibleEdge<Vendor> cEdge = super.wrap(edge);
|
return edge.getInstance(fuel, balance);
|
||||||
Vendor buyer = edge.getTarget().getEntry();
|
|
||||||
List<Order> orders = new ArrayList<>();
|
|
||||||
orders.addAll(((VendorsEdge) edge).getOrders());
|
|
||||||
if (edge.getSource().getEntry() instanceof TransitVendor && !(buyer instanceof TransitVendor)){
|
|
||||||
LOG.trace("{} is transit, search seller", edge.getSource().getEntry());
|
|
||||||
Optional<Vendor> seller = getSeller();
|
|
||||||
if (seller.isPresent()){
|
|
||||||
orders = MarketUtils.getOrders(seller.get(), buyer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
orders = MarketUtils.getStack(orders, balance, getShip().getCargo());
|
|
||||||
VendorsEdge res = new VendorsEdge(edge.getSource(), edge.getTarget(), cEdge.isRefill(), cEdge.getFuel());
|
|
||||||
res.setOrders(orders);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Vendor> getSeller(){
|
|
||||||
Vendor res = null;
|
|
||||||
Edge<Vendor> e = getEdge();
|
|
||||||
Vendor seller = e.getSource().getEntry();
|
|
||||||
if (!(seller instanceof TransitVendor)){
|
|
||||||
res = seller;
|
|
||||||
} else {
|
|
||||||
for (int i = head.size() - 1; i >= 0; i--) {
|
|
||||||
e = head.getEdge();
|
|
||||||
seller = e.getSource().getEntry();
|
|
||||||
if (!(seller instanceof TransitVendor)){
|
|
||||||
res = seller;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Optional.ofNullable(res);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ public abstract class AbstractGraph<T> implements Graph<T> {
|
|||||||
protected Vertex<T> root;
|
protected Vertex<T> root;
|
||||||
protected final List<Vertex<T>> vertexes;
|
protected final List<Vertex<T>> vertexes;
|
||||||
protected final GraphCallBack callback;
|
protected final GraphCallBack callback;
|
||||||
|
|
||||||
protected int minJumps;
|
protected int minJumps;
|
||||||
|
|
||||||
protected AbstractGraph() {
|
protected AbstractGraph() {
|
||||||
@@ -37,17 +36,26 @@ public abstract class AbstractGraph<T> implements Graph<T> {
|
|||||||
minJumps = 1;
|
minJumps = 1;
|
||||||
root = getInstance(start, maxDeep, maxDeep);
|
root = getInstance(start, maxDeep, maxDeep);
|
||||||
POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit));
|
POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit));
|
||||||
|
onEnd();
|
||||||
callback.endBuild();
|
callback.endBuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vertex<T> getInstance(T entry, int level, int deep){
|
protected void onEnd(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Vertex<T> newInstance(T entry, int index){
|
||||||
|
return new Vertex<>(entry, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Vertex<T> getInstance(T entry, int level, int deep){
|
||||||
Vertex<T> vertex = getVertex(entry).orElse(null);
|
Vertex<T> vertex = getVertex(entry).orElse(null);
|
||||||
if (vertex == null) {
|
if (vertex == null) {
|
||||||
synchronized (vertexes){
|
synchronized (vertexes){
|
||||||
vertex = getVertex(entry).orElse(null);
|
vertex = getVertex(entry).orElse(null);
|
||||||
if (vertex == null){
|
if (vertex == null){
|
||||||
LOG.trace("Is new vertex");
|
LOG.trace("Is new vertex");
|
||||||
vertex = new Vertex<>(entry, vertexes.size());
|
vertex = newInstance(entry, vertexes.size());
|
||||||
vertex.setLevel(level);
|
vertex.setLevel(level);
|
||||||
vertexes.add(vertex);
|
vertexes.add(vertex);
|
||||||
int jumps = root != null ? root.getLevel() - deep : 0;
|
int jumps = root != null ? root.getLevel() - deep : 0;
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ public class CCrawler<T extends Connectable<T>> extends Crawler<T> {
|
|||||||
private final static Logger LOG = LoggerFactory.getLogger(CCrawler.class);
|
private final static Logger LOG = LoggerFactory.getLogger(CCrawler.class);
|
||||||
private double startFuel;
|
private double startFuel;
|
||||||
|
|
||||||
public CCrawler(Graph<T> graph, Function<List<Edge<T>>, Boolean> onFoundFunc) {
|
public CCrawler(ConnectibleGraph<T> graph, Function<List<Edge<T>>, Boolean> onFoundFunc) {
|
||||||
super(graph, onFoundFunc);
|
super(graph, onFoundFunc);
|
||||||
startFuel = getShip().getTank();
|
startFuel = getShip().getTank();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CCrawler(Graph<T> graph, Function<Edge<T>, Boolean> isFoundFunc, Function<List<Edge<T>>, Boolean> onFoundFunc) {
|
public CCrawler(ConnectibleGraph<T> graph, Function<Edge<T>, Boolean> isFoundFunc, Function<List<Edge<T>>, Boolean> onFoundFunc) {
|
||||||
super(graph, isFoundFunc, onFoundFunc);
|
super(graph, isFoundFunc, onFoundFunc);
|
||||||
startFuel = getShip().getTank();
|
startFuel = getShip().getTank();
|
||||||
}
|
}
|
||||||
@@ -78,22 +78,23 @@ public class CCrawler<T extends Connectable<T>> extends Crawler<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Edge<T>> collect(Collection<Edge<T>> src) {
|
public List<Edge<T>> collect(Collection<Edge<T>> src) {
|
||||||
return src.stream().filter(this::check).map(this::wrap).collect(Collectors.toList());
|
return src.stream().filter(this::check).map(this::wrap).filter(e -> e != null).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean check(Edge<T> e){
|
protected boolean check(Edge<T> e){
|
||||||
ConnectibleEdge<T> edge = (ConnectibleEdge<T>) e;
|
ConnectibleGraph<T>.BuildEdge edge = (ConnectibleGraph<T>.BuildEdge) e;
|
||||||
return edge.getFuelCost() <= fuel || edge.getSource().getEntry().canRefill();
|
return fuel <= edge.getMaxFuel() && (fuel >= edge.getMinFuel() || edge.getSource().getEntry().canRefill());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ConnectibleEdge<T> wrap(Edge<T> edge){
|
protected ConnectibleEdge<T> wrap(Edge<T> e){
|
||||||
|
ConnectibleGraph<T>.BuildEdge edge = (ConnectibleGraph<T>.BuildEdge) e;
|
||||||
T source = edge.source.getEntry();
|
T source = edge.source.getEntry();
|
||||||
double distance = source.getDistance(edge.target.getEntry());
|
double fuelCost = edge.getFuelCost(fuel);
|
||||||
double fuelCost = getShip().getFuelCost(fuel, distance);
|
double nextLimit = fuel - fuelCost;
|
||||||
double nextLimit = getProfile().withRefill() ? fuel - fuelCost : getShip().getTank();
|
if (nextLimit < 0 && !source.canRefill()) return null;
|
||||||
double refill = nextLimit < 0 && source.canRefill() ? getShip().getRoundMaxFuel(distance) : 0;
|
double refill = nextLimit < 0 ? edge.getRefill() : 0;
|
||||||
if (refill > 0) {
|
if (refill > 0) {
|
||||||
fuelCost = getShip().getFuelCost(refill, distance);
|
fuelCost = edge.getFuelCost(refill);
|
||||||
}
|
}
|
||||||
ConnectibleEdge<T> cEdge = new ConnectibleEdge<>(edge.getSource(), edge.getTarget());
|
ConnectibleEdge<T> cEdge = new ConnectibleEdge<>(edge.getSource(), edge.getTarget());
|
||||||
cEdge.setRefill(refill);
|
cEdge.setRefill(refill);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import ru.trader.core.Ship;
|
|||||||
import ru.trader.graph.Connectable;
|
import ru.trader.graph.Connectable;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T> {
|
public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T> {
|
||||||
private final static Logger LOG = LoggerFactory.getLogger(ConnectibleGraph.class);
|
private final static Logger LOG = LoggerFactory.getLogger(ConnectibleGraph.class);
|
||||||
@@ -42,58 +41,95 @@ public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T>
|
|||||||
super.build(start, set, profile.getJumps(), getShip().getTank());
|
super.build(start, set, profile.getJumps(), getShip().getTank());
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DistanceFilter implements Predicate<Double> {
|
|
||||||
private final double limit;
|
|
||||||
private final T source;
|
|
||||||
|
|
||||||
private DistanceFilter(double limit, T source) {
|
|
||||||
this.limit = limit;
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(Double distance) {
|
|
||||||
return distance <= getShip().getJumpRange(limit) || (profile.withRefill() && distance <= getShip().getMaxJumpRange() && source.canRefill());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class ConnectibleGraphBuilder extends GraphBuilder {
|
protected class ConnectibleGraphBuilder extends GraphBuilder {
|
||||||
private final DistanceFilter distanceFilter;
|
protected double minFuel;
|
||||||
protected double refill;
|
protected double maxFuel;
|
||||||
protected double fuelCost;
|
protected double distance;
|
||||||
|
|
||||||
protected ConnectibleGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
protected ConnectibleGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||||
super(vertex, set, deep, limit);
|
super(vertex, set, deep, limit);
|
||||||
distanceFilter = new DistanceFilter(limit, vertex.getEntry());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected double onConnect(T entry) {
|
protected double onConnect(T entry) {
|
||||||
double distance = vertex.getEntry().getDistance(entry);
|
distance = vertex.getEntry().getDistance(entry);
|
||||||
if (!distanceFilter.test(distance)){
|
if (distance > getShip().getMaxJumpRange()){
|
||||||
LOG.trace("Vertex {} is far away, {}", entry, distance);
|
LOG.trace("Vertex {} is far away, {}", entry, distance);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
fuelCost = getShip().getFuelCost(limit, distance);
|
maxFuel = getShip().getMaxFuel(distance);
|
||||||
double nextLimit = profile.withRefill() ? limit - fuelCost : getShip().getTank();
|
minFuel = getShip().getMinFuel(distance);
|
||||||
if (nextLimit < 0 && vertex.getEntry().canRefill()) {
|
double fuel = getProfile().withRefill() ? vertex.getEntry().canRefill() ? getShip().getRoundMaxFuel(distance) : limit : getShip().getTank();
|
||||||
LOG.trace("Refill");
|
double fuelCost = getShip().getFuelCost(fuel, distance);
|
||||||
refill = getShip().getRoundMaxFuel(distance);
|
return fuel - fuelCost;
|
||||||
fuelCost = getShip().getFuelCost(refill, distance);
|
|
||||||
nextLimit = refill - fuelCost;
|
|
||||||
} else {
|
|
||||||
refill = 0;
|
|
||||||
}
|
|
||||||
return nextLimit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ConnectibleEdge<T> createEdge(Vertex<T> target) {
|
protected BuildEdge createEdge(Vertex<T> target) {
|
||||||
ConnectibleEdge<T> res = new ConnectibleEdge<>(vertex, target);
|
BuildEdge res = new BuildEdge(vertex, target);
|
||||||
res.setRefill(refill);
|
res.setFuel(minFuel, maxFuel);
|
||||||
res.setFuelCost(fuelCost);
|
res.setDistance(distance);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class BuildEdge extends Edge<T> {
|
||||||
|
private double distance;
|
||||||
|
private double minFuel;
|
||||||
|
private double maxFuel;
|
||||||
|
|
||||||
|
public BuildEdge(Vertex<T> source, Vertex<T> target) {
|
||||||
|
super(source, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMinFuel() {
|
||||||
|
return minFuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMaxFuel() {
|
||||||
|
return maxFuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getRoundMaxFuel() {
|
||||||
|
return getShip().getRoundFuel(maxFuel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFuel(double minFuel, double maxFuel) {
|
||||||
|
this.minFuel = minFuel;
|
||||||
|
this.maxFuel = maxFuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDistance(double distance) {
|
||||||
|
this.distance = distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDistance() {
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getFuelCost(double fuel){
|
||||||
|
return getProfile().withRefill() ? getShip().getFuelCost(fuel, distance) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getRefill(){
|
||||||
|
return getShip().getRoundMaxFuel(distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ship getShip(){
|
||||||
|
return ConnectibleGraph.this.getShip();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected double computeWeight() {
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return source.getEntry().toString() + " - "+ weight
|
||||||
|
+"("+minFuel + " - " + maxFuel + ")"
|
||||||
|
+" -> " + target.getEntry().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,112 @@
|
|||||||
package ru.trader.analysis.graph;
|
package ru.trader.analysis.graph;
|
||||||
|
|
||||||
|
import ru.trader.core.Ship;
|
||||||
import ru.trader.graph.Connectable;
|
import ru.trader.graph.Connectable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Path<T extends Connectable<T>> {
|
public class Path<T extends Connectable<T>> {
|
||||||
private final List<PathEntry<T>> entries;
|
private final List<ConnectibleGraph<T>.BuildEdge> entries;
|
||||||
|
private double minFuel;
|
||||||
|
private double maxFuel;
|
||||||
|
private double fuelCost;
|
||||||
|
private int refillCount;
|
||||||
|
|
||||||
public Path(List<ConnectibleEdge<T>> edges) {
|
|
||||||
entries = new ArrayList<>(edges.size());
|
public Path(Collection<ConnectibleGraph<T>.BuildEdge> edges) {
|
||||||
for (int i = 0; i < edges.size(); i++) {
|
entries = new ArrayList<>(edges);
|
||||||
ConnectibleEdge<T> edge = edges.get(i);
|
updateStat();
|
||||||
if (i==0) entries.add(new PathEntry<>(edge.getSource().getEntry(), false));
|
|
||||||
entries.add(new PathEntry<>(edge.getTarget().getEntry(), edge.isRefill()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PathEntry<T> get(int index){
|
private void updateStat(){
|
||||||
return entries.get(index);
|
Ship ship = entries.get(0).getShip();
|
||||||
|
double fuel = ship.getTank();
|
||||||
|
minFuel = 0; maxFuel = 0; fuelCost = 0; refillCount = 0;
|
||||||
|
boolean adding = true;
|
||||||
|
for (int i = entries.size() - 1; i >= 0; i--) {
|
||||||
|
ConnectibleGraph<T>.BuildEdge edge = entries.get(i);
|
||||||
|
if (i < entries.size() - 1 && edge.getSource().getEntry().canRefill()){
|
||||||
|
adding = false;
|
||||||
|
fuel = edge.getMaxFuel();
|
||||||
|
}
|
||||||
|
if (fuel < 0 || fuel < edge.getMinFuel()){
|
||||||
|
minFuel = ship.getTank()+1;
|
||||||
|
}
|
||||||
|
double cost = edge.getFuelCost(fuel);
|
||||||
|
fuelCost += cost;
|
||||||
|
fuel -= cost;
|
||||||
|
if (adding) {
|
||||||
|
minFuel += edge.getMinFuel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maxFuel = -1;
|
||||||
|
for (ConnectibleGraph<T>.BuildEdge edge : entries) {
|
||||||
|
if (maxFuel != -1){
|
||||||
|
maxFuel += edge.getFuelCost(maxFuel + edge.getMinFuel());
|
||||||
|
}
|
||||||
|
if (maxFuel == -1 || edge.getMaxFuel() < maxFuel){
|
||||||
|
maxFuel = edge.getMaxFuel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
refillCount = getRefillCount(ship.getTank());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ConnectibleGraph<T>.BuildEdge> getEdges() {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getFuelCost() {
|
||||||
|
return fuelCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMinFuel() {
|
||||||
|
return minFuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMaxFuel() {
|
||||||
|
return maxFuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize(){
|
||||||
|
return entries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRefillCount() {
|
||||||
|
return refillCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRefillCount(double fuel){
|
||||||
|
int res = 0;
|
||||||
|
for (int i = entries.size() - 1; i >= 0; i--) {
|
||||||
|
ConnectibleGraph<T>.BuildEdge edge = entries.get(i);
|
||||||
|
fuel -= edge.getFuelCost(fuel);
|
||||||
|
if (fuel < 0) {
|
||||||
|
res++;
|
||||||
|
fuel = edge.getMaxFuel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path<T> add(ConnectibleGraph<T>.BuildEdge edge){
|
||||||
|
Path<T> res = new Path<>(entries);
|
||||||
|
res.entries.add(edge);
|
||||||
|
res.updateStat();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder("{");
|
||||||
|
for (int i = entries.size() - 1; i >= 0; i--) {
|
||||||
|
ConnectibleGraph<T>.BuildEdge entry = entries.get(i);
|
||||||
|
sb.append(entry);
|
||||||
|
if (i>0)
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
package ru.trader.analysis.graph;
|
|
||||||
|
|
||||||
public class PathEntry<T> {
|
|
||||||
private final T entry;
|
|
||||||
private boolean refill;
|
|
||||||
|
|
||||||
public PathEntry(T entry, boolean refill) {
|
|
||||||
this.entry = entry;
|
|
||||||
this.refill = refill;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getEntry() {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRefill() {
|
|
||||||
return refill;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package ru.trader.core;
|
package ru.trader.core;
|
||||||
|
|
||||||
public class Profile {
|
public class Profile {
|
||||||
|
public static enum PATH_PRIORITY {FAST, ECO}
|
||||||
|
|
||||||
private double balance;
|
private double balance;
|
||||||
private int jumps;
|
private int jumps;
|
||||||
@@ -14,6 +15,7 @@ public class Profile {
|
|||||||
private double jumpMult;
|
private double jumpMult;
|
||||||
private double landMult;
|
private double landMult;
|
||||||
private double fuelPrice;
|
private double fuelPrice;
|
||||||
|
private PATH_PRIORITY pathPriority;
|
||||||
|
|
||||||
public Profile(Ship ship) {
|
public Profile(Ship ship) {
|
||||||
this.ship = ship;
|
this.ship = ship;
|
||||||
@@ -25,6 +27,7 @@ public class Profile {
|
|||||||
landMult = 0.95;
|
landMult = 0.95;
|
||||||
fuelPrice = 100;
|
fuelPrice = 100;
|
||||||
jumpMult = 0.5;
|
jumpMult = 0.5;
|
||||||
|
pathPriority = PATH_PRIORITY.ECO;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getBalance() {
|
public double getBalance() {
|
||||||
@@ -114,4 +117,12 @@ public class Profile {
|
|||||||
public void setFuelPrice(double fuelPrice) {
|
public void setFuelPrice(double fuelPrice) {
|
||||||
this.fuelPrice = fuelPrice;
|
this.fuelPrice = fuelPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PATH_PRIORITY getPathPriority() {
|
||||||
|
return pathPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPathPriority(PATH_PRIORITY pathPriority) {
|
||||||
|
this.pathPriority = pathPriority;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,11 +93,6 @@ public class TransitVendor implements Vendor {
|
|||||||
return d == 0 ? 0 : d > 0 ? 1 : -1;
|
return d == 0 ? 0 : d > 0 ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canRefill() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ public class RouteSearcherTest extends Assert{
|
|||||||
assertEquals(route.getDistance(), 72.42, 0.01);
|
assertEquals(route.getDistance(), 72.42, 0.01);
|
||||||
|
|
||||||
List<Route> apaths = searcher.getRoutes(ithaca_st, ithaca_st, fWorld.getMarkets(true).collect(Collectors.toList()));
|
List<Route> apaths = searcher.getRoutes(ithaca_st, ithaca_st, fWorld.getMarkets(true).collect(Collectors.toList()));
|
||||||
|
/* List<Route> apaths = searcher.getRoutes(ithaca_st, ithaca_st, Arrays.asList(ithaca_st, lhs3262_st,
|
||||||
|
morgor_st, lhs3006_st, ithaca.asTransit(), lhs3262.asTransit(),
|
||||||
|
morgor.asTransit(), lhs3006.asTransit()));
|
||||||
|
*/
|
||||||
Route actual = apaths.stream().findFirst().get();
|
Route actual = apaths.stream().findFirst().get();
|
||||||
assertEquals("Routes is different", route, actual);
|
assertEquals("Routes is different", route, actual);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.analysis.graph.ConnectibleGraph;
|
||||||
import ru.trader.analysis.graph.Crawler;
|
import ru.trader.analysis.graph.Crawler;
|
||||||
import ru.trader.analysis.graph.SimpleCollector;
|
import ru.trader.analysis.graph.SimpleCollector;
|
||||||
import ru.trader.analysis.graph.Vertex;
|
import ru.trader.analysis.graph.Vertex;
|
||||||
@@ -58,10 +59,10 @@ public class VendorsGraphTest extends Assert {
|
|||||||
LOG.info("Build vendors graph");
|
LOG.info("Build vendors graph");
|
||||||
VendorsGraph vGraph = new VendorsGraph(scorer);
|
VendorsGraph vGraph = new VendorsGraph(scorer);
|
||||||
vGraph.build(cabreraDock, fWorld.getMarkets(true).collect(Collectors.toList()));
|
vGraph.build(cabreraDock, fWorld.getMarkets(true).collect(Collectors.toList()));
|
||||||
|
LOG.info("Search");
|
||||||
SimpleCollector<Vendor> paths = new SimpleCollector<>();
|
SimpleCollector<Vendor> paths = new SimpleCollector<>();
|
||||||
Crawler<Vendor> crawler = vGraph.crawler(paths::add);
|
Crawler<Vendor> crawler = vGraph.crawler(paths::add);
|
||||||
|
// Cabrera Dock -> Transit Wolf 1323 -> Transit Wolf 1325 -> Quimper Ring -> Transit Bhadaba -> Transit Wolf 1325 -> Cabrera Dock]
|
||||||
crawler.findMin(cabreraDock, 100);
|
crawler.findMin(cabreraDock, 100);
|
||||||
assertEquals(100, paths.get().size());
|
assertEquals(100, paths.get().size());
|
||||||
paths.clear();
|
paths.clear();
|
||||||
|
|||||||
Reference in New Issue
Block a user