Skip to content

Commit

Permalink
Merge pull request #17 from mdkdoc15/david_r
Browse files Browse the repository at this point in the history
TrackingAgent with Alerter
  • Loading branch information
mdkdoc15 committed Dec 4, 2023
2 parents 549250a + 38a5eb8 commit 686864b
Show file tree
Hide file tree
Showing 9 changed files with 835 additions and 416 deletions.
297 changes: 191 additions & 106 deletions source/Agents/AgentFactory.hpp
Original file line number Diff line number Diff line change
@@ -1,132 +1,217 @@
//
// Created by Matthew Kight on 9/24/23.
//
/**
* @file AgentFactory.hpp
* @author Matt Kight, David Rackerby
*
* A factory class that abstracts away the initialization of adding an agent to a world
*/

#pragma once

#include "../core/AgentBase.hpp"
#include "AStarAgent.hpp"
#include "PacingAgent.hpp"
#include "PathAgent.hpp"
#include "TrackingAgent.hpp"
#include "AgentLibary.hpp"

#include "../core/Entity.hpp"
#include "../core/WorldBase.hpp"

namespace walle {

// Forward-declare since it's easier to understand how AddXAgent works when the structs are defined nearby
struct PacingAgentData;
struct PathAgentData;
struct AStarAgentData;
struct TrackingAgentData;

class AgentFactory {
private:
cse491::WorldBase &world;

public:
AgentFactory() = delete;

/**
* Constructor for AgentFactory
* @param world we are adding agents too
*/
explicit AgentFactory(cse491::WorldBase &world) : world(world) {}

AStarAgent &AddAStarAgent(const AStarAgentData &agent_data);
cse491::PacingAgent &AddPacingAgent(const PacingAgentData &agent_data);
TrackingAgent &AddTrackingAgent(const TrackingAgentData &agent_data);
PathAgent &AddPathAgent(const PathAgentData &agent_data);

}; // class AgentFactory

/**
* Stores data for AgentBase
*/
struct BaseAgentData {
std::string name;
cse491::GridPosition position;
char symbol = '*';
};
/// Name of the agent
std::string name;

/// Agent's position
cse491::GridPosition position;

/// Agent's representation
char symbol = '*';

struct AStarAgentData : public BaseAgentData {
int recalculate_after_x_turns = 5;
cse491::GridPosition target;
};

/**
* Stores data for a PacingAgent
*/
struct PacingAgentData : public BaseAgentData {
bool vertical = false;
/// Whether the PacingAgent is moving up and down (vertical) or left and right(!vertical)
bool vertical = false;
};

struct TrackingAgentData : public BaseAgentData {
std::string path;
std::vector<cse491::GridPosition> vector_path;
cse491::Entity *target;
int tracking_distance = 5;
cse491::GridPosition start_pos;
};
/**
* Add a PacingAgent to the world
* @param agent_data data for agent we want to create
* @return self
*/
cse491::PacingAgent &AgentFactory::AddPacingAgent(const PacingAgentData &agent_data) {
auto &entity = world.AddAgent<cse491::PacingAgent>(agent_data.name).SetPosition(agent_data.position).SetProperty(
"symbol",
agent_data.symbol);
auto &agent = DownCastAgent<cse491::PacingAgent>(entity);
agent.SetVertical(agent_data.vertical);
return agent;
}

/**
* Stores data for a PathAgent
*/
struct PathAgentData : public BaseAgentData {
std::string path;
std::vector<cse491::GridPosition> vector_path;
/// Starting index into the vector of GridPositions
int index;

/// String representation of the path traveled (e.g. "n s e w" for north south east west)
std::string string_path;

/// Set of grid positions that are applied to the agent's position during one step (constructed from string_path)
std::vector<cse491::GridPosition> vector_path;
};

class AgentFactory {
private:
cse491::WorldBase &world;
public:
AgentFactory() = delete;
/**
* Constructor for AgentFactory
* @param world we are adding agents too
*/
AgentFactory(cse491::WorldBase &world) : world(world) {}

/**
* Add Agent to the world
* @param agent_data data for agent we want to create
* @return self
*/
AStarAgent &AddAStarAgent(const AStarAgentData &agent_data) {
auto &entity = world.AddAgent<walle::AStarAgent>(agent_data.name).SetPosition(agent_data.position).SetProperty(
"symbol",
agent_data.symbol);
assert(dynamic_cast<walle::AStarAgent *>(&entity));
auto &agent = static_cast<walle::AStarAgent &>(entity);
agent.SetGoalPosition(agent_data.target);
agent.SetRecalculate(agent_data.recalculate_after_x_turns);

return agent;
}
/**
* Add Agent to the world
* @param agent_data data for agent we want to create
* @return self
*/
cse491::PacingAgent &AddPacingAgent(const PacingAgentData &agent_data) {
auto
&entity = world.AddAgent<cse491::PacingAgent>(agent_data.name).SetPosition(agent_data.position).SetProperty(
"symbol",
agent_data.symbol);
assert(dynamic_cast<cse491::PacingAgent *>(&entity));
auto &agent = static_cast<cse491::PacingAgent &>(entity);
agent.SetVertical(agent_data.vertical);
return agent;
}
/**
* Add Agent to the world
* @param agent_data data for agent we want to create
* @return self
*/
TrackingAgent &AddTrackingAgent(const TrackingAgentData &agent_data) {
auto &entity =
world.AddAgent<walle::TrackingAgent>(agent_data.name).SetPosition(agent_data.position).SetProperty(
"symbol",
agent_data.symbol);
assert(dynamic_cast<walle::TrackingAgent *>(&entity));
auto &agent = static_cast<walle::TrackingAgent &>(entity);
if (!agent_data.path.empty())
agent.SetProperty<std::basic_string_view<char>>("path",
agent_data.path);
else
agent.SetPath(agent_data.vector_path);
agent.SetTarget(agent_data.target);
agent.SetTrackingDistance(agent_data.tracking_distance);
agent.SetStartPosition(agent_data.start_pos);
agent.Initialize();
return agent;
}

/**
* Add Agent to the world
* @param agent_data data for agent we want to create
* @return self
*/
PathAgent &AddPathAgent(const PathAgentData &agent_data) {
auto &entity =
world.AddAgent<walle::PathAgent>(agent_data.name).SetPosition(agent_data.position).SetProperty(
"symbol",
agent_data.symbol);
assert(dynamic_cast<walle::PathAgent *>(&entity));
auto &agent = static_cast<walle::PathAgent &>(entity);
if (!agent_data.path.empty())
agent.SetProperty<std::basic_string_view<char>>("path",
agent_data.path); // TODO add another option to provide grid point
else
agent.SetPath(agent_data.vector_path);
agent.Initialize();
return agent;
}
/**
* Add a PathAgent to the world
* @param agent_data data for agent we want to create
* @return self
*/
PathAgent &AgentFactory::AddPathAgent(const PathAgentData &agent_data) {
auto &entity = world.AddAgent<walle::PathAgent>(agent_data.name).SetPosition(agent_data.position).SetProperty(
"symbol",
agent_data.symbol);
auto &agent = DownCastAgent<walle::PathAgent>(entity);
if (!agent_data.string_path.empty()) {
agent.SetProperty<std::basic_string_view<char>>("path",
agent_data.string_path); // TODO add another option to provide grid point
} else {
agent.SetPath(agent_data.vector_path);
}
agent.Initialize();
return agent;
}

/**
* Stores data for an AStarAgent
*/
struct AStarAgentData : public BaseAgentData {
/// Number of steps after which the shortest path is recalculated
int recalculate_after_x_turns = 5;

/// The final position in the world that the AStarAgent is travelling to
cse491::GridPosition goal_pos;
};

/**
* Add an AStarAgent to the world
* @param agent_data data for agent we want to create
* @return self
*/
AStarAgent &AgentFactory::AddAStarAgent(const AStarAgentData &agent_data) {
auto &entity = world.AddAgent<walle::AStarAgent>(agent_data.name).SetPosition(agent_data.position).SetProperty(
"symbol",
agent_data.symbol);
auto &agent = DownCastAgent<walle::AStarAgent>(entity);
agent.SetGoalPosition(agent_data.goal_pos);
agent.SetRecalculate(agent_data.recalculate_after_x_turns);
return agent;
}

/**
* Stores data for a TrackingAgent
*/
struct TrackingAgentData : public BaseAgentData {
/// Set of grid positions that are applied to the agent's position during one step (constructed from string_path) like in PathAgent
std::vector<cse491::GridPosition> vector_path;

/// String representation of the path traveled (e.g. "n s e w" for north south east west) like in PathAgent
std::string string_path;

/// Goal Entity being tracked (must not be null or else the agent simply behaves like a PathAgent)
cse491::Entity *target;

/// Distance that the TrackingAgent can "see" such that when the target enters that range, it begins tracking
int tracking_distance = 5;

/// Where the TrackingAgent begins from patrolling from and returns two after the target moves out of range
cse491::GridPosition start_pos;

/// Shared reference to an Alerter, which is non-null if the agent should be able to tell other agents to immediately
/// focus on their targets
/// @remark You should be using the **same** shared pointer across multiple instances of TrackingAgentData in order
/// to make the TrackingAgents part of the same network. This means you need to copy around this shared pointer
/// when using the factory
std::shared_ptr<Alerter> alerter = nullptr;

/// Use initial values
TrackingAgentData() = default;

/// Set all values
TrackingAgentData(std::string name,
cse491::GridPosition curr_pos,
char symbol,
std::string path,
cse491::Entity * target,
int tracking_dist,
cse491::GridPosition start_pos,
std::shared_ptr<Alerter> alerter)
: BaseAgentData({std::move(name), curr_pos, symbol}),
vector_path(StrToOffsets(path)),
string_path(std::move(path)),
target(target),
tracking_distance(tracking_dist),
start_pos(start_pos),
alerter(alerter) {}
};

/**
* Add a TrackingAgent to the world
* @param agent_data data for agent we want to create
* @return self
*/
TrackingAgent &AgentFactory::AddTrackingAgent(const TrackingAgentData &agent_data) {
auto &entity = world.AddAgent<walle::TrackingAgent>(agent_data.name).SetPosition(agent_data.position).SetProperty(
"symbol",
agent_data.symbol);
auto &agent = DownCastAgent<TrackingAgent>(entity);
if (!agent_data.string_path.empty()) {
agent.SetProperty<std::basic_string_view<char>>("path", agent_data.string_path);
} else {
agent.SetPath(agent_data.vector_path);
}
agent.SetTarget(agent_data.target);
agent.SetTrackingDistance(agent_data.tracking_distance);
agent.SetStartPosition(agent_data.start_pos);
if (agent_data.alerter != nullptr) {
agent.SetProperty("alerter", agent_data.alerter);
}
agent.Initialize();
return agent;
}

} // namespace walle
9 changes: 9 additions & 0 deletions source/Agents/AgentLibary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,13 @@ StrToOffsets(std::string_view commands) {
return positions;
}

template<typename T>
concept Agent_Type = std::is_base_of_v<cse491::AgentBase, T>;

template<typename T>
T &DownCastAgent(cse491::Entity &entity) requires(Agent_Type<T>) {
assert(dynamic_cast<T *>(&entity)!=nullptr);
return static_cast<T &>(entity);
}

} // namespace walle
Loading

0 comments on commit 686864b

Please sign in to comment.