Initial import

This commit is contained in:
James P. Howard, II
2020-09-19 17:30:42 -04:00
parent 0bf74891a8
commit 37558713bf
25 changed files with 842 additions and 138 deletions

View File

@@ -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
)

View File

@@ -63,6 +63,7 @@ Example:
``` bash
> mkdir build && cd build
> conan install ..
> cmake .. -DCMAKE_BUILD_TYPE=[Debug | Coverage | Release]
> make
> ./main

View File

@@ -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;
}

View File

@@ -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
View File

@@ -0,0 +1,6 @@
[requires]
catch2/2.2.2@bincrafters/stable
spdlog/1.7.0
[generators]
cmake

151
examples/boltzmann.cpp Normal file
View 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
View 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

View File

@@ -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
View 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

View File

@@ -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
View 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
View 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
View 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

View 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
View 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

View 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

View File

@@ -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
View 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

View File

@@ -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
View 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
View 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
View 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

View File

@@ -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
)

View File

@@ -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";
}

View File

@@ -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.