diff --git a/client/src/main/java/ru/trader/model/MarketModel.java b/client/src/main/java/ru/trader/model/MarketModel.java index 21775cd..124d03e 100644 --- a/client/src/main/java/ru/trader/model/MarketModel.java +++ b/client/src/main/java/ru/trader/model/MarketModel.java @@ -101,12 +101,12 @@ public class MarketModel { return BindingsHelper.observableList(analyzer.getTopPaths(balance), modeler::get); } - PathRoute getPath(SystemModel from, SystemModel to) { - return analyzer.getPath(from.getSystem(), to.getSystem()); + PathRoute getPath(StationModel from, StationModel to) { + return analyzer.getPath(from.getStation(), to.getStation()); } public PathRouteModel getPath(OrderModel order) { - PathRoute p = analyzer.getPath(order.getStation().getStation().getPlace(), order.getBuyer().getStation().getPlace()); + PathRoute p = analyzer.getPath(order.getStation().getStation(), order.getBuyer().getStation()); if (p == null) return null; p.getRoot().getNext().setOrder(new Order(order.getOffer().getOffer(), order.getBuyOffer().getOffer(), order.getCount())); return modeler.get(p); diff --git a/client/src/main/java/ru/trader/model/PathRouteModel.java b/client/src/main/java/ru/trader/model/PathRouteModel.java index add7e1d..4df92c7 100644 --- a/client/src/main/java/ru/trader/model/PathRouteModel.java +++ b/client/src/main/java/ru/trader/model/PathRouteModel.java @@ -82,7 +82,7 @@ public class PathRouteModel { } public void add(OrderModel order){ - PathRoute p = market.getPath(order.getStation().getSystem(), order.getBuyer().getSystem()); + PathRoute p = market.getPath(order.getStation(), order.getBuyer()); if (p == null) return; p.getRoot().getNext().setOrder(new Order(order.getOffer().getOffer(), order.getBuyOffer().getOffer(), order.getCount())); PathRoute head = path.getEnd(); diff --git a/client/src/main/java/ru/trader/model/StationModel.java b/client/src/main/java/ru/trader/model/StationModel.java index 731e6e7..b8b719a 100644 --- a/client/src/main/java/ru/trader/model/StationModel.java +++ b/client/src/main/java/ru/trader/model/StationModel.java @@ -103,10 +103,7 @@ public class StationModel { } public double getDistance(StationModel other){ - Place place = station.getPlace(); - Place otherPlace = other.station.getPlace(); - if (!place.equals(otherPlace)) return station.getPlace().getDistance(other.station.getPlace()); - return (station.getDistance() + other.station.getDistance() + Math.abs(station.getDistance() - other.station.getDistance())) * 0.00000003169 / 2; + return station.getDistance(other.station); } @Override diff --git a/core/src/main/java/ru/trader/core/AbstractItemStat.java b/core/src/main/java/ru/trader/core/AbstractItemStat.java index decef61..5a610ad 100644 --- a/core/src/main/java/ru/trader/core/AbstractItemStat.java +++ b/core/src/main/java/ru/trader/core/AbstractItemStat.java @@ -1,5 +1,7 @@ package ru.trader.core; +import ru.trader.graph.Connectable; + import java.util.Collection; import java.util.Collections; @@ -112,6 +114,11 @@ public abstract class AbstractItemStat implements ItemStat { public void remove(Vendor vendor) { throw new UnsupportedOperationException("Is fake place, change unsupported"); } + + @Override + public int compareTo(Connectable o) { + return 0; + } }; private static Vendor NONE_VENDOR = new Vendor() { @@ -189,5 +196,10 @@ public abstract class AbstractItemStat implements ItemStat { public boolean has(OFFER_TYPE type, Item item) { return false; } + + @Override + public int compareTo(Connectable o) { + return 0; + } }; } diff --git a/core/src/main/java/ru/trader/core/AbstractPlace.java b/core/src/main/java/ru/trader/core/AbstractPlace.java index ad34487..e661023 100644 --- a/core/src/main/java/ru/trader/core/AbstractPlace.java +++ b/core/src/main/java/ru/trader/core/AbstractPlace.java @@ -3,10 +3,11 @@ package ru.trader.core; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ru.trader.graph.Connectable; import java.util.Objects; -public abstract class AbstractPlace implements Place, Comparable { +public abstract class AbstractPlace implements Place { private final static Logger LOG = LoggerFactory.getLogger(AbstractPlace.class); private AbstractMarket market; @@ -84,8 +85,9 @@ public abstract class AbstractPlace implements Place, Comparable { } @Override - public int compareTo(@NotNull Place other) { - Objects.requireNonNull(other, "Not compare with null"); + public int compareTo(@NotNull Connectable o) { + Objects.requireNonNull(o, "Not compare with null"); + Place other = (Place) o; if (this == other) return 0; String name = getName(); String otherName = other.getName(); diff --git a/core/src/main/java/ru/trader/core/AbstractVendor.java b/core/src/main/java/ru/trader/core/AbstractVendor.java index 9a5993e..632fd2f 100644 --- a/core/src/main/java/ru/trader/core/AbstractVendor.java +++ b/core/src/main/java/ru/trader/core/AbstractVendor.java @@ -1,7 +1,11 @@ package ru.trader.core; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ru.trader.graph.Connectable; + +import java.util.Objects; public abstract class AbstractVendor implements Vendor { private final static Logger LOG = LoggerFactory.getLogger(AbstractVendor.class); @@ -107,4 +111,15 @@ public abstract class AbstractVendor implements Vendor { return getName(); } + @Override + public int compareTo(@NotNull Connectable o) { + Objects.requireNonNull(o, "Not compare with null"); + Vendor other = (Vendor) o; + if (this == other) return 0; + int cmp = Double.compare(getDistance(), other.getDistance()); + if (cmp!=0) return cmp; + String name = getName(); + String otherName = other.getName(); + return name != null ? otherName != null ? name.compareTo(otherName) : -1 : 0; + } } diff --git a/core/src/main/java/ru/trader/core/Market.java b/core/src/main/java/ru/trader/core/Market.java index d2c4500..37912c2 100644 --- a/core/src/main/java/ru/trader/core/Market.java +++ b/core/src/main/java/ru/trader/core/Market.java @@ -17,6 +17,9 @@ public interface Market { void remove(Item item); Collection get(); + default Collection getVendors(){ + return new PlacesWrapper(get()); + } Collection getGroups(); Collection getItems(); ItemStat getStat(OFFER_TYPE type, Item item); diff --git a/core/src/main/java/ru/trader/core/MarketAnalyzer.java b/core/src/main/java/ru/trader/core/MarketAnalyzer.java index eed30cd..5a6ce49 100644 --- a/core/src/main/java/ru/trader/core/MarketAnalyzer.java +++ b/core/src/main/java/ru/trader/core/MarketAnalyzer.java @@ -1,5 +1,6 @@ package ru.trader.core; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.trader.graph.*; @@ -44,7 +45,7 @@ public class MarketAnalyzer { private Collection getOrders(Place place, double balance, double lowProfit) { List res = new ArrayList<>(20); Collection places = market.get(); - RouteGraph graph = new RouteGraph(place, places, tank, maxDistance, true, jumps); + Graph graph = new Graph<>(place, places, tank, maxDistance, true, jumps, Path::new); for (Vendor vendor : place.get()) { for (Offer sell : vendor.getAllSellOffers()) { LOG.trace("Sell offer {}", sell); @@ -76,7 +77,7 @@ public class MarketAnalyzer { public Collection getOrders(Place from, Place to, double balance) { Collection res = new ArrayList<>(); - RouteGraph graph = new RouteGraph(from, market.get(), tank, maxDistance, true, jumps); + Graph graph = new Graph<>(from, market.get(), tank, maxDistance, true, jumps, Path::new); if (!graph.isAccessible(to)){ LOG.trace("Is inaccessible buyer"); return res; @@ -102,7 +103,7 @@ public class MarketAnalyzer { public Collection getOrders(Vendor from, Vendor to, double balance) { Collection res = new ArrayList<>(); - RouteGraph graph = new RouteGraph(from.getPlace(), market.get(), tank, maxDistance, true, jumps); + Graph graph = new Graph<>(from.getPlace(), market.get(), tank, maxDistance, true, jumps, Path::new); if (!graph.isAccessible(to.getPlace())){ LOG.trace("Is inaccessible buyer"); return res; @@ -124,31 +125,60 @@ public class MarketAnalyzer { } public Collection> getPaths(Place from, Place to){ - RouteGraph graph = new RouteGraph(from, market.get(), tank, maxDistance, true, jumps); + Graph graph = new Graph<>(from, market.get(), tank, maxDistance, true, jumps, Path::new); return graph.getPathsTo(to); } - public PathRoute getPath(Place from, Place to){ - RouteGraph graph = new RouteGraph(from, market.get(), tank, maxDistance, true, jumps); - return (PathRoute) graph.getFastPathTo(to); + public Path getPath(Place from, Place to){ + Graph graph = new Graph<>(from, market.get(), tank, maxDistance, true, jumps, Path::new); + return graph.getFastPathTo(to); + } + + public PathRoute getPath(Vendor from, Vendor to){ + RouteGraph graph = new RouteGraph(from, market.getVendors(), tank, maxDistance, true, jumps); + return (PathRoute)graph.getFastPathTo(to); } public Collection getPaths(Place from, double balance){ RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segmentSize); - return searcher.getPaths(from, market.get(), jumps, balance, cargo, limit); + Collection vendors = market.getVendors(); + for (Vendor vendor : from.get()) { + Collection paths = searcher.getPaths(vendor, vendors, jumps, balance, cargo, limit); + if (paths.size()>0){ + return paths; + } + } + return Collections.emptyList(); + + } + + public Collection getPaths(Vendor from, Vendor to, double balance){ + RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segmentSize); + return searcher.getPaths(from, to, market.getVendors(), jumps, balance, cargo, limit); } public Collection getPaths(Place from, Place to, double balance){ RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segmentSize); - return searcher.getPaths(from, to, market.get(), jumps, balance, cargo, limit); + Collection vendors = market.getVendors(); + Collection fVendors = from.get(); + Collection toVendors = to.get(); + for (Vendor fromVendor : fVendors) { + for (Vendor toVendor : toVendors) { + Collection paths = searcher.getPaths(fromVendor, toVendor, vendors, jumps, balance, cargo, limit); + if (paths.size()>0){ + return paths; + } + } + } + return Collections.emptyList(); } public Collection getTopPaths(double balance){ List top = new ArrayList<>(limit); RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segmentSize); - Collection places = market.get(); - for (Place place : places) { - Collection paths = searcher.getPaths(place, place, places, jumps, balance, cargo, 3); + Collection vendors = new PlacesWrapper(market.get()); + for (Vendor vendor : vendors) { + Collection paths = searcher.getPaths(vendor, vendor, vendors, jumps, balance, cargo, 3); TopList.addAllToTop(top, paths, limit, RouteGraph.byProfitComparator); } return top; diff --git a/core/src/main/java/ru/trader/core/Place.java b/core/src/main/java/ru/trader/core/Place.java index 516cf76..7a9e454 100644 --- a/core/src/main/java/ru/trader/core/Place.java +++ b/core/src/main/java/ru/trader/core/Place.java @@ -19,6 +19,10 @@ public interface Place extends Connectable { Vendor addVendor(String name); void remove(Vendor vendor); + default int count(){ + return get().size(); + } + default boolean isEmpty(){ return get().isEmpty(); } diff --git a/core/src/main/java/ru/trader/core/PlacesWrapper.java b/core/src/main/java/ru/trader/core/PlacesWrapper.java new file mode 100644 index 0000000..7795e0e --- /dev/null +++ b/core/src/main/java/ru/trader/core/PlacesWrapper.java @@ -0,0 +1,32 @@ +package ru.trader.core; + +import org.jetbrains.annotations.NotNull; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; + +public class PlacesWrapper extends AbstractCollection { + private final Collection places; + private int size; + + public PlacesWrapper(Collection places) { + this.places = places; + size = 0; + for (Place place : places) { + int count = place.count(); + size += count > 0 ? count : 1; + } + } + + @NotNull + @Override + public Iterator iterator() { + return new VendorsIterator(places); + } + + @Override + public int size() { + return size; + } +} diff --git a/core/src/main/java/ru/trader/core/Vendor.java b/core/src/main/java/ru/trader/core/Vendor.java index 74b8644..48c3d46 100644 --- a/core/src/main/java/ru/trader/core/Vendor.java +++ b/core/src/main/java/ru/trader/core/Vendor.java @@ -1,11 +1,9 @@ package ru.trader.core; -import org.jetbrains.annotations.NotNull; - +import ru.trader.graph.Connectable; import java.util.Collection; -import java.util.Objects; -public interface Vendor extends Comparable { +public interface Vendor extends Connectable { String getName(); void setName(String name); @@ -53,14 +51,18 @@ public interface Vendor extends Comparable { } - @Override - default int compareTo(@NotNull Vendor other) { - Objects.requireNonNull(other, "Not compare with null"); - if (this == other) return 0; - int cmp = Double.compare(getDistance(), other.getDistance()); - if (cmp!=0) return cmp; - String name = getName(); - String otherName = other.getName(); - return name != null ? otherName != null ? name.compareTo(otherName) : -1 : 0; + static double LS = 0.00000003169; + + default double getDistance(Vendor other){ + Place place = getPlace(); + Place otherPlace = other.getPlace(); + if (!place.equals(otherPlace)){ + return getPlace().getDistance(other.getPlace()) + other.getDistance() * LS; + } + return (getDistance() + other.getDistance() + Math.abs(getDistance() - other.getDistance())) * LS / 2; + } + + default boolean canRefill(){ + return getPlace().canRefill(); } } diff --git a/core/src/main/java/ru/trader/core/VendorsIterator.java b/core/src/main/java/ru/trader/core/VendorsIterator.java new file mode 100644 index 0000000..ae44114 --- /dev/null +++ b/core/src/main/java/ru/trader/core/VendorsIterator.java @@ -0,0 +1,162 @@ +package ru.trader.core; + +import ru.trader.graph.Connectable; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +public class VendorsIterator implements Iterator { + + private final Iterator places; + private Iterator vendors; + private Vendor next; + + public VendorsIterator(Collection places) { + this.places = places.iterator(); + nextPlace(); + } + + private void nextPlace(){ + if (places.hasNext()){ + Place place = places.next(); + Collection v = place.get(); + if (place.count() > 0){ + vendors = v.iterator(); + nextVendor(); + } else { + next = new TransitVendor(place); + } + } else { + next = null; + } + } + + private void nextVendor(){ + if (vendors != null && vendors.hasNext()) { + next = vendors.next(); + } else { + next = null; + } + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public Vendor next() { + Vendor current = next; + nextVendor(); + if (next == null){ + nextPlace(); + } + return current; + } + + + private class TransitVendor implements Vendor { + + private Place place; + + private TransitVendor(Place place) { + this.place = place; + } + + @Override + public String getName() { + return ""; + } + + @Override + public void setName(String name) { + throw new UnsupportedOperationException("Is fake vendor, change unsupported"); + } + + @Override + public Place getPlace() { + return place; + } + + @Override + public double getDistance() { + return 0; + } + + @Override + public void setDistance(double distance) { + throw new UnsupportedOperationException("Is fake vendor, change unsupported"); + } + + @Override + public void add(SERVICE_TYPE service) { + throw new UnsupportedOperationException("Is fake vendor, change unsupported"); + } + + @Override + public void remove(SERVICE_TYPE service) { + throw new UnsupportedOperationException("Is fake vendor, change unsupported"); + } + + @Override + public boolean has(SERVICE_TYPE service) { + return false; + } + + @Override + public Collection getServices() { + return Collections.emptyList(); + } + + @Override + public Offer addOffer(OFFER_TYPE type, Item item, double price, long count) { + throw new UnsupportedOperationException("Is fake vendor, change unsupported"); + } + + @Override + public void add(Offer offer) { + throw new UnsupportedOperationException("Is fake vendor, change unsupported"); + } + + @Override + public void remove(Offer offer) { + throw new UnsupportedOperationException("Is fake vendor, change unsupported"); + } + + @Override + public Collection get(OFFER_TYPE type) { + return Collections.emptyList(); + } + + @Override + public Offer get(OFFER_TYPE type, Item item) { + return null; + } + + @Override + public boolean has(OFFER_TYPE type, Item item) { + return false; + } + + @Override + public int compareTo(Connectable o) { + double d = getDistance((Vendor)o); + return d == 0 ? 0 : d > 0 ? 1 : -1; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TransitVendor)) return false; + TransitVendor that = (TransitVendor) o; + return place.equals(that.place); + } + + @Override + public int hashCode() { + return place.hashCode(); + } + } + +} diff --git a/core/src/main/java/ru/trader/graph/Connectable.java b/core/src/main/java/ru/trader/graph/Connectable.java index 757f2ec..a8c3060 100644 --- a/core/src/main/java/ru/trader/graph/Connectable.java +++ b/core/src/main/java/ru/trader/graph/Connectable.java @@ -1,6 +1,6 @@ package ru.trader.graph; -public interface Connectable { +public interface Connectable extends Comparable>{ public double getDistance(T other); diff --git a/core/src/main/java/ru/trader/graph/PathRoute.java b/core/src/main/java/ru/trader/graph/PathRoute.java index d785bc1..18f273c 100644 --- a/core/src/main/java/ru/trader/graph/PathRoute.java +++ b/core/src/main/java/ru/trader/graph/PathRoute.java @@ -4,12 +4,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.trader.core.Offer; import ru.trader.core.Order; -import ru.trader.core.Place; import ru.trader.core.Vendor; import java.util.*; -public class PathRoute extends Path { +public class PathRoute extends Path { private final static Logger LOG = LoggerFactory.getLogger(PathRoute.class); private final ArrayList orders = new ArrayList<>(); @@ -21,21 +20,21 @@ public class PathRoute extends Path { private PathRoute tail; public final static Order TRANSIT = null; - public PathRoute(Vertex source) { + public PathRoute(Vertex source) { this(source, false); } - public static PathRoute buildAvg(Vertex source){ + public static PathRoute buildAvg(Vertex source){ return new PathRoute(source, true); } - private PathRoute(Vertex source, boolean byAvg) { + private PathRoute(Vertex source, boolean byAvg) { super(source); this.byAvg = byAvg; } - private PathRoute(PathRoute head, Vertex vertex, boolean refill) { + private PathRoute(PathRoute head, Vertex vertex, boolean refill) { super(head, vertex, refill); assert head.tail == null; head.tail = this; @@ -45,7 +44,7 @@ public class PathRoute extends Path { } @Override - public Path connectTo(Vertex vertex, boolean refill) { + public Path connectTo(Vertex vertex, boolean refill) { LOG.trace("Connect path {} to {}", this, vertex); return new PathRoute(this.getCopy(), vertex, refill); } @@ -127,27 +126,17 @@ public class PathRoute extends Path { orders.clear(); orders.add(TRANSIT); LOG.trace("Fill orders of path {}", this); - Place placeSeller = getPrevious().get(); - for (Vendor seller : placeSeller.get()) { - for (Offer sell : seller.getAllSellOffers()) { - PathRoute p = this; - while (p != null) { - Place placeBuyer = p.get(); - Order best = null; - for (Vendor buyer : placeBuyer.get()) { - Offer buy = buyer.getBuy(sell.getItem()); - if (buy != null){ - Order order = new Order(sell, buy); - if (best == null || best.compareTo(order) < 0){ - best = order; - } - } - } - if (best != null){ - addOrder(best); - } - p = p.getNext(); + Vendor seller = getPrevious().get(); + for (Offer sell : seller.getAllSellOffers()) { + PathRoute p = this; + while (p != null) { + Vendor buyer = p.get(); + Offer buy = buyer.getBuy(sell.getItem()); + if (buy != null){ + Order order = new Order(sell, buy); + addOrder(order); } + p = p.getNext(); } } } @@ -225,7 +214,7 @@ public class PathRoute extends Path { PathRoute p = getPrevious(); balance = p.balance; if (!p.isRoot()) { - Place buyer = p.get(); + Vendor buyer = p.get(); while (!p.isRoot()){ for (Order order : p.orders) { if (order == TRANSIT) continue; @@ -267,7 +256,7 @@ public class PathRoute extends Path { public double getProfit(Order order){ if (order == TRANSIT) return getTransitProfit(); - if (isPathFrom(order.getBuyer().getPlace())) return order.getProfit() + profit; + if (isPathFrom(order.getBuyer())) return order.getProfit() + profit; return hasNext() ? getNext().getProfit(order) : order.getProfit(); } @@ -340,7 +329,7 @@ public class PathRoute extends Path { while (p.hasNext()){ p = p.getNext(); // lands for sell - if (order != null && p.isPathFrom(order.getBuyer().getPlace())){ + if (order != null && p.isPathFrom(order.getBuyer())){ LOG.trace("{} is lands for sell by order {}", p, order); return res + p.getLandsCount() + 1; } else { @@ -403,15 +392,15 @@ public class PathRoute extends Path { public PathRoute dropTo(Vendor vendor){ PathRoute p = getCopy(true).getEnd(); - while (!p.isRoot() && !p.get().equals(vendor.getPlace())){ + while (!p.isRoot() && !p.get().equals(vendor)){ p = p.getPrevious(); } p.tail = null; return p; } - public static PathRoute toPathRoute(Place... items){ - Place t = items[0]; + public static PathRoute toPathRoute(Vendor... items){ + Vendor t = items[0]; PathRoute path = new PathRoute(new Vertex<>(t)); for (int i = 1; i < items.length; i++) { t = items[i]; diff --git a/core/src/main/java/ru/trader/graph/RouteGraph.java b/core/src/main/java/ru/trader/graph/RouteGraph.java index 0171be4..26e9cb2 100644 --- a/core/src/main/java/ru/trader/graph/RouteGraph.java +++ b/core/src/main/java/ru/trader/graph/RouteGraph.java @@ -1,10 +1,10 @@ package ru.trader.graph; -import ru.trader.core.Place; +import ru.trader.core.Vendor; import java.util.*; -public class RouteGraph extends Graph { +public class RouteGraph extends Graph { private double balance; private int cargo; @@ -28,11 +28,11 @@ public class RouteGraph extends Graph { return byProfitComparator.compare(p1, p2); }; - public RouteGraph(Place start, Collection set, double stock, double maxDistance, boolean withRefill, int maxDeep) { + public RouteGraph(Vendor start, Collection set, double stock, double maxDistance, boolean withRefill, int maxDeep) { this(start, set, stock, maxDistance, withRefill, maxDeep, false); } - public RouteGraph(Place start, Collection set, double stock, double maxDistance, boolean withRefill, int maxDeep, boolean groupRes) { + public RouteGraph(Vendor start, Collection set, double stock, double maxDistance, boolean withRefill, int maxDeep, boolean groupRes) { super(start, set, stock, maxDistance, withRefill, maxDeep, groupRes ? PathRoute::buildAvg : PathRoute::new); if (groupRes){ this.groupRes = maxDeep > minJumps; @@ -48,7 +48,7 @@ public class RouteGraph extends Graph { } @Override - protected TopList> newTopList(int count) { + protected TopList> newTopList(int count) { int groupSize = 0; if (groupRes && getMinJumps() > 1){ groupSize = Math.floorDiv(count, root.getLevel()); @@ -56,7 +56,7 @@ public class RouteGraph extends Graph { return new TopRoutes(count, groupSize); } - private class TopRoutes extends TopList> { + private class TopRoutes extends TopList> { private final int groupSize; public TopRoutes(int limit, int groupSize) { @@ -65,7 +65,7 @@ public class RouteGraph extends Graph { } @Override - public boolean add(Path entry) { + public boolean add(Path entry) { if (comparator != null){ ((PathRoute)entry).sort(balance, cargo); } diff --git a/core/src/main/java/ru/trader/graph/RouteSearcher.java b/core/src/main/java/ru/trader/graph/RouteSearcher.java index 219b393..9b11a80 100644 --- a/core/src/main/java/ru/trader/graph/RouteSearcher.java +++ b/core/src/main/java/ru/trader/graph/RouteSearcher.java @@ -4,6 +4,7 @@ package ru.trader.graph; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.trader.core.Place; +import ru.trader.core.Vendor; import java.util.ArrayList; import java.util.Collection; @@ -29,27 +30,27 @@ public class RouteSearcher { this.segmentSize = segmentSize; } - public List getPaths(Place from, Place to, Collection places, int jumps, double balance, int cargo, int limit){ - return POOL.invoke(new SegmentSearcher(from, to, places, jumps, balance, cargo, limit)); + public List getPaths(Vendor from, Vendor to, Collection vendors, int jumps, double balance, int cargo, int limit){ + return POOL.invoke(new SegmentSearcher(from, to, vendors, jumps, balance, cargo, limit)); } - public List getPaths(Place from, Collection places, int jumps, double balance, int cargo, int limit){ - return POOL.invoke(new SegmentSearcher(from, null, places, jumps, balance, cargo, limit)); + public List getPaths(Vendor from, Collection vendors, int jumps, double balance, int cargo, int limit){ + return POOL.invoke(new SegmentSearcher(from, null, vendors, jumps, balance, cargo, limit)); } public class SegmentSearcher extends RecursiveTask> { - protected final Place source; - protected final Place target; - protected final Collection places; + protected final Vendor source; + protected final Vendor target; + protected final Collection vendors; protected final int jumps; protected final double balance; protected final int cargo; protected int limit; - public SegmentSearcher(Place source, Place target, Collection places, int jumps, double balance, int cargo, int limit) { + public SegmentSearcher(Vendor source, Vendor target, Collection vendors, int jumps, double balance, int cargo, int limit) { this.source = source; this.target = target; - this.places = places; + this.vendors = vendors; this.jumps = jumps; this.balance = balance; this.cargo = cargo; @@ -59,7 +60,7 @@ public class RouteSearcher { @Override protected List compute() { LOG.trace("Start search route to {} from {}, jumps {}", source, target, jumps); - RouteGraph sGraph = new RouteGraph(source, places, stock, maxDistance, true, jumps, true); + RouteGraph sGraph = new RouteGraph(source, vendors, stock, maxDistance, true, jumps, true); int jumpsToAll = sGraph.getMinJumps(); LOG.trace("Segment jumps {}", jumpsToAll); sGraph.setCargo(cargo); @@ -67,18 +68,18 @@ public class RouteSearcher { List res = new ArrayList<>(limit); if (jumps <= jumpsToAll){ LOG.trace("Is last segment"); - List> paths; + List> paths; if (target == null){ paths = sGraph.getPaths(limit); } else { paths = sGraph.getPathsTo(target, limit); } - for (Path path : paths) { + for (Path path : paths) { res.add((PathRoute) path); } } else { LOG.trace("Split to segments"); - List> paths = sGraph.getPaths(getPathsOnSegmentCount(sGraph), jumpsToAll-1).getList(); + List> paths = sGraph.getPaths(getPathsOnSegmentCount(sGraph), jumpsToAll-1).getList(); int i = 0; ArrayList subTasks = new ArrayList<>(THRESHOLD); while (i < paths.size()) { @@ -86,7 +87,7 @@ public class RouteSearcher { for (int taskIndex = 0; taskIndex < THRESHOLD && i+taskIndex < paths.size(); taskIndex++) { PathRoute path = (PathRoute) paths.get(i+taskIndex); double newBalance = balance + path.getRoot().getProfit(); - SegmentSearcher task = new SegmentSearcher(path.get(), target, places, jumps - path.getLength(), newBalance, cargo, 1); + SegmentSearcher task = new SegmentSearcher(path.get(), target, vendors, jumps - path.getLength(), newBalance, cargo, 1); task.fork(); subTasks.add(task); } diff --git a/core/src/test/java/ru/trader/core/MarketAnalyzerTest2.java b/core/src/test/java/ru/trader/core/MarketAnalyzerTest2.java index 9ccfd29..badc27a 100644 --- a/core/src/test/java/ru/trader/core/MarketAnalyzerTest2.java +++ b/core/src/test/java/ru/trader/core/MarketAnalyzerTest2.java @@ -26,10 +26,10 @@ public class MarketAnalyzerTest2 extends Assert { // Balance: 6000000, cargo: 440, tank: 40, distance: 13.4, jumps: 6 // Ithaca (Palladium to LHS 3262) -> Morgor -> LHS 3006 -> LHS 3262 (Consumer Technology to Ithaca) -> LHS 3006 -> Morgor -> Ithaca // Profit: 981200, avg: 490600, distance: 67.5, lands: 2 - Place ithaca = market.get().stream().filter((v)->v.getName().equals("Ithaca")).findFirst().get(); - Place morgor = market.get().stream().filter((v)->v.getName().equals("Morgor")).findFirst().get(); - Place lhs3006 = market.get().stream().filter((v)->v.getName().equals("LHS 3006")).findFirst().get(); - Place lhs3262 = market.get().stream().filter((v)->v.getName().equals("LHS 3262")).findFirst().get(); + Vendor ithaca = market.get().stream().filter((v)->v.getName().equals("Ithaca")).findFirst().get().get().iterator().next(); + Vendor morgor = market.get().stream().filter((v)->v.getName().equals("Morgor")).findFirst().get().get().iterator().next(); + Vendor lhs3006 = market.get().stream().filter((v)->v.getName().equals("LHS 3006")).findFirst().get().get().iterator().next(); + Vendor lhs3262 = market.get().stream().filter((v)->v.getName().equals("LHS 3262")).findFirst().get().get().iterator().next(); analyzer.setCargo(440);analyzer.setTank(40);analyzer.setMaxDistance(13.4);analyzer.setJumps(6); Collection paths = analyzer.getPaths(ithaca, ithaca, 6000000); PathRoute expect = PathRoute.toPathRoute(ithaca, morgor, lhs3006, lhs3262, lhs3006, morgor, ithaca); diff --git a/core/src/test/java/ru/trader/graph/PathRouteTest.java b/core/src/test/java/ru/trader/graph/PathRouteTest.java index fadbd8d..e9c9acc 100644 --- a/core/src/test/java/ru/trader/graph/PathRouteTest.java +++ b/core/src/test/java/ru/trader/graph/PathRouteTest.java @@ -36,8 +36,8 @@ public class PathRouteTest extends Assert { v2.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM2, 350, -1)); v2.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM3, 400, -1)); - PathRoute res = new PathRoute(new Vertex<>(v1.getPlace())); - res = (PathRoute) res.connectTo(new Vertex<>(v2.getPlace()), false); + PathRoute res = new PathRoute(new Vertex<>(v1)); + res = (PathRoute) res.connectTo(new Vertex<>(v2), false); res.finish(); res.sort(10000, 5); return res.getRoot(); @@ -77,9 +77,9 @@ public class PathRouteTest extends Assert { v3.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM2, 350, -1)); v3.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM3, 400, -1)); - PathRoute res = new PathRoute(new Vertex<>(v1.getPlace())); - res = (PathRoute) res.connectTo(new Vertex<>(v2.getPlace()), false); - res = (PathRoute) res.connectTo(new Vertex<>(v3.getPlace()), false); + PathRoute res = new PathRoute(new Vertex<>(v1)); + res = (PathRoute) res.connectTo(new Vertex<>(v2), false); + res = (PathRoute) res.connectTo(new Vertex<>(v3), false); res.finish(); res.sort(10000, 5); return res.getRoot(); @@ -129,10 +129,10 @@ public class PathRouteTest extends Assert { v3.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM1, 200, -1)); v4.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM3, 450, -1)); - PathRoute res = new PathRoute(new Vertex<>(v1.getPlace())); - res = (PathRoute) res.connectTo(new Vertex<>(v2.getPlace()), false); - res = (PathRoute) res.connectTo(new Vertex<>(v3.getPlace()), false); - res = (PathRoute) res.connectTo(new Vertex<>(v4.getPlace()), false); + PathRoute res = new PathRoute(new Vertex<>(v1)); + res = (PathRoute) res.connectTo(new Vertex<>(v2), false); + res = (PathRoute) res.connectTo(new Vertex<>(v3), false); + res = (PathRoute) res.connectTo(new Vertex<>(v4), false); res.finish(); res.sort(10000, 5); return res.getRoot(); @@ -193,11 +193,11 @@ public class PathRouteTest extends Assert { v4.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM3, 370, -1)); v5.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM1, 400, -1)); - PathRoute res = new PathRoute(new Vertex<>(v1.getPlace())); - res = (PathRoute) res.connectTo(new Vertex<>(v2.getPlace()), false); - res = (PathRoute) res.connectTo(new Vertex<>(v3.getPlace()), false); - res = (PathRoute) res.connectTo(new Vertex<>(v4.getPlace()), false); - res = (PathRoute) res.connectTo(new Vertex<>(v5.getPlace()), false); + PathRoute res = new PathRoute(new Vertex<>(v1)); + res = (PathRoute) res.connectTo(new Vertex<>(v2), false); + res = (PathRoute) res.connectTo(new Vertex<>(v3), false); + res = (PathRoute) res.connectTo(new Vertex<>(v4), false); + res = (PathRoute) res.connectTo(new Vertex<>(v5), false); res.finish(); res.sort(10000, 5); return res.getRoot(); @@ -265,10 +265,10 @@ public class PathRouteTest extends Assert { v3.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM1, 200, -1)); v4.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM3, 450, -1)); - PathRoute res = new PathRoute(new Vertex<>(v1.getPlace())); - res = (PathRoute) res.connectTo(new Vertex<>(v2.getPlace()), false); - res = (PathRoute) res.connectTo(new Vertex<>(v3.getPlace()), false); - res = (PathRoute) res.connectTo(new Vertex<>(v4.getPlace()), false); + PathRoute res = new PathRoute(new Vertex<>(v1)); + res = (PathRoute) res.connectTo(new Vertex<>(v2), false); + res = (PathRoute) res.connectTo(new Vertex<>(v3), false); + res = (PathRoute) res.connectTo(new Vertex<>(v4), false); res.finish(); res.sort(500, 5); return res.getRoot(); @@ -325,8 +325,8 @@ public class PathRouteTest extends Assert { v2.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM2, 225, -1)); - PathRoute res = new PathRoute(new Vertex<>(v1.getPlace())); - res = (PathRoute) res.connectTo(new Vertex<>(v2.getPlace()), false); + PathRoute res = new PathRoute(new Vertex<>(v1)); + res = (PathRoute) res.connectTo(new Vertex<>(v2), false); res.finish(); res.sort(500, 5); return res.getRoot(); @@ -342,9 +342,9 @@ public class PathRouteTest extends Assert { v3.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM1, 200, -1)); v4.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM3, 450, -1)); - PathRoute res = new PathRoute(new Vertex<>(v2.getPlace())); - res = (PathRoute) res.connectTo(new Vertex<>(v3.getPlace()), false); - res = (PathRoute) res.connectTo(new Vertex<>(v4.getPlace()), false); + PathRoute res = new PathRoute(new Vertex<>(v2)); + res = (PathRoute) res.connectTo(new Vertex<>(v3), false); + res = (PathRoute) res.connectTo(new Vertex<>(v4), false); res.finish(); res.sort(500, 5); return res.getRoot(); diff --git a/core/src/test/java/ru/trader/graph/Point.java b/core/src/test/java/ru/trader/graph/Point.java index f68f026..63ac05f 100644 --- a/core/src/test/java/ru/trader/graph/Point.java +++ b/core/src/test/java/ru/trader/graph/Point.java @@ -1,5 +1,9 @@ package ru.trader.graph; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + public class Point implements Connectable { private int x; private String name; @@ -42,4 +46,11 @@ public class Point implements Connectable { public String toString() { return name; } + + @Override + public int compareTo(@NotNull Connectable o) { + Objects.requireNonNull(o, "Not compare with null"); + Point other =(Point)o; + return Integer.compare(hashCode(), other.hashCode()); + } } diff --git a/core/src/test/java/ru/trader/graph/RouteGraphTest.java b/core/src/test/java/ru/trader/graph/RouteGraphTest.java index 55e3cda..6bf0d02 100644 --- a/core/src/test/java/ru/trader/graph/RouteGraphTest.java +++ b/core/src/test/java/ru/trader/graph/RouteGraphTest.java @@ -15,14 +15,19 @@ public class RouteGraphTest extends Assert { private final static Item ITEM1 = new SimpleItem("ITEM1"); private final static Item ITEM2 = new SimpleItem("ITEM2"); private final static Item ITEM3 = new SimpleItem("ITEM3"); + private final static Item ITEM4 = new SimpleItem("ITEM4"); + private final static Item ITEM5 = new SimpleItem("ITEM5"); private static Vendor v1; private static Vendor v2; private static Vendor v3; private static Vendor v4; + private static Vendor v5; + private static Vendor v6; private static Place p1; private static Place p2; private static Place p3; private static Place p4; + private static Place p5; static { @@ -30,11 +35,14 @@ public class RouteGraphTest extends Assert { p2 = new SimplePlace("v2"); p3 = new SimplePlace("v3"); p4 = new SimplePlace("v4"); + p5 = new SimplePlace("p5",5,5,5); v1 = new SimpleVendor("v1"); v2 = new SimpleVendor("v2"); v3 = new SimpleVendor("v3"); v4 = new SimpleVendor("v4"); + v5 = new SimpleVendor("v5"); + v6 = new SimpleVendor("v6"); v1.add(new SimpleOffer(OFFER_TYPE.SELL, ITEM1, 100, -1)); v1.add(new SimpleOffer(OFFER_TYPE.SELL, ITEM2, 200, -1)); @@ -42,24 +50,28 @@ public class RouteGraphTest extends Assert { v2.add(new SimpleOffer(OFFER_TYPE.SELL, ITEM1, 150, -1)); v2.add(new SimpleOffer(OFFER_TYPE.SELL, ITEM3, 320, -1)); v3.add(new SimpleOffer(OFFER_TYPE.SELL, ITEM3, 390, -1)); + v5.add(new SimpleOffer(OFFER_TYPE.SELL, ITEM4, 100, -1)); + v6.add(new SimpleOffer(OFFER_TYPE.SELL, ITEM5, 100, -1)); v2.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM2, 225, -1)); v3.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM1, 200, -1)); v4.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM3, 450, -1)); + v5.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM5, 500, -1)); + v6.add(new SimpleOffer(OFFER_TYPE.BUY, ITEM4, 500, -1)); - p1.add(v1);p2.add(v2);p3.add(v3);p4.add(v4); - market.add(p1);market.add(p2);market.add(p3);market.add(p4); + p1.add(v1);p2.add(v2);p3.add(v3);p4.add(v4);p5.add(v5);p5.add(v6); + market.add(p1);market.add(p2);market.add(p3);market.add(p4);market.add(p5); } @Test public void testRoutes() throws Exception { - RouteGraph graph = new RouteGraph(p1, market.get(), 1, 1, true, 4); + RouteGraph graph = new RouteGraph(v1, market.getVendors(), 1, 1, true, 4); graph.setBalance(500); graph.setCargo(5); //Profit: 150 180 200 230 670 620 950 890 620 950 1015 1180 890 950 930 //Landings: 1 2 3 4 4 2 3 3 2 3 4 4 3 3 4 //Prof: 150 90 66.66 57.5 167.5 310 316.66 296.66 310 316.66 253.75 295 296.66 316.66 232.5 - ArrayList> routes = (ArrayList>) graph.getPathsTo(p4, 5); + ArrayList> routes = (ArrayList>) graph.getPathsTo(v4, 5); assertEquals(5, routes.size()); PathRoute path = (PathRoute) routes.get(0).getRoot(); @@ -82,4 +94,20 @@ public class RouteGraphTest extends Assert { assertEquals(620, path.getProfit(), 0.001); assertEquals(2, path.getLandsCount()); } + + @Test + public void testRoutes2() throws Exception { + RouteGraph graph = new RouteGraph(v5, market.getVendors(), 1, 15, true, 4); + graph.setBalance(500); + graph.setCargo(5); + ArrayList> routes = (ArrayList>) graph.getPathsTo(v1, 5); + assertEquals(5, routes.size()); + + //v5 -> v6 -> v5 -> v6 -> v1 + PathRoute path = (PathRoute) routes.get(0).getRoot(); + assertEquals(6000, path.getProfit(), 0.001); + assertEquals(4, path.getLandsCount()); + + } + } diff --git a/core/src/test/java/ru/trader/graph/RouteSearcherTest.java b/core/src/test/java/ru/trader/graph/RouteSearcherTest.java index 7e63e65..87605ca 100644 --- a/core/src/test/java/ru/trader/graph/RouteSearcherTest.java +++ b/core/src/test/java/ru/trader/graph/RouteSearcherTest.java @@ -25,18 +25,18 @@ public class RouteSearcherTest extends Assert { // Balance: 6000000, cargo: 440, tank: 40, distance: 13.4, jumps: 6 // Ithaca (Palladium to LHS 3262) -> Morgor -> LHS 3006 -> LHS 3262 (Consumer Technology to Ithaca) -> LHS 3006 -> Morgor -> Ithaca // Profit: 981200, avg: 490600, distance: 67.5, lands: 2 - Place ithaca = market.get().stream().filter((v)->v.getName().equals("Ithaca")).findFirst().get(); + Vendor ithaca = market.get().stream().filter((v)->v.getName().equals("Ithaca")).findFirst().get().get().iterator().next(); RouteSearcher searcher = new RouteSearcher(13.4, 40); - RouteGraph graph = new RouteGraph(ithaca, market.get(), 40, 13.4, true, 6); + RouteGraph graph = new RouteGraph(ithaca, market.getVendors(), 40, 13.4, true, 6); graph.setCargo(440); graph.setBalance(6000000); - List> epaths = graph.getPathsTo(ithaca, 10); + List> epaths = graph.getPathsTo(ithaca, 10); PathRoute expect = epaths.stream().map(p -> (PathRoute) p).findFirst().get(); - List apaths = searcher.getPaths(ithaca, ithaca, market.get(), 6, 6000000, 440, 10); + List apaths = searcher.getPaths(ithaca, ithaca, market.getVendors(), 6, 6000000, 440, 10); PathRoute actual = apaths.stream().findFirst().get(); assertTrue("Routes is different",expect.isRoute(actual)); @@ -47,27 +47,27 @@ public class RouteSearcherTest extends Assert { // Balance: 6000000, cargo: 440, tank: 40, distance: 13.6, jumps: 6 // Ithaca (Palladium to LHS 3262) -> Morgor -> LHS 3006 -> LHS 3262 (Consumer Technology to Ithaca) -> LHS 3006 -> Morgor -> Ithaca // Profit: 981200, avg: 490600, distance: 67.5, lands: 2 - Place ithaca = market.get().stream().filter((v)->v.getName().equals("Ithaca")).findFirst().get(); - Place lhs3262 = market.get().stream().filter((v)->v.getName().equals("LHS 3262")).findFirst().get(); + Vendor ithaca = market.get().stream().filter((v)->v.getName().equals("Ithaca")).findFirst().get().get().iterator().next(); + Vendor lhs3262 = market.get().stream().filter((v)->v.getName().equals("LHS 3262")).findFirst().get().get().iterator().next(); RouteSearcher searcher = new RouteSearcher(13.6, 40); - RouteGraph graph = new RouteGraph(ithaca, market.get(), 40, 13.6, true, 6); + RouteGraph graph = new RouteGraph(ithaca, market.getVendors(), 40, 13.6, true, 6); graph.setCargo(440); graph.setBalance(6000000); - List> epaths = graph.getPathsTo(ithaca, 10); + List> epaths = graph.getPathsTo(ithaca, 10); PathRoute expect = epaths.stream().map(p -> (PathRoute) p).findFirst().get(); - List apaths = searcher.getPaths(ithaca, ithaca, market.get(), 6, 6000000, 440, 10); + List apaths = searcher.getPaths(ithaca, ithaca, market.getVendors(), 6, 6000000, 440, 10); PathRoute actual = apaths.stream().findFirst().get(); assertTrue("Routes is different",expect.isRoute(actual)); - graph = new RouteGraph(lhs3262, market.get(), 40, 13.6, true, 6); + graph = new RouteGraph(lhs3262, market.getVendors(), 40, 13.6, true, 6); graph.setCargo(440); graph.setBalance(6000000); expect = graph.getPathsTo(lhs3262, 10).stream().map(p -> (PathRoute)p).findFirst().get(); - apaths = searcher.getPaths(lhs3262, lhs3262, market.get(), 6, 6000000, 440, 10); + apaths = searcher.getPaths(lhs3262, lhs3262, market.getVendors(), 6, 6000000, 440, 10); actual = apaths.stream().findFirst().get(); assertEquals("Routes is different",expect.getAvgProfit(), actual.getAvgProfit(), 0.00001);