diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..5056f68 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,89 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "main", "develop" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main", "develop" ] + schedule: + - cron: '38 4 * * 1' + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Debug + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + - name: Install Conan + id: conan + uses: turtlebrowser/get-conan@main + + - name: Conan The Frogarian + run: conan frogarian + + - name: Conan Profile Setup + run: | + conan config init + conan profile update settings.compiler.libcxx=libstdc++11 default + + - name: Conan Install Dependencies + run: conan install -if build . --build=missing + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + # Build your program with the given configuration + run: cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Test + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: cmake --build build --target test --config ${{env.BUILD_TYPE}} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..f391381 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,25 @@ +# This CITATION.cff file was generated with cffinit. +# Visit https://bit.ly/cffinit to generate yours today! + +cff-version: 1.2.0 +title: Kami is Agent-Based Modeling in Modern C++ +message: >- + If you use this software, please cite it using the + metadata from this file. +type: software +license: MIT +repository-code: "https://github.com/JHUAPL/kami" +authors: + - given-names: James Patrick + family-names: Howard + name-suffix: II + email: james.howard@jhu.edu + affiliation: Johns Hopkins Applied Physics Laboratory + orcid: 'https://orcid.org/0000-0003-4530-1547' +keywords: + - "agent-based modelling" + - research +identifiers: + - description: "This is the collection of archived snapshots of all versions of Kami" + type: doi + value: 10.5281/zenodo.6975259 diff --git a/CMakeLists.txt b/CMakeLists.txt index 218e085..0e6bcf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.13) set(PROJECT_NAME "kami") set(VERSION_MAJOR 0) -set(VERSION_MINOR 6) +set(VERSION_MINOR 7) set(VERSION_PATCH 0) set(VERSION_STRING ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) diff --git a/COPYRIGHT b/COPYRIGHT index a5dd55d..25849ba 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,4 +1,4 @@ -Copyright (c) 2020-2022 The Johns Hopkins University Applied Physics +Copyright (c) 2020-2023 The Johns Hopkins University Applied Physics Laboratory LLC Permission is hereby granted, free of charge, to any person diff --git a/README.md b/README.md index cb4ec0b..c15fe3e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Documentation status](https://readthedocs.org/projects/kami/badge/?version=main)](https://kami.readthedocs.io/en/main/) [![Release status](https://img.shields.io/github/release/JHUAPL/kami.svg)](https://github.com/JHUAPL/kami/releases) ![License](https://img.shields.io/github/license/JHUAPL/kami) +[![DOI](https://img.shields.io/badge/DOI-10.5281%2Fzenodo.6975259-success.svg)](https://doi.org/10.5281/zenodo.6975259) # Kami is Agent-Based Modeling in Modern C++ diff --git a/conanfile.py b/conanfile.py index b62312b..bae149b 100644 --- a/conanfile.py +++ b/conanfile.py @@ -3,7 +3,7 @@ class KamiConan(ConanFile): name = "kami" - version = "0.6.0" + version = "0.7.0" license = "MIT" author = "James P. Howard, II " url = "https://github.com/jhuapl/kami" @@ -49,3 +49,4 @@ def requirements(self): self.requires("cli11/2.2.0") self.requires("neargye-semver/0.3.0") self.requires("gtest/cci.20210126") + self.requires("nlohmann_json/3.11.1") diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 5d74687..4e41126 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -1425,17 +1425,6 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX @@ -1578,7 +1567,7 @@ EXTRA_SEARCH_MAPPINGS = # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. -GENERATE_LATEX = NO +GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1714,16 +1703,6 @@ LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # http://en.wikipedia.org/wiki/BibTeX and \cite for more info. @@ -1796,16 +1775,6 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_SOURCE_CODE = NO - #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -1895,15 +1864,6 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- @@ -2082,15 +2042,6 @@ EXTERNAL_PAGES = YES # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2123,30 +2074,6 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTNAME = Helvetica - -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTPATH = - # If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for # each documented class showing the direct and indirect inheritance relations. # Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. @@ -2347,18 +2274,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support diff --git a/docs/abm.rst b/docs/abm.rst new file mode 100644 index 0000000..16ebd5e --- /dev/null +++ b/docs/abm.rst @@ -0,0 +1,40 @@ +About Agent-Based Models +======================== + +Agent-based models (ABM) are a type of computational model used +to simulate the behavior of autonomous agents within a system. These +agents can be individuals, groups, organizations, or other entities +that interact with one another and with their environment. + +One of the key features of ABMs is that they focus on the micro-level +interactions between individual agents, rather than aggregating +data to study macro-level phenomena. This allows for the examination +of complex behaviors that emerge from the interactions between +agents, such as the spread of a disease or the formation of social +networks. + +ABMs are often used in fields such as economics, sociology, and +biology to study the behavior of individuals and groups. They can +also be used to simulate the effects of different policies or +interventions on a system. + +In order to create an ABM, the researcher must first define the +agents and their characteristics, such as their behavior, beliefs, +and goals. They must also define the rules of interaction between +the agents and their environment. Once these parameters are set, +the model can be run to simulate the behavior of the agents over +time. + +ABMs are a powerful tool for understanding complex systems, but +they also have their limitations. Because they focus on micro-level +interactions, they may not accurately capture macro-level phenomena. +Additionally, they often require a significant amount of computational +resources and can be difficult to validate. + +Overall, agent-based models are a valuable tool for understanding +the behavior of complex systems and the emergence of complex behaviors +from the interactions between individuals. However, it is important +to use them in conjunction with other methods to fully understand +the system being studied. + +.. toctree:: diff --git a/docs/changelog.rst b/docs/changelog.rst index b298ee7..c3509e1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,17 @@ Changelog ========= +- :release:`0.7.0 <2023.01.22>` +- :support:`0` Added a minimal example and tutorial +- :support:`0` Added documentation for each example +- :feature:`0` Readded step() to the Model interface +- :feature:`0` Moved to exception-based error handling +- :feature:`0` Added Bank Reserves model to demonstrate reporting +- :feature:`0` Added data collecting and reporting modules +- :feature:`0` Added some useful constants, for use as random seeds +- :feature:`0` Switched from ranlux24 to mt19937 due to speed +- :feature:`0` Added distance measures to grid coordinate objects + - :release:`0.6.0 <2022.08.19>` - :feature:`0` Added a to do list to the documentation - :feature:`0` Completed basic unit tests diff --git a/docs/conf.py b/docs/conf.py index e8dcce7..fd378a7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,7 +27,7 @@ def configureDoxyfile(input_dir, output_dir): # -- Project information ----------------------------------------------------- project = 'Kami' -copyright = '2020-2022 The Johns Hopkins University Applied Physics Laboratory LLC' +copyright = '2020-2023 The Johns Hopkins University Applied Physics Laboratory LLC' author = 'James P. Howard, II ' # -- General configuration --------------------------------------------------- @@ -94,11 +94,11 @@ def configureDoxyfile(input_dir, output_dir): # The short X.Y version. github_ref = os.getenv("GITHUB_REF", "") if github_ref == "": - git_ref = "master" + git_ref = "main" else: match = re.match(r"refs/(heads|tags|pull)/(?P.+)", github_ref) if not match: - git_ref = "master" + git_ref = "main" else: git_ref = match.group("ref") diff --git a/docs/examples/bankreserves.rst b/docs/examples/bankreserves.rst new file mode 100644 index 0000000..be915f9 --- /dev/null +++ b/docs/examples/bankreserves.rst @@ -0,0 +1,56 @@ +bankreserves +============ + +This example provides a two-dimensional bank reserves model (BSM) +as an example of a simple application of the reporter classes for +monitoring the internal functioning of the model. + +The BSM is a type of computational model that simulates the behavior +of customers and their interactions with a bank. It is used to study +the dynamics of the money supply and the management of reserves by +the bank. + +In a BSM, individuals are represented as autonomous agents that +make decisions about saving, borrowing, and repaying loans based +on their individual objectives and constraints. The bank is also +represented as an agent that maintains accounts for each individual. +The interactions between individuals and the bank are simulated +over time, and the model can be used to study the effects of different +reserve requirements policies on the creation of money, borrowing, +lending, and savings. + +One of the main advantages of a BSM is that it allows for the +examination of the micro-level interactions between individuals and +the bank, which can provide a more detailed understanding of the +dynamics of the monetary system. + +It is important to note that BSMs are a simplified representation +of the real world and may not capture all the nuances of the monetary +system being studied. It's also important to use this model in +conjunction with other methods to fully understand the monetary +system. + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Option + - Description + * - -c *agent_count* + - Set the number of agents + * - -f *output_file_name* + - Set the JSON report file + * - -l *log_level_option* + - Set the logging level + * - -n *max_steps* + - Set the number of steps to run the model + * - -s *initial_seed* + - Set the initial seed + * - -x *x_size* + - Set the number of columns + * - -y *y_size* + - Set the number of rows + * - -w *max_initial_wealth* + - Set the maximum initial agent wealth + +.. toctree:: diff --git a/docs/examples/boltzmann1d.rst b/docs/examples/boltzmann1d.rst new file mode 100644 index 0000000..958190c --- /dev/null +++ b/docs/examples/boltzmann1d.rst @@ -0,0 +1,60 @@ +boltzmann1d +=========== + +This example provides a one-dimensional Boltzmann wealth model (BWM) +as an example of a simple application of the one-dimensional gridded +system. + +The BWM is a type of agent-based model used to study the distribution +of wealth among individuals or agents within a population. The model +is named after the physicist Ludwig Boltzmann, who first proposed +a similar model to study the distribution of energy among particles +in a gas. + +In a BWM, agents are assigned a certain amount of wealth, and the +model simulates their interactions over time. These interactions +can include buying and selling goods and services, lending and +borrowing money, and inheriting wealth from other agents. + +The key feature of the BWM is that it incorporates a "wealth-exchange +mechanism" which determines the probability of agents making a +wealth exchange with each other. This mechanism is often based on +the difference in wealth between agents, with wealthier agents more +likely to make exchanges with other wealthy agents. + +The model can be run for a specified number of time steps, and the +resulting wealth distribution can be analyzed to study the emergence +of wealth inequality and the factors that contribute to it. The +model can also be used to study the effects of different policies +or interventions on the wealth distribution. + +The BWM has been used to study a variety of different economic +systems, including capitalist, socialist, and feudal systems. +However, it is important to note that like other agent-based models, +the BWM is a simplified representation of the real world and may +not capture all the nuances of the economic system being studied. + +Overall, the BWM is a useful tool for studying the distribution of +wealth and the emergence of wealth inequality in a population. It +can provide insight into the factors that contribute to wealth +inequality and the effects of different policies on the distribution +of wealth. + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Option + - Description + * - -c *agent_count* + - Set the number of agents + * - -l *log_level_option* + - Set the logging level + * - -n *max_steps* + - Set the number of steps to run the model + * - -s *initial_seed* + - Set the initial seed + * - -x *x_size* + - Set the number of columns + +.. toctree:: diff --git a/docs/examples/boltzmann2d.rst b/docs/examples/boltzmann2d.rst new file mode 100644 index 0000000..1ef9624 --- /dev/null +++ b/docs/examples/boltzmann2d.rst @@ -0,0 +1,43 @@ +boltzmann2d +=========== + +This example provides a two-dimensional Boltzmann wealth model (BWM) +as an example of a simple application of the two-dimensional gridded +system. + +The BWM is a type of agent-based model used to study the distribution +of wealth among individuals within a population. The model simulates +agents' interactions over time, such as buying and selling goods, +lending and borrowing money, and inheriting wealth. The model is +based on a "wealth-exchange mechanism" which determines the probability +of agents making a wealth exchange with each other, it is often +based on the difference in wealth between agents. The model can be +run for a specified number of time steps, and the resulting wealth +distribution can be analyzed to study the emergence of wealth +inequality and the factors that contribute to it. + +For more information on BWMs, please see the boltzmann1d_ +example documentation. + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Option + - Description + * - -c *agent_count* + - Set the number of agents + * - -l *log_level_option* + - Set the logging level + * - -n *max_steps* + - Set the number of steps to run the model + * - -s *initial_seed* + - Set the initial seed + * - -x *x_size* + - Set the number of columns + * - -y *y_size* + - Set the number of rows + +.. _boltzmann1d: boltzmann1d.html + +.. toctree:: diff --git a/docs/examples/index.rst b/docs/examples/index.rst new file mode 100644 index 0000000..b9b7674 --- /dev/null +++ b/docs/examples/index.rst @@ -0,0 +1,21 @@ +Examples +======== + +* bankreserves_ +* boltzmann1d_ +* boltzmann2d_ +* starter_ + +.. _bankreserves: bankreserves.html +.. _boltzmann1d: boltzmann1d.html +.. _boltzmann2d: boltzmann2d.html +.. _starter: starter.html + +.. toctree:: + :hidden: + :maxdepth: 1 + + bankreserves + boltzmann1d + boltzmann2d + starter diff --git a/docs/examples/starter.rst b/docs/examples/starter.rst new file mode 100644 index 0000000..207a609 --- /dev/null +++ b/docs/examples/starter.rst @@ -0,0 +1,24 @@ +starter +======= + +This example provides a starter scaffold for beginning a new +agent-based model (ABM). The agents and models perform no real +functions in the starter and is likely to be the most minimum +functioning model. + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Option + - Description + * - -c *agent_count* + - Set the number of agents + * - -l *log_level_option* + - Set the logging level + * - -n *max_steps* + - Set the number of steps to run the model + * - -s *initial_seed* + - Set the initial seed + +.. toctree:: diff --git a/docs/index.rst b/docs/index.rst index 5e86665..a70dcf0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,6 +15,9 @@ Introduction :alt: Release Status .. image:: https://img.shields.io/github/license/JHUAPL/kami :alt: License Information +.. image:: https://img.shields.io/badge/DOI-10.5281%2Fzenodo.6975259-success.svg + :target: https://doi.org/10.5281/zenodo.6975259 + :alt: Package DOI Kami is Agent-Based Modeling in Modern C++. @@ -59,8 +62,10 @@ model. :maxdepth: 2 installation + abm tutorial api/library_root + examples/index changelog todo license \ No newline at end of file diff --git a/docs/license.rst b/docs/license.rst index 083d898..dc8b7c5 100644 --- a/docs/license.rst +++ b/docs/license.rst @@ -1,7 +1,7 @@ License ======= -Copyright (c) 2020-2022 The Johns Hopkins University Applied Physics +Copyright (c) 2020-2023 The Johns Hopkins University Applied Physics Laboratory LLC Permission is hereby granted, free of charge, to any person diff --git a/docs/todo.rst b/docs/todo.rst index f1c0cd8..97479c9 100644 --- a/docs/todo.rst +++ b/docs/todo.rst @@ -7,9 +7,8 @@ The list below is a list of things considered necessary before a 1.0 release. This list is *not* static. - Network domain -- Data collection process -- Demonstration of data collection process -- Tutorial write up +- Hexgrid domain +- Continuous grid domain Wishlist -------- @@ -17,8 +16,8 @@ The list below is a list of things considered nice to have at any point. - Revise unit tests to take advantage of fixtures -- Documentation with basic introduction to ABMs - Network Boltzmann model example - Additional examples as appropriate +- Globe domain, on sphere, with latitude and longitude .. toctree:: diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 5a2d51e..7bcde4d 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -1,55 +1,108 @@ Tutorial ======== -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer -varius est at dignissim sodales. Nullam mauris velit, imperdiet sit -amet neque nec, fringilla consectetur odio. In non erat varius, -fringilla felis ut, sodales orci. Aliquam in turpis ultricies enim -accumsan commodo. Duis at dolor quis dolor tristique suscipit eget -at magna. Integer non eros vitae ipsum pellentesque pharetra ac sed -sapien. Duis justo diam, bibendum ut ullamcorper ac, viverra sit -amet risus. Curabitur blandit nisl ac posuere fermentum. Nulla -convallis purus id velit pellentesque tempus. Pellentesque euismod -augue non diam eleifend fermentum. Vestibulum ante ipsum primis in -faucibus orci luctus et ultrices posuere cubilia curae; - -Nulla iaculis orci neque, a rhoncus mi vestibulum vitae. Nam ut -gravida magna. Nam vel dignissim lacus, id accumsan orci. Nullam -cursus, dui nec finibus sagittis, nisi purus feugiat tortor, a -aliquet quam metus eget enim. Cras et quam vitae nisi auctor varius -eget vel lacus. In nibh orci, tempus eu odio et, euismod sodales -nulla. Fusce luctus sit amet orci non interdum. Ut cursus volutpat -feugiat. Nulla vitae ultricies augue. Donec orci dolor, convallis -non tincidunt sit amet, consectetur ut nibh. Cras efficitur dictum -eros, faucibus pretium odio rutrum at. - -Phasellus lobortis ex nec felis iaculis tincidunt. Sed consequat -sagittis urna at lobortis. Cras velit lorem, iaculis non felis et, -sodales tempus erat. Mauris in ultricies metus. Ut bibendum nisl -vel lectus consequat, vel pharetra est ultrices. Aliquam non lobortis -massa. Mauris euismod turpis mi, eu tempor lectus molestie in. Donec -auctor ante sed eros scelerisque volutpat. Morbi semper diam vitae -ante feugiat, eu hendrerit felis aliquet. Sed placerat velit sit -amet odio suscipit, a posuere lectus hendrerit. Nulla felis augue, -cursus a tempus vitae, ullamcorper a ante. Aenean et elit mi. -Suspendisse potenti. Mauris ac enim libero. Donec finibus id enim -ut ullamcorper. Suspendisse eu imperdiet tellus. - -Cras commodo vitae massa ac blandit. Donec ut mauris at lectus -congue euismod in eleifend felis. Mauris id sapien orci. Cras ac -enim et lectus fringilla vestibulum. Aliquam varius est mattis -condimentum finibus. Nunc tristique justo nec nunc mollis, sit amet -tempor neque iaculis. Class aptent taciti sociosqu ad litora torquent -per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum -primis in faucibus orci luctus et ultrices posuere cubilia curae; -In commodo molestie porttitor. Duis blandit ligula a purus bibendum -volutpat id in metus. Cras bibendum vel ex in accumsan. Phasellus -congue ex eu scelerisque consectetur. - -Maecenas pellentesque eget quam ac pellentesque. Morbi id tempus -urna. In accumsan molestie neque nec imperdiet. Nam ultricies lacinia -magna. Nullam dictum, massa ac fermentum rhoncus, lacus eros -pellentesque ante, sed sollicitudin eros est in dui. Interdum et -malesuada fames ac ante ipsum primis in faucibus. Integer porttitor, -ante id bibendum volutpat, mi nunc mollis eros, sed auctor turpis -mi et sem. +Kami's interface is heavily influenced by Mesa_'s interface. However, +by being written in C++, Kami runs substantially faster. This +allows for faster runs and more runs within a fixed amount of time. +The advantage here is that an agent-based model (ABM) built on the +Kami platform is better suited for statistical and Monte Carlo +approaches to modelling. + +Model Form +---------- + +Kami-based models have five key components: + +1. Agents, which are objects representing the actors within the model +2. Populations, which are collections of Agents +3. Domains, which provide a representation of "physical" space the Agent inhabits +4. Schedulers, which provide a representation of "time" within the model +5. Model, which are objects connecting Populations, Domains, and Schedulers + +In general, a model should have one scheduler, one domain, and some +number of agents. However, it would not be impossible to have more +than one scheduler or more than one domain. Because this is +implemented in C++, your agents should subclass Agent and your model +should subclass model. The schedulers and domains are sufficient +as is for their purposes though custom schedulers and domains are +not unreasonable. + +A Minimal ABM +------------- + +The minimal ABM starts with the simplest possible agent. Here, we +create a class called ``MinimalAgent``: + +.. code-block:: c++ + :linenos: + + class MinimalAgent : public kami::Agent { + public: + kami::AgentID step(std::shared_ptr model) override { + return this->get_agent_id(); + } + }; + +An ``Agent``, and its subclasses, will automatically inherit an ``AgentID``, +which is the unique identifier for the session. The only explicit +requirement on the ``Agent`` subclass is a `step()` method that accepts +a ``shared_ptr`` to a ``Model`` and it must return the ``Agent``'s ``AgentID``. +Obviously, an ``Agent`` should do something useful before returning. + +The second component is ``MinimalModel:`` + +.. code-block:: c++ + :linenos: + + class MinimalModel: public kami::Model { + public: + MinimalModel() { + auto sched = std::make_shared(); + set_scheduler(sched); + + auto pop = std::make_shared(); + set_population(pop); + + for (auto i = 0; i < 10; i++) { + auto new_agent = std::make_shared(); + pop->add_agent(new_agent); + } + } + }; + +The ``MinimalModel`` performs some important tasks that important to do +during the setup or soon thereafter. In the constructor, first, a +scheduler is created. The ``SequentialScheduler`` is the simplest +scheduler and has no configuration needed. Using `set_scheduler()`, +part of the Model class, the scheduler is associated with this +model. Second, a `Population` is created and associated with this +model with the `set_population()` method. + +After this, the constructor initializes 10 ``MinimalAgents`` and adds +them to the population. + +.. code-block:: c++ + :linenos: + + int main() { + auto model = std::make_shared(); + + for (int i = 0; i < 10; i++) + model->step(); + + return 0; + } + +The last part is our `main()` function. It creates the `MinimalModel` +then executes its `step()` method 10 times. The `step()` method, by +default, calls the `step()` method of the scheduler. In the case of +the ``SequentialScheduler``, it loops over all the ``Agent`` instances in the +``Population`` and executes the associated `step()` method of each ``Agent``. + +That is it. It is the simplest minimal model that can be created +using the Kami platform. However, for a basis, it is likely better +to use the starter model, included in the examples directory. + +.. _Mesa: https://mesa.readthedocs.io + +.. toctree:: diff --git a/examples/bankreserves/CMakeLists.txt b/examples/bankreserves/CMakeLists.txt new file mode 100644 index 0000000..d70fcd8 --- /dev/null +++ b/examples/bankreserves/CMakeLists.txt @@ -0,0 +1,22 @@ +#### +# Set minimum version of CMake. +cmake_minimum_required(VERSION 3.13) + +find_package(spdlog) + +set(EXAMPLE_NAME "bankreserves") + +project(${EXAMPLE_NAME} LANGUAGES CXX) + +file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc") + +create_executable( + NAME ${EXAMPLE_NAME} + SOURCES ${EXAMPLE_SOURCES} + PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1 + PRIVATE_DEFINITIONS DEBUG_VERBOSE + PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include + PUBLIC_LINKED_TARGETS fmt spdlog::spdlog kami::libkami +) + +set_target_properties(${EXAMPLE_NAME} PROPERTIES VERSION ${VERSION_STRING}) diff --git a/examples/bankreserves/bankreserves.cc b/examples/bankreserves/bankreserves.cc new file mode 100644 index 0000000..1da813a --- /dev/null +++ b/examples/bankreserves/bankreserves.cc @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 2022 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "bankreserves.h" + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +std::shared_ptr console = nullptr; +std::shared_ptr rng = nullptr; + +#pragma clang diagnostic push +#pragma ide diagnostic ignored "EmptyDeclOrStmt" + +int main( + int argc, + char** argv +) { + std::string ident = "bankreserves"; + std::string log_level_option = "info"; + std::string output_file_name = ident + ".json"; + CLI::App app{ident}; + unsigned int + initial_seed = kami::Constants::JENNYS_NUMBER, + max_steps = 100, + x_size = 20, + y_size = 20, + agent_count = x_size * y_size, + max_initial_wealth = 10; + + // This exercise is really stupid. + auto levels_list = std::make_unique>(); + for (auto& level_name : SPDLOG_LEVEL_NAMES) + levels_list->push_back(std::string(level_name.data(), level_name.size())); + + app.add_option("-c", agent_count, "Set the number of agents")->check(CLI::PositiveNumber); + app.add_option("-f", output_file_name, "Set the JSON report file")->check(CLI::ExistingPath); + app.add_option("-l", log_level_option, "Set the logging level")->check( + CLI::IsMember(levels_list.get(), CLI::ignore_case)); + app.add_option("-n", max_steps, "Set the number of steps to run the model")->check(CLI::PositiveNumber); + app.add_option("-s", initial_seed, "Set the initial seed")->check(CLI::Number); + app.add_option("-x", x_size, "Set the number of columns")->check(CLI::PositiveNumber); + app.add_option("-y", y_size, "Set the number of rows")->check(CLI::PositiveNumber); + app.add_option("-w", max_initial_wealth, "Set the maximum initial agent wealth")->check(CLI::PositiveNumber); + CLI11_PARSE(app, argc, argv); + + console = spdlog::stdout_color_st(ident); + console->set_level(spdlog::level::from_str(log_level_option)); + console->info("Compiled with Kami/{}, log level {}", kami::version.to_string(), log_level_option); + + console->info("Starting Bank Reserves Model with {} agents for {} steps", agent_count, max_steps); + auto model = std::make_shared(agent_count, x_size, y_size, initial_seed, max_initial_wealth); + spdlog::stopwatch sw; + for (int i = 0; i < max_steps; i++) + model->step(); + console->info("Bank Reserves Model simulation complete, requiring {} seconds", sw); + + auto rpt = model->report(); + std::ofstream output_file(output_file_name); + output_file << *rpt; + console->info("JSON data report written to {}", output_file_name); + console->trace("Done."); +} + +#pragma clang diagnostic pop diff --git a/examples/bankreserves/bankreserves.h b/examples/bankreserves/bankreserves.h new file mode 100644 index 0000000..7ceb283 --- /dev/null +++ b/examples/bankreserves/bankreserves.h @@ -0,0 +1,195 @@ +/*- + * Copyright (c) 2022 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once +#ifndef BANKRESERVES_H +//! @cond SuppressGuard +#define BANKRESERVES_H +//! @endcond + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +extern std::shared_ptr console; +extern std::shared_ptr rng; + +template<> +struct fmt::formatter + : fmt::formatter { + static auto format( + kami::AgentID agent_id, + format_context& ctx + ) { + return format_to(ctx.out(), "{}", agent_id.to_string()); + } +}; + +template<> +struct fmt::formatter + : fmt::formatter { + static auto format( + const kami::GridCoord2D& coord, + format_context& ctx + ) { + return format_to(ctx.out(), "{}", coord.to_string()); + } +}; + +/** + * A starter agent for a starter model + */ +class BankAgent + : public kami::ReporterAgent { +public: + /** + * Constructor + */ + explicit BankAgent(int reserve_percent) + :_reserve_percent(reserve_percent) { + }; + + inline std::unique_ptr collect() override { + auto ret = std::make_unique(); + + (*ret)["reserves"] = _reserves; + (*ret)["available"] = _available_to_loan; + + return std::move(ret); + } + + inline kami::AgentID step(std::shared_ptr model) override { + return get_agent_id(); + }; + + int bank_balance() { + _reserves = (_reserve_percent / 100) * _deposits; + return _available_to_loan = _deposits - (_reserves + _bank_loans); + } + +private: + double _bank_loans = 0; + double _reserve_percent = 0; + double _deposits = 0; + double _reserves = (_reserve_percent / 100) * _deposits; + double _available_to_loan = 0; + + friend class PersonAgent; +}; + +class PersonAgent + : public kami::ReporterAgent { +public: + /** + * Constructor + */ + explicit PersonAgent( + int wallet, + std::shared_ptr& bank + ) + : + _wallet(wallet), _bank(bank) { + }; + + inline std::unique_ptr collect() override { + auto ret = std::make_unique(); + + (*ret)["savings"] = _savings; + (*ret)["loans"] = _loans; + (*ret)["wallet"] = _wallet; + (*ret)["wealth"] = _wealth; + + return std::move(ret); + } + + /** + * Execute a single time-step for the agent + */ + kami::AgentID step(std::shared_ptr model) override; + +private: + int _step_counter = 0; + double _savings = 0; + double _loans = 0; + double _wallet = 0; + double _wealth = 0; + std::shared_ptr _bank; + + /** + * Move the agent to a random location on the world + */ + std::optional move_agent(std::shared_ptr& model); + + std::optional do_business(std::shared_ptr& model); + + std::optional balance_books(std::shared_ptr& model); + + kami::AgentID deposit_to_savings(double amount); + + kami::AgentID withdraw_from_savings(double amount); + + kami::AgentID repay_a_loan(double amount); + + kami::AgentID take_out_loan(double amount); +}; + +/** + * The one-dimensional Boltzmann wealth model + */ +class BankReservesModel + : public kami::ReporterModel { +public: + /** + * Create an instance of the one-dimensional Boltzmann wealth model. + * + * @param[in] number_agents the number of agents to assign to the model. + * @param[in] length_x the length of the one-dimensional world the agents + * occupy. + */ + explicit BankReservesModel( + unsigned int agent_count, + unsigned int x_size, + unsigned int y_size, + unsigned int initial_seed, + unsigned int max_initial_wealth + ); + + inline std::unique_ptr collect() override { + return nullptr; + } +}; + +#endif // BANKRESERVES_H diff --git a/examples/bankreserves/brmodel.cc b/examples/bankreserves/brmodel.cc new file mode 100644 index 0000000..68c023b --- /dev/null +++ b/examples/bankreserves/brmodel.cc @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2022 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "bankreserves.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +BankReservesModel::BankReservesModel( + unsigned int agent_count, + unsigned int x_size, + unsigned int y_size, + unsigned int initial_seed, + unsigned int max_initial_wealth +) { + rng = std::make_shared(); + rng->seed(initial_seed); + + auto domain = std::make_shared(x_size, y_size, true, true); + auto sched = std::make_shared(rng); + auto pop = std::make_shared(); + + std::static_pointer_cast(set_scheduler(sched)); + std::static_pointer_cast(set_domain(domain)); + std::static_pointer_cast(set_population(pop)); + + console->debug("Scheduler initiated with seed {}", initial_seed); + auto bank_agent = std::make_shared(10); + pop->add_agent(bank_agent); + + _step_count = 0; + + std::uniform_int_distribution dist_x(0, (int) x_size - 1); + std::uniform_int_distribution dist_y(0, (int) y_size - 1); + std::uniform_int_distribution dist(1, max_initial_wealth); + + for (auto i = 0; i < agent_count; i++) { + auto wallet = dist(*rng); + auto new_agent = std::make_shared(10, bank_agent); + + pop->add_agent(new_agent); + domain->add_agent(new_agent->get_agent_id(), kami::GridCoord2D(dist_x(*rng), dist_x(*rng))); + console->trace("Initialized agent with AgentID {} with wallet {}", new_agent->get_agent_id(), wallet); + } +} \ No newline at end of file diff --git a/examples/bankreserves/person.cc b/examples/bankreserves/person.cc new file mode 100644 index 0000000..34ddd4a --- /dev/null +++ b/examples/bankreserves/person.cc @@ -0,0 +1,183 @@ +/*- + * Copyright (c) 2022 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "bankreserves.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +kami::AgentID PersonAgent::step(std::shared_ptr model) { + console->trace("step() called for agent {}", get_agent_id()); + move_agent(model); + do_business(model); + balance_books(model); + _bank->bank_balance(); + + return get_agent_id(); +} + +std::optional PersonAgent::move_agent(std::shared_ptr& model) { + console->trace("move_agent() called for agent {}", get_agent_id()); + auto agent_id = get_agent_id(); + + auto domain = model->get_domain(); + auto world = std::static_pointer_cast(domain); + + auto move_list = world->get_neighborhood(agent_id, false, kami::GridNeighborhoodType::Moore); + std::uniform_int_distribution dist(0, (int) move_list->size() - 1); + auto new_location = *std::next(move_list->begin(), dist(*rng)); + + console->trace("Moving Agent {} to location {}", agent_id, new_location); + world->move_agent(agent_id, new_location); + + return new_location; +} + +std::optional PersonAgent::do_business(std::shared_ptr& model) { + console->trace("do_business() called for agent {}", get_agent_id()); + auto agent_id = get_agent_id(); + + if (!(_savings > 0 | _wallet > 0 | _bank->_available_to_loan > 0)) + return agent_id; + + auto world = std::static_pointer_cast(model->get_domain()); + auto population = model->get_population(); + + auto location = world->get_location_by_agent(agent_id); + auto cell_mates_opt = world->get_location_contents(location); + + if (!cell_mates_opt) + return std::nullopt; + + // Note, here we reverse the logic from that used in the Mesa + // implementation. We prefer the guard clause to the nested + // if statements. See Fowler. + auto cell_mates = cell_mates_opt; + if (cell_mates->size() < 2) + return std::nullopt; + + auto customer_id = agent_id; + std::uniform_int_distribution dist(0, (int) cell_mates->size() - 1); + do { // There aren't enough do loops, ya know? + customer_id = *std::next(cell_mates->begin(), dist(*rng)); + } while (customer_id == agent_id); + + std::bernoulli_distribution coin_flip(0.5); + if (coin_flip(*rng)) + return std::nullopt; + + // If true, trade_amount = 5, if false, trade_amount = 2 + // Dropping the conditional should make this faster, but + // really, this is just more elegant. + auto trade_amount = (int) std::round(coin_flip(*rng)) * 3 + 2; + + auto customer = std::static_pointer_cast(population->get_agent_by_id(customer_id)); + console->debug("Agent {} trading amount {} with agent {}", agent_id, trade_amount, customer_id); + customer->_wallet += trade_amount; + _wallet -= trade_amount; + + return customer_id; +} + +std::optional PersonAgent::balance_books(std::shared_ptr& model) { + console->debug( + "balance_books() called for agent {} with wallet {}, savings {}, loans {}", get_agent_id(), _wallet, + _savings, _loans); + + if (_wallet < 0) { + if (_savings >= -_wallet) { + withdraw_from_savings(-_wallet); + } else { + if (_savings > 0) { + withdraw_from_savings(_savings); + } + auto temp_loan = _bank->_available_to_loan; + if (temp_loan >= -_wallet) { + take_out_loan(-_wallet); + } else { + take_out_loan(temp_loan); + } + } + } else { + deposit_to_savings(_wallet); + } + + if (_loans > 0 & _savings > 0) { + if (_savings > _loans) { + withdraw_from_savings(_loans); + repay_a_loan(_loans); + } else { + withdraw_from_savings(_savings); + repay_a_loan(_wallet); + } + } + + _wealth = _savings - _loans; + console->debug("balance_books() exiting for agent {}, and wealth {}", get_agent_id(), _wealth); + return _wealth; +} + +kami::AgentID PersonAgent::deposit_to_savings(double amount) { + console->debug("deposit_to_savings() called for agent {}, and amount {}", get_agent_id(), amount); + _wallet -= amount; + _savings += amount; + _bank->_deposits += amount; + return get_agent_id(); +} + +kami::AgentID PersonAgent::withdraw_from_savings(double amount) { + console->debug("withdraw_from_savings() called for agent {}, and amount {}", get_agent_id(), amount); + _wallet += amount; + _savings -= amount; + _bank->_deposits -= amount; + return get_agent_id(); +} + +kami::AgentID PersonAgent::repay_a_loan(double amount) { + console->debug("repay_a_loan() called for agent {}, and amount {}", get_agent_id(), amount); + _loans -= amount; + _wallet -= amount; + _bank->_available_to_loan += amount; + _bank->_bank_loans -= amount; + return get_agent_id(); +} + +kami::AgentID PersonAgent::take_out_loan(double amount) { + console->debug("take_out_loan() called for agent {}, and amount {}", get_agent_id(), amount); + _loans += amount; + _wallet += amount; + _bank->_available_to_loan -= amount; + _bank->_bank_loans += amount; + return get_agent_id(); +} diff --git a/examples/boltzmann1d/CMakeLists.txt b/examples/boltzmann1d/CMakeLists.txt index 52a42d7..822a013 100644 --- a/examples/boltzmann1d/CMakeLists.txt +++ b/examples/boltzmann1d/CMakeLists.txt @@ -8,9 +8,11 @@ set(EXAMPLE_NAME "boltzmann1d") project(${EXAMPLE_NAME} LANGUAGES CXX) +file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc") + create_executable( NAME ${EXAMPLE_NAME} - SOURCES boltzmann1d.cc + SOURCES ${EXAMPLE_SOURCES} PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1 PRIVATE_DEFINITIONS DEBUG_VERBOSE PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include diff --git a/examples/boltzmann1d/boltzmann1d.cc b/examples/boltzmann1d/boltzmann1d.cc index c0f1ac7..649e554 100644 --- a/examples/boltzmann1d/boltzmann1d.cc +++ b/examples/boltzmann1d/boltzmann1d.cc @@ -25,13 +25,11 @@ #include "boltzmann1d.h" -#include #include #include #include #include #include -#include #include @@ -46,18 +44,26 @@ #include std::shared_ptr console = nullptr; -std::shared_ptr rng = nullptr; +std::shared_ptr rng = nullptr; -template <> -struct fmt::formatter : fmt::formatter { - static auto format(kami::AgentID agent_id, format_context &ctx) { +template<> +struct fmt::formatter + : fmt::formatter { + static auto format( + kami::AgentID agent_id, + format_context& ctx + ) { return format_to(ctx.out(), "{}", agent_id.to_string()); } }; template<> -struct fmt::formatter : fmt::formatter { - static auto format(const kami::GridCoord1D &coord, format_context &ctx) { +struct fmt::formatter + : fmt::formatter { + static auto format( + const kami::GridCoord1D& coord, + format_context& ctx + ) { return format_to(ctx.out(), "{}", coord.to_string()); } }; @@ -84,12 +90,12 @@ std::optional MoneyAgent1D::move_agent(std::shared_ptrget_domain(); if (!domain) throw (std::domain_error("model is missing domain")); - auto world = std::static_pointer_cast(domain.value()); + auto world = std::static_pointer_cast(domain); auto move_list_opt = world->get_neighborhood(agent_id, false); if (!move_list_opt) return std::nullopt; - auto move_list = move_list_opt.value(); + auto move_list = move_list_opt; std::uniform_int_distribution dist(0, (int) move_list->size() - 1); auto new_location = *std::next(move_list->begin(), dist(*rng)); @@ -107,26 +113,26 @@ std::optional MoneyAgent1D::give_money(std::shared_ptrget_domain(); if (!domain) throw (std::domain_error("model is missing domain")); - auto world = std::static_pointer_cast(domain.value()); + auto world = std::static_pointer_cast(domain); auto agents = model->get_population(); if (!agents) throw (std::domain_error("model is missing population")); - auto population = std::static_pointer_cast(agents.value()); + auto population = std::static_pointer_cast(agents); auto location = world->get_location_by_agent(agent_id); - auto cell_mates_opt = world->get_location_contents(location.value()); + auto cell_mates_opt = world->get_location_contents(location); if (!cell_mates_opt) return std::nullopt; - auto cell_mates = cell_mates_opt.value(); + auto cell_mates = cell_mates_opt; if (cell_mates->size() < 2) return std::nullopt; std::uniform_int_distribution dist(0, (int) cell_mates->size() - 1); auto other_agent_id = *std::next(cell_mates->begin(), dist(*rng)); - auto other_agent = std::static_pointer_cast(population->get_agent_by_id(other_agent_id).value()); + auto other_agent = std::static_pointer_cast(population->get_agent_by_id(other_agent_id)); console->trace("Agent {} giving unit of wealth to agent {}", agent_id, other_agent_id); other_agent->_agent_wealth += 1; @@ -136,8 +142,12 @@ std::optional MoneyAgent1D::give_money(std::shared_ptr(); +BoltzmannWealthModel1D::BoltzmannWealthModel1D( + unsigned int number_agents, + unsigned int length_x, + unsigned int new_seed +) { + rng = std::make_shared(); rng->seed(new_seed); auto domain = std::make_shared(length_x, true); @@ -169,7 +179,13 @@ std::shared_ptr BoltzmannWealthModel1D::step() { return shared_from_this(); } -int main(int argc, char **argv) { +#pragma clang diagnostic push +#pragma ide diagnostic ignored "EmptyDeclOrStmt" + +int main( + int argc, + char** argv +) { std::string ident = "boltzmann1d"; std::string log_level_option = "info"; CLI::App app{ident}; @@ -177,7 +193,7 @@ int main(int argc, char **argv) { // This exercise is really stupid. auto levels_list = std::make_unique>(); - for (auto &level_name: SPDLOG_LEVEL_NAMES) + for (auto& level_name : SPDLOG_LEVEL_NAMES) levels_list->push_back(std::string(level_name.data(), level_name.size())); app.add_option("-c", agent_count, "Set the number of agents")->check(CLI::PositiveNumber); @@ -191,8 +207,9 @@ int main(int argc, char **argv) { console = spdlog::stdout_color_st(ident); console->set_level(spdlog::level::from_str(log_level_option)); console->info("Compiled with Kami/{}, log level {}", kami::version.to_string(), log_level_option); - console->info("Starting Boltzmann Wealth Model with {} agents on a {}-unit grid for {} steps", agent_count, x_size, - max_steps); + console->info( + "Starting Boltzmann Wealth Model with {} agents on a {}-unit grid for {} steps", agent_count, x_size, + max_steps); spdlog::stopwatch sw; @@ -202,3 +219,5 @@ int main(int argc, char **argv) { console->info("Boltzmann Wealth Model simulation complete, requiring {} seconds", sw); } + +#pragma clang diagnostic pop diff --git a/examples/boltzmann1d/boltzmann1d.h b/examples/boltzmann1d/boltzmann1d.h index be38c89..b6844af 100644 --- a/examples/boltzmann1d/boltzmann1d.h +++ b/examples/boltzmann1d/boltzmann1d.h @@ -44,13 +44,16 @@ /** * A sample agent for a one-dimensional Boltzmann wealth model */ -class MoneyAgent1D : public kami::Agent { +class MoneyAgent1D + : public kami::Agent { public: /** * Create the agent */ - MoneyAgent1D() : _step_counter(0), _agent_wealth(1) {} + MoneyAgent1D() + :_step_counter(0), _agent_wealth(1) { + } /** * Deconstruct the agent @@ -81,7 +84,8 @@ class MoneyAgent1D : public kami::Agent { /** * The one-dimensional Boltzmann wealth model */ -class BoltzmannWealthModel1D : public kami::Model { +class BoltzmannWealthModel1D + : public kami::Model { public: /** @@ -91,16 +95,19 @@ class BoltzmannWealthModel1D : public kami::Model { * @param[in] length_x the length of the one-dimensional world the agents * occupy. */ - explicit BoltzmannWealthModel1D(unsigned int number_agents = 10, unsigned int length_x = 10, unsigned int new_seed = 42); + explicit BoltzmannWealthModel1D( + unsigned int number_agents = 10, + unsigned int length_x = 10, + unsigned int new_seed = 42 + ); /** * Execute a single time-step for the model. */ - std::shared_ptr step(); + std::shared_ptr step() final; private: unsigned int _step_count; - }; #endif // BOLTZMANN1D_H diff --git a/examples/boltzmann2d/CMakeLists.txt b/examples/boltzmann2d/CMakeLists.txt index 246d5fe..cee8cd2 100644 --- a/examples/boltzmann2d/CMakeLists.txt +++ b/examples/boltzmann2d/CMakeLists.txt @@ -8,9 +8,11 @@ set(EXAMPLE_NAME "boltzmann2d") project(${EXAMPLE_NAME} LANGUAGES CXX) +file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc") + create_executable( NAME ${EXAMPLE_NAME} - SOURCES boltzmann2d.cc + SOURCES ${EXAMPLE_SOURCES} PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1 PRIVATE_DEFINITIONS DEBUG_VERBOSE PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include diff --git a/examples/boltzmann2d/boltzmann2d.cc b/examples/boltzmann2d/boltzmann2d.cc index bb91463..0c0a423 100644 --- a/examples/boltzmann2d/boltzmann2d.cc +++ b/examples/boltzmann2d/boltzmann2d.cc @@ -25,13 +25,11 @@ #include "boltzmann2d.h" -#include #include #include #include #include #include -#include #include @@ -46,18 +44,26 @@ #include std::shared_ptr console = nullptr; -std::shared_ptr rng = nullptr; +std::shared_ptr rng = nullptr; -template <> -struct fmt::formatter : fmt::formatter { - static auto format(kami::AgentID agent_id, format_context &ctx) { +template<> +struct fmt::formatter + : fmt::formatter { + static auto format( + kami::AgentID agent_id, + format_context& ctx + ) { return format_to(ctx.out(), "{}", agent_id.to_string()); } }; template<> -struct fmt::formatter : fmt::formatter { - static auto format(const kami::GridCoord2D &coord, format_context &ctx) { +struct fmt::formatter + : fmt::formatter { + static auto format( + const kami::GridCoord2D& coord, + format_context& ctx + ) { return format_to(ctx.out(), "{}", coord.to_string()); } }; @@ -77,19 +83,19 @@ kami::AgentID MoneyAgent2D::step(std::shared_ptr model) { return this->get_agent_id(); } -std::optional MoneyAgent2D::move_agent(std::shared_ptr model) { +std::optional MoneyAgent2D::move_agent(const std::shared_ptr& model) { console->trace("Entering move_agent"); auto agent_id = get_agent_id(); auto domain = model->get_domain(); if (!domain) throw (std::domain_error("model is missing domain")); - auto world = std::static_pointer_cast(domain.value()); + auto world = std::static_pointer_cast(domain); auto move_list_opt = world->get_neighborhood(agent_id, false, kami::GridNeighborhoodType::VonNeumann); if (!move_list_opt) return std::nullopt; - auto move_list = move_list_opt.value(); + auto move_list = move_list_opt; std::uniform_int_distribution dist(0, (int) move_list->size() - 1); auto new_location = *std::next(move_list->begin(), dist(*rng)); @@ -100,33 +106,33 @@ std::optional MoneyAgent2D::move_agent(std::shared_ptr MoneyAgent2D::give_money(std::shared_ptr model) { +std::optional MoneyAgent2D::give_money(const std::shared_ptr& model) { console->trace("Entering give_money"); auto agent_id = get_agent_id(); auto domain = model->get_domain(); if (!domain) throw (std::domain_error("model is missing domain")); - auto world = std::static_pointer_cast(domain.value()); + auto world = std::static_pointer_cast(domain); auto agents = model->get_population(); if (!agents) throw (std::domain_error("model is missing population")); - auto population = std::static_pointer_cast(agents.value()); + auto population = std::static_pointer_cast(agents); auto location = world->get_location_by_agent(agent_id); - auto cell_mates_opt = world->get_location_contents(location.value()); + auto cell_mates_opt = world->get_location_contents(location); if (!cell_mates_opt) return std::nullopt; - auto cell_mates = cell_mates_opt.value(); + auto cell_mates = cell_mates_opt; if (cell_mates->size() < 2) return std::nullopt; std::uniform_int_distribution dist(0, (int) cell_mates->size() - 1); auto other_agent_id = *std::next(cell_mates->begin(), dist(*rng)); - auto other_agent = std::static_pointer_cast(population->get_agent_by_id(other_agent_id).value()); + auto other_agent = std::static_pointer_cast(population->get_agent_by_id(other_agent_id)); console->trace("Agent {} giving unit of wealth to agent {}", agent_id, other_agent_id); other_agent->_agent_wealth += 1; @@ -136,8 +142,13 @@ std::optional MoneyAgent2D::give_money(std::shared_ptr(); +BoltzmannWealthModel2D::BoltzmannWealthModel2D( + unsigned int number_agents, + unsigned int length_x, + unsigned int length_y, + unsigned int new_seed +) { + rng = std::make_shared(); rng->seed(new_seed); auto domain = std::make_shared(length_x, length_y, true, true); @@ -170,7 +181,13 @@ std::shared_ptr BoltzmannWealthModel2D::step() { return shared_from_this(); } -int main(int argc, char **argv) { +#pragma clang diagnostic push +#pragma ide diagnostic ignored "EmptyDeclOrStmt" + +int main( + int argc, + char** argv +) { std::string ident = "boltzmann2d"; std::string log_level_option = "info"; CLI::App app{ident}; @@ -178,7 +195,7 @@ int main(int argc, char **argv) { // This exercise is really stupid. auto levels_list = std::make_unique>(); - for (auto &level_name: SPDLOG_LEVEL_NAMES) + for (auto& level_name : SPDLOG_LEVEL_NAMES) levels_list->push_back(std::string(level_name.data(), level_name.size())); app.add_option("-c", agent_count, "Set the number of agents")->check(CLI::PositiveNumber); @@ -193,8 +210,9 @@ int main(int argc, char **argv) { console = spdlog::stdout_color_st(ident); console->set_level(spdlog::level::from_str(log_level_option)); console->info("Compiled with Kami/{}, log level {}", kami::version.to_string(), log_level_option); - console->info("Starting Boltzmann Wealth Model with {} agents on a {}x{}-unit grid for {} steps", agent_count, - x_size, y_size, max_steps); + console->info( + "Starting Boltzmann Wealth Model with {} agents on a {}x{}-unit grid for {} steps", agent_count, + x_size, y_size, max_steps); spdlog::stopwatch sw; @@ -204,3 +222,5 @@ int main(int argc, char **argv) { console->info("Boltzmann Wealth Model simulation complete, requiring {} seconds", sw); } + +#pragma clang diagnostic pop diff --git a/examples/boltzmann2d/boltzmann2d.h b/examples/boltzmann2d/boltzmann2d.h index 8e52426..4641a95 100644 --- a/examples/boltzmann2d/boltzmann2d.h +++ b/examples/boltzmann2d/boltzmann2d.h @@ -43,13 +43,16 @@ /** * A sample agent for a two-dimensional Boltzmann wealth model */ -class MoneyAgent2D : public kami::Agent { +class MoneyAgent2D + : public kami::Agent { public: /** * Create the agent */ - MoneyAgent2D() : _step_counter(0), _agent_wealth(1) {} + MoneyAgent2D() + :_step_counter(0), _agent_wealth(1) { + } /** * Deconstruct the agent @@ -64,23 +67,23 @@ class MoneyAgent2D : public kami::Agent { /** * Move the agent to a random location on the world */ - std::optional move_agent(std::shared_ptr model); + std::optional move_agent(const std::shared_ptr& model); /** * Give money to a random agent */ - std::optional give_money(std::shared_ptr model); + std::optional give_money(const std::shared_ptr& model); private: int _step_counter; int _agent_wealth; - }; /** * The two-dimensional Boltzmann wealth model */ -class BoltzmannWealthModel2D : public kami::Model { +class BoltzmannWealthModel2D + : public kami::Model { public: /** @@ -94,16 +97,20 @@ class BoltzmannWealthModel2D : public kami::Model { * @param[in] new_seed the initial seed used for the random number * generator. */ - explicit BoltzmannWealthModel2D(unsigned int number_agents = 10, unsigned int length_x = 10, unsigned int length_y = 10, unsigned int new_seed = 42); + explicit BoltzmannWealthModel2D( + unsigned int number_agents = 10, + unsigned int length_x = 10, + unsigned int length_y = 10, + unsigned int new_seed = 42 + ); /** * Execute a single time-step for the model. */ - std::shared_ptr step(); + std::shared_ptr step() final; private: unsigned int _step_count; - }; #endif // BOLTZMANN2D_H diff --git a/examples/minimal/CMakeLists.txt b/examples/minimal/CMakeLists.txt new file mode 100644 index 0000000..bb09e34 --- /dev/null +++ b/examples/minimal/CMakeLists.txt @@ -0,0 +1,22 @@ +#### +# Set minimum version of CMake. +cmake_minimum_required(VERSION 3.13) + +find_package(spdlog) + +set(EXAMPLE_NAME "minimal") + +project(${EXAMPLE_NAME} LANGUAGES CXX) + +file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc") + +create_executable( + NAME ${EXAMPLE_NAME} + SOURCES ${EXAMPLE_SOURCES} + PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1 + PRIVATE_DEFINITIONS DEBUG_VERBOSE + PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include + PUBLIC_LINKED_TARGETS fmt spdlog::spdlog kami::libkami +) + +set_target_properties(${EXAMPLE_NAME} PROPERTIES VERSION ${VERSION_STRING}) diff --git a/examples/minimal/minimal.cc b/examples/minimal/minimal.cc new file mode 100644 index 0000000..9c5d40c --- /dev/null +++ b/examples/minimal/minimal.cc @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2023 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +class MinimalAgent + : public kami::Agent { +public: + kami::AgentID step(std::shared_ptr model) override { + return this->get_agent_id(); + } +}; + +class MinimalModel + : public kami::Model { +public: + MinimalModel() { + auto sched = std::make_shared(); + set_scheduler(sched); + + auto pop = std::make_shared(); + set_population(pop); + + for (auto i = 0; i < 10; i++) { + auto new_agent = std::make_shared(); + pop->add_agent(new_agent); + } + } +}; + +int main() { + auto model = std::make_shared(); + + for (int i = 0; i < 10; i++) + model->step(); + + return 0; +} diff --git a/examples/starter/CMakeLists.txt b/examples/starter/CMakeLists.txt index d0e96a7..1f07f4e 100644 --- a/examples/starter/CMakeLists.txt +++ b/examples/starter/CMakeLists.txt @@ -8,9 +8,11 @@ set(EXAMPLE_NAME "starter") project(${EXAMPLE_NAME} LANGUAGES CXX) +file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc") + create_executable( NAME ${EXAMPLE_NAME} - SOURCES starter.cc + SOURCES ${EXAMPLE_SOURCES} PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1 PRIVATE_DEFINITIONS DEBUG_VERBOSE PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include diff --git a/examples/starter/starter.cc b/examples/starter/starter.cc index 693d63b..0225651 100644 --- a/examples/starter/starter.cc +++ b/examples/starter/starter.cc @@ -43,11 +43,15 @@ #include std::shared_ptr console = nullptr; -std::shared_ptr rng = nullptr; - -template <> -struct fmt::formatter : fmt::formatter { - static auto format(kami::AgentID agent_id, format_context &ctx) { +std::shared_ptr rng = nullptr; + +template<> +struct fmt::formatter + : fmt::formatter { + static auto format( + kami::AgentID agent_id, + format_context& ctx + ) { return format_to(ctx.out(), "{}", agent_id.to_string()); } }; @@ -65,8 +69,11 @@ kami::AgentID StarterAgent::step(std::shared_ptr model) { return this->get_agent_id(); } -StarterModel::StarterModel(unsigned int number_agents, unsigned int new_seed) { - rng = std::make_shared(); +StarterModel::StarterModel( + unsigned int number_agents, + unsigned int new_seed +) { + rng = std::make_shared(); rng->seed(new_seed); auto sched = std::make_shared(rng); @@ -93,7 +100,10 @@ std::shared_ptr StarterModel::step() { return shared_from_this(); } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { std::string ident = "starter"; std::string log_level_option = "info"; CLI::App app{ident}; @@ -101,7 +111,7 @@ int main(int argc, char **argv) { // This exercise is really stupid. auto levels_list = std::make_unique>(); - for (auto &level_name: SPDLOG_LEVEL_NAMES) + for (auto& level_name : SPDLOG_LEVEL_NAMES) levels_list->push_back(std::string(level_name.data(), level_name.size())); app.add_option("-c", agent_count, "Set the number of agents")->check(CLI::PositiveNumber); diff --git a/examples/starter/starter.h b/examples/starter/starter.h index 5f337ff..d156623 100644 --- a/examples/starter/starter.h +++ b/examples/starter/starter.h @@ -39,7 +39,8 @@ /** * A starter agent for a starter model */ -class StarterAgent : public kami::Agent { +class StarterAgent + : public kami::Agent { private: int _step_counter = 0; @@ -65,7 +66,8 @@ class StarterAgent : public kami::Agent { /** * The one-dimensional Boltzmann wealth model */ -class StarterModel : public kami::Model { +class StarterModel + : public kami::Model { private: unsigned int _step_count; @@ -77,7 +79,10 @@ class StarterModel : public kami::Model { * @param[in] length_x the length of the one-dimensional world the agents * occupy. */ - explicit StarterModel(unsigned int number_agents = 10, unsigned int new_seed = 42); + explicit StarterModel( + unsigned int number_agents = 10, + unsigned int new_seed = 42 + ); /** * Execute a single time-step for the model. diff --git a/include/kami/agent.h b/include/kami/agent.h index 50b2792..71d9765 100644 --- a/include/kami/agent.h +++ b/include/kami/agent.h @@ -33,7 +33,6 @@ #include #include -#include #include namespace kami { @@ -65,14 +64,14 @@ namespace kami { /** * @brief Constructs a new unique identifier. */ - AgentID() : _id(_id_next++) {}; + AgentID(); /** * @brief Convert the identifier to a human-readable string. * * @return a human-readable form of the `AgentID` as `std::string`. */ - [[nodiscard]] std::string to_string() const { return std::to_string(_id); } + [[nodiscard]] std::string to_string() const; /** * @brief Test if two `AgentID` instances are equal. @@ -81,7 +80,10 @@ namespace kami { * @param rhs is the right-hand side of the equality test. * @return true is they are equal and false if not. */ - friend bool operator==(const AgentID &lhs, const AgentID &rhs); + friend bool operator==( + const AgentID& lhs, + const AgentID& rhs + ); /** * @brief Test if two `AgentID` instances are not equal. @@ -90,7 +92,10 @@ namespace kami { * @param rhs is the right-hand side of the equality test. * @return true is they are not equal and false if they are. */ - friend bool operator!=(const AgentID &lhs, const AgentID &rhs); + friend bool operator!=( + const AgentID& lhs, + const AgentID& rhs + ); /** * @brief Test if one AgentID is less than another. @@ -104,7 +109,10 @@ namespace kami { * @return true if `lhs` is "less than" `rhs` as determined by the * underlying implementation of the `AgentID`. */ - friend bool operator<(const AgentID &lhs, const AgentID &rhs); + friend bool operator<( + const AgentID& lhs, + const AgentID& rhs + ); /** * @brief Output an AgentID to the specified output stream @@ -116,7 +124,10 @@ namespace kami { * @param rhs is the `AgentID` to output * @return the output stream for reuse */ - friend std::ostream &operator<<(std::ostream &lhs, const AgentID &rhs); + friend std::ostream& operator<<( + std::ostream& lhs, + const AgentID& rhs + ); }; /** @@ -126,7 +137,7 @@ namespace kami { * implement the `step()` function, to execute a single time step for each * agent. * - * @see `StagedAgent` + * @see `ReporterAgent`, `StagedAgent` */ class LIBKAMI_EXPORT Agent { private: @@ -147,6 +158,8 @@ namespace kami { * agent should perform as part of its time step should be in this function. * * @param model a reference copy of the model + * + * @returns a copy of the AgentID */ virtual AgentID step(std::shared_ptr model) = 0; @@ -164,7 +177,10 @@ namespace kami { * Subclasses of Agent may chose to extend this operator to tighten * the restrictions on the comparison. */ - friend bool operator==(const Agent &lhs, const Agent &rhs); + friend bool operator==( + const Agent& lhs, + const Agent& rhs + ); /** * @brief Compare if two `Agent`s are not the same `Agent`. @@ -180,7 +196,10 @@ namespace kami { * Subclasses of `Agent` may chose to extend this operator to tighten * the restrictions on the comparison. */ - friend bool operator!=(const Agent &lhs, const Agent &rhs); + friend bool operator!=( + const Agent& lhs, + const Agent& rhs + ); }; /** @@ -195,7 +214,8 @@ namespace kami { * * `StagedAgents` must implement both the `step()` and `advance()` functions. */ - class LIBKAMI_EXPORT StagedAgent : public Agent { + class LIBKAMI_EXPORT StagedAgent + : public Agent { public: /** * @brief Post-step advance the agent diff --git a/include/kami/domain.h b/include/kami/domain.h index 7bb4df0..418f060 100644 --- a/include/kami/domain.h +++ b/include/kami/domain.h @@ -82,7 +82,10 @@ namespace kami { * @param rhs is the `Coord` to output * @return the output stream for reuse */ - friend std::ostream &operator<<(std::ostream &lhs, const Coord &rhs); + friend std::ostream& operator<<( + std::ostream& lhs, + const Coord& rhs + ); }; } // namespace kami diff --git a/include/kami/error.h b/include/kami/error.h new file mode 100644 index 0000000..14bda9f --- /dev/null +++ b/include/kami/error.h @@ -0,0 +1,136 @@ +// +// Created by James Howard on 9/9/22. +// + +#ifndef KAMI_ERROR_H +//! @cond SuppressGuard +#define KAMI_ERROR_H +//! @endcond + +#include +#include + +namespace kami::error { + + /** + * @brief Agent was not found + */ + class AgentNotFound + : public std::logic_error { + public: + /** + * @brief Constructor + * @param s text description of the exception + */ + explicit AgentNotFound(const char* s) + :std::logic_error(s) { + }; + + /** + * @brief Constructor + * @param s text description of the exception + */ + explicit AgentNotFound(const std::string& s) + :std::logic_error(s) { + }; + }; + + /** + * @brief Location specified is invalid + * + * @see `LocationUnavailable` + */ + class LocationInvalid + : public std::domain_error { + public: + /** + * @brief Constructor + * @param s text description of the exception + */ + explicit LocationInvalid(const char* s) + :std::domain_error(s) { + }; + + /** + * @brief Constructor + * @param s text description of the exception + */ + explicit LocationInvalid(const std::string& s) + :std::domain_error(s) { + }; + }; + + /** + * @brief Location specified is unavailable + * + * @see `LocationInvalid` + */ + class LocationUnavailable + : public std::domain_error { + public: + /** + * @brief Constructor + * @param s text description of the exception + */ + explicit LocationUnavailable(const char* s) + :std::domain_error(s) { + }; + + /** + * @brief Constructor + * @param s text description of the exception + */ + explicit LocationUnavailable(const std::string& s) + :std::domain_error(s) { + }; + }; + + /** + * @brief The option given is not valid at this time + */ + class OptionInvalid + : public std::invalid_argument { + public: + /** + * @brief Constructor + * @param s text description of the exception + */ + explicit OptionInvalid(const char* s) + :std::invalid_argument(s) { + }; + + /** + * @brief Constructor + * @param s text description of the exception + */ + explicit OptionInvalid(const std::string& s) + :std::invalid_argument(s) { + }; + }; + + /** + * @brief The resource specified is not available at this time + */ + class ResourceNotAvailable + : public std::logic_error { + public: + /** + * @brief Constructor + * @param s text description of the exception + */ + explicit ResourceNotAvailable(const char* s) + :std::logic_error(s) { + }; + + /** + * @brief Constructor + * @param s text description of the exception + */ + explicit ResourceNotAvailable(const std::string& s) + :std::logic_error(s) { + }; + }; + +} + +#endif //KAMI_ERROR_H diff --git a/include/kami/grid.h b/include/kami/grid.h index c9c583e..f8f49e1 100644 --- a/include/kami/grid.h +++ b/include/kami/grid.h @@ -83,7 +83,17 @@ namespace kami { * "taxicab distance," "rectilinear distance," or many other [formal * names](https://en.wikipedia.org/wiki/Taxicab_geometry). */ - Manhattan + Manhattan, + + /** + * @brief Chebyshev distance. + * + * @details The Chebyshev distance, also called the "chessboard" distance + * is the number of single point jumps necessary to move from one point to + * the next. This can be likened to a king on a chessboard and the number + * of moves necessary to move from a given point to any other given point. + */ + Chebyshev }; /** @@ -93,14 +103,38 @@ namespace kami { * rectilinear grid where the cells are equal size and laid out in an ordered * fashion. */ - class LIBKAMI_EXPORT GridDomain : public Domain {}; + class LIBKAMI_EXPORT GridDomain + : public Domain { + }; /** * @brief An abstract for gridded coordinates. * * @details All gridded coordinates are expected to subclass `GridCoord`. */ - class LIBKAMI_EXPORT GridCoord : public Coord {}; + class LIBKAMI_EXPORT GridCoord + : public Coord { + + public: + + /** + * @brief Find the distance between two points + * + * @details Find the distance between two points using the + * specified metric. + * + * However, the coordinate class is not aware of the + * properties of the `GridDomain` it is operating on. Accordingly, + * if the direct path is measured, without accounting for + * and toroidal wrapping of the underlying `GridDomain`. + * + * @param p the point to measure the distance to + * + * @returns the distance as a `double` + */ + virtual double distance(std::shared_ptr& p) const = 0; + + }; } // namespace kami diff --git a/include/kami/grid1d.h b/include/kami/grid1d.h index aaa8386..dfcec23 100644 --- a/include/kami/grid1d.h +++ b/include/kami/grid1d.h @@ -35,8 +35,10 @@ #include #include #include +#include #include +#include #include #include @@ -45,17 +47,18 @@ namespace kami { /** * @brief One-dimensional coordinates */ - class LIBKAMI_EXPORT GridCoord1D : public GridCoord { + class LIBKAMI_EXPORT GridCoord1D + : public GridCoord { public: /** * @brief Constructor for one-dimensional coordinates */ - explicit GridCoord1D(int x_coord) : _x_coord(x_coord){}; + explicit GridCoord1D(int x_coord); /** * @brief Return the `x` coordinate */ - [[nodiscard]] int get_x_location() const; + [[nodiscard]] int x() const; /** * @brief Convert the coordinate to a human-readable string. @@ -64,20 +67,87 @@ namespace kami { */ [[nodiscard]] std::string to_string() const override; + /** + * @brief Find the distance between two points + * + * @details Find the distance between two points using the + * specified metric. There are three options provided by + * the `GridDistanceType` class. However, of the three + * distance types provided, all provide the same result so + * the value is ignored and the single result is returned. + * + * However, the coordinate class is not aware of the + * properties of the `Grid1D` it is operating on. Accordingly, + * if the direct path is measured, without accounting for + * and toroidal wrapping of the underlying `Grid1D`. + * + * @param p the point to measure the distance to + * + * @returns the distance as a `double` + */ + double distance(std::shared_ptr& p) const override; + /** * @brief Test if two coordinates are equal */ - friend bool operator==(const GridCoord1D &lhs, const GridCoord1D &rhs); + friend bool operator==( + const GridCoord1D& lhs, + const GridCoord1D& rhs + ); /** * @brief Test if two coordinates are not equal */ - friend bool operator!=(const GridCoord1D &lhs, const GridCoord1D &rhs); + friend bool operator!=( + const GridCoord1D& lhs, + const GridCoord1D& rhs + ); /** * @brief Output a given coordinate to the specified stream */ - friend std::ostream &operator<<(std::ostream &lhs, const GridCoord1D &rhs); + friend std::ostream& operator<<( + std::ostream& lhs, + const GridCoord1D& rhs + ); + + /** + * @brief Add two coordinates together + */ + inline friend GridCoord1D operator+( + const GridCoord1D& lhs, + const GridCoord1D& rhs + ); + + /** + * @brief Subtract one coordinate from another + */ + inline friend GridCoord1D operator-( + const GridCoord1D& lhs, + const GridCoord1D& rhs + ); + + /** + * @brief Multiply a coordinate by a scalar + * + * @details If any component of the resulting value is not a whole number, it is + * truncated following the same rules as `int`. + */ + inline friend GridCoord1D operator*( + const GridCoord1D& lhs, + double rhs + ); + + /** + * @brief Multiply a coordinate by a scalar + * + * @details If any component of the resulting value is not a whole number, it is + * truncated following the same rules as `int`. + */ + inline friend GridCoord1D operator*( + double lhs, + const GridCoord1D& rhs + ); private: int _x_coord; @@ -91,7 +161,8 @@ namespace kami { * @see `MultiGrid1D` * @see `SoloGrid1D` */ - class LIBKAMI_EXPORT Grid1D : public GridDomain { + class LIBKAMI_EXPORT Grid1D + : public GridDomain { public: /** * @brief Constructor @@ -99,7 +170,10 @@ namespace kami { * @param[in] maximum_x the length of the grid. * @param[in] wrap_x should the grid wrap around on itself. */ - explicit Grid1D(unsigned int maximum_x, bool wrap_x = false); + explicit Grid1D( + unsigned int maximum_x, + bool wrap_x = false + ); /** * @brief Place agent on the grid at the specified location. @@ -110,7 +184,10 @@ namespace kami { * @returns false if the agent is not placed at the specified * location, otherwise, true. */ - virtual std::optional add_agent(const AgentID agent_id, const GridCoord1D &coord) = 0; + virtual AgentID add_agent( + AgentID agent_id, + const GridCoord1D& coord + ) = 0; /** * @brief Remove agent from the grid. @@ -119,7 +196,7 @@ namespace kami { * * @returns the `AgentID` of the `Agent` deleted */ - std::optional delete_agent(const AgentID agent_id); + AgentID delete_agent(AgentID agent_id); /** * @brief Remove agent from the grid at the specified location @@ -129,7 +206,10 @@ namespace kami { * * @returns the `AgentID` of the `Agent` deleted */ - std::optional delete_agent(const AgentID agent_id, const GridCoord1D &coord); + AgentID delete_agent( + AgentID agent_id, + const GridCoord1D& coord + ); /** * @brief Move an agent to the specified location. @@ -137,7 +217,10 @@ namespace kami { * @param[in] agent_id the `AgentID` of the agent to move. * @param[in] coord the coordinates of the agent. */ - std::optional move_agent(const AgentID agent_id, const GridCoord1D &coord); + AgentID move_agent( + AgentID agent_id, + const GridCoord1D& coord + ); /** * @brief Inquire if the specified location is empty. @@ -165,7 +248,7 @@ namespace kami { * * @return the location of the specified `Agent` */ - [[nodiscard]] std::optional get_location_by_agent(const AgentID &agent_id) const; + [[nodiscard]] GridCoord1D get_location_by_agent(const AgentID& agent_id) const; /** * @brief Get the contents of the specified location. @@ -177,8 +260,8 @@ namespace kami { * to that object will update the state of the gird. Further, the pointer * should not be deleted when no longer used. */ - [[nodiscard]] std::optional>> - get_location_contents(const GridCoord1D &coord) const; + [[nodiscard]] std::shared_ptr> + get_location_contents(const GridCoord1D& coord) const; /** * @brief Inquire to whether the grid wraps in the `x` dimension. @@ -197,8 +280,11 @@ namespace kami { * @return an `unordered_set` of `GridCoord1D` that includes all of the coordinates * for all adjacent points. */ - [[nodiscard]] std::optional>> - get_neighborhood(const AgentID agent_id, const bool include_center) const; + [[nodiscard]] std::shared_ptr> + get_neighborhood( + AgentID agent_id, + bool include_center + ) const; /** * @brief Return the neighborhood of the specified location @@ -210,8 +296,11 @@ namespace kami { * @return an `unordered_set` of `GridCoord1D` that includes all of the coordinates * for all adjacent points. */ - [[nodiscard]] std::optional>> - get_neighborhood(const GridCoord1D &coord, const bool include_center) const; + [[nodiscard]] std::shared_ptr> + get_neighborhood( + const GridCoord1D& coord, + bool include_center + ) const; /** * @brief Get the size of the grid in the `x` dimension. @@ -221,6 +310,17 @@ namespace kami { [[nodiscard]] unsigned int get_maximum_x() const; protected: + /** + * @brief Direction coordinates + * + * @details This can be used for addition to coordinates. Direction + * `0` is the first direction clockwise from "vertical." In this + * case, it can be on a vertically-oriented column, upwards, or to + * the right on a horizontally-oriented column. Then the additional + * directions are enumerated clockwise. + */ + const std::vector directions = {GridCoord1D(1), GridCoord1D(-1)}; + /** * @brief An `unordered_set` containing the `AgentID`s of all agents assigned to this * grid. @@ -239,7 +339,7 @@ namespace kami { * * @return the adjusted coordinate wrapped if appropriate. */ - [[nodiscard]] GridCoord1D coord_wrap(const GridCoord1D &coord) const; + [[nodiscard]] GridCoord1D coord_wrap(const GridCoord1D& coord) const; private: unsigned int _maximum_x; @@ -248,13 +348,16 @@ namespace kami { } // namespace kami +//! @cond SuppressHashMethod +#define KAMI_GRID1D_H namespace std { template<> struct hash { - size_t operator()(const kami::GridCoord1D &key) const { - return (hash()(key.get_x_location())); + size_t operator()(const kami::GridCoord1D& key) const { + return (hash()(key.x())); } }; } // namespace std +//! @endcond #endif // KAMI_GRID1D_H diff --git a/include/kami/grid2d.h b/include/kami/grid2d.h index 147bb43..345d624 100644 --- a/include/kami/grid2d.h +++ b/include/kami/grid2d.h @@ -29,12 +29,15 @@ #define KAMI_GRID2D_H //! @endcond +#include +#include #include #include #include #include #include #include +#include #include #include @@ -45,23 +48,26 @@ namespace kami { /** * @brief Two-dimensional coordinates */ - class LIBKAMI_EXPORT GridCoord2D : public GridCoord { + class LIBKAMI_EXPORT GridCoord2D + : public GridCoord { public: /** * @brief Constructor for two-dimensional coordinates */ - GridCoord2D(int x_coord, int y_coord) - : _x_coord(x_coord), _y_coord(y_coord){}; + GridCoord2D( + int x_coord, + int y_coord + ); /** * @brief Get the coordinate in the first dimension or `x`. */ - [[nodiscard]] int get_x_location() const; + [[nodiscard]] int x() const; /** * @brief Get the coordinate in the second dimension or `y`. */ - [[nodiscard]] int get_y_location() const; + [[nodiscard]] int y() const; /** * @brief Convert the coordinate to a human-readable string. @@ -70,20 +76,135 @@ namespace kami { */ [[nodiscard]] std::string to_string() const override; + /** + * @brief Find the distance between two points + * + * @details Find the distance between two points using the + * specified metric. + * + * However, the coordinate class is not aware of the + * properties of the `Grid2D` it is operating on. Accordingly, + * if the direct path is measured, without accounting for + * and toroidal wrapping of the underlying `Grid2D`. + * + * @param p the point to measure the distance to + * + * @returns the distance as a `double` + */ + double distance(std::shared_ptr& p) const override; + + /** + * @brief Find the distance between two points + * + * @details Find the distance between two points using the + * specified metric. There are three options provided by + * the `GridDistanceType` class. + * + * However, the coordinate class is not aware of the + * properties of the `Grid2D` it is operating on. Accordingly, + * if the direct path is measured, without accounting for + * and toroidal wrapping of the underlying `Grid2D`. + * + * @param p the point to measure the distance to + * @param distance_type specify the distance type + * + * @returns the distance as a `double` + */ + double + distance( + std::shared_ptr& p, + GridDistanceType distance_type = GridDistanceType::Euclidean + ) const; + /** * @brief Test if two coordinates are equal */ - friend bool operator==(const GridCoord2D &, const GridCoord2D &); + friend bool operator==( + const GridCoord2D&, + const GridCoord2D& + ); /** * @brief Test if two coordinates are not equal */ - friend bool operator!=(const GridCoord2D &, const GridCoord2D &); + friend bool operator!=( + const GridCoord2D&, + const GridCoord2D& + ); /** * @brief Output a given coordinate to the specified stream */ - friend std::ostream &operator<<(std::ostream &, const GridCoord2D &); + friend std::ostream& operator<<( + std::ostream&, + const GridCoord2D& + ); + + /** + * @brief Add two coordinates together + */ + inline friend GridCoord2D operator+( + const GridCoord2D& lhs, + const GridCoord2D& rhs + ); + + /** + * @brief Subtract one coordinate from another + */ + inline friend GridCoord2D operator-( + const GridCoord2D& lhs, + const GridCoord2D& rhs + ); + + /** + * @brief Multiply a coordinate by a scalar + * + * @details If any component of the resulting value is not a whole number, it is + * truncated following the same rules as `int`. + */ + inline friend GridCoord2D operator*( + const GridCoord2D& lhs, + const double rhs + ); + + /** + * @brief Multiply a coordinate by a scalar + * + * @details If any component of the resulting value is not a whole number, it is + * truncated following the same rules as `int`. + */ + inline friend GridCoord2D operator*( + const double lhs, + const GridCoord2D& rhs + ); + + protected: + /** + * @brief Find the distance between two points using the Chebyshev metric + * + * @param p the point to measure the distance to + * + * @returns the distance as a `double` + */ + inline double distance_chebyshev(std::shared_ptr& p) const; + + /** + * @brief Find the distance between two points using the Euclidean metric + * + * @param p the point to measure the distance to + * + * @returns the distance as a `double` + */ + inline double distance_euclidean(std::shared_ptr& p) const; + + /** + * @brief Find the distance between two points using the Manhattan metric + * + * @param p the point to measure the distance to + * + * @returns the distance as a `double` + */ + inline double distance_manhattan(std::shared_ptr& p) const; private: int _x_coord, _y_coord; @@ -97,7 +218,8 @@ namespace kami { * @see `MultiGrid2D` * @see `SoloGrid2D` */ - class LIBKAMI_EXPORT Grid2D : public GridDomain { + class LIBKAMI_EXPORT Grid2D + : public GridDomain { public: /** * @brief Constructor @@ -109,7 +231,12 @@ namespace kami { * @param[in] wrap_y should the grid wrap around on itself in the second * dimension */ - explicit Grid2D(unsigned int maximum_x, unsigned int maximum_y, bool wrap_x = false, bool wrap_y = false); + explicit Grid2D( + unsigned int maximum_x, + unsigned int maximum_y, + bool wrap_x = false, + bool wrap_y = false + ); /** * @brief Place agent on the grid at the specified location. @@ -120,7 +247,10 @@ namespace kami { * @returns false if the agent is not placed at the specified * location, otherwise, true. */ - virtual std::optional add_agent(const AgentID agent_id, const GridCoord2D &coord) = 0; + virtual AgentID add_agent( + AgentID agent_id, + const GridCoord2D& coord + ) = 0; /** * @brief Remove agent from the grid. @@ -129,7 +259,7 @@ namespace kami { * * @returns false if the agent is not removed, otherwise, true. */ - std::optional delete_agent(const AgentID agent_id); + AgentID delete_agent(AgentID agent_id); /** * @brief Remove agent from the grid at the specified location @@ -139,7 +269,10 @@ namespace kami { * * @returns false if the agent is not removed, otherwise, true. */ - std::optional delete_agent(const AgentID agent_id, const GridCoord2D &coord); + AgentID delete_agent( + AgentID agent_id, + const GridCoord2D& coord + ); /** * @brief Move an agent to the specified location. @@ -147,7 +280,10 @@ namespace kami { * @param[in] agent_id the `AgentID` of the agent to move. * @param[in] coord the coordinates of the agent. */ - std::optional move_agent(const AgentID agent_id, const GridCoord2D &coord); + AgentID move_agent( + AgentID agent_id, + const GridCoord2D& coord + ); /** * @brief Inquire if the specified location is empty. @@ -168,14 +304,14 @@ namespace kami { */ [[nodiscard]] bool is_location_valid(const GridCoord2D& coord) const; - /** + virtual /** * @brief Get the location of the specified agent. * * @param[in] agent_id the `AgentID` of the agent in question. * * @return the location of the specified `Agent` */ - [[nodiscard]] std::optional get_location_by_agent(const AgentID &agent_id) const; + GridCoord2D get_location_by_agent(const AgentID& agent_id) const; /** * @brief Get the contents of the specified location. @@ -187,15 +323,15 @@ namespace kami { * to that object will update the state of the gird. Further, the pointer * should not be deleted when no longer used. */ - [[nodiscard]] std::optional>> - get_location_contents(const GridCoord2D &coord) const; + [[nodiscard]] std::shared_ptr> + get_location_contents(const GridCoord2D& coord) const; /** * @brief Inquire to whether the grid wraps in the `x` dimension. * * @return true if the grid wraps, and false otherwise */ - [[nodiscard]] bool get_wrap_x() const; + [[nodiscard]] bool get_wrap_x() const; /** * @brief Inquire to whether the grid wraps in the `y` dimension. @@ -204,7 +340,7 @@ namespace kami { */ [[nodiscard]] bool get_wrap_y() const; - /** + virtual /** * @brief Return the neighborhood of the specified Agent * * @param[in] agent_id the `AgentID` of the agent in question. @@ -212,13 +348,17 @@ namespace kami { * @param[in] include_center should the center-point, occupied by the agent, * be in the list. * - * @return a set of `GridCoord1D` that includes all of the coordinates + * @return a set of `GridCoord2D` that includes all of the coordinates * for all adjacent points. * * @see `NeighborhoodType` */ - [[nodiscard]] std::optional>> - get_neighborhood(AgentID agent_id, bool include_center, GridNeighborhoodType neighborhood_type) const; + std::shared_ptr> + get_neighborhood( + AgentID agent_id, + bool include_center, + GridNeighborhoodType neighborhood_type + ) const; /** * @brief Return the neighborhood of the specified location @@ -233,8 +373,12 @@ namespace kami { * * @see `NeighborhoodType` */ - [[nodiscard]] std::optional>> - get_neighborhood(const GridCoord2D &coord, bool include_center, GridNeighborhoodType neighborhood_type) const; + [[nodiscard]] std::shared_ptr> + get_neighborhood( + const GridCoord2D& coord, + bool include_center, + GridNeighborhoodType neighborhood_type + ) const; /** * @brief Get the size of the grid in the `x` dimension. @@ -251,6 +395,28 @@ namespace kami { [[nodiscard]] unsigned int get_maximum_y() const; protected: + /** + * @brief von Neumann neighborhood coordinates + * + * @details This can be used for addition to coordinates. Direction + * `0` is the first direction clockwise from "vertical." Then the additional + * directions are enumerated clockwise. + */ + const std::vector directions_vonneumann = {GridCoord2D(0, 1), GridCoord2D(1, 0), + GridCoord2D(0, -1), GridCoord2D(-1, 0)}; + + /** + * @brief Moore neighborhood coordinates + * + * @details This can be used for addition to coordinates. Direction + * `0` is the first direction clockwise from "vertical." Then the additional + * directions are enumerated clockwise. + */ + const std::vector directions_moore = {GridCoord2D(0, 1), GridCoord2D(1, 1), + GridCoord2D(1, 0), GridCoord2D(1, -1), + GridCoord2D(0, -1), GridCoord2D(-1, -1), + GridCoord2D(-1, 0), GridCoord2D(-1, 1)}; + /** * @brief A map containing the `AgentID`s of all agents assigned to this * grid. @@ -269,7 +435,7 @@ namespace kami { * * @return the adjusted coordinate wrapped if appropriate. */ - [[nodiscard]] GridCoord2D coord_wrap(const GridCoord2D &coord) const; + [[nodiscard]] GridCoord2D coord_wrap(const GridCoord2D& coord) const; private: unsigned int _maximum_x, _maximum_y; @@ -278,13 +444,15 @@ namespace kami { } // namespace kami +//! @cond SuppressHashMethod namespace std { template<> struct hash { - size_t operator()(const kami::GridCoord2D &key) const { - return ((hash()(key.get_x_location()) ^ (hash()(key.get_y_location()) << 1)) >> 1); + size_t operator()(const kami::GridCoord2D& key) const { + return ((hash()(key.x()) ^ (hash()(key.y()) << 1)) >> 1); } }; } // namespace std +//! @endcond #endif // KAMI_GRID2D_H diff --git a/include/kami/kami.h b/include/kami/kami.h index d2ed6d2..0e0381e 100644 --- a/include/kami/kami.h +++ b/include/kami/kami.h @@ -38,10 +38,19 @@ namespace kami { // Forward declarations to clean up a lot of include-file madness class Agent; + class AgentID; + class Domain; + class Model; + class Population; + + class ReporterAgent; + + class ReporterModel; + class Scheduler; /** @@ -49,7 +58,31 @@ namespace kami { * * @return a `semver::version` object containing version information */ - inline semver::version get_version() { return version; } + inline semver::version get_version() { + return version; + } + + /** + * @brief A catalog of handy constants, mostly useful for seeding + * a random number generator + */ + class Constants { + public: + /** + * @brief Life, the Universe, and Everything! + */ + static constexpr auto ADAMS_CONSTANT = 42u; + + /** + * @brief Jenny, I've got your number + */ + static constexpr auto JENNYS_NUMBER = 8675309u; + + /** + * @brief $(7^(e - 1/e) - 9) * pi^2$ + */ + static constexpr auto JENNYS_CONSTANT = 867.530901981; + }; } // namespace kami diff --git a/include/kami/model.h b/include/kami/model.h index 60ce064..41a4f6f 100644 --- a/include/kami/model.h +++ b/include/kami/model.h @@ -30,7 +30,6 @@ //! @endcond #include -#include #include #include @@ -41,8 +40,11 @@ namespace kami { /** * @brief An abstract for generic models + * + * @see `ReporterModel` */ - class LIBKAMI_EXPORT Model : public std::enable_shared_from_this { + class LIBKAMI_EXPORT Model + : public std::enable_shared_from_this { public: /** @@ -50,7 +52,7 @@ namespace kami { * * @returns a shared pointer to the `Domain` */ - std::optional> get_domain(); + std::shared_ptr get_domain(); /** * @brief Add a `Domain` to this scheduler @@ -67,7 +69,7 @@ namespace kami { * * @returns a shared pointer to the `Population` */ - std::optional> get_population(); + std::shared_ptr get_population(); /** * @brief Add a `Model` to this scheduler @@ -84,7 +86,7 @@ namespace kami { * * @returns a shared pointer to the `Scheduler` */ - std::optional> get_scheduler(); + std::shared_ptr get_scheduler(); /** * @brief Add a `Model` to this scheduler @@ -96,6 +98,16 @@ namespace kami { */ std::shared_ptr set_scheduler(std::shared_ptr scheduler); + /** + * @brief Execute a single time step of the model + * + * @details This method will collect all the `Agent`s in the `Population` associated + * with model and pass them to the associated `Scheduler` for stepping. + * + * @returns a shared pointer to the model instance + */ + virtual std::shared_ptr step(); + protected: /** * @brief Reference copy of the `Domain` diff --git a/include/kami/multigrid1d.h b/include/kami/multigrid1d.h index 0820dae..e3a7326 100644 --- a/include/kami/multigrid1d.h +++ b/include/kami/multigrid1d.h @@ -45,7 +45,8 @@ namespace kami { * @see `Grid1D` * @see `SoloGrid1D` */ - class LIBKAMI_EXPORT MultiGrid1D : public Grid1D { + class LIBKAMI_EXPORT MultiGrid1D + : public Grid1D { public: /** * @brief Constructor @@ -53,8 +54,10 @@ namespace kami { * @param[in] maximum_x the length of the grid. * @param[in] wrap_x should the grid wrap around on itself. */ - MultiGrid1D(unsigned int maximum_x, bool wrap_x) - : Grid1D(maximum_x, wrap_x) {} + MultiGrid1D( + unsigned int maximum_x, + bool wrap_x + ); /** * @brief Place agent on the grid at the specified location. @@ -65,7 +68,10 @@ namespace kami { * @returns false if the agent is not placed at the specified * location, otherwise, true */ - std::optional add_agent(const AgentID agent_id, const GridCoord1D &coord) override; + AgentID add_agent( + AgentID agent_id, + const GridCoord1D& coord + ) override; }; } // namespace kami diff --git a/include/kami/multigrid2d.h b/include/kami/multigrid2d.h index 821adbe..79634fc 100644 --- a/include/kami/multigrid2d.h +++ b/include/kami/multigrid2d.h @@ -45,7 +45,8 @@ namespace kami { * @see `Grid2D` * @see `SoloGrid2D` */ - class LIBKAMI_EXPORT MultiGrid2D : public Grid2D { + class LIBKAMI_EXPORT MultiGrid2D + : public Grid2D { public: /** * @brief Constructor @@ -57,8 +58,12 @@ namespace kami { * @param[in] wrap_y should the grid wrap around on itself in the second * dimension */ - MultiGrid2D(unsigned int maximum_x, unsigned int maximum_y, bool wrap_x, bool wrap_y) - : Grid2D(maximum_x, maximum_y, wrap_x, wrap_y) {}; + MultiGrid2D( + unsigned int maximum_x, + unsigned int maximum_y, + bool wrap_x, + bool wrap_y + ); /** * @brief Place agent on the grid at the specified location. @@ -66,10 +71,12 @@ namespace kami { * @param[in] agent_id the `AgentID` of the agent to add. * @param[in] coord the coordinates of the agent. * - * @returns false if the agent is not placed at the specified - * location, otherwise, true + * @returns the `AgentID` of the agent added */ - std::optional add_agent(const AgentID agent_id, const GridCoord2D &coord) override; + AgentID add_agent( + AgentID agent_id, + const GridCoord2D& coord + ) override; }; } // namespace kami diff --git a/include/kami/population.h b/include/kami/population.h index be18614..0c36169 100644 --- a/include/kami/population.h +++ b/include/kami/population.h @@ -41,17 +41,6 @@ namespace kami { * @brief An abstract for generic models */ class LIBKAMI_EXPORT Population { - protected: - /** - * @brief A mapping of `AgentID` to `Agent` pointers - * - * @details This is the mapping of all `AgentID`s to - * pointers to the corresponding `Agent` in this population. - * This is left exposed as protected should any subclass - * wish to manipulate this mapping directly. - */ - std::map> _agent_map; - public: /** * @brief Get a reference to an `Agent` by `AgentID` @@ -60,7 +49,7 @@ namespace kami { * * @return a reference to the desired `Agent` or nothing is not found */ - [[nodiscard]] std::optional> get_agent_by_id(AgentID agent_id) const; + [[nodiscard]] std::shared_ptr get_agent_by_id(AgentID agent_id) const; /** * @brief Add an Agent to the Population. @@ -69,7 +58,7 @@ namespace kami { * * @returns the ID of the agent added */ - AgentID add_agent(const std::shared_ptr& agent); + AgentID add_agent(const std::shared_ptr& agent) noexcept; /** * @brief Remove an Agent from the Population. @@ -78,14 +67,25 @@ namespace kami { * * @returns a shared pointer to the Agent deleted */ - std::optional> delete_agent(AgentID agent_id); + std::shared_ptr delete_agent(AgentID agent_id); /** * @brief Returns the agent list. * * @returns a `std::vector` of all the `AgentID`'s in the `Population` */ - [[nodiscard]] std::shared_ptr> get_agent_list() const; + [[nodiscard]] std::unique_ptr> get_agent_list() const; + + protected: + /** + * @brief A mapping of `AgentID` to `Agent` pointers + * + * @details This is the mapping of all `AgentID`s to + * pointers to the corresponding `Agent` in this population. + * This is left exposed as protected should any subclass + * wish to manipulate this mapping directly. + */ + std::map> _agent_map; }; } // namespace kami diff --git a/include/kami/position.h b/include/kami/position.h new file mode 100644 index 0000000..02f53e9 --- /dev/null +++ b/include/kami/position.h @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2020 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once +#ifndef KAMI_POSITION_H +//! @cond SuppressGuard +#define KAMI_POSITION_H +//! @endcond + +#include + +#include +#include + +namespace kami { + + typedef std::variant< + GridCoord1D, + GridCoord2D + > Position; + +} + +#endif //KAMI_POSITION_H diff --git a/include/kami/random.h b/include/kami/random.h index d807693..b454c3e 100644 --- a/include/kami/random.h +++ b/include/kami/random.h @@ -30,7 +30,6 @@ //! @endcond #include -#include #include #include @@ -39,7 +38,6 @@ #include namespace kami { - /** * @brief Will execute all agent steps in a random order. * @@ -48,10 +46,9 @@ namespace kami { * That order should be different for each subsequent call to `step()`, * but is not guaranteed not to repeat. */ - class LIBKAMI_EXPORT RandomScheduler : public SequentialScheduler, std::enable_shared_from_this { - private: - std::shared_ptr _rng = nullptr; - + class LIBKAMI_EXPORT RandomScheduler + : public SequentialScheduler, + std::enable_shared_from_this { public: /** * @brief Constructor. @@ -64,7 +61,7 @@ namespace kami { * @param rng [in] A uniform random number generator of type * `std::mt19937`, used as the source of randomness. */ - explicit RandomScheduler(std::shared_ptr rng); + explicit RandomScheduler(std::shared_ptr rng); /** * @brief Execute a single time step. @@ -77,7 +74,28 @@ namespace kami { * * @returns returns vector of agents successfully stepped */ - std::optional>> step(std::shared_ptr model, std::shared_ptr> agent_list) override; + std::unique_ptr> + step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) override; + + /** + * @brief Execute a single time step for a `ReporterModel` + * + * @details This method will randomize the list of Agents provided + * then execute the `Agent::step()` method for every Agent listed. + * + * @param model a reference copy of the `ReporterModel` + * @param agent_list list of agents to execute the step + * + * @returns returns vector of agents successfully stepped + */ + std::unique_ptr> + step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) override; /** * @brief Set the RNG @@ -90,7 +108,7 @@ namespace kami { * * @returns a shared pointer to the random number generator */ - std::shared_ptr set_rng(std::shared_ptr rng); + std::shared_ptr set_rng(std::shared_ptr rng); /** * @brief Get the RNG @@ -98,7 +116,11 @@ namespace kami { * @details Get a reference to the random number generator used to randomize * the order of agent stepping. */ - std::shared_ptr get_rng(); + std::shared_ptr get_rng(); + + private: + std::shared_ptr _rng = nullptr; + }; } // namespace kami diff --git a/include/kami/reporter.h b/include/kami/reporter.h new file mode 100644 index 0000000..310a47b --- /dev/null +++ b/include/kami/reporter.h @@ -0,0 +1,259 @@ +/*- + * Copyright (c) 2022 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once +#ifndef KAMI_REPORTER_H +//! @cond SuppressGuard +#define KAMI_REPORTER_H +//! @endcond + +#include +#include + +#include + +#include +#include + +namespace kami { + + class Reporter; + + class ReporterAgent; + + class ReporterModel; + + /** + * @brief A superclass for all reporting agents + * + * @details All reporting agents should subclass the `ReportingAgent` class. At a minimum, + * subclasses must implement the `step()` function, to execute a single time step for each + * agent. + * + * @see `Agent`, `StagedAgent` + */ + class LIBKAMI_EXPORT ReporterAgent + : public Agent { + public: + /** + * @brief Collect the current state of the agent + * + * @details This function should collect the agent's + * current state. The agent, notably, does not need + * to collect historical state, as the historical state + * is retained by the `Reporter` instance until such + * time as `Reporter::report()` is called. However, + * the implementation of the collect() function is + * up to the end user who can, ultimately, return whatever + * they want. + * + * The only restriction on collect is that it must return + * its data as a [nlohmann::json](https://json.nlohmann.me/) + * JSON object. See `Reporter` for additional details. + */ + virtual std::unique_ptr collect() = 0; + + /** + * @brief Execute a time-step for the agent + * + * @details This function should step the agent instance. Any activities that the + * agent should perform as part of its time step should be in this function. + * + * @param model a reference copy of the model + * + * @returns a copy of the AgentID + */ + virtual AgentID step(std::shared_ptr model) = 0; + + private: + // This should be uncallable, but knocks out the inherited method. + AgentID step(std::shared_ptr model) override;; + + int _step_counter = 0; + }; + + /** + * @brief An abstract for generic models with a reporting capability + * + * @see `Model` + */ + class LIBKAMI_EXPORT ReporterModel + : public Model { + public: + /** + * @brief Constructor + */ + ReporterModel(); + + /** + * @brief Collect the current state of the model + * + * @details This function should collect the model's + * current state. The model, notably, does not need + * to collect historical state, as the historical state + * is retained by the `Reporter` instance until such + * time as `Reporter::report()` is called. However, + * the implementation of the collect() function is + * up to the end user who can, ultimately, return whatever + * they want. + * + * This is not expected to return agent data collection, + * as the agents' information is collected separately. + */ + virtual std::unique_ptr collect() = 0; + + /** + * @brief Get the step id of the model + * + * @details The step_id should probably be a monotonically + * incrementing integer. + */ + virtual unsigned int get_step_id(); + + /** + * @brief Execute a single time step of the model + * + * @details This method will collect all the `Agent`s in the `Population` associated + * with the model and pass them to the associated `Scheduler` for stepping. After scheduling, + * this method will run the collect() for the `Reporter` associated with this model. + * + * @returns a shared pointer to the model instance + */ + virtual std::shared_ptr step() override; + + /** + * @brief Get the current report + * + * @details This method will return an object containg the data collected to that + * point in the simulation. + * + * @returns a unique pointer to a `nlohmann::json` object representing the current report + */ + std::unique_ptr report(); + + protected: + + /** + * @brief The current report + */ + std::shared_ptr _rpt; + + /** + * @brief The model's current step count + */ + unsigned int _step_count{}; + }; + + /** + * @brief A `Reporter` is a module that works with `ReporterAgent` and `ReporterModel` + * to collect information about the state of the model for later analysis + */ + class LIBKAMI_EXPORT Reporter + : public std::enable_shared_from_this { + public: + /** + * @brief Constructor + */ + Reporter(); + + /** + * @brief Empty the report + * + * @details Clear all entries from the report; new collection + * operations begin with a blank slate. + * + * @returns a reference copy of the `Reporter` + */ + std::shared_ptr clear(); + + /** + * @brief Collect the current state of the model + * + * @details This will collect the current state of + * each agent associated with the population returned + * by the `Model`'s `get_population()`. + * + * @param model reference copy of the model + * + * @returns a copy of the current report + */ + std::unique_ptr collect(const std::shared_ptr& model); + + /** + * @brief Collect the current state of the model + * + * @details This will collect the current state of + * each agent associated with the `Population`. + * + * @param model reference copy of the model + * @param pop Population to collect on + * + * @returns a copy of the current report + */ + std::unique_ptr + collect( + const std::shared_ptr& model, + const std::shared_ptr& pop + ); + + /** + * @brief Collect the current state of the model + * + * @details This will collect the current state of + * each agent given + * + * @param model reference copy of the model + * @param agent_list a vector agents to report on + * + * @returns a copy of the current report + */ + std::unique_ptr + collect( + const std::shared_ptr& model, + const std::unique_ptr>& agent_list + ); + + /** + * @brief Collect the report + * + * @details This will return the aggregate report + * of all the data collected by this `Reporter`. + * + * @param model reference copy of the model + * + * @returns a copy of the current report + */ + std::unique_ptr report(const std::shared_ptr& model); + + protected: + /** + * @brief A vector of the the report collected so far + */ + std::unique_ptr> _report_data = nullptr; + }; + +} + +#endif //KAMI_REPORTER_H diff --git a/include/kami/scheduler.h b/include/kami/scheduler.h index d392581..4ce6d30 100644 --- a/include/kami/scheduler.h +++ b/include/kami/scheduler.h @@ -30,9 +30,10 @@ //! @endcond #include -#include #include +#include +#include #include namespace kami { @@ -44,12 +45,6 @@ namespace kami { * the step function for each agent based on the type of scheduling implemented. */ class LIBKAMI_EXPORT Scheduler { - protected: - /** - * Counter to increment on each step - */ - int _step_counter = 0; - public: /** * @brief Execute a single time step. @@ -63,7 +58,21 @@ namespace kami { * * @returns returns vector of agents successfully stepped */ - virtual std::optional>> step(std::shared_ptr model) = 0; + virtual std::unique_ptr> step(std::shared_ptr model) = 0; + + /** + * @brief Execute a single time step for a `ReporterModel` + * + * @details This method will step through the list of Agents in the + * scheduler's internal queue and then execute the `Agent::step()` + * method for every Agent assigned to this scheduler in the order + * assigned. + * + * @param model a reference copy of the `ReporterModel` + * + * @returns returns vector of agents successfully stepped + */ + virtual std::unique_ptr> step(std::shared_ptr model) = 0; /** * @brief Execute a single time step. @@ -78,7 +87,36 @@ namespace kami { * * @returns returns vector of agents successfully stepped */ - virtual std::optional>> step(std::shared_ptr model, std::shared_ptr> agent_list) = 0; + virtual std::unique_ptr> + step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) = 0; + + /** + * @brief Execute a single time step for a `ReporterModel` + * + * @details This method will step through the list of Agents in the + * scheduler's internal queue and then execute the `Agent::step()` + * method for every Agent assigned to this scheduler in the order + * assigned. + * + * @param model a reference copy of the `ReporterModel` + * @param agent_list list of agents to execute the step + * + * @returns returns vector of agents successfully stepped + */ + virtual std::unique_ptr> + step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) = 0; + + protected: + /** + * Counter to increment on each step + */ + int _step_counter = 0; }; } // namespace kami diff --git a/include/kami/sequential.h b/include/kami/sequential.h index 2466988..7c9b344 100644 --- a/include/kami/sequential.h +++ b/include/kami/sequential.h @@ -30,7 +30,6 @@ //! @endcond #include -#include #include #include @@ -48,7 +47,8 @@ namespace kami { * That order is preserved between calls to `step()` but may be modified by * `addAgent()` or `deleteAgent()`. */ - class LIBKAMI_EXPORT SequentialScheduler : public Scheduler { + class LIBKAMI_EXPORT SequentialScheduler + : public Scheduler { public: /** * @brief Execute a single time step. @@ -62,7 +62,21 @@ namespace kami { * * @returns returns vector of agents successfully stepped */ - std::optional>> step(std::shared_ptr model) override; + std::unique_ptr> step(std::shared_ptr model) override; + + /** + * @brief Execute a single time step for a `ReporterModel` + * + * @details This method will step through the list of Agents in the + * scheduler's internal queue and then execute the `Agent::step()` + * method for every Agent assigned to this scheduler in the order + * assigned. + * + * @param model a reference copy of the `ReporterModel` + * + * @returns returns vector of agents successfully stepped + */ + std::unique_ptr> step(std::shared_ptr model) override; /** * @brief Execute a single time step. @@ -77,7 +91,30 @@ namespace kami { * * @returns returns vector of agents successfully stepped */ - std::optional>> step(std::shared_ptr model, std::shared_ptr> agent_list) override; + std::unique_ptr> + step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) override; + + /** + * @brief Execute a single time step for a `ReporterModel` + * + * @details This method will step through the list of Agents in the + * scheduler's internal queue and then execute the `Agent::step()` + * method for every Agent assigned to this scheduler in the order + * assigned. + * + * @param model a reference copy of the `ReporterModel` + * @param agent_list list of agents to execute the step + * + * @returns returns vector of agents successfully stepped + */ + std::unique_ptr> + step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) override; }; } // namespace kami diff --git a/include/kami/sologrid1d.h b/include/kami/sologrid1d.h index 3572ba9..2a5d7fe 100644 --- a/include/kami/sologrid1d.h +++ b/include/kami/sologrid1d.h @@ -37,14 +37,15 @@ namespace kami { /** - * @brief A one-dimensional grid where each cell may contain one agenta + * @brief A one-dimensional grid where each cell may contain one agents * * @details The grid is linear and may wrap around in its only dimension. * * @see `Grid1D` * @see `MultiGrid1D` */ - class LIBKAMI_EXPORT SoloGrid1D : public Grid1D { + class LIBKAMI_EXPORT SoloGrid1D + : public Grid1D { public: /** * @brief Constructor @@ -52,8 +53,10 @@ namespace kami { * @param[in] maximum_x the length of the grid. * @param[in] wrap_x should the grid wrap around on itself. */ - SoloGrid1D(unsigned int maximum_x, bool wrap_x) - : Grid1D(maximum_x, wrap_x) {} + SoloGrid1D( + unsigned int maximum_x, + bool wrap_x + ); /** * @brief Place agent on the grid at the specified location. @@ -64,7 +67,10 @@ namespace kami { * @returns false if the agent is not placed at the specified * location, otherwise, true */ - std::optional add_agent(const AgentID agent_id, const GridCoord1D &coord) override; + AgentID add_agent( + AgentID agent_id, + const GridCoord1D& coord + ) override; }; } // namespace kami diff --git a/include/kami/sologrid2d.h b/include/kami/sologrid2d.h index 47568f0..0ed66d0 100644 --- a/include/kami/sologrid2d.h +++ b/include/kami/sologrid2d.h @@ -44,7 +44,8 @@ namespace kami { * @see `Grid2D` * @see `MultiGrid2D` */ - class LIBKAMI_EXPORT SoloGrid2D : public Grid2D { + class LIBKAMI_EXPORT SoloGrid2D + : public Grid2D { public: /** * @details Constructor @@ -54,8 +55,12 @@ namespace kami { * @param[in] wrap_x should the grid wrap around on itself in the first dimension * @param[in] wrap_y should the grid wrap around on itself in the second dimension */ - SoloGrid2D(unsigned int maximum_x, unsigned int maximum_y, bool wrap_x, bool wrap_y) - : Grid2D(maximum_x, maximum_y, wrap_x, wrap_y) {}; + SoloGrid2D( + unsigned int maximum_x, + unsigned int maximum_y, + bool wrap_x, + bool wrap_y + );; /** * @details Place agent on the grid at the specified location. @@ -66,7 +71,10 @@ namespace kami { * @returns false if the agent is not placed at the specified * location, otherwise, true */ - std::optional add_agent(const AgentID agent_id, const GridCoord2D &coord) override; + AgentID add_agent( + AgentID agent_id, + const GridCoord2D& coord + ) override; }; diff --git a/include/kami/staged.h b/include/kami/staged.h index 97ae9c6..a4afafb 100644 --- a/include/kami/staged.h +++ b/include/kami/staged.h @@ -30,7 +30,6 @@ //! @endcond #include -#include #include #include @@ -47,7 +46,34 @@ namespace kami { * preserved between calls to `step()` but may be modified by `add_agent()` or * `delete_agent()`. */ - class LIBKAMI_EXPORT StagedScheduler : public SequentialScheduler { + class LIBKAMI_EXPORT StagedScheduler + : public SequentialScheduler { + public: + /** + * @brief Execute a single time step + * + * @details This method will step through the list of Agents in the scheduler's + * internal queue and execute the `Agent::step()` method for each `Agent` + * in the same order. Finally, it will execute the `Agent::advance()` + * method for each Agent in the same order. + * + * @param model a reference copy of the model + * @param agent_list list of agents to execute the step + * + * @returns returns vector of agents successfully stepped + */ + std::unique_ptr> + step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) override; + + std::unique_ptr> + step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) override; + private: /** * @brief Advance a single time step. @@ -61,7 +87,9 @@ namespace kami { * * @returns returns vector of agents successfully advanced */ - std::optional>> advance(std::shared_ptr model); + std::unique_ptr> advance(std::shared_ptr model); + + std::unique_ptr> advance(std::shared_ptr model); /** * @brief Advance a single time step. @@ -76,23 +104,11 @@ namespace kami { * * @returns returns vector of agents successfully advanced */ - std::optional>> advance(std::shared_ptr model, std::shared_ptr> agent_list); - - public: - /** - * @brief Execute a single time step - * - * @details This method will step through the list of Agents in the scheduler's - * internal queue and execute the `Agent::step()` method for each `Agent` - * in the same order. Finally, it will execute the `Agent::advance()` - * method for each Agent in the same order. - * - * @param model a reference copy of the model - * @param agent_list list of agents to execute the step - * - * @returns returns vector of agents successfully stepped - */ - std::optional>> step(std::shared_ptr model, std::shared_ptr> agent_list) override; + std::unique_ptr> + advance( + std::shared_ptr model, + std::unique_ptr> agent_list + ); }; } // namespace kami diff --git a/src/libkami/CMakeLists.txt b/src/libkami/CMakeLists.txt index c7e4fe0..1fa30f2 100644 --- a/src/libkami/CMakeLists.txt +++ b/src/libkami/CMakeLists.txt @@ -10,25 +10,15 @@ project(${LIBRARY_NAME} VERSION ${VERSION_STRING} LANGUAGES CXX) +file(GLOB LIBRARY_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc") + create_library( NAME ${LIBRARY_NAME} NAMESPACE ${LIBRARY_NAME} - SOURCES - agent.cc - domain.cc - grid1d.cc - grid2d.cc - model.cc - multigrid1d.cc - multigrid2d.cc - population.cc - random.cc - sequential.cc - sologrid1d.cc - sologrid2d.cc - staged.cc + SOURCES ${LIBRARY_SOURCES} PUBLIC_INCLUDE_PATHS "$" "$" PRIVATE_LINKED_TARGETS ${COVERAGE_TARGET} + PUBLIC_LINKED_TARGETS fmt EXPORT_FILE_PATH "${CMAKE_CURRENT_BINARY_DIR}/generated_headers/kami/KAMI_EXPORT.h" ) diff --git a/src/libkami/agent.cc b/src/libkami/agent.cc index 33ccbf5..a23391c 100644 --- a/src/libkami/agent.cc +++ b/src/libkami/agent.cc @@ -23,35 +23,65 @@ * SOFTWARE. */ -#include - #include #include +#include + namespace kami { - bool operator==(const AgentID &lhs, const AgentID &rhs) { + AgentID::AgentID() + :_id(_id_next++) { + } + + std::string AgentID::to_string() 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) { + bool operator!=( + const AgentID& lhs, + const AgentID& rhs + ) { return !(lhs == rhs); } - bool operator<(const AgentID &lhs, const AgentID &rhs) { + bool operator<( + const AgentID& lhs, + const AgentID& rhs + ) { return lhs._id < rhs._id; } - std::ostream &operator<<(std::ostream &lhs, const AgentID &rhs) { + std::ostream& operator<<( + std::ostream& lhs, + const AgentID& rhs + ) { return lhs << rhs.to_string(); } - AgentID Agent::get_agent_id() const { return this->_agent_id; } + AgentID Agent::get_agent_id() const { + return this->_agent_id; + } - bool operator==(const Agent &lhs, const Agent &rhs) { + bool operator==( + const Agent& lhs, + const Agent& rhs + ) { return lhs._agent_id == rhs._agent_id; } - bool operator!=(const Agent &lhs, const Agent &rhs) { return !(lhs == rhs); } + bool operator!=( + const Agent& lhs, + const Agent& rhs + ) { + return !(lhs == rhs); + } } // namespace kami diff --git a/src/libkami/domain.cc b/src/libkami/domain.cc index f64f18b..c2c1dcd 100644 --- a/src/libkami/domain.cc +++ b/src/libkami/domain.cc @@ -23,14 +23,17 @@ * SOFTWARE. */ -#include - #include #include +#include + namespace kami { - std::ostream &operator<<(std::ostream &lhs, const Coord &rhs) { + std::ostream& operator<<( + std::ostream& lhs, + const Coord& rhs + ) { return lhs << rhs.to_string(); } diff --git a/src/libkami/grid1d.cc b/src/libkami/grid1d.cc index 165ee00..efa555d 100644 --- a/src/libkami/grid1d.cc +++ b/src/libkami/grid1d.cc @@ -23,21 +23,27 @@ * SOFTWARE. */ -#include -#include -#include - #include #include -#include #include #include #include #include +#include + +#include +#include +#include +#include + namespace kami { - int GridCoord1D::get_x_location() const { + GridCoord1D::GridCoord1D(int x_coord) + :_x_coord(x_coord) { + } + + int GridCoord1D::x() const { return _x_coord; } @@ -45,19 +51,65 @@ namespace kami { return std::string("(" + std::to_string(_x_coord) + ")"); } - bool operator==(const GridCoord1D &lhs, const GridCoord1D &rhs) { + double GridCoord1D::distance(std::shared_ptr& p) const { + auto p1d = std::static_pointer_cast(p); + return static_cast(abs(_x_coord - p1d->_x_coord)); + } + + bool operator==( + const GridCoord1D& lhs, + const GridCoord1D& rhs + ) { return (lhs._x_coord == rhs._x_coord); } - bool operator!=(const GridCoord1D &lhs, const GridCoord1D &rhs) { + bool operator!=( + const GridCoord1D& lhs, + const GridCoord1D& rhs + ) { return !(lhs == rhs); } - std::ostream &operator<<(std::ostream &lhs, const GridCoord1D &rhs) { + std::ostream& operator<<( + std::ostream& lhs, + const GridCoord1D& rhs + ) { return lhs << rhs.to_string(); } - Grid1D::Grid1D(unsigned int maximum_x, bool wrap_x) { + GridCoord1D operator+( + const GridCoord1D& lhs, + const GridCoord1D& rhs + ) { + return GridCoord1D(lhs._x_coord + rhs._x_coord); + } + + GridCoord1D operator-( + const GridCoord1D& lhs, + const GridCoord1D& rhs + ) { + return GridCoord1D(lhs._x_coord - rhs._x_coord); + } + + GridCoord1D operator*( + const GridCoord1D& lhs, + const double rhs + ) { + return GridCoord1D(static_cast(lhs._x_coord * rhs)); + } + + GridCoord1D operator*( + const double lhs, + const GridCoord1D& rhs + ) { + return GridCoord1D(static_cast(rhs._x_coord * lhs)); + } + + + Grid1D::Grid1D( + unsigned int maximum_x, + bool wrap_x + ) { _maximum_x = maximum_x; _wrap_x = wrap_x; @@ -65,89 +117,77 @@ namespace kami { _agent_index = std::make_unique>(); } - std::optional Grid1D::delete_agent(AgentID agent_id) { - auto coord = get_location_by_agent(agent_id); - if (!coord) - return std::nullopt; - - return delete_agent(agent_id, coord.value()); + AgentID Grid1D::delete_agent(AgentID agent_id) { + return delete_agent(agent_id, get_location_by_agent(agent_id)); } - std::optional Grid1D::delete_agent(AgentID agent_id, const GridCoord1D &coord) { - auto agent_location = _agent_grid->find(coord); - if (agent_location == _agent_grid->end()) - return std::nullopt; - - for (auto test_agent_id = agent_location; test_agent_id != _agent_grid->end(); test_agent_id++) + AgentID Grid1D::delete_agent( + AgentID agent_id, + const GridCoord1D& coord + ) { + for (auto test_agent_id = _agent_grid->find(coord); test_agent_id != _agent_grid->end(); test_agent_id++) if (test_agent_id->second == agent_id) { _agent_grid->erase(test_agent_id); _agent_index->erase(agent_id); return agent_id; } - return std::nullopt; + throw error::AgentNotFound( + fmt::format("Agent {} not found at location {}", agent_id.to_string(), coord.to_string())); } - bool Grid1D::is_location_valid(const GridCoord1D &coord) const { - auto x = coord.get_x_location(); + bool Grid1D::is_location_valid(const GridCoord1D& coord) const { + auto x = coord.x(); return (x >= 0 && x < static_cast(_maximum_x)); } - bool Grid1D::is_location_empty(const GridCoord1D &coord) const { + bool Grid1D::is_location_empty(const GridCoord1D& coord) const { auto grid_location = _agent_grid->equal_range(coord); return grid_location.first == grid_location.second; } - std::optional Grid1D::move_agent(const AgentID agent_id, const GridCoord1D &coord) { - auto coord_current = get_location_by_agent(agent_id); - - if (!coord_current) - return std::nullopt; - if (!delete_agent(agent_id, coord_current.value())) - return std::nullopt; - - return add_agent(agent_id, coord); + AgentID Grid1D::move_agent( + const AgentID agent_id, + const GridCoord1D& coord + ) { + return add_agent(delete_agent(agent_id, get_location_by_agent(agent_id)), coord); } - std::optional>> - Grid1D::get_neighborhood(const AgentID agent_id, const bool include_center) const { - auto coord = get_location_by_agent(agent_id); - if (!coord) - return std::nullopt; - - return std::move(get_neighborhood(coord.value(), include_center)); + std::shared_ptr> + Grid1D::get_neighborhood( + const AgentID agent_id, + const bool include_center + ) const { + return std::move(get_neighborhood(get_location_by_agent(agent_id), include_center)); } - std::optional>> - Grid1D::get_neighborhood(const GridCoord1D &coord, const bool include_center) const { + std::shared_ptr> + Grid1D::get_neighborhood( + const GridCoord1D& coord, + const bool include_center + ) const { auto neighborhood = std::make_shared>(); - auto x = coord.get_x_location(); // We assume our starting position is valid if (include_center) neighborhood->insert(coord); - // E, W - { - auto new_location = coord_wrap(GridCoord1D(x + 1)); - if (is_location_valid(new_location)) - neighborhood->insert(coord_wrap(new_location)); - } - { - auto new_location = coord_wrap(GridCoord1D(x - 1)); + for (auto& direction : directions) { + auto new_location = coord_wrap(coord + direction); + if (is_location_valid(new_location)) - neighborhood->insert(coord_wrap(new_location)); + neighborhood->insert(new_location); } return std::move(neighborhood); } - std::optional>> Grid1D::get_location_contents(const GridCoord1D &coord) const { + std::shared_ptr> Grid1D::get_location_contents(const GridCoord1D& coord) const { auto agent_ids = std::make_shared>(); if (!is_location_valid(coord)) - return std::nullopt; + throw error::LocationUnavailable(fmt::format("Coordinates {} are invalid", coord.to_string())); if (is_location_empty(coord)) return agent_ids; @@ -160,19 +200,23 @@ namespace kami { return agent_ids; } - bool Grid1D::get_wrap_x() const { return _wrap_x; } + bool Grid1D::get_wrap_x() const { + return _wrap_x; + } - unsigned int Grid1D::get_maximum_x() const { return _maximum_x; } + unsigned int Grid1D::get_maximum_x() const { + return _maximum_x; + } - std::optional Grid1D::get_location_by_agent(const AgentID &agent_id) const { + GridCoord1D Grid1D::get_location_by_agent(const AgentID& agent_id) const { auto coord = _agent_index->find(agent_id); if (coord == _agent_index->end()) - return std::nullopt; + throw error::AgentNotFound(fmt::format("Agent {} not found on grid", agent_id.to_string())); return coord->second; } - GridCoord1D Grid1D::coord_wrap(const GridCoord1D &coord) const { - auto x = coord.get_x_location(); + GridCoord1D Grid1D::coord_wrap(const GridCoord1D& coord) const { + auto x = coord.x(); if (_wrap_x) x = (x + static_cast(_maximum_x)) % static_cast(_maximum_x); diff --git a/src/libkami/grid2d.cc b/src/libkami/grid2d.cc index 168ba37..1478533 100644 --- a/src/libkami/grid2d.cc +++ b/src/libkami/grid2d.cc @@ -23,24 +23,26 @@ * SOFTWARE. */ -#include -#include -#include - #include #include -#include #include #include #include +#include + +#include +#include +#include +#include + namespace kami { - int GridCoord2D::get_x_location() const { + int GridCoord2D::x() const { return _x_coord; } - int GridCoord2D::get_y_location() const { + int GridCoord2D::y() const { return _y_coord; } @@ -48,20 +50,101 @@ namespace kami { return std::string("(" + std::to_string(_x_coord) + ", " + std::to_string(_y_coord) + ")"); } - bool operator==(const GridCoord2D &lhs, const GridCoord2D &rhs) { + double GridCoord2D::distance(std::shared_ptr& p) const { + auto p2d = std::static_pointer_cast(p); + return distance(p2d); + } + + double GridCoord2D::distance( + std::shared_ptr& p, + GridDistanceType distance_type + ) const { + switch (distance_type) { + case GridDistanceType::Chebyshev: + return distance_chebyshev(p); + case GridDistanceType::Manhattan: + return distance_manhattan(p); + case GridDistanceType::Euclidean: + return distance_euclidean(p); + default: + throw error::OptionInvalid("Unknown distance type given"); + } + } + + bool operator==( + const GridCoord2D& lhs, + const GridCoord2D& rhs + ) { return (lhs._x_coord == rhs._x_coord && lhs._y_coord == rhs._y_coord); } - bool operator!=(const GridCoord2D &lhs, const GridCoord2D &rhs) { + bool operator!=( + const GridCoord2D& lhs, + const GridCoord2D& rhs + ) { return !(lhs == rhs); } - std::ostream &operator<<(std::ostream &lhs, const GridCoord2D &rhs) { + std::ostream& operator<<( + std::ostream& lhs, + const GridCoord2D& rhs + ) { return lhs << rhs.to_string(); } - Grid2D::Grid2D(unsigned int maximum_x, unsigned int maximum_y, bool wrap_x, - bool wrap_y) { + GridCoord2D::GridCoord2D( + int x_coord, + int y_coord + ) + :_x_coord(x_coord), _y_coord(y_coord) { + } + + double GridCoord2D::distance_chebyshev(std::shared_ptr& p) const { + return static_cast(fmax(abs(_x_coord - p->_x_coord), abs(_x_coord - p->_x_coord))); + } + + double GridCoord2D::distance_euclidean(std::shared_ptr& p) const { + return sqrt(pow(_x_coord - p->_x_coord, 2) + pow(_x_coord - p->_x_coord, 2)); + } + + double GridCoord2D::distance_manhattan(std::shared_ptr& p) const { + return static_cast(abs(_x_coord - p->_x_coord) + abs(_x_coord - p->_x_coord)); + } + + GridCoord2D operator+( + const GridCoord2D& lhs, + const GridCoord2D& rhs + ) { + return {lhs._x_coord + rhs._x_coord, lhs._y_coord + rhs._y_coord}; + } + + GridCoord2D operator-( + const GridCoord2D& lhs, + const GridCoord2D& rhs + ) { + return {lhs._x_coord - rhs._x_coord, lhs._y_coord - rhs._y_coord}; + } + + GridCoord2D operator*( + const GridCoord2D& lhs, + const double rhs + ) { + return {static_cast(lhs._x_coord * rhs), static_cast(lhs._y_coord * rhs)}; + } + + GridCoord2D operator*( + const double lhs, + const GridCoord2D& rhs + ) { + return {static_cast(rhs._x_coord * lhs), static_cast(rhs._y_coord * lhs)}; + } + + Grid2D::Grid2D( + unsigned int maximum_x, + unsigned int maximum_y, + bool wrap_x, + bool wrap_y + ) { _maximum_x = maximum_x; _maximum_y = maximum_y; _wrap_x = wrap_x; @@ -71,128 +154,92 @@ namespace kami { _agent_index = std::make_unique>(); } - std::optional Grid2D::delete_agent(AgentID agent_id) { - auto coord = get_location_by_agent(agent_id); - if (!coord) - return std::nullopt; - - return delete_agent(agent_id, coord.value()); + AgentID Grid2D::delete_agent(const AgentID agent_id) { + return delete_agent(agent_id, get_location_by_agent(agent_id)); } - std::optional Grid2D::delete_agent(AgentID agent_id, const GridCoord2D &coord) { - auto agent_location = _agent_grid->find(coord); - if (agent_location == _agent_grid->end()) - return std::nullopt; - - for (auto test_agent_id = agent_location; test_agent_id != _agent_grid->end(); test_agent_id++) + AgentID Grid2D::delete_agent( + const AgentID agent_id, + const GridCoord2D& coord + ) { + for (auto test_agent_id = _agent_grid->find(coord); test_agent_id != _agent_grid->end(); test_agent_id++) if (test_agent_id->second == agent_id) { _agent_grid->erase(test_agent_id); _agent_index->erase(agent_id); return agent_id; } - return std::nullopt; + throw error::AgentNotFound("Agent not found on grid"); } - bool Grid2D::is_location_valid(const GridCoord2D &coord) const { - auto x = coord.get_x_location(); - auto y = coord.get_y_location(); + bool Grid2D::is_location_valid(const GridCoord2D& coord) const { + auto x = coord.x(); + auto y = coord.y(); return (x >= 0 && x < static_cast(_maximum_x) && y >= 0 && y < static_cast(_maximum_y)); } - bool Grid2D::is_location_empty(const GridCoord2D &coord) const { + bool Grid2D::is_location_empty(const GridCoord2D& coord) const { auto grid_location = _agent_grid->equal_range(coord); return grid_location.first == grid_location.second; } - std::optional Grid2D::move_agent(const AgentID agent_id, const GridCoord2D &coord) { - auto coord_current = get_location_by_agent(agent_id); - - if (!coord_current) - return std::nullopt; - if (!delete_agent(agent_id, coord_current.value())) - return std::nullopt; - - return add_agent(agent_id, coord); + AgentID Grid2D::move_agent( + const AgentID agent_id, + const GridCoord2D& coord + ) { + return add_agent(delete_agent(agent_id, get_location_by_agent(agent_id)), coord); } - std::optional>> - Grid2D::get_neighborhood(const AgentID agent_id, const bool include_center, - const GridNeighborhoodType neighborhood_type) const { - auto coord = get_location_by_agent(agent_id); - if (!coord) - return std::nullopt; - - return std::move(get_neighborhood(coord.value(), include_center, neighborhood_type)); + std::shared_ptr> + Grid2D::get_neighborhood( + const AgentID agent_id, + const bool include_center, + const GridNeighborhoodType neighborhood_type + ) const { + return std::move(get_neighborhood(get_location_by_agent(agent_id), include_center, neighborhood_type)); } - std::optional>> - Grid2D::get_neighborhood(const GridCoord2D &coord, const bool include_center, - const GridNeighborhoodType neighborhood_type) const { + std::shared_ptr> + Grid2D::get_neighborhood( + const GridCoord2D& coord, + const bool include_center, + const GridNeighborhoodType neighborhood_type + ) const { auto neighborhood = std::make_unique>(); - auto x = coord.get_x_location(); - auto y = coord.get_y_location(); + std::vector directions; + + switch (neighborhood_type) { + case GridNeighborhoodType::VonNeumann: + directions = directions_vonneumann; + break; + case GridNeighborhoodType::Moore: + directions = directions_moore; + break; + default: + throw error::OptionInvalid( + fmt::format("Invalid neighborhood type {} given", (unsigned int) neighborhood_type)); + } - // We assume our starting position is valid - if (include_center) + if (include_center and is_location_valid(coord)) neighborhood->insert(coord); - // N, E, S, W - { - auto new_location = coord_wrap(GridCoord2D(x, y - 1)); - if (is_location_valid(new_location)) - neighborhood->insert(coord_wrap(new_location)); - } - { - auto new_location = coord_wrap(GridCoord2D(x, y + 1)); - if (is_location_valid(new_location)) - neighborhood->insert(coord_wrap(new_location)); - } - { - auto new_location = coord_wrap(GridCoord2D(x + 1, y)); - if (is_location_valid(new_location)) - neighborhood->insert(coord_wrap(new_location)); - } - { - auto new_location = coord_wrap(GridCoord2D(x - 1, y)); - if (is_location_valid(new_location)) - neighborhood->insert(coord_wrap(new_location)); - } + for (auto& direction : directions) { + auto new_location = coord_wrap(coord + direction); - if (neighborhood_type == GridNeighborhoodType::Moore) { - // NE, SE, SW, NW - { - auto new_location = coord_wrap(GridCoord2D(x + 1, y - 1)); - if (is_location_valid(new_location)) - neighborhood->insert(coord_wrap(new_location)); - } - { - auto new_location = coord_wrap(GridCoord2D(x + 1, y + 1)); - if (is_location_valid(new_location)) - neighborhood->insert(coord_wrap(new_location)); - } - { - auto new_location = coord_wrap(GridCoord2D(x - 1, y + 1)); - if (is_location_valid(new_location)) - neighborhood->insert(coord_wrap(new_location)); - } - { - auto new_location = coord_wrap(GridCoord2D(x - 1, y - 1)); - if (is_location_valid(new_location)) - neighborhood->insert(coord_wrap(new_location)); - } + if (is_location_valid(new_location)) + neighborhood->insert(new_location); } return std::move(neighborhood); } - std::optional>> Grid2D::get_location_contents(const GridCoord2D &coord) const { + std::shared_ptr> Grid2D::get_location_contents(const GridCoord2D& coord) const { auto agent_ids = std::make_shared>(); if (!is_location_valid(coord)) - return std::nullopt; + throw error::LocationUnavailable(fmt::format("Coordinates {} are invalid", coord.to_string())); if (is_location_empty(coord)) return agent_ids; @@ -205,24 +252,32 @@ namespace kami { return agent_ids; } - bool Grid2D::get_wrap_x() const { return _wrap_x; } + bool Grid2D::get_wrap_x() const { + return _wrap_x; + } - bool Grid2D::get_wrap_y() const { return _wrap_y; } + bool Grid2D::get_wrap_y() const { + return _wrap_y; + } - unsigned int Grid2D::get_maximum_x() const { return _maximum_x; } + unsigned int Grid2D::get_maximum_x() const { + return _maximum_x; + } - unsigned int Grid2D::get_maximum_y() const { return _maximum_y; } + unsigned int Grid2D::get_maximum_y() const { + return _maximum_y; + } - std::optional Grid2D::get_location_by_agent(const AgentID &agent_id) const { + GridCoord2D Grid2D::get_location_by_agent(const AgentID& agent_id) const { auto coord = _agent_index->find(agent_id); if (coord == _agent_index->end()) - return std::nullopt; + throw error::AgentNotFound(fmt::format("Agent {} not found on grid", agent_id.to_string())); return coord->second; } - GridCoord2D Grid2D::coord_wrap(const GridCoord2D &coord) const { - auto x = coord.get_x_location(); - auto y = coord.get_y_location(); + GridCoord2D Grid2D::coord_wrap(const GridCoord2D& coord) const { + auto x = coord.x(); + auto y = coord.y(); if (_wrap_x) x = (x + static_cast(_maximum_x)) % static_cast(_maximum_x); diff --git a/src/libkami/model.cc b/src/libkami/model.cc index 5b006ab..1bbcff6 100644 --- a/src/libkami/model.cc +++ b/src/libkami/model.cc @@ -26,15 +26,16 @@ #include #include +#include #include #include namespace kami { - std::optional> Model::get_domain() { - if(_domain == nullptr) - return std::nullopt; - return(_domain); + std::shared_ptr Model::get_domain() { + if (_domain == nullptr) + throw error::ResourceNotAvailable("Domain not found in model"); + return _domain; } std::shared_ptr Model::set_domain(std::shared_ptr domain) { @@ -42,10 +43,10 @@ namespace kami { return _domain; } - std::optional> Model::get_population() { - if(_pop == nullptr) - return std::nullopt; - return(_pop); + std::shared_ptr Model::get_population() { + if (_pop == nullptr) + throw error::ResourceNotAvailable("Population not found in model"); + return _pop; } std::shared_ptr Model::set_population(std::shared_ptr population) { @@ -53,10 +54,10 @@ namespace kami { return _pop; } - std::optional> Model::get_scheduler() { - if(_sched == nullptr) - return std::nullopt; - return(_sched); + std::shared_ptr Model::get_scheduler() { + if (_sched == nullptr) + throw error::ResourceNotAvailable("Scheduler not found in model"); + return _sched; } std::shared_ptr Model::set_scheduler(std::shared_ptr scheduler) { @@ -64,4 +65,9 @@ namespace kami { return _sched; } + std::shared_ptr Model::step() { + _sched->step(shared_from_this()); + return shared_from_this(); + } + } // namespace kami diff --git a/src/libkami/multigrid1d.cc b/src/libkami/multigrid1d.cc index eefe2b5..926306a 100644 --- a/src/libkami/multigrid1d.cc +++ b/src/libkami/multigrid1d.cc @@ -23,16 +23,29 @@ * SOFTWARE. */ +#include + #include #include +#include #include #include namespace kami { - std::optional MultiGrid1D::add_agent(const AgentID agent_id, const GridCoord1D &coord) { + MultiGrid1D::MultiGrid1D( + unsigned int maximum_x, + bool wrap_x + ) + :Grid1D(maximum_x, wrap_x) { + } + + AgentID MultiGrid1D::add_agent( + const AgentID agent_id, + const GridCoord1D& coord + ) { if (!is_location_valid(coord)) - return std::nullopt; + throw error::LocationInvalid(fmt::format("Coordinates {} are invalid", coord.to_string())); _agent_index->insert(std::pair(agent_id, coord)); _agent_grid->insert(std::pair(coord, agent_id)); diff --git a/src/libkami/multigrid2d.cc b/src/libkami/multigrid2d.cc index 087d353..cf2049b 100644 --- a/src/libkami/multigrid2d.cc +++ b/src/libkami/multigrid2d.cc @@ -23,16 +23,31 @@ * SOFTWARE. */ +#include + #include #include +#include #include #include namespace kami { - std::optional MultiGrid2D::add_agent(const AgentID agent_id, const GridCoord2D &coord) { + MultiGrid2D::MultiGrid2D( + unsigned int maximum_x, + unsigned int maximum_y, + bool wrap_x, + bool wrap_y + ) + :Grid2D(maximum_x, maximum_y, wrap_x, wrap_y) { + } + + AgentID MultiGrid2D::add_agent( + const AgentID agent_id, + const GridCoord2D& coord + ) { if (!is_location_valid(coord)) - return std::nullopt; + throw error::LocationInvalid(fmt::format("Coordinates {} are invalid", coord.to_string())); _agent_index->insert(std::pair(agent_id, coord)); _agent_grid->insert(std::pair(coord, agent_id)); diff --git a/src/libkami/population.cc b/src/libkami/population.cc index 0b60b38..46d6696 100644 --- a/src/libkami/population.cc +++ b/src/libkami/population.cc @@ -24,42 +24,47 @@ */ #include -#include #include #include #include +#include #include namespace kami { - AgentID Population::add_agent(const std::shared_ptr& agent) { + AgentID Population::add_agent(const std::shared_ptr& agent) noexcept { auto agent_id = agent->get_agent_id(); _agent_map.insert(std::pair>(agent_id, agent)); - return(agent->get_agent_id()); + return agent->get_agent_id(); } - std::optional> Population::delete_agent(const AgentID agent_id) { + std::shared_ptr Population::delete_agent(const AgentID agent_id) { auto agent_it = _agent_map.find(agent_id); - if(agent_it == _agent_map.end()) - return std::nullopt; + if (agent_it == _agent_map.end()) + throw error::ResourceNotAvailable("Agent not found in population"); auto agent = agent_it->second; _agent_map.erase(agent_it); - return std::make_optional(agent); + return std::move(agent); } - std::optional> Population::get_agent_by_id(const AgentID agent_id) const { + std::shared_ptr Population::get_agent_by_id(const AgentID agent_id) const { auto agent_it = _agent_map.find(agent_id); - if(agent_it != _agent_map.end()) return(agent_it->second); - return std::nullopt; + if (agent_it == _agent_map.end()) + throw error::AgentNotFound("Agent not found in population"); + + return agent_it->second; } - std::shared_ptr> Population::get_agent_list() const { - auto key_selector = [](auto pair){ return pair.first; }; - auto agent_ids = std::make_shared>(_agent_map.size()); + std::unique_ptr> Population::get_agent_list() const { + auto key_selector = [](auto pair) + { + return pair.first; + }; + auto agent_ids = std::make_unique>(_agent_map.size()); transform(_agent_map.begin(), _agent_map.end(), agent_ids->begin(), key_selector); return std::move(agent_ids); diff --git a/src/libkami/random.cc b/src/libkami/random.cc index ead10ec..040c1ae 100644 --- a/src/libkami/random.cc +++ b/src/libkami/random.cc @@ -25,34 +25,52 @@ #include #include -#include #include #include #include +#include #include #include #include namespace kami { - RandomScheduler::RandomScheduler(std::shared_ptr rng) { + RandomScheduler::RandomScheduler(std::shared_ptr rng) { this->_rng = std::move(rng); } - std::optional>> RandomScheduler::step(std::shared_ptr model, std::shared_ptr> agent_list) { + std::unique_ptr> + RandomScheduler::step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) { if (_rng == nullptr) - return std::nullopt; + throw error::ResourceNotAvailable("No random number generator available"); shuffle(agent_list->begin(), agent_list->end(), *_rng); - return std::move(this->SequentialScheduler::step(model, agent_list)); + return std::move(this->SequentialScheduler::step(model, std::move(agent_list))); } - std::shared_ptr RandomScheduler::set_rng(std::shared_ptr rng) { + std::unique_ptr> + RandomScheduler::step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) { + if (_rng == nullptr) + throw error::ResourceNotAvailable("No random number generator available"); + + shuffle(agent_list->begin(), agent_list->end(), *_rng); + return std::move(this->SequentialScheduler::step(model, std::move(agent_list))); + } + + std::shared_ptr RandomScheduler::set_rng(std::shared_ptr rng) { this->_rng = std::move(rng); return _rng; } - std::shared_ptr RandomScheduler::get_rng() { return (this->_rng); } + std::shared_ptr RandomScheduler::get_rng() { + return (this->_rng); + } } // namespace kami diff --git a/src/libkami/reporter.cc b/src/libkami/reporter.cc new file mode 100644 index 0000000..6f8b14f --- /dev/null +++ b/src/libkami/reporter.cc @@ -0,0 +1,130 @@ +/*- + * Copyright (c) 2022 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace kami { + + ReporterModel::ReporterModel() { + _step_count = 0; + _rpt = std::make_shared(); + } + + std::shared_ptr ReporterModel::step() { + _step_count++; + + auto ret = _sched->step(std::static_pointer_cast(shared_from_this())); + auto rpt = _rpt->collect(std::static_pointer_cast(shared_from_this())); + + return shared_from_this(); + } + + std::unique_ptr ReporterModel::report() { + return std::move(_rpt->report(std::static_pointer_cast(shared_from_this()))); + } + + inline unsigned int ReporterModel::get_step_id() { + return _step_count; + } + + Reporter::Reporter() { + _report_data = std::make_unique>(); + } + + std::shared_ptr Reporter::clear() { + // I _can_ do this in one line, but I won't + _report_data.reset(); + _report_data = std::make_unique>(); + return shared_from_this(); + } + + std::unique_ptr + Reporter::collect(const std::shared_ptr& model) { + auto pop = model->get_population(); + return collect(model, pop); + } + + std::unique_ptr + Reporter::collect( + const std::shared_ptr& model, + const std::shared_ptr& pop + ) { + auto agent_list = pop->get_agent_list(); + return collect(model, agent_list); + } + + std::unique_ptr + Reporter::collect( + const std::shared_ptr& model, + const std::unique_ptr>& agent_list + ) { + auto collection_array = std::vector(); + + for (auto& agent_id : *agent_list) { + auto agent_data = nlohmann::json(); + auto agent = std::static_pointer_cast(model->get_population()->get_agent_by_id(agent_id)); + + agent_data["agent_id"] = agent_id.to_string(); + + auto agent_collection = agent->collect(); + if (agent_collection) + agent_data["data"] = *agent_collection; + + collection_array.push_back(agent_data); + } + auto model_data = model->collect(); + auto agent_collection = std::make_unique(collection_array); + auto collection = std::make_unique(); + + (*collection)["step_id"] = model->get_step_id(); + if (model_data) + (*collection)["model_data"] = *model_data; + (*collection)["agent_data"] = *agent_collection; + + _report_data->push_back(*collection); + + return std::move(collection); + } + + std::unique_ptr Reporter::report(const std::shared_ptr& model) { + auto json_data = std::make_unique(*_report_data); + return std::move(json_data); + } + + AgentID ReporterAgent::step(std::shared_ptr model) { + return get_agent_id(); + } + + +} // namespace kami diff --git a/src/libkami/sequential.cc b/src/libkami/sequential.cc index 8b45cc5..9d6ea82 100644 --- a/src/libkami/sequential.cc +++ b/src/libkami/sequential.cc @@ -24,40 +24,57 @@ */ #include -#include #include #include +#include #include namespace kami { - std::optional>> SequentialScheduler::step(std::shared_ptr model) { + std::unique_ptr> SequentialScheduler::step(std::shared_ptr model) { auto population = model->get_population(); - - if(!population) - return std::nullopt; - - return std::move(this->step(model, population.value()->get_agent_list())); + return std::move(this->step(model, population->get_agent_list())); } - std::optional>> SequentialScheduler::step(std::shared_ptr model, std::shared_ptr> agent_list) { - auto return_agent_list = std::make_shared>(); + std::unique_ptr> SequentialScheduler::step(std::shared_ptr model) { auto population = model->get_population(); + return std::move(this->step(model, population->get_agent_list())); + } - if(!population) - return std::nullopt; + std::unique_ptr> + SequentialScheduler::step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) { + auto return_agent_list = std::make_unique>(); + auto population = model->get_population(); Scheduler::_step_counter++; - for(auto & agent_id : *agent_list) { - auto agent_opt = population.value()->get_agent_by_id(agent_id); + for (auto& agent_id : *agent_list) { + auto agent = population->get_agent_by_id(agent_id); + + agent->step(model); + return_agent_list->push_back(agent_id); + } - if(agent_opt) { - auto agent = agent_opt.value(); + return std::move(return_agent_list); + } + + std::unique_ptr> + SequentialScheduler::step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) { + auto return_agent_list = std::make_unique>(); + auto population = model->get_population(); + + Scheduler::_step_counter++; + for (auto& agent_id : *agent_list) { + auto agent = population->get_agent_by_id(agent_id); - agent->step(model); - return_agent_list->push_back(agent_id); - } + agent->step(model); + return_agent_list->push_back(agent_id); } return std::move(return_agent_list); diff --git a/src/libkami/sologrid1d.cc b/src/libkami/sologrid1d.cc index a9ad568..06d4405 100644 --- a/src/libkami/sologrid1d.cc +++ b/src/libkami/sologrid1d.cc @@ -23,16 +23,29 @@ * SOFTWARE. */ +#include + #include +#include #include namespace kami { - std::optional SoloGrid1D::add_agent(const AgentID agent_id, const GridCoord1D &coord) { + SoloGrid1D::SoloGrid1D( + unsigned int maximum_x, + bool wrap_x + ) + :Grid1D(maximum_x, wrap_x) { + } + + AgentID SoloGrid1D::add_agent( + const AgentID agent_id, + const GridCoord1D& coord + ) { if (!is_location_valid(coord)) - return std::nullopt; + throw error::LocationInvalid(fmt::format("Coordinates {} are invalid", coord.to_string())); if (!is_location_empty(coord)) - return std::nullopt; + throw error::LocationUnavailable(fmt::format("Coordinates {} already occupied", coord.to_string())); _agent_index->insert(std::pair(agent_id, coord)); _agent_grid->insert(std::pair(coord, agent_id)); diff --git a/src/libkami/sologrid2d.cc b/src/libkami/sologrid2d.cc index d83714f..e0f580c 100644 --- a/src/libkami/sologrid2d.cc +++ b/src/libkami/sologrid2d.cc @@ -23,18 +23,33 @@ * SOFTWARE. */ +#include + +#include + #include +#include #include -#include - namespace kami { - std::optional SoloGrid2D::add_agent(const AgentID agent_id, const GridCoord2D &coord) { + SoloGrid2D::SoloGrid2D( + unsigned int maximum_x, + unsigned int maximum_y, + bool wrap_x, + bool wrap_y + ) + :Grid2D(maximum_x, maximum_y, wrap_x, wrap_y) { + } + + AgentID SoloGrid2D::add_agent( + const AgentID agent_id, + const GridCoord2D& coord + ) { if (!is_location_valid(coord)) - return std::nullopt; + throw error::LocationInvalid(fmt::format("Coordinates {} are invalid", coord.to_string())); if (!is_location_empty(coord)) - return std::nullopt; + throw error::LocationUnavailable(fmt::format("Coordinates {} already occupied", coord.to_string())); _agent_index->insert(std::pair(agent_id, coord)); _agent_grid->insert(std::pair(coord, agent_id)); diff --git a/src/libkami/staged.cc b/src/libkami/staged.cc index 73aec24..bbd4213 100644 --- a/src/libkami/staged.cc +++ b/src/libkami/staged.cc @@ -24,45 +24,57 @@ */ #include -#include #include #include +#include +#include #include #include namespace kami { - std::optional>> StagedScheduler::step(std::shared_ptr model, std::shared_ptr> agent_list) { - this->SequentialScheduler::step(model, agent_list); - return std::move(this->advance(model, agent_list)); + std::unique_ptr> + StagedScheduler::step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) { + auto stepped_agent_list = this->SequentialScheduler::step(model, std::move(agent_list)); + return std::move(this->advance(model, std::move(stepped_agent_list))); } - std::optional>> StagedScheduler::advance(std::shared_ptr model) { - auto population = model->get_population(); - - if(!population) - return std::nullopt; - - return std::move(this->advance(model, population.value()->get_agent_list())); + std::unique_ptr> + StagedScheduler::step( + std::shared_ptr model, + std::unique_ptr> agent_list + ) { + auto stepped_agent_list = this->SequentialScheduler::step(model, std::move(agent_list)); + return std::move(this->advance(model, std::move(stepped_agent_list))); } - std::optional>> StagedScheduler::advance(std::shared_ptr model, std::shared_ptr> agent_list) { - auto return_agent_list = std::make_shared>(); + std::unique_ptr> StagedScheduler::advance(std::shared_ptr model) { auto population = model->get_population(); + return std::move(this->advance(model, population->get_agent_list())); + } - if(!population) - return std::nullopt; + std::unique_ptr> StagedScheduler::advance(std::shared_ptr model) { + auto population = model->get_population(); + return std::move(this->advance(model, population->get_agent_list())); + } - for(auto & agent_id : *agent_list) { - auto agent_opt = population.value()->get_agent_by_id(agent_id); + std::unique_ptr> + StagedScheduler::advance( + std::shared_ptr model, + std::unique_ptr> agent_list + ) { + auto return_agent_list = std::make_unique>(); + auto population = model->get_population(); - if(agent_opt) { - auto agent = std::static_pointer_cast(agent_opt.value()); + for (auto& agent_id : *agent_list) { + auto agent = std::static_pointer_cast(population->get_agent_by_id(agent_id)); - agent->advance(model); - return_agent_list->push_back(agent_id); - } + agent->advance(model); + return_agent_list->push_back(agent_id); } return std::move(return_agent_list); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d413e56..eb494cc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,6 +4,7 @@ set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) +find_package(nlohmann_json 3.11.1 REQUIRED) file(GLOB test_modules "${CMAKE_CURRENT_SOURCE_DIR}/*.cc") FOREACH (test_module ${test_modules}) @@ -11,7 +12,7 @@ FOREACH (test_module ${test_modules}) create_test( NAME ${test_src} SOURCES ${test_src}.cc - PUBLIC_LINKED_TARGETS gmock gtest kami::libkami Threads::Threads + PUBLIC_LINKED_TARGETS gmock gtest kami::libkami Threads::Threads nlohmann_json::nlohmann_json COMMAND ${test_src} PUBLIC_COMPILE_FEATURES ${COVERAGE_FLAGS} ) diff --git a/test/unit-kami-agent.cc b/test/unit-kami-agent.cc index a307cab..ee4b5ba 100644 --- a/test/unit-kami-agent.cc +++ b/test/unit-kami-agent.cc @@ -28,63 +28,72 @@ #include #include +#include #include using namespace kami; using namespace std; -class TestAgent : public Agent { +class TestAgent + : public Agent { public: AgentID step(shared_ptr model) override { return get_agent_id(); } }; -class TestModel : public Model { +class TestModel + : public Model { +}; + +class AgentTest + : public ::testing::Test { +protected: + TestAgent agent_foo; + TestAgent agent_bar; + shared_ptr model_world = nullptr; + + void SetUp() override { + model_world = make_shared(); + } }; TEST(Agent, DefaultConstructor) { - const TestAgent agent_foo; - const TestAgent agent_bar; + EXPECT_NO_THROW( + const TestAgent agent_baz; + const TestAgent agent_qux; + ); +} +TEST_F(AgentTest, equivalance) { EXPECT_EQ(agent_foo, agent_foo); EXPECT_NE(agent_foo, agent_bar); } -TEST(Agent, get_agent_id) { - const TestAgent agent_foo; - const TestAgent agent_bar; - +TEST_F(AgentTest, get_agent_id) { EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.get_agent_id()); EXPECT_NE(agent_bar.get_agent_id(), agent_foo.get_agent_id()); } -TEST(Agent, step) { - TestAgent agent_foo; - TestAgent agent_bar; - auto model_world = make_shared(); - +TEST_F(AgentTest, step) { EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.step(model_world)); EXPECT_NE(agent_bar.get_agent_id(), agent_foo.step(model_world)); } -TEST(Agent, Equality) { - const TestAgent agent_foo; - const TestAgent agent_bar; - +TEST_F(AgentTest, equality) { EXPECT_TRUE(agent_foo == agent_foo); EXPECT_TRUE(agent_bar == agent_bar); } -TEST(Agent, Inequality) { - const TestAgent agent_foo; - const TestAgent agent_bar; - +TEST_F(AgentTest, inequality) { EXPECT_TRUE(agent_foo != agent_bar); EXPECT_FALSE(agent_bar != agent_bar); } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-agentid.cc b/test/unit-kami-agentid.cc index 5ff41dc..31a5fbf 100644 --- a/test/unit-kami-agentid.cc +++ b/test/unit-kami-agentid.cc @@ -30,48 +30,43 @@ using namespace kami; -TEST(AgentID, DefaultConstructor) { - const AgentID agent_id_foo; - const AgentID agent_id_bar; +class AgentIDTest + : public ::testing::Test { +protected: + AgentID agent_id_foo; + AgentID agent_id_bar; +}; +TEST_F(AgentIDTest, DefaultConstructor) { EXPECT_EQ(agent_id_foo, agent_id_foo); EXPECT_NE(agent_id_foo, agent_id_bar); } -TEST(AgentID, to_string) { - const AgentID agent_id_foo; - const AgentID agent_id_bar; - +TEST_F(AgentIDTest, to_string) { EXPECT_THAT(agent_id_foo.to_string(), testing::MatchesRegex("[0-9]+")); EXPECT_THAT(agent_id_bar.to_string(), testing::MatchesRegex("[0-9]+")); } -TEST(AgentID, Equality) { - const AgentID agent_id_foo; - const AgentID agent_id_bar; - +TEST_F(AgentIDTest, equality) { EXPECT_TRUE(agent_id_foo == agent_id_foo); EXPECT_TRUE(agent_id_bar == agent_id_bar); EXPECT_FALSE(agent_id_foo == agent_id_bar); } -TEST(AgentID, Inequality) { - const AgentID agent_id_foo; - const AgentID agent_id_bar; - +TEST_F(AgentIDTest, inequality) { EXPECT_TRUE(agent_id_foo != agent_id_bar); EXPECT_FALSE(agent_id_bar != agent_id_bar); } -TEST(AgentID, Ordering) { - const AgentID agent_id_foo; - const AgentID agent_id_bar; - +TEST_F(AgentIDTest, ordering) { EXPECT_TRUE(agent_id_foo < agent_id_bar); EXPECT_FALSE(agent_id_bar < agent_id_foo); } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-gridcoord1d.cc b/test/unit-kami-gridcoord1d.cc index 26e961e..f77fe8b 100644 --- a/test/unit-kami-gridcoord1d.cc +++ b/test/unit-kami-gridcoord1d.cc @@ -30,12 +30,16 @@ using namespace kami; -TEST(GridCoord1D, DefaultConstructor) { - const GridCoord1D gridcoord1d_foo(0); - const GridCoord1D gridcoord1d_bar(1); - const GridCoord1D gridcoord1d_baz(-1); - const GridCoord1D gridcoord1d_qux(0); - +class GridCoord1DTest + : public ::testing::Test { +protected: + GridCoord1D gridcoord1d_foo = GridCoord1D(0); + GridCoord1D gridcoord1d_bar = GridCoord1D(1); + GridCoord1D gridcoord1d_baz = GridCoord1D(-1); + GridCoord1D gridcoord1d_qux = GridCoord1D(0); +}; + +TEST_F(GridCoord1DTest, DefaultConstructor) { EXPECT_EQ(gridcoord1d_foo, gridcoord1d_foo); EXPECT_EQ(gridcoord1d_foo, gridcoord1d_qux); @@ -44,22 +48,13 @@ TEST(GridCoord1D, DefaultConstructor) { EXPECT_NE(gridcoord1d_bar, gridcoord1d_baz); } -TEST(GridCoord1D, to_string) { - const GridCoord1D gridcoord1d_foo(0); - const GridCoord1D gridcoord1d_bar(1); - const GridCoord1D gridcoord1d_baz(-1); - +TEST_F(GridCoord1DTest, to_string) { EXPECT_THAT(gridcoord1d_foo.to_string(), "(0)"); EXPECT_THAT(gridcoord1d_bar.to_string(), "(1)"); EXPECT_THAT(gridcoord1d_baz.to_string(), "(-1)"); } -TEST(GridCoord1D, Equality) { - const GridCoord1D gridcoord1d_foo(0); - const GridCoord1D gridcoord1d_bar(1); - const GridCoord1D gridcoord1d_baz(-1); - const GridCoord1D gridcoord1d_qux(0); - +TEST_F(GridCoord1DTest, equality) { EXPECT_TRUE(gridcoord1d_foo == gridcoord1d_foo); EXPECT_TRUE(gridcoord1d_foo == gridcoord1d_qux); @@ -68,12 +63,7 @@ TEST(GridCoord1D, Equality) { EXPECT_FALSE(gridcoord1d_bar == gridcoord1d_baz); } -TEST(GridCoord1D, Inequality) { - const GridCoord1D gridcoord1d_foo(0); - const GridCoord1D gridcoord1d_bar(1); - const GridCoord1D gridcoord1d_baz(-1); - const GridCoord1D gridcoord1d_qux(0); - +TEST_F(GridCoord1DTest, inequality) { EXPECT_FALSE(gridcoord1d_foo != gridcoord1d_foo); EXPECT_FALSE(gridcoord1d_foo != gridcoord1d_qux); @@ -82,25 +72,23 @@ TEST(GridCoord1D, Inequality) { EXPECT_TRUE(gridcoord1d_bar != gridcoord1d_baz); } -TEST(GridCoord1D, get_x_location) { - const GridCoord1D gridcoord1d_foo(0); - const GridCoord1D gridcoord1d_bar(1); - const GridCoord1D gridcoord1d_baz(-1); - const GridCoord1D gridcoord1d_qux(0); - - EXPECT_TRUE(gridcoord1d_foo.get_x_location() == 0); - EXPECT_TRUE(gridcoord1d_bar.get_x_location() == 1); - EXPECT_TRUE(gridcoord1d_baz.get_x_location() == -1); +TEST_F(GridCoord1DTest, x) { + EXPECT_TRUE(gridcoord1d_foo.x() == 0); + EXPECT_TRUE(gridcoord1d_bar.x() == 1); + EXPECT_TRUE(gridcoord1d_baz.x() == -1); - EXPECT_TRUE(gridcoord1d_foo.get_x_location() == gridcoord1d_foo.get_x_location()); - EXPECT_TRUE(gridcoord1d_foo.get_x_location() == gridcoord1d_qux.get_x_location()); + EXPECT_TRUE(gridcoord1d_foo.x() == gridcoord1d_foo.x()); + EXPECT_TRUE(gridcoord1d_foo.x() == gridcoord1d_qux.x()); - EXPECT_FALSE(gridcoord1d_foo.get_x_location() == gridcoord1d_bar.get_x_location()); - EXPECT_FALSE(gridcoord1d_foo.get_x_location() == gridcoord1d_baz.get_x_location()); - EXPECT_FALSE(gridcoord1d_bar.get_x_location() == gridcoord1d_baz.get_x_location()); + EXPECT_FALSE(gridcoord1d_foo.x() == gridcoord1d_bar.x()); + EXPECT_FALSE(gridcoord1d_foo.x() == gridcoord1d_baz.x()); + EXPECT_FALSE(gridcoord1d_bar.x() == gridcoord1d_baz.x()); } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-gridcoord2d.cc b/test/unit-kami-gridcoord2d.cc index ceae253..801fc74 100644 --- a/test/unit-kami-gridcoord2d.cc +++ b/test/unit-kami-gridcoord2d.cc @@ -30,13 +30,17 @@ using namespace kami; -TEST(GridCoord2D, DefaultConstructor) { - const GridCoord2D gridcoord2d_foo(0, 0); - const GridCoord2D gridcoord2d_bar(1, 1); - const GridCoord2D gridcoord2d_baz(-1, -1); - const GridCoord2D gridcoord2d_qux(0, 1); - const GridCoord2D gridcoord2d_qu2(1, 0); - +class GridCoord2DTest + : public ::testing::Test { +protected: + GridCoord2D gridcoord2d_foo = GridCoord2D(0, 0); + GridCoord2D gridcoord2d_bar = GridCoord2D(1, 1); + GridCoord2D gridcoord2d_baz = GridCoord2D(-1, -1); + GridCoord2D gridcoord2d_qux = GridCoord2D(0, 1); + GridCoord2D gridcoord2d_qu2 = GridCoord2D(1, 0); +}; + +TEST_F(GridCoord2DTest, DefaultConstructor) { EXPECT_EQ(gridcoord2d_foo, gridcoord2d_foo); EXPECT_NE(gridcoord2d_foo, gridcoord2d_bar); @@ -47,7 +51,7 @@ TEST(GridCoord2D, DefaultConstructor) { } -TEST(GridCoord2D, to_string) { +TEST_F(GridCoord2DTest, to_string) { const GridCoord2D gridcoord2d_foo(0, 0); const GridCoord2D gridcoord2d_bar(1, 1); const GridCoord2D gridcoord2d_baz(-1, -1); @@ -61,7 +65,7 @@ TEST(GridCoord2D, to_string) { EXPECT_THAT(gridcoord2d_qu2.to_string(), "(1, 0)"); } -TEST(GridCoord2D, Equality) { +TEST_F(GridCoord2DTest, Equality) { const GridCoord2D gridcoord2d_foo(0, 0); const GridCoord2D gridcoord2d_bar(1, 1); const GridCoord2D gridcoord2d_baz(-1, -1); @@ -78,7 +82,7 @@ TEST(GridCoord2D, Equality) { EXPECT_FALSE(gridcoord2d_qux == gridcoord2d_qu2); } -TEST(GridCoord2D, Inequality) { +TEST_F(GridCoord2DTest, Inequality) { const GridCoord2D gridcoord2d_foo(0, 0); const GridCoord2D gridcoord2d_bar(1, 1); const GridCoord2D gridcoord2d_baz(-1, -1); @@ -94,56 +98,58 @@ TEST(GridCoord2D, Inequality) { EXPECT_TRUE(gridcoord2d_qux != gridcoord2d_qu2); } -TEST(GridCoord2D, get_x_location) { +TEST_F(GridCoord2DTest, x) { const GridCoord2D gridcoord2d_foo(0, 0); const GridCoord2D gridcoord2d_bar(1, 1); const GridCoord2D gridcoord2d_baz(-1, -1); const GridCoord2D gridcoord2d_qux(0, 1); const GridCoord2D gridcoord2d_qu2(1, 0); - EXPECT_TRUE(gridcoord2d_foo.get_x_location() == 0); - EXPECT_TRUE(gridcoord2d_bar.get_x_location() == 1); - EXPECT_TRUE(gridcoord2d_baz.get_x_location() == -1); - EXPECT_FALSE(gridcoord2d_qux.get_x_location() == -1); - EXPECT_FALSE(gridcoord2d_qu2.get_x_location() == -1); - - EXPECT_TRUE(gridcoord2d_foo.get_x_location() == gridcoord2d_foo.get_x_location()); - EXPECT_TRUE(gridcoord2d_foo.get_x_location() == gridcoord2d_qux.get_x_location()); - EXPECT_TRUE(gridcoord2d_bar.get_x_location() == gridcoord2d_qu2.get_x_location()); - - EXPECT_FALSE(gridcoord2d_foo.get_x_location() == gridcoord2d_bar.get_x_location()); - EXPECT_FALSE(gridcoord2d_foo.get_x_location() == gridcoord2d_baz.get_x_location()); - EXPECT_FALSE(gridcoord2d_bar.get_x_location() == gridcoord2d_baz.get_x_location()); - EXPECT_FALSE(gridcoord2d_foo.get_x_location() == gridcoord2d_baz.get_x_location()); - EXPECT_FALSE(gridcoord2d_qux.get_x_location() == gridcoord2d_qu2.get_x_location()); + EXPECT_TRUE(gridcoord2d_foo.x() == 0); + EXPECT_TRUE(gridcoord2d_bar.x() == 1); + EXPECT_TRUE(gridcoord2d_baz.x() == -1); + EXPECT_FALSE(gridcoord2d_qux.x() == -1); + EXPECT_FALSE(gridcoord2d_qu2.x() == -1); + + EXPECT_TRUE(gridcoord2d_foo.x() == gridcoord2d_foo.x()); + EXPECT_TRUE(gridcoord2d_foo.x() == gridcoord2d_qux.x()); + EXPECT_TRUE(gridcoord2d_bar.x() == gridcoord2d_qu2.x()); + + EXPECT_FALSE(gridcoord2d_foo.x() == gridcoord2d_bar.x()); + EXPECT_FALSE(gridcoord2d_foo.x() == gridcoord2d_baz.x()); + EXPECT_FALSE(gridcoord2d_bar.x() == gridcoord2d_baz.x()); + EXPECT_FALSE(gridcoord2d_foo.x() == gridcoord2d_baz.x()); + EXPECT_FALSE(gridcoord2d_qux.x() == gridcoord2d_qu2.x()); } -TEST(GridCoord2D, get_y_location) { +TEST_F(GridCoord2DTest, y) { const GridCoord2D gridcoord2d_foo(0, 0); const GridCoord2D gridcoord2d_bar(1, 1); const GridCoord2D gridcoord2d_baz(-1, -1); const GridCoord2D gridcoord2d_qux(0, 1); const GridCoord2D gridcoord2d_qu2(1, 0); - EXPECT_TRUE(gridcoord2d_foo.get_y_location() == 0); - EXPECT_TRUE(gridcoord2d_bar.get_y_location() == 1); - EXPECT_TRUE(gridcoord2d_baz.get_y_location() == -1); - EXPECT_FALSE(gridcoord2d_qux.get_y_location() == -1); - EXPECT_FALSE(gridcoord2d_qu2.get_y_location() == -1); - - EXPECT_TRUE(gridcoord2d_foo.get_y_location() == gridcoord2d_foo.get_y_location()); - EXPECT_TRUE(gridcoord2d_bar.get_y_location() == gridcoord2d_qux.get_y_location()); - - EXPECT_FALSE(gridcoord2d_foo.get_y_location() == gridcoord2d_bar.get_y_location()); - EXPECT_FALSE(gridcoord2d_foo.get_y_location() == gridcoord2d_baz.get_y_location()); - EXPECT_FALSE(gridcoord2d_bar.get_y_location() == gridcoord2d_baz.get_y_location()); - EXPECT_FALSE(gridcoord2d_foo.get_y_location() == gridcoord2d_baz.get_y_location()); - EXPECT_FALSE(gridcoord2d_qux.get_y_location() == gridcoord2d_qu2.get_y_location()); - EXPECT_FALSE(gridcoord2d_bar.get_y_location() == gridcoord2d_qu2.get_y_location()); + EXPECT_TRUE(gridcoord2d_foo.y() == 0); + EXPECT_TRUE(gridcoord2d_bar.y() == 1); + EXPECT_TRUE(gridcoord2d_baz.y() == -1); + EXPECT_FALSE(gridcoord2d_qux.y() == -1); + EXPECT_FALSE(gridcoord2d_qu2.y() == -1); + + EXPECT_TRUE(gridcoord2d_foo.y() == gridcoord2d_foo.y()); + EXPECT_TRUE(gridcoord2d_bar.y() == gridcoord2d_qux.y()); + + EXPECT_FALSE(gridcoord2d_foo.y() == gridcoord2d_bar.y()); + EXPECT_FALSE(gridcoord2d_foo.y() == gridcoord2d_baz.y()); + EXPECT_FALSE(gridcoord2d_bar.y() == gridcoord2d_baz.y()); + EXPECT_FALSE(gridcoord2d_foo.y() == gridcoord2d_baz.y()); + EXPECT_FALSE(gridcoord2d_qux.y() == gridcoord2d_qu2.y()); + EXPECT_FALSE(gridcoord2d_bar.y() == gridcoord2d_qu2.y()); } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } - diff --git a/test/unit-kami-model.cc b/test/unit-kami-model.cc index b03582b..77a0c1b 100644 --- a/test/unit-kami-model.cc +++ b/test/unit-kami-model.cc @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -34,18 +35,21 @@ #include using namespace kami; +using namespace kami::error; using namespace std; -class TestAgent : public Agent { +class TestAgent + : public Agent { public: AgentID step(shared_ptr model) override { return get_agent_id(); } }; -class TestModel : public Model { +class TestModel + : public Model { public: - shared_ptr step() { + shared_ptr step() final { return shared_from_this(); } }; @@ -61,24 +65,24 @@ TEST(Model, DefaultConstructor) { TEST(Model, set_population) { auto model_foo = make_shared(); - auto popul_foo = make_shared(); + auto pop_foo = make_shared(); - auto popul_bar = model_foo->set_population(popul_foo); - EXPECT_EQ(popul_foo, popul_bar); + auto pop_bar = model_foo->set_population(pop_foo); + EXPECT_EQ(pop_foo, pop_bar); } TEST(Model, get_population) { auto model_foo = make_shared(); - auto popul_foo = make_shared(); + auto pop_foo = make_shared(); - auto popul_nul = model_foo->get_population(); + EXPECT_THROW(auto pop_nul = model_foo->get_population(), ResourceNotAvailable); - auto popul_bar = model_foo->set_population(popul_foo); - auto popul_baz = model_foo->get_population(); + auto pop_bar = model_foo->set_population(pop_foo); + auto pop_baz = model_foo->get_population(); - EXPECT_TRUE(popul_baz); - EXPECT_EQ(popul_foo, popul_baz); - EXPECT_EQ(popul_bar, popul_baz); + EXPECT_TRUE(pop_baz); + EXPECT_EQ(pop_foo, pop_baz); + EXPECT_EQ(pop_bar, pop_baz); } TEST(Model, set_scheduler) { @@ -93,8 +97,7 @@ TEST(Model, get_scheduler) { auto model_foo = make_shared(); auto sched_foo = make_shared(); - auto sched_nul = model_foo->get_scheduler(); - EXPECT_FALSE(sched_nul); + EXPECT_THROW(auto sched_nul = model_foo->get_scheduler(), ResourceNotAvailable); auto sched_bar = model_foo->set_scheduler(sched_foo); auto sched_baz = model_foo->get_scheduler(); @@ -116,8 +119,7 @@ TEST(Model, get_domain) { auto model_foo = make_shared(); auto grid2_foo = make_shared(10, 10, true, true); - auto grid2_nul = model_foo->get_domain(); - EXPECT_FALSE(grid2_nul); + EXPECT_THROW(auto grid2_nul = model_foo->get_domain(), ResourceNotAvailable); auto grid2_bar = model_foo->set_domain(grid2_foo); auto grid2_baz = model_foo->get_domain(); @@ -127,7 +129,10 @@ TEST(Model, get_domain) { EXPECT_EQ(grid2_bar, grid2_baz); } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-multigrid1d.cc b/test/unit-kami-multigrid1d.cc index 62d1a78..efe1663 100644 --- a/test/unit-kami-multigrid1d.cc +++ b/test/unit-kami-multigrid1d.cc @@ -35,6 +35,7 @@ #include using namespace kami; +using namespace kami::error; using namespace std; TEST(MultiGrid1D, DefaultConstructor) { @@ -53,18 +54,15 @@ TEST(MultiGrid1D, add_agent) { { auto agent_id_baz = multigrid1d_foo.add_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { auto agent_id_baz = multigrid1d_foo.add_agent(agent_id_bar, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_bar); + EXPECT_EQ(agent_id_baz, agent_id_bar); } { auto agent_id_baz = multigrid1d_foo.add_agent(agent_id_bar, coord3); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_bar); + EXPECT_EQ(agent_id_baz, agent_id_bar); } } @@ -77,8 +75,7 @@ TEST(MultiGrid1D, delete_agent) { static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid1D multigrid1d_foo(10, true); @@ -86,8 +83,7 @@ TEST(MultiGrid1D, delete_agent) { static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid1d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid1D multigrid1d_foo(10, true); @@ -95,8 +91,7 @@ TEST(MultiGrid1D, delete_agent) { static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid1d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_bar); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_bar); + EXPECT_EQ(agent_id_baz, agent_id_bar); } { @@ -104,8 +99,7 @@ TEST(MultiGrid1D, delete_agent) { static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid1D multigrid1d_foo(10, true); @@ -113,8 +107,7 @@ TEST(MultiGrid1D, delete_agent) { static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid1d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid1D multigrid1d_foo(10, true); @@ -122,31 +115,27 @@ TEST(MultiGrid1D, delete_agent) { static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid1d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_bar, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_bar); + EXPECT_EQ(agent_id_baz, agent_id_bar); } { MultiGrid1D multigrid1d_foo(10, true); static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); - auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound); } { MultiGrid1D multigrid1d_foo(10, true); static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid1d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound); } { MultiGrid1D multigrid1d_foo(10, true); static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid1d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_bar, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_bar, coord3), AgentNotFound); } } @@ -202,15 +191,13 @@ TEST(MultiGrid1D, move_agent) { static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord7); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid1D multigrid1d_foo(10, true); static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); - auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord10); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord10), LocationInvalid); } { MultiGrid1D multigrid1d_foo(10, true); @@ -218,8 +205,7 @@ TEST(MultiGrid1D, move_agent) { static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid1d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid1D multigrid1d_foo(10, true); @@ -227,172 +213,189 @@ TEST(MultiGrid1D, move_agent) { static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid1d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord7); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } } TEST(MultiGrid1D, get_neighborhood) { - const AgentID agent_id_foo, agent_id_bar; + const AgentID agent_id_foo; const GridCoord1D coord0(0), coord1(1), coord2(2), coord3(3), coord9(9); { MultiGrid1D multigrid1d_foo(10, true); - auto tval = unordered_set({coord0, coord1, coord9}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord9 + }); auto rval = multigrid1d_foo.get_neighborhood(coord0, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, true); - auto tval = unordered_set({coord0, coord1, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord2 + }); auto rval = multigrid1d_foo.get_neighborhood(coord1, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, false); - auto tval = unordered_set({coord0, coord1}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1 + }); auto rval = multigrid1d_foo.get_neighborhood(coord0, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, false); - auto tval = unordered_set({coord0, coord1, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord2 + }); auto rval = multigrid1d_foo.get_neighborhood(coord1, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, true); - auto tval = unordered_set({coord1, coord9}); + auto tval = unordered_set < GridCoord1D > ({ + coord1, coord9 + }); auto rval = multigrid1d_foo.get_neighborhood(coord0, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, true); - auto tval = unordered_set({coord0, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord2 + }); auto rval = multigrid1d_foo.get_neighborhood(coord1, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, false); - auto tval = unordered_set({coord1}); + auto tval = unordered_set < GridCoord1D > ({ + coord1 + }); auto rval = multigrid1d_foo.get_neighborhood(coord0, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, false); - auto tval = unordered_set({coord0, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord2 + }); auto rval = multigrid1d_foo.get_neighborhood(coord1, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, true); - auto tval = unordered_set({coord0, coord1, coord9}); - auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true); - EXPECT_FALSE(rval); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord9 + }); + + EXPECT_THROW(auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true), AgentNotFound); } { MultiGrid1D multigrid1d_foo(10, true); multigrid1d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({coord0, coord1, coord9}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord9 + }); auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, true); multigrid1d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({coord0, coord1, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord2 + }); auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, false); multigrid1d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({coord0, coord1}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1 + }); auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, false); multigrid1d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({coord0, coord1, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord2 + }); auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, true); multigrid1d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({coord1, coord9}); + auto tval = unordered_set < GridCoord1D > ({ + coord1, coord9 + }); auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, true); multigrid1d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({coord0, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord2 + }); auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, false); multigrid1d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({coord1}); + auto tval = unordered_set < GridCoord1D > ({ + coord1 + }); auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, false); multigrid1d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({coord0, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord2 + }); auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } } @@ -403,17 +406,16 @@ TEST(MultiGrid1D, get_location_by_agent) { { MultiGrid1D multigrid1d_foo(10, true); - EXPECT_FALSE(multigrid1d_foo.get_location_by_agent(agent_id_foo)); - EXPECT_FALSE(multigrid1d_foo.get_location_by_agent(agent_id_bar)); + EXPECT_THROW(auto loc1 = multigrid1d_foo.get_location_by_agent(agent_id_foo), AgentNotFound); + EXPECT_THROW(auto loc2 = multigrid1d_foo.get_location_by_agent(agent_id_bar), AgentNotFound); } { MultiGrid1D multigrid1d_foo(10, true); static_cast(multigrid1d_foo.add_agent(agent_id_foo, coord2)); auto local = multigrid1d_foo.get_location_by_agent(agent_id_foo); - EXPECT_TRUE(local); EXPECT_EQ(local, coord2); - EXPECT_FALSE(multigrid1d_foo.get_location_by_agent(agent_id_bar)); + EXPECT_THROW(auto loc = multigrid1d_foo.get_location_by_agent(agent_id_bar), AgentNotFound); } } @@ -423,9 +425,7 @@ TEST(MultiGrid1D, get_location_contents) { { MultiGrid1D multigrid1d_foo(10, true); - auto agent_list_foo = multigrid1d_foo.get_location_contents(coord10); - - EXPECT_FALSE(agent_list_foo); + EXPECT_THROW(auto agent_list_foo = multigrid1d_foo.get_location_contents(coord10), LocationUnavailable); } { MultiGrid1D multigrid1d_foo(10, true); @@ -433,7 +433,7 @@ TEST(MultiGrid1D, get_location_contents) { auto agent_list_foo = multigrid1d_foo.get_location_contents(coord1); EXPECT_TRUE(agent_list_foo); - EXPECT_TRUE(agent_list_foo.value()->empty()); + EXPECT_TRUE(agent_list_foo->empty()); } { MultiGrid1D multigrid1d_foo(10, true); @@ -441,11 +441,13 @@ TEST(MultiGrid1D, get_location_contents) { static_cast(multigrid1d_foo.add_agent(agent_id_bar, coord1)); static_cast(multigrid1d_foo.add_agent(agent_id_baz, coord1)); - auto tval = set({agent_id_foo, agent_id_bar, agent_id_baz}); + auto tval = set < AgentID > ({ + agent_id_foo, agent_id_bar, agent_id_baz + }); auto rval = multigrid1d_foo.get_location_contents(coord1); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid1D multigrid1d_foo(10, true); @@ -453,15 +455,20 @@ TEST(MultiGrid1D, get_location_contents) { static_cast(multigrid1d_foo.add_agent(agent_id_bar, coord1)); static_cast(multigrid1d_foo.add_agent(agent_id_baz, coord9)); - auto tval = set({agent_id_foo, agent_id_bar}); + auto tval = set < AgentID > ({ + agent_id_foo, agent_id_bar + }); auto rval = multigrid1d_foo.get_location_contents(coord1); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-multigrid2d.cc b/test/unit-kami-multigrid2d.cc index fb1a9dc..8a1e464 100644 --- a/test/unit-kami-multigrid2d.cc +++ b/test/unit-kami-multigrid2d.cc @@ -29,12 +29,14 @@ #include #include +#include #include #include #include using namespace kami; +using namespace kami::error; using namespace std; TEST(MultiGrid2D, DefaultConstructor) { @@ -53,18 +55,15 @@ TEST(MultiGrid2D, add_agent) { { auto agent_id_baz = multigrid2d_foo.add_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { auto agent_id_baz = multigrid2d_foo.add_agent(agent_id_bar, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_bar); + EXPECT_EQ(agent_id_baz, agent_id_bar); } { auto agent_id_baz = multigrid2d_foo.add_agent(agent_id_bar, coord3); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_bar); + EXPECT_EQ(agent_id_baz, agent_id_bar); } } @@ -77,8 +76,7 @@ TEST(MultiGrid2D, delete_agent) { static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_foo); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); @@ -86,8 +84,7 @@ TEST(MultiGrid2D, delete_agent) { static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid2d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_foo); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); @@ -95,8 +92,7 @@ TEST(MultiGrid2D, delete_agent) { static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid2d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_bar); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_bar); + EXPECT_EQ(agent_id_baz, agent_id_bar); } { @@ -104,8 +100,7 @@ TEST(MultiGrid2D, delete_agent) { static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); @@ -113,8 +108,7 @@ TEST(MultiGrid2D, delete_agent) { static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid2d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); @@ -122,31 +116,27 @@ TEST(MultiGrid2D, delete_agent) { static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid2d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_bar, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_bar); + EXPECT_EQ(agent_id_baz, agent_id_bar); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); - auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_foo, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid2d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_foo, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid2d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_bar, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = multigrid2d_foo.delete_agent(agent_id_bar, coord3), AgentNotFound); } } @@ -202,15 +192,13 @@ TEST(MultiGrid2D, move_agent) { static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = multigrid2d_foo.move_agent(agent_id_foo, coord7); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); - auto agent_id_baz = multigrid2d_foo.move_agent(agent_id_foo, coord10); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = multigrid2d_foo.move_agent(agent_id_foo, coord10), LocationInvalid); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); @@ -218,8 +206,7 @@ TEST(MultiGrid2D, move_agent) { static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid2d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid2d_foo.move_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); @@ -227,8 +214,7 @@ TEST(MultiGrid2D, move_agent) { static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); static_cast(multigrid2d_foo.add_agent(agent_id_bar, coord2)); auto agent_id_baz = multigrid2d_foo.move_agent(agent_id_foo, coord7); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } } @@ -239,206 +225,363 @@ TEST(MultiGrid2D, get_neighborhood_VonNeumann) { { MultiGrid2D multigrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{0, 1}, - {0, 1}, - {9, 0}, - {0, 9}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 0, 9 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, true, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{1, 2}, - {2, 1}, - {1, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord1, true, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{0, 1}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, true, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 2}, - {2, 1}, - {1, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord1, true, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{1, 0}, - {0, 1}, - {9, 0}, - {0, 9}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 0, 9 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{1, 0}, - {0, 1}, - {9, 0}, - {0, 9}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 0, 9 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 0}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 0}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); - auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann); - - EXPECT_FALSE(rval); + EXPECT_THROW(auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann), + AgentNotFound); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); multigrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{0, 1}, - {9, 0}, - {0, 9}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 9, 0 + }, + { + 0, 9 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); multigrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{1, 2}, - {2, 1}, - {1, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); multigrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{0, 1}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); multigrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{1, 2}, - {2, 1}, - {1, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); multigrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{1, 0}, - {0, 1}, - {9, 0}, - {0, 9}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 0, 9 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); multigrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{0, 1}, - {1, 2}, - {2, 1}, - {1, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); multigrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{1, 0}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); multigrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{0, 1}, - {1, 2}, - {2, 1}, - {1, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::VonNeumann); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } } @@ -449,254 +592,507 @@ TEST(MultiGrid2D, get_neighborhood_Moore) { { MultiGrid2D multigrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{9, 9}, - {9, 1}, - {1, 1}, - {0, 1}, - {9, 0}, - {1, 9}, - {0, 9}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 9, 9 + }, + { + 9, 1 + }, + { + 1, 1 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 1, 9 + }, + { + 0, 9 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{2, 2}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}, - {2, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord1, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 1}, - {0, 1}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 1 + }, + { + 0, 1 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{2, 2}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}, - {2, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord1, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{9, 9}, - {9, 1}, - {1, 0}, - {1, 1}, - {0, 1}, - {0, 9}, - {1, 9}, - {9, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 9, 9 + }, + { + 9, 1 + }, + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + }, + { + 0, 9 + }, + { + 1, 9 + }, + { + 9, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{9, 9}, - {9, 1}, - {1, 0}, - {1, 1}, - {0, 1}, - {0, 9}, - {1, 9}, - {9, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 9, 9 + }, + { + 9, 1 + }, + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + }, + { + 0, 9 + }, + { + 1, 9 + }, + { + 9, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 0}, - {1, 1}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 0}, - {1, 1}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); - auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore); - - EXPECT_FALSE(rval); + EXPECT_THROW(auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore), + AgentNotFound); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); multigrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{9, 9}, - {9, 1}, - {1, 1}, - {0, 1}, - {9, 0}, - {1, 9}, - {0, 9}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 9, 9 + }, + { + 9, 1 + }, + { + 1, 1 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 1, 9 + }, + { + 0, 9 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); multigrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{2, 2}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}, - {2, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); multigrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{1, 1}, - {0, 1}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 1 + }, + { + 0, 1 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); multigrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{2, 2}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}, - {2, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); multigrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{9, 9}, - {9, 1}, - {1, 0}, - {1, 1}, - {0, 1}, - {0, 9}, - {1, 9}, - {9, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 9, 9 + }, + { + 9, 1 + }, + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + }, + { + 0, 9 + }, + { + 1, 9 + }, + { + 9, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); multigrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{2, 2}, - {2, 0}, - {0, 1}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); multigrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{1, 0}, - {1, 1}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { MultiGrid2D multigrid2d_foo(10, 10, false, false); multigrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{2, 2}, - {2, 0}, - {0, 1}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + } + }); auto rval = multigrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } } @@ -707,21 +1103,23 @@ TEST(MultiGrid2D, get_location_by_agent) { { MultiGrid2D multigrid2d_foo(10, 10, true, true); - EXPECT_FALSE(multigrid2d_foo.get_location_by_agent(agent_id_foo)); - EXPECT_FALSE(multigrid2d_foo.get_location_by_agent(agent_id_bar)); + EXPECT_THROW(auto loc1 = multigrid2d_foo.get_location_by_agent(agent_id_foo), AgentNotFound); + EXPECT_THROW(auto loc2 = multigrid2d_foo.get_location_by_agent(agent_id_bar), AgentNotFound); } { MultiGrid2D multigrid2d_foo(10, 10, true, true); static_cast(multigrid2d_foo.add_agent(agent_id_foo, coord2)); auto local = multigrid2d_foo.get_location_by_agent(agent_id_foo); - EXPECT_TRUE(local); EXPECT_EQ(local, coord2); - EXPECT_FALSE(multigrid2d_foo.get_location_by_agent(agent_id_bar)); + EXPECT_THROW(auto loc = multigrid2d_foo.get_location_by_agent(agent_id_bar), AgentNotFound); } } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-population.cc b/test/unit-kami-population.cc index 7984363..f7a01c0 100644 --- a/test/unit-kami-population.cc +++ b/test/unit-kami-population.cc @@ -24,20 +24,25 @@ */ #include +#include #include #include #include using namespace kami; +using namespace kami::error; using namespace std; -class TestAgent : public Agent { +class TestAgent + : public Agent { private: int _x; public: - explicit TestAgent(int x) : _x(x) {}; + explicit TestAgent(int x) + :_x(x) { + }; AgentID step(shared_ptr model) override { return get_agent_id(); @@ -88,14 +93,13 @@ TEST(Population, get_agent_by_id) { auto agent_baz_opt = population_foo.get_agent_by_id(agent_foo->get_agent_id()); EXPECT_TRUE(agent_baz_opt); - auto agent_baz = dynamic_pointer_cast(agent_baz_opt.value()); + auto agent_baz = dynamic_pointer_cast(agent_baz_opt); EXPECT_EQ(agent_baz->getval(), 8675309); } { Population population_foo; static_cast(population_foo.add_agent(agent_foo)); - auto agent_baz_opt = population_foo.get_agent_by_id(agent_bar->get_agent_id()); - EXPECT_FALSE(agent_baz_opt); + EXPECT_THROW(auto agent_baz_opt = population_foo.get_agent_by_id(agent_bar->get_agent_id()), AgentNotFound); } { Population population_foo; @@ -105,13 +109,13 @@ TEST(Population, get_agent_by_id) { auto agent_baz_opt = population_foo.get_agent_by_id(agent_foo->get_agent_id()); EXPECT_TRUE(agent_baz_opt); - auto agent_baz = dynamic_pointer_cast(agent_baz_opt.value()); + auto agent_baz = dynamic_pointer_cast(agent_baz_opt); EXPECT_EQ(agent_baz->getval(), 8675309); auto agent_qux_opt = population_foo.get_agent_by_id(agent_bar->get_agent_id()); EXPECT_TRUE(agent_qux_opt); - auto agent_qux = dynamic_pointer_cast(agent_qux_opt.value()); + auto agent_qux = dynamic_pointer_cast(agent_qux_opt); EXPECT_EQ(agent_qux->getval(), 1729); } { @@ -122,13 +126,13 @@ TEST(Population, get_agent_by_id) { auto agent_qux_opt = population_foo.get_agent_by_id(agent_bar->get_agent_id()); EXPECT_TRUE(agent_qux_opt); - auto agent_qux = dynamic_pointer_cast(agent_qux_opt.value()); + auto agent_qux = dynamic_pointer_cast(agent_qux_opt); EXPECT_EQ(agent_qux->getval(), 1729); auto agent_baz_opt = population_foo.get_agent_by_id(agent_foo->get_agent_id()); EXPECT_TRUE(agent_baz_opt); - auto agent_baz = dynamic_pointer_cast(agent_baz_opt.value()); + auto agent_baz = dynamic_pointer_cast(agent_baz_opt); EXPECT_EQ(agent_baz->getval(), 8675309); } } @@ -190,7 +194,10 @@ TEST(Population, get_agent_list) { } } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-position.cc b/test/unit-kami-position.cc new file mode 100644 index 0000000..203d1ea --- /dev/null +++ b/test/unit-kami-position.cc @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2022 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include +#include + +using namespace kami; + +class PositionTest + : public ::testing::Test { +protected: + Position pos_foo = GridCoord1D(5); + Position pos_bar = GridCoord2D(2, 5); +}; + +TEST_F(PositionTest, DefaultConstructor) { + EXPECT_EQ(pos_foo, pos_foo); + EXPECT_NE(pos_foo, pos_bar); +} + +int main( + int argc, + char** argv +) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/test/unit-kami-random.cc b/test/unit-kami-random.cc index 819427e..39a42c5 100644 --- a/test/unit-kami-random.cc +++ b/test/unit-kami-random.cc @@ -39,32 +39,39 @@ using namespace kami; using namespace std; -class TestAgent : public Agent { +class TestAgent + : public Agent { public: AgentID step(shared_ptr model) override { return get_agent_id(); } }; -class TestModel : public Model { +class TestModel + : public Model { public: - optional>> step() { - return _sched->step(shared_from_this()); + shared_ptr> retval; + + shared_ptr step() override { + retval = _sched->step(shared_from_this()); + return shared_from_this(); } - optional>> step(shared_ptr> agent_list) { - return _sched->step(shared_from_this(), move(agent_list)); + shared_ptr step(unique_ptr> agent_list) { + retval = _sched->step(shared_from_this(), std::move(agent_list)); + return shared_from_this(); } }; -class RandomSchedulerTest : public ::testing::Test { +class RandomSchedulerTest + : public ::testing::Test { protected: shared_ptr mod = nullptr; - shared_ptr rng = nullptr; + shared_ptr rng = nullptr; void SetUp() override { mod = make_shared(); - rng = make_shared(); + rng = make_shared(); auto popul_foo = make_shared(); auto sched_foo = make_shared(rng); @@ -89,64 +96,75 @@ TEST(RandomScheduler, DefaultConstructor) { } TEST_F(RandomSchedulerTest, step_interface1) { - auto tval = mod->get_population().value()->get_agent_list(); - auto rval = mod->step(); + auto tval = mod->get_population()->get_agent_list(); + auto aval = mod->get_population()->get_agent_list(); + mod->step(std::move(aval)); - EXPECT_TRUE(rval); - EXPECT_EQ(rval.value()->size(), 10); + auto rval = mod->retval; + + EXPECT_EQ(rval->size(), 10); // Sort both return values and just make sure all of them all the same... // We cannot test permutation since, well, you know... - set tval_set = set(tval->begin(), tval->end()); - set rval_set = set(rval.value()->begin(), rval.value()->end()); + set < AgentID > tval_set = set(tval->begin(), tval->end()); + set < AgentID > rval_set = set(rval->begin(), rval->end()); EXPECT_EQ(tval_set, rval_set); } TEST_F(RandomSchedulerTest, step_interface2) { - auto tval = mod->get_population().value()->get_agent_list(); - auto rval = mod->step(tval); + auto tval = mod->get_population()->get_agent_list(); + auto aval = mod->get_population()->get_agent_list(); + mod->step(std::move(aval)); + + auto rval = mod->retval; EXPECT_TRUE(rval); - EXPECT_EQ(rval.value()->size(), 10); + EXPECT_EQ(rval->size(), 10); - set tval_set = set(tval->begin(), tval->end()); - set rval_set = set(rval.value()->begin(), rval.value()->end()); + set < AgentID > tval_set = set(tval->begin(), tval->end()); + set < AgentID > rval_set = set(rval->begin(), rval->end()); EXPECT_EQ(tval_set, rval_set); } TEST_F(RandomSchedulerTest, step_10000) { - auto tval = mod->get_population().value()->get_agent_list(); - set tval_set = set(tval->begin(), tval->end()); - // Do it a lot... for (auto i = 0; i < 10000; i++) { - auto rval = mod->step(); + auto tval = mod->get_population()->get_agent_list(); + auto aval = mod->get_population()->get_agent_list(); + mod->step(std::move(aval)); + + auto rval = mod->retval; + EXPECT_TRUE(rval); - EXPECT_EQ(rval.value()->size(), 10); + EXPECT_EQ(rval->size(), 10); - set rval_set = set(rval.value()->begin(), rval.value()->end()); + set < AgentID > tval_set = set(tval->begin(), tval->end()); + set < AgentID > rval_set = set(rval->begin(), rval->end()); EXPECT_EQ(tval_set, rval_set); } } TEST_F(RandomSchedulerTest, get_rng) { - auto rval = static_pointer_cast(mod->get_scheduler().value())->get_rng(); + auto rval = static_pointer_cast(mod->get_scheduler())->get_rng(); EXPECT_EQ(rng, rval); } TEST_F(RandomSchedulerTest, set_rng) { - auto new_rng = make_shared(); - auto rval1 = static_pointer_cast(mod->get_scheduler().value())->get_rng(); + auto new_rng = make_shared(); + auto rval1 = static_pointer_cast(mod->get_scheduler())->get_rng(); - static_cast(static_pointer_cast(mod->get_scheduler().value())->set_rng(new_rng)); - auto rval2 = static_pointer_cast(mod->get_scheduler().value())->get_rng(); + static_cast(static_pointer_cast(mod->get_scheduler())->set_rng(new_rng)); + auto rval2 = static_pointer_cast(mod->get_scheduler())->get_rng(); EXPECT_EQ(new_rng, rval2); EXPECT_NE(new_rng, rval1); } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-reporter.cc b/test/unit-kami-reporter.cc new file mode 100644 index 0000000..5dfabfe --- /dev/null +++ b/test/unit-kami-reporter.cc @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 2022 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace kami; +using namespace std; + +class TestAgent + : public ReporterAgent { +public: + AgentID step(shared_ptr model) override { + return get_agent_id(); + } + + std::unique_ptr collect() override { + auto json_ret_val = std::make_unique(); + + (*json_ret_val)["fname"] = "Jesse"; + (*json_ret_val)["lname"] = "Pinkman"; + + return json_ret_val; + } +}; + +class TestModel + : public ReporterModel { +public: + shared_ptr> retval; + + std::unique_ptr collect() override { + auto json_ret_val = std::make_unique(); + + (*json_ret_val)["fname"] = "Walter"; + (*json_ret_val)["lname"] = "White"; + + return json_ret_val; + } +}; + +class ReporterModelTest + : public ::testing::Test { +protected: + shared_ptr mod = nullptr; + + void SetUp() override { + mod = make_shared(); + auto popul_foo = make_shared(); + auto sched_foo = make_shared(); + + // Domain is not required for this test + static_cast(mod->set_population(popul_foo)); + static_cast(mod->set_scheduler(sched_foo)); + + for (auto i = 0; i < 3; i++) { + auto agent_foo = make_shared(); + static_cast(popul_foo->add_agent(agent_foo)); + } + } +}; + +TEST(ReporterModel, DefaultConstructor) { + // There is really no way this can go wrong, but + // we add this check anyway in case of future + // changes. + EXPECT_NO_THROW( + const TestModel reporter_foo; + ); +} + +TEST_F(ReporterModelTest, collect) { + auto aval = mod->get_population()->get_agent_list(); + mod->step(); + + auto rval = mod->collect(); + EXPECT_TRUE(rval); + EXPECT_EQ(rval->dump(), "{\"fname\":\"Walter\",\"lname\":\"White\"}"); +} + +TEST_F(ReporterModelTest, report) { + for (auto i = 0; i < 2; i++) { + auto aval = mod->get_population()->get_agent_list(); + + mod->step(); + auto rval = mod->collect(); + } + + auto rval = mod->report(); + EXPECT_EQ(rval->dump(), + "[{\"agent_data\":[{\"agent_id\":\"13\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}},{\"agent_id\":\"14\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}},{\"agent_id\":\"15\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}}],\"model_data\":{\"fname\":\"Walter\",\"lname\":\"White\"},\"step_id\":1},{\"agent_data\":[{\"agent_id\":\"13\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}},{\"agent_id\":\"14\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}},{\"agent_id\":\"15\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}}],\"model_data\":{\"fname\":\"Walter\",\"lname\":\"White\"},\"step_id\":2}]"); +} + +int main( + int argc, + char** argv +) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/test/unit-kami-reporteragent.cc b/test/unit-kami-reporteragent.cc new file mode 100644 index 0000000..cf1b6a2 --- /dev/null +++ b/test/unit-kami-reporteragent.cc @@ -0,0 +1,119 @@ +/*- + * Copyright (c) 2022 The Johns Hopkins University Applied Physics + * Laboratory LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include +#include +#include + +#include +#include + +using namespace kami; +using namespace std; + +class TestAgent + : public ReporterAgent { +public: + AgentID step(shared_ptr model) override { + return get_agent_id(); + } + + std::unique_ptr collect() override { + auto json_ret_val = std::make_unique(); + + (*json_ret_val)["fname"] = "Gus"; + (*json_ret_val)["lname"] = "Fring"; + + return json_ret_val; + } +}; + +class TestModel + : public ReporterModel { +public: + std::unique_ptr collect() override { + return std::make_unique(); + } +}; + +class ReporterAgentTest + : public ::testing::Test { +protected: + TestAgent agent_foo; + TestAgent agent_bar; + shared_ptr model_world = nullptr; + + void SetUp() override { + model_world = make_shared(); + } +}; + +TEST(ReporterAgent, DefaultConstructor) { + EXPECT_NO_THROW( + const TestAgent agent_baz; + const TestAgent agent_qux; + ); +} + +TEST_F(ReporterAgentTest, equivalance) { + EXPECT_EQ(agent_foo, agent_foo); + EXPECT_NE(agent_foo, agent_bar); +} + +TEST_F(ReporterAgentTest, get_agent_id) { + EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.get_agent_id()); + EXPECT_NE(agent_bar.get_agent_id(), agent_foo.get_agent_id()); +} + +TEST_F(ReporterAgentTest, step) { + EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.step(model_world)); + EXPECT_NE(agent_bar.get_agent_id(), agent_foo.step(model_world)); +} + +TEST_F(ReporterAgentTest, collect) { + EXPECT_EQ(agent_foo.collect()->dump(), "{\"fname\":\"Gus\",\"lname\":\"Fring\"}"); + EXPECT_NE(agent_bar.collect()->dump(), "{\"fname\":\"Hank\",\"lname\":\"Schrader\"}"); +} + +TEST_F(ReporterAgentTest, equality) { + EXPECT_TRUE(agent_foo == agent_foo); + EXPECT_TRUE(agent_bar == agent_bar); +} + +TEST_F(ReporterAgentTest, inequality) { + EXPECT_TRUE(agent_foo != agent_bar); + EXPECT_FALSE(agent_bar != agent_bar); +} + +int main( + int argc, + char** argv +) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/test/unit-kami-sequential.cc b/test/unit-kami-sequential.cc index 9fef136..51debe7 100644 --- a/test/unit-kami-sequential.cc +++ b/test/unit-kami-sequential.cc @@ -37,40 +37,47 @@ using namespace kami; using namespace std; -class TestAgent : public Agent { +class TestAgent + : public Agent { public: AgentID step(shared_ptr model) override { return get_agent_id(); } }; -class TestModel : public Model { +class TestModel + : public Model { public: - optional>> step() { - return _sched->step(shared_from_this()); + shared_ptr> retval; + + shared_ptr step() override { + retval = _sched->step(shared_from_this()); + return shared_from_this(); } - optional>> step(shared_ptr> agent_list) { - return _sched->step(shared_from_this(), move(agent_list)); + shared_ptr step(unique_ptr> agent_list) { + retval = _sched->step(shared_from_this(), std::move(agent_list)); + return shared_from_this(); } }; -class SequentialSchedulerTest : public ::testing::Test { +class SequentialSchedulerTest + : public ::testing::Test { protected: shared_ptr mod = nullptr; void SetUp() override { mod = make_shared(); - auto popul_foo = make_shared(); + auto pop_foo = make_shared(); auto sched_foo = make_shared(); // Domain is not required for this test - static_cast(mod->set_population(popul_foo)); + static_cast(mod->set_population(pop_foo)); static_cast(mod->set_scheduler(sched_foo)); for (auto i = 0; i < 10; i++) { auto agent_foo = make_shared(); - static_cast(popul_foo->add_agent(agent_foo)); + static_cast(pop_foo->add_agent(agent_foo)); } } }; @@ -85,36 +92,48 @@ TEST(SequentialScheduler, DefaultConstructor) { } TEST_F(SequentialSchedulerTest, step_interface1) { - auto tval = mod->get_population().value()->get_agent_list(); - auto rval = mod->step(); + auto tval = mod->get_population()->get_agent_list(); + auto aval = mod->get_population()->get_agent_list(); + mod->step(std::move(aval)); + + auto rval = mod->retval; EXPECT_TRUE(rval); - EXPECT_EQ(rval.value()->size(), 10); - EXPECT_EQ(*rval.value(), *tval); + EXPECT_EQ(rval->size(), 10); + EXPECT_EQ(*rval, *tval); } TEST_F(SequentialSchedulerTest, step_interface2) { - auto tval = mod->get_population().value()->get_agent_list(); - auto rval = mod->step(tval); + auto tval = mod->get_population()->get_agent_list(); + auto aval = mod->get_population()->get_agent_list(); + mod->step(std::move(aval)); + + auto rval = mod->retval; EXPECT_TRUE(rval); - EXPECT_EQ(rval.value()->size(), 10); - EXPECT_EQ(*rval.value(), *tval); + EXPECT_EQ(rval->size(), 10); + EXPECT_EQ(*rval, *tval); } TEST_F(SequentialSchedulerTest, step_10000) { - auto tval = mod->get_population().value()->get_agent_list(); - // Do it a lot... for (auto i = 0; i < 10000; i++) { - auto rval = mod->step(); + auto tval = mod->get_population()->get_agent_list(); + auto aval = mod->get_population()->get_agent_list(); + mod->step(std::move(aval)); + + auto rval = mod->retval; + EXPECT_TRUE(rval); - EXPECT_EQ(rval.value()->size(), 10); - EXPECT_EQ(*rval.value(), *tval); + EXPECT_EQ(rval->size(), 10); + EXPECT_EQ(*rval, *tval); } } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-sologrid1d.cc b/test/unit-kami-sologrid1d.cc index 08444cd..4af15ee 100644 --- a/test/unit-kami-sologrid1d.cc +++ b/test/unit-kami-sologrid1d.cc @@ -29,12 +29,14 @@ #include #include +#include #include #include #include using namespace kami; +using namespace kami::error; using namespace std; TEST(SoloGrid1D, DefaultConstructor) { @@ -53,17 +55,14 @@ TEST(SoloGrid1D, add_agent) { { auto agent_id_baz = sologrid1d_foo.add_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { - auto agent_id_baz = sologrid1d_foo.add_agent(agent_id_bar, coord2); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.add_agent(agent_id_bar, coord2), LocationUnavailable); } { auto agent_id_baz = sologrid1d_foo.add_agent(agent_id_bar, coord3); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_bar); + EXPECT_EQ(agent_id_baz, agent_id_bar); } } @@ -76,25 +75,22 @@ TEST(SoloGrid1D, delete_agent) { static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); + EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar), AgentNotFound); } { @@ -102,48 +98,42 @@ TEST(SoloGrid1D, delete_agent) { static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar, coord2); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); + EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar, coord2), AgentNotFound); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); + EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); + EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar, coord3), AgentNotFound); } } @@ -184,7 +174,7 @@ TEST(SoloGrid1D, is_location_empty) { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); EXPECT_FALSE(sologrid1d_foo.is_location_empty(coord2)); EXPECT_TRUE(sologrid1d_foo.is_location_empty(coord3)); } @@ -199,33 +189,29 @@ TEST(SoloGrid1D, move_agent) { static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord7); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord10); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord10), LocationInvalid); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord7); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } } @@ -236,160 +222,176 @@ TEST(SoloGrid1D, get_neighborhood) { { SoloGrid1D sologrid1d_foo(10, true); - auto tval = unordered_set({coord0, coord1, coord9}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord9 + }); auto rval = sologrid1d_foo.get_neighborhood(coord0, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, true); - auto tval = unordered_set({coord0, coord1, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord2 + }); auto rval = sologrid1d_foo.get_neighborhood(coord1, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, false); - auto tval = unordered_set({coord0, coord1}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1 + }); auto rval = sologrid1d_foo.get_neighborhood(coord0, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, false); - auto tval = unordered_set({coord0, coord1, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord2 + }); auto rval = sologrid1d_foo.get_neighborhood(coord1, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, true); - auto tval = unordered_set({coord1, coord9}); + auto tval = unordered_set < GridCoord1D > ({ + coord1, coord9 + }); auto rval = sologrid1d_foo.get_neighborhood(coord0, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, true); - auto tval = unordered_set({coord0, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord2 + }); auto rval = sologrid1d_foo.get_neighborhood(coord1, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, false); - auto tval = unordered_set({coord1}); + auto tval = unordered_set < GridCoord1D > ({ + coord1 + }); auto rval = sologrid1d_foo.get_neighborhood(coord0, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, false); - auto tval = unordered_set({coord0, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord2 + }); auto rval = sologrid1d_foo.get_neighborhood(coord1, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, true); - auto tval = unordered_set({coord0, coord1, coord9}); - auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true); - - EXPECT_FALSE(rval); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord9 + }); + EXPECT_THROW(auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true), AgentNotFound); } { SoloGrid1D sologrid1d_foo(10, true); sologrid1d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({coord0, coord1, coord9}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord9 + }); auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, true); sologrid1d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({coord0, coord1, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord2 + }); auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, false); sologrid1d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({coord0, coord1}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1 + }); auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, false); sologrid1d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({coord0, coord1, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord1, coord2 + }); auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, true); sologrid1d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({coord1, coord9}); + auto tval = unordered_set < GridCoord1D > ({ + coord1, coord9 + }); auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, true); sologrid1d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({coord0, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord2 + }); auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, false); sologrid1d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({coord1}); + auto tval = unordered_set < GridCoord1D > ({ + coord1 + }); auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, false); sologrid1d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({coord0, coord2}); + auto tval = unordered_set < GridCoord1D > ({ + coord0, coord2 + }); auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } } @@ -400,17 +402,16 @@ TEST(SoloGrid1D, get_location_by_agent) { { SoloGrid1D sologrid1d_foo(10, true); - EXPECT_FALSE(sologrid1d_foo.get_location_by_agent(agent_id_foo)); - EXPECT_FALSE(sologrid1d_foo.get_location_by_agent(agent_id_bar)); + EXPECT_THROW(auto loc1 = sologrid1d_foo.get_location_by_agent(agent_id_foo), AgentNotFound); + EXPECT_THROW(auto loc2 = sologrid1d_foo.get_location_by_agent(agent_id_bar), AgentNotFound); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord2)); auto local = sologrid1d_foo.get_location_by_agent(agent_id_foo); - EXPECT_TRUE(local); EXPECT_EQ(local, coord2); - EXPECT_FALSE(sologrid1d_foo.get_location_by_agent(agent_id_bar)); + EXPECT_THROW(auto loc = sologrid1d_foo.get_location_by_agent(agent_id_bar), AgentNotFound); } } @@ -420,9 +421,7 @@ TEST(SoloGrid1D, get_location_contents) { { SoloGrid1D sologrid1d_foo(10, true); - auto agent_list_foo = sologrid1d_foo.get_location_contents(coord10); - - EXPECT_FALSE(agent_list_foo); + EXPECT_THROW(auto agent_list_foo = sologrid1d_foo.get_location_contents(coord10), LocationUnavailable); } { SoloGrid1D sologrid1d_foo(10, true); @@ -430,35 +429,42 @@ TEST(SoloGrid1D, get_location_contents) { auto agent_list_foo = sologrid1d_foo.get_location_contents(coord1); EXPECT_TRUE(agent_list_foo); - EXPECT_TRUE(agent_list_foo.value()->empty()); + EXPECT_TRUE(agent_list_foo->empty()); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord1)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord1)); - static_cast(sologrid1d_foo.add_agent(agent_id_baz, coord1)); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord1)), LocationUnavailable); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_baz, coord1)), LocationUnavailable); - auto tval = set({agent_id_foo}); + auto tval = set < AgentID > ({ + agent_id_foo + }); auto rval = sologrid1d_foo.get_location_contents(coord1); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid1D sologrid1d_foo(10, true); static_cast(sologrid1d_foo.add_agent(agent_id_foo, coord1)); - static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord1)); - static_cast(sologrid1d_foo.add_agent(agent_id_baz, coord9)); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_bar, coord1)), LocationUnavailable); + EXPECT_THROW(static_cast(sologrid1d_foo.add_agent(agent_id_baz, coord1)), LocationUnavailable); - auto tval = set({agent_id_foo}); + auto tval = set < AgentID > ({ + agent_id_foo + }); auto rval = sologrid1d_foo.get_location_contents(coord1); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-sologrid2d.cc b/test/unit-kami-sologrid2d.cc index 844f7b4..68ff47b 100644 --- a/test/unit-kami-sologrid2d.cc +++ b/test/unit-kami-sologrid2d.cc @@ -29,12 +29,14 @@ #include #include +#include #include #include #include using namespace kami; +using namespace kami::error; using namespace std; TEST(SoloGrid2D, DefaultConstructor) { @@ -53,17 +55,14 @@ TEST(SoloGrid2D, add_agent) { { auto agent_id_baz = sologrid2d_foo.add_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { - auto agent_id_baz = sologrid2d_foo.add_agent(agent_id_bar, coord2); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = sologrid2d_foo.add_agent(agent_id_bar, coord2), LocationUnavailable); } { auto agent_id_baz = sologrid2d_foo.add_agent(agent_id_bar, coord3); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_bar); + EXPECT_EQ(agent_id_baz, agent_id_bar); } } @@ -76,74 +75,64 @@ TEST(SoloGrid2D, delete_agent) { static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_foo); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)); + EXPECT_THROW(static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_foo); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_bar); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); + EXPECT_THROW(auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_bar), AgentNotFound); } - { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)); + EXPECT_THROW(static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_bar, coord2); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); + EXPECT_THROW(auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_bar, coord2), AgentNotFound); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_foo, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_foo, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); + EXPECT_THROW(auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_bar, coord3); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); + EXPECT_THROW(auto agent_id_baz = sologrid2d_foo.delete_agent(agent_id_bar, coord3), AgentNotFound); } } @@ -184,7 +173,6 @@ TEST(SoloGrid2D, is_location_empty) { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)); EXPECT_FALSE(sologrid2d_foo.is_location_empty(coord2)); EXPECT_TRUE(sologrid2d_foo.is_location_empty(coord3)); } @@ -192,40 +180,34 @@ TEST(SoloGrid2D, is_location_empty) { TEST(SoloGrid2D, move_agent) { const AgentID agent_id_foo, agent_id_bar; - const GridCoord2D coord2(2, 5), coord3(3, 7), coord7(7, 2), coord10(10, 5); + const GridCoord2D coord2(2, 5), coord7(7, 2), coord10(10, 5); { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); auto agent_id_baz = sologrid2d_foo.move_agent(agent_id_foo, coord7); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - auto agent_id_baz = sologrid2d_foo.move_agent(agent_id_foo, coord10); - EXPECT_FALSE(agent_id_baz); + EXPECT_THROW(auto agent_id_baz = sologrid2d_foo.move_agent(agent_id_foo, coord10), LocationInvalid); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)); - auto agent_id_baz = sologrid2d_foo.move_agent(agent_id_foo, coord2); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_THROW(static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); - static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)); + EXPECT_THROW(static_cast(sologrid2d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable); auto agent_id_baz = sologrid2d_foo.move_agent(agent_id_foo, coord7); - EXPECT_TRUE(agent_id_baz); - EXPECT_EQ(agent_id_baz.value(), agent_id_foo); + EXPECT_EQ(agent_id_baz, agent_id_foo); } } @@ -236,206 +218,347 @@ TEST(SoloGrid2D, get_neighborhood_VonNeumann) { { SoloGrid2D sologrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{0, 1}, - {0, 1}, - {9, 0}, - {0, 9}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 0, 9 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, true, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{1, 2}, - {2, 1}, - {1, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord1, true, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{0, 1}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, true, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 2}, - {2, 1}, - {1, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord1, true, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{1, 0}, - {0, 1}, - {9, 0}, - {0, 9}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 0, 9 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{1, 0}, - {0, 1}, - {9, 0}, - {0, 9}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 0, 9 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 0}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 0}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); - auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann); - - EXPECT_FALSE(rval); + EXPECT_THROW(auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann), + AgentNotFound); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); sologrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{0, 1}, - {9, 0}, - {0, 9}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 9, 0 + }, + { + 0, 9 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); sologrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{1, 2}, - {2, 1}, - {1, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); sologrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{0, 1}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); sologrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{1, 2}, - {2, 1}, - {1, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); sologrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{1, 0}, - {0, 1}, - {9, 0}, - {0, 9}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 0, 9 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); sologrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{0, 1}, - {1, 2}, - {2, 1}, - {1, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); sologrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{1, 0}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 0, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); sologrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{0, 1}, - {1, 2}, - {2, 1}, - {1, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 0, 1 + }, + { + 1, 2 + }, + { + 2, 1 + }, + { + 1, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::VonNeumann); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } } @@ -446,279 +569,532 @@ TEST(SoloGrid2D, get_neighborhood_Moore) { { SoloGrid2D sologrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{9, 9}, - {9, 1}, - {1, 1}, - {0, 1}, - {9, 0}, - {1, 9}, - {0, 9}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 9, 9 + }, + { + 9, 1 + }, + { + 1, 1 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 1, 9 + }, + { + 0, 9 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, true, GridNeighborhoodType::Moore); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{2, 2}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}, - {2, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord1, true, GridNeighborhoodType::Moore); - EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 1}, - {0, 1}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 1 + }, + { + 0, 1 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{2, 2}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}, - {2, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord1, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{9, 9}, - {9, 1}, - {1, 0}, - {1, 1}, - {0, 1}, - {0, 9}, - {1, 9}, - {9, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 9, 9 + }, + { + 9, 1 + }, + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + }, + { + 0, 9 + }, + { + 1, 9 + }, + { + 9, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); - auto tval = unordered_set({{9, 9}, - {9, 1}, - {1, 0}, - {1, 1}, - {0, 1}, - {0, 9}, - {1, 9}, - {9, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 9, 9 + }, + { + 9, 1 + }, + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + }, + { + 0, 9 + }, + { + 1, 9 + }, + { + 9, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 0}, - {1, 1}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); - auto tval = unordered_set({{1, 0}, - {1, 1}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(coord0, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); - auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore); - - EXPECT_FALSE(rval); + EXPECT_THROW(auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore), + AgentNotFound); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); sologrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{9, 9}, - {9, 1}, - {1, 1}, - {0, 1}, - {9, 0}, - {1, 9}, - {0, 9}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 9, 9 + }, + { + 9, 1 + }, + { + 1, 1 + }, + { + 0, 1 + }, + { + 9, 0 + }, + { + 1, 9 + }, + { + 0, 9 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); sologrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{2, 2}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}, - {2, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); sologrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{1, 1}, - {0, 1}, - {1, 0}, - {0, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 1 + }, + { + 0, 1 + }, + { + 1, 0 + }, + { + 0, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); sologrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{2, 2}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}, - {2, 0}, - {0, 1}, - {1, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 1, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, true, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); sologrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{9, 9}, - {9, 1}, - {1, 0}, - {1, 1}, - {0, 1}, - {0, 9}, - {1, 9}, - {9, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 9, 9 + }, + { + 9, 1 + }, + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + }, + { + 0, 9 + }, + { + 1, 9 + }, + { + 9, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); sologrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{2, 2}, - {2, 0}, - {0, 1}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); sologrid2d_foo.add_agent(agent_id_foo, coord0); - auto tval = unordered_set({{1, 0}, - {1, 1}, - {0, 1}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 1, 0 + }, + { + 1, 1 + }, + { + 0, 1 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } { SoloGrid2D sologrid2d_foo(10, 10, false, false); sologrid2d_foo.add_agent(agent_id_foo, coord1); - auto tval = unordered_set({{2, 2}, - {2, 0}, - {0, 1}, - {0, 2}, - {1, 2}, - {0, 0}, - {2, 1}, - {1, 0}}); + auto tval = unordered_set < GridCoord2D > ({ + { + 2, 2 + }, + { + 2, 0 + }, + { + 0, 1 + }, + { + 0, 2 + }, + { + 1, 2 + }, + { + 0, 0 + }, + { + 2, 1 + }, + { + 1, 0 + } + }); auto rval = sologrid2d_foo.get_neighborhood(agent_id_foo, false, GridNeighborhoodType::Moore); EXPECT_TRUE(rval); - EXPECT_EQ(tval, *rval.value()); + EXPECT_EQ(tval, *rval); } } TEST(SoloGrid2D, get_location_by_agent) { const AgentID agent_id_foo, agent_id_bar; - const GridCoord2D coord2(2, 5), coord3(3, 7); + const GridCoord2D coord2(2, 5); { SoloGrid2D sologrid2d_foo(10, 10, true, true); - EXPECT_FALSE(sologrid2d_foo.get_location_by_agent(agent_id_foo)); - EXPECT_FALSE(sologrid2d_foo.get_location_by_agent(agent_id_bar)); + EXPECT_THROW(auto loc1 = sologrid2d_foo.get_location_by_agent(agent_id_foo), AgentNotFound); + EXPECT_THROW(auto loc2 = sologrid2d_foo.get_location_by_agent(agent_id_bar), AgentNotFound); } { SoloGrid2D sologrid2d_foo(10, 10, true, true); static_cast(sologrid2d_foo.add_agent(agent_id_foo, coord2)); auto local = sologrid2d_foo.get_location_by_agent(agent_id_foo); - EXPECT_TRUE(local); EXPECT_EQ(local, coord2); - EXPECT_FALSE(sologrid2d_foo.get_location_by_agent(agent_id_bar)); + EXPECT_THROW(auto loc = sologrid2d_foo.get_location_by_agent(agent_id_bar), AgentNotFound); } } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-staged.cc b/test/unit-kami-staged.cc index 77e69ef..70c0b7a 100644 --- a/test/unit-kami-staged.cc +++ b/test/unit-kami-staged.cc @@ -23,6 +23,7 @@ * SOFTWARE. */ +#include #include #include #include @@ -37,7 +38,8 @@ using namespace kami; using namespace std; -class TestAgent : public StagedAgent { +class TestAgent + : public StagedAgent { public: AgentID step(shared_ptr model) override { return get_agent_id(); @@ -48,18 +50,24 @@ class TestAgent : public StagedAgent { } }; -class TestModel : public Model { +class TestModel + : public Model { public: - optional>> step() { - return _sched->step(shared_from_this()); + shared_ptr> retval; + + shared_ptr step() override { + retval = _sched->step(shared_from_this()); + return shared_from_this(); } - optional>> step(shared_ptr> agent_list) { - return _sched->step(shared_from_this(), move(agent_list)); + shared_ptr step(unique_ptr> agent_list) { + retval = _sched->step(shared_from_this(), std::move(agent_list)); + return shared_from_this(); } }; -class StagedSchedulerTest : public ::testing::Test { +class StagedSchedulerTest + : public ::testing::Test { protected: shared_ptr mod = nullptr; @@ -89,36 +97,45 @@ TEST(StagedScheduler, DefaultConstructor) { } TEST_F(StagedSchedulerTest, step_interface1) { - auto tval = mod->get_population().value()->get_agent_list(); - auto rval = mod->step(); + auto tval = mod->get_population()->get_agent_list(); + auto aval = mod->get_population()->get_agent_list(); + mod->step(std::move(aval)); + + auto rval = mod->retval; - EXPECT_TRUE(rval); - EXPECT_EQ(rval.value()->size(), 10); - EXPECT_EQ(*rval.value(), *tval); + EXPECT_EQ(rval->size(), 10); + EXPECT_EQ(*rval, *tval); } TEST_F(StagedSchedulerTest, step_interface2) { - auto tval = mod->get_population().value()->get_agent_list(); - auto rval = mod->step(tval); + auto tval = mod->get_population()->get_agent_list(); + auto aval = mod->get_population()->get_agent_list(); + mod->step(std::move(aval)); + + auto rval = mod->retval; - EXPECT_TRUE(rval); - EXPECT_EQ(rval.value()->size(), 10); - EXPECT_EQ(*rval.value(), *tval); + EXPECT_EQ(rval->size(), 10); + EXPECT_EQ(*rval, *tval); } TEST_F(StagedSchedulerTest, step_10000) { - auto tval = mod->get_population().value()->get_agent_list(); - // Do it a lot... for (auto i = 0; i < 10000; i++) { - auto rval = mod->step(); - EXPECT_TRUE(rval); - EXPECT_EQ(rval.value()->size(), 10); - EXPECT_EQ(*rval.value(), *tval); + auto tval = mod->get_population()->get_agent_list(); + auto aval = mod->get_population()->get_agent_list(); + mod->step(std::move(aval)); + + auto rval = mod->retval; + + EXPECT_EQ(rval->size(), 10); + EXPECT_EQ(*rval, *tval); } } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/test/unit-kami-stagedagent.cc b/test/unit-kami-stagedagent.cc index 4441af0..d9e32a0 100644 --- a/test/unit-kami-stagedagent.cc +++ b/test/unit-kami-stagedagent.cc @@ -28,12 +28,14 @@ #include #include +#include #include using namespace kami; using namespace std; -class TestStagedAgent : public StagedAgent { +class TestAgent + : public StagedAgent { public: AgentID advance(shared_ptr model) override { return get_agent_id(); @@ -44,60 +46,63 @@ class TestStagedAgent : public StagedAgent { } }; -class TestModel : public Model { +class TestModel + : public Model { +}; + +class StagedAgentTest + : public ::testing::Test { +protected: + TestAgent agent_foo; + TestAgent agent_bar; + shared_ptr model_world = nullptr; + + void SetUp() override { + model_world = make_shared(); + } }; TEST(StagedAgent, DefaultConstructor) { - const TestStagedAgent agent_foo; - const TestStagedAgent agent_bar; + EXPECT_NO_THROW( + const TestAgent agent_baz; + const TestAgent agent_qux; + ); +} +TEST_F(StagedAgentTest, equivalance) { EXPECT_EQ(agent_foo, agent_foo); EXPECT_NE(agent_foo, agent_bar); } -TEST(StagedAgent, get_agent_id) { - const TestStagedAgent agent_foo; - const TestStagedAgent agent_bar; - +TEST_F(StagedAgentTest, get_agent_id) { EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.get_agent_id()); EXPECT_NE(agent_bar.get_agent_id(), agent_foo.get_agent_id()); } -TEST(StagedAgent, advance) { - TestStagedAgent agent_foo; - TestStagedAgent agent_bar; - auto model_world = make_shared(); - - EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.advance(model_world)); - EXPECT_NE(agent_bar.get_agent_id(), agent_foo.advance(model_world)); -} - -TEST(StagedAgent, step) { - TestStagedAgent agent_foo; - TestStagedAgent agent_bar; - auto model_world = make_shared(); - +TEST_F(StagedAgentTest, step) { EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.step(model_world)); EXPECT_NE(agent_bar.get_agent_id(), agent_foo.step(model_world)); } -TEST(StagedAgent, Equality) { - const TestStagedAgent agent_foo; - const TestStagedAgent agent_bar; +TEST_F(StagedAgentTest, advance) { + EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.advance(model_world)); + EXPECT_NE(agent_bar.get_agent_id(), agent_foo.advance(model_world)); +} +TEST_F(StagedAgentTest, equality) { EXPECT_TRUE(agent_foo == agent_foo); EXPECT_TRUE(agent_bar == agent_bar); } -TEST(StagedAgent, Inequality) { - const TestStagedAgent agent_foo; - const TestStagedAgent agent_bar; - +TEST_F(StagedAgentTest, inequality) { EXPECT_TRUE(agent_foo != agent_bar); EXPECT_FALSE(agent_bar != agent_bar); } -int main(int argc, char **argv) { +int main( + int argc, + char** argv +) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }