Skip to content

Commit

Permalink
Fix build errors under GCC
Browse files Browse the repository at this point in the history
Various changes made to fix errors and
warnings when building using GCC.
These changes included qualifying
base class template member types and
functions, eliminating static constexpr
variables in functions, and fixing
typos in unused templates.

Also updated readme and analysis
scripts to acknowledge GCC builds.
  • Loading branch information
rhysgoldstein committed Jan 31, 2024
1 parent bbe2e93 commit 7f29ca2
Show file tree
Hide file tree
Showing 24 changed files with 192 additions and 38 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,6 @@ FodyWeavers.xsd

# JetBrains Rider
*.sln.iml

# Visual Studio Code directory
.vscode
8 changes: 4 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#######################################
# CMake File for the Central64 Examples
#######################################
#########################################################
# CMake File for the Central64 Examples and Analysis Code
#########################################################
cmake_minimum_required(VERSION 3.10)
project(Central64)

Expand All @@ -13,7 +13,7 @@ if(MSVC)
string(REGEX REPLACE "/Z[iI7]" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Z7 /bigobj")
else()
set(WARNING_FLAGS "-Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces -Wno-ignored-attributes -Wignored-qualifiers -Woverloaded-virtual -Winline")
set(WARNING_FLAGS "-Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces -Wno-ignored-attributes -Wignored-qualifiers -Woverloaded-virtual")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -pedantic-errors -std=c++${CMAKE_CXX_STANDARD} ${WARNING_FLAGS}")
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS}")
endif(MSVC)
Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The Central64 library is intended to be fast and flexible. All combinations of t
- *Search Method:* Choose between **A\* Search**, **Jump Point Search**, **Bounded Jump Point Search**, **Mixed A\***, and **Mixed Jump Point Search**.
- *Smoothing Method:* Choose between **No Smoothing**, **Greedy Smoothing**, and **Tentpole Smoothing**.

The library was also designed to support an empirical comparison of these path planning techniques. Based on the results of the study, **16-Neighbor Bounded Jump Point Search with Tentpole Smoothing** is recommended as the combined method that provides the best overall balance of quality and speed. See the [Central64 Technical Report](report/00-index.md) ([PDF](report/central64-technical-report.pdf)) for details.
The library was also designed to support an empirical comparison of these path planning techniques. Based on the results of the study, **16-Neighbor Central Bounded Jump Point Search with Tentpole Smoothing** is recommended as the combined method that provides the best overall balance of quality and speed. See the [Central64 Technical Report](report/00-index.md) ([PDF](report/central64-technical-report.pdf)) for details.

## How to Use

Expand Down Expand Up @@ -199,7 +199,9 @@ The following instructions regenerate the PDF version of the report using Visual

### Build Instructions

Building the Central64 project is necessary only to run the examples or analyze the various methods. Otherwise, simply include the header files in your project as outlined in the [How to Use](#how-to-use) section. Below are instructions for building the examples and analysis code using CMake and Visual Studio 2019:
Building the Central64 project is necessary only to run the examples or analyze the various methods. Otherwise, simply include the header files in your project as outlined in the [How to Use](#how-to-use) section.

Below are instructions for building the examples and analysis code using [CMake](https://cmake.org/) and Visual Studio 2019:

* Open a command prompt and go to the root directory of the project.
* `mkdir build`
Expand All @@ -209,6 +211,15 @@ Building the Central64 project is necessary only to run the examples or analyze
* Build the solution.
* Run `Debug/Central64Examples.exe` or `Release/Central64Examples.exe` to test. The output should match [results/examples.txt](results/examples.txt).

Below are instructions for building the examples and analysis code using [CMake](https://cmake.org/), [Make](https://www.gnu.org/software/make/), and GCC (using 64-bit [TDM-GCC](https://jmeubank.github.io/tdm-gcc/)) on Windows:

* Open a command prompt and go to the root directory of the project.
* `mkdir build`
* `cd build`
* `cmake -G "MinGW Makefiles" ..`
* `make`
* Run `Central64Examples.exe` to test. The output should match [results/examples.txt](results/examples.txt).

### Analysis Instructions

Below are additional instructions for analyzing the *Dragon Age: Origins* maps and scenarios [distributed by the Moving AI Lab](https://movingai.com/benchmarks/grids.html) with permission from [BioWare](https://www.bioware.com/). Other Moving AI Lab benchmarks can be analyzed in a similar manner.
Expand Down
1 change: 0 additions & 1 deletion analysis/AnalyzeBenchmarks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ BenchmarkResult AnalyzeBenchmark(const std::filesystem::path& mapFilePath,
int validPaths = 0;
int totalScenarios = 0;
for (const auto& scen : scenarios) {
auto tA = std::chrono::steady_clock::now();
if (totalScenarios%25 == 0) {
auto tA = std::chrono::steady_clock::now();
planner.SearchAllNodes(scen.first);
Expand Down
4 changes: 2 additions & 2 deletions include/central64/grid/Array2D.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ class Array2D

void Fill(const T& value); ///< Set every element to `value`.

Offset2D Dims() const { return dims_; } ///< Get the dimensions.
Offset2D Size() const { return dims_.X()*dims_.Y(); } ///< Get the total number of elements.
Offset2D Dims() const { return dims_; } ///< Get the dimensions.
int Size() const { return dims_.X()*dims_.Y(); } ///< Get the total number of elements.

bool Contains(Offset2D coords) const; ///< Check whether coordinates `coords` are within the array.

Expand Down
4 changes: 4 additions & 0 deletions include/central64/grid/Grid2D.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class Grid2D
Offset2D Dims() const { return gridGraph_.Dims(); } ///< Get the number of vertices in each dimension.
CellAlignment Alignment() const { return alignment_; } ///< Get the cell alignment.

const Array2D<bool>& CenterCells() { return centerCells_; } ///< Obtain a const reference to the array of center-aligned cells.
const Array2D<bool>& CornerCells() { return cornerCells_; } ///< Obtain a const reference to the array of corner-aligned cells.
const Array2D<Connections<L>>& GridGraph() { return gridGraph_; } ///< Obtain a const reference to the array of sets of connections.

bool CenterCell(Offset2D coords) const { return centerCells_[coords]; } ///< Check whether the center-aligned cell at coordinates `coords` is obstructed.
bool CornerCell(Offset2D coords) const { return cornerCells_[coords]; } ///< Check whether the corner-aligned cell at coordinates `coords` is obstructed.

Expand Down
16 changes: 8 additions & 8 deletions include/central64/grid/Neighborhood.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ namespace central64 {
template <int L>
class Neighborhood
{
// Permit L == 128 to allow Neighborhood<2*L>::Move to be a friend of Neighborhood<L>::Move with L == 64.
static_assert(L == 4 || L == 8 || L == 16 || L == 32 || L == 64 || L == 128, "Neighborhood size must be 4, 8, 16, 32, or 64.");
static_assert(L == 4 || L == 8 || L == 16 || L == 32 || L == 64, "Neighborhood size must be 4, 8, 16, 32, or 64.");

public:
class Move;
Expand All @@ -29,8 +28,12 @@ class Neighborhood
Neighborhood() = delete;

struct MoveArray;
static constexpr std::array<Move, L> moves = MoveArray::data;
};

/// A specialization allowing Neighborhood<128>::Move to be a friend of Neighborhood<64>::Move.
template <> class Neighborhood<128> { class Move; };

/// A data type representing one of the `L` neighborhood moves.
template <int L>
class Neighborhood<L>::Move
Expand Down Expand Up @@ -95,9 +98,9 @@ constexpr Neighborhood<L>::Move::Move(int index)
else {
// Compute the 2D offset by finding the corresponding move from the previous neighborhood (even moves),
// or by adding the 2D offsets of the two surrounding moves from the previous neighborhood (odd moves).
offset_ = (index%2 == 0) ? Neighborhood<L/2>::Move(index_/2).Offset() :
Neighborhood<L/2>::Move(index_/2).Offset() +
Neighborhood<L/2>::Move(index_/2 + 1).Offset();
offset_ = (index%2 == 0) ? typename Neighborhood<L/2>::Move(index_/2).Offset() :
typename Neighborhood<L/2>::Move(index_/2).Offset() +
typename Neighborhood<L/2>::Move(index_/2 + 1).Offset();
// Select the Euclidean cost at compile time from a list of precomputed square root values.
const int distanceSquared = offset_.X()*offset_.X() + offset_.Y()*offset_.Y();
cost_ = PathCost{ (distanceSquared == 1) ? 1.0 :
Expand All @@ -115,9 +118,6 @@ constexpr Neighborhood<L>::Move::Move(int index)
template <int L>
constexpr const std::array<typename Neighborhood<L>::Move, L>& Neighborhood<L>::Moves()
{
// Instantiate the moves at compile time.
static constexpr std::array<typename Neighborhood<L>::Move, L> moves = MoveArray::data;

return moves;
}

Expand Down
2 changes: 1 addition & 1 deletion include/central64/grid/PathCost.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class PathCost
constexpr const PathCost& operator-=(PathCost rhs) { *this = *this - rhs; return *this; } ///< Subtract the path cost on the right-hand side.
constexpr const PathCost& operator*=(int rhs) { *this = *this * rhs; return *this; } ///< Multiply by the integer on the right-hand side.

constexpr const double operator/(PathCost rhs) const { return double(multiplier_)/double(rhs.multiplier_); } ///< Compute the ratio between two path costs.
constexpr double operator/(PathCost rhs) const { return double(multiplier_)/double(rhs.multiplier_); } ///< Compute the ratio between two path costs.

constexpr bool operator==(PathCost rhs) const { return multiplier_ == rhs.multiplier_; } ///< Check whether two path costs are equal.
constexpr bool operator!=(PathCost rhs) const { return multiplier_ != rhs.multiplier_; } ///< Check whether two path costs are different.
Expand Down
2 changes: 1 addition & 1 deletion include/central64/grid/PathCount.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class PathCount
constexpr const PathCount& operator+=(PathCount rhs) { *this = *this + rhs; return *this; } ///< Add the path count on the right-hand side.
constexpr const PathCount& operator*=(PathCount rhs) { *this = *this * rhs; return *this; } ///< Multiply by the path count on the right-hand side.

const double operator/(PathCount rhs) const { return pow(2.0, logCount_ - rhs.logCount_); } ///< Compute the ratio between two path counts.
double operator/(PathCount rhs) const { return pow(2.0, logCount_ - rhs.logCount_); } ///< Compute the ratio between two path counts.

constexpr bool operator==(PathCount rhs) const { return logCount_ == rhs.logCount_; } ///< Check whether two path counts are equal.
constexpr bool operator!=(PathCount rhs) const { return logCount_ != rhs.logCount_; } ///< Check whether two path counts are different.
Expand Down
1 change: 1 addition & 0 deletions include/central64/grid/Types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <string>
#include <charconv>
#include <memory>
#include <algorithm>

namespace central64 {

Expand Down
21 changes: 20 additions & 1 deletion include/central64/search/AStarSearch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,36 @@ template <int L>
class AStarSearch : public BasicSearch<L>
{
public:
explicit AStarSearch(const Grid2D<L>& grid) : BasicSearch{ grid } {} ///< Create an A* search object that references an existing grid object.
explicit AStarSearch(const Grid2D<L>& grid) : BasicSearch<L>{ grid } {} ///< Create an A* search object that references an existing grid object.

AStarSearch(AStarSearch&&) = default; ///< Ensure the default move constructor is public.
AStarSearch& operator=(AStarSearch&&) = default; ///< Ensure the default move assignment operator is public.

std::string MethodName() const { return "A* Search"; } ///< Get the name of the search method.
std::string AllNodesMethodName() const { return "Dijkstra Search"; } ///< Get the name of the all-nodes version of the search method.

const Grid2D<L>& Grid() const { return BasicSearch<L>::Grid(); }
bool IsAllNodesSearch() const { return BasicSearch<L>::IsAllNodesSearch(); }
Offset2D SourceCoords() const { return BasicSearch<L>::SourceCoords(); }
Offset2D SampleCoords() const { return BasicSearch<L>::SampleCoords(); }
bool Centralize() const { return BasicSearch<L>::Centralize(); }
bool FromSource() const { return BasicSearch<L>::FromSource(); }
PathTree<L>& Tree() const { return BasicSearch<L>::Tree(); }
PathFlow<L>& Flow() const { return BasicSearch<L>::Flow(); }

private:
// Process the node at coordinates `coords`, given its f-cost `fCost`.
bool ProcessSearchNode(Offset2D coords, PathCost fCost);

bool IsSearchNodeInitialized(Offset2D coords) const { return BasicSearch<L>::IsSearchNodeInitialized(coords); }
void InitializeDijkstraNode(Offset2D coords) { BasicSearch<L>::InitializeDijkstraNode(coords); }
void InitializeHeuristicNode(Offset2D coords) { BasicSearch<L>::InitializeHeuristicNode(coords); }
using DijkstraQueue = typename BasicSearch<L>::DijkstraQueue;
using HeuristicQueue = typename BasicSearch<L>::HeuristicQueue;
DijkstraQueue CreateDijkstraQueue() { return BasicSearch<L>::CreateDijkstraQueue(); }
HeuristicQueue CreateHeuristicQueue() { return BasicSearch<L>::CreateHeuristicQueue(); }

void ExpandSearchNode(Offset2D coords, Connections<L> successors) { BasicSearch<L>::ExpandSearchNode(coords, successors); }
};

template <int L>
Expand Down
8 changes: 4 additions & 4 deletions include/central64/search/AbstractSearch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ class AbstractSearch
public:
virtual ~AbstractSearch() = default; ///< Ensure the destructor is virtual, since the class is to be inherited.

const Grid2D<L>& Grid() const { return grid_; } ///< Obtain a const reference to the grid.

/// Compute a path between source coordinates `sourceCoords` and sample coordinates `sampleCoords`.
/// If `centralize` is true, then compute a central grid path; otherwise compute a regular grid path.
/// If `fromSource` is true, then arrange the path from source to sample; otherwise from sample to source.
Expand All @@ -37,6 +35,8 @@ class AbstractSearch
bool centralize = true,
bool fromSource = true);

const Grid2D<L>& Grid() const { return grid_; } ///< Obtain a const reference to the grid.

bool IsAllNodesSearch() const { return Tree().IsAllNodesSearch(); } ///< Check if the current search is an all-nodes (e.g. Dijkstra) search.

Offset2D SourceCoords() const { return Tree().SourceCoords(); } ///< Get the coordinates of the current source vertex, the root of the shortest grid path tree.
Expand All @@ -57,6 +57,8 @@ class AbstractSearch
AbstractSearch(AbstractSearch&&) = default; ///< Ensure the default move constructor is protected because the class is to be inherited.
AbstractSearch& operator=(AbstractSearch&&) = default; ///< Ensure the default move assignment operator is protected because the class is to be inherited.

virtual void PerformSearch() = 0; ///< Perform the current path search, populating the shortest grid path tree.

bool IsSearchNodeInitialized(Offset2D coords) const { return Tree().IsSearchNodeInitialized(coords); } ///< Check whether the node at coordinates `coords` has been initialized for the current search.

void InitializeDijkstraNode(Offset2D coords) { Tree().InitializeDijkstraNode(coords); } ///< Initialize the node at coordinates `coords` for the current search, without computing the heuristic.
Expand All @@ -68,8 +70,6 @@ class AbstractSearch
DijkstraQueue CreateDijkstraQueue() { return Tree().CreateDijkstraQueue(); } ///< Create a Dijkstra queue associated with this path tree.
HeuristicQueue CreateHeuristicQueue() { return Tree().CreateHeuristicQueue(); } ///< Create a heuristic queue associated with this path tree.

virtual void PerformSearch() = 0; ///< Perform the current path search, populating the shortest grid path tree.

private:
AbstractSearch(const AbstractSearch&) = delete;
AbstractSearch& operator=(const AbstractSearch&) = delete;
Expand Down
21 changes: 19 additions & 2 deletions include/central64/search/BasicSearch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ class BasicSearch : public AbstractSearch<L>
public:
virtual ~BasicSearch() = default; ///< Ensure the destructor is virtual, since the class is to be inherited.

const Grid2D<L>& Grid() const { return AbstractSearch<L>::Grid(); }
bool IsAllNodesSearch() const { return AbstractSearch<L>::IsAllNodesSearch(); }
Offset2D SourceCoords() const { return AbstractSearch<L>::SourceCoords(); }
Offset2D SampleCoords() const { return AbstractSearch<L>::SampleCoords(); }
bool Centralize() const { return AbstractSearch<L>::Centralize(); }
bool FromSource() const { return AbstractSearch<L>::FromSource(); }
PathTree<L>& Tree() const { return AbstractSearch<L>::Tree(); }
PathFlow<L>& Flow() const { return AbstractSearch<L>::Flow(); }

protected:
explicit BasicSearch(const Grid2D<L>& grid); ///< Create a basic search object that references an existing grid object.

Expand All @@ -21,17 +30,25 @@ class BasicSearch : public AbstractSearch<L>

void PerformSearch(); ///< Perform the current path search, populating the shortest grid path tree.

virtual bool ProcessSearchNode(Offset2D coords, PathCost fCost) = 0; ///< Process the node at coordinates `coords`, given its f-cost `fCost`.

void ExpandSearchNode(Offset2D coords, Connections<L> successors); ///< Expand the node at coordinates `coords` according to the set of `successors`.

virtual bool ProcessSearchNode(Offset2D coords, PathCost fCost) = 0; ///< Process the node at coordinates `coords`, given its f-cost `fCost`.
bool IsSearchNodeInitialized(Offset2D coords) const { return AbstractSearch<L>::IsSearchNodeInitialized(coords); }
void InitializeDijkstraNode(Offset2D coords) { AbstractSearch<L>::InitializeDijkstraNode(coords); }
void InitializeHeuristicNode(Offset2D coords) { AbstractSearch<L>::InitializeHeuristicNode(coords); }
using DijkstraQueue = typename AbstractSearch<L>::DijkstraQueue;
using HeuristicQueue = typename AbstractSearch<L>::HeuristicQueue;
DijkstraQueue CreateDijkstraQueue() { return AbstractSearch<L>::CreateDijkstraQueue(); }
HeuristicQueue CreateHeuristicQueue() { return AbstractSearch<L>::CreateHeuristicQueue(); }

private:
HeuristicQueue queue_;
};

template <int L>
BasicSearch<L>::BasicSearch(const Grid2D<L>& grid)
: AbstractSearch{ grid }
: AbstractSearch<L>{ grid }
, queue_{ CreateHeuristicQueue() }
{
}
Expand Down
Loading

0 comments on commit 7f29ca2

Please sign in to comment.