Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Group 1 Wed Sept 27th checkin #21

Merged
merged 50 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
f2f407f
dummy commit
mdkdoc15 Sep 21, 2023
0e00916
Updated specs to add challenges
mdkdoc15 Sep 25, 2023
87d3539
Monika's version of the agent class - 9.24.23:
Sep 25, 2023
235171d
Monika's version of the agent class - 9.24.23:
Sep 25, 2023
7d2570a
Merge branch 'MSU-CSE491:main' into main
mdkdoc15 Sep 26, 2023
98bc7b3
Merge branch 'MSU-CSE491:main' into main
mdkdoc15 Sep 26, 2023
558a0cd
Added A* agent
mdkdoc15 Sep 27, 2023
261975a
Merge branch 'main' into matt_k
mdkdoc15 Sep 27, 2023
97e0a8e
Creation of a PathAgent with ability to move in custom patterns
rikipls Sep 27, 2023
04eb1b4
Merge branch 'MSU-CSE491:main' into david_r
rikipls Sep 27, 2023
3df0249
Merge branch 'MSU-CSE491:main' into monika_k
Kanakmk Sep 27, 2023
60dd1e1
Add property modifiers for path agent, empty test file
rikipls Sep 27, 2023
3760e41
Create Group 1 - README
Kanakmk Sep 27, 2023
4669cd7
Rename Group 1 - README to Group 1 - README.md
Kanakmk Sep 27, 2023
23c4c85
Make PathAgent actually compile ;-;
rikipls Sep 27, 2023
cfaa581
Added A* agent
mdkdoc15 Sep 27, 2023
0734e46
Removed personal config files
mdkdoc15 Sep 27, 2023
20d0e06
Removed more user config files
mdkdoc15 Sep 27, 2023
ae73dca
Cleaned up comments on WorldBase.hpp
mdkdoc15 Sep 27, 2023
4f7eaef
Included imports to work with A*
mdkdoc15 Sep 27, 2023
a1f2599
Update Group 1 - README.md
Kanakmk Sep 28, 2023
4d11f5d
Delete source/Agents/AgentMonika.hpp
Kanakmk Sep 28, 2023
3383e42
Merge pull request #2 from mdkdoc15/monika_k
mdkdoc15 Sep 28, 2023
5afd5ae
Removed Monika's agent
mdkdoc15 Sep 28, 2023
767a204
refactor naming convention to be consistant with style
mdkdoc15 Oct 2, 2023
af4c9b1
Merge pull request #1 from mdkdoc15/matt_k
mdkdoc15 Oct 2, 2023
8f96d64
Merge branch 'MSU-CSE491:main' into david_r
rikipls Oct 2, 2023
feba0cd
Fixed seg fault when attempting to make path with no world set
mdkdoc15 Oct 3, 2023
dc45d21
Merge branch 'MSU-CSE491:main' into david_r
rikipls Oct 4, 2023
c17413e
Merge branch 'MSU-CSE491:main' into david_r
rikipls Oct 9, 2023
ce3df4a
Add support for testing using CMake and CLion Catch2 configurations
rikipls Oct 10, 2023
c4e0352
Change CMake files to take absolute and relative paths that are indep…
rikipls Oct 10, 2023
aa8ee52
CMake try not to explode challenge (possible!?)
rikipls Oct 10, 2023
f6c8581
PR #21 fixes
mdkdoc15 Oct 10, 2023
ae8bc66
Testcase to throw exception upon passing empty offsets
rikipls Oct 10, 2023
331ddfb
Remove Monika agent
rikipls Oct 10, 2023
31fc99f
Merge pull request #4 from mdkdoc15/matt_k
mdkdoc15 Oct 10, 2023
7d4f78f
Rename PathAgent helper function; refactor CMakeLists.txt in tests to…
rikipls Oct 10, 2023
6d35c45
Decouple agent movement from updating the agent itself; add much more…
rikipls Oct 10, 2023
d9cf8de
Fix minor error in StrToOffsets documentation example
rikipls Oct 10, 2023
6210f96
Merge branch 'main' into matt_k
mdkdoc15 Oct 11, 2023
a11d191
Fixed small error for PR 21
mdkdoc15 Oct 11, 2023
2197f98
Merge pull request #5 from mdkdoc15/matt_k
mdkdoc15 Oct 11, 2023
fe028a7
Merge pull request #3 from mdkdoc15/david_r
mdkdoc15 Oct 11, 2023
2375031
Merge branch 'MSU-CSE491:main' into matt_k
mdkdoc15 Oct 11, 2023
ee5f5d0
Merge branch 'main' into matt_k
mdkdoc15 Oct 11, 2023
a67210d
Removed duplicate file
mdkdoc15 Oct 11, 2023
cce96b7
Refactor A* search out of WorldBase -> AgentLib
mdkdoc15 Oct 12, 2023
8c51802
Added simple_main back after accidental deletion
mdkdoc15 Oct 12, 2023
6db0e90
Merge pull request #6 from mdkdoc15/matt_k
mdkdoc15 Oct 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions docs/Group 1 - README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Group 1: Agent Library Function Descriptions

Below is a set of classes and functions designed to create multiple different Agents with various functionalities.

<details>

<summary> Path Agent </summary>
Agent that has a user-defined custom movement pattern passed a sequence to be sequentially applied as the agent is updated

- Constructor with ```std::vector```
- param ```id```: unique agent id
- param ```name```: name of path agent
- param ```offsets```: collection of offsets to move the agent
```PathAgent(size_t id, std::string const& name, std::vector<cse491::GridPosition> && offsets = {}) : cse491::AgentBase(id, name), offsets_(offsets);```

- Constructor with ```std::string```
- param ```id```: unique agent id
- param ```name```: name of path agent
- param ```commands```: sequence of commands to be interpreted as offsets
```PathAgent::PathAgent(size_t id, std::string const& name, std::string_view commands) : cse491::AgentBase(id, name), offsets_(str_to_offsets(commands))```

- Initializer
- Checks that the agent is able to freely change its own grid location. Verifies that it can currently index into a valid offset
- return: ```true``` if so; ```false``` otherwise.
```bool PathAgent::Initialize();```

- Select Action
- Moves the agent by applying the current offset
- param ```WorldGrid```: reference grid
- param ```type_options_t```: type options
- param ```item_set_t```: set of items
- param ```agent_set_t```: agent set
```size_t PathAgent::SelectAction(const WorldGrid & /* grid*/, const type_options_t & /* type_options*/, const item_set_t & /* item_set*/, const agent_set_t & /* agent_set*/)```


- Set Properties with ```std::vector```
- Assigns the offsets_member to a new series of offsets
- param ```offsets```: collection of grid positions used as the new offsets
- param ```start_index = 0```: which offset to start indexing into (beginning by default)
- return: ```self```
- **Attention:** throws an `std::invalid_argument` when an invalid start index is provided
```PathAgent& PathAgent::SetProperties(std::vector<cse491::GridPosition> && offsets, size_t start_index = 0)```

- Set properties with ```std::string```
- Assigns the offsets_ member to a new series of offsets, taking a command string
- param ```commands```: formatted string of commands used as offsets
- param ```start_index = 0```: which command to begin indexing into (first command by default)
- return: ```self```
- **Attention:** throws an `std::invalid_argument` when mis-formatted commands an invalid index is provided
```PathAgent& PathAgent::SetProperties(std::string_view commands, size_t start_index = 0)```

- Helper function for a formatted ```std::string``` input
- Converts a string to a sequence of offsets
- This convenience method takes a string with a special formatting that allows one to specify a sequence of inputs in linear directions.
- The format is [steps[*]]<direction> where `steps` is a positive integer and optional (assumed to be 1 by default)
- star `*` represents scaling the movement by `steps`. Optional, but cannot be used if `steps` is not provided
- if the star is not present, then `steps` individual offsets are created in the direction `direction`
- Example: "n w 3e 10*s 5*w x" should create the sequence of offsets
- `{-1, 0}, {0, 1}, {0, -1}, {0, -1}, {0, -1}, {10, 0}, {0, 5}, {0, 0}`
- param `commands`: string in a format of sequential directions
- **note:** throws an `std::invalid_argument` when input string is poorly formatted
- **note:** this includes when a negative integer is passed as `steps`. If a zero is used, treated as the default (one)

```static std::vector<cse491::GridPosition> str_to_offsets(std::string_view commands)```

</details>

More agents to be documented...
5 changes: 5 additions & 0 deletions project_specs/team-1-specs.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,8 @@ modules.
ii. Params and returns will be added after discussion with other groups to
see if this is wanted


#### Challenge
1. Creating an Autonomous agent that is capable of moving around an interacting
with the world
2. Working with other agent teams to implement functionality they need for their agents
1 change: 1 addition & 0 deletions source/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.swp
*.swo
game
simple
106 changes: 106 additions & 0 deletions source/Agents/AStarAgent.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* @file AStarAgent.h
* @author Matt Kight
*/
#pragma once

#include <vector>
#include "../core/AgentBase.hpp"
#include "AgentLibary.hpp"

namespace walle {
/**
* Class that describes a AStarAgent class
*/
class AStarAgent : public cse491::AgentBase {
private:
std::vector<cse491::GridPosition> path; ///< Path this agent is taking
cse491::GridPosition goal_position; ///< Where the agent wants to end up
int recalculate_after_x_turns = 100; ///< How often agent recalculates moves
int current_move_num = 0; ///< What move # we are currently on
const cse491::WorldBase *world = nullptr; ///< What world this agent is a part of

public:
AStarAgent(size_t id, const std::string &name) : AgentBase(id, name) {
}

~AStarAgent() = default;

/**
* @brief Set the word object
*
* @param world world this agent is a part of
*/
void SetWorld(const cse491::WorldBase *const _world) { this->world = _world; }

/// @brief This agent needs a specific set of actions to function.
/// @return Success.
bool Initialize() override {
return HasAction("up") && HasAction("down") && HasAction("left") && HasAction("right");
}

/**
* @brief Set where the agent should head towards
* @param x x-coordinate of the goal position
* @param y y-coordinate of the goal position
*/
void SetGoalPosition(const double x, const double y) {
goal_position = cse491::GridPosition(x, y);
}

/**
* @brief Set where the agent should head towards
* @param gp position agent should go towards
*/
void SetGoalPosition(const cse491::GridPosition gp) {
goal_position = gp;
}
/**
* @brief Set how many moves should occur before recalculating path
* A lower number will react faster to updates in the world but will call A* search more often
* @param recalculate How often path should be recalculated
*/
void SetRecalculate(const int recalculate) {
recalculate_after_x_turns = recalculate;
}

/**
* @brief Update the path to go to goal position
*/
void RecalculatePath() {
path = GetShortestPath(GetPosition(), goal_position, world, this);
current_move_num = 0;
}
/// Choose the action to take a step in the appropriate direction.
size_t SelectAction(const cse491::WorldGrid & /*grid*/,
const cse491::type_options_t & /* type_options*/,
const cse491::item_set_t & /* item_set*/,
const cse491::agent_set_t & /* agent_set*/) override {
if (world == nullptr) {
// Do nothing if the agent doesn't know about its world
return 0;
}
// We are taking an action so another turn has passed
++current_move_num;
// If the last step failed, or we need a new path the then regenerate the path
if (action_result == 0 || path.empty() || current_move_num > recalculate_after_x_turns) {
RecalculatePath();
}
// Return whatever action gets us closer to our goal
if (!path.empty()) {
auto pos = path.back();
path.pop_back();
if (pos == position.Above())
return action_map["up"];
if (pos == position.Below())
return action_map["down"];
if (pos == position.ToLeft())
return action_map["left"];
if (pos == position.ToRight())
return action_map["right"];
}
return 0; // If no path then do not do anything
}
};
};

124 changes: 124 additions & 0 deletions source/Agents/AgentLibary.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//
// Created by Matthew Kight on 9/24/23.
//
#pragma once

#include "../core/AgentBase.hpp"
#include "../core/WorldBase.hpp"
#include <vector>
#include <map>
#include <queue>
#include <tuple>

namespace walle {

/**
* @brief Node class to hold information about positions for A* search
*/
struct Node {
cse491::GridPosition position; ///< Where node is located
double g; ///< Cost from start to current node
double h; ///< Heuristic (estimated cost from current node to goal)
std::shared_ptr<Node> parent; ///< How we got to this node (Used to construct final path)

/**
* Constructor for a node
* @param position location on grid of this node
* @param g actual distance to get from start to this node
* @param h heuristic guess for distance from this node to end location
* @param parent Used to construct path back at end
*/
Node(const cse491::GridPosition &position, double g, double h, std::shared_ptr<Node> parent)
: position(position), g(g), h(h), parent(std::move(parent)) {}

/**
* @brief Calculate the total cost (f) of the node
* @return sum of actual distance and heuristic distance
*/
[[nodiscard]] double f() const {
return g + h;
}
};

/**
* @brief Custom comparison function for priority queue
* @return if a has a greater total cost than b
*/
struct CompareNodes {
bool operator()(const std::shared_ptr<walle::Node> &a, const std::shared_ptr<walle::Node> &b) const {
return a->f() > b->f();
}
};

/// @brief Uses A* to return a list of grid positions
/// @author @mdkdoc15
/// @param start Starting position for search
/// @param end Ending position for the search
/// @return vector of A* path from start to end, empty vector if no path
/// exist
std::vector<cse491::GridPosition> GetShortestPath(const cse491::GridPosition &start,
const cse491::GridPosition &end,
const cse491::WorldBase *world,
const cse491::AgentBase *agent) {
// Generated with the help of chat.openai.com
const size_t rows = world->GetGrid().GetWidth();
const size_t cols = world->GetGrid().GetHeight();
std::vector<cse491::GridPosition> path;
// If the start or end is not valid then return empty list
if (!(world->GetGrid().IsValid(start) && world->GetGrid().IsValid(end)))
return path;

// Define possible movements (up, down, left, right)
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};

// Create a 2D vector to store the cost to reach each cell
std::vector<std::vector<double>> cost(rows, std::vector<double>(cols, INT_MAX));

// Create an open list as a priority queue
std::priority_queue<std::shared_ptr<walle::Node>, std::vector<std::shared_ptr<walle::Node>>, walle::CompareNodes>
openList;

// Create the start and end nodes
auto startNode = std::make_shared<walle::Node>(start, 0, 0, nullptr);
auto endNode = std::make_shared<walle::Node>(end, 0, 0, nullptr);

openList.push(startNode);
cost[start.CellX()][start.CellY()] = 0;

while (!openList.empty()) {
auto current = openList.top();
openList.pop();

if (current->position == endNode->position) {

// Reached the goal, reconstruct the path
while (current != nullptr) {
path.push_back(current->position);
current = current->parent;
}
break;
}

// Explore the neighbors
for (int i = 0; i < 4; ++i) {
cse491::GridPosition newPos(current->position.GetX() + dx[i], current->position.GetY() + dy[i]);
// Check if the neighbor is within bounds and is a valid move
if (world->GetGrid().IsValid(newPos) && world->IsTraversable(agent, newPos)) {
double newG = current->g + 1; // Assuming a cost of 1 to move to a neighbor
double newH = std::abs(newPos.GetX() - endNode->position.GetX()) +
std::abs(newPos.GetY() - endNode->position.GetY()); // Manhattan distance

if (newG + newH < cost[newPos.CellX()][newPos.CellY()]) {
auto neighbor = std::make_shared<walle::Node>(newPos, newG, newH, current);
openList.push(neighbor);
cost[newPos.CellX()][newPos.CellY()] = newG + newH;
}
}
}
}

return path;
}

}
Loading