diff API/Objects/Requirement/RequiresNUnitsForMObjectsRequirement.cs @ 455:afc6410e4efc

Re #379: Fix validation of requirements to check for unit * Move to "Unit" requirements, since we assume things depend on units * Rename some classes to more meaningful names from unit-based change * Rename "requires N for M" requirement as we can make it more flexible
author IBBoard <dev@ibboard.co.uk>
date Wed, 22 Feb 2012 20:45:39 +0000
parents
children 52baffdd2ab9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Objects/Requirement/RequiresNUnitsForMObjectsRequirement.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -0,0 +1,281 @@
+// This file(RequiresNUnitsForMUnitsRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2011 IBBoard
+// 
+// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or(at your option) any later version. Please see COPYING for more information and the full license.
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace IBBoard.WarFoundry.API.Objects.Requirement
+{
+	public abstract class RequiresNUnitsForMObjectsRequirement<OBJECT_TYPE> : AbstractUnitRequirement<OBJECT_TYPE> where OBJECT_TYPE : IWarFoundryObject
+	{
+		public RequiresNUnitsForMObjectsRequirement(OBJECT_TYPE allowedObject, params UnitType[] requiredUnitTypes) : base(allowedObject, requiredUnitTypes)
+		{
+			//Do nothing
+		}
+
+		public override int GetHashCode()
+		{
+			int hash = 0;
+
+			foreach (UnitCountRequirementData req in RequiredTypes)
+			{
+				hash += req.UnitType.GetHashCode();
+			}
+
+			return hash;
+		}
+
+		protected override bool TypeEquals(object obj)
+		{
+			RequiresNUnitsForMObjectsRequirement<OBJECT_TYPE> otherReq = (RequiresNUnitsForMObjectsRequirement<OBJECT_TYPE>)obj;
+			if (!Collections.Collections.AreEqual(RequiredTypes, otherReq.RequiredTypes))
+			{
+				return false;
+			}
+			else
+			{
+				return true;
+			}
+		}
+
+		protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy)
+		{
+			return String.Join("; ", GetFailedAddingRequirements(toAdd, toArmy).ToArray());
+		}
+
+		private List<string> GetFailedAddingRequirements(IWarFoundryObject obj, Army toArmy)
+		{
+			List<string> failures = new List<string>();
+			int allowedObjectCount = GetAllowedObjectCount(toArmy, AllowedObject, obj);
+
+			foreach (UnitCountRequirementData limit in RequiredTypes)
+			{
+				int limitedTypeCount = GetUnitTypesCount(toArmy, limit.UnitTypes, obj);
+
+				if (!IsValidByRequirement(obj, toArmy, limit, allowedObjectCount))
+				{
+					string unitTypeList = GetUnitTypeList(limit);
+					failures.Add(String.Format("{0} × {1} for every {2} × {3} (would have {4} for {5})", limit.Count, unitTypeList, limit.AllowsCount, AllowedObject.Name, limitedTypeCount, allowedObjectCount));
+				}
+			}
+
+			return failures;
+		}
+
+		private string GetUnitTypeList(UnitCountRequirementData limit)
+		{
+			List<String> namesList = new List<String>();
+
+			foreach (UnitType unitType in limit.UnitTypes)
+			{
+				namesList.Add(unitType.Name);
+			}
+
+			string[] names = namesList.ToArray();
+			return String.Join(" or ", names);
+		}
+
+		private bool IsValidByRequirement(IWarFoundryObject wfObject, Army toArmy, UnitCountRequirementData limit, int allowedTypeCount)
+		{
+			int limitedTypeCount = GetUnitTypesCount(toArmy, limit.UnitTypes, wfObject);
+			return IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount);
+		}
+
+		private bool IsValidByRequirement(UnitCountRequirementData limit, int allowedTypeCount, int limitedTypeCount)
+		{
+			int wholeNumLimited = (limitedTypeCount / limit.Count);
+			double allowedRatio = (limit.AllowsCount / (limit.Count * 1.0));
+			return allowedRatio * wholeNumLimited >= allowedTypeCount;
+		}
+
+		public override Validation AllowsAdding(IWarFoundryObject wfObject, Army toArmy)
+		{
+			Validation canAdd = Validation.NotApplicable;
+			UnitType addedUnitType = (wfObject is Unit) ? ((Unit)wfObject).UnitType : wfObject as UnitType;
+			bool typeFound = (wfObject == (IWarFoundryObject)AllowedObject);
+			int allowedTypeCount = GetAllowedObjectCount(toArmy, AllowedObject, wfObject);
+
+			foreach (UnitCountRequirementData limit in RequiredTypes)
+			{
+				typeFound |= (addedUnitType == limit.UnitType);
+
+				if (!IsValidByRequirement(wfObject, toArmy, limit, allowedTypeCount))
+				{
+					canAdd = Validation.Failed;
+					break;
+				}
+			}
+
+			if (typeFound && canAdd == Validation.NotApplicable)
+			{
+				canAdd = Validation.Passed;
+			}
+
+			return canAdd;
+		}
+
+		private int GetUnitTypesCount(Army toArmy, UnitType[] unitTypes, IWarFoundryObject wfObject)
+		{
+			int count = 0;
+
+			foreach (UnitType unitType in unitTypes)
+			{
+				count += GetUnitTypeCount(toArmy, unitType, wfObject);
+			}
+
+			return count;
+		}
+
+		private int GetAllowedObjectCount(Army toArmy, OBJECT_TYPE obj, IWarFoundryObject wfObject)
+		{
+			return GetObjectCountFromArmy(toArmy, obj) + GetCountFromObject(wfObject, obj);
+		}
+
+		protected abstract int GetObjectCountFromArmy(Army toArmy, OBJECT_TYPE obj);
+
+		private int GetCountFromObject(IWarFoundryObject wfObject, OBJECT_TYPE limitedType)
+		{
+			return (limitedType.Equals(wfObject) || (wfObject is Unit && ((Unit)wfObject).UnitType.Equals(limitedType))) ? 1 : 0;
+		}
+
+		public override Validation ValidatesArmy(Army army)
+		{
+			Validation canAdd = Validation.NotApplicable;
+			int allowedTypeCount = GetObjectCountFromArmy(army, AllowedObject);
+			bool typeFound = (allowedTypeCount > 0);
+
+			foreach (UnitCountRequirementData limit in RequiredTypes)
+			{
+				int limitedTypeCount = GetUnitTypesCount(army, limit.UnitTypes);
+				typeFound |= (limitedTypeCount > 0);
+
+				if (!IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount))
+				{
+					canAdd = Validation.Failed;
+					break;
+				}
+			}
+
+			if (typeFound && canAdd == Validation.NotApplicable)
+			{
+				canAdd = Validation.Passed;
+			}
+
+			return canAdd;
+		}
+
+		protected override string GetFailedRequirementsString(Army army)
+		{
+			return String.Join("; ", GetFailedValidationRequirements(army).ToArray());
+		}
+
+		private List<string> GetFailedValidationRequirements(Army army)
+		{
+			List<string> failures = new List<string>();
+			int allowedTypeCount = GetAllowedObjectCount(army);
+
+			foreach (UnitCountRequirementData limit in RequiredTypes)
+			{
+				int limitedTypeCount = GetUnitTypesCount(army, limit.UnitTypes);
+
+				if (!IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount))
+				{
+					string unitTypeList = GetUnitTypeList(limit);
+					failures.Add(String.Format("{0} × {1} for every {2} × {3} (have {4} for {5})", limit.Count, unitTypeList, limit.AllowsCount, AllowedObject.Name, limitedTypeCount, allowedTypeCount));
+				}
+			}
+
+			return failures;
+		}
+
+		protected abstract int GetAllowedObjectCount(Army army);
+
+		private int GetUnitTypesCount(Army army, UnitType[] unitTypes)
+		{
+			return GetUnitTypesCount(army, unitTypes, null);
+		}
+
+		/// <summary>
+		/// Adds a requirement for there to be at least minCount of a given UnitType, allowing allowedCount of this UnitType
+		/// </summary>
+		/// <param name='unitType'>
+		/// The unit type to require.
+		/// </param>
+		/// <param name='minCount'>
+		/// The minimum number of that type that must exist.
+		/// </param>
+		/// <param name='allowedCount'>
+		/// The number of units allowed for every minCount units of the supplied unit type.
+		/// </param>
+		public void AddUnitTypeRequirement(UnitType unitType, int minCount, int allowedCount)
+		{
+			RequiredTypes.Add(new UnitCountRequirementData(unitType, minCount, allowedCount));
+		}
+
+		/// <summary>
+		/// Adds a requirement for there to be at least one of a given UnitType, allowing allowedCount of this UnitType
+		/// </summary>
+		/// <param name='unitType'>
+		/// The unit type to require.
+		/// </param>
+		/// <param name='allowedCount'>
+		/// The number of units allowed for each unit of the supplied unit type.
+		/// </param>
+		public void AddUnitTypeRequirement(UnitType unitType, int allowedCount)
+		{
+			AddUnitTypeRequirement(allowedCount, unitType);
+		}
+
+		public override void AddUnitTypeRequirement(UnitType unitType)
+		{
+			AddUnitTypeRequirement(1, unitType);
+		}
+
+		/// <summary>
+		/// Adds a requirement for there to be one or more of the given UnitTypes, allowing one of this UnitType. If multiple unit types
+		/// are supplied here then the number is additive (so 1 x unitType1 and 1 x unitType2 allows two of this UnitType).
+		/// </summary>
+		/// <param name='unitType'>
+		/// The unit type or types to require.
+		/// </param>
+		public void AddUnitTypeRequirement(params UnitType[] unitTypes)
+		{
+			AddUnitTypeRequirement(1, unitTypes);
+		}
+
+		/// <summary>
+		/// Adds a requirement for there to be one or more of the given UnitTypes, allowing allowsAmount of this UnitType. If multiple unit types
+		/// are supplied here then the number is additive (so 1 x unitType1 and 1 x unitType2 allows two of this UnitType).
+		/// </summary>
+		/// <param name="allowsAmount">
+		/// The number of units to be allowed for each 1 of unitType
+		/// </param>
+		/// <param name='unitType'>
+		/// The unit type or types to require.
+		/// </param>
+		public void AddUnitTypeRequirement(int allowsAmount, params UnitType[] unitTypes)
+		{
+			RequiredTypes.Add(new UnitCountRequirementData(unitTypes, 1, allowsAmount));
+		}
+
+		/// <summary>
+		/// Adds a requirement for there to be minCount or more of the given UnitTypes, allowing allowsAmount of this UnitType. If multiple unit types
+		/// are supplied here then the number is additive (so 1 x unitType1 and 1 x unitType2 allows two of this UnitType).
+		/// </summary>
+		/// <param name='minCount'>
+		/// The minimum number of that type that must exist.
+		/// </param>
+		/// <param name="allowsAmount">
+		/// The number of units to be allowed for each 1 of unitType
+		/// </param>
+		/// <param name='unitType'>
+		/// The unit type or types to require.
+		/// </param>
+		public void AddUnitTypeRequirement(int minCount, int allowsAmount, params UnitType[] unitTypes)
+		{
+			RequiredTypes.Add(new UnitCountRequirementData(unitTypes, minCount, allowsAmount));
+		}
+	}
+}
+