Archived
0

don't use frok join on build graph

fix multi thread search
This commit is contained in:
iMoHax
2015-07-30 11:58:47 +03:00
parent 011158edf1
commit d1ac962d3d
9 changed files with 263 additions and 169 deletions

View File

@@ -6,6 +6,9 @@ import ru.trader.analysis.graph.Traversal;
public interface RouteSpecification<T> {
public boolean specified(Edge<T> edge, Traversal<T> entry);
public default boolean updateSpecified(Edge<T> edge, Traversal<T> entry){
return specified(edge, entry);
}
default RouteSpecification<T> and(final RouteSpecification<T> other){
return (edge, entry) -> RouteSpecification.this.specified(edge, entry) && other.specified(edge, entry);

View File

@@ -7,7 +7,6 @@ import ru.trader.analysis.graph.*;
import ru.trader.core.*;
import java.util.*;
import java.util.concurrent.ForkJoinTask;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -62,7 +61,7 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
private void runDeferredTasks(){
deferredTasks.sort((b1,b2) -> Integer.compare(b2.getDeep(), b1.getDeep()));
for (VendorsGraphBuilder task : deferredTasks) {
ForkJoinTask.invokeAll(task);
task.compute();
}
deferredTasks.clear();
}
@@ -104,54 +103,45 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
}
@Override
protected void build() {
protected void compute() {
if (isAdding){
if (!vertex.locker().tryLock()){
throw new ConcurrentModificationException("Adding must do in single thread");
} else {
try {
addAlreadyCheckedEdges();
} finally {
vertex.locker().unlock();
}
}
addAlreadyCheckedEdges();
} else {
super.build();
super.compute();
}
}
@Override
protected double checkConnect(Vendor buyer) {
double nextlimit = super.checkConnect(buyer);
Vendor seller = vertex.getEntry();
if (nextlimit > 0){
protected BuildHelper<Vendor> createHelper(Vendor buyer) {
BuildHelper<Vendor> helper = super.createHelper(buyer);
if (helper.isConnected()){
Vendor seller = vertex.getEntry();
if (buyer instanceof TransitVendor && (deep == 0 || seller.getPlace().equals(buyer.getPlace()))){
LOG.trace("Buyer is transit of seller or is end, skipping");
nextlimit = -1;
return new BuildHelper<>(buyer, -1);
}
if (seller instanceof TransitVendor && seller.getPlace().equals(buyer.getPlace())){
LOG.trace("Seller is transit of buyer, skipping");
nextlimit = -1;
return new BuildHelper<>(buyer, -1);
}
}
return nextlimit;
return helper;
}
@Override
protected void connect(Vertex<Vendor> next, double nextLimit) {
BuildEdge e;
protected void connect(Edge<Vendor> edge) {
if (edge instanceof VendorsBuildEdge){
super.connect(edge);
}
}
@Override
protected BuildEdge createEdge(BuildHelper<Vendor> helper, Vertex<Vendor> next) {
BuildEdge cEdge = super.createEdge(helper, next);
if (next.getEntry() instanceof TransitVendor){
e = super.createEdge(next);
} else {
e = createEdge(next);
vertex.connect(e);
return cEdge;
}
addSubTask(e, nextLimit);
}
@Override
protected VendorsBuildEdge createEdge(Vertex<Vendor> target) {
BuildEdge cEdge = super.createEdge(target);
if (vertex.getEntry() instanceof TransitVendor){
addEdgesToHead(cEdge);
}
@@ -193,8 +183,8 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
private void addAlreadyCheckedEdges(){
LOG.trace("Adding already checked vertex");
vertex.getEdges().parallelStream().forEach(aEdge -> {
VendorsBuildEdge e = (VendorsBuildEdge) aEdge;
vertex.getEdges().parallelStream().forEach(edge -> {
VendorsBuildEdge e = (VendorsBuildEdge) edge;
if (callback.isCancel()) return;
Vendor entry = e.getTarget().getEntry();
LOG.trace("Check {}", entry);
@@ -247,25 +237,22 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
}
}
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 (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);
}
@Override
protected GraphBuilder createSubTask(Edge<Vendor> edge, Collection<Vendor> set, int deep, double limit) {
return new VendorsGraphBuilder(this, (BuildEdge) edge, set, deep, limit);
}
@Override
protected void addSubTask(Edge<Vendor> edge, double nextLimit) {
Vertex<Vendor> next = edge.getTarget();
if (next.getLevel() >= deep && next.getEntry() instanceof TransitVendor) {
if (deep > 0){
VendorsGraphBuilder task = new VendorsGraphBuilder(this, (BuildEdge) edge, set, deep - 1, nextLimit);
task.isAdding = true;
holdTask(task);
}
} else {
LOG.trace("Vertex {} already check", next);
}
super.addSubTask(edge, nextLimit);
}
}
@@ -533,7 +520,7 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
protected boolean check(Edge<Vendor> e){
VendorsBuildEdge edge = (VendorsBuildEdge) e;
return fuel <= edge.getMaxFuel() && (fuel >= edge.getMinFuel() || edge.getSource().getEntry().canRefill())
&& (edge.getProfit() > 0 || isFound(edge, this));
&& (edge.getProfit() > 0 || VendorsCrawler.this.isFound(edge, this));
}
protected VendorsEdge wrap(Edge<Vendor> e) {

View File

@@ -5,21 +5,17 @@ import org.slf4j.LoggerFactory;
import ru.trader.analysis.AnalysisCallBack;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
public abstract class AbstractGraph<T> implements Graph<T> {
private final static ForkJoinPool POOL = new ForkJoinPool();
//TODO: make it worked in multi thread
private final static int THRESHOLD = 1;
private final static Logger LOG = LoggerFactory.getLogger(AbstractGraph.class);
protected Vertex<T> root;
protected final List<Vertex<T>> vertexes;
protected final GraphCallBack callback;
protected int minJumps;
private final ReentrantLock lock = new ReentrantLock();
protected AbstractGraph() {
this(new AnalysisCallBack());
@@ -27,16 +23,17 @@ public abstract class AbstractGraph<T> implements Graph<T> {
protected AbstractGraph(AnalysisCallBack callback) {
this.callback = new GraphCallBack(callback);
vertexes = new CopyOnWriteArrayList<>();
vertexes = new ArrayList<>();
}
protected abstract RecursiveAction createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit);
protected abstract GraphBuilder createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit);
public void build(T start, Collection<T> set, int maxDeep, double limit) {
callback.startBuild(start);
minJumps = 1;
root = getInstance(start, maxDeep, maxDeep);
POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit));
GraphBuilder builder = createGraphBuilder(root, set, maxDeep - 1, limit);
builder.compute();
onEnd();
callback.endBuild();
}
@@ -49,21 +46,22 @@ public abstract class AbstractGraph<T> implements Graph<T> {
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 = newInstance(entry, vertexes.size());
vertex.setLevel(level);
vertexes.add(vertex);
int jumps = root != null ? root.getLevel() - deep : 0;
if (jumps > minJumps)
minJumps = jumps;
}
private Vertex<T> getInstance(T entry, int level, int deep){
Vertex<T> vertex = null;
lock.lock();
try {
vertex = getVertex(entry).orElse(null);
if (vertex == null){
LOG.trace("Is new vertex");
vertex = newInstance(entry, vertexes.size());
vertexes.add(vertex);
vertex.setLevel(level);
int jumps = root != null ? root.getLevel() - deep : 0;
if (jumps > minJumps)
minJumps = jumps;
}
} finally {
lock.unlock();
}
return vertex;
}
@@ -103,8 +101,7 @@ public abstract class AbstractGraph<T> implements Graph<T> {
return vertexes.size();
}
protected abstract class GraphBuilder extends RecursiveAction {
protected final List<RecursiveAction> subTasks = new ArrayList<>(THRESHOLD);
protected abstract class GraphBuilder {
protected final Vertex<T> vertex;
protected final Collection<T> set;
protected final int deep;
@@ -117,70 +114,89 @@ public abstract class AbstractGraph<T> implements Graph<T> {
this.limit = limit;
}
protected abstract double checkConnect(T entry);
protected abstract Edge<T> createEdge(Vertex<T> target);
protected RecursiveAction createSubTask(Vertex<T> vertex, Collection<T> set, int deep, double limit){
return createGraphBuilder(vertex, set, deep, limit);
protected BuildHelper<T> createHelper(final T entry){
return new BuildHelper<>(entry, limit);
}
protected abstract Edge<T> createEdge(BuildHelper<T> helper, Vertex<T> target);
protected void connect(Edge<T> edge){
vertex.connect(edge);
}
protected GraphBuilder createSubTask(Edge<T> edge, Collection<T> set, int deep, double limit){
return createGraphBuilder(edge.getTarget(), set, deep, limit);
}
@Override
protected final void compute() {
vertex.locker().lock();
try {
if (vertex.getLevel() <= deep){
vertex.setLevel(deep+1);
protected void compute() {
List<BuildHelper<T>> helpers;
if (vertex.getLevel() <= deep){
vertex.setLevel(deep+1);
} else {
if (vertex.getLevel() > deep+1){
LOG.trace("Already build");
return;
}
} finally {
vertex.locker().unlock();
}
build();
helpers = build();
runSubTasks(helpers);
}
protected void build(){
private List<BuildHelper<T>> build(){
LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep);
for (T entry : set) {
if (callback.isCancel()) break;
if (entry == vertex.getEntry()) continue;
double nextLimit = checkConnect(entry);
if (nextLimit >= 0) {
LOG.trace("Connect {} to {}", vertex, entry);
Vertex<T> next = getInstance(entry, 0, deep);
connect(next, nextLimit);
} else {
LOG.trace("Vertex {} is far away", entry);
}
if (subTasks.size() >= THRESHOLD) {
joinSubTasks();
}
}
if (!subTasks.isEmpty()){
joinSubTasks();
}
List<BuildHelper<T>> helpers = set.parallelStream()
.filter(entry -> entry != vertex.getEntry())
.map(this::createHelper)
.filter(BuildHelper::isConnected)
.collect(Collectors.toList());
helpers.parallelStream().forEach(this::connect);
LOG.trace("End build graph from {} on deep {}", vertex, deep);
return helpers;
}
protected void connect(Vertex<T> next, double nextLimit){
vertex.connect(createEdge(next));
private void connect(final BuildHelper<T> helper){
LOG.trace("Connect {} to {}", vertex, helper.entry);
Vertex<T> next = getInstance(helper.entry, 0, deep);
Edge<T> edge = createEdge(helper, next);
helper.setEdge(edge);
connect(edge);
}
private void runSubTasks(final List<BuildHelper<T>> helpers){
LOG.trace("Build sub graph from {}, limit {}, deep {}", vertex, limit, deep);
for (BuildHelper<T> helper : helpers) {
if (callback.isCancel()) break;
addSubTask(helper.edge, helper.nextLimit);
}
LOG.trace("End build sub graph from {}, limit {}, deep {}", vertex, limit, deep);
}
protected void addSubTask(Edge<T> edge, double nextLimit){
Vertex<T> next = edge.getTarget();
if (next.getLevel() < deep) {
if (deep > 0) {
//Recursive build
RecursiveAction task = createSubTask(next, set, deep - 1, nextLimit);
task.fork();
subTasks.add(task);
GraphBuilder task = createSubTask(edge, set, deep - 1, nextLimit);
task.compute();
}
}
}
}
protected void joinSubTasks(){
for (RecursiveAction subTask : subTasks) {
if (callback.isCancel()){
subTask.cancel(true);
} else {
subTask.join();
}
}
subTasks.clear();
protected class BuildHelper<T> {
private final T entry;
private final double nextLimit;
private Edge<T> edge;
public BuildHelper(T entry, double nextLimit) {
this.entry = entry;
this.nextLimit = nextLimit;
this.edge = null;
}
private void setEdge(Edge<T> edge) {
this.edge = edge;
}
public boolean isConnected(){
return nextLimit >= 0;
}
}
}

View File

@@ -42,33 +42,32 @@ public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T>
}
protected class ConnectibleGraphBuilder extends GraphBuilder {
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);
}
@Override
protected double checkConnect(T entry) {
distance = vertex.getEntry().getDistance(entry);
protected BuildHelper<T> createHelper(T entry) {
double distance = vertex.getEntry().getDistance(entry);
if (distance > getShip().getMaxJumpRange()){
LOG.trace("Vertex {} is far away, {}", entry, distance);
return -1;
return new BuildHelper<>(entry,-1);
}
maxFuel = getShip().getMaxFuel(distance);
minFuel = getShip().getMinFuel(distance);
double maxFuel = getShip().getMaxFuel(distance);
double 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;
double nextLimit = getProfile().withRefill() ? fuel - fuelCost : fuel;
return new CBuildHelper<>(entry, nextLimit, minFuel, maxFuel, distance);
}
@Override
protected BuildEdge createEdge(Vertex<T> target) {
protected BuildEdge createEdge(BuildHelper<T> helper, Vertex<T> target) {
CBuildHelper h = (CBuildHelper) helper;
BuildEdge res = new BuildEdge(vertex, target);
res.setFuel(minFuel, maxFuel);
res.setDistance(distance);
res.setFuel(h.minFuel, h.maxFuel);
res.setDistance(h.distance);
return res;
}
}
@@ -132,4 +131,19 @@ public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T>
}
}
public class CBuildHelper<T> extends BuildHelper<T> {
private final double minFuel;
private final double maxFuel;
private final double distance;
private CBuildHelper(T entry, double nextLimit, double minFuel, double maxFuel, double distance) {
super(entry, nextLimit);
this.minFuel = minFuel;
this.maxFuel = maxFuel;
this.distance = distance;
}
}
}

View File

@@ -60,7 +60,11 @@ public class Crawler<T> {
}
protected boolean isFound(Edge<T> edge, Traversal<T> head){
return specification.specified(edge, head);
return isFound(edge, head, false);
}
private boolean isFound(Edge<T> edge, Traversal<T> head, boolean updateStates){
return updateStates ? specification.updateSpecified(edge, head) : specification.specified(edge, head);
}
public int getMaxSize() {
@@ -100,7 +104,7 @@ public class Crawler<T> {
if (maxDeep < 0) maxDeep = 0;
found = bfs(start(s), maxDeep, count);
} else {
found = dfs(start(s), Math.min(t.get().getLevel() + 1, s.getLevel()), count);
found = dfs(start(s), t.get().getLevel() + 1, count);
}
}
LOG.debug("Found {} paths", found);
@@ -158,7 +162,7 @@ public class Crawler<T> {
boolean stop = false;
if (deep == source.getLevel()){
for (Edge<T> next : entry.getEdges()) {
if (isFound(next, entry)){
if (isFound(next, entry, true)){
List<Edge<T>> res = getCopyList(entry, next);
LOG.debug("Last edge found, path {}", res);
found++;
@@ -170,9 +174,13 @@ public class Crawler<T> {
}
}
if (!stop && found < count){
if (deep <= source.getLevel() && entry.size() < maxSize-1) {
if (deep < source.getLevel() && entry.size() < maxSize-1) {
LOG.trace("Search around");
for (Edge<T> edge : entry.getEdges()) {
if (entry.isSkipped()){
LOG.trace("Is skipped");
break;
}
if (edge.getTarget().isSingle()) continue;
found += dfs(travers(entry, edge), deep, count-found);
if (found >= count) break;
@@ -190,6 +198,10 @@ public class Crawler<T> {
queue.add(root);
while (!queue.isEmpty() && count > found){
CostTraversalEntry entry = queue.poll();
if (entry.isSkipped()){
LOG.trace("Is skipped");
continue;
}
Vertex<T> source = entry.vertex;
if (entry.size() >= maxSize){
LOG.trace("Is limit deep");
@@ -199,7 +211,7 @@ public class Crawler<T> {
Iterator<Edge<T>> iterator = entry.iterator();
while (iterator.hasNext()){
Edge<T> edge = iterator.next();
if (isFound(edge, entry)){
if (isFound(edge, entry, true)){
List<Edge<T>> res = getCopyList(entry, edge);
LOG.debug("Last edge found, path {}", res);
found++;
@@ -207,6 +219,10 @@ public class Crawler<T> {
break;
}
}
if (entry.isSkipped()){
LOG.trace("Is skipped");
break;
}
if (found >= count) break;
if (edge.getTarget().isSingle()) continue;
if (deep < source.getLevel()) {
@@ -226,10 +242,14 @@ public class Crawler<T> {
queue.add(root);
while (!queue.isEmpty() && count > found){
CostTraversalEntry entry = queue.poll();
if (entry.isSkipped()){
LOG.trace("Is skipped");
continue;
}
LOG.trace("Check path entry {}, weight {}", entry, entry.weight);
Edge<T> edge = entry.getEdge();
if (edge != null) {
if (isFound(edge, entry)) {
if (isFound(edge, entry, true)) {
List<Edge<T>> res = entry.toEdges();
LOG.debug("Path found {}", res);
found++;
@@ -238,6 +258,10 @@ public class Crawler<T> {
}
if (found >= count) break;
}
if (entry.isSkipped()){
LOG.trace("Is skipped");
continue;
}
if (edge.getTarget().isSingle()){
continue;
}
@@ -288,6 +312,10 @@ public class Crawler<T> {
if (found >= count) break;
CTEntrySupport next = targetsQueue.peek();
limit = next != null ? next.entry.getWeight() : Double.NaN;
if (deep > entry.getTarget().getLevel() || entry.size() >= maxSize){
LOG.trace("Is limit deep");
continue;
}
}
if (alreadyFound + found < count){
LOG.trace("Continue search, limit {}", limit);
@@ -295,10 +323,6 @@ public class Crawler<T> {
LOG.trace("Already {} found, extracting", alreadyFound);
continue;
}
if (deep >= entry.getTarget().getLevel() || entry.size() >= maxSize){
LOG.trace("Is limit deep");
continue;
}
DFS task = new DFS(curr, deep, count - found, limit);
POOL.invoke(task);
targetsQueue.addAll(task.getTargets());
@@ -371,7 +395,8 @@ public class Crawler<T> {
}
private class DFS extends RecursiveAction {
private final CTEntrySupport root;
private CTEntrySupport root;
private CTEntrySupport curr;
private final int count;
private final int deep;
private final Collection<CTEntrySupport> queue;
@@ -411,14 +436,52 @@ public class Crawler<T> {
return queue;
}
private boolean cancel(){
if (isCancelled()) return true;
if (root.entry.isSkipped()){
LOG.trace("Root skipped");
if (isSubTask){
LOG.trace("Stop sub task");
return true;
} else {
curr = root;
}
}
return false;
}
private boolean skip(){
if (curr.entry.isSkipped()){
while (curr.entry.isSkipped()){
LOG.trace("Is skipped, return to prev level");
if (!levelUp()) break;
}
return true;
}
return false;
}
private boolean levelUp(){
if (isRoot(curr)) return false;
assert curr.parent != null;
LOG.trace("Return to prev level");
if (curr == root){
root = root.parent;
}
curr = curr.parent;
return true;
}
private void search(){
CTEntrySupport curr = root;
curr = root;
LOG.trace("Start {}", root);
while (curr.hasNext()){
if (cancel()) break;
Edge<T> edge = curr.next();
CostTraversalEntry entry = curr.entry;
if (skip()) continue;
LOG.trace("Check edge {}, entry {}, weight {}", edge, entry, entry.weight);
boolean isTarget = isFound(edge, entry);
boolean isTarget = isFound(edge, entry, true);
boolean canDeep = !entry.getTarget().isSingle() && deep < entry.getTarget().getLevel() && entry.size() < maxSize-1;
if (canDeep || isTarget){
CostTraversalEntry nextEntry = travers(entry, edge);
@@ -430,8 +493,9 @@ public class Crawler<T> {
LOG.trace("Found, add entry {} to queue", nextEntry);
targets.add(curr);
limit = Double.isNaN(limit) ? nextEntry.getWeight() : Math.min(limit, nextEntry.getWeight());
curr = curr.parent;
levelUp();
} else {
if (skip()) continue;
if (!Double.isNaN(limit) && nextEntry.getWeight() >= limit){
if (targets.size() < count){
LOG.trace("Not found, limit {}, add entry {} to queue", limit, nextEntry);
@@ -439,24 +503,22 @@ public class Crawler<T> {
} else {
LOG.trace("Not found, limit {}, don't add entry {} to queue", limit, nextEntry);
}
if (!isRoot(curr.parent)){
curr = curr.parent.parent;
} else {
levelUp();
if (!levelUp()){
break;
}
} else {
if (!isRoot(curr) && maxSize-nextEntry.size() < SPLIT_SIZE){
if (addSubTask(curr))
curr = curr.parent;
levelUp();
}
}
}
} else {
LOG.trace("Is limit deep");
}
while (!curr.hasNext() && !isRoot(curr)){
LOG.trace("Level complete, return to prev level");
curr = curr.parent;
while (!curr.hasNext() && levelUp()){
LOG.trace("Level complete");
}
}
LOG.trace("Done {}", root);
@@ -503,6 +565,7 @@ public class Crawler<T> {
private final Edge<T> edge;
private List<Edge<T>> edges;
private Integer size;
private transient boolean skipped;
protected TraversalEntry(Vertex<T> vertex) {
this.vertex = vertex;
@@ -515,6 +578,7 @@ public class Crawler<T> {
this.head = head;
this.vertex = edge.getTarget();
this.edge = edge;
this.skipped = head.isSkipped();
edges = null;
}
@@ -550,6 +614,16 @@ public class Crawler<T> {
getEdges().sort(Comparator.<Edge<T>>naturalOrder());
}
@Override
public void setSkipped(boolean skipped) {
this.skipped = skipped;
}
@Override
public boolean isSkipped() {
return skipped;
}
protected List<Edge<T>> collect(Collection<Edge<T>> src){
return new ArrayList<>(src);
}

View File

@@ -9,6 +9,8 @@ public interface Traversal<T> {
List<Edge<T>> getEdges();
Iterator<Edge<T>> iterator();
void sort();
void setSkipped(boolean skipped);
boolean isSkipped();
default boolean isConnect(T target){
Edge<T> edge = getEdge();

View File

@@ -10,7 +10,7 @@ public class Vertex<T> {
private final T entry;
private final int index;
private final ReentrantLock lock = new ReentrantLock();
private volatile int level = -1;
private int level = -1;
public Vertex(T entry, int index) {
this.entry = entry;
@@ -37,16 +37,15 @@ public class Vertex<T> {
return level;
}
public ReentrantLock locker(){
return lock;
}
public void connect(Edge<T> edge){
assert this == edge.getSource();
synchronized (edges){
lock.lock();
try {
if (!edges.contains(edge)){
edges.add(edge);
}
} finally {
lock.unlock();
}
}

View File

@@ -248,13 +248,14 @@ public class Ship {
private double ladenJumpRange = Double.NaN;
private void fillFuelTable(){
double fuel = getEngine().getMaxFuel();
fuelTable = new FuelHelper[(int) (fuel/FUEL_TABLE_STEP)];
FuelHelper[] fuelTable = new FuelHelper[(int) (fuel/FUEL_TABLE_STEP)];
maxJumpRange = Double.NaN; ladenJumpRange = Double.NaN;
for (int i = fuelTable.length - 1; i >= 0; i--) {
double distance = getJumpRange(fuel);
fuelTable[i] = new FuelHelper(distance, fuel);
fuel = fuel - FUEL_TABLE_STEP;
}
this.fuelTable = fuelTable;
}
public double getMaxFuel(double distance){

View File

@@ -118,7 +118,7 @@ public class CrawlerTest extends Assert {
paths.clear();
crawler.findMin(x4, 20);
TestUtil.assertPaths(true, paths.get(), PPath.of(x5, x4), PPath.of(x5, x3, x4), PPath.of(x5, x6, x4),
TestUtil.assertPaths(paths.get(), PPath.of(x5, x4), PPath.of(x5, x3, x4), PPath.of(x5, x6, x4),
PPath.of(x5, x6, x5, x4), PPath.of(x5, x4, x5, x4), PPath.of(x5, x4, x3, x4),
PPath.of(x5, x4, x6, x4), PPath.of(x5, x6, x3, x4),
PPath.of(x5, x3, x5, x4), PPath.of(x5, x4, x2, x4),
@@ -240,7 +240,8 @@ public class CrawlerTest extends Assert {
paths.clear();
crawler.findFast(x2);
TestUtil.assertPaths(paths.get(), PPath.of(x5, x3, x2));
assertNotNull(paths.get());
assertEquals(2, paths.get().get(0).size());
paths.clear();
}
@@ -277,9 +278,6 @@ public class CrawlerTest extends Assert {
TestUtil.assertPaths(paths.get(), PPath.of(x6, x4, x2));
paths.clear();
crawler.findFast(x6, x2);
TestUtil.assertPaths(paths.get(), PPath.of(x6, x4, x2));
paths.clear();
}
@After