diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index ec519aa7a..cd74085ab 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -520,6 +520,28 @@ EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTow if(vstd::contains(t->forbiddenBuildings, ID)) return EBuildingState::FORBIDDEN; //forbidden + std::function allowedTest; + std::function possiblyNotBuiltTest; + + allowedTest = [&](BuildingID id) -> bool + { + if (vstd::contains(t->forbiddenBuildings, id)) + { + return false; + } + + return t->genBuildingRequirements(id, true).satisfiable(allowedTest, possiblyNotBuiltTest); + }; + + possiblyNotBuiltTest = [&](BuildingID id) -> bool + { + //TODO consider destroing + return !t->hasBuilt(id); + }; + + if (!t->genBuildingRequirements(ID, true).satisfiable(allowedTest, possiblyNotBuiltTest)) + return EBuildingState::FORBIDDEN; + if(ID == BuildingID::CAPITOL) { const PlayerState *ps = getPlayer(t->tempOwner, false); diff --git a/lib/LogicalExpression.h b/lib/LogicalExpression.h index 0c9e94b86..e308fddd7 100644 --- a/lib/LogicalExpression.h +++ b/lib/LogicalExpression.h @@ -99,6 +99,123 @@ namespace LogicalExpressionDetail } }; + template + class FalsifiabilityVisitor; + + /// Visitor to test whether expression's value can be true + template + class SatisfiabilityVisitor : public boost::static_visitor + { + typedef ExpressionBase Base; + + std::function satisfiabilityTest; + FalsifiabilityVisitor *falsifiabilityVisitor; + + size_t countSatisfiable(const std::vector & element) const + { + return boost::range::count_if(element, [&](const typename Base::Variant & expr) + { + return boost::apply_visitor(*this, expr); + }); + } + + size_t countFalsifiable(const std::vector & element) const + { + return boost::range::count_if(element, [&](const typename Base::Variant & expr) + { + return boost::apply_visitor(*falsifiabilityVisitor, expr); + }); + } + + public: + SatisfiabilityVisitor(std::function satisfiabilityTest): + satisfiabilityTest(satisfiabilityTest), + falsifiabilityVisitor(nullptr) + {} + + void setFalsifiabilityVisitor(FalsifiabilityVisitor *falsifiabilityVisitor) + { + this->falsifiabilityVisitor = falsifiabilityVisitor; + } + + bool operator()(const typename Base::OperatorAny & element) const + { + return countSatisfiable(element.expressions) != 0; + } + + bool operator()(const typename Base::OperatorAll & element) const + { + return countSatisfiable(element.expressions) == element.expressions.size(); + } + + bool operator()(const typename Base::OperatorNone & element) const + { + return countFalsifiable(element.expressions) == element.expressions.size(); + } + + bool operator()(const typename Base::Value & element) const + { + return satisfiabilityTest(element); + } + }; + + /// Visitor to test whether expression's value can be false + template + class FalsifiabilityVisitor : public boost::static_visitor + { + typedef ExpressionBase Base; + + std::function falsifiabilityTest; + SatisfiabilityVisitor *satisfiabilityVisitor; + + size_t countSatisfiable(const std::vector & element) const + { + return boost::range::count_if(element, [&](const typename Base::Variant & expr) + { + return boost::apply_visitor(*satisfiabilityVisitor, expr); + }); + } + + size_t countFalsifiable(const std::vector & element) const + { + return boost::range::count_if(element, [&](const typename Base::Variant & expr) + { + return boost::apply_visitor(*this, expr); + }); + } + + public: + FalsifiabilityVisitor(std::function falsifiabilityTest): + falsifiabilityTest(falsifiabilityTest), + satisfiabilityVisitor(nullptr) + {} + + void setFalsifiabilityVisitor(SatisfiabilityVisitor *satisfiabilityVisitor) + { + this->satisfiabilityVisitor = satisfiabilityVisitor; + } + + bool operator()(const typename Base::OperatorAny & element) const + { + return countFalsifiable(element.expressions) == element.expressions.size(); + } + + bool operator()(const typename Base::OperatorAll & element) const + { + return countFalsifiable(element.expressions) != 0; + } + + bool operator()(const typename Base::OperatorNone & element) const + { + return countSatisfiable(element.expressions) != 0; + } + + bool operator()(const typename Base::Value & element) const + { + return falsifiabilityTest(element); + } + }; + /// visitor that is trying to generates candidates that must be fulfilled /// to complete this expression template @@ -436,6 +553,30 @@ public: return boost::apply_visitor(testVisitor, data); } + /// calculates if expression can evaluate to "true". + bool satisfiable (std::function satisfiabilityTest, std::function falsifiabilityTest) const + { + LogicalExpressionDetail::SatisfiabilityVisitor satisfiabilityVisitor(satisfiabilityTest); + LogicalExpressionDetail::FalsifiabilityVisitor falsifiabilityVisitor(falsifiabilityTest); + + satisfiabilityVisitor.setFalsifiabilityVisitor(&falsifiabilityVisitor); + falsifiabilityVisitor.setFalsifiabilityVisitor(&satisfiabilityVisitor); + + return boost::apply_visitor(satisfiabilityVisitor, data); + } + + /// calculates if expression can evaluate to "false". + bool falsifiable(std::function satisfiabilityTest, std::function falsifiabilityTest) const + { + LogicalExpressionDetail::SatisfiabilityVisitor satisfiabilityVisitor(satisfiabilityTest); + LogicalExpressionDetail::FalsifiabilityVisitor falsifiabilityVisitor(falsifiabilityTest); + + satisfiabilityVisitor.setFalsifiabilityVisitor(&falsifiabilityVisitor); + falsifiabilityVisitor.setFalsifiabilityVisitor(&satisfiabilityVisitor); + + return boost::apply_visitor(falsifiabilityVisitor, data); + } + /// generates list of candidates that can be fulfilled by caller (like AI) std::vector getFulfillmentCandidates(std::function toBool) const { diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index f15707f65..410aa9499 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -1126,7 +1126,7 @@ bool CGTownInstance::hasBuilt(BuildingID buildingID) const return vstd::contains(builtBuildings, buildingID); } -CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID) const +CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID, bool deep) const { const CBuilding * building = town->buildings.at(buildID); @@ -1134,17 +1134,22 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID) [&](const BuildingID & id) -> CBuilding::TRequired::Variant { const CBuilding * build = town->buildings.at(id); + CBuilding::TRequired::OperatorAll requirements; if (!hasBuilt(id)) - return id; + { + requirements.expressions.push_back(id); - CBuilding::TRequired::OperatorAll requirements; + if (!deep) + { + return requirements; + } + } if (build->upgrade != BuildingID::NONE) requirements.expressions.push_back(dependTest(build->upgrade)); requirements.expressions.push_back(build->requirements.morph(dependTest)); - return requirements; }; diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 620b37794..ef31c8843 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -238,7 +238,7 @@ public: bool armedGarrison() const; //true if town has creatures in garrison or garrisoned hero int getTownLevel() const; - CBuilding::TRequired genBuildingRequirements(BuildingID build) const; + CBuilding::TRequired genBuildingRequirements(BuildingID build, bool deep = false) const; void mergeGarrisonOnSiege() const; // merge garrison into army of visiting hero void removeCapitols (PlayerColor owner) const;