From 0392cae46ee5bcad95e68e23f9c69f563db732c8 Mon Sep 17 00:00:00 2001 From: John Buck Date: Tue, 13 Jan 2026 08:07:42 -0500 Subject: [PATCH 1/3] i_1175 First pass at displaying correct ranks This commit is an incomplete solution. While it does set the ranks according to the number of problems solved for "obeyFreeze" scoreboards, it does not maintain individual ranks for medalists. In addition, the teams within a number solved grouping should be sorted alphabetically by institution - they are not. This is a WIP. --- .../core/scoring/DefaultScoringAlgorithm.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java b/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java index 8b8c65286..aee5f3731 100644 --- a/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java +++ b/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java @@ -410,10 +410,10 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi } summaryMemento.putLong("problemCount", problems.length); - + Site[] sites = theContest.getSites(); summaryMemento.putInteger("siteCount", sites.length); - + Group[] groups = theContest.getGroups(); boolean bGroupsExcluded = false; if (groups != null) { @@ -425,7 +425,7 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi //Note also that generateSummaryTotalsForProblem() already existed and was inserting MOST of that information in the output already; // the "colorList" processing code which used to be here was simply added to that method instead (adding internalId, letter, and // url to the elements), avoiding duplication of problem description data in the output XML. - + if (runs == null) { // Note: we do not deal with divisionNumber here since // 1) it is being deprecated @@ -521,7 +521,7 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi treeMap.put(record, record); } - createStandingXML(treeMap, mementoRoot, accountList, problems, problemsIndexHash, groups, theContest, summaryMemento, bGroupsExcluded); + createStandingXML(treeMap, mementoRoot, accountList, problems, problemsIndexHash, groups, theContest, summaryMemento, bGroupsExcluded, isThawn); } // mutex @@ -655,7 +655,7 @@ private boolean dumpGroupList(Group[] groups, IMemento memento, List want */ private void createStandingXML (TreeMap treeMap, XMLMemento mementoRoot, AccountList accountList, Problem[] problems, Hashtable problemsIndexHash, Group[] groups, - IInternalContest theContest, IMemento summaryMememento, boolean excludedGroups) { + IInternalContest theContest, IMemento summaryMememento, boolean excludedGroups, boolean isThawn) { ContestInformation contestInformation = theContest.getContestInformation(); // easy access @@ -755,7 +755,7 @@ private void createStandingXML (TreeMap treeMa StandingsRecord standingsRecord = (StandingsRecord)o; indexRank++; - if (!isTeamTied(standingsRecord, numSolved, score, lastSolved)) { + if (!isTeamTied(standingsRecord, numSolved, score, lastSolved, isThawn)) { numSolved = standingsRecord.getNumberSolved(); score = standingsRecord.getPenaltyPoints(); lastSolved = standingsRecord.getLastSolved(); @@ -816,7 +816,7 @@ private void createStandingXML (TreeMap treeMa int groupIndex = groupIndexHash.get(group).intValue(); // do the same thing as above, now for the group groupIndexRank[groupIndex]++; - if (!isTeamTied(standingsRecord,groupNumSolved[groupIndex], groupScore[groupIndex],groupLastSolved[groupIndex])) { + if (!isTeamTied(standingsRecord,groupNumSolved[groupIndex], groupScore[groupIndex],groupLastSolved[groupIndex], isThawn)) { groupNumSolved[groupIndex] = standingsRecord.getNumberSolved(); groupScore[groupIndex] = standingsRecord.getPenaltyPoints(); groupLastSolved[groupIndex] = standingsRecord.getLastSolved(); @@ -835,7 +835,7 @@ private void createStandingXML (TreeMap treeMa if (divisionIndexHash.containsKey(group)) { int divisionIndex = divisionIndexHash.get(group).intValue()-1; divisionIndexRank[divisionIndex]++; - if (!isTeamTied(standingsRecord, divisionNumSolved[divisionIndex], divisionScore[divisionIndex],divisionLastSolved[divisionIndex])) { + if (!isTeamTied(standingsRecord, divisionNumSolved[divisionIndex], divisionScore[divisionIndex],divisionLastSolved[divisionIndex], isThawn)) { divisionNumSolved[divisionIndex] = standingsRecord.getNumberSolved(); divisionScore[divisionIndex] = standingsRecord.getPenaltyPoints(); divisionLastSolved[divisionIndex] = standingsRecord.getLastSolved(); @@ -1001,12 +1001,19 @@ boolean isValidJudgement(Run run) { * @param numSolved * @param score * @param lastSolved + * @param isThawn - if the contest is finalized and obeyfreeze is in effect (public board) and the board is unfrozen * @return True if the long parameters match the corresponding numbers in the StandingsRecord */ - boolean isTeamTied(StandingsRecord standingsRecord, long numSolved, long score, long lastSolved) { + boolean isTeamTied(StandingsRecord standingsRecord, long numSolved, long score, long lastSolved, boolean isThawn) { if (numSolved != standingsRecord.getNumberSolved()) { return false; } + // JB - Need to determine # of metals and only do this if metal count < rank + // We may have to use the FinalsStandingsRecordComparator in getStandings() if isThawn is set there so + // that the teams will be sorted by name within a "# solved" block. + if(isThawn) { + return(true); + } if (score != standingsRecord.getPenaltyPoints()) { return false; } @@ -1034,7 +1041,7 @@ private void generateSummaryTotalsForProblem(Problem[] problems, Hashtable Date: Tue, 27 Jan 2026 17:52:02 -0500 Subject: [PATCH 2/3] i_1175 complete changes for final public scoreboard In the case of the final public scoreboard using WF honors ranking (that is, obeyFreeze, unfrozen and finalized), we generate the XML standings report slightly differently. We use honors grouping logic so all teams in an honors group have the same rank (except for medalists - they all have their own rank, unless really tied). CI: Add Utilities method to return a default FinalizeData object (4g/4s/4b). Also charge ResultsFile to use the new method and remove its copy of the code. CI: In NSA, there was a bug where it wasn't properly counting Judged runs (CE's don't count as judged runs if CE's are set to be ignored). CI: Add code to the index.xsl to generate the HTML page for the public final scoreboard with "HM" as the rank for Honorable Mention teams, rather than putting a bogus rank there. --- data/xsl/index.xsl | 18 +- .../csus/ecs/pc2/core/model/FinalizeData.java | 19 ++- .../core/scoring/DefaultScoringAlgorithm.java | 157 ++++++++++++++---- .../pc2/core/scoring/NewScoringAlgorithm.java | 17 +- .../csus/ecs/pc2/exports/ccs/ResultsFile.java | 4 +- 5 files changed, 171 insertions(+), 44 deletions(-) diff --git a/data/xsl/index.xsl b/data/xsl/index.xsl index 8f077f58b..e817426df 100644 --- a/data/xsl/index.xsl +++ b/data/xsl/index.xsl @@ -234,7 +234,14 @@ - + + + HM + + + + + @@ -342,7 +349,14 @@ - + + + HM + + + + + diff --git a/src/edu/csus/ecs/pc2/core/model/FinalizeData.java b/src/edu/csus/ecs/pc2/core/model/FinalizeData.java index af58b213d..a7f2a108c 100644 --- a/src/edu/csus/ecs/pc2/core/model/FinalizeData.java +++ b/src/edu/csus/ecs/pc2/core/model/FinalizeData.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2026 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.model; import java.io.Serializable; @@ -199,4 +199,21 @@ public int getHighHonorSolvedCount() { public int getHonorSolvedCount() { return honorSolvedCount; } + + + /** + * Generate some default finalize data + * @return FinalizeData object + */ + public static FinalizeData getDefaultFinalizeData() + { + FinalizeData finalizeData = new FinalizeData(); + finalizeData.setGoldRank(4); + finalizeData.setSilverRank(8); + finalizeData.setBronzeRank(12); + finalizeData.setCertified(false); + finalizeData.setComment("Preliminary Results - Contest not Finalized"); + finalizeData.setUseWFGroupRanking(true); + return(finalizeData); + } } diff --git a/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java b/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java index aee5f3731..776d3fabb 100644 --- a/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java +++ b/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2026 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.scoring; import java.io.IOException; @@ -354,6 +354,8 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi this.log = inputLog; long freezeSeconds = -1; boolean isThawn = false; + FinalizeData finalizeData = null; + if (obeyFreeze) { String freezeTime = theContest.getContestInformation().getFreezeTime(); try { @@ -366,7 +368,7 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi log.warning("Could not convert '"+freezeTime+"' to seconds"); throw new InvalidParameterException("Invalid freezeTime "+freezeTime); } - FinalizeData finalizeData = theContest.getFinalizeData(); // sometimes, eg junit this is null + finalizeData = theContest.getFinalizeData(); // sometimes, eg junit this is null if (finalizeData != null && finalizeData.isCertified() && theContest.getContestInformation().isUnfrozen()) { isThawn = true; } @@ -512,16 +514,46 @@ public String getStandings(IInternalContest theContest, Run[] runs, Integer divi applyScoringAdjustments(standingsRecordHash, accountList); + boolean wfStandings = false; + int medianSolved = 0; + + DefaultStandingsRecordComparator dsrc = new DefaultStandingsRecordComparator(); + dsrc.setCachedAccountList(accountList); + // use TreeMap to sort - DefaultStandingsRecordComparator src = new DefaultStandingsRecordComparator(); - src.setCachedAccountList(accountList); - TreeMap treeMap = new TreeMap(src); + TreeMap treeMap = new TreeMap(dsrc); Collection enumeration = standingsRecordHash.values(); for (StandingsRecord record : enumeration) { treeMap.put(record, record); } - createStandingXML(treeMap, mementoRoot, accountList, problems, problemsIndexHash, groups, theContest, summaryMemento, bGroupsExcluded, isThawn); + // Determine how we want to sort the teams. We may have to re-sort using WF rankings for the public final scoreboard + // For the public scoreboard, isThawn is true, however, we only want to use WF rankings if it is specified + // to do so, and, it's for the entire contest, not just a group(s) + // If you're wondering why we sort twice (once above, and once in this "if" block: + // We need to get the median solved, so, we have to sort normally, + // just to get the median so the FinalsStandingsRecordComparator can use it. + if(isThawn && finalizeData.isUseWFGroupRanking() && (wantedGroups == null || wantedGroups.isEmpty())) { + FinalsStandingsRecordComparator fsrc = new FinalsStandingsRecordComparator(); + fsrc.setCachedAccountList(accountList); + fsrc.setLastRank(finalizeData.getBronzeRank()); + if (finalizeData.getHonorSolvedCount() != 0) { + medianSolved = finalizeData.getHonorSolvedCount(); + } else { + medianSolved = getMedian(treeMap.keySet().toArray(new StandingsRecord[0])); + } + fsrc.setMedian(medianSolved); + wfStandings = true; + + // Now we have to re-sort the treeMap using WF sorting + treeMap = new TreeMap(fsrc); + enumeration = standingsRecordHash.values(); + for (StandingsRecord record : enumeration) { + treeMap.put(record, record); + } + } + + createStandingXML(treeMap, mementoRoot, accountList, problems, problemsIndexHash, groups, theContest, summaryMemento, bGroupsExcluded, wfStandings, medianSolved); } // mutex @@ -655,9 +687,12 @@ private boolean dumpGroupList(Group[] groups, IMemento memento, List want */ private void createStandingXML (TreeMap treeMap, XMLMemento mementoRoot, AccountList accountList, Problem[] problems, Hashtable problemsIndexHash, Group[] groups, - IInternalContest theContest, IMemento summaryMememento, boolean excludedGroups, boolean isThawn) { + IInternalContest theContest, IMemento summaryMememento, boolean excludedGroups, boolean isWFStandings, int medianSolved) { ContestInformation contestInformation = theContest.getContestInformation(); + // This may be needed if using WF Standings + int lastMedalRank = 0; + // easy access Hashtable groupHash = new Hashtable(); Hashtable groupIndexHash = new Hashtable(); @@ -695,7 +730,16 @@ private void createStandingXML (TreeMap treeMa divisionCount = highestFound; String teamVarDisplayString = contestInformation.getTeamScoreboardDisplayFormat(); - StandingsRecord[] srArray = new StandingsRecord[treeMap.size()]; + // get the sorted treeMap as an array for createCitationRankInformation and later for medal citations + StandingsRecord[] srArray = treeMap.values().toArray(new StandingsRecord[0]); + + // If doing public WF style standings, we assign the block ranks now + if(isWFStandings) { + if(Utilities.isDebugMode()) { + System.err.println("createStandingsXML: Generating Final board for public (obeyFreeze)"); + } + assignRanksBlock(srArray, theContest, medianSolved); + } Collection coll = treeMap.values(); Iterator iterator = coll.iterator(); @@ -754,16 +798,19 @@ private void createStandingXML (TreeMap treeMa Object o = iterator.next(); StandingsRecord standingsRecord = (StandingsRecord)o; - indexRank++; - if (!isTeamTied(standingsRecord, numSolved, score, lastSolved, isThawn)) { - numSolved = standingsRecord.getNumberSolved(); - score = standingsRecord.getPenaltyPoints(); - lastSolved = standingsRecord.getLastSolved(); - rank = indexRank; - standingsRecord.setRankNumber(rank); - } else { - // current user tied with last user, so same rank - standingsRecord.setRankNumber(rank); + // Only assign rank if not doing WF final rankings on public scoreboard because we did it above with assignRankBlocks() + if(!isWFStandings) { + indexRank++; + if (!isTeamTied(standingsRecord, numSolved, score, lastSolved)) { + numSolved = standingsRecord.getNumberSolved(); + score = standingsRecord.getPenaltyPoints(); + lastSolved = standingsRecord.getLastSolved(); + rank = indexRank; + standingsRecord.setRankNumber(rank); + } else { + // current user tied with last user, so same rank + standingsRecord.setRankNumber(rank); + } } // mementoRoot.putMemento(standingsRecord.toMemento()); long totalAttempts = 0; @@ -816,7 +863,7 @@ private void createStandingXML (TreeMap treeMa int groupIndex = groupIndexHash.get(group).intValue(); // do the same thing as above, now for the group groupIndexRank[groupIndex]++; - if (!isTeamTied(standingsRecord,groupNumSolved[groupIndex], groupScore[groupIndex],groupLastSolved[groupIndex], isThawn)) { + if (!isTeamTied(standingsRecord,groupNumSolved[groupIndex], groupScore[groupIndex],groupLastSolved[groupIndex])) { groupNumSolved[groupIndex] = standingsRecord.getNumberSolved(); groupScore[groupIndex] = standingsRecord.getPenaltyPoints(); groupLastSolved[groupIndex] = standingsRecord.getLastSolved(); @@ -835,7 +882,7 @@ private void createStandingXML (TreeMap treeMa if (divisionIndexHash.containsKey(group)) { int divisionIndex = divisionIndexHash.get(group).intValue()-1; divisionIndexRank[divisionIndex]++; - if (!isTeamTied(standingsRecord, divisionNumSolved[divisionIndex], divisionScore[divisionIndex],divisionLastSolved[divisionIndex], isThawn)) { + if (!isTeamTied(standingsRecord, divisionNumSolved[divisionIndex], divisionScore[divisionIndex],divisionLastSolved[divisionIndex])) { divisionNumSolved[divisionIndex] = standingsRecord.getNumberSolved(); divisionScore[divisionIndex] = standingsRecord.getPenaltyPoints(); divisionLastSolved[divisionIndex] = standingsRecord.getLastSolved(); @@ -901,13 +948,14 @@ private void createStandingXML (TreeMap treeMa // entire contest, not one particular group (or groups). So, if any groups were excluded // from the scoreboard, we do not fill in citations. if(!excludedGroups) { - // Now go back and fill in any award citations for the teams - ResultsFile resultsInfo = new ResultsFile(); - // Note that createCitationRankInformation() may change the order of srArray - CitationRankInformation ri = resultsInfo.createCitationRankInformation(theContest, srArray); if(index == teamStandingsMementos.size()) { int teamRank, sIndex; IMemento standingsRecordMemento; + // Now go back and fill in any award citations for the teams + ResultsFile resultsInfo = new ResultsFile(); + // Note that createCitationRankInformation() may change the order of srArray + CitationRankInformation ri = resultsInfo.createCitationRankInformation(theContest, srArray); + for(sIndex = 0; sIndex < index; ) { standingsRecordMemento = teamStandingsMementos.get(sIndex); teamRank = srArray[sIndex].getRankNumber(); @@ -938,6 +986,58 @@ private void createStandingXML (TreeMap treeMa } } + /** + * Assigns WF style "honors" ranking values. + * This is only used to generate the public (obeyFreeze), unfrozen, finalized scoreboard. + * This logic similar to ResultsFile.createFileLines(). + * + * @param srArray - sorted array of standings records + * @param theContest + * @param median - number of problems solved by median team + */ + private void assignRanksBlock(StandingsRecord [] srArray, IInternalContest theContest, int median) { + + int rank = 1; + StandingsRecord sr, prev = null; + int idx = 0; + int numRecs = srArray.length; + FinalizeData finalizeData = theContest.getFinalizeData(); + boolean didFirstHonorableMention = false; + + // Tammy: just in case it IS null; purportedly it may be null for Junit's, but that is unconfirmed. + if(finalizeData == null) { + finalizeData = FinalizeData.getDefaultFinalizeData(); + } + + if(Utilities.isDebugMode()) { + System.err.printf(" rank=%d sr.getNumberSolved=%d prev.getNumberSolver=%d numInBlock=%d%n", + rank, sr.getNumberSolved(), prev.getNumberSolved(), numInBlock); + } + // For medals, each team is ranked. Or, if after medals, teams are grouped by # solved until honorable mention + if (rank <= alwaysRanked || (!didFirstHonorableMention && sr.getNumberSolved() != prev.getNumberSolved())) { + rank++; + rank += numInBlock; + numInBlock = 0; + if(sr.getNumberSolved() < median) { + // From now on, all teams are the same rank since they're all honorable mention + if(Utilities.isDebugMode()) { + System.err.printf(" FOUND first honorable mention at idx=%d rank=%d%n", idx, rank); + } + didFirstHonorableMention = true; + } + } else { + numInBlock++; + } + + sr.setRankNumber(rank); + prev = sr; + if(Utilities.isDebugMode()) { + System.err.printf(" Setting team at index %d(%s) to rank %d%n", idx, sr.getClientId().getName(), rank); + } + } + } + + /** * Input is a sorted ranking list. What is the number of problems solved by the median team? * @@ -1001,19 +1101,12 @@ boolean isValidJudgement(Run run) { * @param numSolved * @param score * @param lastSolved - * @param isThawn - if the contest is finalized and obeyfreeze is in effect (public board) and the board is unfrozen * @return True if the long parameters match the corresponding numbers in the StandingsRecord */ - boolean isTeamTied(StandingsRecord standingsRecord, long numSolved, long score, long lastSolved, boolean isThawn) { + boolean isTeamTied(StandingsRecord standingsRecord, long numSolved, long score, long lastSolved) { if (numSolved != standingsRecord.getNumberSolved()) { return false; } - // JB - Need to determine # of metals and only do this if metal count < rank - // We may have to use the FinalsStandingsRecordComparator in getStandings() if isThawn is set there so - // that the teams will be sorted by name within a "# solved" block. - if(isThawn) { - return(true); - } if (score != standingsRecord.getPenaltyPoints()) { return false; } diff --git a/src/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithm.java b/src/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithm.java index 01c4118d5..baa5040eb 100644 --- a/src/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithm.java +++ b/src/edu/csus/ecs/pc2/core/scoring/NewScoringAlgorithm.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2026 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.scoring; import java.io.IOException; @@ -736,12 +736,6 @@ public ProblemScoreRecord createProblemScoreRecord(Run[] runs, Problem problem, ignoreSub = false; - if (run.isJudged()) { - numberJudged++; - } else { - numberPending++; - } - if (run.isSolved() && solutionTime == 0) { // set to solved, set solution time solved = true; @@ -771,6 +765,15 @@ public ProblemScoreRecord createProblemScoreRecord(Run[] runs, Problem problem, } } + if (run.isJudged()) { + // Only count as judged run if not ignoring it. + if(!ignoreSub) { + numberJudged++; + } + } else { + numberPending++; + } + if(!ignoreSub) { numberSubmissions++; } diff --git a/src/edu/csus/ecs/pc2/exports/ccs/ResultsFile.java b/src/edu/csus/ecs/pc2/exports/ccs/ResultsFile.java index 2d6734e1f..8bfee5165 100644 --- a/src/edu/csus/ecs/pc2/exports/ccs/ResultsFile.java +++ b/src/edu/csus/ecs/pc2/exports/ccs/ResultsFile.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2025 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2026 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.exports.ccs; import java.util.ArrayList; @@ -170,7 +170,7 @@ public String[] createFileLines(IInternalContest contest, Group group, String re int highHonorSolvedCount = 0; if (finalizeData == null) { - finalizeData = GenDefaultFinalizeData(); + finalizeData = FinalizeData.getDefaultFinalizeData(); } // Only use Bill honors WF ranking rules if not for a specific group and we're doing WF ranks // Calculating the Bill honors rules doesn't make sense for sub-groups From 155bbdadf212c9cf78cc4867af7f32daedcebf90 Mon Sep 17 00:00:00 2001 From: John Buck Date: Tue, 27 Jan 2026 17:53:47 -0500 Subject: [PATCH 3/3] i_1175 Include the DSA because it was forgotten in previous commit. Changes to support public final scoreboard for WF Honors rules. --- .../core/scoring/DefaultScoringAlgorithm.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java b/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java index 776d3fabb..e3b4c34af 100644 --- a/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java +++ b/src/edu/csus/ecs/pc2/core/scoring/DefaultScoringAlgorithm.java @@ -1009,6 +1009,29 @@ private void assignRanksBlock(StandingsRecord [] srArray, IInternalContest theCo finalizeData = FinalizeData.getDefaultFinalizeData(); } + // First one, handle it special to make the for loop below easier. Basically, we're priming the pump here (prev). + if (numRecs > 0) { + sr = srArray[idx]; + sr.setRankNumber(rank); + prev = sr; + idx++; + } + + int numInBlock = 0; + + // We always rank teams uniquely that get medals, and, the first team AFTER the medals is assigned + // its own rank as well. + int alwaysRanked = finalizeData.getBronzeRank(); + + if(finalizeData.getHonorSolvedCount() > 0) { + median = finalizeData.getHonorSolvedCount(); + } + + if(Utilities.isDebugMode()) { + System.err.printf("assignRankBlock: alwaysRanked=%d median=%d%n", alwaysRanked, median); + } + for(; idx < numRecs; idx++) { + sr = srArray[idx]; if(Utilities.isDebugMode()) { System.err.printf(" rank=%d sr.getNumberSolved=%d prev.getNumberSolver=%d numInBlock=%d%n", rank, sr.getNumberSolved(), prev.getNumberSolved(), numInBlock);