// Once Teams are completely balanced, output and save them into an Excel sheet. private void save_TeamsExcel(Balance outBalance) { SaveFileDialog sfd_Excel = new SaveFileDialog(); sfd_Excel.Filter = "Excel Sheet (*.xlsx)|*.xlsx"; sfd_Excel.Title = "Save Teams"; if (sfd_Excel.ShowDialog() == DialogResult.OK) { Application.DoEvents(); Cursor.Current = Cursors.WaitCursor; // Make an Excel Sheet Excel.Application xlApp = new Excel.Application(); Excel.Workbook xlWorkBook; Excel.Worksheet xlWorkSheet; object mis = System.Reflection.Missing.Value; xlWorkBook = xlApp.Workbooks.Add(mis); xlWorkSheet = (Excel.Worksheet)xlWorkBook.ActiveSheet; xlWorkSheet.Name = "Teams Balance"; try { xlWorkSheet.PageSetup.PaperSize = Excel.XlPaperSize.xlPaper11x17; xlWorkSheet.PageSetup.Orientation = Excel.XlPageOrientation.xlLandscape; } catch { } #region Make Excel Sheet xlWorkSheet.Columns["A"].ColumnWidth = 20.00; // Name xlWorkSheet.Columns["B"].ColumnWidth = 20.00; // Summoner Name xlWorkSheet.Columns["C"].ColumnWidth = 20.00; // Assigned Role xlWorkSheet.Columns["D"].ColumnWidth = 15.00; // Ranking xlWorkSheet.Cells[1, 1] = "Name"; xlWorkSheet.Cells[1, 2] = "Summoner Name"; xlWorkSheet.Cells[1, 3] = "Assigned Role"; xlWorkSheet.Cells[1, 4] = "Ranking"; xlWorkSheet.Rows[1].Font.Bold = true; xlWorkSheet.Rows[1].Font.Underline = true; xlWorkSheet.get_Range("A1", "D1").Cells.HorizontalAlignment = Excel.XlHAlign.xlHAlignCenter; int row = 3, team = 1; List <Role> roleList = new List <Role>(new Role[] { Role.TOP, Role.JNG, Role.MID, Role.ADC, Role.SUP }); Dictionary <Role, string> role2String = new Dictionary <Role, string>() { { Role.TOP, "Top" }, { Role.JNG, "Jungle" }, { Role.MID, "Mid" }, { Role.ADC, "ADC" }, { Role.SUP, "Support" }, { Role.NONE, "None" } }; foreach (Team teamSel in outBalance.teams) { xlWorkSheet.Cells[row, 1] = "Team " + team; xlWorkSheet.get_Range("A" + row, "D" + row).Merge(); foreach (Role role in roleList) { row++; Player player = teamSel.getPlayerRole(role); xlWorkSheet.Cells[row, 1] = player.name; string ignString = player.ign.ToString(); if (player.hasDuo()) { ignString += " (D)"; } xlWorkSheet.Cells[row, 2] = ignString; string roleString = role2String[player.assignedRole]; if (player.isAutoFilled()) { roleString += " (Autofilled)"; } else if (player.isRoleFilled()) { roleString += " (Filled)"; } else if (player.isSecondaryAssigned()) { roleString += " (Secondary)"; } xlWorkSheet.Cells[row, 3] = roleString; string ranking = player.getRank(); xlWorkSheet.Cells[row, 4] = ranking; xlWorkSheet.Cells[row, 4].Interior.Color = ColorTranslator.ToOle(MainForm.RankToColor(player.getTier())); } row++; row++; team++; } #endregion releaseObject(xlWorkSheet); string filename = sfd_Excel.FileName; try { xlApp.DisplayAlerts = false; xlWorkBook.SaveAs(filename); xlApp.Visible = true; } catch (Exception ex) { MessageBox.Show("Can't overwrite file. Please save it as another name." + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } releaseObject(xlWorkBook); releaseObject(xlApp); Cursor.Current = Cursors.Default; } }
// ----- PART 2: MEGA LOOP public bool balance(ref RichTextBox rtb) { int randSeed = startSeed - 1, currChecks = 0; Balance bestBalanceEver = new Balance(); Balance bestBalanceSeed = new Balance(); // This will store our bestBalance within that seed // MEGALOOP: Number of checks has not exceeded maxChecks while (currChecks < maxChecks) { int lowestSeedRange = int.MaxValue; currChecks++; randSeed++; // Preparations: Make a deep copy of roster that can be edited Dictionary <Name, Player> masterList = deepClone(roster); // Begin Dictionary <Role, List <Player> > assignRoleList = new Dictionary <Role, List <Player> >() { { Role.TOP, new List <Player>() }, { Role.JNG, new List <Player>() }, { Role.MID, new List <Player>() }, { Role.ADC, new List <Player>() }, { Role.SUP, new List <Player>() }, }; // Assign non-Fill Primary Roles try { assignRoles(ref masterList, ref assignRoleList, duoList, numTeams, randSeed, true); } catch (Exception ex) { string msg = "ERROR - Assigning primary roles. " + "Reason: " + ex.Message; write_ConsoleLog(msg, ref rtb); return(false); } // Assign non-Fill Secondary Roles try { assignRoles(ref masterList, ref assignRoleList, duoList, numTeams, randSeed, false); } catch (Exception ex) { string msg = "ERROR - Assigning secondary roles. " + "Reason: " + ex.Message; write_ConsoleLog(msg, ref rtb); return(false); } // The remaining masterList is now fillList. // MASTER-LOOP BEGIN while (masterList.Count > 0) { // Find role with smallest points, and add a random player Role addFillRole = roleMinPointsFill(assignRoleList, numTeams); Player addPlayer = masterList.Values.ToList().Max(); // Greedy approach addPlayer.assignedRole = addFillRole; assignRoleList[addFillRole].Add(addPlayer); masterList.Remove(addPlayer.ign); // Make sure the player does not have the same role as their duo // If so, randomly switch a solo player who is autoFill -> secondary -> Primary // from another role if (containsName(assignRoleList[addFillRole], addPlayer.duo)) { Player swapPlayer = aSoloFromRole(assignRoleList, addFillRole, addPlayer, randSeed); // Taking the assumption that the above function also takes into account that the swapped player // is not in the same role as their duo if (swapPlayer.getName() == null) { string msg = "ERROR - " + swapPlayer.getName() + " cannot swap. Too many duos."; write_ConsoleLog(msg, ref rtb); return(false); } Role swapRole = swapPlayer.assignedRole; assignRoleList[swapRole].Remove(swapPlayer); assignRoleList[addFillRole].Remove(addPlayer); swapPlayer.assignedRole = addFillRole; assignRoleList[addFillRole].Add(swapPlayer); addPlayer.assignedRole = swapRole; assignRoleList[swapRole].Add(addPlayer); } } // MASTER-LOOP END bool breakLoop = false; foreach (List <Player> roleList in assignRoleList.Values) { // If No AutoFill option is selected, check masterList if there is an AutoFill. If so, skip if (noAutoFill) { foreach (Player player in roleList) { if (player.isAutoFilled()) { breakLoop = true; string msg = "Seed " + randSeed + " has an Autofill"; write_ConsoleLog(msg, ref rtb); } } } // Validation: Should hopefully never occur if (roleList.Count != numTeams) { string msg = "ERROR - assignRoleList does not have " + numTeams + " teams. It might be impossible to form a Balance."; write_ConsoleLog(msg, ref rtb); return(false); } } if (breakLoop) { continue; } // Initialize the Balance abstract Balance currBalance = new Balance(assignRoleList, out lowestSeedRange); // All queues acting as a clock queue int currMaxIndex = currBalance.getMaxTeamIndex(); int currMinIndex = currBalance.getMinTeamIndex(); int rolesRemain = NUM_ROLES; Queue <Role> roleQ = new Queue <Role>(new Role[] { Role.TOP, Role.JNG, Role.MID, Role.ADC, Role.SUP }); Queue <int> maxIndexQ = new Queue <int>(); Queue <int> minIndexQ = new Queue <int>(); for (int i = 0; i < numTeams; ++i) { maxIndexQ.Enqueue(i); minIndexQ.Enqueue(i); } // Conditions: // 1) rolesRemaining == 0 // If lowestRange changes: rolesRemaining set back to 5 // To move onto the next Role // 1) Check every iteration of minTeamIndex (minCheckRemain == 0) // 2) Check every iteration of maxTeamIndex (maxCheckRemain == 0) // If lowestRange changes: both checkRema counters set back to numTeams // ROLE-LOOP BEGIN while (rolesRemain > 0) { Role checkRole = roleQ.Peek(); int minCheckRemain = numTeams, maxCheckRemain = numTeams; bool minFlag = true; // Check within each Role while (!(minCheckRemain == 0 && maxCheckRemain == 0)) { // Work on minIndex first int swapIndex = (minFlag) ? minIndexQ.Peek() : maxIndexQ.Peek(); int newRange = (minFlag) ? currBalance.switchPlayer(checkRole, currMinIndex, swapIndex) : currBalance.switchPlayer(checkRole, currMaxIndex, swapIndex); if (newRange < lowestSeedRange) { // Condition 2: New minRange bestBalanceSeed = deepClone(currBalance); currMinIndex = currBalance.getMinTeamIndex(); currMaxIndex = currBalance.getMaxTeamIndex(); lowestSeedRange = newRange; minCheckRemain = numTeams; maxCheckRemain = numTeams; rolesRemain = NUM_ROLES; minFlag = true; } else { // Condition 3: Continue iterating. Switch currBalance back if (minFlag) { minCheckRemain--; currBalance.switchPlayer(checkRole, currMinIndex, swapIndex); } else { maxCheckRemain--; currBalance.switchPlayer(checkRole, currMaxIndex, swapIndex); } if (minCheckRemain == 0) { minFlag = false; } } // Cycle through the clock queue if (minFlag) { minIndexQ.Dequeue(); minIndexQ.Enqueue(swapIndex); } else { maxIndexQ.Dequeue(); maxIndexQ.Enqueue(swapIndex); } } // Made it to the end of a complete role check rolesRemain--; roleQ.Dequeue(); roleQ.Enqueue(checkRole); } // ROLE-LOOP END // An entire "seedCheck" is finished here if (writeRangeConsole) { string msg = "Lowest range in Seed " + randSeed + ": " + lowestSeedRange; write_ConsoleLog(msg, ref rtb); } if (!bestOutput && lowestSeedRange == threshold) { DialogResult resultBox = MessageBox.Show("A balance was found with a Range of " + lowestSeedRange + " at seed " + randSeed + ".\nWould you like to close and save it? Pressing No continues the balance.", "Finished", MessageBoxButtons.YesNoCancel); if (resultBox == DialogResult.Yes) { save_TeamsExcel(bestBalanceSeed); writeTeamPoints(bestBalanceSeed, ref rtb); break; } else if (resultBox == DialogResult.Cancel) { break; } } else if (bestOutput && lowestSeedRange < bestBalanceEver.getRange()) { bestBalanceEver = deepClone(bestBalanceSeed); } } // MEGALOOP END Cursor.Current = Cursors.Default; // If we got to this point, that means bestBalance has our best teams if (!bestOutput && bestBalanceSeed.getRange() > threshold) { MessageBox.Show("Could not find a balance with the desired range of " + threshold + "."); } else if (bestOutput) { MessageBox.Show("The best balance possible for this roster is with a Range of " + bestBalanceEver.getRange() + "\nSaving the output.", "Finished", MessageBoxButtons.OK); save_TeamsExcel(bestBalanceEver); writeTeamPoints(bestBalanceEver, ref rtb); } return(true); }