view API/Objects/Requirement/RequiresNUnitsForMUnitsRequirement.cs @ 439:5252dfb9cdfb

Re #350: Add requirement to allow N of unit for specific other units * Add code for validating army (based on code for validating adding)
author IBBoard <dev@ibboard.co.uk>
date Wed, 30 Nov 2011 21:06:41 +0000
parents 410f3d85c9c5
children baa34d91031f
line wrap: on
line source

// 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 class RequiresNUnitsForMUnitsRequirement : AbstractRequirement
	{
		public static readonly string REQUIREMENT_ID = "RequiresNUnitsForMUnits";
		private List<UnitCountRequirementData> requiredTypes;
		private UnitType allowedType;

		public RequiresNUnitsForMUnitsRequirement(UnitType allowedType, params UnitType[] requiredUnitTypes)
		{
			this.allowedType = allowedType;
			FailureStringPrefix = "Army must contain: ";
			requiredTypes = new List<UnitCountRequirementData>();

			foreach (UnitType unitType in requiredUnitTypes)
			{
				AddUnitTypeRequirement(unitType);
			}
		}

		public override int GetHashCode()
		{
			int hash = 0;

			foreach (UnitCountRequirementData req in requiredTypes)
			{
				hash += req.UnitType.GetHashCode();
			}

			return hash;
		}

		protected override bool TypeEquals(object obj)
		{
			RequiresNUnitsForMUnitsRequirement otherReq = (RequiresNUnitsForMUnitsRequirement)obj;
			if (!Collections.Collections.AreEqual(requiredTypes, otherReq.requiredTypes))
			{
				return false;
			}
			else
			{
				return true;
			}
		}

		protected string FailureStringPrefix { get; set; }

		protected override string GetAllowsAddingFailedMessage(UnitType toAdd, Army toArmy)
		{
			StringBuilder sb = new StringBuilder();
			sb.Append(FailureStringPrefix);
			sb.Append(String.Join("; ", GetFailedAddingRequirements(toAdd, toArmy).ToArray()));
			sb.Append(".");
			return sb.ToString();
		}

		private List<string> GetFailedAddingRequirements(UnitType unitType, Army toArmy)
		{
			List<string> failures = new List<string>();
			int allowedTypeCount = GetUnitTypeCount(toArmy, allowedType, unitType);

			foreach (UnitCountRequirementData limit in requiredTypes)
			{
				int limitedTypeCount = GetUnitTypeCount(toArmy, limit.UnitType, unitType);

				if (!IsValidByRequirement(unitType, toArmy, limit, allowedTypeCount))
				{
					failures.Add(String.Format("{0} {1} for every {2} {3} (have {4} for {5})", limit.Count, limit.UnitType.Name, limit.AllowsCount, allowedType.Name, limitedTypeCount, allowedTypeCount));
				}
			}

			return failures;
		}

		private bool IsValidByRequirement(WarFoundryObject wfObject, Army toArmy, UnitCountRequirementData limit, int allowedTypeCount)
		{
			int limitedTypeCount = GetUnitTypeCount(toArmy, limit.UnitType, wfObject);
			return IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount);
		}

		private bool IsValidByRequirement(UnitCountRequirementData limit, int allowedTypeCount, int limitedTypeCount)
		{
			double limitedTypeMultiplier = limitedTypeCount / (limit.Count * 1.0);
			double allowedTypeMultiplier = allowedTypeCount / (limit.AllowsCount * 1.0);
			return allowedTypeMultiplier <= limitedTypeMultiplier;
		}

		public override Validation AllowsAdding(WarFoundryObject wfObject, Army toArmy)
		{
			Validation canAdd = Validation.NotApplicable;
			UnitType addedUnitType = (wfObject is Unit) ? ((Unit)wfObject).UnitType : wfObject as UnitType;
			bool typeFound = (addedUnitType == allowedType);
			int allowedTypeCount = GetUnitTypeCount(toArmy, allowedType, 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 GetUnitTypeCount(Army toArmy, UnitType unitType, WarFoundryObject wfObject)
		{
			return toArmy.GetUnitTypeCount(unitType) + GetCountFromObject(wfObject, unitType);
		}

		private int GetCountFromObject(WarFoundryObject wfObject, UnitType 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 = army.GetUnitTypeCount(allowedType);
			bool typeFound = (allowedTypeCount > 0);

			foreach (UnitCountRequirementData limit in requiredTypes)
			{
				int limitedTypeCount = army.GetUnitTypeCount(limit.UnitType);
				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 GetValidationFailedMessage(Army army)
		{
			StringBuilder sb = new StringBuilder();
			sb.Append(FailureStringPrefix);
			sb.Append(String.Join("; ", GetFailedValidationRequirements(army).ToArray()));
			sb.Append(".");
			return sb.ToString();
		}

		private List<string> GetFailedValidationRequirements(Army army)
		{
			List<string> failures = new List<string>();
			int allowedTypeCount = army.GetUnitTypeCount(allowedType);

			foreach (UnitCountRequirementData limit in requiredTypes)
			{
				int limitedTypeCount =  army.GetUnitTypeCount(limit.UnitType);

				if (!IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount))
				{
					failures.Add(String.Format("{0} {1} for every {2} {3} (have {4} for {5})", limit.Count, limit.UnitType.Name, limit.AllowsCount, allowedType.Name, limitedTypeCount, allowedTypeCount));
				}
			}

			return failures;
		}

		public override string RequirementID
		{
			get
			{
				return REQUIREMENT_ID;
			}
		}

		/// <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>
		public void AddUnitTypeRequirement(UnitType unitType, int minCount, int allowedCount)
		{
			requiredTypes.Add(new UnitCountRequirementData(unitType, minCount, allowedCount));
		}

		/// <summary>
		/// Adds a requirement for there to be one or more of a given UnitType, allowing one of this UnitType
		/// </summary>
		/// <param name='unitType'>
		/// The unit type to require.
		/// </param>
		public void AddUnitTypeRequirement(UnitType unitType)
		{
			AddUnitTypeRequirement(unitType, 1, 1);
		}
	}
}