Skip to content

Commit

Permalink
introduce GridPathFinder & make it the locus of accessible cell querying
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaspayette committed Nov 16, 2024
1 parent 1876528 commit 964a156
Showing 15 changed files with 336 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -23,13 +23,12 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import sim.util.Int2D;
import uk.ac.ox.poseidon.agents.vessels.Vessel;
import uk.ac.ox.poseidon.agents.vessels.VesselScopeFactory;
import uk.ac.ox.poseidon.core.Factory;
import uk.ac.ox.poseidon.core.Simulation;
import uk.ac.ox.poseidon.geography.bathymetry.BathymetricGrid;
import uk.ac.ox.poseidon.geography.paths.PathFinder;
import uk.ac.ox.poseidon.geography.paths.GridPathFinder;

@Getter
@Setter
@@ -39,18 +38,16 @@ public class RandomDestinationSupplierFactory
extends VesselScopeFactory<DestinationSupplier> {

private Factory<? extends BathymetricGrid> bathymetricGrid;
private Factory<? extends PathFinder<Int2D>> pathFinder;
private Factory<? extends GridPathFinder> pathFinder;

@Override
protected DestinationSupplier newInstance(
final Simulation simulation,
final Vessel vessel
) {
final PathFinder<Int2D> pathFinder = this.pathFinder.get(simulation);
final GridPathFinder pathFinder = this.pathFinder.get(simulation);
return new RandomDestinationSupplier(
bathymetricGrid
.get(simulation)
.getAccessibleCells(vessel.getCurrentCell(), pathFinder),
pathFinder.getAccessibleWaterCells(vessel.getCurrentCell()),
simulation.random
);
}
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@
import uk.ac.ox.poseidon.core.Factory;
import uk.ac.ox.poseidon.core.Simulation;
import uk.ac.ox.poseidon.geography.bathymetry.BathymetricGrid;
import uk.ac.ox.poseidon.geography.paths.PathFinder;
import uk.ac.ox.poseidon.geography.paths.GridPathFinder;

@Getter
@Setter
@@ -40,17 +40,15 @@
public class RandomGridExplorerFactory extends VesselScopeFactory<Explorer<Int2D>> {

private Factory<? extends BathymetricGrid> bathymetricGrid;
private Factory<? extends PathFinder<Int2D>> pathFinder;
private Factory<? extends GridPathFinder> pathFinder;

@Override
protected Explorer<Int2D> newInstance(
final Simulation simulation,
final Vessel vessel
) {
return new RandomExplorer<>(
bathymetricGrid
.get(simulation)
.getAccessibleCells(vessel.getCurrentCell(), pathFinder.get(simulation)),
pathFinder.get(simulation).getAccessibleWaterCells(vessel.getCurrentCell()),
simulation.random
);
}
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
import lombok.Data;
import uk.ac.ox.poseidon.biology.Content;

import static com.google.common.base.Preconditions.checkArgument;
import static uk.ac.ox.poseidon.core.utils.Preconditions.checkNonNegative;

@Data
public class Biomass implements Content<Biomass> {
@@ -33,16 +33,15 @@ private Biomass(final double value) {
this.value = value;
}

public static Biomass of(final double value) {
checkArgument(value >= 0);
return new Biomass(value);
}

@Override
public Biomass add(final Biomass other) {
return of(value + other.value);
}

public static Biomass of(final double value) {
return new Biomass(checkNonNegative(value, "biomass"));
}

@Override
public Biomass subtract(final Biomass other) {
return of(value - other.value);
12 changes: 12 additions & 0 deletions core/src/main/java/uk/ac/ox/poseidon/core/utils/Preconditions.java
Original file line number Diff line number Diff line change
@@ -32,4 +32,16 @@ public static double checkUnitRange(
);
return value;
}

public static double checkNonNegative(
final double value,
final String name
) {
checkArgument(
value >= 0,
name + " must be not be negative but was " + value
);
return value;
}

}
Original file line number Diff line number Diff line change
@@ -22,7 +22,6 @@
import lombok.Getter;
import lombok.Setter;
import sim.engine.Steppable;
import sim.util.Int2D;
import uk.ac.ox.poseidon.agents.behaviours.BackToInitialBehaviourFactory;
import uk.ac.ox.poseidon.agents.behaviours.WaitBehaviourFactory;
import uk.ac.ox.poseidon.agents.behaviours.choices.ExponentialMovingAverageOptionValuesFactory;
@@ -56,7 +55,7 @@
import uk.ac.ox.poseidon.geography.grids.GridExtent;
import uk.ac.ox.poseidon.geography.grids.GridExtentFactory;
import uk.ac.ox.poseidon.geography.paths.DefaultPathFinderFactory;
import uk.ac.ox.poseidon.geography.paths.PathFinder;
import uk.ac.ox.poseidon.geography.paths.GridPathFinder;
import uk.ac.ox.poseidon.geography.ports.PortGrid;
import uk.ac.ox.poseidon.geography.ports.RandomLocationsPortGridFactory;
import uk.ac.ox.poseidon.geography.ports.SimplePortFactory;
@@ -108,7 +107,7 @@ public class BasicScenario extends Scenario {
2
);

private Factory<? extends PathFinder<Int2D>> pathFinder =
private Factory<? extends GridPathFinder> pathFinder =
new DefaultPathFinderFactory(
bathymetricGrid,
portGrid,
Original file line number Diff line number Diff line change
@@ -22,7 +22,6 @@
import sim.field.grid.DoubleGrid2D;
import sim.util.Int2D;
import uk.ac.ox.poseidon.geography.grids.NumberGrid;
import uk.ac.ox.poseidon.geography.paths.PathFinder;

import java.util.List;

@@ -46,16 +45,6 @@ default List<Int2D> getLandCells() {
.collect(toImmutableList());
}

default List<Int2D> getAccessibleCells(
final Int2D startingCell,
final PathFinder<Int2D> pathFinder
) {
return getWaterCells()
.stream()
.filter(cell -> pathFinder.getPath(startingCell, cell).isPresent())
.collect(toImmutableList());
}

default List<Int2D> getAllCells() {
return getGridExtent().getAllCells();
}
Original file line number Diff line number Diff line change
@@ -23,33 +23,37 @@
import com.badlogic.gdx.ai.pfa.Heuristic;
import com.badlogic.gdx.ai.pfa.indexed.IndexedAStarPathFinder;
import com.google.common.collect.ImmutableList;
import lombok.AllArgsConstructor;
import sim.util.Int2D;
import uk.ac.ox.poseidon.geography.bathymetry.BathymetricGrid;
import uk.ac.ox.poseidon.geography.distance.Distance;
import uk.ac.ox.poseidon.geography.ports.PortGrid;

import java.util.Optional;

@AllArgsConstructor
public class AStarPathFinder implements PathFinder<Int2D> {
public class AStarPathFinder extends AbstractGridPathFinder {

private final GridAdaptor gridAdaptor;
private final IndexedAStarPathFinder<Int2D> pathFinder;
private final Heuristic<Int2D> heuristic;

public AStarPathFinder(
final GridAdaptor gridAdaptor
final BathymetricGrid bathymetricGrid,
final PortGrid portGrid,
final Distance distance
) {
this(
gridAdaptor,
new IndexedAStarPathFinder<>(gridAdaptor),
(Int2D a, Int2D b) -> (float) gridAdaptor.getDistance().distanceBetween(a, b)
);
super(bathymetricGrid, portGrid);
this.gridAdaptor = new GridAdaptor(bathymetricGrid, portGrid, distance);
this.pathFinder = new IndexedAStarPathFinder<>(gridAdaptor);
this.heuristic = (Int2D a, Int2D b) ->
(float) gridAdaptor.getDistance().distanceBetween(a, b);
}

@Override
public Optional<ImmutableList<Int2D>> getPath(
final Int2D start,
final Int2D end
) {
if (!(isNavigable(start) && isNavigable(end))) return Optional.empty();
final DefaultGraphPath<Int2D> path = new DefaultGraphPath<>();
final boolean found =
pathFinder.searchNodePath(
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* POSEIDON: an agent-based model of fisheries
* Copyright (c) 2024 CoHESyS Lab cohesys.lab@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package uk.ac.ox.poseidon.geography.paths;

import lombok.RequiredArgsConstructor;
import sim.util.Int2D;
import uk.ac.ox.poseidon.geography.bathymetry.BathymetricGrid;
import uk.ac.ox.poseidon.geography.grids.GridExtent;
import uk.ac.ox.poseidon.geography.ports.PortGrid;

@RequiredArgsConstructor
abstract class AbstractGridPathFinder implements GridPathFinder {
private final BathymetricGrid bathymetricGrid;
private final PortGrid portGrid;

@Override
public boolean isNavigable(final Int2D cell) {
return isWater(cell) || portGrid.anyPortsAt(cell);
}

@Override
public boolean isWater(final Int2D cell) {
return bathymetricGrid.isWater(cell);
}

@Override
public GridExtent getGridExtent() {
return bathymetricGrid.getGridExtent();
}
}
Original file line number Diff line number Diff line change
@@ -23,24 +23,27 @@
import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.utils.Array;
import com.google.common.collect.ImmutableList;
import lombok.AllArgsConstructor;
import sim.util.Int2D;
import uk.ac.ox.poseidon.geography.bathymetry.BathymetricGrid;
import uk.ac.ox.poseidon.geography.ports.PortGrid;

import java.util.Optional;

@AllArgsConstructor
public class BresenhamPathFinder implements PathFinder<Int2D> {
public class BresenhamPathFinder extends AbstractGridPathFinder {

private final BathymetricGrid bathymetricGrid;
private final PortGrid portGrid;
public BresenhamPathFinder(
final BathymetricGrid bathymetricGrid,
final PortGrid portGrid
) {
super(bathymetricGrid, portGrid);
}

@Override
public Optional<ImmutableList<Int2D>> getPath(
final Int2D start,
final Int2D end
) {
if (!(isNavigable(start) && isNavigable(end))) return Optional.empty();
final Array<GridPoint2> linePoints = new Bresenham2().line(start.x, start.y, end.x, end.y);
final ImmutableList.Builder<Int2D> pathBuilder = ImmutableList.builder();
for (final GridPoint2 point : linePoints) {
@@ -52,7 +55,4 @@ public Optional<ImmutableList<Int2D>> getPath(
return Optional.of(pathBuilder.build());
}

private boolean isNavigable(final Int2D cell) {
return bathymetricGrid.isWater(cell) || portGrid.getPortsAt(cell).findAny().isPresent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* POSEIDON: an agent-based model of fisheries
* Copyright (c) 2024 CoHESyS Lab cohesys.lab@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package uk.ac.ox.poseidon.geography.paths;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import sim.util.Int2D;
import uk.ac.ox.poseidon.geography.grids.GridExtent;

import java.util.Map.Entry;

import static java.util.Map.entry;

public class CachingGridPathFinder extends CachingPathFinder<Int2D> implements GridPathFinder {

private final GridPathFinder pathFinder;
private final Interner<ImmutableList<Int2D>> cellListInterner = Interners.newStrongInterner();
private final LoadingCache<Int2D, ImmutableList<Int2D>> accessibleCells =
CacheBuilder.newBuilder().build(CacheLoader.from(this::computeAccessibleCells));

CachingGridPathFinder(
final GridPathFinder pathFinder,
final PathCache<Int2D> cache
) {
super(pathFinder, cache);
this.pathFinder = pathFinder;
}

private ImmutableList<Int2D> computeAccessibleCells(final Int2D startingCell) {
return pathFinder.getAccessibleWaterCells(startingCell);
}

@Override
public ImmutableList<Int2D> getAccessibleWaterCells(final Int2D startingCell) {
return cellListInterner.intern(accessibleCells.getUnchecked(startingCell));
}

private ImmutableList<Int2D> computeAccessibleNeighbours(
final Entry<Int2D, Integer> entry
) {
return getAccessibleWaterNeighbours(entry.getKey(), entry.getValue());
}

@Override
public ImmutableList<Int2D> getAccessibleWaterNeighbours(
final Int2D startingCell,
final int neighbourhoodSize
) {
return accessibleNeighbours.getUnchecked(entry(startingCell, neighbourhoodSize));
}

@Override
public GridExtent getGridExtent() {
return pathFinder.getGridExtent();
}

@Override
public boolean isWater(final Int2D cell) {
return pathFinder.isWater(cell);
}

@Override
public boolean isNavigable(final Int2D cell) {
return pathFinder.isNavigable(cell);
}

private final LoadingCache<Entry<Int2D, Integer>, ImmutableList<Int2D>> accessibleNeighbours =
CacheBuilder.newBuilder().build(CacheLoader.from(this::computeAccessibleNeighbours));

}
Loading

0 comments on commit 964a156

Please sign in to comment.