From 37558713bfee20fd43ef1b2e469680c1117d26b8 Mon Sep 17 00:00:00 2001 From: "James P. Howard, II" Date: Sat, 19 Sep 2020 17:30:42 -0400 Subject: [PATCH] Initial import --- CMakeLists.txt | 30 ++- README.md | 1 + app/main.cpp | 35 ---- cmake/Misc.cmake | 4 +- conanfile.txt | 6 + examples/boltzmann.cpp | 151 ++++++++++++++ examples/boltzmann.hpp | 60 ++++++ include/example.h | 28 --- include/kami/agent.hpp | 59 ++++++ .../config.hpp.in} | 14 +- include/kami/domain.hpp | 27 +++ include/kami/kami.hpp | 13 ++ include/kami/model.hpp | 33 +++ include/kami/multigrid2d.hpp | 88 ++++++++ include/kami/random.hpp | 33 +++ include/kami/scheduler.hpp | 37 ++++ setup.sh | 22 -- src/agent.cpp | 52 +++++ src/example.cpp | 21 -- src/kami.cpp | 10 + src/multigrid2d.cpp | 189 ++++++++++++++++++ src/random.cpp | 49 +++++ tests/CMakeLists.txt | 1 - tests/dummy.cpp | 12 +- tests/main.cpp | 5 - 25 files changed, 842 insertions(+), 138 deletions(-) delete mode 100644 app/main.cpp create mode 100644 conanfile.txt create mode 100644 examples/boltzmann.cpp create mode 100644 examples/boltzmann.hpp delete mode 100644 include/example.h create mode 100644 include/kami/agent.hpp rename include/{exampleConfig.h.in => kami/config.hpp.in} (57%) create mode 100644 include/kami/domain.hpp create mode 100644 include/kami/kami.hpp create mode 100644 include/kami/model.hpp create mode 100644 include/kami/multigrid2d.hpp create mode 100644 include/kami/random.hpp create mode 100644 include/kami/scheduler.hpp delete mode 100755 setup.sh create mode 100644 src/agent.cpp delete mode 100644 src/example.cpp create mode 100644 src/kami.cpp create mode 100644 src/multigrid2d.cpp create mode 100644 src/random.cpp delete mode 100644 tests/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 199bdfe..682670b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) diff --git a/README.md b/README.md index 3ddfeff..5e87f3f 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ Example: ``` bash > mkdir build && cd build +> conan install .. > cmake .. -DCMAKE_BUILD_TYPE=[Debug | Coverage | Release] > make > ./main diff --git a/app/main.cpp b/app/main.cpp deleted file mode 100644 index ed8b2fe..0000000 --- a/app/main.cpp +++ /dev/null @@ -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 -#include - -#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; -} diff --git a/cmake/Misc.cmake b/cmake/Misc.cmake index c755e62..37bf879 100644 --- a/cmake/Misc.cmake +++ b/cmake/Misc.cmake @@ -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 diff --git a/conanfile.txt b/conanfile.txt new file mode 100644 index 0000000..61a38b3 --- /dev/null +++ b/conanfile.txt @@ -0,0 +1,6 @@ +[requires] +catch2/2.2.2@bincrafters/stable +spdlog/1.7.0 + +[generators] +cmake diff --git a/examples/boltzmann.cpp b/examples/boltzmann.cpp new file mode 100644 index 0000000..bffd429 --- /dev/null +++ b/examples/boltzmann.cpp @@ -0,0 +1,151 @@ +/*- + * TODO FILEHEADER + */ + +#include "boltzmann.hpp" + +// #include + +#include +#include +#include +#include +#include + +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 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 *cellMates = world->getCellContents(location); + + if (cellMates->size() > 1) { + std::uniform_int_distribution 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(seed)); + std::uniform_int_distribution distWidth(0, width - 1); + std::uniform_int_distribution 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(newAgent->getAgentID(), newAgent)); + sched->addAgent(newAgent->getAgentID()); + world->addAgent(newAgent->getAgentID(), static_cast(distWidth(rng)), static_cast(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(); +} diff --git a/examples/boltzmann.hpp b/examples/boltzmann.hpp new file mode 100644 index 0000000..1dedaca --- /dev/null +++ b/examples/boltzmann.hpp @@ -0,0 +1,60 @@ +/*- + * TODO FILEHEADER + */ + +#pragma once +#ifndef BOLTZMAN_HPP +#define BOLTZMAN_HPP + +#include +#include +#include +#include +#include +#include + +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 agentList; + kami::RandomScheduler *sched; + kami::MultiGrid2D *world; + unsigned int stepCount; +}; + +#endif // BOLTZMAN_HPP diff --git a/include/example.h b/include/example.h deleted file mode 100644 index eedb53a..0000000 --- a/include/example.h +++ /dev/null @@ -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 diff --git a/include/kami/agent.hpp b/include/kami/agent.hpp new file mode 100644 index 0000000..8660f98 --- /dev/null +++ b/include/kami/agent.hpp @@ -0,0 +1,59 @@ +/*- + * TODO FILEHEADER + */ + +#pragma once +#ifndef KAMI_AGENT_HPP +#define KAMI_AGENT_HPP + +#include +#include + +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 diff --git a/include/exampleConfig.h.in b/include/kami/config.hpp.in similarity index 57% rename from include/exampleConfig.h.in rename to include/kami/config.hpp.in index f482c40..8656021 100644 --- a/include/exampleConfig.h.in +++ b/include/kami/config.hpp.in @@ -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 diff --git a/include/kami/domain.hpp b/include/kami/domain.hpp new file mode 100644 index 0000000..fa2e5fb --- /dev/null +++ b/include/kami/domain.hpp @@ -0,0 +1,27 @@ +/*- + * TODO FILEHEADER + */ + +#pragma once +#ifndef KAMI_DOMAIN_HPP +#define KAMI_DOMAIN_HPP + +#include + +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 diff --git a/include/kami/kami.hpp b/include/kami/kami.hpp new file mode 100644 index 0000000..b9ed243 --- /dev/null +++ b/include/kami/kami.hpp @@ -0,0 +1,13 @@ +/*- + * TODO FILEHEADER + */ + +#pragma once +#ifndef KAMI_KAMI_HPP +#define KAMI_KAMI_HPP + +#include +#include +#include + +#endif // KAMI_KAMI_HPP diff --git a/include/kami/model.hpp b/include/kami/model.hpp new file mode 100644 index 0000000..35f4d4d --- /dev/null +++ b/include/kami/model.hpp @@ -0,0 +1,33 @@ +/*- + * TODO FILEHEADER + */ + +#pragma once +#ifndef KAMI_MODEL_HPP +#define KAMI_MODEL_HPP + +#include + +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 diff --git a/include/kami/multigrid2d.hpp b/include/kami/multigrid2d.hpp new file mode 100644 index 0000000..184f557 --- /dev/null +++ b/include/kami/multigrid2d.hpp @@ -0,0 +1,88 @@ +/*- + * TODO FILEHEADER + */ + +#pragma once +#ifndef KAMI_MULTIGRID2D_HPP +#define KAMI_MULTIGRID2D_HPP + +#include +#include +#include +#include +#include +#include +#include + +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 getNeighborhood(MultiGrid2DCoord, NeighborhoodType, bool) const; + std::vector getNeighborhood(AgentID, NeighborhoodType, bool) const; + + std::vector *getCellContents(MultiGrid2DCoord); + + void setMaxRows(unsigned int); + void setMaxCols(unsigned int); + unsigned int getMaxRows(void) const; + unsigned int getMaxCols(void) const; + + private: + std::vector **agentGrid; + std::map *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 diff --git a/include/kami/random.hpp b/include/kami/random.hpp new file mode 100644 index 0000000..0ba8be3 --- /dev/null +++ b/include/kami/random.hpp @@ -0,0 +1,33 @@ +#pragma once +#ifndef KAMI_RANDOM_HPP +#define KAMI_RANDOM_HPP + +#include +#include +#include +#include +#include +#include + +namespace kami { + +class RandomScheduler : public Scheduler { + public: + RandomScheduler(Model *); + virtual ~RandomScheduler(); + + void addAgent(AgentID); + void deleteAgent(AgentID); + void step(); + + private: + std::vector agentList; + std::random_device rd; + std::mt19937 rng{rd()}; + Model *model; + int stepCounter; +}; + +}; // namespace kami + +#endif // KAMI_RANDOM_HPP diff --git a/include/kami/scheduler.hpp b/include/kami/scheduler.hpp new file mode 100644 index 0000000..2c2d41a --- /dev/null +++ b/include/kami/scheduler.hpp @@ -0,0 +1,37 @@ +/*- + * TODO FILEHEADER + */ + +#pragma once +#ifndef KAMI_SCHEDULER_HPP +#define KAMI_SCHEDULER_HPP + +#include + +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 diff --git a/setup.sh b/setup.sh deleted file mode 100755 index 1cfb69d..0000000 --- a/setup.sh +++ /dev/null @@ -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 " -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 diff --git a/src/agent.cpp b/src/agent.cpp new file mode 100644 index 0000000..28348c3 --- /dev/null +++ b/src/agent.cpp @@ -0,0 +1,52 @@ +/* + * + */ + +#include "kami/agent.hpp" + +#include + +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 diff --git a/src/example.cpp b/src/example.cpp deleted file mode 100644 index ab5d7b5..0000000 --- a/src/example.cpp +++ /dev/null @@ -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 diff --git a/src/kami.cpp b/src/kami.cpp new file mode 100644 index 0000000..a243663 --- /dev/null +++ b/src/kami.cpp @@ -0,0 +1,10 @@ +#include "kami/model.hpp" +#include "kami/scheduler.hpp" + +namespace kami { + +Model::~Model() {} + +Scheduler::~Scheduler() {} + +} // namespace kami diff --git a/src/multigrid2d.cpp b/src/multigrid2d.cpp new file mode 100644 index 0000000..399946e --- /dev/null +++ b/src/multigrid2d.cpp @@ -0,0 +1,189 @@ +/*- + * TODO FILEHEADER + */ + +#include "kami/multigrid2d.hpp" + +#include +#include +#include + +#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 *[newMaxCols]; + for (unsigned int i = 0; i < newMaxRows; i++) + agentGrid[i] = new std::vector[newMaxRows]; + + agentIndex = new std::map; +} + +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, 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(location.getXCoord())][static_cast(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(maxCols) && yCoord >= 0 && yCoord < static_cast(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 MultiGrid2D::getNeighborhood(AgentID agentID, NeighborhoodType nType, bool includeCenter) const { + MultiGrid2DCoord location = getLocationByAgent(agentID); + + return getNeighborhood(location, nType, includeCenter); +} + +std::vector MultiGrid2D::getNeighborhood(MultiGrid2DCoord location, NeighborhoodType nType, bool includeCenter) const { + std::vector 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 *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(maxCols)) % static_cast(maxCols); + if (rowWrap == true) + yCoord = (yCoord + static_cast(maxRows)) % static_cast(maxRows); + return MultiGrid2DCoord(xCoord, yCoord); +} + +} // namespace kami diff --git a/src/random.cpp b/src/random.cpp new file mode 100644 index 0000000..2aee5a6 --- /dev/null +++ b/src/random.cpp @@ -0,0 +1,49 @@ +/* + * + */ + +#include "kami/random.hpp" + +#include +#include + +#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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a8c1df7..1ac10c7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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 ) diff --git a/tests/dummy.cpp b/tests/dummy.cpp index 045318b..c2ac6ad 100644 --- a/tests/dummy.cpp +++ b/tests/dummy.cpp @@ -1,11 +1,5 @@ -#include "doctest.h" -#include "example.h" +#include -// 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"; } diff --git a/tests/main.cpp b/tests/main.cpp deleted file mode 100644 index d2bc7a6..0000000 --- a/tests/main.cpp +++ /dev/null @@ -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.