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.LoggerFactory;
|
||||
import ru.trader.analysis.graph.ConnectibleEdge;
|
||||
import ru.trader.analysis.graph.Crawler;
|
||||
import ru.trader.analysis.graph.Edge;
|
||||
import ru.trader.core.Order;
|
||||
import ru.trader.core.TransitVendor;
|
||||
import ru.trader.core.Vendor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class RouteSearcher {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(RouteSearcher.class);
|
||||
private final VendorsGraph vGraph;
|
||||
private final Scorer scorer;
|
||||
|
||||
public RouteSearcher(Scorer scorer) {
|
||||
vGraph = new VendorsGraph(scorer);
|
||||
this.scorer = scorer;
|
||||
}
|
||||
|
||||
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){
|
||||
LOG.trace("Start search route to {} from {}", source, target);
|
||||
VendorsGraph vGraph = new VendorsGraph(scorer);
|
||||
LOG.trace("Build vendors graph");
|
||||
vGraph.build(source, vendors);
|
||||
|
||||
LOG.trace("Graph is builds");
|
||||
RouteCollector collector = new RouteCollector();
|
||||
Crawler<Vendor> crawler = vGraph.crawler(collector::add);
|
||||
crawler.setMaxSize(scorer.getProfile().getLands());
|
||||
if (target == null){
|
||||
int count = vGraph.getProfile().getRoutesCount() / vendors.size();
|
||||
for (Vendor vendor : vendors) {
|
||||
@@ -49,10 +50,11 @@ public class RouteSearcher {
|
||||
private class RouteCollector {
|
||||
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.setBalance(vGraph.getProfile().getBalance());
|
||||
route.setBalance(scorer.getProfile().getBalance());
|
||||
routes.add(route);
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<Route> get() {
|
||||
@@ -62,34 +64,31 @@ public class RouteSearcher {
|
||||
private Route toRoute(List<Edge<Vendor>> edges){
|
||||
List<RouteEntry> entries = new ArrayList<>(edges.size()+1);
|
||||
Vendor buyer = null;
|
||||
VendorsGraph.VendorsEdge edge = null;
|
||||
for (int i = 0; i < edges.size(); i++) {
|
||||
edge = (VendorsGraph.VendorsEdge) edges.get(i);
|
||||
VendorsGraph.VendorsEdge vEdge = null;
|
||||
for (Edge<Vendor> e : edges) {
|
||||
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();
|
||||
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)) {
|
||||
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()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
entries.add(entry);
|
||||
}
|
||||
|
||||
@@ -76,10 +76,6 @@ public class Scorer {
|
||||
return avgDistance;
|
||||
}
|
||||
|
||||
public double getFuel(double distance){
|
||||
return profile.getShip().getFuelCost(distance);
|
||||
}
|
||||
|
||||
public double getScore(RouteEntry entry, int jumps) {
|
||||
int lands = entry.isLand() ? 1 : 0;
|
||||
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.LoggerFactory;
|
||||
import ru.trader.analysis.graph.*;
|
||||
import ru.trader.core.Order;
|
||||
import ru.trader.core.TransitVendor;
|
||||
import ru.trader.core.Vendor;
|
||||
import ru.trader.core.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ForkJoinTask;
|
||||
import java.util.concurrent.RecursiveAction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(VendorsGraph.class);
|
||||
private final static int THRESHOLD = 8;
|
||||
|
||||
private final Scorer scorer;
|
||||
private final List<VendorsGraphBuilder> deferredTasks = new ArrayList<>();
|
||||
|
||||
public VendorsGraph(Scorer scorer) {
|
||||
super(scorer.getProfile());
|
||||
@@ -29,18 +29,118 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
this.scorer = scorer;
|
||||
}
|
||||
|
||||
public VendorsCrawler crawler(Consumer<List<Edge<Vendor>>> onFoundFunc){
|
||||
public VendorsCrawler crawler(Function<List<Edge<Vendor>>, Boolean> onFoundFunc){
|
||||
return new VendorsCrawler(onFoundFunc);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vertex<Vendor> newInstance(Vendor entry, int index) {
|
||||
return new VendorVertex(entry, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GraphBuilder createGraphBuilder(Vertex<Vendor> vertex, Collection<Vendor> set, int deep, double 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) {
|
||||
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
|
||||
@@ -48,23 +148,241 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
double nextlimit = super.onConnect(buyer);
|
||||
Vendor seller = vertex.getEntry();
|
||||
if (nextlimit > 0){
|
||||
if (buyer instanceof TransitVendor && seller.getPlace().equals(buyer.getPlace())) nextlimit = -1;
|
||||
if (seller instanceof TransitVendor && seller.getPlace().equals(buyer.getPlace())) nextlimit = -1;
|
||||
if (buyer instanceof TransitVendor && (deep == 0 || seller.getPlace().equals(buyer.getPlace()))){
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectibleEdge<Vendor> createEdge(Vertex<Vendor> target) {
|
||||
return new VendorsEdge(vertex, target, refill, fuelCost);
|
||||
protected VendorsBuildEdge createEdge(Vertex<Vendor> target) {
|
||||
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> {
|
||||
private TransitPath path;
|
||||
private List<Order> orders;
|
||||
|
||||
protected VendorsEdge(Vertex<Vendor> source, Vertex<Vendor> target, boolean refill, double fuel) {
|
||||
super(source, target, refill, fuel);
|
||||
protected VendorsEdge(Vertex<Vendor> source, Vertex<Vendor> target, TransitPath path) {
|
||||
super(source, target);
|
||||
if (path == null) throw new IllegalArgumentException("Path must be no-null");
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
protected void setOrders(List<Order> orders){
|
||||
@@ -84,100 +402,118 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
return orders;
|
||||
}
|
||||
|
||||
public double getRemain() {
|
||||
return path.getRemain();
|
||||
}
|
||||
|
||||
public boolean isRefill() {
|
||||
return path.isRefill();
|
||||
}
|
||||
|
||||
public TransitPath getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double computeWeight() {
|
||||
int jumps = source.getEntry().getPlace().equals(target.getEntry().getPlace())? 0 : 1;
|
||||
int lands = !(target.getEntry() instanceof TransitVendor) ? 1 : 0;
|
||||
boolean transit = lands == 0 && source.getEntry() instanceof TransitVendor || target.getEntry() instanceof TransitVendor;
|
||||
int lands = 1; double fuel = fuelCost;
|
||||
if (path != null){
|
||||
jumps = path.size(); fuel = getFuelCost();
|
||||
lands += path.getRefillCount();
|
||||
}
|
||||
double profit = getProfit();
|
||||
double score = transit ? scorer.getTransitScore(fuel) :
|
||||
scorer.getScore(target.getEntry(), profit, jumps, lands, fuel);
|
||||
double score = scorer.getScore(target.getEntry(), profit, jumps, lands, fuel);
|
||||
score = scorer.getMaxScore() - score;
|
||||
if (score < 0)
|
||||
score = 0;
|
||||
if (score < 0) score = 0;
|
||||
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> {
|
||||
protected VendorsCrawler(Consumer<List<Edge<Vendor>>> onFoundFunc) {
|
||||
@Override
|
||||
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);
|
||||
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
|
||||
protected VendorsTraversalEntry start(Vertex<Vendor> vertex) {
|
||||
double balance = getProfile().getBalance();
|
||||
return new VendorsTraversalEntry(super.start(vertex), balance);
|
||||
return new VendorsTraversalEntry(super.start(vertex), startFuel, startBalance);
|
||||
}
|
||||
|
||||
@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;
|
||||
CCostTraversalEntry ce = super.travers(entry, edge, target);
|
||||
return new VendorsTraversalEntry((VendorsTraversalEntry) entry, edge, ce.getFuel(), ((VendorsTraversalEntry)entry).balance + vEdge.getProfit());
|
||||
return new VendorsTraversalEntry(vEntry, vEdge);
|
||||
}
|
||||
|
||||
protected class VendorsTraversalEntry extends CCostTraversalEntry {
|
||||
protected class VendorsTraversalEntry extends CostTraversalEntry {
|
||||
private final double fuel;
|
||||
private final double balance;
|
||||
|
||||
protected VendorsTraversalEntry(CCostTraversalEntry entry, double balance) {
|
||||
super(entry.getTarget(), entry.getFuel());
|
||||
protected VendorsTraversalEntry(CostTraversalEntry entry, double fuel, double balance) {
|
||||
super(entry.getTarget());
|
||||
this.fuel = fuel;
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
protected VendorsTraversalEntry(VendorsTraversalEntry head, Edge<Vendor> edge, double fuel, double balance) {
|
||||
super(head, edge, 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<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){
|
||||
boolean good = super.check(e);
|
||||
// remove transit cicles
|
||||
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;
|
||||
VendorsBuildEdge edge = (VendorsBuildEdge) e;
|
||||
return fuel <= edge.getMaxFuel() && (fuel >= edge.getMinFuel() || edge.getSource().getEntry().canRefill()) && (edge.getProfit() > 0 || isFound(edge));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VendorsEdge wrap(Edge<Vendor> edge) {
|
||||
ConnectibleEdge<Vendor> cEdge = super.wrap(edge);
|
||||
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;
|
||||
protected VendorsEdge wrap(Edge<Vendor> e) {
|
||||
VendorsBuildEdge edge = (VendorsBuildEdge) e;
|
||||
return edge.getInstance(fuel, balance);
|
||||
}
|
||||
|
||||
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 final List<Vertex<T>> vertexes;
|
||||
protected final GraphCallBack callback;
|
||||
|
||||
protected int minJumps;
|
||||
|
||||
protected AbstractGraph() {
|
||||
@@ -37,17 +36,26 @@ public abstract class AbstractGraph<T> implements Graph<T> {
|
||||
minJumps = 1;
|
||||
root = getInstance(start, maxDeep, maxDeep);
|
||||
POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit));
|
||||
onEnd();
|
||||
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);
|
||||
if (vertex == null) {
|
||||
synchronized (vertexes){
|
||||
vertex = getVertex(entry).orElse(null);
|
||||
if (vertex == null){
|
||||
LOG.trace("Is new vertex");
|
||||
vertex = new Vertex<>(entry, vertexes.size());
|
||||
vertex = newInstance(entry, vertexes.size());
|
||||
vertex.setLevel(level);
|
||||
vertexes.add(vertex);
|
||||
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 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);
|
||||
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);
|
||||
startFuel = getShip().getTank();
|
||||
}
|
||||
@@ -78,22 +78,23 @@ public class CCrawler<T extends Connectable<T>> extends Crawler<T> {
|
||||
|
||||
@Override
|
||||
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){
|
||||
ConnectibleEdge<T> edge = (ConnectibleEdge<T>) e;
|
||||
return edge.getFuelCost() <= fuel || edge.getSource().getEntry().canRefill();
|
||||
ConnectibleGraph<T>.BuildEdge edge = (ConnectibleGraph<T>.BuildEdge) e;
|
||||
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();
|
||||
double distance = source.getDistance(edge.target.getEntry());
|
||||
double fuelCost = getShip().getFuelCost(fuel, distance);
|
||||
double nextLimit = getProfile().withRefill() ? fuel - fuelCost : getShip().getTank();
|
||||
double refill = nextLimit < 0 && source.canRefill() ? getShip().getRoundMaxFuel(distance) : 0;
|
||||
double fuelCost = edge.getFuelCost(fuel);
|
||||
double nextLimit = fuel - fuelCost;
|
||||
if (nextLimit < 0 && !source.canRefill()) return null;
|
||||
double refill = nextLimit < 0 ? edge.getRefill() : 0;
|
||||
if (refill > 0) {
|
||||
fuelCost = getShip().getFuelCost(refill, distance);
|
||||
fuelCost = edge.getFuelCost(refill);
|
||||
}
|
||||
ConnectibleEdge<T> cEdge = new ConnectibleEdge<>(edge.getSource(), edge.getTarget());
|
||||
cEdge.setRefill(refill);
|
||||
|
||||
@@ -8,7 +8,6 @@ import ru.trader.core.Ship;
|
||||
import ru.trader.graph.Connectable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T> {
|
||||
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());
|
||||
}
|
||||
|
||||
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 {
|
||||
private final DistanceFilter distanceFilter;
|
||||
protected double refill;
|
||||
protected double fuelCost;
|
||||
protected double minFuel;
|
||||
protected double maxFuel;
|
||||
protected double distance;
|
||||
|
||||
protected ConnectibleGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
super(vertex, set, deep, limit);
|
||||
distanceFilter = new DistanceFilter(limit, vertex.getEntry());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double onConnect(T entry) {
|
||||
double distance = vertex.getEntry().getDistance(entry);
|
||||
if (!distanceFilter.test(distance)){
|
||||
distance = vertex.getEntry().getDistance(entry);
|
||||
if (distance > getShip().getMaxJumpRange()){
|
||||
LOG.trace("Vertex {} is far away, {}", entry, distance);
|
||||
return -1;
|
||||
}
|
||||
fuelCost = getShip().getFuelCost(limit, distance);
|
||||
double nextLimit = profile.withRefill() ? limit - fuelCost : getShip().getTank();
|
||||
if (nextLimit < 0 && vertex.getEntry().canRefill()) {
|
||||
LOG.trace("Refill");
|
||||
refill = getShip().getRoundMaxFuel(distance);
|
||||
fuelCost = getShip().getFuelCost(refill, distance);
|
||||
nextLimit = refill - fuelCost;
|
||||
} else {
|
||||
refill = 0;
|
||||
}
|
||||
return nextLimit;
|
||||
maxFuel = getShip().getMaxFuel(distance);
|
||||
minFuel = getShip().getMinFuel(distance);
|
||||
double fuel = getProfile().withRefill() ? vertex.getEntry().canRefill() ? getShip().getRoundMaxFuel(distance) : limit : getShip().getTank();
|
||||
double fuelCost = getShip().getFuelCost(fuel, distance);
|
||||
return fuel - fuelCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectibleEdge<T> createEdge(Vertex<T> target) {
|
||||
ConnectibleEdge<T> res = new ConnectibleEdge<>(vertex, target);
|
||||
res.setRefill(refill);
|
||||
res.setFuelCost(fuelCost);
|
||||
protected BuildEdge createEdge(Vertex<T> target) {
|
||||
BuildEdge res = new BuildEdge(vertex, target);
|
||||
res.setFuel(minFuel, maxFuel);
|
||||
res.setDistance(distance);
|
||||
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;
|
||||
|
||||
import ru.trader.core.Ship;
|
||||
import ru.trader.graph.Connectable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
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());
|
||||
for (int i = 0; i < edges.size(); i++) {
|
||||
ConnectibleEdge<T> edge = edges.get(i);
|
||||
if (i==0) entries.add(new PathEntry<>(edge.getSource().getEntry(), false));
|
||||
entries.add(new PathEntry<>(edge.getTarget().getEntry(), edge.isRefill()));
|
||||
}
|
||||
|
||||
public Path(Collection<ConnectibleGraph<T>.BuildEdge> edges) {
|
||||
entries = new ArrayList<>(edges);
|
||||
updateStat();
|
||||
}
|
||||
|
||||
public PathEntry<T> get(int index){
|
||||
return entries.get(index);
|
||||
private void updateStat(){
|
||||
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;
|
||||
|
||||
public class Profile {
|
||||
public static enum PATH_PRIORITY {FAST, ECO}
|
||||
|
||||
private double balance;
|
||||
private int jumps;
|
||||
@@ -14,6 +15,7 @@ public class Profile {
|
||||
private double jumpMult;
|
||||
private double landMult;
|
||||
private double fuelPrice;
|
||||
private PATH_PRIORITY pathPriority;
|
||||
|
||||
public Profile(Ship ship) {
|
||||
this.ship = ship;
|
||||
@@ -25,6 +27,7 @@ public class Profile {
|
||||
landMult = 0.95;
|
||||
fuelPrice = 100;
|
||||
jumpMult = 0.5;
|
||||
pathPriority = PATH_PRIORITY.ECO;
|
||||
}
|
||||
|
||||
public double getBalance() {
|
||||
@@ -114,4 +117,12 @@ public class Profile {
|
||||
public void setFuelPrice(double 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRefill() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@@ -78,6 +78,10 @@ public class RouteSearcherTest extends Assert{
|
||||
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, Arrays.asList(ithaca_st, lhs3262_st,
|
||||
morgor_st, lhs3006_st, ithaca.asTransit(), lhs3262.asTransit(),
|
||||
morgor.asTransit(), lhs3006.asTransit()));
|
||||
*/
|
||||
Route actual = apaths.stream().findFirst().get();
|
||||
assertEquals("Routes is different", route, actual);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.trader.analysis.graph.ConnectibleGraph;
|
||||
import ru.trader.analysis.graph.Crawler;
|
||||
import ru.trader.analysis.graph.SimpleCollector;
|
||||
import ru.trader.analysis.graph.Vertex;
|
||||
@@ -58,10 +59,10 @@ public class VendorsGraphTest extends Assert {
|
||||
LOG.info("Build vendors graph");
|
||||
VendorsGraph vGraph = new VendorsGraph(scorer);
|
||||
vGraph.build(cabreraDock, fWorld.getMarkets(true).collect(Collectors.toList()));
|
||||
|
||||
LOG.info("Search");
|
||||
SimpleCollector<Vendor> paths = new SimpleCollector<>();
|
||||
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);
|
||||
assertEquals(100, paths.get().size());
|
||||
paths.clear();
|
||||
|
||||
Reference in New Issue
Block a user