/* Called in a background thread. Used to generate bitstrings of a specified length. These strings * are represented as an array of bools - true = 1, false = 0. This method is more efficient. * EX: ("11001") * * We use these bitstrings to represent all possible combinations of the sections (even invalid ones). * * @param e.Argument This value passed through the BackgroundWorker call is the length of the bitstring. */ private void generateAllBitStrings(object sender, DoWorkEventArgs e) { Dictionary<string, List<int>> classes = new Dictionary<string, List<int>>(); //Separate sections by class int i = 0; foreach (ClassSection section in possibleSections) { List<int> indices = new List<int>(); if (!classes.Keys.Contains(section.parentClass)) { indices.Add(i); classes.Add(section.parentClass, indices); } else { classes.TryGetValue(section.parentClass, out indices); indices.Add(i); } i++; } //Check each class for working combinations i = 0; Dictionary<int, List<DepartmentClass>> groups = new Dictionary<int, List<DepartmentClass>>(); foreach (var kv in classes) { short n = (short)kv.Value.Count; //The length of the bitstrings List<DepartmentClass> validClassCombinations = new List<DepartmentClass>(); List<ClassSection> sectionPool = getSectionPool(kv.Value, possibleSections); /* Progress tracking */ double targetStringCount = Math.Pow(2, n); //This is the target number of strings to generate double totalResults = 0; /* Begin generating bitstrings */ bool[] first = new bool[n]; //First string is all false for (short x= 0; x < n; x++) { first[x] = false; } bool[] intermediate = first; List<ClassSection> firstSections = checkBitstring(first, sectionPool); if (firstSections != null) { DepartmentClass temp = new DepartmentClass(); temp.sections = firstSections; validClassCombinations.Add(temp); } totalResults++; //For user-facing progress updates currentClassRunning++; ulong rollingTotal = 0; short multiplier = 1; ulong progressStep = (ulong)Math.Ceiling(targetStringCount / 100); this.Dispatcher.BeginInvoke(delegate { progress.Value = 0; }); MyStopwatch timer = new MyStopwatch(); /* Generating loop */ do { //Start timing if (rollingTotal == 0) { timer.start(); } //Make the next bitstring intermediate = generateNextBitstring(intermediate); //Update timeleft if (rollingTotal == Math.Pow(2, multiplier)) { float averageTime = (float)timer.stop() / (float)Math.Pow(2, multiplier); secondsRemaining = (int)((averageTime * (targetStringCount - totalResults)) / 1000.0); multiplier++; rollingTotal = 0; } //Update progress bar if (totalResults % progressStep == 0) { this.Dispatcher.BeginInvoke(delegate { progress.Value += 1; }); } //Increment counters totalResults++; rollingTotal++; //Check the bitstring we generated List<ClassSection> nextSections = checkBitstring(intermediate, sectionPool); if (nextSections != null) { DepartmentClass temp = new DepartmentClass(); temp.sections = nextSections; validClassCombinations.Add(temp); } } while (totalResults < targetStringCount); groups.Add(i, validClassCombinations); i++; } i = 1; //Combination two classes and weed out the combinations that aren't valid while (groups.Keys.Count > 1) { //Tournament! List<DepartmentClass> champCombination = new List<DepartmentClass>(); groups.TryGetValue(0, out champCombination); List<DepartmentClass> challengerCombination = new List<DepartmentClass>(); groups.TryGetValue(i, out challengerCombination); List<DepartmentClass> winnersCircle = new List<DepartmentClass>(); //Fight! foreach (DepartmentClass champ in champCombination) { foreach (DepartmentClass challenger in challengerCombination) { List<ClassSection> contender = new List<ClassSection>(); foreach (ClassSection section in champ.sections) { contender.Add(section); } foreach (ClassSection section in challenger.sections) { contender.Add(section); } if (combinationSuccessful(contender)) { DepartmentClass success = new DepartmentClass(); success.sections = contender; winnersCircle.Add(success); } } } groups.Remove(i); groups.Remove(0); groups.Add(0, winnersCircle); i++; } //Prepare the confirmed classed to be displayed List<DepartmentClass> confirmed = new List<DepartmentClass>(); groups.TryGetValue(0, out confirmed); i = 0; foreach (DepartmentClass parent in confirmed) { confirmedSections.Add(parent.sections); } //Report that we're done and ready for GUI update (sender as BackgroundWorker).ReportProgress(100, null); //Because the time remaining isn't an exact science, we cheat at the end and force the values to be where they should // at the end. secondsRemaining = 1; this.Dispatcher.BeginInvoke(delegate { progress.Value = 100; }); }
/** * Adds a list of courses to the possibility list that were picked from the class's list of sections * * @param sectionPool The class that contains the sections we want * @param randCount The number of random sections to return * @param randGen The random number generator to use. We make one if there isn't one specified * * @return The list of classes we picked */ private List<ClassSection> pickSections(DepartmentClass sectionPool, int randCount, Random randGen = null) { List<ClassSection> finalSections = new List<ClassSection>(); List<ClassSection> sectionsToRemove = new List<ClassSection>(); //Add classes that are not excluded to the temp pool foreach (ClassSection section in sectionPool.sections) { //If course is not in exlusions if (!section.excluded) { finalSections.Add(section); } } for (short i = 0; i < dayLimiters.Count; i++) { CheckBox chkDay = dayLimiters[i]; if (chkDay.IsChecked == false) continue; string day = convertIndexToDay(i); Tuple<ComboBox, ComboBox> dayMaster = timeCombos[i]; string time = ((ComboBoxItem)(dayMaster.Item1.SelectedItem)).Content + " - " + ((ComboBoxItem)(dayMaster.Item2.SelectedItem)).Content; foreach (ClassSection section in finalSections) { if (section.time.ToLower() == "tba" || !section.days.ToLower().Contains(day)) continue; if (doTimesIntersect(time, section.time)) { sectionsToRemove.Add(section); } } } //Remove the classes foreach (ClassSection removal in sectionsToRemove) { finalSections.Remove(removal); } //Now pick random selections from what's left if (limitRandom) { if (randCount >= finalSections.Count) { //Do nothing and add all of the final sections to the possible sections } else { if (randGen == null) { randGen = new Random((int)Math.Round(DateTime.Now.Ticks * 6911.0)); } List<ClassSection> randomSections = new List<ClassSection>(); for (; randCount > 0; randCount--) { //Pick a number from zero to the sections count int selection = (int)Math.Round(randGen.NextDouble()) * (finalSections.Count - 1); //Remove the index we picked randomSections.Add(finalSections[selection]); finalSections.RemoveAt(selection); } finalSections.Clear(); foreach (ClassSection section in randomSections) { finalSections.Add(section); } } } return finalSections; }