static float evaluateAssignments(Dictionary <AbstractActor, UnitRole> unitAssignments, List <AbstractActor> allUnits, Dictionary <UnitRoleAssignmentRecord, float> roleAssignmentDictionary) { float penalty = getLegalTeamCountPenalty(countRolesAfterAssignment(unitAssignments, allUnits), allUnits); if (penalty > 0) { return(-penalty); } float score = 0.0f; for (int unitIndex = 0; unitIndex < allUnits.Count; ++unitIndex) { AbstractActor unit = allUnits[unitIndex]; UnitRole role = unit.DynamicUnitRole; if ((unitAssignments != null) && (unitAssignments.ContainsKey(unit))) { role = unitAssignments[unit]; } UnitRoleAssignmentRecord assignmentRecord = new UnitRoleAssignmentRecord(unit, role); // might not actually be there, e.g. if the unit is currently unassigned. if (roleAssignmentDictionary.ContainsKey(assignmentRecord)) { score += roleAssignmentDictionary[assignmentRecord]; } } return(score); }
public static void AssignRoleToUnit(AbstractActor unit, List <AbstractActor> otherUnits) { if (unit.BehaviorTree.HasPriorityTargets() || (!unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Bool_UseDynamicLanceRoles).BoolVal)) { // don't assign dynamic roles when there's a priority target unit.DynamicUnitRole = UnitRole.Undefined; return; } if ((unit.StaticUnitRole == UnitRole.Turret) || (unit.StaticUnitRole == UnitRole.Vehicle && (!UnitIsECMRole(unit) && !UnitIsActiveProbe(unit)))) { // don't assign roles to turrets or vehicles, except if they are ECM vehicles - Allie return; } //Debug.Log("trying to assign role to " + unit.DisplayName); otherUnits = otherUnits.FindAll(x => (x != unit) && (x.StaticUnitRole != UnitRole.Turret) && (x.StaticUnitRole != UnitRole.Vehicle) && (!x.IsDead)); //Debug.Log("other unit count: " + otherUnits.Count); // and now a list of the other interesing units plus our selected unit List <AbstractActor> allUnits = new List <AbstractActor>(); allUnits.Add(unit); for (int unitIndex = 0; unitIndex < otherUnits.Count; ++unitIndex) { allUnits.Add(otherUnits[unitIndex]); } // OK So we do want to give an ECM Carrier role to ECM carrying vehicles.. - Allie if (unit.StaticUnitRole == UnitRole.Vehicle) { Dictionary <AbstractActor, UnitRole> assignmentDict = new Dictionary <AbstractActor, UnitRole>(); if (UnitIsEWE(unit)) { assignmentDict[unit] = UnitRole.Ewe; } else if (UnitIsECMRole(unit)) { assignmentDict[unit] = UnitRole.EcmCarrier; } else if (UnitIsActiveProbe(unit)) { assignmentDict[unit] = UnitRole.ActiveProbe; } else { return; } applyAssignments(assignmentDict, allUnits, true); return; } // lastManStanding is its own thing (sigh) if (UnitIsLastManStandingRole(unit)) { Dictionary <AbstractActor, UnitRole> assignmentDict = new Dictionary <AbstractActor, UnitRole>(); assignmentDict[unit] = UnitRole.LastManStanding; applyAssignments(assignmentDict, allUnits); return; } // noncombatant units are their own thing (sigh) if (UnitIsNonCombatantRole(unit)) { Dictionary <AbstractActor, UnitRole> assignmentDict = new Dictionary <AbstractActor, UnitRole>(); assignmentDict[unit] = UnitRole.NonCombatant; applyAssignments(assignmentDict, allUnits); return; } // melee only units are their own thing (sigh) if (UnitIsMeleeOnlyRole(unit)) { Dictionary <AbstractActor, UnitRole> assignmentDict = new Dictionary <AbstractActor, UnitRole>(); assignmentDict[unit] = UnitRole.MeleeOnly; applyAssignments(assignmentDict, allUnits); return; } if (UnitIsEWE(unit)) { Dictionary <AbstractActor, UnitRole> assignmentDict = new Dictionary <AbstractActor, UnitRole>(); assignmentDict[unit] = UnitRole.Ewe; applyAssignments(assignmentDict, allUnits); return; } if (UnitIsActiveProbe(unit)) { Dictionary <AbstractActor, UnitRole> assignmentDict = new Dictionary <AbstractActor, UnitRole>(); assignmentDict[unit] = UnitRole.ActiveProbe; applyAssignments(assignmentDict, allUnits); return; } if (UnitIsECMRole(unit)) { Dictionary <AbstractActor, UnitRole> assignmentDict = new Dictionary <AbstractActor, UnitRole>(); assignmentDict[unit] = UnitRole.EcmCarrier; applyAssignments(assignmentDict, allUnits); return; } // scouts are their own thing (sigh) if (UnitMustBeScout(unit)) { Dictionary <AbstractActor, UnitRole> assignmentDict = new Dictionary <AbstractActor, UnitRole>(); assignmentDict[unit] = UnitRole.Scout; applyAssignments(assignmentDict, allUnits); return; } // first check to see if we're meeting our minimums. e.g. at startup, we won't. if (!unitsMeetMinimums(allUnits)) { fillOutMinimums(allUnits); } List <Dictionary <AbstractActor, UnitRole> > possibleAssignments = new List <Dictionary <AbstractActor, UnitRole> >(); UnitRole[] dynamicRoles = { UnitRole.Brawler, UnitRole.Sniper, // UnitRole.Scout, // scouts don't use the normal evaluation routines }; // do the raw evaluation, without considering role tags Dictionary <UnitRoleAssignmentRecord, float> unNormalizedRoleEvaluations = new Dictionary <UnitRoleAssignmentRecord, float>(); for (int unitIndex = 0; unitIndex < allUnits.Count; ++unitIndex) { AbstractActor assignUnit = allUnits[unitIndex]; for (int roleIndex = 0; roleIndex < dynamicRoles.Length; ++roleIndex) { UnitRole assignRole = dynamicRoles[roleIndex]; UnitRoleAssignmentRecord assignmentRecord = new UnitRoleAssignmentRecord(assignUnit, assignRole); float evaluation = EvaluateAssignmentForUnit(assignUnit, assignRole); unNormalizedRoleEvaluations[assignmentRecord] = evaluation; } } // normalize the evaluations across roles, so that 0.0 is the worst brawler and 1.0 is the best // do the raw evaluation, without considering role tags Dictionary <UnitRoleAssignmentRecord, float> normalizedRoleEvaluations = new Dictionary <UnitRoleAssignmentRecord, float>(); for (int roleIndex = 0; roleIndex < dynamicRoles.Length; ++roleIndex) { UnitRole assignRole = dynamicRoles[roleIndex]; float maxValue = float.MinValue; float minValue = float.MaxValue; for (int unitIndex = 0; unitIndex < allUnits.Count; ++unitIndex) { AbstractActor assignUnit = allUnits[unitIndex]; float val = unNormalizedRoleEvaluations[new UnitRoleAssignmentRecord(assignUnit, assignRole)]; maxValue = Mathf.Max(maxValue, val); minValue = Mathf.Min(minValue, val); } float valueRange = maxValue - minValue; for (int unitIndex = 0; unitIndex < allUnits.Count; ++unitIndex) { AbstractActor assignUnit = allUnits[unitIndex]; float val = unNormalizedRoleEvaluations[new UnitRoleAssignmentRecord(assignUnit, assignRole)]; float normalized = valueRange == 0.0f ? 1.0f : (val - minValue) / valueRange; normalizedRoleEvaluations[new UnitRoleAssignmentRecord(assignUnit, assignRole)] = normalized; } } // now add in the role tag multipliers Dictionary <UnitRoleAssignmentRecord, float> normalizedRoleEvaluationsWithTagMultipliers = new Dictionary <UnitRoleAssignmentRecord, float>(); for (int roleIndex = 0; roleIndex < dynamicRoles.Length; ++roleIndex) { UnitRole assignRole = dynamicRoles[roleIndex]; for (int unitIndex = 0; unitIndex < allUnits.Count; ++unitIndex) { AbstractActor assignUnit = allUnits[unitIndex]; UnitRoleAssignmentRecord assignmentRecord = new UnitRoleAssignmentRecord(assignUnit, assignRole); float val = normalizedRoleEvaluations[assignmentRecord]; float scaled = val * getRoleTagMultiplierForUnit(assignUnit, assignRole); normalizedRoleEvaluationsWithTagMultipliers[assignmentRecord] = scaled; } } // first, consider just assigning this unit to a new role (abandoning the old role) for (int roleIndex = 0; roleIndex < dynamicRoles.Length; ++roleIndex) { UnitRole newRole = dynamicRoles[roleIndex]; if (newRole == unit.DynamicUnitRole) { continue; } Dictionary <AbstractActor, UnitRole> newAssignment = new Dictionary <AbstractActor, UnitRole>(); newAssignment[unit] = newRole; //Debug.LogFormat("Assignment {0} abandonOld", possibleAssignments.Count); //Debug.LogFormat("Assigning {0} to role {1})", unit.DisplayName, newRole); possibleAssignments.Add(newAssignment); } // now, try swapping with each of the other units if (unit.DynamicUnitRole != UnitRole.Undefined) { for (int otherUnitIndex = 0; otherUnitIndex < otherUnits.Count; ++otherUnitIndex) { AbstractActor otherUnit = otherUnits[otherUnitIndex]; if ((otherUnit.DynamicUnitRole == unit.DynamicUnitRole) || (otherUnit.DynamicUnitRole == UnitRole.Undefined)) { // can't swap with someone who's the same as me, and don't want to swap with an undefined role continue; } Dictionary <AbstractActor, UnitRole> newAssignment = new Dictionary <AbstractActor, UnitRole>(); newAssignment[unit] = otherUnit.DynamicUnitRole; newAssignment[otherUnit] = unit.DynamicUnitRole; //Debug.LogFormat("Assignment {0} swap", possibleAssignments.Count); //Debug.LogFormat("Assigning {0} to role {1}", unit.DisplayName, otherUnit.DynamicUnitRole); //Debug.LogFormat("Assigning {0} to role {1}", otherUnit.DisplayName, unit.DynamicUnitRole); possibleAssignments.Add(newAssignment); } } // now, iterate over all the possible assignements and find the one with the highest value float bestNonPenaltyEvaluationScore = float.MinValue; int bestNonPenaltyIndex = -1; float bestPenaltyEvaluationScore = float.MinValue; int bestPenaltyIndex = -1; for (int assignmentIndex = 0; assignmentIndex < possibleAssignments.Count; ++assignmentIndex) { float assignmentEvaluationScore = evaluateAssignments(possibleAssignments[assignmentIndex], allUnits, normalizedRoleEvaluationsWithTagMultipliers); if (assignmentEvaluationScore > 0.0f) { if (assignmentEvaluationScore > bestNonPenaltyEvaluationScore) { bestNonPenaltyIndex = assignmentIndex; bestNonPenaltyEvaluationScore = assignmentEvaluationScore; } } else { if (assignmentEvaluationScore > bestPenaltyEvaluationScore) { bestPenaltyIndex = assignmentIndex; bestPenaltyEvaluationScore = assignmentEvaluationScore; } } } float statusQuoEvaluationScore = evaluateAssignments(null, allUnits, normalizedRoleEvaluationsWithTagMultipliers); bool isUnassigned = unit.DynamicUnitRole == UnitRole.Undefined; if (bestNonPenaltyIndex >= 0) { float ratio = statusQuoEvaluationScore <= 0 ? float.MaxValue : (bestNonPenaltyEvaluationScore - statusQuoEvaluationScore) / statusQuoEvaluationScore; if (isUnassigned || (ratio > unit.Combat.Constants.DynamicAIRoleConstants.hysteresis)) { //Debug.LogFormat("applying {0} nonpenalty", bestNonPenaltyIndex); applyAssignments(possibleAssignments[bestNonPenaltyIndex], allUnits); } } else { if (isUnassigned || (bestPenaltyEvaluationScore > statusQuoEvaluationScore)) { //Debug.LogFormat("applying {0} penalty", bestPenaltyIndex); applyAssignments(possibleAssignments[bestPenaltyIndex], allUnits); } } if (unit.DynamicUnitRole == UnitRole.Undefined) { Debug.LogError("Dynamic Role Assignment: chose to leave unit undefined"); Debug.LogError("bestNonPenaltyIndex: " + bestNonPenaltyIndex); Debug.LogError("bestNonPenaltyEvaluationScore: " + bestNonPenaltyEvaluationScore); Debug.LogError("bestPenaltyIndex: " + bestPenaltyIndex); Debug.LogError("bestPenaltyEvaluationScore: " + bestPenaltyEvaluationScore); } }