Browse Source

Added Position, HTN Refactoring

Added a simple Position class which the world will eventually use. I also did some small refactoring here and there in HTNs and updated tests.
combatComponentRefactor
Macoy Madson 6 years ago
parent
commit
43f2d624cd
  1. 30
      Jamrules
  2. 1
      src/Jamfile
  3. 18
      src/ai/htn/HTNPlanner.cpp
  4. 26
      src/ai/htn/HTNTasks.cpp
  5. 35
      src/ai/htn/HTNTasks.hpp
  6. 3
      src/ai/htn/HTNTypes.hpp
  7. 26
      src/unitTesting/HTN_test.cpp
  8. 5
      src/unitTesting/Jamfile
  9. 51
      src/unitTesting/Position_test.cpp
  10. 5
      src/world/Jamfile
  11. 101
      src/world/Position.cpp
  12. 38
      src/world/Position.hpp

30
Jamrules

@ -1,17 +1,33 @@
##
## Compiler
##
CC = gcc ;
# GCC
#C++ = g++ ;
#LINK = g++ ;
# Clang
C++ = clang++ ;
LINK = clang++ ;
##
## Compiler arguments
##
# Galavant requires C++11
# fPIC = position independent code. This is so the lib can be linked in other libs/executables
# g = debug symbols
# lstdc++ = standard library
# -stdlib=lib++ = Unreal uses fucking libc++, which is busted on Ubuntu 16.04. Remove this option if you don't
# give a shit about Unreal and/or want to build the tests
# Og = compile without optimizations for debugging
C++FLAGS = -std=c++11 -stdlib=libc++ -fPIC -g -Og -Wall -Wextra ;
# fPIC = position independent code. This is so the lib can be linked in other libs/executables
# g = debug symbols
# lstdc++ = standard library
# -stdlib=lib++ = Unreal uses libc++, which is busted on Ubuntu 16.04. Remove this option
# if you don't give a shit about Unreal and/or want to build the tests
# Og = compile without optimizations for debugging
# -Wall -Wextra = Error detection/tolerance
#C++FLAGS = -std=c++11 -stdlib=libc++ -fPIC -g -Og -Wall -Wextra ; # Required arguments for Unreal
C++FLAGS = -std=c++11 -fPIC -g -Og -Wall -Wextra ; # Required arguments for tests
OBJECTC++FLAGS = -std=c++11 -lstdc++ -g -Og ;
HDRS = thirdParty/flatbuffers/include ;

1
src/Jamfile

@ -9,6 +9,7 @@ MakeLocate libGalavant.a : lib ;
SubInclude . src thirdPartyWrapper ;
SubInclude . src entityComponentSystem ;
SubInclude . src ai ;
SubInclude . src world ;
# Experiments and Testing (feel free to remove these if you don't want them built)
SubInclude . src experiments ;

18
src/ai/htn/HTNPlanner.cpp

@ -14,9 +14,6 @@ bool DecomposeGoalTask(GoalDecompositionStack& decompositionStack, GoalTask* goa
// GoalTask* goalTask = currentTask->GetGoal();
decomposition.DecomposedGoalTask = goalTask;
decomposition.MethodIndex = methodIndex;
// How the hell are parameters off of goals used? Fuck
// Well, a method could be restricted to a single Primitive Task or a
// CompoundTask
decomposition.Parameters = parameters;
decomposition.InitialState = state;
@ -40,6 +37,9 @@ bool DecomposeCompoundTask(TaskCallList& compoundDecompositions, CompoundTask* c
return compoundTask->Decompose(compoundDecompositions, taskArguments);
}
// TODO: Pool various task lists?
// TODO: Pull more things out into functions, if possible. It's bad that whenever I make a change to
// something I have to change it in two places
Planner::Status Planner::PlanStep(void)
{
// When the stack is empty, find a goal task to push onto the task or add tasks as per usual
@ -75,8 +75,10 @@ Planner::Status Planner::PlanStep(void)
if (!currentTask)
continue;
TaskType currentTaskType = currentTask->GetType();
// NullEntity = TODO
TaskArguments taskArguments = {0, StacklessState, currentTaskCall.Parameters};
// Prepare arguments based on our current working state & parameters
// TODO: I don't like this
TaskArguments taskArguments = {StacklessState, currentTaskCall.Parameters};
if (DebugPrint)
std::cout << "TaskType currentTaskType = " << (int)currentTaskType << "\n";
@ -211,8 +213,10 @@ Planner::Status Planner::PlanStep(void)
if (!currentTask)
continue;
TaskType currentTaskType = currentTask->GetType();
// NullEntity = TODO
TaskArguments taskArguments = {0, currentStackFrame.WorkingState,
// Prepare arguments based on our current working state & parameters
// TODO: I don't like this
TaskArguments taskArguments = {currentStackFrame.WorkingState,
currentTaskCall.Parameters};
if (DebugPrint)

26
src/ai/htn/HTNTasks.cpp

@ -5,16 +5,6 @@
namespace Htn
{
/*Task::Task(void)
{
Type = TaskType::None;
}
TaskType Task::GetType(void)
{
return Type;
}*/
int GoalTask::GetNumMethods(void)
{
return Methods ? Methods->size() : 0;
@ -45,22 +35,6 @@ void GoalTask::SetMethods(TaskList* newMethods)
Methods = newMethods;
}
CompoundTask::CompoundTask(void)
{
}
PrimitiveTask::PrimitiveTask(void)
{
}
CompoundTask::~CompoundTask(void)
{
}
PrimitiveTask::~PrimitiveTask(void)
{
}
Task::Task(GoalTask* goal)
{
Goal = goal;

35
src/ai/htn/HTNTasks.hpp

@ -19,10 +19,10 @@ typedef std::vector<TaskCall> TaskCallList;
typedef TaskCallList::iterator TaskCallListIterator;
//
// Different types of tasks
// Tasks
//
class GoalTask //: public Task
class GoalTask
{
private:
TaskList* Methods;
@ -38,20 +38,20 @@ public:
void SetMethods(TaskList* newMethods);
};
class CompoundTask //: public Task
class CompoundTask
{
public:
CompoundTask(void);
virtual ~CompoundTask(void);
CompoundTask(void) = default;
virtual ~CompoundTask(void) = default;
virtual bool StateMeetsPreconditions(const TaskArguments& arguments) = 0;
virtual bool Decompose(TaskCallList& taskCallList, const TaskArguments& arguments) = 0;
};
class PrimitiveTask //: public Task
class PrimitiveTask
{
public:
PrimitiveTask(void);
virtual ~PrimitiveTask(void);
PrimitiveTask(void) = default;
virtual ~PrimitiveTask(void) = default;
virtual bool StateMeetsPreconditions(const TaskArguments& arguments) = 0;
virtual void ApplyStateChange(TaskArguments& arguments) = 0;
// Returns whether or not starting the task was successful (NOT whether the task completed)
@ -65,22 +65,8 @@ enum class TaskType
Compound,
Primitive
};
/*
// Task only exists so that we can store all our Task types in a flat list
class Task
{
public:
Task(void);
// Type is used to know what to cast this Task to
TaskType GetType(void);
protected:
// This is set by the constructors of each different Task
TaskType Type;
};*/
// Instead of the commented code, just use a simple struct which stores all types of tasks but
// Instead of using inheritance, just use a simple struct which stores all types of tasks but
// only allow only one thing to be filled in for it
struct Task
{
@ -95,7 +81,8 @@ struct Task
CompoundTask* GetCompound(void);
PrimitiveTask* GetPrimitive(void);
friend std::ostream& operator<<(std::ostream& os, const Task& task);
friend std::ostream& operator<<(std::ostream& os, const Task& task);
private:
union
{

3
src/ai/htn/HTNTypes.hpp

@ -40,10 +40,9 @@ typedef std::vector<Parameter>::reverse_iterator ParameterListReverseIterator;
// The arguments passed to most all Task functions
typedef int WorldState; // TODO
// TODO: Should this struct exist, especially considering the user can't pass it to planner anyways?
struct TaskArguments
{
Entity Agent;
// AgentState* AgentState;
WorldState worldState;
ParameterList Parameters;
};

26
src/unitTesting/HTN_test.cpp

@ -98,8 +98,8 @@ public:
virtual bool Decompose(Htn::TaskCallList& taskCallList, const Htn::TaskArguments& args)
{
static TestPrimitiveTask testPrimitiveTask;
std::cout << "\tDecompose TestCompoundTaskA: " << args.Parameters[0].IntValue << "\n";
static Htn::Task primitiveTask(&testPrimitiveTask);
std::cout << "\tDecompose TestCompoundTaskA: " << args.Parameters[0].IntValue << "\n";
Htn::TaskCall taskCall = {&primitiveTask, args.Parameters};
taskCallList.push_back(taskCall);
return true;
@ -151,14 +151,16 @@ TEST_CASE("Hierarchical Task Networks Planner")
args.Parameters = params;
testCompoundTaskAA.Decompose(testPlan.InitialCallList, args);
Htn::Planner::Status status;
for (int i = 0; i < 10; i++)
{
Htn::Planner::Status status = testPlan.PlanStep();
status = testPlan.PlanStep();
std::cout << "[" << i << "] Returned Status " << (int)status << "\n";
if (status == Htn::Planner::Status::PlanComplete)
break;
}
REQUIRE(status == Htn::Planner::Status::PlanComplete);
REQUIRE(testPlan.FinalCallList.size() == 3);
std::cout << "\n\nFinal Plan length: " << testPlan.FinalCallList.size() << "\n\n";
}
@ -169,14 +171,16 @@ TEST_CASE("Hierarchical Task Networks Planner")
Htn::Planner testPlan;
testPlan.InitialCallList.push_back(goalTaskCall);
Htn::Planner::Status status;
for (int i = 0; i < 10; i++)
{
Htn::Planner::Status status = testPlan.PlanStep();
status = testPlan.PlanStep();
std::cout << "[" << i << "] Returned Status " << (int)status << "\n";
if (status == Htn::Planner::Status::PlanComplete)
break;
}
REQUIRE(status == Htn::Planner::Status::PlanComplete);
REQUIRE(testPlan.FinalCallList.size() == 1);
std::cout << "\n\nFinal Plan length: " << testPlan.FinalCallList.size() << "\n";
printTaskCallList(testPlan.FinalCallList);
@ -190,14 +194,16 @@ TEST_CASE("Hierarchical Task Networks Planner")
testPlan.InitialCallList.push_back(nestedGoalTaskCall);
testPlan.InitialCallList.push_back(nestedGoalTaskCall);
Htn::Planner::Status status;
for (int i = 0; i < 12; i++)
{
Htn::Planner::Status status = testPlan.PlanStep();
status = testPlan.PlanStep();
std::cout << "[" << i << "] Returned Status " << (int)status << "\n";
if (status == Htn::Planner::Status::PlanComplete)
break;
}
REQUIRE(status == Htn::Planner::Status::PlanComplete);
REQUIRE(testPlan.FinalCallList.size() == 2);
std::cout << "\n\nFinal Plan length: " << testPlan.FinalCallList.size() << "\n";
printTaskCallList(testPlan.FinalCallList);
@ -218,14 +224,16 @@ TEST_CASE("Hierarchical Task Networks Planner")
Htn::Planner testPlan;
testPlan.InitialCallList.push_back(failGoalTaskCall);
Htn::Planner::Status status;
for (int i = 0; i < 12; i++)
{
Htn::Planner::Status status = testPlan.PlanStep();
status = testPlan.PlanStep();
std::cout << "[" << i << "] Returned Status " << (int)status << "\n";
if (status == Htn::Planner::Status::PlanComplete)
break;
}
REQUIRE(status == Htn::Planner::Status::PlanComplete);
REQUIRE(testPlan.FinalCallList.size() == 1);
std::cout << "\n\nFinal Plan length: " << testPlan.FinalCallList.size() << "\n";
printTaskCallList(testPlan.FinalCallList);
@ -241,14 +249,16 @@ TEST_CASE("Hierarchical Task Networks Planner")
testPlan.InitialCallList.push_back(requiresStateTaskCall);
testPlan.State = 0;
Htn::Planner::Status status;
for (int i = 0; i < 12; i++)
{
Htn::Planner::Status status = testPlan.PlanStep();
status = testPlan.PlanStep();
std::cout << "[" << i << "] Returned Status " << (int)status << "\n";
if (status == Htn::Planner::Status::PlanComplete)
break;
}
REQUIRE(status == Htn::Planner::Status::PlanComplete);
REQUIRE(testPlan.FinalCallList.size() == 2);
std::cout << "\n\nFinal Plan length: " << testPlan.FinalCallList.size() << "\n";
printTaskCallList(testPlan.FinalCallList);
@ -266,15 +276,17 @@ TEST_CASE("Hierarchical Task Networks Planner")
testPlan.InitialCallList.push_back(nestedGoalTaskCall);
testPlan.State = 0;
Htn::Planner::Status status;
for (int i = 0; i < 12; i++)
{
Htn::Planner::Status status = testPlan.PlanStep();
status = testPlan.PlanStep();
std::cout << "[" << i << "] Returned Status " << (int)status << "\n";
if (status == Htn::Planner::Status::PlanComplete ||
status == Htn::Planner::Status::Failed_NoPossiblePlan)
break;
}
REQUIRE(status == Htn::Planner::Status::Failed_NoPossiblePlan);
REQUIRE(testPlan.FinalCallList.size() == 0);
std::cout << "\n\nFinal Plan length: " << testPlan.FinalCallList.size() << "\n";
printTaskCallList(testPlan.FinalCallList);

5
src/unitTesting/Jamfile

@ -8,7 +8,10 @@ Main objectPoolTest : ObjectPoolTest.cpp ;
Main htnTest : HTN_test.cpp ;
LinkLibraries htnTest : libGalaAi ;
MakeLocate objectComponentTest entityComponentTest objectPoolTest htnTest : bin ;
Main positionTest : Position_test.cpp ;
LinkLibraries positionTest : libGalaWorld ;
MakeLocate objectComponentTest entityComponentTest objectPoolTest htnTest positionTest : bin ;
SubInclude . src entityComponentSystem ;
SubInclude . src ai ;

51
src/unitTesting/Position_test.cpp

@ -0,0 +1,51 @@
#include <iostream>
#define CATCH_CONFIG_MAIN
#include "../../thirdParty/Catch/single_include/catch.hpp"
#include "../world/Position.hpp"
TEST_CASE("Position and GlobalPosition")
{
SECTION("Position Initialization")
{
gv::Position position;
REQUIRE((position.X == 0.f && position.Y == 0.f && position.Z == 0.f));
gv::Position position2(1.f, 1.f, 1.f);
REQUIRE((position2.X == 1.f && position2.Y == 1.f && position2.Z == 1.f));
}
SECTION("Position Array Accessor")
{
gv::Position position(1.f, 2.f, 3.f);
REQUIRE(
(position.X == position[0] && position.Y == position[1] && position.Z == position[2]));
}
SECTION("Position Equals")
{
gv::Position a(1.f, 2.f, 3.f);
gv::Position b(1.08f, 2.08f, 3.08f);
REQUIRE(a.Equals(b, 0.1f));
REQUIRE(!a.Equals(b, 0.01f));
}
SECTION("Position Const Operators")
{
gv::Position a(1.f, 2.f, 3.f);
gv::Position b(1.f, 2.f, 3.f);
gv::Position expectedResult(2.f, 4.f, 6.f);
gv::Position result = a + b;
REQUIRE(result.Equals(expectedResult, 0.01f));
}
SECTION("Position Modifier Operators")
{
gv::Position a(1.f, 2.f, 3.f);
gv::Position b(1.f, 2.f, 3.f);
gv::Position expectedResult(2.f, 4.f, 6.f);
a += b;
REQUIRE(a.Equals(expectedResult, 0.01f));
}
}

5
src/world/Jamfile

@ -0,0 +1,5 @@
SubDir . src world ;
Library libGalaWorld : Position.cpp ;
MakeLocate libGalaWorld.a : lib ;

101
src/world/Position.cpp

@ -0,0 +1,101 @@
#include "Position.hpp"
#include <cmath>
namespace gv
{
Position::Position(float x, float y, float z) : X(x), Y(y), Z(z)
{
}
bool Position::Equals(const Position& otherPosition, float tolerance) const
{
return (fabs(X - otherPosition.X) <= tolerance && fabs(Y - otherPosition.Y) <= tolerance &&
fabs(Z - otherPosition.Z) <= tolerance);
}
float& Position::operator[](int index)
{
switch (index)
{
case 0:
return X;
case 1:
return Y;
case 2:
return Z;
}
return X;
}
Position Position::operator+(const Position& otherPosition) const
{
Position newPosition(*this);
newPosition.X += otherPosition.X;
newPosition.Y += otherPosition.Y;
newPosition.Z += otherPosition.Z;
return newPosition;
}
Position Position::operator-(const Position& otherPosition) const
{
Position newPosition(*this);
newPosition.X -= otherPosition.X;
newPosition.Y -= otherPosition.Y;
newPosition.Z -= otherPosition.Z;
return newPosition;
}
Position Position::operator*(const Position& otherPosition) const
{
Position newPosition(*this);
newPosition.X *= otherPosition.X;
newPosition.Y *= otherPosition.Y;
newPosition.Z *= otherPosition.Z;
return newPosition;
}
Position Position::operator/(const Position& otherPosition) const
{
Position newPosition(*this);
newPosition.X /= otherPosition.X;
newPosition.Y /= otherPosition.Y;
newPosition.Z /= otherPosition.Z;
return newPosition;
}
Position& Position::operator+=(const Position& otherPosition)
{
X += otherPosition.X;
Y += otherPosition.Y;
Z += otherPosition.Z;
return *this;
}
Position& Position::operator-=(const Position& otherPosition)
{
X -= otherPosition.X;
Y -= otherPosition.Y;
Z -= otherPosition.Z;
return *this;
}
Position& Position::operator*=(const Position& otherPosition)
{
X *= otherPosition.X;
Y *= otherPosition.Y;
Z *= otherPosition.Z;
return *this;
}
Position& Position::operator/=(const Position& otherPosition)
{
X /= otherPosition.X;
Y /= otherPosition.Y;
Z /= otherPosition.Z;
return *this;
}
GlobalPosition::GlobalPosition(Position& localPosition) : LocalPosition(localPosition)
{
}
}

38
src/world/Position.hpp

@ -0,0 +1,38 @@
#pragma once
namespace gv
{
struct Position
{
float X = 0.f;
float Y = 0.f;
float Z = 0.f;
Position() = default;
Position(float x, float y, float z);
bool Equals(const Position& otherPosition, float tolerance) const;
float& operator[](int index);
Position operator+(const Position& otherPosition) const;
Position operator-(const Position& otherPosition) const;
Position operator*(const Position& otherPosition) const;
Position operator/(const Position& otherPosition) const;
Position& operator+=(const Position& otherPosition);
Position& operator-=(const Position& otherPosition);
Position& operator*=(const Position& otherPosition);
Position& operator/=(const Position& otherPosition);
};
struct GlobalPosition
{
Position LocalPosition;
// Will be added once chunks/world is figured out
// Position ChunkPosition;
GlobalPosition() = default;
GlobalPosition(Position& localPosition);
};
};
Loading…
Cancel
Save