Skip to content

Commit

Permalink
Fixing boundary conditions (#189)
Browse files Browse the repository at this point in the history
* fixing boundary conditions

* fixing bugglies

* more tests
  • Loading branch information
Bam4d authored Apr 28, 2022
1 parent 75f92a7 commit b71ff1f
Show file tree
Hide file tree
Showing 22 changed files with 153 additions and 69 deletions.
Binary file modified python/examples/Level Design/custom_level_global.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified python/examples/Level Design/custom_level_string_player_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified python/examples/Level Design/custom_level_string_player_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified python/examples/Level Design/level_0_global.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified python/examples/Level Design/level_1_global.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified python/examples/Level Design/level_1_player_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified python/examples/Level Design/level_1_player_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified python/examples/Level Design/level_2_global.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified python/examples/Level Design/level_2_player_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified python/examples/Level Design/level_2_player_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 11 additions & 3 deletions src/Griddly/Core/GDY/Actions/Action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,29 +68,37 @@ std::shared_ptr<Object> Action::getSourceObject() const {

spdlog::debug("getting default object");

return grid()->getPlayerDefaultObject(playerId_);
return grid()->getPlayerDefaultEmptyObject(playerId_);
}
}

std::shared_ptr<Object> Action::getDestinationObject() const {
switch (actionMode_) {
case ActionMode::SRC_LOC_DST_LOC:
case ActionMode::SRC_OBJ_DST_LOC: {
if (destinationLocation_.x >= grid()->getWidth() || destinationLocation_.x < 0 ||
destinationLocation_.y >= grid()->getHeight() || destinationLocation_.y < 0) {
return grid()->getPlayerDefaultBoundaryObject(playerId_);
}
auto dstObject = grid()->getObject(destinationLocation_);
if (dstObject != nullptr) {
return dstObject;
}
return grid()->getPlayerDefaultObject(playerId_);
return grid()->getPlayerDefaultEmptyObject(playerId_);
}
case ActionMode::SRC_OBJ_DST_OBJ:
return destinationObject_;
case ActionMode::SRC_OBJ_DST_VEC: {
auto destinationLocation = (getSourceLocation() + vectorToDest_);
if (destinationLocation.x >= grid()->getWidth() || destinationLocation.x < 0 ||
destinationLocation.y >= grid()->getHeight() || destinationLocation.y < 0) {
return grid()->getPlayerDefaultBoundaryObject(playerId_);
}
auto dstObject = grid()->getObject(destinationLocation);
if (dstObject != nullptr) {
return dstObject;
}
return grid()->getPlayerDefaultObject(playerId_);
return grid()->getPlayerDefaultEmptyObject(playerId_);
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/Griddly/Core/GDY/Objects/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ BehaviourResult Object::onActionDst(std::shared_ptr<Action> action, std::vector<
auto &behavioursForAction = behavioursForActionIt->second;

auto behavioursForActionAndDestinationObject = behavioursForAction.find(sourceObjectName);

if (behavioursForActionAndDestinationObject == behavioursForAction.end()) {
spdlog::debug("Aborting dst behaviour, (no behaviours for action)", action->getDescription());
return {true};
Expand Down Expand Up @@ -708,6 +709,7 @@ std::vector<uint32_t> Object::getValidBehaviourIdxs(std::shared_ptr<Action> acti
auto width = grid()->getWidth();
auto height = grid()->getHeight();


// Check that the destination of the action is not outside the grid
auto destinationLocation = action->getDestinationLocation();
if (destinationLocation.x >= width || destinationLocation.x < 0 ||
Expand Down
44 changes: 18 additions & 26 deletions src/Griddly/Core/Grid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ void Grid::reset() {
objectIds_.clear();
objectVariableIds_.clear();
delayedActions_ = {};
defaultObject_.clear();
defaultEmptyObject_.clear();
defaultBoundaryObject_.clear();

collisionObjectActionNames_.clear();
collisionSourceObjectActionNames_.clear();
Expand Down Expand Up @@ -218,15 +219,7 @@ std::unordered_map<uint32_t, int32_t> Grid::executeAction(uint32_t playerId, std
auto destinationObject = action->getDestinationObject();

// Need to get this name before anything happens to the object for example if the object is removed in onActionDst.
auto originalDestinationObjectName = destinationObject->getObjectName();
if (originalDestinationObjectName == "_empty") {
// Check that the destination of the action is not outside the grid
auto destinationLocation = action->getDestinationLocation();
if (destinationLocation.x >= width_ || destinationLocation.x < 0 ||
destinationLocation.y >= height_ || destinationLocation.y < 0) {
originalDestinationObjectName = "_boundary";
}
}
auto destinationObjectName = destinationObject->getObjectName();

if (sourceObject == nullptr) {
spdlog::debug("Cannot perform action on empty space. ({0},{1})", action->getSourceLocation()[0], action->getSourceLocation()[1]);
Expand Down Expand Up @@ -259,7 +252,7 @@ std::unordered_map<uint32_t, int32_t> Grid::executeAction(uint32_t playerId, std
return rewardAccumulator;
}

auto srcBehaviourResult = sourceObject->onActionSrc(originalDestinationObjectName, action, validBehaviourIdxs);
auto srcBehaviourResult = sourceObject->onActionSrc(destinationObjectName, action, validBehaviourIdxs);
accumulateRewards(rewardAccumulator, srcBehaviourResult.rewards);
return rewardAccumulator;
}
Expand All @@ -276,14 +269,6 @@ GridEvent Grid::buildGridEvent(const std::shared_ptr<Action>& action, uint32_t p
event.actionName = action->getActionName();
event.sourceObjectName = sourceObject->getObjectName();
event.destObjectName = destObject->getObjectName();
if (event.destObjectName == "_empty") {
// Check that the destination of the action is not outside the grid
auto destinationLocation = action->getDestinationLocation();
if (destinationLocation.x >= width_ || destinationLocation.x < 0 ||
destinationLocation.y >= height_ || destinationLocation.y < 0) {
event.destObjectName = "_boundary";
}
}

if (sourceObject->getObjectName() != "_empty") {
event.sourceObjectPlayerId = sourceObject->getPlayerId();
Expand Down Expand Up @@ -553,17 +538,24 @@ void Grid::addActionTrigger(std::string actionName, ActionTriggerDefinition acti
addCollisionDetector(objectNames, actionName, collisionDetector);
}

void Grid::addPlayerDefaultObject(std::shared_ptr<Object> object) {
spdlog::debug("Adding default object for player {0}", object->getPlayerId());
void Grid::addPlayerDefaultObjects(std::shared_ptr<Object> emptyObject, std::shared_ptr<Object> boundaryObject) {
spdlog::debug("Adding default objects for player {0}", emptyObject->getPlayerId());

emptyObject->init({-1, -1});
boundaryObject->init({-1,-1});

object->init({-1, -1});
defaultEmptyObject_[emptyObject->getPlayerId()] = emptyObject;
defaultBoundaryObject_[boundaryObject->getPlayerId()] = boundaryObject;
}

defaultObject_[object->getPlayerId()] = object;
std::shared_ptr<Object> Grid::getPlayerDefaultEmptyObject(uint32_t playerId) const {
spdlog::debug("Getting default empty object for player {0}", playerId);
return defaultEmptyObject_.at(playerId);
}

std::shared_ptr<Object> Grid::getPlayerDefaultObject(uint32_t playerId) const {
spdlog::debug("Getting default object for player {0}", playerId);
return defaultObject_.at(playerId);
std::shared_ptr<Object> Grid::getPlayerDefaultBoundaryObject(uint32_t playerId) const {
spdlog::debug("Getting default boundary object for player {0}", playerId);
return defaultBoundaryObject_.at(playerId);
}

void Grid::addObject(glm::ivec2 location, std::shared_ptr<Object> object, bool applyInitialActions, std::shared_ptr<Action> originatingAction, DiscreteOrientation orientation) {
Expand Down
13 changes: 10 additions & 3 deletions src/Griddly/Core/Grid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,11 @@ class Grid : public std::enable_shared_from_this<Grid> {

virtual const std::unordered_set<std::shared_ptr<Object>>& getObjects();

virtual void addPlayerDefaultObject(std::shared_ptr<Object> object);
virtual std::shared_ptr<Object> getPlayerDefaultObject(uint32_t playerId) const;
virtual void addPlayerDefaultObjects(std::shared_ptr<Object> emptyObject, std::shared_ptr<Object> boundaryObject);

virtual std::shared_ptr<Object> getPlayerDefaultEmptyObject(uint32_t playerId) const;

virtual std::shared_ptr<Object> getPlayerDefaultBoundaryObject(uint32_t playerId) const;

/**
* Gets all the objects at a certain location
Expand Down Expand Up @@ -232,7 +235,11 @@ class Grid : public std::enable_shared_from_this<Grid> {

// An object that is used if the source of destination location of an action is '_empty'
// Allows a subset of actions like "spawn" to be performed in empty space.
std::unordered_map<uint32_t, std::shared_ptr<Object>> defaultObject_;
std::unordered_map<uint32_t, std::shared_ptr<Object>> defaultEmptyObject_;

// An object that is used if the source of destination location of an action is '_boundary'
// This is used for special actions where objects go out of bounds for the level
std::unordered_map<uint32_t, std::shared_ptr<Object>> defaultBoundaryObject_;

std::shared_ptr<RandomGenerator> randomGenerator_ = std::make_shared<RandomGenerator>(RandomGenerator());

Expand Down
5 changes: 3 additions & 2 deletions src/Griddly/Core/LevelGenerators/MapGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ void MapGenerator::reset(std::shared_ptr<Grid> grid) {
}

for (auto playerId = 0; playerId < playerCount_ + 1; playerId++) {
auto defaultObject = objectGenerator_->newInstance("_empty", playerId, grid);
grid->addPlayerDefaultObject(defaultObject);
auto defaultEmptyObject = objectGenerator_->newInstance("_empty", playerId, grid);
auto defaultBoundaryObject = objectGenerator_->newInstance("_boundary", playerId, grid);
grid->addPlayerDefaultObjects(defaultEmptyObject, defaultBoundaryObject);
}

for (auto& actionTriggerDefinitionIt : objectGenerator_->getActionTriggerDefinitions()) {
Expand Down
11 changes: 7 additions & 4 deletions src/Griddly/Core/TurnBasedGameProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,15 @@ std::shared_ptr<TurnBasedGameProcess> TurnBasedGameProcess::clone() {

// Adding player default objects
for (auto playerId = 0; playerId < players_.size() + 1; playerId++) {
auto defaultObject = objectGenerator->newInstance("_empty", playerId, clonedGrid);
clonedGrid->addPlayerDefaultObject(defaultObject);
auto defaultEmptyObject = objectGenerator->newInstance("_empty", playerId, clonedGrid);
auto defaultBoundaryObject = objectGenerator->newInstance("_boundary", playerId, clonedGrid);
clonedGrid->addPlayerDefaultObjects(defaultEmptyObject, defaultBoundaryObject);

auto defaultObjectToCopy = grid_->getPlayerDefaultObject(playerId);
auto defaultEmptyObjectToCopy = grid_->getPlayerDefaultEmptyObject(playerId);
auto defaultBoundaryObjectToCopy = grid_->getPlayerDefaultBoundaryObject(playerId);

clonedObjectMapping[defaultObjectToCopy] = defaultObject;
clonedObjectMapping[defaultEmptyObjectToCopy] = defaultEmptyObject;
clonedObjectMapping[defaultBoundaryObjectToCopy] = defaultBoundaryObject;
}

// Behaviour probabilities
Expand Down
26 changes: 20 additions & 6 deletions tests/src/Griddly/Core/GDY/GDYFactoryTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,8 @@ TEST(GDYFactoryTest, wallTest) {

auto mockWall2Object = std::make_shared<MockObject>();
auto mockWall16Object = std::make_shared<MockObject>();
auto mockDefaultObject = std::make_shared<MockObject>();
auto mockDefaultEmptyObject = std::make_shared<MockObject>();
auto mockDefaultBoundaryObject = std::make_shared<MockObject>();

std::string wall2String = "Wall2";
std::string wall16String = "Wall16";
Expand All @@ -1141,10 +1142,16 @@ TEST(GDYFactoryTest, wallTest) {
.WillRepeatedly(ReturnRef(wall16String));

EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_empty"), Eq(1), Eq(grid)))
.WillRepeatedly(Return(mockDefaultObject));
.WillRepeatedly(Return(mockDefaultEmptyObject));

EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_empty"), Eq(0), Eq(grid)))
.WillRepeatedly(Return(mockDefaultObject));
.WillRepeatedly(Return(mockDefaultEmptyObject));

EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_boundary"), Eq(1), Eq(grid)))
.WillRepeatedly(Return(mockDefaultBoundaryObject));

EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_boundary"), Eq(0), Eq(grid)))
.WillRepeatedly(Return(mockDefaultBoundaryObject));

EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq(wall2String), Eq(0), Eq(grid)))
.WillRepeatedly(Return(mockWall2Object));
Expand All @@ -1171,7 +1178,8 @@ TEST(GDYFactoryTest, zIndexTest) {
auto mockFloorObject = std::make_shared<MockObject>();
auto mockGhostObject = std::make_shared<MockObject>();

auto mockDefaultObject = std::make_shared<MockObject>();
auto mockDefaultEmptyObject = std::make_shared<MockObject>();
auto mockDefaultBoundaryObject = std::make_shared<MockObject>();

std::string wall = "Wall2";
std::string floor = "floor";
Expand Down Expand Up @@ -1201,10 +1209,16 @@ TEST(GDYFactoryTest, zIndexTest) {
.WillRepeatedly(ReturnRef(floor));

EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_empty"), Eq(1), Eq(grid)))
.WillRepeatedly(Return(mockDefaultObject));
.WillRepeatedly(Return(mockDefaultEmptyObject));

EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_empty"), Eq(0), Eq(grid)))
.WillRepeatedly(Return(mockDefaultObject));
.WillRepeatedly(Return(mockDefaultEmptyObject));

EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_boundary"), Eq(1), Eq(grid)))
.WillRepeatedly(Return(mockDefaultBoundaryObject));

EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_boundary"), Eq(0), Eq(grid)))
.WillRepeatedly(Return(mockDefaultBoundaryObject));

EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq(wall), Eq(0), Eq(grid)))
.WillRepeatedly(Return(mockWallObject));
Expand Down
6 changes: 4 additions & 2 deletions tests/src/Griddly/Core/GameProcessTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,8 @@ TEST(GameProcessTest, clone) {
auto clonedMockObject2 = mockObject("object2", 'b', 1, 0, {4, 6}, DiscreteOrientation(), {}, {{"global_var", globalVar}, {"test_param2", _V(5)}});
auto clonedMockObject3 = mockObject("object3", 'c', 1, 0, {20, 13}, DiscreteOrientation(), {}, {{"global_var", globalVar}, {"test_param3", _V(12)}});

auto mockPlayerDefaultObject = mockObject("_empty", ' ', 1, 0, {-1, -1}, DiscreteOrientation(), {}, {});
auto mockPlayerDefaultEmptyObject = mockObject("_empty", ' ', 1, 0, {-1, -1}, DiscreteOrientation(), {}, {});
auto mockPlayerDefaultBoundaryObject = mockObject("_boundary", ' ', 1, 0, {-1, -1}, DiscreteOrientation(), {}, {});

auto objects = std::unordered_set<std::shared_ptr<Object>>{mockObject1, mockObject2, mockObject3};

Expand Down Expand Up @@ -891,7 +892,8 @@ TEST(GameProcessTest, clone) {
EXPECT_CALL(*mockObjectGeneratorPtr, cloneInstance(Eq(mockObject1), _)).WillRepeatedly(Return(clonedMockObject1));
EXPECT_CALL(*mockObjectGeneratorPtr, cloneInstance(Eq(mockObject2), _)).WillRepeatedly(Return(clonedMockObject2));
EXPECT_CALL(*mockObjectGeneratorPtr, cloneInstance(Eq(mockObject3), _)).WillRepeatedly(Return(clonedMockObject3));
EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_empty"), _, _)).WillRepeatedly(Return(mockPlayerDefaultObject));
EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_empty"), _, _)).WillRepeatedly(Return(mockPlayerDefaultEmptyObject));
EXPECT_CALL(*mockObjectGeneratorPtr, newInstance(Eq("_boundary"), _, _)).WillRepeatedly(Return(mockPlayerDefaultBoundaryObject));

EXPECT_CALL(*mockGDYFactoryPtr, getObjectGenerator()).WillRepeatedly(Return(mockObjectGeneratorPtr));

Expand Down
2 changes: 1 addition & 1 deletion tests/src/Griddly/Core/GridTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ TEST(GridTest, performActionDestinationObjectOutsideGrid) {
grid->setBehaviourProbabilities(behaviourProbabilities);


auto mockActionPtr = mockAction("action", mockSourceObjectPtr, actionDestinationLocation);
auto mockActionPtr = mockAction("action", mockSourceObjectPtr, actionDestinationLocation, true);

EXPECT_CALL(*mockSourceObjectPtr, onActionSrc(Eq("_boundary"), Eq(mockActionPtr), UnorderedElementsAre(0)))
.Times(1)
Expand Down
Loading

0 comments on commit b71ff1f

Please sign in to comment.