Exemplo n.º 1
0
        /*
         * 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));
        }
Exemplo n.º 2
0
    IEnumerator AnimateInvasion(InvasionSolution solution)
    {
        yield return(new WaitForFixedUpdate());

        AnimationNavigationParent.SetActive(false);
        SimulationButtonParent.gameObject.SetActive(true);
        ToggleAutoContinue(true);
        ContinueButton.interactable = false;
        mIsWaitingOnContinue        = false;

        float subArmyAnimationDuration = mAnimationDuration * AnimationSpeed;
        float assaultHealthDuration    = mAnimationDuration * AnimationSpeed;
        float reformAnimationDuration  = mAnimationDuration * AnimationSpeed;

        List <InvasionWave> bestSolution = solution.InvasionOrder;

        for (int i = 0; i < bestSolution.Count; ++i)
        {
            WaveText.text = "WAVE " + (i + 1);
            List <AssaultTemplate> attackWave = bestSolution[i].Wave;
            Debug.Assert(DefaultArmyNumber >= attackWave.Count);
            List <ArmyBlueprint> subArmies = new List <ArmyBlueprint>();
            foreach (var wave in attackWave)
            {
                subArmies.Add(wave.Attackers);
            }
            FormSubArmies(subArmies, subArmyAnimationDuration);
            yield return(_WaitForInputAfterAnimation(subArmyAnimationDuration * 1.5f));

            // display "Attack wave #"
            int subArmyIdx = 0;
            foreach (AssaultTemplate attack in attackWave)
            {
                ArmyBlueprint afterAttack = attack.AssaultFortification(false);
                mArmyControllers[subArmyIdx].UpdateHealth(afterAttack, assaultHealthDuration);
                if (!attack.ToAssault.IsPlaceHolderFort)
                {
                    NationController.DestroyFort(attack.ToAssault, assaultHealthDuration);
                    yield return(_WaitForInputAfterAnimation(assaultHealthDuration * 1.5f));
                }

                afterAttack.Heal();
                mArmyControllers[subArmyIdx].UpdateHealth(afterAttack, assaultHealthDuration);
                yield return(_WaitForInputAfterAnimation(assaultHealthDuration * 1.5f));

                subArmyIdx++;
            }

            MergeSubArmies(reformAnimationDuration);
            NationController.UpgradeForts(reformAnimationDuration);
            yield return(_WaitForInputAfterAnimation(reformAnimationDuration * 1.5f));
        }
        WaveText.text = "Invasion Complete!";
        yield return(new WaitForSeconds(reformAnimationDuration * 1.5f));
    }
Exemplo n.º 3
0
 public InvasionSolution(ArmyBlueprint initialArmy, NationBlueprint initialNation,
                         ArmyBlueprint finalArmy, NationBlueprint finalNation,
                         List <InvasionWave> invasionOrder, bool isCompleteSolution)
 {
     InvasionOrder      = invasionOrder;
     InitialArmy        = initialArmy;
     InitialNation      = initialNation;
     FinalArmy          = finalArmy;
     FinalNation        = finalNation;
     IsCompleteSolution = isCompleteSolution;
 }
Exemplo n.º 4
0
    public void BeginSimulation()
    {
        Debug.Assert(mSearchComplete);
        InvasionInputParent.gameObject.SetActive(false);
        InvasionAnimationParent.gameObject.SetActive(false);
        InvasionResultsParent.SetActive(true);

        mInitialInvaders = new ArmyBlueprint(SoldierCounter.InvaderCount, HealerCounter.InvaderCount, ArcherCounter.InvaderCount);
        mInitialNation   = new NationBlueprint(NationLayoutController.GetNationLayout());
        StartCoroutine(_BeginSimulation());
    }
Exemplo n.º 5
0
 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;
 }
Exemplo n.º 6
0
 public void Cache(ArmyBlueprint invasionGroup)
 {
     if (!mPriorityCache.ContainsKey(invasionGroup.Size))
     {
         HashSet <ArmyBlueprint> toAdd = new HashSet <ArmyBlueprint>();
         toAdd.Add(invasionGroup);
         mPriorityCache.Add(invasionGroup.Size, toAdd);
     }
     else
     {
         mPriorityCache[invasionGroup.Size].Add(invasionGroup);
     }
 }
Exemplo n.º 7
0
 public bool IsCached(ArmyBlueprint invasionGroup)
 {
     if (mPriorityCache.ContainsKey(invasionGroup.Size))
     {
         foreach (ArmyBlueprint skeleton in mPriorityCache[invasionGroup.Size])
         {
             if (invasionGroup.Equals(skeleton))
             {
                 return(true);
             }
         }
     }
     return(false);
 }
Exemplo n.º 8
0
        /*
         * Driver for recursive division of invading army into all possible sub army invasions
         * Does future checks to see if there will ever be a valid solution coming from this leaf.
         *      example: Checks if the army will ever be able to capture the strongest city
         */
        public List <SearchState> Fdiv(SearchState state, bool keepArmyTogether)
        {
            //Debug.Assert(!state.IsSolved, "Attempting to apply Div on a solved problem");

            // Get all attackable forts
            List <Fortification> fortifications = new List <Fortification>(state.Prob.NationBlueprint.GetBorderCities());
            ArmyBlueprint        invadingArmy   = state.Prob.InvadingArmy;

            fortifications.Add(new Fortification()); // add a place holder fort for units to be held in reserve

            List <SearchState> problemDivisions = new List <SearchState>();

            // TODO: pick most difficult city to capture. Temp patch just check the first
            Fortification hardestCity = fortifications[0];

            // Since damage doesn't increase over time, if the army can't take the city now it will never be able to
            if (!mIsOptimized && invadingArmy.Damage < hardestCity.Defense)
            {
                return(problemDivisions);
            }
            // If the army is strong enough to take the city but doesn't have enough health,
            // and it either can't heal, or it's max health even after healing wont be enough to take the city,
            // abandon this search
            else if (!mIsOptimized && invadingArmy.Health <= hardestCity.Offense &&
                     !(invadingArmy.CanHeal && invadingArmy.MaxHealth > hardestCity.Offense))
            {
                return(problemDivisions);
            }

            // come up with all possible transitions through recursing through all army combinations
            else
            {
                List <InvasionWave> armyDistributions = new List <InvasionWave>();
                RecurseFdiv(
                    new ArmyBlueprint(invadingArmy.Soldiers, invadingArmy.Healers, invadingArmy.Archers),
                    0, fortifications, new InvasionWave(), armyDistributions, keepArmyTogether);

                foreach (InvasionWave transition in armyDistributions)
                {
                    SearchState nextState = Ftrans(state, transition);
                    // if the transition proved to be wasteful, discard
                    if (nextState != null)
                    {
                        problemDivisions.Add(nextState);
                    }
                }
            }
            return(problemDivisions);
        }
Exemplo n.º 9
0
        // Helper function to convert the invasion stack into a solution
        public static InvasionSolution ToSolution(SearchState state, ArmyBlueprint initialArmy, NationBlueprint initialNation)
        {
            SearchState         currentState  = state;
            List <InvasionWave> invasionOrder = new List <InvasionWave>();

            while (currentState.TransitionFromParent != null)
            {
                invasionOrder.Add(currentState.TransitionFromParent);
                currentState = currentState.ParentProblem;
            }
            invasionOrder.Reverse();
            return(new InvasionSolution(initialArmy, initialNation,
                                        state.Prob.InvadingArmy, state.Prob.NationBlueprint,
                                        invasionOrder, state.IsSolved));
        }
Exemplo n.º 10
0
    public void UpdateHealth(ArmyBlueprint afterAssault, float overSeconds)
    {
        for (int i = 0; i < afterAssault.Archers.Count; ++i)
        {
            InvaderController controller = mInvaderControllers[InvaderType.Archer][i];
            controller.UpdateHealth(afterAssault.Archers[i], overSeconds);
        }

        for (int i = 0; i < afterAssault.Healers.Count; ++i)
        {
            mInvaderControllers[InvaderType.Healer][i].UpdateHealth(afterAssault.Healers[i], overSeconds);
        }

        for (int i = 0; i < afterAssault.Soldiers.Count; ++i)
        {
            mInvaderControllers[InvaderType.Soldier][i].UpdateHealth(afterAssault.Soldiers[i], overSeconds);
        }
    }
Exemplo n.º 11
0
 // Should only be called once when army is first being created
 public void Initialize(ArmyBlueprint army)
 {
     Initialize();
     foreach (ushort archer in army.Archers)
     {
         InvaderController controller = Instantiate(InvaderPrefab, transform.parent);
         controller.Initialize(archer, InvaderType.Archer);
         mInvaderControllers[InvaderType.Archer].Add(controller);
     }
     foreach (ushort healer in army.Healers)
     {
         InvaderController controller = Instantiate(InvaderPrefab, transform.parent);
         controller.Initialize(healer, InvaderType.Healer);
         mInvaderControllers[InvaderType.Healer].Add(controller);
     }
     foreach (ushort soldier in army.Soldiers)
     {
         InvaderController controller = Instantiate(InvaderPrefab, transform.parent);
         controller.Initialize(soldier, InvaderType.Soldier);
         mInvaderControllers[InvaderType.Soldier].Add(controller);
     }
     LayoutArmy(); // create at position in army
 }
Exemplo n.º 12
0
 public Prob(ArmyBlueprint army, NationBlueprint defender)
 {
     InvadingArmy    = army;
     NationBlueprint = defender;
 }
Exemplo n.º 13
0
        /*
         * 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
                    }
                }
            }
        }
Exemplo n.º 14
0
        public SearchResults SearchForSolutions(ArmyBlueprint attackers, NationBlueprint defenders, bool optimize)
        {
            mInitialArmy   = attackers;
            mInitialNation = defenders;
            mIsOptimized   = true; /* = optimize; Replace after data collection*/

            // Initialize start variables and start invasion search
            SearchState initialState = new SearchState(attackers, defenders, 0, null, null);

            mOpenLeafs.Clear();
            // orrient the nations layout relative to the army. TODO: allow for dynamic invasion direction
            initialState.Prob.NationBlueprint.BeginInvasion(Vector2.right);

            // Do the default search keeping the units together
            mOpenLeafs.AddLast(initialState);
            mPruneSolutions = true;                    // only care about the best solution with army grouped up. Only record
            mMaxDepthOfTree = mBestSolutionDepth = -1; // max depth = -1 until a solution is found creating a limit to depth

            // Search for solutions with linear invasion strategy
            while (mOpenLeafs.Count > 0)
            {
                SearchState pop = mOpenLeafs.Last.Value;
                mOpenLeafs.RemoveLast();
                Search(pop, true);
            }
            // record default invasion stats
            InvasionSolution linearSolution = null;

            if (mBestSolution == null) // no solution was found
            {
                // record the best result possible
                linearSolution = ToSolution(mBestPartialSolution, mInitialArmy, mInitialNation);
            }
            else
            {
                linearSolution = mBestSolution;
            }

            int bestLinearDepth = linearSolution != null ? mBestSolutionDepth : -1;

            // update the max depth of the optimized search to be the depth of the default time
            mMaxDepthOfTree = bestLinearDepth;
            //Debug.Log("Best Case Group Scenario took " + mMaxDepthOfTree + " turns to complete, needing " + (mMaxDepthOfTree - initialState.Prob.NationBlueprint.NumCitiesRemaining) + " turns to heal");
            // reset search variables for the optimized search
            mPruneSolutions = optimize;
            mOpenLeafs.Clear();
            mBestSolution        = null;
            mBestPartialSolution = null;

            mOpenLeafs.AddLast(initialState);
            // search for parallel attack strategy solutions
            while (mOpenLeafs.Count > 0)
            {
                SearchState pop = mOpenLeafs.Last.Value;
                mOpenLeafs.RemoveLast();
                Search(pop, false);
            }


            // Record optimized Solution
            InvasionSolution parallelSolution = null;

            if (mBestSolution == null)
            {
                parallelSolution = ToSolution(mBestPartialSolution, mInitialArmy, mInitialNation);
            }
            else
            {
                parallelSolution = mBestSolution;
            }

            int bestOptimizedDepth = parallelSolution != null ? mBestSolutionDepth : -1;

            return(new SearchResults(mNumLeafsCreated, mNumSolutionsFound,
                                     linearSolution, parallelSolution));
        }