Esempio n. 1
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;
 }
Esempio n. 2
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);
        }
Esempio n. 3
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));
        }
Esempio n. 4
0
        /*
         * Driver for search logic.
         * i) Check if the chosen state is solved, and if so update solution records accordiningly
         * ii) If not solved, check for preliminary qualities that would disqualify this sub problem from being searched
         * iii) If not disqualified, run a recursive division on the invading army into all possible invasion groupings
         *      Each combination found will be added as a new leaf on the tree
         */
        public void Search(SearchState chosenState, bool keepArmyTogether)
        {
            if (chosenState.IsSolved)
            {
                mNumLeafsCreated++;

                #region SolutionFound
                // Check if the solution is relevant depending on which optimizations are being used and solution quality
                bool updateSolution = false;
                bool recordSolution = false;

                int numStepsInInvasion = chosenState.Depth;

                if (mBestSolution == null) // havent found a solution yet
                {
                    recordSolution = updateSolution = true;
                    if (mIsOptimized)
                    {
                        // only accept solutions that occur in as many turns as the new solution
                        mMaxDepthOfTree = numStepsInInvasion;
                    }
                }
                else if (numStepsInInvasion < mBestSolutionDepth)  // found a quicker invasion strategy
                {
                    recordSolution = updateSolution = true;
                    mBestSolution  = null;
                    // reset solution tracking
                    if (mIsOptimized)
                    {
                        mNumSolutionsFound = 0;
                        mMaxDepthOfTree    = numStepsInInvasion;
                    }
                }
                else if (mBestSolutionDepth == numStepsInInvasion) // occurs in the same number of turns as previous solutions
                {
                    recordSolution = true;
                    if (chosenState.Prob.InvadingArmy.CalculateArmyValue() > mBestSolutionArmyValue)
                    {
                        updateSolution = true;
                    }
                }
                else // worse quality of solution
                {
                    recordSolution = !mIsOptimized;
                }

                if (recordSolution)
                {
                    mNumSolutionsFound++;
                }

                // Create and store new solution
                if (updateSolution)
                {
                    mBestSolution          = ToSolution(chosenState, mInitialArmy, mInitialNation);
                    mBestSolutionDepth     = numStepsInInvasion;
                    mBestSolutionArmyValue = chosenState.Prob.InvadingArmy.CalculateArmyValue();
                }
                #endregion
            }
            else // sub problem is not solved. Check if sub problem can be divided
            {
                if (mBestSolution == null) // record partial solutions until an actual solution is found
                {
                    if (mBestPartialSolution == null)
                    {
                        mBestPartialSolution      = chosenState;
                        mBestPartialSolutionValue = chosenState.Prob.ProblemValue;
                    }
                    else
                    {
                        // only update partial solution if it was able to take more cities than the previous, regardless of better army value
                        if (chosenState.Prob.NationBlueprint.NumCitiesRemaining < mBestPartialSolution.Prob.NationBlueprint.NumCitiesRemaining)
                        {
                            float probValue = chosenState.Prob.ProblemValue;
                            if (probValue < mBestPartialSolutionValue)
                            {
                                mBestPartialSolution      = chosenState;
                                mBestPartialSolutionValue = probValue;
                            }
                        }
                    }
                }

                // check for disqualifying features of invasion state
                bool validState = true;
                if (chosenState.ParentProblem != null)
                {
                    if (chosenState.ParentProblem.StateValue == chosenState.StateValue) // army did not heal nor was a city taken
                    {
                        // do nothing, this state didn't progress the invasion
                        validState = false;
                    }
                }
                // took more turns to complete than our max turn limit
                if (mMaxDepthOfTree != -1 && mIsOptimized && chosenState.Depth >= mMaxDepthOfTree)
                {
                    validState = false;
                }

                // valid subproblem. Add all new leafs possible from this state
                if (validState)
                {
                    List <SearchState> subProblems = Fdiv(chosenState, keepArmyTogether);
                    mNumLeafsCreated += subProblems.Count;
                    foreach (SearchState subProblem in subProblems)
                    {
                        mOpenLeafs.AddLast(subProblem);
                    }
                }
                else
                {
                    mNumLeafsCreated++;
                }
            }
        }
Esempio n. 5
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));
        }