mirror of
https://github.com/OpenTrespasser/JurassicParkTrespasser.git
synced 2024-12-20 23:51:58 +00:00
Merge pull request #96 from meekee7/ai-removesomeinfluences-refactor
Refactor CWorldView::RemoveSomeInfluences
This commit is contained in:
commit
040c99d81b
jp2_pc
@ -42,6 +42,7 @@ include(cmake/CMakeCommon.cmake)
|
||||
|
||||
add_subdirectory(cmake/AI)
|
||||
add_subdirectory(cmake/AITest)
|
||||
add_subdirectory(cmake/AIUnitTest)
|
||||
add_subdirectory(cmake/Audio)
|
||||
add_subdirectory(cmake/AudioTest)
|
||||
add_subdirectory(cmake/Bugs)
|
||||
|
@ -136,7 +136,7 @@ public:
|
||||
CInstance* const pinsTarget; // The object associated with this influence
|
||||
// or 0 if none.
|
||||
|
||||
CSet<EInfluenceFlag>setFlags; // A bunch of flags for an influence.
|
||||
mutable CSet<EInfluenceFlag> setFlags; // A bunch of flags for an influence.
|
||||
|
||||
// CFeeling feelDBGWeighted; // The emotional baggage associated with the influence,
|
||||
|
||||
@ -415,6 +415,10 @@ public:
|
||||
// return (CInfluence)*this;
|
||||
// }
|
||||
|
||||
static bool IsDiscardable(const CInfluence& influence)
|
||||
{
|
||||
return influence.setFlags[eifIS_DISCARDABLE];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -367,56 +367,38 @@ void CWorldView::RemoveSomeInfluences()
|
||||
// unimportant enough, go ahead.
|
||||
|
||||
// If we have a bunch of influences, remove one.
|
||||
CInfluenceList::iterator pinf_lowest = inflInfluences.end();
|
||||
CInfluenceList::iterator pinf = inflInfluences.begin();
|
||||
size_t discardable = 0;
|
||||
|
||||
int i_discardable_count = 0;
|
||||
std::list<const CInfluence*> lpinf_discard;
|
||||
|
||||
// Find the lamest influence and remove it.
|
||||
// For now, we only remove one bad apple each cycle.
|
||||
for (; pinf != inflInfluences.end(); pinf++)
|
||||
CInstance* lowestInstance = nullptr;
|
||||
std::list<CInstance*> todelete;
|
||||
|
||||
for (const auto& entry : inflInfluences)
|
||||
{
|
||||
TReal r_importance = (*pinf).rImportance;
|
||||
|
||||
if (gaiSystem.sNow - (*pinf).sLastSeen > paniOwner->pbrBrain->sForgetInfluence)
|
||||
if (gaiSystem.sNow - entry.sLastSeen > paniOwner->pbrBrain->sForgetInfluence)
|
||||
entry.setFlags[eifIS_DISCARDABLE] = true;
|
||||
else if (entry.rImportance < r_lowest)
|
||||
{
|
||||
// It's been a while. Forget me, please.
|
||||
((CInfluence*)&(*pinf))->setFlags[eifIS_DISCARDABLE] = true;
|
||||
}
|
||||
else if ( r_importance < r_lowest)
|
||||
{
|
||||
r_lowest = r_importance;
|
||||
pinf_lowest = pinf;
|
||||
r_lowest = entry.rImportance;
|
||||
lowestInstance = entry.pinsTarget;
|
||||
}
|
||||
|
||||
if ((*pinf).setFlags[eifIS_DISCARDABLE])
|
||||
if (entry.setFlags[eifIS_DISCARDABLE])
|
||||
{
|
||||
// Add to discard list!
|
||||
lpinf_discard.push_back(&(*pinf));
|
||||
++i_discardable_count;
|
||||
todelete.push_back(entry.pinsTarget);
|
||||
discardable++;
|
||||
}
|
||||
}
|
||||
|
||||
if (pinf_lowest != inflInfluences.end())
|
||||
{
|
||||
// Add to discard list!
|
||||
lpinf_discard.push_back(&(*pinf_lowest));
|
||||
((CInfluence*)&(*pinf_lowest))->setFlags[eifIS_DISCARDABLE] = true;
|
||||
}
|
||||
|
||||
if (i_discardable_count > 10)
|
||||
if (lowestInstance)
|
||||
{
|
||||
std::list<const CInfluence*>::iterator itpinf;
|
||||
for(itpinf = lpinf_discard.begin(); itpinf != lpinf_discard.end(); ++itpinf)
|
||||
{
|
||||
// Is it discardable?
|
||||
Assert((*itpinf)->setFlags[eifIS_DISCARDABLE]);
|
||||
|
||||
// Discard it.
|
||||
bRemoveInfluence((*itpinf)->pinsTarget);
|
||||
}
|
||||
//Influence set is based on instance pointers, find result is guaranteed to be the correct one
|
||||
iterFindInfluence(lowestInstance)->setFlags[eifIS_DISCARDABLE] = true;
|
||||
todelete.push_back(lowestInstance);
|
||||
}
|
||||
|
||||
if (discardable > 10)
|
||||
for (auto* entry : todelete)
|
||||
bRemoveInfluence(entry);
|
||||
}
|
||||
|
||||
//*********************************************************************************
|
||||
|
38
jp2_pc/Source/Test/Novel/AIUnitTest/InfluenceTest.cpp
Normal file
38
jp2_pc/Source/Test/Novel/AIUnitTest/InfluenceTest.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "common.hpp"
|
||||
#include "Game/AI/Influence.hpp"
|
||||
#include "Lib/EntityDBase/Instance.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
|
||||
TEST(CInfluence, ComparisonSetFlagsNoInfluence)
|
||||
{
|
||||
//Comparison is based on the pins_target pointer value
|
||||
//Use specific pointers
|
||||
CInfluence a(reinterpret_cast<CInstance*>(60));
|
||||
CInfluence b(reinterpret_cast<CInstance*>(80));
|
||||
|
||||
const std::less<> less;
|
||||
ASSERT_TRUE(less(a, b));
|
||||
ASSERT_FALSE(less(b, a));
|
||||
|
||||
//Test that the discardable flag has no influence on comparison
|
||||
for (int i=0; i < 4; i++)
|
||||
{
|
||||
a.setFlags[eifIS_DISCARDABLE] = static_cast<bool>(i & (1 << 0)); //First bit
|
||||
b.setFlags[eifIS_DISCARDABLE] = static_cast<bool>(i & (1 << 1)); //Second bit
|
||||
|
||||
EXPECT_TRUE(less(a, b));
|
||||
EXPECT_FALSE(less(b, a));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CInfluence, SetFlagOnConst)
|
||||
{
|
||||
const CInfluence influence(nullptr);
|
||||
ASSERT_FALSE(influence.setFlags[eifIS_DISCARDABLE]);
|
||||
|
||||
//Test that we can set the flag despite constness
|
||||
influence.setFlags[eifIS_DISCARDABLE] = true;
|
||||
EXPECT_TRUE(influence.setFlags[eifIS_DISCARDABLE]);
|
||||
}
|
246
jp2_pc/Source/Test/Novel/AIUnitTest/WorldViewTest.cpp
Normal file
246
jp2_pc/Source/Test/Novel/AIUnitTest/WorldViewTest.cpp
Normal file
@ -0,0 +1,246 @@
|
||||
#include "common.hpp"
|
||||
#include "Game/AI/WorldView.hpp"
|
||||
#include "Game/AI/AIMain.hpp"
|
||||
#include "Game/AI/AIInfo.hpp"
|
||||
#include "Lib/EntityDBase/Instance.hpp"
|
||||
#include "Lib/EntityDBase/Animal.hpp"
|
||||
//#include "Test/AI/TestAnimal.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
class WorldViewTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
|
||||
virtual void SetUp() override
|
||||
{
|
||||
aisystem = std::make_unique<CAISystem>(nullptr);
|
||||
aisystem->sNow = 100.0f;
|
||||
gpaiSystem = aisystem.get();
|
||||
owner = std::make_unique<CAnimal>();
|
||||
view = std::make_unique<CWorldView>(owner.get());
|
||||
}
|
||||
|
||||
virtual void TearDown() override
|
||||
{
|
||||
aisystem = nullptr;
|
||||
gpaiSystem = nullptr;
|
||||
view = nullptr;
|
||||
owner = nullptr;
|
||||
}
|
||||
|
||||
void InsertNInfluences(size_t n, TSec lastSeen, TReal prio, bool markDiscarded = false)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
InsertInfluence(lastSeen, prio, markDiscarded);
|
||||
}
|
||||
|
||||
void InsertInfluence(TSec lastSeen, TReal prio, bool markDiscarded = false)
|
||||
{
|
||||
//Instance is deleted in CInfluence destructor
|
||||
CInfluence influence(new CInstance());
|
||||
influence.sLastSeen = lastSeen;
|
||||
influence.rImportance = prio;
|
||||
influence.setFlags[eifIS_DISCARDABLE] = markDiscarded;
|
||||
view->inflInfluences.insert(influence);
|
||||
}
|
||||
|
||||
CInfluenceList& GetList()
|
||||
{
|
||||
return view->inflInfluences;
|
||||
}
|
||||
|
||||
static bool IsMarkedDiscardable(const CInfluence& influence)
|
||||
{
|
||||
return influence.setFlags[eifIS_DISCARDABLE];
|
||||
}
|
||||
|
||||
std::unique_ptr<CAISystem> aisystem;
|
||||
std::unique_ptr<CAnimal> owner;
|
||||
std::unique_ptr<CWorldView> view;
|
||||
};
|
||||
|
||||
//Nothing weird happens with an empty list
|
||||
TEST_F(WorldViewTest, RemoveOnEmpty)
|
||||
{
|
||||
ASSERT_TRUE(GetList().empty());
|
||||
view->RemoveSomeInfluences();
|
||||
EXPECT_TRUE(GetList().empty());
|
||||
}
|
||||
|
||||
//Few old influences, marked but not discarded
|
||||
TEST_F(WorldViewTest, MarkOldsAsDiscardable)
|
||||
{
|
||||
constexpr size_t elems = 5;
|
||||
InsertNInfluences(elems, aisystem->sNow - 2.5f, 10.0f);
|
||||
|
||||
ASSERT_EQ(GetList().size(), elems);
|
||||
ASSERT_TRUE(std::none_of(GetList().begin(), GetList().end(), &IsMarkedDiscardable));
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_EQ(GetList().size(), elems);
|
||||
EXPECT_TRUE(std::all_of(GetList().begin(), GetList().end(), &IsMarkedDiscardable));
|
||||
}
|
||||
|
||||
//Few new influences, not marked and not removed
|
||||
TEST_F(WorldViewTest, NoMarkCurrentAsDiscardable)
|
||||
{
|
||||
constexpr size_t elems = 5;
|
||||
InsertNInfluences(elems, aisystem->sNow - 0.5f, 10.0f);
|
||||
|
||||
ASSERT_EQ(GetList().size(), elems);
|
||||
ASSERT_TRUE(std::none_of(GetList().begin(), GetList().end(), &CInfluence::IsDiscardable));
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_EQ(GetList().size(), elems);
|
||||
EXPECT_TRUE(std::none_of(GetList().begin(), GetList().end(), &CInfluence::IsDiscardable));
|
||||
}
|
||||
|
||||
//One zero prio influence, marked but not removed
|
||||
TEST_F(WorldViewTest, MarkZeroPrioAsDiscardable)
|
||||
{
|
||||
InsertInfluence(aisystem->sNow - 0.5f, 0.0f);
|
||||
|
||||
ASSERT_FALSE(GetList().empty());
|
||||
ASSERT_FALSE(GetList().begin()->setFlags[eifIS_DISCARDABLE]);
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_FALSE(GetList().empty());
|
||||
EXPECT_TRUE(GetList().begin()->setFlags[eifIS_DISCARDABLE]);
|
||||
}
|
||||
|
||||
//Many current influences, lowest prio one is removed
|
||||
TEST_F(WorldViewTest, MarkLowestOfFew)
|
||||
{
|
||||
constexpr size_t elems = 5;
|
||||
InsertNInfluences(elems, aisystem->sNow - 0.5f, 0.05f);
|
||||
InsertInfluence(aisystem->sNow - 0.5f, 0.0f);
|
||||
|
||||
ASSERT_EQ(GetList().size(), elems + 1);
|
||||
ASSERT_TRUE(std::none_of(GetList().begin(), GetList().end(), &CInfluence::IsDiscardable));
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_EQ(GetList().size(), elems + 1);
|
||||
EXPECT_EQ(std::count_if(GetList().begin(), GetList().end(), &CInfluence::IsDiscardable), 1);
|
||||
EXPECT_EQ(std::find_if(GetList().begin(), GetList().end(), &CInfluence::IsDiscardable)->rImportance, 0.0f);
|
||||
}
|
||||
|
||||
TEST_F(WorldViewTest, NoDiscardUnmarked)
|
||||
{
|
||||
constexpr size_t elems = 20;
|
||||
InsertNInfluences(elems, aisystem->sNow - 0.5f, 10.0f, false);
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_EQ(GetList().size(), elems);
|
||||
}
|
||||
|
||||
TEST_F(WorldViewTest, DiscardMarkedAll)
|
||||
{
|
||||
constexpr size_t elems = 20;
|
||||
InsertNInfluences(elems, aisystem->sNow - 0.5f, 10.0f, true);
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_TRUE(GetList().empty());
|
||||
}
|
||||
|
||||
TEST_F(WorldViewTest, DiscardMarkedMixed)
|
||||
{
|
||||
constexpr size_t keep = 12;
|
||||
constexpr size_t discard = 12;
|
||||
InsertNInfluences(keep, aisystem->sNow - 0.5f, 10.0f, false);
|
||||
InsertNInfluences(discard, aisystem->sNow - 0.5f, 10.0f, true);
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_EQ(GetList().size(), keep);
|
||||
EXPECT_TRUE(std::none_of(GetList().begin(), GetList().end(), &CInfluence::IsDiscardable));
|
||||
}
|
||||
|
||||
//Many current influences, none discarded
|
||||
TEST_F(WorldViewTest, NoDiscardCurrent)
|
||||
{
|
||||
constexpr size_t tocreate = 20;
|
||||
InsertNInfluences(tocreate, aisystem->sNow - 0.5f, 10.0f);
|
||||
|
||||
ASSERT_EQ(GetList().size(), tocreate);
|
||||
view->RemoveSomeInfluences();
|
||||
EXPECT_EQ(GetList().size(), tocreate);
|
||||
}
|
||||
|
||||
//Many old influences, will be discarded
|
||||
TEST_F(WorldViewTest, DiscardOldsMany)
|
||||
{
|
||||
constexpr size_t olds = 20;
|
||||
constexpr size_t news = 5;
|
||||
InsertNInfluences(news, aisystem->sNow - 0.5f, 10.0f);
|
||||
InsertNInfluences(olds, aisystem->sNow - 2.5f, 10.0f);
|
||||
|
||||
ASSERT_EQ(GetList().size(), olds + news);
|
||||
view->RemoveSomeInfluences();
|
||||
EXPECT_EQ(GetList().size(), news);
|
||||
}
|
||||
|
||||
//Many old influences and a current low-prio one, will be discarded
|
||||
TEST_F(WorldViewTest, DiscardOldsAndLowestPrioCurrent)
|
||||
{
|
||||
constexpr size_t olds = 20;
|
||||
constexpr size_t news = 5;
|
||||
InsertNInfluences(news, aisystem->sNow - 0.5f, 10.0f);
|
||||
InsertNInfluences(olds, aisystem->sNow - 2.5f, 10.0f);
|
||||
InsertInfluence(aisystem->sNow - 0.5f, 0.0f);
|
||||
|
||||
ASSERT_EQ(GetList().size(), olds + news + 1);
|
||||
view->RemoveSomeInfluences();
|
||||
EXPECT_EQ(GetList().size(), news);
|
||||
}
|
||||
|
||||
//Few influences, one with zero prio is to be removed
|
||||
TEST_F(WorldViewTest, DiscardFewZeroPrio)
|
||||
{
|
||||
InsertNInfluences(15, aisystem->sNow - 2.5f, 10.0f);
|
||||
InsertInfluence(aisystem->sNow - 0.5f, 0.0f);
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_TRUE(GetList().empty());
|
||||
}
|
||||
|
||||
//Some old influences to be removed, one with some prio is not to be removed
|
||||
TEST_F(WorldViewTest, NoDiscardFewWithPrio)
|
||||
{
|
||||
InsertNInfluences(15, aisystem->sNow - 2.5f, 10.0f);
|
||||
InsertInfluence(aisystem->sNow - 0.5f, 1.0f);
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_EQ(GetList().size(), 1);
|
||||
}
|
||||
|
||||
//Many old influences to be removed, new influence but with lowest prio is also removed
|
||||
TEST_F(WorldViewTest, DiscardManyWithPrio)
|
||||
{
|
||||
InsertNInfluences(40, aisystem->sNow - 2.5f, 10.0f);
|
||||
InsertInfluence(aisystem->sNow - 0.5f, 1.0f);
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_TRUE(GetList().empty());
|
||||
}
|
||||
|
||||
//Very few influences, none are removed, including one with zero prio
|
||||
TEST_F(WorldViewTest, NoDiscardVeryFewZeroPrio)
|
||||
{
|
||||
constexpr size_t elems = 5;
|
||||
InsertNInfluences(elems, aisystem->sNow - 2.5f, 10.0f);
|
||||
InsertInfluence(aisystem->sNow - 0.5f, 0.0f);
|
||||
|
||||
view->RemoveSomeInfluences();
|
||||
|
||||
EXPECT_EQ(GetList().size(), elems + 1);
|
||||
}
|
47
jp2_pc/cmake/AIUnitTest/CMakeLists.txt
Normal file
47
jp2_pc/cmake/AIUnitTest/CMakeLists.txt
Normal file
@ -0,0 +1,47 @@
|
||||
project(AIUnitTest)
|
||||
|
||||
list(APPEND AIUnitTest_Src
|
||||
${CMAKE_SOURCE_DIR}/Source/Test/Novel/AIUnitTest/WorldViewTest.cpp
|
||||
${CMAKE_SOURCE_DIR}/Source/Test/Novel/AIUnitTest/InfluenceTest.cpp
|
||||
)
|
||||
|
||||
add_common_options()
|
||||
|
||||
add_executable(${PROJECT_NAME} ${AIUnitTest_Src})
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER Tests/Novel)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/Source
|
||||
${CMAKE_SOURCE_DIR}/Source/gblinc
|
||||
${CMAKE_SOURCE_DIR}/gtest/googletest/include
|
||||
)
|
||||
|
||||
target_link_libraries( ${PROJECT_NAME}
|
||||
AI
|
||||
Audio
|
||||
EntityDBase
|
||||
File
|
||||
Game
|
||||
GeomDBase
|
||||
Loader
|
||||
Math
|
||||
Physics
|
||||
Render3D
|
||||
ScreenRenderDWI
|
||||
Std
|
||||
System
|
||||
View
|
||||
|
||||
lz32
|
||||
winmm
|
||||
|
||||
dxguid
|
||||
dsound
|
||||
ddraw
|
||||
|
||||
GTestLibGlobals
|
||||
|
||||
gtest
|
||||
gtest_main
|
||||
)
|
Loading…
Reference in New Issue
Block a user