Пример #1
0
        /// <summary>
        /// Gets the time require to train the given skills and their prerequisites
        /// </summary>
        /// <param name="trainings">A sequence of pairs of skills and the target levels.</param>
        /// <returns></returns>
        public TimeSpan GetTrainingTimeToMultipleSkills <T>(IEnumerable <T> trainings)
            where T : ISkillLevel
        {
            CharacterScratchpad scratchpad = this.After(trainings);

            return(scratchpad.TrainingTime);
        }
Пример #2
0
        /// <summary>
        /// Generate a trainings array from the skills already know by a character.
        /// </summary>
        /// <param name="character">The character.</param>
        /// <param name="plan">The plan.</param>
        /// <returns></returns>
        public static RemappingResult OptimizeFromCharacter(Character character, BasePlan plan)
        {
            // Create a character without any skill
            var scratchpad = new CharacterScratchpad(character.After(plan.ChosenImplantSet));

            scratchpad.ClearSkills();

            // Create a new plan
            var newPlan = new Plan(scratchpad);

            // Add all trained skill levels that the character has trained so far
            foreach (var skill in character.Skills)
            {
                newPlan.PlanTo(skill, skill.Level);
            }

            // Create a new remapping
            var remapping = new RemappingResult(scratchpad);

            // Add those skills to the remapping
            foreach (var entry in newPlan)
            {
                remapping.Skills.Add(entry);
            }

            // Optimize
            remapping.Optimize(TimeSpan.MaxValue);
            return(remapping);
        }
Пример #3
0
        /// <summary>
        /// Gets the list of remapping results from a plan.
        /// </summary>
        /// <param name="plan"></param>
        /// <returns></returns>
        public static List <RemappingResult> GetResultsFromRemappingPoints(BasePlan plan)
        {
            var             scratchpad    = new CharacterScratchpad(plan.Character.After(plan.ChosenImplantSet));
            var             remappingList = new List <RemappingResult>();
            var             list          = new List <ISkillLevel>();
            RemappingResult remapping     = null;

            // Scroll through the entries and split it into remappings
            foreach (var entry in plan)
            {
                // Ends the current remapping and start a new one
                if (entry.Remapping != null)
                {
                    // Creates a new remapping
                    remapping = new RemappingResult(entry.Remapping, scratchpad.Clone());
                    remappingList.Add(remapping);
                    list = remapping.Skills;
                }

                // Add this skill to the training list
                scratchpad.Train(entry);
                list.Add(entry);
            }

            // Return
            return(remappingList);
        }
Пример #4
0
        /// <summary>
        /// Updates the statistics of the entries in the same way the given character would train this plan.
        /// </summary>
        /// <param name="scratchpad"></param>
        /// <param name="applyRemappingPoints"></param>
        /// <param name="trainSkills">When true, the character will train every skill, increasing SP, etc.</param>
        public void UpdateStatistics(CharacterScratchpad scratchpad, bool applyRemappingPoints, bool trainSkills)
        {
            var scratchpadWithoutImplants = scratchpad.Clone();

            scratchpadWithoutImplants.ClearImplants();
            DateTime time = DateTime.Now;

            // Update the statistics
            foreach (var entry in m_items)
            {
                // Apply the remapping
                if (applyRemappingPoints && entry.Remapping != null && entry.Remapping.Status == RemappingPoint.PointStatus.UpToDate)
                {
                    scratchpad.Remap(entry.Remapping);
                    scratchpadWithoutImplants.Remap(entry.Remapping);
                }

                // Update entry's statistics
                entry.UpdateStatistics(scratchpad, scratchpadWithoutImplants, ref time);

                // Update the scratchpad
                if (trainSkills)
                {
                    scratchpad.Train(entry.Skill, entry.Level);
                    scratchpadWithoutImplants.Train(entry.Skill, entry.Level);
                }
            }
        }
Пример #5
0
        /// <summary>
        /// Inserts the given learning skill at the best possible position
        /// </summary>
        /// <param name="skill"></param>
        /// <param name="level"></param>
        public void InsertAtBestPosition(StaticSkill skill, int level)
        {
            using (SuspendingEvents())
            {
                var entry = new PlanEntry(this, skill, level);

                // Look at prerequisites to search min and max insertion positions
                int minPosition = 0, maxPosition = m_items.Count;
                for (int i = 0; i < m_items.Count; i++)
                {
                    var pEntry = m_items[i];
                    if (entry.IsDependentOf(pEntry))
                    {
                        minPosition = Math.Max(minPosition, i + 1);
                    }
                    if (pEntry.IsDependentOf(entry))
                    {
                        maxPosition = Math.Min(maxPosition, i);
                    }
                }

                var bestTime              = TimeSpan.MaxValue;
                var bestCandidatePosition = maxPosition;
                var scratchpad            = new CharacterScratchpad(m_character);

                // We now search for the best insertion position
                for (int index = maxPosition; index >= minPosition; index--)
                {
                    using (scratchpad.BeginTemporaryChanges())
                    {
                        // Compute list's training time if the next item was inserted at index
                        for (int i = 0; i <= m_items.Count; i++)
                        {
                            if (i < index)
                            {
                                scratchpad.Train(m_items[i]);
                            }
                            else if (i > index)
                            {
                                scratchpad.Train(m_items[i - 1]);
                            }
                            else
                            {
                                scratchpad.Train(entry);
                            }
                        }

                        // Is it better with this index ? Then, we retain this as the best candidate
                        if (bestTime > scratchpad.TrainingTime)
                        {
                            bestTime = scratchpad.TrainingTime;
                            bestCandidatePosition = index;
                        }
                    }
                }

                // Insert at the best candidate position
                InsertCore(bestCandidatePosition, entry);
            }
        }
Пример #6
0
        /// <summary>
        /// Gets a character scratchpad representing this character after the provided skill levels trainings.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="trainings"></param>
        /// <returns></returns>
        public CharacterScratchpad After <T>(IEnumerable <T> trainings)
            where T : ISkillLevel
        {
            var scratchpad = new CharacterScratchpad(this);

            scratchpad.Train(trainings);
            return(scratchpad);
        }
Пример #7
0
        /// <summary>
        /// Gets a character scratchpad representing this character after a switch to the provided implant set.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="trainings"></param>
        /// <returns></returns>
        public CharacterScratchpad After(ImplantSet set)
        {
            var scratchpad = new CharacterScratchpad(this);

            for (int i = 0; i < 5; i++)
            {
                var attribute = (EveAttribute)i;
                scratchpad[attribute].ImplantBonus = set[attribute].Bonus;
            }
            return(scratchpad);
        }
Пример #8
0
        /// <summary>
        /// Gets the total training time for this plan, using the given scratchpad.
        /// </summary>
        /// <param name="scratchpad">The scratchpad to use for the computation, may be null.</param>
        /// <param name="applyRemappingPoints"></param>
        /// <returns></returns>
        public TimeSpan GetTotalTime(CharacterScratchpad scratchpad, bool applyRemappingPoints)
        {
            // No scratchpad ? Let's create one
            if (scratchpad == null)
            {
                scratchpad = new CharacterScratchpad(m_character);
            }

            // Train entries
            TimeSpan time = TimeSpan.Zero;

            scratchpad.TrainEntries(m_items, applyRemappingPoints);
            return(scratchpad.TrainingTime - time);
        }
Пример #9
0
        /// <summary>
        /// Resets this scratchpad using the provided scratchpad.
        /// </summary>
        /// <param name="scratchpad"></param>
        public void Reset(CharacterScratchpad scratchpad)
        {
            m_totalSP      = scratchpad.m_totalSP;
            m_trainingTime = scratchpad.m_trainingTime;

            m_trainedSkills.Clear();
            m_trainedSkills.AddRange(scratchpad.m_trainedSkills);

            for (int i = 0; i < m_attributes.Length; i++)
            {
                m_attributes[i].Reset(scratchpad.m_attributes[i]);
            }

            scratchpad.m_skillSP.CopyTo(m_skillSP, 0);
            scratchpad.m_skillLevels.CopyTo(m_skillLevels, 0);
        }
Пример #10
0
        /// <summary>
        /// Gets a string representation of the attribute
        /// </summary>
        /// <param name="attrib"></param>
        /// <param name="oldScratchpad"></param>
        /// <param name="newScratchpad"></param>
        /// <returns></returns>
        public static string GetStringForAttribute(EveAttribute attrib, CharacterScratchpad oldScratchpad, CharacterScratchpad newScratchpad)
        {
            var bonusDifference = newScratchpad[attrib].Base - oldScratchpad[attrib].Base;

            if (bonusDifference == 0)
            {
                return(newScratchpad[attrib].ToString("%N (-) = %e = (%b + %s + %i) * %f"));
            }
            else if (bonusDifference > 0)
            {
                return(newScratchpad[attrib].ToString("%N (+" + bonusDifference.ToString() + ") = %e = (%b + %s + %i) * %f"));
            }
            else
            {
                return(newScratchpad[attrib].ToString("%N (" + bonusDifference.ToString() + ") = %e = (%b + %s + %i) * %f"));
            }
        }
Пример #11
0
        /// <summary>
        /// Gets a string representation of the attribute
        /// </summary>
        /// <param name="attrib"></param>
        /// <param name="oldScratchpad"></param>
        /// <param name="newScratchpad"></param>
        /// <returns></returns>
        public static string GetStringForAttribute(EveAttribute attrib, CharacterScratchpad oldScratchpad, CharacterScratchpad newScratchpad)
        {
            int bonusDifference = newScratchpad[attrib].Base - oldScratchpad[attrib].Base;

            if (bonusDifference == 0)
            {
                return(newScratchpad[attrib].ToString("%N (0) = %e = (%B + %r + %i)"));
            }
            else if (bonusDifference > 0)
            {
                return(newScratchpad[attrib].ToString(String.Format("%N (+{0}) = %e = (%B + %r + %i)", bonusDifference)));
            }
            else
            {
                return(newScratchpad[attrib].ToString(String.Format("%N ({0}) = %e = (%B + %r + %i)", bonusDifference)));
            }
        }
Пример #12
0
        /// <summary>
        /// Performs the sort.
        /// </summary>
        /// <returns></returns>
        public IEnumerable <PlanEntry> Sort()
        {
            int initialCount = m_entries.Count();

            // Apply first pass (priorities grouping)
            // We split the entries into multiple priority groups if that selection is made
            List <PlanScratchpad> groupedPlan = new List <PlanScratchpad>();
            var scratchpad = new CharacterScratchpad(m_character);

            if (m_groupByPriority)
            {
                foreach (var group in m_entries.GroupBy(x => x.Priority).OrderBy(x => x.Key))
                {
                    groupedPlan.Add(new PlanScratchpad(scratchpad, group));
                }
            }
            else
            {
                groupedPlan.Add(new PlanScratchpad(scratchpad, m_entries));
            }

            // Apply second pass (sorts)
            // We sort every group, and merge them once they're sorted
            List <PlanEntry> list = new List <PlanEntry>();

            foreach (var group in groupedPlan)
            {
                group.UpdateStatistics(scratchpad, false, false);
                group.SimpleSort(m_sort, m_reverseOrder);
                list.AddRange(group);
            }

            // Fix prerequisites order
            FixPrerequisitesOrder(list);

            // Check we didn't mess up anything
            if (initialCount != list.Count)
            {
                throw new UnauthorizedAccessException("The sort algorithm messed up and deleted items");
            }

            // Return
            return(list);
        }
Пример #13
0
        /// <summary>
        /// Updates the statistics of the entries in the same way the given character would train this plan.
        /// </summary>
        /// <param name="scratchpad"></param>
        /// <param name="applyRemappingPoints"></param>
        /// <param name="trainSkills">When true, the character will train every skill, increasing SP, etc.</param>
        public void UpdateOldTrainingTimes(CharacterScratchpad scratchpad, bool applyRemappingPoints, bool trainSkills)
        {
            // Update the statistics
            foreach (var entry in m_items)
            {
                // Apply the remapping
                if (applyRemappingPoints && entry.Remapping != null && entry.Remapping.Status == RemappingPoint.PointStatus.UpToDate)
                {
                    scratchpad.Remap(entry.Remapping);
                }

                // Update entry's statistics
                entry.UpdateOldTrainingTime(scratchpad);

                // Update the scratchpad
                if (trainSkills)
                {
                    scratchpad.Train(entry.Skill, entry.Level);
                }
            }
        }
Пример #14
0
        /// <summary>
        /// Will set the provided character scratchpad's base attributes as the target values to remap.
        /// </summary>
        /// <param name="newScratchpad">The scratchpad with the target base values to assign to this point</param>
        /// <param name="oldScratchpad">The scratchpad before we remapped</param>
        internal void SetBaseAttributes(CharacterScratchpad newScratchpad, CharacterScratchpad oldScratchpad)
        {
            // Update the status
            this.m_status = PointStatus.UpToDate;

            // Initialize the string
            StringBuilder builder = new StringBuilder();

            // Scroll through attributes
            for (int i = 0; i < 5; i++)
            {
                // Compute the new base attribute
                EveAttribute attrib = (EveAttribute)i;
                m_attributes[i] = newScratchpad[attrib].Base;

                // Update description
                builder.AppendLine().Append(GetStringForAttribute(attrib, oldScratchpad, newScratchpad));
            }

            // Return the final string
            this.m_description = builder.ToString();
        }
Пример #15
0
        /// <summary>
        /// Generate a trainings array from the skills already know by a character
        /// </summary>
        /// <param name="character"></param>
        /// <returns></returns>
        public static RemappingResult OptimizeFromCharacter(Character character)
        {
            // Create a character without any skill
            var scratchpad = new CharacterScratchpad(character);

            scratchpad.ClearSkills();

            // Create a sorted plan for the learning skills
            var plan = new Plan(scratchpad);

            foreach (var learning in character.SkillGroups["Learning"])
            {
                plan.PlanTo(learning, learning.Level);
            }
            plan.SortLearningSkills(false);

            // Add the non-training skills after that
            foreach (var skill in character.Skills)
            {
                if (skill.Group.Name != "Learning")
                {
                    plan.PlanTo(skill, skill.Level);
                }
            }

            // Add those learning skills to a list
            var remapping = new RemappingResult(scratchpad);

            foreach (var entry in plan)
            {
                remapping.Skills.Add(entry);
            }

            // Optimize
            remapping.Optimize(TimeSpan.FromDays(365.0));
            return(remapping);
        }
Пример #16
0
 /// <summary>
 /// Computes an optimized scratchpad, then call <see cref="Update"/>.
 /// </summary>
 /// <param name="maxDuration">The max duration to take into account for optimization.</param>
 /// <returns></returns>
 public void Optimize(TimeSpan maxDuration)
 {
     m_bestScratchpad = AttributesOptimizer.Optimize(m_skills, m_baseScratchpad, maxDuration);
     Update();
 }
Пример #17
0
        /// <summary>
        /// Performs the sort
        /// </summary>
        /// <param name="startSp"></param>
        /// <returns></returns>
        public IEnumerable <PlanEntry> Sort(int startSp)
        {
            int initialCount = m_entries.Count();

            // Apply first pass (learning skills)
            // We split the entries into a head (learnings) and a tail (non-learnings)
            PlanScratchpad   headPlan    = new PlanScratchpad(m_character);
            List <PlanEntry> tailEntries = new List <PlanEntry>();

            if (m_learningSkillsFirst)
            {
                tailEntries.AddRange(m_entries.Where(x => x.Skill.LearningClass == LearningClass.None));

                var learningSkills = m_entries.Where(x => x.Skill.LearningClass != LearningClass.None);
                headPlan = OptimizeLearningSkills(learningSkills, startSp);
            }
            else
            {
                tailEntries.AddRange(m_entries);
            }


            // Apply second pass (priorities grouping)
            // We split the tail into multiple tail groups
            List <PlanScratchpad> tailEntryPlans = new List <PlanScratchpad>();
            var scratchpad = new CharacterScratchpad(m_character);

            scratchpad.Train(headPlan);

            if (m_groupByPriority)
            {
                foreach (var group in tailEntries.GroupBy(x => x.Priority))
                {
                    tailEntryPlans.Add(new PlanScratchpad(scratchpad, group));
                }
            }
            else
            {
                tailEntryPlans.Add(new PlanScratchpad(scratchpad, tailEntries));
            }


            // Apply third pass (sorts)
            // We sort every tail group, and merge them once they're sorted.
            List <PlanEntry> list = new List <PlanEntry>();

            list.AddRange(headPlan);

            foreach (var tailPlan in tailEntryPlans)
            {
                tailPlan.UpdateStatistics(scratchpad, false, false);
                tailPlan.SimpleSort(m_sort, m_reverseOrder);
                list.AddRange(tailPlan);
            }

            // This is actually what GroupByPriority should do
            if (m_groupByPriority)
            {
                list.StableSort(PlanSorter.CompareByPriority);
            }

            // Fix prerequisites order
            FixPrerequisitesOrder(list);

            // Check we didn't mess up anything
            if (initialCount != list.Count)
            {
                throw new UnauthorizedAccessException("The sort algorithm messed up and deleted items");
            }

            // Return
            return(list);
        }
Пример #18
0
 /// <summary>
 /// Constructor for a manually edited result from a base result.
 /// </summary>
 /// <param name="point">Associated remapping point, may be null.</param>
 /// <param name="baseScratchpad"></param>
 /// <param name="bestScratchpad"></param>
 public RemappingResult(RemappingResult result, CharacterScratchpad bestScratchpad)
     : this(result.Point, result.BaseScratchpad)
 {
     m_skills.AddRange(result.m_skills);
     m_bestScratchpad = bestScratchpad;
 }
Пример #19
0
 /// <summary>
 /// Constructor for a result bound to a remapping point
 /// </summary>
 /// <param name="point">Associated remapping point, may be null.</param>
 /// <param name="baseScratchpad"></param>
 public RemappingResult(RemappingPoint point, CharacterScratchpad baseScratchpad)
     : this(baseScratchpad)
 {
     m_point = point;
 }
Пример #20
0
        /// <summary>
        /// Sens a mail alert for a skill completion
        /// </summary>
        /// <param name="skillLevel"></param>
        /// <param name="skillName"></param>
        /// <param name="character"></param>
        /// <returns></returns>
        public static bool SendSkillCompletionMail(int skillLevel, string skillName, Character character)
        {
            string charName         = character.Name;
            string skillLevelString = Skill.GetRomanForInt(skillLevel);

            // Message's first line
            StringBuilder messageText = new StringBuilder();

            messageText.Append(charName + " has finished training " + skillName + " ");

            // Short format (also for SMS)
            if (Settings.Notifications.UseEmailShortFormat)
            {
                return(SendMail(Settings.Notifications, "[STC] " + charName + " :: " + skillName + " " + skillLevelString, messageText.ToString()));
            }

            // Long format
            messageText.Append("\r\n\r\nNext skills listed in plans:\r\n\r\n");

            foreach (var plan in character.Plans)
            {
                if (plan.Count > 0)
                {
                    // Print plan name
                    CharacterScratchpad scratchpad = new CharacterScratchpad(character);
                    messageText.Append(plan.Name + ":\r\n");

                    // Scroll through entries
                    int i       = 0;
                    int minDays = 1;
                    foreach (PlanEntry entry in plan)
                    {
                        TimeSpan trainTime = scratchpad.GetTrainingTime(entry.Skill, entry.Level, TrainingOrigin.FromPreviousLevelOrCurrent);

                        // Only print the first three skills, and the very long skills (first limit is one day, then we add skills duration
                        if (++i <= 3 || trainTime.Days > minDays)
                        {
                            if (i > 3)
                            {
                                // Print long message once
                                if (minDays == 1)
                                {
                                    messageText.Append("\r\n" + "Longer skills from " + plan.Name + ":\r\n");
                                }

                                minDays = trainTime.Days + minDays;
                            }
                            messageText.Append("\t" + entry.ToString());

                            // Notes
                            if (entry.Notes != null && entry.Notes.Length > 0)
                            {
                                messageText.Append(" (" + entry.Notes + ")");
                            }

                            // Training time
                            string timeText = String.Format("{0:00}:{1:00}:{2:00}", trainTime.Hours, trainTime.Minutes, trainTime.Seconds);
                            if (trainTime.Days > 0)
                            {
                                messageText.Append(" - " + trainTime.Days + "d, " + timeText);
                            }
                            else
                            {
                                messageText.Append(" - " + timeText);
                            }

                            messageText.Append("\r\n");
                        }
                    }
                    messageText.Append("\r\n");
                }
            }

            return(SendMail(Settings.Notifications, charName + " has finished training " + skillName + " " + skillLevelString, messageText.ToString()));
        }
Пример #21
0
 /// <summary>
 /// Constructor without any remapping point associated
 /// </summary>
 /// <param name="baseScratchpad"></param>
 public RemappingResult(CharacterScratchpad baseScratchpad)
 {
     m_baseScratchpad = baseScratchpad;
     m_startTime      = m_baseScratchpad.TrainingTime;
 }
Пример #22
0
        /// <summary>
        /// Sends a mail alert for a skill completion
        /// </summary>
        /// <param name="queueList">Current Skill Queue</param>
        /// <param name="skill">Skill that has just completed</param>
        /// <param name="character">Character affected</param>
        /// <returns></returns>
        public static bool SendSkillCompletionMail(IList <QueuedSkill> queueList, QueuedSkill skill, Character character)
        {
            CCPCharacter ccpCharacter = character as CCPCharacter;

            // Current character isn't a CCP character, so can't have a Queue.
            if (ccpCharacter == null)
            {
                return(false);
            }

            string charName         = character.Name;
            string skillName        = skill.SkillName;
            string skillLevelString = Skill.GetRomanForInt(skill.Level);

            var      skillQueueEndTime = ccpCharacter.SkillQueue.EndTime;
            bool     freeTime          = skillQueueEndTime < DateTime.UtcNow.AddHours(24);
            TimeSpan timeLeft          = DateTime.UtcNow.AddHours(24).Subtract(skillQueueEndTime);
            string   timeLeftText      = timeLeft.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas, false);

            // Message's first line
            StringBuilder body = new StringBuilder();

            body.AppendFormat(CultureConstants.DefaultCulture, "{0} has finished training {1} {2}.{3}{3}", charName, skillName, skillLevelString, Environment.NewLine);

            // Next skills in queue
            if (queueList[0] != null)
            {
                body.AppendFormat(CultureConstants.DefaultCulture, "Next skill{0} in queue:{1}", (queueList.Count > 1 ? "s" : String.Empty), Environment.NewLine);
                foreach (var qskill in queueList)
                {
                    body.AppendFormat(CultureConstants.DefaultCulture, "- {0}{1}", qskill, Environment.NewLine);
                }
                body.AppendLine();
            }
            else
            {
                body.AppendFormat(CultureConstants.DefaultCulture, "Character is not training.{0}{0}", Environment.NewLine);
            }

            // Free room in skill queue
            if (freeTime)
            {
                body.AppendFormat(CultureConstants.DefaultCulture, "There is also {0} free room in skill queue.{1}", timeLeftText, Environment.NewLine);
            }

            // Short format (also for SMS)
            if (Settings.Notifications.UseEmailShortFormat)
            {
                return(SendMail(Settings.Notifications, String.Format(CultureConstants.DefaultCulture, "[STC] {0} :: {1} {2}", charName, skillName, skillLevelString), body.ToString()));
            }

            // Long format
            if (character.Plans.Count > 0)
            {
                body.AppendFormat(CultureConstants.DefaultCulture, "Next skills listed in plans:{0}{0}", Environment.NewLine);
            }

            foreach (var plan in character.Plans)
            {
                if (plan.Count > 0)
                {
                    // Print plan name
                    CharacterScratchpad scratchpad = new CharacterScratchpad(character);
                    body.AppendFormat(CultureConstants.DefaultCulture, "{0}:{1}", plan.Name, Environment.NewLine);

                    // Scroll through entries
                    int i       = 0;
                    int minDays = 1;
                    foreach (PlanEntry entry in plan)
                    {
                        TimeSpan trainTime = scratchpad.GetTrainingTime(entry.Skill, entry.Level, TrainingOrigin.FromPreviousLevelOrCurrent);

                        // Only print the first three skills, and the very long skills
                        // (first limit is one day, then we add skills duration)
                        if (++i <= 3 || trainTime.Days > minDays)
                        {
                            if (i > 3)
                            {
                                // Print long message once
                                if (minDays == 1)
                                {
                                    body.AppendFormat(CultureConstants.DefaultCulture, "{1}Longer skills from {0}:{1}", plan.Name, Environment.NewLine);
                                }

                                minDays = trainTime.Days + minDays;
                            }
                            body.AppendFormat(CultureConstants.DefaultCulture, "\t{0}", entry);

                            // Notes
                            if (entry.Notes != null && entry.Notes.Length > 0)
                            {
                                body.AppendFormat(CultureConstants.DefaultCulture, " ({0})", entry.Notes);
                            }

                            // Training time
                            if (trainTime.Days > 0)
                            {
                                body.AppendFormat(CultureConstants.DefaultCulture, " - {0}d, {1}", trainTime.Days, trainTime);
                            }
                            else
                            {
                                body.AppendFormat(CultureConstants.DefaultCulture, " - {0}", trainTime);
                            }

                            body.AppendLine();
                        }
                    }
                    body.AppendLine();
                }
            }

            string subject = String.Format(CultureConstants.DefaultCulture, "{0} has finished training {1} {2}", charName, skillName, skillLevelString);

            return(SendMail(Settings.Notifications, subject, body.ToString()));
        }
Пример #23
0
        /// <summary>
        /// Compute the best possible attributes to fulfill the given trainings array
        /// </summary>
        /// <param name="skills"></param>
        /// <param name="baseScratchpad"></param>
        /// <param name="maxDuration"></param>
        /// <returns></returns>
        private static CharacterScratchpad Optimize <T>(IEnumerable <T> skills, CharacterScratchpad baseScratchpad, TimeSpan maxDuration)
            where T : ISkillLevel
        {
            CharacterScratchpad bestScratchpad = new CharacterScratchpad(baseScratchpad);
            CharacterScratchpad tempScratchpad = new CharacterScratchpad(baseScratchpad);
            TimeSpan            baseTime       = baseScratchpad.TrainingTime;
            TimeSpan            bestTime       = TimeSpan.MaxValue;
            int bestSkillCount = 0;

            // Now, we have the points to spend, let's perform all the
            // combinations (less than 11^4 = 14,641)
            for (int per = 0; per <= EveConstants.MaxRemappablePointsPerAttribute; per++)
            {
                // WIL
                int maxWillpower = EveConstants.SpareAttributePointsOnRemap - per;
                for (int will = 0; will <= maxWillpower && will <= EveConstants.MaxRemappablePointsPerAttribute; will++)
                {
                    // INT
                    int maxIntelligence = maxWillpower - will;
                    for (int intell = 0; intell <= maxIntelligence && intell <= EveConstants.MaxRemappablePointsPerAttribute; intell++)
                    {
                        // MEM
                        int maxMemory = maxIntelligence - intell;
                        for (int mem = 0; mem <= maxMemory && mem <= EveConstants.MaxRemappablePointsPerAttribute; mem++)
                        {
                            // CHA
                            int cha = maxMemory - mem;

                            // Reject invalid combinations
                            if (cha <= EveConstants.MaxRemappablePointsPerAttribute)
                            {
                                // Resets the scratchpad
                                tempScratchpad.Reset();

                                // Set new attributes
                                tempScratchpad.Memory.Base       = mem + EveConstants.CharacterBaseAttributePoints;
                                tempScratchpad.Charisma.Base     = cha + EveConstants.CharacterBaseAttributePoints;
                                tempScratchpad.Willpower.Base    = will + EveConstants.CharacterBaseAttributePoints;
                                tempScratchpad.Perception.Base   = per + EveConstants.CharacterBaseAttributePoints;
                                tempScratchpad.Intelligence.Base = intell + EveConstants.CharacterBaseAttributePoints;

                                // Train skills
                                int tempSkillCount = 0;
                                foreach (var skill in skills)
                                {
                                    tempSkillCount++;
                                    tempScratchpad.Train(skill);

                                    // Did it go over max duration ?
                                    if (tempScratchpad.TrainingTime - baseTime > maxDuration)
                                    {
                                        break;
                                    }

                                    // Did it go over the best time so far without training more skills ?
                                    if (tempSkillCount <= bestSkillCount && tempScratchpad.TrainingTime > bestTime)
                                    {
                                        break;
                                    }
                                }

                                // Did it manage to train more skills before the max duration,
                                // or did it train the same number of skills in a lesser time ?
                                if (tempSkillCount > bestSkillCount || (tempSkillCount == bestSkillCount && tempScratchpad.TrainingTime < bestTime))
                                {
                                    bestScratchpad.Reset();
                                    bestScratchpad.Memory.Base       = tempScratchpad.Memory.Base;
                                    bestScratchpad.Charisma.Base     = tempScratchpad.Charisma.Base;
                                    bestScratchpad.Willpower.Base    = tempScratchpad.Willpower.Base;
                                    bestScratchpad.Perception.Base   = tempScratchpad.Perception.Base;
                                    bestScratchpad.Intelligence.Base = tempScratchpad.Intelligence.Base;
                                    bestTime       = tempScratchpad.TrainingTime;
                                    bestSkillCount = tempSkillCount;
                                }
                            }
                        }
                    }
                }
            }

            // Return the best scratchpad found
            return(bestScratchpad);
        }