ソースを参照

Implemented StateMachine class

- implemented StateMachine class by owning a state holder
- added tests for StateMachine class
- added state machine test graphics
pcmattulat 3 年 前
コミット
95367a234f

+ 54 - 7
source/logic/StateMachine.cpp

@@ -3,22 +3,69 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-09-05
- * Changed:         2020-09-07
+ * Changed:         2020-09-10
  *
  * */
 
 #include "StateMachine.hpp"
 
-ls_std::StateMachine::StateMachine() : Class("StateMachine")
+ls_std::StateMachine::StateMachine(std::string _name) :
+Class("StateMachine"),
+name(std::move(_name))
 {}
 
-bool ls_std::StateMachine::addState(std::shared_ptr<State> _state) {
-  bool added {};
-  this->states.insert({_state->getId(), std::move(_state)});
+bool ls_std::StateMachine::addState(const std::shared_ptr<State>& _state) {
+  bool condition = !this->_stateExists(_state->getId());
 
-  return added;
+  if(condition) {
+    this->states.insert({_state->getId(), _state});
+    condition = this->_stateExists(_state->getId());
+  }
+
+  return condition;
+}
+
+std::shared_ptr<ls_std::State> ls_std::StateMachine::getCurrentState() {
+  return this->currentState;
+}
+
+std::string ls_std::StateMachine::getName() {
+  return this->name;
+}
+
+bool ls_std::StateMachine::proceed() {
+  std::vector<ls_std::StateId> nextValidStates = this->_getNextValidStates();
+  bool condition = nextValidStates.size() == 1;
+
+  if(condition) {
+    this->currentState = this->states[nextValidStates.at(0)];
+  }
+
+  return condition;
+}
+
+bool ls_std::StateMachine::setStartState(const ls_std::StateId&_id) {
+  bool exists = this->_stateExists(_id);
+
+  if(exists) {
+    this->currentState = this->states[_id];
+  }
+
+  return exists;
+}
+
+std::vector<ls_std::StateId> ls_std::StateMachine::_getNextValidStates() {
+  std::vector<ls_std::StateId> validStates {};
+
+  for(const auto& state : this->currentState->getConnectedStates()) {
+    if(state.second->isPassable()) {
+      validStates.push_back(state.second->getStateId());
+    }
+  }
+
+  return validStates;
 }
 
-bool ls_std::StateMachine::_stateExists(const std::string &_id) {
+bool ls_std::StateMachine::_stateExists(const ls_std::StateId &_id) {
   return this->states.find(_id) != this->states.end();
 }

+ 12 - 6
source/logic/StateMachine.hpp

@@ -3,7 +3,7 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-09-05
- * Changed:         2020-09-07
+ * Changed:         2020-09-10
  *
  * */
 
@@ -13,26 +13,32 @@
 #include <memory>
 #include <unordered_map>
 #include <string>
+#include <vector>
 #include "../base/Class.hpp"
 #include "State.hpp"
+#include "StateMachineTypes.hpp"
 
 namespace ls_std {
   class StateMachine : public Class {
     public:
 
-      StateMachine();
+      explicit StateMachine(std::string _name);
       ~StateMachine() = default;
 
-      bool addState(std::shared_ptr<State> _state);
+      bool addState(const std::shared_ptr<State>& _state);
+      std::shared_ptr<State> getCurrentState();
+      std::string getName();
       bool proceed();
-      bool setStartState(const std::string& _id);
+      bool setStartState(const StateId& _id);
 
     private:
 
       std::shared_ptr<State> currentState {};
-      std::unordered_map<std::string, std::shared_ptr<State>> states {};
+      std::string name {};
+      std::unordered_map<StateId, std::shared_ptr<State>> states {};
 
-      bool _stateExists(const std::string& _id);
+      std::vector<StateId> _getNextValidStates();
+      bool _stateExists(const StateId& _id);
   };
 }
 

+ 140 - 0
test/cases/logic/StateMachineTest.cpp

@@ -0,0 +1,140 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-09-09
+ * Changed:         2020-09-10
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include "../../../source/logic/StateMachine.hpp"
+
+namespace {
+  class StateMachineTest : public ::testing::Test {
+    protected:
+
+      StateMachineTest() = default;
+      ~StateMachineTest() override = default;
+
+      void SetUp() override {}
+      void TearDown() override {}
+
+      static ls_std::StateMachine _createStateMachine() {
+        ls_std::StateMachine stateMachine {"test_machine"};
+
+        std::shared_ptr<ls_std::State> stateA = std::make_shared<ls_std::State>("A");
+        std::shared_ptr<ls_std::State> stateB = std::make_shared<ls_std::State>("B");
+        std::shared_ptr<ls_std::State> stateC = std::make_shared<ls_std::State>("C");
+        std::shared_ptr<ls_std::State> stateD = std::make_shared<ls_std::State>("D");
+        std::shared_ptr<ls_std::State> stateE = std::make_shared<ls_std::State>("E");
+
+        // add states
+
+        stateMachine.addState(stateA);
+        stateMachine.addState(stateB);
+        stateMachine.addState(stateC);
+        stateMachine.addState(stateD);
+        stateMachine.addState(stateE);
+
+        // add connections / see state_machine_test.png
+
+        stateA->addStateConnection("AB", stateB);
+        stateB->addStateConnection("BC", stateC);
+        stateB->addStateConnection("BD", stateD);
+        stateC->addStateConnection("CB", stateB);
+        stateC->addStateConnection("CE", stateE);
+        stateD->addStateConnection("DE", stateE);
+
+        return stateMachine;
+      }
+  };
+
+  TEST_F(StateMachineTest, addStateConnection)
+  {
+    ls_std::StateMachine stateMachine {"test_machine"};
+    ASSERT_TRUE(stateMachine.addState(std::make_shared<ls_std::State>("A")));
+  }
+
+  TEST_F(StateMachineTest, addStateConnectionNegative)
+  {
+    ls_std::StateMachine stateMachine {"test_machine"};
+    ASSERT_TRUE(stateMachine.addState(std::make_shared<ls_std::State>("A")));
+    ASSERT_FALSE(stateMachine.addState(std::make_shared<ls_std::State>("A")));
+  }
+
+  TEST_F(StateMachineTest, getCurrentState)
+  {
+    ls_std::StateMachine stateMachine {"test_machine"};
+    ASSERT_TRUE(stateMachine.addState(std::make_shared<ls_std::State>("A")));
+
+    ASSERT_TRUE(stateMachine.getCurrentState() == nullptr);
+  }
+
+  TEST_F(StateMachineTest, getName)
+  {
+    ls_std::StateMachine stateMachine {"test_machine"};
+    ASSERT_STREQ("test_machine", stateMachine.getName().c_str());
+  }
+
+  TEST_F(StateMachineTest, proceed)
+  {
+    ls_std::StateMachine stateMachine = _createStateMachine();
+    ASSERT_STREQ("test_machine", stateMachine.getName().c_str());
+    ASSERT_TRUE(stateMachine.setStartState("A"));
+
+    ASSERT_FALSE(stateMachine.proceed());
+
+    // activate AB
+
+    stateMachine.getCurrentState()->getConnectedStates().at("AB")->updatePassCondition(true);
+    ASSERT_TRUE(stateMachine.proceed());
+    ASSERT_STREQ("B", stateMachine.getCurrentState()->getId().c_str());
+
+    // activate BC and BD
+
+    stateMachine.getCurrentState()->getConnectedStates().at("BC")->updatePassCondition(true);
+    stateMachine.getCurrentState()->getConnectedStates().at("BD")->updatePassCondition(true);
+    ASSERT_FALSE(stateMachine.proceed());
+
+    // BC only!
+
+    stateMachine.getCurrentState()->getConnectedStates().at("BD")->updatePassCondition(false);
+    ASSERT_TRUE(stateMachine.proceed());
+    ASSERT_STREQ("C", stateMachine.getCurrentState()->getId().c_str());
+
+    // activate CB
+
+    stateMachine.getCurrentState()->getConnectedStates().at("CB")->updatePassCondition(true);
+    ASSERT_TRUE(stateMachine.proceed());
+    ASSERT_STREQ("B", stateMachine.getCurrentState()->getId().c_str());
+
+    // BD only!
+
+    stateMachine.getCurrentState()->getConnectedStates().at("BD")->updatePassCondition(true);
+    stateMachine.getCurrentState()->getConnectedStates().at("BC")->updatePassCondition(false);
+    ASSERT_TRUE(stateMachine.proceed());
+    ASSERT_STREQ("D", stateMachine.getCurrentState()->getId().c_str());
+
+    // activate DE
+
+    stateMachine.getCurrentState()->getConnectedStates().at("DE")->updatePassCondition(true);
+    ASSERT_TRUE(stateMachine.proceed());
+    ASSERT_STREQ("E", stateMachine.getCurrentState()->getId().c_str());
+
+    // end reached
+
+    ASSERT_FALSE(stateMachine.proceed());
+    ASSERT_STREQ("E", stateMachine.getCurrentState()->getId().c_str());
+  }
+
+  TEST_F(StateMachineTest, setStartState)
+  {
+    ls_std::StateMachine stateMachine {"test_machine"};
+    ASSERT_FALSE(stateMachine.getCurrentState() != nullptr);
+
+    ASSERT_TRUE(stateMachine.addState(std::make_shared<ls_std::State>("A")));
+    ASSERT_TRUE(stateMachine.setStartState("A"));
+    ASSERT_TRUE(stateMachine.getCurrentState() != nullptr);
+  }
+}

BIN
test/resources/state_machine_test.png