/* * Perform all calculations that occur during a sub armies invasion of a city. * Assault template records some calculated values used in the FDiv process to prevent redundant calculations * Any sub army in the invasion is assumed to be strong enough to capture the corresponding fort * Once the fort is captured, the army will have it's health reduced, and if any healers are left alive, will heal * Once each army has performed attack/healing, they will be merged into a new army. * Remaining forts are merged into a new nation * New army/nation objects are created to prevent deep/shallow copy issues */ public SearchState Ftrans(SearchState initialState, InvasionWave transition) { List <ArmyBlueprint> armiesAfterAttack = new List <ArmyBlueprint>(); List <Fortification> captured = new List <Fortification>(); for (int subArmyIdx = 0; subArmyIdx < transition.Wave.Count; ++subArmyIdx) { AssaultTemplate assaultTemplate = transition.Wave[subArmyIdx]; // run assault simulation. Capture fort, deal damage to invaders, and heal up after if possible ArmyBlueprint afterAssault = assaultTemplate.AssaultFortification(); armiesAfterAttack.Add(afterAssault); // was there an unneccessary step in keeping units in reserve. Was not able to heal and could have contributed to the taking of a fort if (assaultTemplate.ToAssault.IsPlaceHolderFort && assaultTemplate.Attackers.CalculateArmyValue() == afterAssault.CalculateArmyValue()) { // assert false; return(null); } if (!assaultTemplate.ToAssault.IsPlaceHolderFort) // fort used to simulate units held in reserve { captured.Add(assaultTemplate.ToAssault); } } return(new SearchState(ArmyBlueprint.MergeSubArmies(armiesAfterAttack), new NationBlueprint(initialState.Prob.NationBlueprint, captured), initialState.Depth + 1, initialState, transition)); }
public SearchState(ArmyBlueprint army, NationBlueprint defender, int depth, SearchState parent, InvasionWave fromParent) { if (depth == 0) { sStateNumber = 0; } mStateNumber = sStateNumber++; Depth = depth; Prob = new Prob(army, defender); ParentProblem = parent; TransitionFromParent = fromParent != null ? new InvasionWave(fromParent.Wave) : null; }
/* * Recurses through all possible army combinations into sub armies to attack each border city provided. * Checks if the sub army will be able to capture the assigned fort, and if not discards the combination * Sub Army combinations are cached to prevent situations such as 3 soldiers with the same health being split into armies of size 1 and being treated as distinct * Caching system uses a similarity value defined in Common Constants. * TODO: the similarity factor is too simplistic and should be scaled based on percent health, and how close to death the unit is, rather than a fixed value */ private static void RecurseFdiv(ArmyBlueprint armyToSubdivide, int currentGroup, List <Fortification> toCapture, InvasionWave currentWave, List <InvasionWave> possibleInvasionWaves, bool keepArmyTogether) { if (currentGroup == toCapture.Count - 1) // last fort to capture { if (!armyToSubdivide.IsEmpty) // units left over to subdivide { // put all units in last invasion group // ASSUMPTION: Last fort is always a place holder fort currentWave.Wave.Add(AssaultTemplate.GetAssualtTemplate(toCapture[currentGroup], armyToSubdivide)); } // add invasion wave to possible solutions possibleInvasionWaves.Add(currentWave); } else if (armyToSubdivide.IsEmpty) // No units left to allocate to the remaining sub armies { // add invasion wave up to this point to return list possibleInvasionWaves.Add(currentWave); } else { int armySize = armyToSubdivide.Size; // num remaining units to subdivide long numDifferentCombinations = (long)Mathf.Pow(2, armySize); IteratorCache invasionCache = new IteratorCache(); // iterate through all possible bitwise combinations of the subarmy // Combinations begin with all units being added as it orrients the leaf list to have a higher chance of // a successful solution and arriving at optimization values quicker. // Doing in reverse order (ie combinationNumber = 0, combinationNumber < NumDifferentCombinations) will // bias all units to be in reserve and take longer to arrive at a solution for (long combinationNumber = numDifferentCombinations - 1; combinationNumber >= 0; combinationNumber--) { ArmyBlueprint subArmy = new ArmyBlueprint(); ArmyBlueprint remainingArmy = new ArmyBlueprint(); #region Create SubArmy from combination number BitArray combinationArray = new BitArray(System.BitConverter.GetBytes(combinationNumber)); for (int j = 0; j < armySize; ++j) { if (j < armyToSubdivide.Soldiers.Count) // bit represents a soldier { if (combinationArray[j]) // put soldier in sub army { subArmy.Soldiers.Add(armyToSubdivide.Soldiers[j]); } else // keep soldier in reserve { remainingArmy.Soldiers.Add(armyToSubdivide.Soldiers[j]); } } else if (j < armyToSubdivide.Soldiers.Count + armyToSubdivide.Healers.Count) // bit is a healer { if (combinationArray[j]) // put healer in sub army { subArmy.Healers.Add(armyToSubdivide.Healers[j - armyToSubdivide.Soldiers.Count]); } else // keep healer in reserve { remainingArmy.Healers.Add(armyToSubdivide.Healers[j - armyToSubdivide.Soldiers.Count]); } } else // bit represents an archer { if (combinationArray[j]) // put archer in sub army { subArmy.Archers.Add(armyToSubdivide.Archers[j - armyToSubdivide.Soldiers.Count - armyToSubdivide.Healers.Count]); } else // keep archer in reserve { remainingArmy.Archers.Add(armyToSubdivide.Archers[j - armyToSubdivide.Soldiers.Count - armyToSubdivide.Healers.Count]); } } } #endregion // Only consider iteration if it can capture the city it was tasked to take if (combinationNumber == 0) // Empty sub army. { InvasionWave recursiveTransitionCopy = new InvasionWave(currentWave.Wave); RecurseFdiv(remainingArmy, currentGroup + 1, toCapture, recursiveTransitionCopy, possibleInvasionWaves, keepArmyTogether); } else if (!mIsOptimized || !invasionCache.IsCached(subArmy)) { // check is assualt will be successful and record values in assault template AssaultTemplate assaultTemplate = AssaultTemplate.GetAssualtTemplate(toCapture[currentGroup], subArmy); if (assaultTemplate != null /* is valid assault */) { invasionCache.Cache(subArmy); InvasionWave recursiveTransitionCopy = new InvasionWave(currentWave.Wave); recursiveTransitionCopy.Wave.Add(assaultTemplate); // recurse with remaining units not included in this sub army RecurseFdiv(remainingArmy, currentGroup + 1, toCapture, recursiveTransitionCopy, possibleInvasionWaves, keepArmyTogether); } // else discard combination } // Used for linear invasion strategies to prevent unneccessary combination checks. // jump to putting the army in reserve if it wasn't assigned to a fort if (keepArmyTogether && combinationNumber == numDifferentCombinations - 1) // first sub army created { combinationNumber = 1; // will become 0 next loop iteration and second army option will be empty } } } }