Pārlūkot izejas kodu

Merge pull request #211 from Chocimier/iss2321

Tested. Everthing looks fine so merging.
ArseniyShestakov 9 gadi atpakaļ
vecāks
revīzija
b5fa97b697

+ 18 - 0
lib/CGameInfoCallback.cpp

@@ -520,6 +520,24 @@ EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTow
 	if(vstd::contains(t->forbiddenBuildings, ID))
 		return EBuildingState::FORBIDDEN; //forbidden
 
+	auto possiblyNotBuiltTest = [&](BuildingID id) -> bool
+	{
+		return ((id == BuildingID::CAPITOL) ? true : !t->hasBuilt(id));
+	};
+
+	std::function<bool(BuildingID id)> allowedTest = [&](BuildingID id) -> bool
+	{
+		if (vstd::contains(t->forbiddenBuildings, id))
+		{
+			return false;
+		}
+
+		return t->genBuildingRequirements(id, true).satisfiable(allowedTest, possiblyNotBuiltTest);
+	};
+
+	if (!t->genBuildingRequirements(ID, true).satisfiable(allowedTest, possiblyNotBuiltTest))
+		return EBuildingState::FORBIDDEN;
+
 	if(ID == BuildingID::CAPITOL)
 	{
 		const PlayerState *ps = getPlayer(t->tempOwner, false);

+ 147 - 0
lib/LogicalExpression.h

@@ -99,6 +99,129 @@ namespace LogicalExpressionDetail
 		}
 	};
 
+	template <typename ContainedClass>
+	class SatisfiabilityVisitor;
+
+	template <typename ContainedClass>
+	class FalsifiabilityVisitor;
+
+	template <typename ContainedClass>
+	class PossibilityVisitor : public boost::static_visitor<bool>
+	{
+		typedef ExpressionBase<ContainedClass> Base;
+
+	protected:
+		std::function<bool(const typename Base::Value &)> satisfiabilityTest;
+		std::function<bool(const typename Base::Value &)> falsifiabilityTest;
+		SatisfiabilityVisitor<ContainedClass> *satisfiabilityVisitor;
+		FalsifiabilityVisitor<ContainedClass> *falsifiabilityVisitor;
+
+		size_t countSatisfiable(const std::vector<typename Base::Variant> & 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<typename Base::Variant> & element) const
+		{
+			return boost::range::count_if(element, [&](const typename Base::Variant & expr)
+			{
+				return boost::apply_visitor(*falsifiabilityVisitor, expr);
+			});
+		}
+
+	public:
+		PossibilityVisitor(std::function<bool (const typename Base::Value &)> satisfiabilityTest,
+		                   std::function<bool (const typename Base::Value &)> falsifiabilityTest):
+			satisfiabilityTest(satisfiabilityTest),
+			falsifiabilityTest(falsifiabilityTest),
+			satisfiabilityVisitor(nullptr),
+			falsifiabilityVisitor(nullptr)
+		{}
+
+		void setSatisfiabilityVisitor(SatisfiabilityVisitor<ContainedClass> *satisfiabilityVisitor)
+		{
+			this->satisfiabilityVisitor = satisfiabilityVisitor;
+		}
+
+		void setFalsifiabilityVisitor(FalsifiabilityVisitor<ContainedClass> *falsifiabilityVisitor)
+		{
+			this->falsifiabilityVisitor = falsifiabilityVisitor;
+		}
+	};
+
+	/// Visitor to test whether expression's value can be true
+	template <typename ContainedClass>
+	class SatisfiabilityVisitor : public PossibilityVisitor<ContainedClass>
+	{
+		typedef ExpressionBase<ContainedClass> Base;
+
+	public:
+		SatisfiabilityVisitor(std::function<bool (const typename Base::Value &)> satisfiabilityTest,
+		                      std::function<bool (const typename Base::Value &)> falsifiabilityTest):
+			PossibilityVisitor<ContainedClass>(satisfiabilityTest, falsifiabilityTest)
+		{
+			this->setSatisfiabilityVisitor(this);
+		}
+
+		bool operator()(const typename Base::OperatorAny & element) const
+		{
+			return this->countSatisfiable(element.expressions) != 0;
+		}
+
+		bool operator()(const typename Base::OperatorAll & element) const
+		{
+			return this->countSatisfiable(element.expressions) == element.expressions.size();
+		}
+
+		bool operator()(const typename Base::OperatorNone & element) const
+		{
+			return this->countFalsifiable(element.expressions) == element.expressions.size();
+		}
+
+		bool operator()(const typename Base::Value & element) const
+		{
+			return this->satisfiabilityTest(element);
+		}
+	};
+
+	/// Visitor to test whether expression's value can be false
+	template <typename ContainedClass>
+	class FalsifiabilityVisitor : public PossibilityVisitor<ContainedClass>
+	{
+		typedef ExpressionBase<ContainedClass> Base;
+
+	public:
+		FalsifiabilityVisitor(std::function<bool (const typename Base::Value &)> satisfiabilityTest,
+		                      std::function<bool (const typename Base::Value &)> falsifiabilityTest):
+			PossibilityVisitor<ContainedClass>(satisfiabilityTest, falsifiabilityTest)
+		{
+			this->setFalsifiabilityVisitor(this);
+		}
+
+		bool operator()(const typename Base::OperatorAny & element) const
+		{
+			return this->countFalsifiable(element.expressions) == element.expressions.size();
+		}
+
+		bool operator()(const typename Base::OperatorAll & element) const
+		{
+			return this->countFalsifiable(element.expressions) != 0;
+		}
+
+		bool operator()(const typename Base::OperatorNone & element) const
+		{
+			return this->countSatisfiable(element.expressions) != 0;
+		}
+
+		bool operator()(const typename Base::Value & element) const
+		{
+			return this->falsifiabilityTest(element);
+		}
+	};
+
 	/// visitor that is trying to generates candidates that must be fulfilled
 	/// to complete this expression
 	template <typename ContainedClass>
@@ -436,6 +559,30 @@ public:
 		return boost::apply_visitor(testVisitor, data);
 	}
 
+	/// calculates if expression can evaluate to "true".
+	bool satisfiable(std::function<bool(const Value &)> satisfiabilityTest, std::function<bool(const Value &)> falsifiabilityTest) const
+	{
+		LogicalExpressionDetail::SatisfiabilityVisitor<Value> satisfiabilityVisitor(satisfiabilityTest, falsifiabilityTest);
+		LogicalExpressionDetail::FalsifiabilityVisitor<Value> falsifiabilityVisitor(satisfiabilityTest, falsifiabilityTest);
+
+		satisfiabilityVisitor.setFalsifiabilityVisitor(&falsifiabilityVisitor);
+		falsifiabilityVisitor.setSatisfiabilityVisitor(&satisfiabilityVisitor);
+
+		return boost::apply_visitor(satisfiabilityVisitor, data);
+	}
+
+	/// calculates if expression can evaluate to "false".
+	bool falsifiable(std::function<bool(const Value &)> satisfiabilityTest, std::function<bool(const Value &)> falsifiabilityTest) const
+	{
+		LogicalExpressionDetail::SatisfiabilityVisitor<Value> satisfiabilityVisitor(satisfiabilityTest);
+		LogicalExpressionDetail::FalsifiabilityVisitor<Value> 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<Value> getFulfillmentCandidates(std::function<bool(const Value &)> toBool) const
 	{

+ 9 - 4
lib/mapObjects/CGTownInstance.cpp

@@ -1124,7 +1124,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);
 
@@ -1132,17 +1132,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;
 	};
 

+ 1 - 1
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;