mirror of
https://github.com/JHUAPL/kami.git
synced 2026-01-08 14:33:53 -05:00
Initial import
This commit is contained in:
@@ -6,13 +6,18 @@
|
||||
# For many purposes, you may not need to change anything about this file.
|
||||
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
project("KAMI" CXX)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/")
|
||||
|
||||
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
|
||||
conan_basic_setup()
|
||||
|
||||
# Set project name, version and laguages here. (change as needed)
|
||||
# Version numbers are available by including "exampleConfig.h" in
|
||||
# the source. See exampleConfig.h.in for some more details.
|
||||
project(CPP_BOILERPLATE VERSION 1.2.3.4 LANGUAGES CXX)
|
||||
|
||||
|
||||
# Options: Things you can set via commandline options to cmake (e.g. -DENABLE_LTO=[ON|OFF])
|
||||
option(ENABLE_WARNINGS_SETTINGS "Allow target_set_warnings to add flags and defines.
|
||||
Set this to OFF if you want to provide your own warning parameters." ON)
|
||||
@@ -38,12 +43,15 @@ find_lto(CXX)
|
||||
# Locate files (change as needed).
|
||||
# --------------------------------------------------------------------------------
|
||||
set(SOURCES # All .cpp files in src/
|
||||
src/example.cpp
|
||||
src/agent.cpp
|
||||
src/kami.cpp
|
||||
src/multigrid2d.cpp
|
||||
src/random.cpp
|
||||
)
|
||||
set(TESTFILES # All .cpp files in tests/
|
||||
tests/main.cpp
|
||||
tests/dummy.cpp
|
||||
)
|
||||
set(LIBRARY_NAME engine) # Default name for the library built from src/*.cpp (change if you wish)
|
||||
set(LIBRARY_NAME kami) # Default name for the library built from src/*.cpp (change if you wish)
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
# Build! (Change as needed)
|
||||
@@ -55,7 +63,7 @@ add_library(${LIBRARY_NAME} OBJECT ${SOURCES})
|
||||
target_include_directories(${LIBRARY_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
# There's also (probably) doctests within the library, so we need to see this as well.
|
||||
target_link_libraries(${LIBRARY_NAME} PUBLIC doctest)
|
||||
target_link_libraries(${LIBRARY_NAME} PUBLIC spdlog)
|
||||
|
||||
# Set the compile options you want (change as needed).
|
||||
target_set_warnings(${LIBRARY_NAME} ENABLE ALL AS_ERROR ALL DISABLE Annoying)
|
||||
@@ -63,16 +71,16 @@ target_set_warnings(${LIBRARY_NAME} ENABLE ALL AS_ERROR ALL DISABLE Annoying)
|
||||
|
||||
# Add an executable for the file app/main.cpp.
|
||||
# If you add more executables, copy these lines accordingly.
|
||||
add_executable(main app/main.cpp) # Name of exec. and location of file.
|
||||
target_link_libraries(main PRIVATE ${LIBRARY_NAME}) # Link the executable to library (if it uses it).
|
||||
target_set_warnings(main ENABLE ALL AS_ERROR ALL DISABLE Annoying) # Set warnings (if needed).
|
||||
target_enable_lto(main optimized) # enable link-time-optimization if available for non-debug configurations
|
||||
add_executable(boltzmann examples/boltzmann.cpp) # Name of exec. and location of file.
|
||||
target_link_libraries(boltzmann PRIVATE ${LIBRARY_NAME}) # Link the executable to library (if it uses it).
|
||||
target_set_warnings(boltzmann ENABLE ALL AS_ERROR ALL DISABLE Annoying) # Set warnings (if needed).
|
||||
target_enable_lto(boltzmann optimized) # enable link-time-optimization if available for non-debug configurations
|
||||
|
||||
# Set the properties you require, e.g. what C++ standard to use. Here applied to library and main (change as needed).
|
||||
set_target_properties(
|
||||
${LIBRARY_NAME} main
|
||||
${LIBRARY_NAME} boltzmann
|
||||
PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED YES
|
||||
CXX_EXTENSIONS NO
|
||||
)
|
||||
|
||||
@@ -63,6 +63,7 @@ Example:
|
||||
|
||||
``` bash
|
||||
> mkdir build && cd build
|
||||
> conan install ..
|
||||
> cmake .. -DCMAKE_BUILD_TYPE=[Debug | Coverage | Release]
|
||||
> make
|
||||
> ./main
|
||||
|
||||
35
app/main.cpp
35
app/main.cpp
@@ -1,35 +0,0 @@
|
||||
// Executables must have the following defined if the library contains
|
||||
// doctest definitions. For builds with this disabled, e.g. code shipped to
|
||||
// users, this can be left out.
|
||||
#ifdef ENABLE_DOCTEST_IN_LIBRARY
|
||||
#define DOCTEST_CONFIG_IMPLEMENT
|
||||
#include "doctest.h"
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "exampleConfig.h"
|
||||
#include "example.h"
|
||||
|
||||
/*
|
||||
* Simple main program that demontrates how access
|
||||
* CMake definitions (here the version number) from source code.
|
||||
*/
|
||||
int main() {
|
||||
std::cout << "C++ Boiler Plate v"
|
||||
<< PROJECT_VERSION_MAJOR
|
||||
<< "."
|
||||
<< PROJECT_VERSION_MINOR
|
||||
<< "."
|
||||
<< PROJECT_VERSION_PATCH
|
||||
<< "."
|
||||
<< PROJECT_VERSION_TWEAK
|
||||
<< std::endl;
|
||||
std::system("cat ../LICENSE");
|
||||
|
||||
// Bring in the dummy class from the example source,
|
||||
// just to show that it is accessible from main.cpp.
|
||||
Dummy d = Dummy();
|
||||
return d.doSomething() ? 0 : -1;
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
# correct definitions. Here only used to make version number available to
|
||||
# the source code. Include "exampleConfig.h" (no .in suffix) in the source.
|
||||
configure_file (
|
||||
"${PROJECT_SOURCE_DIR}/include/exampleConfig.h.in"
|
||||
"${PROJECT_BINARY_DIR}/exampleConfig.h"
|
||||
"${PROJECT_SOURCE_DIR}/include/kami/config.hpp.in"
|
||||
"${PROJECT_BINARY_DIR}/include/kami/config.hpp"
|
||||
)
|
||||
# add the binary tree to the search path for include files
|
||||
# so that we will find exampleConfig.h
|
||||
|
||||
6
conanfile.txt
Normal file
6
conanfile.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
[requires]
|
||||
catch2/2.2.2@bincrafters/stable
|
||||
spdlog/1.7.0
|
||||
|
||||
[generators]
|
||||
cmake
|
||||
151
examples/boltzmann.cpp
Normal file
151
examples/boltzmann.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/*-
|
||||
* TODO FILEHEADER
|
||||
*/
|
||||
|
||||
#include "boltzmann.hpp"
|
||||
|
||||
// #include <spdlog/spdlog.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <kami/agent.hpp>
|
||||
#include <kami/kami.hpp>
|
||||
#include <kami/multigrid2d.hpp>
|
||||
#include <map>
|
||||
|
||||
MoneyAgent::MoneyAgent() {
|
||||
rd = new std::random_device();
|
||||
rng = new std::mt19937{(*rd)()};
|
||||
|
||||
stepCounter = 0;
|
||||
agentWealth = 1;
|
||||
}
|
||||
|
||||
MoneyAgent::~MoneyAgent() {
|
||||
delete rng;
|
||||
delete rd;
|
||||
};
|
||||
|
||||
void MoneyAgent::step() {
|
||||
stepCounter++;
|
||||
|
||||
moveAgent();
|
||||
if (agentWealth > 0)
|
||||
giveMoney();
|
||||
}
|
||||
|
||||
void MoneyAgent::setWorld(kami::MultiGrid2D *w) {
|
||||
world = w;
|
||||
}
|
||||
|
||||
void MoneyAgent::setModel(class BoltzmannWealthModel *m) {
|
||||
model = m;
|
||||
}
|
||||
|
||||
void MoneyAgent::moveAgent() {
|
||||
auto agentID = this->getAgentID();
|
||||
auto moveList = world->getNeighborhood(agentID, kami::NeighborhoodType::Moore, false);
|
||||
|
||||
std::uniform_int_distribution<unsigned long> dist(0, moveList.size() - 1);
|
||||
auto newLocation = moveList[dist(*rng)];
|
||||
|
||||
world->moveAgent(agentID, newLocation);
|
||||
}
|
||||
|
||||
int MoneyAgent::getWealth() {
|
||||
return agentWealth;
|
||||
}
|
||||
|
||||
void MoneyAgent::setWealth(int newWealth) {
|
||||
agentWealth = newWealth;
|
||||
}
|
||||
|
||||
void MoneyAgent::giveMoney() {
|
||||
kami::AgentID agentID = getAgentID();
|
||||
kami::MultiGrid2DCoord location = world->getLocationByAgent(agentID);
|
||||
std::vector<kami::AgentID> *cellMates = world->getCellContents(location);
|
||||
|
||||
if (cellMates->size() > 1) {
|
||||
std::uniform_int_distribution<unsigned long> dist(0, cellMates->size() - 1);
|
||||
kami::AgentID otherAgentID = cellMates->at(dist(*rng));
|
||||
auto otherAgent = model->getAgentByID(otherAgentID);
|
||||
|
||||
otherAgent->agentWealth += 1;
|
||||
agentWealth -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void MoneyAgent::prinfo(void) const {
|
||||
kami::AgentID agentID = getAgentID();
|
||||
kami::MultiGrid2DCoord location = world->getLocationByAgent(agentID);
|
||||
|
||||
std::cout << " agent: " << agentID << " step: " << stepCounter << " wealth: " << agentWealth << " location: " << location << std::endl;
|
||||
}
|
||||
|
||||
BoltzmannWealthModel::BoltzmannWealthModel(unsigned int numberAgents, unsigned int width, unsigned int height) {
|
||||
auto seed = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
std::minstd_rand rng(static_cast<unsigned int>(seed));
|
||||
std::uniform_int_distribution<unsigned long> distWidth(0, width - 1);
|
||||
std::uniform_int_distribution<unsigned long> distHeight(0, height - 1);
|
||||
world = new kami::MultiGrid2D(width, height, true, true);
|
||||
sched = new kami::RandomScheduler(this);
|
||||
|
||||
stepCount = 0;
|
||||
MoneyAgent::setWorld(world);
|
||||
MoneyAgent::setModel(this);
|
||||
|
||||
for (unsigned int i = 0; i < numberAgents; i++) {
|
||||
MoneyAgent *newAgent = new MoneyAgent();
|
||||
agentList.insert(std::pair<kami::AgentID, MoneyAgent *>(newAgent->getAgentID(), newAgent));
|
||||
sched->addAgent(newAgent->getAgentID());
|
||||
world->addAgent(newAgent->getAgentID(), static_cast<int>(distWidth(rng)), static_cast<int>(distHeight(rng)));
|
||||
}
|
||||
}
|
||||
|
||||
BoltzmannWealthModel::~BoltzmannWealthModel() {
|
||||
for (auto agent = agentList.begin(); agent != agentList.end(); ++agent) {
|
||||
world->deleteAgent(agent->first);
|
||||
sched->deleteAgent(agent->first);
|
||||
agentList.erase(agent);
|
||||
}
|
||||
delete sched;
|
||||
delete world;
|
||||
}
|
||||
|
||||
void BoltzmannWealthModel::step() {
|
||||
stepCount++;
|
||||
sched->step();
|
||||
}
|
||||
|
||||
void BoltzmannWealthModel::run(unsigned int n) {
|
||||
for (unsigned int i = 0; i < n; i++)
|
||||
step();
|
||||
}
|
||||
|
||||
void BoltzmannWealthModel::prinfo(void) const {
|
||||
std::cout << " step: " << stepCount << std::endl
|
||||
<< " agentinfo: " << std::endl;
|
||||
for (auto agent = agentList.begin(); agent != agentList.end(); ++agent) {
|
||||
agent->second->prinfo();
|
||||
}
|
||||
}
|
||||
|
||||
MoneyAgent *BoltzmannWealthModel::getAgentByID(kami::AgentID agentID) const {
|
||||
MoneyAgent *agentPair = agentList.at(agentID);
|
||||
|
||||
return agentPair;
|
||||
}
|
||||
|
||||
std::random_device *MoneyAgent::rd = nullptr;
|
||||
std::mt19937 *MoneyAgent::rng = nullptr;
|
||||
kami::MultiGrid2D *MoneyAgent::world = nullptr;
|
||||
BoltzmannWealthModel *MoneyAgent::model = nullptr;
|
||||
|
||||
int main(void) {
|
||||
BoltzmannWealthModel model(100, 10, 10);
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
model.step();
|
||||
}
|
||||
|
||||
model.prinfo();
|
||||
}
|
||||
60
examples/boltzmann.hpp
Normal file
60
examples/boltzmann.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/*-
|
||||
* TODO FILEHEADER
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef BOLTZMAN_HPP
|
||||
#define BOLTZMAN_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <kami/agent.hpp>
|
||||
#include <kami/kami.hpp>
|
||||
#include <kami/multigrid2d.hpp>
|
||||
#include <map>
|
||||
#include <random>
|
||||
|
||||
class MoneyAgent : public kami::Agent {
|
||||
public:
|
||||
MoneyAgent();
|
||||
~MoneyAgent();
|
||||
|
||||
void step();
|
||||
|
||||
static void setWorld(kami::MultiGrid2D *w);
|
||||
static void setModel(class BoltzmannWealthModel *m);
|
||||
|
||||
void moveAgent();
|
||||
|
||||
int getWealth();
|
||||
void setWealth(int newWealth);
|
||||
void giveMoney();
|
||||
|
||||
void prinfo(void) const;
|
||||
|
||||
private:
|
||||
static kami::MultiGrid2D *world;
|
||||
static BoltzmannWealthModel *model;
|
||||
static std::random_device *rd;
|
||||
static std::mt19937 *rng;
|
||||
int stepCounter;
|
||||
int agentWealth;
|
||||
};
|
||||
|
||||
class BoltzmannWealthModel : public kami::Model {
|
||||
public:
|
||||
BoltzmannWealthModel(unsigned int = 100, unsigned int = 10, unsigned int = 10);
|
||||
~BoltzmannWealthModel();
|
||||
void step();
|
||||
void run(unsigned int n);
|
||||
void prinfo(void) const;
|
||||
|
||||
MoneyAgent *getAgentByID(kami::AgentID agentID) const;
|
||||
|
||||
private:
|
||||
std::map<kami::AgentID, MoneyAgent *> agentList;
|
||||
kami::RandomScheduler *sched;
|
||||
kami::MultiGrid2D *world;
|
||||
unsigned int stepCount;
|
||||
};
|
||||
|
||||
#endif // BOLTZMAN_HPP
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* This is a dummy class to demonstrate features of the boiler plate.
|
||||
*/
|
||||
class Dummy {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Default constructor for Dummy (does nothing).
|
||||
*/
|
||||
Dummy();
|
||||
/**
|
||||
* Returns a bool.
|
||||
* @return Always True.
|
||||
*/
|
||||
bool doSomething();
|
||||
};
|
||||
|
||||
|
||||
#ifdef ENABLE_DOCTEST_IN_LIBRARY
|
||||
#include "doctest.h"
|
||||
TEST_CASE("we can have tests in headers if we want")
|
||||
{
|
||||
Dummy d;
|
||||
CHECK(d.doSomething() == true);
|
||||
}
|
||||
#endif
|
||||
59
include/kami/agent.hpp
Normal file
59
include/kami/agent.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/*-
|
||||
* TODO FILEHEADER
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_AGENT_HPP
|
||||
#define KAMI_AGENT_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/// A unique identifier for each agent
|
||||
class AgentID {
|
||||
public:
|
||||
/// Create a new unique identifier
|
||||
AgentID();
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
friend bool operator==(const AgentID &, const AgentID &);
|
||||
friend bool operator!=(const AgentID &, const AgentID &);
|
||||
friend bool operator<(const AgentID &, const AgentID &);
|
||||
friend std::ostream &operator<<(std::ostream &, const AgentID &);
|
||||
|
||||
private:
|
||||
static uint64_t idGen;
|
||||
uint64_t id;
|
||||
};
|
||||
|
||||
class Agent {
|
||||
public:
|
||||
virtual ~Agent() = default;
|
||||
|
||||
AgentID getAgentID() const;
|
||||
virtual void step();
|
||||
|
||||
friend bool operator==(const Agent &, const Agent &);
|
||||
friend bool operator!=(const Agent &, const Agent &);
|
||||
|
||||
private:
|
||||
AgentID agentID;
|
||||
};
|
||||
|
||||
/// A class for agents with pre-`step()` and post-`step()` staging.
|
||||
class StagedAgent : public Agent {
|
||||
public:
|
||||
///
|
||||
StagedAgent();
|
||||
virtual ~StagedAgent() = default;
|
||||
|
||||
virtual void preStep() = 0;
|
||||
virtual void postStep() = 0;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
#endif // KAMI_AGENT_HPP
|
||||
@@ -6,7 +6,13 @@
|
||||
* right definition values. Change the namings as you wish,
|
||||
* but make sure they match up with whats in CMakeLists.txt.
|
||||
*/
|
||||
#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||
#define PROJECT_VERSION_TWEAK @PROJECT_VERSION_TWEAK@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_KAMI_VERSION_HPP
|
||||
#define KAMI_KAMI_VERSION_HPP
|
||||
|
||||
#define KAMI_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||
#define KAMI_VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||
#define KAMI_VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||
|
||||
#endif // KAMI_KAMI_VERSION_HPP
|
||||
27
include/kami/domain.hpp
Normal file
27
include/kami/domain.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/*-
|
||||
* TODO FILEHEADER
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_DOMAIN_HPP
|
||||
#define KAMI_DOMAIN_HPP
|
||||
|
||||
#include <kami/kami.hpp>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
A domain provides an environment for the agents to participate in
|
||||
*/
|
||||
class Domain {
|
||||
};
|
||||
|
||||
/**
|
||||
A domain based on a grid with integer steps
|
||||
*/
|
||||
class GridDomain : public Domain {
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
#endif // KAMI_DOMAIN_HPP
|
||||
13
include/kami/kami.hpp
Normal file
13
include/kami/kami.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
/*-
|
||||
* TODO FILEHEADER
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_KAMI_HPP
|
||||
#define KAMI_KAMI_HPP
|
||||
|
||||
#include <kami/agent.hpp>
|
||||
#include <kami/model.hpp>
|
||||
#include <kami/random.hpp>
|
||||
|
||||
#endif // KAMI_KAMI_HPP
|
||||
33
include/kami/model.hpp
Normal file
33
include/kami/model.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/*-
|
||||
* TODO FILEHEADER
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_MODEL_HPP
|
||||
#define KAMI_MODEL_HPP
|
||||
|
||||
#include <kami/agent.hpp>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/// An abstract for generic models
|
||||
class Model {
|
||||
public:
|
||||
virtual ~Model();
|
||||
|
||||
/// Get a reference to an Agent by AgentID
|
||||
virtual Agent *getAgentByID(AgentID) const = 0;
|
||||
|
||||
/// Run the model for a fixed number of steps.
|
||||
void run(unsigned int) { return; }
|
||||
|
||||
/// Execute a time-step for the model.
|
||||
///
|
||||
/// This function should step the model instance. Any activities that the
|
||||
/// model should perform as part of its time step should be in this function.
|
||||
void step() { return; }
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
#endif // KAMI_MODEL_HPP
|
||||
88
include/kami/multigrid2d.hpp
Normal file
88
include/kami/multigrid2d.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/*-
|
||||
* TODO FILEHEADER
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_MULTIGRID2D_HPP
|
||||
#define KAMI_MULTIGRID2D_HPP
|
||||
|
||||
#include <kami/agent.hpp>
|
||||
#include <kami/domain.hpp>
|
||||
#include <kami/kami.hpp>
|
||||
#include <kami/multigrid2d.hpp>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace kami {
|
||||
|
||||
typedef enum NeighborhoodType { Moore,
|
||||
VonNeumann } NeighborhoodType;
|
||||
|
||||
class MultiGrid2DCoord {
|
||||
public:
|
||||
MultiGrid2DCoord(int, int);
|
||||
|
||||
void setCoord(int, int);
|
||||
void setXCoord(int);
|
||||
void setYCoord(int);
|
||||
|
||||
int getXCoord(void) const;
|
||||
int getYCoord(void) const;
|
||||
|
||||
friend bool operator==(const MultiGrid2DCoord &, const MultiGrid2DCoord &);
|
||||
friend bool operator!=(const MultiGrid2DCoord &, const MultiGrid2DCoord &);
|
||||
friend std::ostream &operator<<(std::ostream &, const MultiGrid2DCoord &);
|
||||
|
||||
private:
|
||||
int xCoord;
|
||||
int yCoord;
|
||||
};
|
||||
|
||||
class MultiGrid2D : public GridDomain {
|
||||
public:
|
||||
MultiGrid2D(unsigned int, unsigned int, bool, bool);
|
||||
~MultiGrid2D(void);
|
||||
|
||||
bool addAgent(AgentID, int, int);
|
||||
bool addAgent(AgentID, MultiGrid2DCoord);
|
||||
|
||||
void deleteAgent(AgentID);
|
||||
void deleteAgent(AgentID, int, int);
|
||||
void deleteAgent(AgentID, MultiGrid2DCoord);
|
||||
|
||||
bool isLocationValid(MultiGrid2DCoord) const;
|
||||
|
||||
MultiGrid2DCoord getLocationByAgent(AgentID) const;
|
||||
|
||||
void moveAgent(AgentID, MultiGrid2DCoord);
|
||||
|
||||
void setColWrap(bool);
|
||||
void setRowWrap(bool);
|
||||
bool getColWrap(void) const;
|
||||
bool getRowWrap(void) const;
|
||||
|
||||
std::vector<MultiGrid2DCoord> getNeighborhood(MultiGrid2DCoord, NeighborhoodType, bool) const;
|
||||
std::vector<MultiGrid2DCoord> getNeighborhood(AgentID, NeighborhoodType, bool) const;
|
||||
|
||||
std::vector<AgentID> *getCellContents(MultiGrid2DCoord);
|
||||
|
||||
void setMaxRows(unsigned int);
|
||||
void setMaxCols(unsigned int);
|
||||
unsigned int getMaxRows(void) const;
|
||||
unsigned int getMaxCols(void) const;
|
||||
|
||||
private:
|
||||
std::vector<AgentID> **agentGrid;
|
||||
std::map<AgentID, MultiGrid2DCoord> *agentIndex;
|
||||
static std::mutex lock;
|
||||
unsigned int maxRows, maxCols;
|
||||
bool colWrap, rowWrap;
|
||||
|
||||
MultiGrid2DCoord locationWrap(int, int) const;
|
||||
MultiGrid2DCoord locationWrap(MultiGrid2DCoord) const;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
#endif // KAMI_MULTIGRID2D_HPP
|
||||
33
include/kami/random.hpp
Normal file
33
include/kami/random.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#ifndef KAMI_RANDOM_HPP
|
||||
#define KAMI_RANDOM_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <kami/agent.hpp>
|
||||
#include <kami/model.hpp>
|
||||
#include <kami/scheduler.hpp>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
namespace kami {
|
||||
|
||||
class RandomScheduler : public Scheduler {
|
||||
public:
|
||||
RandomScheduler(Model *);
|
||||
virtual ~RandomScheduler();
|
||||
|
||||
void addAgent(AgentID);
|
||||
void deleteAgent(AgentID);
|
||||
void step();
|
||||
|
||||
private:
|
||||
std::vector<AgentID> agentList;
|
||||
std::random_device rd;
|
||||
std::mt19937 rng{rd()};
|
||||
Model *model;
|
||||
int stepCounter;
|
||||
};
|
||||
|
||||
}; // namespace kami
|
||||
|
||||
#endif // KAMI_RANDOM_HPP
|
||||
37
include/kami/scheduler.hpp
Normal file
37
include/kami/scheduler.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*-
|
||||
* TODO FILEHEADER
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_SCHEDULER_HPP
|
||||
#define KAMI_SCHEDULER_HPP
|
||||
|
||||
#include <kami/agent.hpp>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/// An abstract class for a Scheduler
|
||||
class Scheduler {
|
||||
public:
|
||||
|
||||
virtual ~Scheduler();
|
||||
|
||||
/// Add an Agent to the Scheduler.
|
||||
///
|
||||
/// @param agentID The AgentID of the agent to add.
|
||||
virtual void addAgent(AgentID agentID) = 0;
|
||||
|
||||
/// Remove an Agent from the Scheduler.
|
||||
///
|
||||
/// @param agentID The AgentID of the agent to remove.
|
||||
virtual void deleteAgent(AgentID agentID) = 0;
|
||||
|
||||
/// Step the Scheduler.
|
||||
///
|
||||
/// A generic step function that executes a single time step.
|
||||
virtual void step() = 0;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
#endif // KAMI_SCHEDULER_HPP
|
||||
22
setup.sh
22
setup.sh
@@ -1,22 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Revert to first commit, add and commit everything as single commit.
|
||||
git reset $(git commit-tree HEAD^{tree} -m "Bolier plate for C++ project added")
|
||||
|
||||
name=$(git config user.name)
|
||||
email=$(git config user.email)
|
||||
|
||||
# If this script is called with an argument, use that as the amended author
|
||||
# EX: ./setup.sh "Author Name <author_email@email.com>"
|
||||
if [[ "$1" ]]; then
|
||||
git commit --amend --author="$1"
|
||||
else
|
||||
git commit --amend --author="$name <$email>"
|
||||
fi
|
||||
|
||||
# Initialize submodules: This should already be done when cloning, but there are ways to muck it
|
||||
# up if you do things in the wrong order. So just to be sure, we do it now.
|
||||
git submodule update --init --recursive
|
||||
|
||||
# Remove the remote (you probably want your own instead).
|
||||
git remote remove origin
|
||||
52
src/agent.cpp
Normal file
52
src/agent.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kami/agent.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace kami {
|
||||
|
||||
uint64_t AgentID::idGen = 1;
|
||||
|
||||
AgentID::AgentID() {
|
||||
id = idGen++;
|
||||
}
|
||||
|
||||
std::string AgentID::toString() const {
|
||||
return std::to_string(id);
|
||||
}
|
||||
|
||||
bool operator==(const AgentID &lhs, const AgentID &rhs) {
|
||||
return lhs.id == rhs.id;
|
||||
}
|
||||
|
||||
bool operator!=(const AgentID &lhs, const AgentID &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator<(const AgentID &lhs, const AgentID &rhs) {
|
||||
return lhs.id < rhs.id;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &lhs, const AgentID &rhs) {
|
||||
return lhs << rhs.id;
|
||||
}
|
||||
|
||||
AgentID Agent::getAgentID() const {
|
||||
return agentID;
|
||||
}
|
||||
|
||||
void Agent::step() {
|
||||
}
|
||||
|
||||
bool operator==(const Agent &lhs, const Agent &rhs) {
|
||||
return lhs.agentID == rhs.agentID;
|
||||
}
|
||||
|
||||
bool operator!=(const Agent &lhs, const Agent &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
@@ -1,21 +0,0 @@
|
||||
#include "example.h"
|
||||
|
||||
Dummy::Dummy() {
|
||||
|
||||
}
|
||||
|
||||
bool Dummy::doSomething() {
|
||||
// Do silly things, using some C++17 features to enforce C++17 builds only.
|
||||
constexpr int digits[2] = {0, 1};
|
||||
auto [zero, one] = digits;
|
||||
return zero + one;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_DOCTEST_IN_LIBRARY
|
||||
#include "doctest.h"
|
||||
TEST_CASE("we can have tests written here, to test impl. details")
|
||||
{
|
||||
CHECK(true);
|
||||
}
|
||||
#endif
|
||||
10
src/kami.cpp
Normal file
10
src/kami.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "kami/model.hpp"
|
||||
#include "kami/scheduler.hpp"
|
||||
|
||||
namespace kami {
|
||||
|
||||
Model::~Model() {}
|
||||
|
||||
Scheduler::~Scheduler() {}
|
||||
|
||||
} // namespace kami
|
||||
189
src/multigrid2d.cpp
Normal file
189
src/multigrid2d.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
/*-
|
||||
* TODO FILEHEADER
|
||||
*/
|
||||
|
||||
#include "kami/multigrid2d.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "kami/agent.hpp"
|
||||
#include "kami/domain.hpp"
|
||||
#include "kami/kami.hpp"
|
||||
|
||||
namespace kami {
|
||||
|
||||
MultiGrid2DCoord::MultiGrid2DCoord(int newXCoord, int newYCoord) {
|
||||
setXCoord(newXCoord);
|
||||
setYCoord(newYCoord);
|
||||
}
|
||||
|
||||
void MultiGrid2DCoord::setCoord(int newXCoord, int newYCoord) {
|
||||
setXCoord(newXCoord);
|
||||
setYCoord(newYCoord);
|
||||
}
|
||||
|
||||
void MultiGrid2DCoord::setXCoord(int newXCoord) {
|
||||
xCoord = newXCoord;
|
||||
}
|
||||
|
||||
void MultiGrid2DCoord::setYCoord(int newYCoord) {
|
||||
yCoord = newYCoord;
|
||||
}
|
||||
|
||||
int MultiGrid2DCoord::getXCoord(void) const {
|
||||
return xCoord;
|
||||
}
|
||||
|
||||
int MultiGrid2DCoord::getYCoord(void) const {
|
||||
return yCoord;
|
||||
}
|
||||
|
||||
bool operator==(const MultiGrid2DCoord &lhs, const MultiGrid2DCoord &rhs) {
|
||||
return (lhs.xCoord == rhs.yCoord && lhs.yCoord == rhs.yCoord);
|
||||
}
|
||||
|
||||
bool operator!=(const MultiGrid2DCoord &lhs, const MultiGrid2DCoord &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &lhs, const MultiGrid2DCoord &rhs) {
|
||||
return lhs << "(" << rhs.xCoord << ", " << rhs.yCoord << ")";
|
||||
}
|
||||
|
||||
MultiGrid2D::MultiGrid2D(unsigned int newMaxCols, unsigned int newMaxRows, bool newColWrap = false, bool newRowWrap = false) {
|
||||
setMaxCols(newMaxCols);
|
||||
setMaxRows(newMaxRows);
|
||||
|
||||
setColWrap(newColWrap);
|
||||
setRowWrap(newRowWrap);
|
||||
|
||||
agentGrid = new std::vector<AgentID> *[newMaxCols];
|
||||
for (unsigned int i = 0; i < newMaxRows; i++)
|
||||
agentGrid[i] = new std::vector<AgentID>[newMaxRows];
|
||||
|
||||
agentIndex = new std::map<AgentID, MultiGrid2DCoord>;
|
||||
}
|
||||
|
||||
MultiGrid2D::~MultiGrid2D(void) {
|
||||
delete agentIndex;
|
||||
|
||||
for (unsigned int i = 0; i < maxRows; i++)
|
||||
delete [] agentGrid[i];
|
||||
delete [] agentGrid;
|
||||
}
|
||||
|
||||
bool MultiGrid2D::addAgent(AgentID agentID, int xCoord, int yCoord) {
|
||||
return addAgent(agentID, MultiGrid2DCoord(xCoord, yCoord));
|
||||
}
|
||||
bool MultiGrid2D::addAgent(AgentID agentID, MultiGrid2DCoord location) {
|
||||
if (isLocationValid(location)) {
|
||||
agentIndex->insert(std::pair<AgentID, MultiGrid2DCoord>(agentID, location));
|
||||
agentGrid[location.getXCoord()][location.getYCoord()].push_back(agentID);
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
void MultiGrid2D::deleteAgent(AgentID agentID) {
|
||||
MultiGrid2DCoord location = getLocationByAgent(agentID);
|
||||
|
||||
deleteAgent(agentID, location);
|
||||
}
|
||||
|
||||
void MultiGrid2D::deleteAgent(AgentID agentID, int xCoord, int yCoord) {
|
||||
deleteAgent(agentID, MultiGrid2DCoord(xCoord, yCoord));
|
||||
}
|
||||
|
||||
void MultiGrid2D::deleteAgent(AgentID agentID, MultiGrid2DCoord location) {
|
||||
auto agentList = agentGrid[static_cast<int>(location.getXCoord())][static_cast<int>(location.getYCoord())];
|
||||
for (auto testAgentID = agentList.begin(); testAgentID < agentList.end(); testAgentID++) {
|
||||
if (*testAgentID == agentID) {
|
||||
agentList.erase(testAgentID);
|
||||
agentIndex->erase(agentID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MultiGrid2D::isLocationValid(MultiGrid2DCoord location) const {
|
||||
auto xCoord = location.getXCoord();
|
||||
auto yCoord = location.getYCoord();
|
||||
|
||||
return (xCoord >= 0 && xCoord < static_cast<int>(maxCols) && yCoord >= 0 && yCoord < static_cast<int>(maxRows));
|
||||
}
|
||||
|
||||
MultiGrid2DCoord MultiGrid2D::getLocationByAgent(AgentID agentID) const {
|
||||
return agentIndex->at(agentID);
|
||||
}
|
||||
|
||||
void MultiGrid2D::moveAgent(AgentID agentID, MultiGrid2DCoord nextLocation) {
|
||||
MultiGrid2DCoord currLocation = getLocationByAgent(agentID);
|
||||
|
||||
deleteAgent(agentID, currLocation);
|
||||
addAgent(agentID, nextLocation);
|
||||
}
|
||||
|
||||
void MultiGrid2D::setColWrap(bool newColWrap) { colWrap = newColWrap; }
|
||||
void MultiGrid2D::setRowWrap(bool newRowWrap) { rowWrap = newRowWrap; }
|
||||
bool MultiGrid2D::getColWrap(void) const { return colWrap; }
|
||||
bool MultiGrid2D::getRowWrap(void) const { return rowWrap; }
|
||||
|
||||
std::vector<MultiGrid2DCoord> MultiGrid2D::getNeighborhood(AgentID agentID, NeighborhoodType nType, bool includeCenter) const {
|
||||
MultiGrid2DCoord location = getLocationByAgent(agentID);
|
||||
|
||||
return getNeighborhood(location, nType, includeCenter);
|
||||
}
|
||||
|
||||
std::vector<MultiGrid2DCoord> MultiGrid2D::getNeighborhood(MultiGrid2DCoord location, NeighborhoodType nType, bool includeCenter) const {
|
||||
std::vector<MultiGrid2DCoord> neighborhood;
|
||||
auto xCoord = location.getXCoord();
|
||||
auto yCoord = location.getYCoord();
|
||||
|
||||
if (includeCenter == true)
|
||||
neighborhood.push_back(location);
|
||||
|
||||
// N, E, S, W
|
||||
neighborhood.push_back(locationWrap(xCoord, yCoord - 1));
|
||||
neighborhood.push_back(locationWrap(xCoord + 1, yCoord));
|
||||
neighborhood.push_back(locationWrap(xCoord, yCoord + 1));
|
||||
neighborhood.push_back(locationWrap(xCoord - 1, yCoord));
|
||||
|
||||
if (nType == NeighborhoodType::Moore) {
|
||||
// NE, SE, SW, NW
|
||||
neighborhood.push_back(locationWrap(xCoord + 1, yCoord - 1));
|
||||
neighborhood.push_back(locationWrap(xCoord + 1, yCoord + 1));
|
||||
neighborhood.push_back(locationWrap(xCoord - 1, yCoord + 1));
|
||||
neighborhood.push_back(locationWrap(xCoord - 1, yCoord - 1));
|
||||
}
|
||||
|
||||
return neighborhood;
|
||||
}
|
||||
|
||||
std::vector<AgentID> *MultiGrid2D::getCellContents(MultiGrid2DCoord location) {
|
||||
if (isLocationValid(location)) {
|
||||
return &agentGrid[location.getXCoord()][location.getYCoord()];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MultiGrid2D::setMaxCols(unsigned int newMaxCols) { maxCols = newMaxCols; }
|
||||
void MultiGrid2D::setMaxRows(unsigned int newMaxRows) { maxRows = newMaxRows; }
|
||||
unsigned int MultiGrid2D::getMaxCols(void) const { return maxCols; }
|
||||
unsigned int MultiGrid2D::getMaxRows(void) const { return maxRows; }
|
||||
|
||||
MultiGrid2DCoord MultiGrid2D::locationWrap(MultiGrid2DCoord location) const {
|
||||
return locationWrap(location.getXCoord(), location.getYCoord());
|
||||
}
|
||||
|
||||
MultiGrid2DCoord MultiGrid2D::locationWrap(int xCoord, int yCoord) const {
|
||||
if (colWrap == true)
|
||||
xCoord = (xCoord + static_cast<int>(maxCols)) % static_cast<int>(maxCols);
|
||||
if (rowWrap == true)
|
||||
yCoord = (yCoord + static_cast<int>(maxRows)) % static_cast<int>(maxRows);
|
||||
return MultiGrid2DCoord(xCoord, yCoord);
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
49
src/random.cpp
Normal file
49
src/random.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kami/random.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
|
||||
#include "kami/agent.hpp"
|
||||
#include "kami/model.hpp"
|
||||
#include "kami/scheduler.hpp"
|
||||
|
||||
namespace kami {
|
||||
|
||||
RandomScheduler::RandomScheduler(Model *newModel) {
|
||||
stepCounter = 0;
|
||||
model = newModel;
|
||||
}
|
||||
|
||||
RandomScheduler::~RandomScheduler() {}
|
||||
|
||||
void RandomScheduler::addAgent(AgentID newAgentID) {
|
||||
agentList.push_back(newAgentID);
|
||||
}
|
||||
|
||||
void RandomScheduler::deleteAgent(AgentID oldAgentID) {
|
||||
for (auto agentID = agentList.begin(); agentID < agentList.end(); agentID++) {
|
||||
if (*agentID == oldAgentID) {
|
||||
agentList.erase(agentID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// ERROR HERE
|
||||
}
|
||||
|
||||
void RandomScheduler::step() {
|
||||
stepCounter++;
|
||||
|
||||
shuffle(agentList.begin(), agentList.end(), rng);
|
||||
for (auto agentID = agentList.begin(); agentID < agentList.end(); agentID++) {
|
||||
Agent *agent = model->getAgentByID(*agentID);
|
||||
if (agent != nullptr)
|
||||
agent->step();
|
||||
// ERROR HERE
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
@@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.8.2)
|
||||
|
||||
# List all files containing tests. (Change as needed)
|
||||
set(TESTFILES # All .cpp files in tests/
|
||||
main.cpp
|
||||
dummy.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
#include "doctest.h"
|
||||
#include "example.h"
|
||||
#include <iostream>
|
||||
|
||||
// Tests that don't naturally fit in the headers/.cpp files directly
|
||||
// can be placed in a tests/*.cpp file. Integration tests are a good example.
|
||||
|
||||
TEST_CASE("complicated integration tests could be here")
|
||||
{
|
||||
Dummy d;
|
||||
CHECK(d.doSomething() == true);
|
||||
int main(void) {
|
||||
std::cout << "foo";
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include "doctest.h"
|
||||
|
||||
// This is all that is needed to compile a test-runner executable.
|
||||
// More tests can be added here, or in a new tests/*.cpp file.
|
||||
Reference in New Issue
Block a user