diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..d057157c Binary files /dev/null and b/.DS_Store differ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/cse_491_fall_2023.iml b/.idea/cse_491_fall_2023.iml new file mode 100644 index 00000000..d0876a78 --- /dev/null +++ b/.idea/cse_491_fall_2023.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..5cb71ef0 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..d56657ad --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..663c3710 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..319ce6b0 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,20 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "macFrameworkPath": [ + "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks" + ], + "compilerPath": "/usr/bin/clang", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "macos-clang-x86", + "configurationProvider": "ms-vscode.cpptools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..ef199922 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,64 @@ +{ + "C_Cpp.errorSquiggles": "enabled", + "files.associations": { + "__bit_reference": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__errc": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__node_handle": "cpp", + "__split_buffer": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__verbose_abort": "cpp", + "array": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "complex": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "exception": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "locale": "cpp", + "map": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "queue": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "variant": "cpp", + "vector": "cpp", + "algorithm": "cpp" + } +} \ No newline at end of file diff --git a/docs/test.txt b/docs/test.txt new file mode 100644 index 00000000..e69de29b diff --git a/project_specs/team-1-specs.md b/project_specs/team-1-specs.md index 29e5d500..90c1bf52 100644 --- a/project_specs/team-1-specs.md +++ b/project_specs/team-1-specs.md @@ -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 diff --git a/source/Agents/AgentLibary.h b/source/Agents/AgentLibary.h new file mode 100644 index 00000000..7e7046cf --- /dev/null +++ b/source/Agents/AgentLibary.h @@ -0,0 +1,51 @@ +// +// Created by Matthew Kight on 9/24/23. +// +#pragma once + +#include "../core/AgentBase.hpp" +#include "../core/WorldBase.hpp" +#include +#include +#include +#include +namespace cse491 +{ + namespace walle + { + + /** + * @brief Node class to make A* search easier + * + */ + struct Node + { + cse491::GridPosition position; // Where node is located + int g; // Cost from start to current node + int h; // Heuristic (estimated cost from current node to goal) + std::shared_ptr parent; + + Node(cse491::GridPosition position, double g, double h, std::shared_ptr parent) + : position(position), g(g), h(h), parent(parent) {} + + // Calculate the total cost (f) of the node + int f() const + { + return g + h; + } + }; + + /** + * @brief Custom comparison function for priority queue + * + */ + struct CompareNodes + { + bool operator()(const std::shared_ptr a, const std::shared_ptr b) const + { + return a->f() > b->f(); + } + }; + + } +} diff --git a/source/Agents/AgentMonika.hpp b/source/Agents/AgentMonika.hpp new file mode 100644 index 00000000..8ead81e7 --- /dev/null +++ b/source/Agents/AgentMonika.hpp @@ -0,0 +1,32 @@ +/** + * This file is part of the Fall 2023, CSE 491 course project - Group 1 fork - monika_k branch. + * @brief An Agent that will walk. + * @note Status: PROPOSAL + **/ + +#pragma once + +#include "../core/AgentBase.hpp" + +namespace cse491 { + class BasicAgent : public AgentBase { + protected: + std::unordered_map a_star_map; // Map of the tiles on the board with their corresponding values for a* search + bool isMoving = false; // Is the agent moving? + int intelligenceLevel = 0; // Depending on the type of agent, mobility varies + // Measure of intelligence; if agent is an enemy, are they "smart enough" to be able to use a*, or would they resort to + // another search method? If the agent is the user/player's agent they would have maximum intelligence. + int healthPoints = 10; + int mobility = 20; // Number of tiles the agent can move in a given turn? + int level = 1; // Level of character; scales the rest of their attributes. + + public: + BasicAgent(size_t id, const std::string & name) : AgentBase(id, name) { } + ~BasicAgent() = default; + + // a_star function here + + // could also add getters/setters here for different attributes + + }; +} \ No newline at end of file diff --git a/source/Agents/matt_k_first_agent.h b/source/Agents/matt_k_first_agent.h new file mode 100644 index 00000000..9d5995ec --- /dev/null +++ b/source/Agents/matt_k_first_agent.h @@ -0,0 +1,83 @@ +/** + * @file matt_k_first_agent.h + * @author Matt Kight + */ +#pragma once + +#include +#include "../core/AgentBase.hpp" +#include "AgentLibary.h" +#include +namespace cse491 +{ + namespace walle + { + /** + * Class that describes a matt_k_first_agent class + */ + class matt_k_first_agent : public cse491::AgentBase + { + private: + std::vector 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 + WorldBase *world = nullptr; + + public: + matt_k_first_agent(size_t id, const std::string &name) : AgentBase(id, name) + { + } + ~matt_k_first_agent() = default; + + /** + * @brief Set the word object + * + * @param world world this agent is a part of + */ + void set_word(WorldBase *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"); + } + + void set_goal_position(const GridPosition gp) + { + goal_position = gp; + } + /// Choose the action to take a step in the appropriate direction. + size_t SelectAction(const WorldGrid & /*grid*/, + const type_options_t & /* type_options*/, + const item_set_t & /* item_set*/, + const agent_set_t & /* agent_set*/) override + { + // TODO REMOVE THIS + // We are taking an action so another turn has passed + ++(this->current_move_num); + // If the last step failed, or we need a new path the then regenerate the path + if (action_result == 0 || this->path.empty() || this->current_move_num > this->recalculate_after_x_turns) + { + this->path = this->world->shortest_path(GetPosition(), this->goal_position); + } + // Return whatever action gets us closer to our goal + if (!this->path.empty()) + { + auto pos = path.back(); + path.pop_back(); + if (pos.GetX() == position.GetX() && pos.GetY() == position.GetY() - 1) + return action_map["up"]; + if (pos.GetX() == position.GetX() && pos.GetY() == position.GetY() + 1) + return action_map["down"]; + if (pos.GetX() == position.GetX() - 1 && pos.GetY() == position.GetY()) + return action_map["left"]; + if (pos.GetX() == position.GetX() + 1 && pos.GetY() == position.GetY()) + return action_map["right"]; + } + return 0; // If no path then do not do anything + } + }; + }; +} diff --git a/source/Worlds/MazeWorld.hpp b/source/Worlds/MazeWorld.hpp index 23fab2e9..2c4e4a55 100644 --- a/source/Worlds/MazeWorld.hpp +++ b/source/Worlds/MazeWorld.hpp @@ -53,10 +53,11 @@ namespace cse491 { // Set the agent to its new postion. agent.SetPosition(new_position); - return true; } + /// Can walk on all tiles except for walls + bool is_walkable (cse491::GridPosition pos) override {return main_grid.At(pos) != wall_id;} }; } // End of namespace cse491 diff --git a/source/core/WorldBase.hpp b/source/core/WorldBase.hpp index 6a57d289..bbd2a773 100644 --- a/source/core/WorldBase.hpp +++ b/source/core/WorldBase.hpp @@ -160,6 +160,99 @@ namespace cse491 { if (id >= type_options.size()) return type_options[0].symbol; return type_options[id].symbol; } + + /// @brief Determine if this tile is able to be walked on, defaults to every tile is walkable + /// @param pos The grid position we are checking + /// @return If an agent should be allowed on this square + virtual bool is_walkable (cse491::GridPosition /*pos*/) {return true;} + + /// @brief Uses A* to return a list of grid positions + /// @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 shortest_path(cse491::GridPosition start, cse491::GridPosition end); + + /// @brief Determine if this tile is able to be walked on, defaults to every + /// tile is walkable + /// @author @mdkdoc15 + /// @param pos The grid position we are checking + /// @return If an agent should be allowed on this square + virtual bool is_walkable(GridPosition /*pos*/) { return true; } + + /// @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 shortest_path(GridPosition start, GridPosition end) + { + // TODO remove the use of new and this + + // Generated with the help of chat.openai.com + const int rows = this->main_grid.GetWidth(); + const int cols = this->main_grid.GetHeight(); + std::vector path; + // If the start or end is not valid then return empty list + if (!(this->main_grid.IsValid(start) && this->main_grid.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> cost(rows, std::vector(cols, INT_MAX)); + + // Create an open list as a priority queue + std::priority_queue, std::vector>, walle::CompareNodes> openList; + + // Create the start and end nodes + auto startNode = std::make_shared(start, 0, 0, nullptr); + auto endNode = std::make_shared(end, 0, 0, nullptr); + + openList.push(startNode); + cost[start.GetX()][start.GetY()] = 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) + { + 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 (this->main_grid.IsValid(newPos) && this->is_walkable(newPos)) + { + int newG = current->g + 1; // Assuming a cost of 1 to move to a neighbor + int newH = std::abs(newPos.GetX() - endNode->position.GetX()) + std::abs(newPos.GetY() - endNode->position.GetY()); // Manhattan distance + + if (newG + newH < cost[newPos.GetX()][newPos.GetY()]) + { + auto neighbor = std::make_shared(newPos, newG, newH, current); + openList.push(neighbor); + cost[newPos.GetX()][newPos.GetY()] = newG + newH; + } + } + } + } + + return path; + } }; } // End of namespace cse491 diff --git a/source/simple b/source/simple new file mode 100755 index 00000000..639e846a Binary files /dev/null and b/source/simple differ