File ManualWorld.hpp
File List > source > Worlds > ManualWorld.hpp
Go to the documentation of this file
#pragma once
#include <cassert>
#include <stdlib.h>
#include <map>
#include <tuple>
#include "../core/WorldBase.hpp"
#include "../core/AgentBase.hpp"
namespace cse491_team8 {
class ManualWorld : public cse491::WorldBase {
protected:
enum ActionType { REMAIN_STILL=0, MOVE_UP, MOVE_DOWN, MOVE_LEFT, MOVE_RIGHT, USE_AXE, USE_BOAT, STATS, HEAL, RUN,
ATTACK, SPECIAL, BUFF, HELP };
enum FacingDirection { UP=0, RIGHT, DOWN, LEFT};
size_t grass_id;
size_t tree_id;
size_t water_id;
size_t bridge_id;
size_t rock_id;
size_t portal_id_a;
size_t portal_id_b;
size_t portal_id_c;
size_t portal_id_d;
void ConfigAgent(cse491::AgentBase & agent) override {
agent.AddAction("up", MOVE_UP);
agent.AddAction("down", MOVE_DOWN);
agent.AddAction("left", MOVE_LEFT);
agent.AddAction("right", MOVE_RIGHT);
agent.AddAction("use_axe", USE_AXE);
agent.AddAction("use_boat", USE_BOAT);
agent.AddAction("stats", STATS);
agent.AddAction("heal", HEAL);
agent.AddAction("run", RUN);
agent.AddAction("attack", ATTACK);
agent.AddAction("special", SPECIAL);
agent.AddAction("buff", BUFF);
agent.AddAction("help", HELP);
agent.SetProperties("Strength", 10, "Health", 100, "Max_Health", 150, "Direction", 0, "Battling", false);
}
public:
ManualWorld() {
grass_id = AddCellType("grass", "Grass that you can easily walk over.", ' ');
tree_id = AddCellType("tree", "Tree that you cannot pass without an axe.", '^');
water_id = AddCellType("water", "Water that cannot be crossed without a boat.", '~');
bridge_id = AddCellType("bridge", "Bridge that allows the playerto cross water.", '#');
rock_id = AddCellType("rock", "Rock that the player cannot cross", '$');
portal_id_a = AddCellType("portal_a", "Portal that teleports player to another a-portal spot.", '}');
portal_id_b = AddCellType("portal_b", "Portal that teleports player to another b-portal spot.", '{');
portal_id_c = AddCellType("portal_c", "Portal that teleports player to another c-portal spot.", '(');
portal_id_d = AddCellType("portal_d", "Portal that teleports player to another d-portal spot.", ')');
main_grid.Read("../assets/grids/team8_grid_v2.grid", type_options);
}
~ManualWorld() = default;
void GenerateMoveSets()
{
for (auto & [id, agent_ptr] : agent_map)
{
auto agent_strength = agent_ptr->GetProperty<int>("Strength");
if (agent_ptr->GetName() == "Interface")
{
std::map<std::string, std::tuple<char, double>> move_set = {
{"Attack", std::make_tuple('d', 1.0)},
{"Special", std::make_tuple('d', 1.5)}, {"Run", std::make_tuple('d', 0.0)},
{"Heal", std::make_tuple('h', 0.25)}};
// agent_ptr->SetProperty<std::map<std::string, std::tuple<char, double>>>("MoveSet", move_set);
agent_ptr->SetProperty("MoveSet", move_set);
continue;
}
std::map<std::string, std::tuple<char, double>> move_set = {{"Attack", std::make_tuple('d', 1.0)}};
if (agent_strength >= 10)
{
move_set["Special"] = std::make_tuple('d', 1.5);
}
if (agent_strength >= 15)
{
move_set["Heal"] = std::make_tuple('h', 0);
}
if (agent_strength >= 20)
{
move_set["Buff"] = std::make_tuple('s', 0.5);
}
agent_ptr->SetProperty<std::map<std::string, std::tuple<char, double>>>("MoveSet", move_set);
}
}
void AddMove(cse491::AgentBase& agent, std::string& move, char stat, double modification)
{
if (!agent.HasProperty("MoveSet")) {
agent.Notify("Error: Agent does not have a Move Set\n");
return;
}
else
{
std::map<std::string, std::tuple<char, double>> move_set = agent.GetProperty<std::map<std::string, std::tuple<char, double>>>("MoveSet");
move_set[move] = std::make_tuple(stat, modification);
agent.SetProperty<std::map<std::string, std::tuple<char, double>>>("MoveSet", move_set);
}
}
bool RemoveMove(cse491::AgentBase& agent, std::string& move)
{
if (!agent.HasProperty("MoveSet")) {
agent.Notify("Error: Agent does not have a Move Set\n");
return false;
}
else
{
std::map<std::string, std::tuple<char, double>> move_set = agent.GetProperty<std::map<std::string, std::tuple<char, double>>>("MoveSet");
move_set.erase(move);
agent.SetProperty<std::map<std::string, std::tuple<char, double>>>("MoveSet", move_set);
return true;
}
}
void HealAction(cse491::AgentBase & agent)
{
size_t can_heal = FindItem(agent, "Health Potion");
if (can_heal != SIZE_MAX) {
int healing_req = agent.GetProperty<int>("Max_Health") - agent.GetProperty<int>("Health");
int healing = item_map[can_heal]->GetProperty<int>("Healing");
if (healing_req >= healing) {
agent.Notify("You healed " + std::to_string(healing) + " health!\n");
agent.SetProperty("Health", agent.GetProperty<int>("Health") + healing);
agent.RemoveItem(can_heal);
RemoveItem(can_heal);
}
else if (healing_req == 0) {
agent.Notify("You already have max health");
} else {
agent.Notify("You healed " + std::to_string(healing_req) + " health!\n");
agent.SetProperty("Health", agent.GetProperty<int>("Health") + healing_req);
item_map[can_heal]->SetProperty("Healing", healing - healing_req);
}
} else {
agent.Notify("You do not have any health potions!\n");
}
}
void StatsAction(cse491::AgentBase & agent)
{
std::string output = "";
output += "Items owned by the player:\n";
for (const auto & [item_id, item] : item_map)
{
if (item->IsOwnedBy(agent.GetID()))
{
for (const auto & [name, entity] : item->GetPropertyMap())
{
if (name == "Uses" || name == "Strength" || name == "Healing")
{
size_t value = item->GetProperty<int>(name);
output += item->GetName() + ": " + name + ": " + std::to_string(value) + "\n";
}
}
}
}
output += "\nProperties of the player:\n";
for (const auto & [name, entity] : agent.GetPropertyMap())
{
if (name == "Strength" || name == "Health" || name == "Max_Health")
{
size_t value = agent.GetProperty<int>(name);
output += name + ": " + std::to_string(value) + "\n";
}
}
agent.Notify(output);
}
void MoveSetAction(cse491::AgentBase & agent)
{
agent.Notify("Your Moveset is:\nMove Up: W\nMove Down: S\nMove Left: A\nMove Right: D\nUse Axe: C\nUse Boat: V\nDisplay Stats: T\nHeal: H\nAttack: F\nSpecial: G\nRun: R\nBuff: B\nDisplay Moveset: Y");
}
cse491::GridPosition LookAhead(cse491::AgentBase & agent)
{
size_t direction = agent.GetProperty<int>("Direction");
cse491::GridPosition look_position;
switch (direction)
{
case (0):
look_position = agent.GetPosition().Above();
break;
case (1):
look_position = agent.GetPosition().ToRight();
break;
case (2):
look_position = agent.GetPosition().Below();
break;
case (3):
look_position = agent.GetPosition().ToLeft();
break;
default:
agent.Notify("Invalid Position: Returning Current Position");
look_position = agent.GetPosition();
break;
}
return look_position;
}
void DropItems(cse491::AgentBase & agent, cse491::AgentBase & other_agent)
{
for (const auto& [id, item] : item_map)
{
if (item->IsOwnedBy(other_agent.GetID()))
{
if (item->HasProperty("Strength") && other_agent.HasProperty("Strength"))
{
auto agent_health = other_agent.GetProperty<int>("Strength");
auto item_strength = item->GetProperty<int>("Strength");
other_agent.SetProperty<int>("Strength", (int)(agent_health - item_strength));
}
other_agent.RemoveItem(item->GetID());
item->SetPosition(other_agent.GetPosition());
agent.Notify(other_agent.GetName() + " dropped their " + item->GetName() + "!");
}
}
}
int OtherAction(cse491::AgentBase & other_agent, cse491::AgentBase & agent)
{
int other_damage = 0;
std::map<std::string, std::tuple<char, double>> move_set = other_agent.GetProperty<std::map<std::string, std::tuple<char, double>>>("MoveSet");
auto iterator = move_set.begin();
int random = GetRandom(move_set.size());
std::advance(iterator, random);
std::string random_key = iterator->first;
agent.Notify("\nThe enemy has used: " + random_key);
std::tuple<char, double> move_info = iterator->second;
char stat_char = std::get<0>(move_info);
double stat_modification = std::get<1>(move_info);
if (stat_char == 'd') {
other_damage = static_cast<int>(other_agent.GetProperty<int>("Strength") * stat_modification);
}
if (stat_char == 'h') {
HealAction(other_agent);
}
if (stat_char == 's') {
if (stat_modification < 0) {
int agent_strength = agent.GetProperty<int>("Strength");
int new_strength = static_cast<int>(agent_strength - abs(stat_modification) * agent_strength);
agent.SetProperty<int>("Strength", new_strength);
}
else {
int other_agent_strength = other_agent.GetProperty<int>("Strength");
int new_strength_other = static_cast<int>(other_agent_strength + abs(stat_modification) * other_agent_strength);
other_agent.SetProperty<int>("Strength", new_strength_other);
}
}
return other_damage;
}
void DoBattle(cse491::AgentBase & other_agent, cse491::AgentBase & agent, char attack_type) {
// User Input for Player Decision
bool won = false;
bool run = false;
int damage = 0;
switch (attack_type) {
case 'a': case 'A': damage = agent.GetProperty<int>("Strength"); break;
case 's': case 'S': damage = static_cast<int>(agent.GetProperty<int>("Strength") * 1.5); break;
case 'r': case 'R': won = false; run = true; break;
case 'b': case 'B': agent.SetProperty<int>("Strength", static_cast<int>(1.5 * agent.GetProperty<int>("Strength"))); break;
case 'h': case 'H': HealAction(agent); break;
default: break;
}
// Other Agent Move Choice, will modify strength of player agent or NPC agent based on the move selected
int other_damage = OtherAction(other_agent, agent);
// Process the Player's Move and the Agent's Move
if (run)
{
agent.SetProperty<int>("Health", agent.GetProperty<int>("Health") - other_damage);
}
else
{
other_agent.SetProperty<int>("Health", other_agent.GetProperty<int>("Health") - damage);
if (other_agent.GetProperty<int>("Health") <= 0)
{
won = true;
other_damage = 0;
}
agent.SetProperty<int>("Health", agent.GetProperty<int>("Health") - other_damage);
if (agent.GetProperty<int>("Health") <= 0)
{
won = false;
// run_over = true;
}
}
agent.Notify("Player Health: " + std::to_string(agent.GetProperty<int>("Health"))+"\n"+
"Player Strength: " + std::to_string(agent.GetProperty<int>("Strength"))+"\n"+
"Enemy Health: " + std::to_string(other_agent.GetProperty<int>("Health"))+"\n"+
"Enemy Strength: " + std::to_string(other_agent.GetProperty<int>("Strength")));
std::string other_agent_name = other_agent.GetName();
DataCollection::DataManager::GetInstance().GetAgentInteractionCollector().RecordInteraction(other_agent_name);
if (!won) {
if (run)
{
agent.Notify("You ran away, this means you don't gain health or strength and any battle damage stays!\n");
}
std::cout << agent.GetName() << ": " << agent.GetProperty<int>("Health") << std::endl;
if (agent.IsInterface() && agent.GetProperty<int>("Health") <= 0)
{
agent.Notify(other_agent_name + " has beat " + agent.GetName() + "\nYou Lost...\n");
DropItems(agent, agent);
agent.SetProperty<int>("Health", 100);
agent.SetProperty<int>("Direction", 0);
agent.SetProperty<bool>("Battling", false);
other_agent.SetProperty<bool>("Battling", false);
agent.SetPosition(80, 63);
agent.Notify("You Have died and dropped all your items");
}
}
else
{
agent.Notify(agent.GetName() + " has beat " + other_agent.GetName());
agent.SetProperty<bool>("Battling", false);
other_agent.SetProperty<bool>("Battling", false);
DropItems(agent, other_agent);
other_agent.SetProperty<bool>("deleted", true);
}
}
void UpdateWorld() override
{
}
void Run() override
{
run_over = false;
while (!run_over) {
RunAgents();
CollectData();
UpdateWorld();
}
}
void RunAgents() override {
for (auto & [id, agent_ptr] : agent_map) {
if (agent_ptr->HasProperty("deleted")) {
continue;
}
size_t action_id = agent_ptr->SelectAction(main_grid, type_options, item_map, agent_map);
agent_ptr->storeActionMap(agent_ptr->GetName());
int result = DoAction(*agent_ptr, action_id);
agent_ptr->SetActionResult(result);
}
}
void DoActionAttemptItemPickup(cse491::AgentBase & agent, const cse491::GridPosition & new_position) {
for (const auto & [id, item_ptr] : item_map) {
if (item_ptr->GetPosition() == new_position && item_ptr->GetOwnerID() == 0)
{
std::string uses_property = "";
if (item_ptr->GetName() == "Stick" || item_ptr->GetName() == "Sword") { uses_property = "Strength"; }
if (item_ptr->GetName() == "Boat" || item_ptr->GetName() == "Axe") { uses_property = "Uses"; }
if (item_ptr->GetName() == "Health Potion") { uses_property = "Healing"; }
if (uses_property == "Strength")
{
if (agent.HasProperty(uses_property))
{
agent.SetProperty(uses_property, item_ptr->GetProperty<int>(uses_property) + agent.GetProperty<int>(uses_property));
}
else
{
agent.SetProperty(uses_property, item_ptr->GetProperty<int>(uses_property));
}
}
DataCollection::DataManager::GetInstance().GetItemUseCollector().IncrementItemUsage(item_ptr->GetName());
agent.Notify("Picked up the " + item_ptr->GetName() + "!\nYou gained " +
std::to_string(item_ptr->GetProperty<int>(uses_property)) + " " +
uses_property + "!\n");
// remove it from the board
agent.AddItem(item_ptr->GetID());
break;
}
}
}
cse491::GridPosition DoActionFindNewPosition(cse491::AgentBase& agent, size_t action_id) {
// Determine where the agent is trying to move.
cse491::GridPosition new_position, look_position;
char move = ' ';
bool battling = agent.GetProperty<bool>("Battling");
// Update Direction property and get new position.
switch (action_id) {
case REMAIN_STILL:
{
new_position = agent.GetPosition();
break;
}
case MOVE_UP:
{
if (battling)
{
new_position = agent.GetPosition();
}
else
{
agent.SetProperty<int>("Direction", UP);
new_position = agent.GetPosition().Above();
}
break;
}
case MOVE_DOWN:
{
if (battling)
{
new_position = agent.GetPosition();
}
else
{
agent.SetProperty<int>("Direction", DOWN);
new_position = agent.GetPosition().Below();
}
break;
}
case MOVE_LEFT:
{
if (battling)
{
new_position = agent.GetPosition();
}
else
{
agent.SetProperty<int>("Direction", LEFT);
new_position = agent.GetPosition().ToLeft();
}
break;
}
case MOVE_RIGHT:
{
if (battling)
{
new_position = agent.GetPosition();
}
else
{
agent.SetProperty<int>("Direction", RIGHT);
new_position = agent.GetPosition().ToRight();
}
break;
}
case USE_AXE:
{
new_position = agent.GetPosition();
look_position = LookAhead(agent);
if (main_grid.IsValid(look_position) && main_grid.At(look_position) == tree_id)
{
DoActionTestNewPositionTree(agent, look_position);
} else {
agent.Notify("You can not use an Axe here!");
}
break;
}
case USE_BOAT:
{
new_position = agent.GetPosition();
if (main_grid.At(new_position) == water_id)
{
agent.Notify("Already on Water");
break;
}
look_position = LookAhead(agent);
if (main_grid.IsValid(look_position) && main_grid.At(look_position) == water_id)
{
if (DoActionTestNewPositionWater(agent))
{
new_position = look_position;
}
} else {
agent.Notify("You can not use a Boat here!");
}
break;
}
case STATS:
{
new_position = agent.GetPosition();
StatsAction(agent);
break;
}
case HEAL:
{
new_position = agent.GetPosition();
if (battling)
{
move = 'h';
}
else
{
HealAction(agent);
}
break;
}
case RUN:
{
if (!battling)
{
new_position = agent.GetPosition();
break;
}
new_position = agent.GetPosition();
agent.SetProperty<bool>("Battling", false);
auto agents = FindAgentsNear(agent.GetPosition(), 1);
for (auto agent_id : agents)
{
if (!agent_map[agent_id]->IsInterface() && !agent_map[agent_id]->HasProperty("deleted"))
{
agent.Notify("You are running away");
agent_map[agent_id]->SetProperty<bool>("Battling", false);
DoBattle(*agent_map[agent_id], agent, 'r');
}
}
break;
}
case ATTACK:
{
move = 'a';
break;
}
case SPECIAL:
{
move = 's';
break;
}
case BUFF:
{
move = 'b';
break;
}
case HELP:
{
new_position = agent.GetPosition();
MoveSetAction(agent);
break;
}
}
if (move != ' ')
{
if (agent.GetProperty<bool>("Battling") == false)
{
agent.Notify("You are in a battle! Use Y and choose battling moves!");
}
auto agents = FindAgentsNear(agent.GetPosition(), 1);
for (auto agent_id : agents)
{
// Battle other agent near the player
if (!agent_map[agent_id]->IsInterface() && !agent_map[agent_id]->HasProperty("deleted"))
{
agent.SetProperty<bool>("Battling", true);
agent_map[agent_id]->SetProperty<bool>("Battling", true);
DoBattle(*agent_map[agent_id], agent, move);
break;
}
}
new_position = agent.GetPosition();
}
// assume new position is valid
return new_position;
}
size_t FindItem(cse491::AgentBase & agent, const std::string & item_name) {
size_t item_id = SIZE_MAX;
for (auto & item : item_map)
{
if (item.second->GetName() == item_name && item.second->IsOwnedBy(agent.GetID()))
{
item_id = item.second->GetID();
break;
}
}
return item_id;
}
void DoActionTestNewPositionTree(cse491::AgentBase & agent, const cse491::GridPosition & new_position) {
size_t item_id = FindItem(agent, "Axe");
if (item_id != SIZE_MAX)
{
agent.Notify("You have used your Axe to chop down this tree. You have " +
std::to_string(item_map[item_id]->GetProperty<int>("Uses") - 1) + " uses remaining");
// decrement uses by 1, change the tree to grass
item_map[item_id]->SetProperty("Uses", item_map[item_id]->GetProperty<int>("Uses") - 1);
if (item_map[item_id]->GetProperty<int>("Uses") == 0)
{
agent.RemoveItem(item_id);
RemoveItem(item_id);
}
main_grid[new_position] = grass_id;
}
}
bool DoActionTestNewPositionWater(cse491::AgentBase& agent) {
size_t item_id = FindItem(agent, "Boat");
if (item_id != SIZE_MAX)
{
agent.Notify("You have used your Boat to float on the water. You have " +
std::to_string(item_map[item_id]->GetProperty<int>("Uses") - 1) + " uses remaining");
// decrement uses by 1
item_map[item_id]->SetProperty("Uses", item_map[item_id]->GetProperty<int>("Uses") - 1);
if (item_map[item_id]->GetProperty<int>("Uses") == 0)
{
agent.RemoveItem(item_id);
RemoveItem(item_id);
}
return true;
}
return false;
}
int DoAction(cse491::AgentBase & agent, size_t action_id) override {
cse491::GridPosition new_position = DoActionFindNewPosition(agent, action_id);
// Don't let the agent move off the world or into a wall.
if (!main_grid.IsValid(new_position)) { return false; }
DoActionAttemptItemPickup(agent, new_position);
if (main_grid.At(new_position) == tree_id)
{
return false;
}
if (main_grid.At(new_position) == water_id)
{
if (agent.HasProperty("OnlyWater"))
{
agent.SetPosition(new_position);
return true;
}
if (action_id != USE_BOAT && main_grid.At(agent.GetPosition()) != water_id) { return false; }
}
if (main_grid.At(new_position) == grass_id)
{
if (agent.HasProperty("OnlyWater")) { return false; }
}
if (main_grid.At(new_position) == rock_id)
{
return false;
}
if (main_grid.At(new_position) == portal_id_a)
{
// which portal should we go to - for medium grid
if (new_position.GetX() == 33)
new_position = cse491::GridPosition(3, 1);
else if (new_position.GetX() == 3)
new_position = cse491::GridPosition(33, 0);
// for large grid, big right sand patch (x=137) to circle lake island (x=41)
else if (new_position.GetX() == 137)
new_position = cse491::GridPosition(41, 98);
else if (new_position.GetX() == 41)
new_position = cse491::GridPosition(137, 40);
}
if (main_grid.At(new_position) == portal_id_b)
{
// for medium grid
if (new_position.GetX() == 44)
new_position = cse491::GridPosition(2, 10);
else if (new_position.GetX() == 2)
new_position = cse491::GridPosition(44, 17);
// for large grid, tree-locked upper left sand patch(x=32) to lower right along water (x=114)
else if (new_position.GetX() == 32)
new_position = cse491::GridPosition(114, 132);
else if (new_position.GetX() == 114)
new_position = cse491::GridPosition(32, 36);
}
if (main_grid.At(new_position) == portal_id_c)
{
// above top right lake (x=120) to left of circle lake (x=16)
if (new_position.GetX() == 120)
new_position = cse491::GridPosition(16, 140);
else if (new_position.GetX() == 16)
new_position = cse491::GridPosition(120, 25);
}
if (main_grid.At(new_position) == portal_id_d)
{
// from circle lake island (x=36) to very bottom right corner (x=146)
if (new_position.GetX() == 36)
new_position = cse491::GridPosition(146, 147);
else if (new_position.GetX() == 146)
new_position = cse491::GridPosition(36, 94);
}
// Set the agent to its new postion.
agent.SetPosition(new_position);
return true;
}
[[nodiscard]] bool IsTraversable(const cse491::AgentBase & /*agent*/, cse491::GridPosition pos) const override {
return main_grid.At(pos) == grass_id;
}
};
} // End of namespace cse491_team8