Mercurial > repos > IBDev-IBBoard.WarFoundry.API
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)); + } + } +} +