Skip to content

Commit

Permalink
state transitions refactored + parameterized tests
Browse files Browse the repository at this point in the history
  • Loading branch information
chrxh committed Nov 21, 2024
1 parent 32ff33d commit 4923126
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 94 deletions.
102 changes: 64 additions & 38 deletions source/EngineGpuKernels/CellProcessor.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public:
__inline__ __device__ static void verletVelocityUpdate(SimulationData& data);

__inline__ __device__ static void aging(SimulationData& data);
__inline__ __device__ static void livingStateTransition(SimulationData& data);
__inline__ __device__ static void livingStateTransition_calcNextState(SimulationData& data);
__inline__ __device__ static void livingStateTransition_applyNextState(SimulationData& data);

__inline__ __device__ static void applyInnerFriction(SimulationData& data);
__inline__ __device__ static void applyFriction(SimulationData& data);
Expand Down Expand Up @@ -593,58 +594,83 @@ __inline__ __device__ void CellProcessor::aging(SimulationData& data)
}


__inline__ __device__ void CellProcessor::livingStateTransition(SimulationData& data)
__inline__ __device__ void CellProcessor::livingStateTransition_calcNextState(SimulationData& data)
{
auto& cells = data.objects.cellPointers;
auto partition = calcAllThreadsPartition(cells.getNumEntries());

for (int index = partition.startIndex; index <= partition.endIndex; ++index) {
auto& cell = cells.at(index);
auto origLivingState = atomicCAS(&cell->livingState, LivingState_Activating, LivingState_Ready);

bool isSameCreatureNeighborDetaching = false;
bool isOtherCreatureNeighborDetaching = false;
bool isSameCreatureNeighborReviving= false;
bool isNeighborActivating = false;
for (int i = 0; i < cell->numConnections; ++i) {
auto const& connectedCell = cell->connections[i].cell;
if (connectedCell->creatureId == cell->creatureId) {
auto connectedLivingState = connectedCell->livingState;
if (connectedLivingState == LivingState_Detaching) {
isSameCreatureNeighborDetaching = true;
} else if (connectedLivingState == LivingState_Reviving) {
isSameCreatureNeighborReviving = true;
} else if (connectedLivingState == LivingState_Activating) {
isNeighborActivating = true;
}
} else {
if (connectedCell->livingState == LivingState_Detaching) {
isOtherCreatureNeighborDetaching = true;
}
}
}

auto origLivingState = cell->livingState;
auto livingState = origLivingState;

if (origLivingState == LivingState_Activating) {
livingState = LivingState_Ready;
if (cudaSimulationParameters.features.cellAgeLimiter && cudaSimulationParameters.cellResetAgeAfterActivation) {
atomicExch(&cell->age, 0);
}
for (int i = 0; i < cell->numConnections; ++i) {
auto const& connectedCell = cell->connections[i].cell;
auto origLivingStateConnectedCell = atomicCAS(&connectedCell->livingState, LivingState_UnderConstruction, LivingState_Activating);
if (origLivingStateConnectedCell == LivingState_UnderConstruction) {
if (cudaSimulationParameters.features.cellAgeLimiter && cudaSimulationParameters.cellResetAgeAfterActivation) {
atomicExch(&connectedCell->age, 0);
}
}
} else if (origLivingState == LivingState_Reviving) {
livingState = LivingState_Ready;
} else if (origLivingState == LivingState_UnderConstruction) {
if (isNeighborActivating) {
livingState = LivingState_Activating;
}
}
if (origLivingState == LivingState_Reviving) {
atomicExch(&cell->livingState, LivingState_Ready);
for (int i = 0; i < cell->numConnections; ++i) {
auto const& connectedCell = cell->connections[i].cell;
if (connectedCell->creatureId == cell->creatureId) {
atomicCAS(&connectedCell->livingState, LivingState_Detaching, LivingState_Reviving);
}
if (isOtherCreatureNeighborDetaching && cudaSimulationParameters.cellDeathConsequences != CellDeathConsquences_None) {
livingState = LivingState_Detaching;
}
}
if (origLivingState == LivingState_Detaching) {
if (cudaSimulationParameters.cellDeathConsequences == CellDeathConsquences_DetachedPartsDie
|| cudaSimulationParameters.cellDeathConsequences == CellDeathConsquences_CreatureDies) {
for (int i = 0; i < cell->numConnections; ++i) {
auto const& connectedCell = cell->connections[i].cell;
if (connectedCell->creatureId == cell->creatureId) {
if (cudaSimulationParameters.cellDeathConsequences == CellDeathConsquences_DetachedPartsDie
&& connectedCell->cellFunction == CellFunction_Constructor
&& GenomeDecoder::containsSelfReplication(connectedCell->cellFunctionData.constructor)) {
atomicExch(&connectedCell->livingState, LivingState_Reviving);
} else {
atomicCAS(&connectedCell->livingState, LivingState_Ready, LivingState_Detaching);
}
} else {
atomicCAS(&connectedCell->livingState, LivingState_UnderConstruction, LivingState_Detaching);
}
} else if (origLivingState == LivingState_Detaching) {
if (isSameCreatureNeighborReviving && cudaSimulationParameters.cellDeathConsequences == CellDeathConsquences_DetachedPartsDie) {
livingState = LivingState_Reviving;
}
if (cudaSimulationParameters.cellDeathConsequences == CellDeathConsquences_None) {
livingState = LivingState_Ready;
}
} else if (origLivingState == LivingState_Ready) {
if (isSameCreatureNeighborDetaching && cudaSimulationParameters.cellDeathConsequences != CellDeathConsquences_None) {
if (cudaSimulationParameters.cellDeathConsequences == CellDeathConsquences_DetachedPartsDie && cell->cellFunction == CellFunction_Constructor
&& GenomeDecoder::containsSelfReplication(cell->cellFunctionData.constructor)) {
livingState = LivingState_Reviving;
} else {
livingState = LivingState_Detaching;
}
} else {
atomicExch(&cell->livingState, LivingState_Ready);
}
}
cell->tag = livingState;
}
}

__inline__ __device__ void CellProcessor::livingStateTransition_applyNextState(SimulationData& data)
{
auto& cells = data.objects.cellPointers;
auto partition = calcAllThreadsPartition(cells.getNumEntries());

for (int index = partition.startIndex; index <= partition.endIndex; ++index) {
auto& cell = cells.at(index);
cell->livingState = cell->tag;
cell->tag = 0;
}
}

Expand Down
3 changes: 2 additions & 1 deletion source/EngineGpuKernels/SimulationKernels.cu
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ __global__ void cudaNextTimestep_cellFunction_prepare_substep1(SimulationData da

__global__ void cudaNextTimestep_cellFunction_prepare_substep2(SimulationData data)
{
CellProcessor::livingStateTransition(data);
CellProcessor::livingStateTransition_calcNextState(data);
CellProcessor::livingStateTransition_applyNextState(data);
CellFunctionProcessor::collectCellFunctionOperations(data);
CellFunctionProcessor::updateRenderingData(data);
}
Expand Down
160 changes: 105 additions & 55 deletions source/EngineTests/LivingStateTransitionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
#include "Base/NumberGenerator.h"
#include "EngineInterface/DescriptionEditService.h"
#include "EngineInterface/Descriptions.h"
#include "EngineInterface/GenomeConstants.h"
#include "EngineInterface/GenomeDescriptionService.h"
#include "EngineInterface/SimulationFacade.h"
#include "IntegrationTestFramework.h"

class LivingStateTransitionTests : public IntegrationTestFramework
class LivingStateTransitionTests
: public IntegrationTestFramework
, public testing::WithParamInterface<CellDeathConsquences>
{
public:
static SimulationParameters getParameters()
Expand All @@ -28,12 +29,18 @@ class LivingStateTransitionTests : public IntegrationTestFramework
{}

~LivingStateTransitionTests() = default;

protected:
};

TEST_F(LivingStateTransitionTests, staysReady)
INSTANTIATE_TEST_SUITE_P(
LivingStateTransitionTests,
LivingStateTransitionTests,
::testing::Values(CellDeathConsquences_None, CellDeathConsquences_DetachedPartsDie, CellDeathConsquences_CreatureDies));

TEST_P(LivingStateTransitionTests, ready_ready)
{
_parameters.cellDeathConsequences = GetParam();
_simulationFacade->setSimulationParameters(_parameters);

DataDescription data;
data.addCells({
CellDescription().setId(1).setPos({10.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Ready),
Expand All @@ -48,8 +55,11 @@ TEST_F(LivingStateTransitionTests, staysReady)
EXPECT_EQ(LivingState_Ready, getCell(actualData, 2).livingState);
}

TEST_F(LivingStateTransitionTests, dyingIfAdjacentDying)
TEST_P(LivingStateTransitionTests, ready_dying)
{
_parameters.cellDeathConsequences = GetParam();
_simulationFacade->setSimulationParameters(_parameters);

DataDescription data;
data.addCells({
CellDescription().setId(1).setPos({10.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Ready),
Expand All @@ -60,111 +70,151 @@ TEST_F(LivingStateTransitionTests, dyingIfAdjacentDying)
_simulationFacade->setSimulationData(data);
_simulationFacade->calcTimesteps(1);
auto actualData = _simulationFacade->getSimulationData();
EXPECT_EQ(LivingState_Dying, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Ready, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Dying, getCell(actualData, 2).livingState);
}

TEST_F(LivingStateTransitionTests, activatingUnderConstruction)
TEST_P(LivingStateTransitionTests, ready_detaching)
{
_parameters.cellDeathConsequences = GetParam();
_simulationFacade->setSimulationParameters(_parameters);

DataDescription data;
data.addCells({
CellDescription().setId(1).setPos({10.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_UnderConstruction),
CellDescription().setId(2).setPos({11.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Activating),
CellDescription().setId(1).setPos({10.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Ready),
CellDescription().setId(2).setPos({11.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Detaching),
});
data.addConnection(1, 2);

_simulationFacade->setSimulationData(data);
_simulationFacade->calcTimesteps(1);
auto actualData = _simulationFacade->getSimulationData();
EXPECT_EQ(LivingState_Activating, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Ready, getCell(actualData, 2).livingState);

if (GetParam() == CellDeathConsquences_None) {
EXPECT_EQ(LivingState_Ready, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Ready, getCell(actualData, 2).livingState);
} else if (GetParam() == CellDeathConsquences_CreatureDies) {
EXPECT_EQ(LivingState_Detaching, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Detaching, getCell(actualData, 2).livingState);
} else if (GetParam() == CellDeathConsquences_DetachedPartsDie) {
EXPECT_EQ(LivingState_Detaching, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Detaching, getCell(actualData, 2).livingState);
}
}

TEST_F(LivingStateTransitionTests, staysReadyIfAdjacentDying_differentCreatureId)
TEST_P(LivingStateTransitionTests, ready_detaching_onSelfReplicator)
{
_parameters.cellDeathConsequences = GetParam();
_simulationFacade->setSimulationParameters(_parameters);

auto genome = GenomeDescriptionService::get().convertDescriptionToBytes(
GenomeDescription()
.setHeader(GenomeHeaderDescription())
.setCells({CellGenomeDescription().setCellFunction(ConstructorGenomeDescription().setMakeSelfCopy())}));

DataDescription data;
data.addCells({
CellDescription().setId(1).setPos({10.0f, 10.0f}).setMaxConnections(1).setCreatureId(1).setLivingState(LivingState_Ready),
CellDescription().setId(2).setPos({11.0f, 10.0f}).setMaxConnections(1).setCreatureId(2).setLivingState(LivingState_Dying),
CellDescription()
.setId(1)
.setCellFunction(ConstructorDescription().setGenome(genome))
.setPos({10.0f, 10.0f})
.setMaxConnections(1)
.setLivingState(LivingState_Ready),
CellDescription()
.setId(2)
.setPos({11.0f, 10.0f})
.setMaxConnections(1)
.setLivingState(LivingState_Detaching),
});
data.addConnection(1, 2);

_simulationFacade->setSimulationData(data);
_simulationFacade->calcTimesteps(1);
auto actualData = _simulationFacade->getSimulationData();
EXPECT_EQ(LivingState_Ready, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Dying, getCell(actualData, 2).livingState);

if (GetParam() == CellDeathConsquences_None) {
EXPECT_EQ(LivingState_Ready, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Ready, getCell(actualData, 2).livingState);
} else if (GetParam() == CellDeathConsquences_CreatureDies) {
EXPECT_EQ(LivingState_Detaching, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Detaching, getCell(actualData, 2).livingState);
} else if (GetParam() == CellDeathConsquences_DetachedPartsDie) {
EXPECT_EQ(LivingState_Reviving, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Detaching, getCell(actualData, 2).livingState);
}
}

TEST_F(LivingStateTransitionTests, noSelfReplicatingConstructorIsDyingIfAdjacentDying)
TEST_P(LivingStateTransitionTests, ready_detaching_differentCreature)
{
_parameters.cellDeathConsequences = GetParam();
_simulationFacade->setSimulationParameters(_parameters);

DataDescription data;
data.addCells({
CellDescription().setId(1).setPos({10.0f, 10.0f}).setMaxConnections(1).setCellFunction(ConstructorDescription()).setLivingState(LivingState_Ready),
CellDescription().setId(2).setPos({11.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Dying),
CellDescription().setId(1).setPos({10.0f, 10.0f}).setMaxConnections(1).setCreatureId(1).setLivingState(LivingState_Ready),
CellDescription().setId(2).setPos({11.0f, 10.0f}).setMaxConnections(1).setCreatureId(2).setLivingState(LivingState_Detaching),
});
data.addConnection(1, 2);

_simulationFacade->setSimulationData(data);
_simulationFacade->calcTimesteps(1);
auto actualData = _simulationFacade->getSimulationData();
EXPECT_EQ(LivingState_Dying, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Dying, getCell(actualData, 2).livingState);

if (GetParam() == CellDeathConsquences_None) {
EXPECT_EQ(LivingState_Ready, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Ready, getCell(actualData, 2).livingState);
} else if (GetParam() == CellDeathConsquences_CreatureDies) {
EXPECT_EQ(LivingState_Ready, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Detaching, getCell(actualData, 2).livingState);
} else if (GetParam() == CellDeathConsquences_DetachedPartsDie) {
EXPECT_EQ(LivingState_Ready, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Detaching, getCell(actualData, 2).livingState);
}
}

TEST_F(LivingStateTransitionTests, separatingSelfReplicatorIsDyingIfAdjacentDying)
TEST_P(LivingStateTransitionTests, detaching_reviving)
{
auto genome = GenomeDescriptionService::get().convertDescriptionToBytes(
GenomeDescription().setHeader(GenomeHeaderDescription().setSeparateConstruction(true)).setCells({CellGenomeDescription().setCellFunction(ConstructorGenomeDescription().setMakeSelfCopy())}));
_parameters.cellDeathConsequences = GetParam();
_simulationFacade->setSimulationParameters(_parameters);

DataDescription data;
data.addCells({
CellDescription()
.setId(1)
.setPos({10.0f, 10.0f})
.setMaxConnections(1)
.setCellFunction(ConstructorDescription().setGenome(genome))
.setLivingState(LivingState_Ready),
CellDescription().setId(2).setPos({11.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Dying),
CellDescription().setId(1).setPos({10.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Detaching),
CellDescription().setId(2).setPos({11.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Reviving),
});
data.addConnection(1, 2);

_simulationFacade->setSimulationData(data);
_simulationFacade->calcTimesteps(1);
auto actualData = _simulationFacade->getSimulationData();
EXPECT_EQ(LivingState_Dying, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Dying, getCell(actualData, 2).livingState);

if (GetParam() == CellDeathConsquences_None) {
EXPECT_EQ(LivingState_Ready, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Ready, getCell(actualData, 2).livingState);
} else if (GetParam() == CellDeathConsquences_CreatureDies) {
EXPECT_EQ(LivingState_Detaching, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Ready, getCell(actualData, 2).livingState);
} else if (GetParam() == CellDeathConsquences_DetachedPartsDie) {
EXPECT_EQ(LivingState_Reviving, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Ready, getCell(actualData, 2).livingState);
}
}

TEST_F(LivingStateTransitionTests, noSeparatingSelfReplicatorStaysReadyIfAdjacentDying)
TEST_P(LivingStateTransitionTests, underConstruction_activating)
{
auto genome = GenomeDescriptionService::get().convertDescriptionToBytes(
GenomeDescription()
.setHeader(GenomeHeaderDescription().setSeparateConstruction(false))
.setCells({CellGenomeDescription().setCellFunction(ConstructorGenomeDescription().setMakeSelfCopy()), CellGenomeDescription()}));
_parameters.cellDeathConsequences = GetParam();
_simulationFacade->setSimulationParameters(_parameters);

DataDescription data;
data.addCells({
CellDescription()
.setId(1)
.setPos({10.0f, 10.0f})
.setEnergy(_parameters.cellNormalEnergy[0] * 3)
.setMaxConnections(2)
.setCellFunction(ConstructorDescription().setGenome(genome))
.setLivingState(LivingState_Ready),
CellDescription().setId(2).setPos({11.0f, 10.0f}).setMaxConnections(2).setLivingState(LivingState_Ready),
CellDescription().setId(3).setPos({12.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Dying),
CellDescription().setId(1).setPos({10.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_UnderConstruction),
CellDescription().setId(2).setPos({11.0f, 10.0f}).setMaxConnections(1).setLivingState(LivingState_Activating),
});
data.addConnection(1, 2);
data.addConnection(2, 3);

_simulationFacade->setSimulationData(data);
_simulationFacade->calcTimesteps(1);
auto actualData = _simulationFacade->getSimulationData();
auto actualCell1 = getCell(actualData, 1);
auto actualConstructor = std::get<ConstructorDescription>(*actualCell1.cellFunction);
EXPECT_EQ(4, actualData.cells.size());
EXPECT_EQ(1, actualConstructor.genomeCurrentNodeIndex);
EXPECT_EQ(LivingState_Ready, actualCell1.livingState);
EXPECT_EQ(LivingState_Dying, getCell(actualData, 2).livingState);
EXPECT_EQ(LivingState_Activating, getCell(actualData, 1).livingState);
EXPECT_EQ(LivingState_Ready, getCell(actualData, 2).livingState);
}

0 comments on commit 4923126

Please sign in to comment.