diff --git a/admin/judges_sa.php b/admin/judges_sa.php
index 1a61987..e835403 100644
--- a/admin/judges_sa.php
+++ b/admin/judges_sa.php
@@ -34,40 +34,6 @@ if($_SERVER['SERVER_ADDR']) {
exit;
}
-/*
-send_header("Judging teams automatic scheduler");
-
-
-echo i18n("The scheduler is running. Depending on the effort it may take
-several minutes to complete. It will generate a lot of output on this page,
-most of it is just information about what the scheduler is doing. You can copy
-this page and save it as a text file to have a record of what the scheduler
-did. (And this output helps us to debug problems with it if you encounter
-something that clearly isn't correct, just send us this output)");
-echo "
";
-echo "
";
-echo i18n("When complete, a green bar will appear at the BOTTOM of this page
-saying that everything has completed successfully. When complete, you can use
-the following links to manage the Judging Teams and the Judges assigned to them
-(clicking on these links now will stop the scheduler).");
-echo "
";
-echo "
";
-echo "".i18n("Manage Judge Teams")."";
-echo "
";
-echo "".i18n("Manage Judge Members")."";
-echo "
";
-echo "
";
-echo i18n("If you get an error like: \"Fatal error: Maximum execution time of
-30 seconds exceeded...\" you will need to talk to your system admin and have
-them adjust the \"max_execution_time\" variable in the \"php.ini\" file from
-30(seconds) to something larger, like 900 (15 minutes). And then have them
-restart the webserver for the change to take effect");
-echo "
";
-echo "
";
-
-*/
-
-
//function TRACE() { }
//function TRACE_R() { }
function TRACE($str) { print($str); }
@@ -131,10 +97,12 @@ function judges_cost_function($annealer, $bucket_id, $ids)
// TRACE_R($ids);
$cost = 0;
- $have_chair = 0;
+ $have_chair = false;
+ $have_div2 = false;
if($bucket_id == 0) {
- /* This is the placeholder */
+ /* This is the placeholder for all judges, there's a slight
+ * cost for not using a judge */
$cost = count($ids) * 5;
// TRACE("Extra judge team cost=$cost\n");
return $cost;
@@ -180,7 +148,7 @@ function judges_cost_function($annealer, $bucket_id, $ids)
$cost += 2 * $dpref;
/* See if the judge is willing to chair a team */
- if($j['willing_chair'] == 'yes') $have_chair = 1;
+ if($j['willing_chair'] == 'yes') $have_chair = true;
/* For each lang the team needs that the judge doesn't have,
* increase the cost */
@@ -188,10 +156,19 @@ function judges_cost_function($annealer, $bucket_id, $ids)
$l = $t['langs'][$y];
if(!in_array($l, $j['languages'])) $cost += 25;
}
+
+ /* If divisional round2 is enabled, make sure there is a judge
+ * on the team for round2 */
+ if($j['available_for_divisional2'] == true) $have_div2 = true;
}
/* Huge penalty for a team without a willing chair, but only if the min judges per team >1 */
if(!$have_chair && $config['min_judges_per_team']>1)
$cost += 40;
+
+ /* Huge penalty for not having a round2 personal on the
+ * team */
+ if($have_div2 == false)
+ $cost += 40;
// TRACE("Team $bucket_id, cost is $cost\n");
@@ -238,6 +215,96 @@ function jdiv_compute_cost($annealer, $bucket_id, $ids)
return $cost;
}
+/* Returns true if a judge time preference indicates they are available for the
+ * specified round. Always returns true if judge time availablility selection
+ * is off */
+function judge_available_for_round($j, $r)
+{
+ global $config;
+ if($config['judges_availability_enable'] == 'no') return true;
+
+ foreach($j['availability'] as $a) {
+ if($a['start'] <= $r['starttime']
+ && $a['end'] >= $r['endtime']
+ && $a['date'] == $r['date'] ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+function judge_mark_for_round($j, $r)
+{
+ /* The judge has been assigned to round $r, modify their available to
+ * exclude any time that falls within this time
+ * TODO: modify the DB to store date/times in timestamps, so we don't
+ * have to deal with dates separately. */
+ global $config;
+ global $judges;
+ if($config['judges_availability_enable'] == 'no') return true;
+
+ /* Grab a pointer to the real judge, because we want to
+ * modify it, not a copy of it */
+ $ju =& $judges[$j['id']];
+
+ foreach($ju['availability'] as $key=>&$a) {
+ if($r['starttime'] >= $a['start'] && $r['starttime'] <= $a['end']) {
+ /* Round starts in the middle of this availablity slot
+ * modify this availabilty so it doesn't overlap */
+ /* This may cause $a['start'] == $a['end'], that's ok */
+ $a['end'] = $r['starttime'];
+ TRACE("adjust starttime\n");
+ }
+
+ if($r['endtime'] >= $a['start'] && $r['endtime'] <= $a['end']) {
+ /* Round ends in the middle of this availablity slot
+ * modify this availabilty so it doesn't overlap */
+ /* This may cause $a['start'] == $a['end'], that's ok */
+ $a['start'] = $r['endtime'];
+ TRACE("adjust starttime\n");
+ }
+
+ if($a['start'] >= $a['end']) {
+ /* Delete the whole round */
+ unset($ju['availability'][$key]);
+ }
+ }
+
+ print_r($ju['availability']);
+
+}
+
+/* UNUSED: should be moved to the timeslot manager to ensure rounds
+ * don't overlap. */
+function rounds_overlap($r1, $r2) {
+ $s1 = strtotime("{$r1['date']} {$r1['starttime']}");
+ $e1 = strtotime("{$r1['date']} {$r1['endtime']}");
+ $s2 = strtotime("{$r1['date']} {$r2['starttime']}");
+ $e2 = strtotime("{$r1['date']} {$r2['endtime']}");
+
+ if($s1 <= $s2 && $e1 > $s1) return true;
+ if($s1 > $s2 && $s1 < $e2) return true;
+ return false;
+}
+
+/* Print a judge */
+function pr_judge(&$jt, $jid)
+{
+ global $judges;
+ $j =& $judges[$jid];
+ print(" - {$j['name']} (".join(' ', $j['languages']).')');
+ print("(");
+ foreach($jt['cats'] as $c)
+ print("c{$c}={$j['cat_prefs'][$c]} ");
+ foreach($jt['divs'] as $d)
+ print("d{$d}={$j['div_prefs'][$d]} ");
+
+ print(")");
+ if($j['willing_chair'] == 'yes') print(" (chair) ");
+
+ print("\n");
+}
+
set_status("Loading Data From Database...");
TRACE("\n\n");
@@ -267,6 +334,27 @@ while($r=mysql_fetch_object($q)) {
TRACE(" {$r->lang} - {$r->langname}\n");
}
+TRACE("Loading Judging Round time data...\n");
+$round_divisional1 = NULL;
+$round_divisional2 = NULL;
+$round_special_awards = array();
+$round = array();
+$q = mysql_query("SELECT * FROM judges_timeslots WHERE round_id='0' AND `year`='{$config['FAIRYEAR']}'");
+/* Loads judges_timeslots.id, .starttime, .endtime, .date, .name */
+while($r = mysql_fetch_assoc($q)) {
+ TRACE(" id:{$r['id']} type:{$r['type']} name:{$r['name']}\n");
+ $round[] = $r;
+
+ if($r['type'] == 'divisional1') $round_divisional1 = $r;
+ if($r['type'] == 'divisional2') $round_divisional2 = $r;
+ if($r['type'] == 'special') $round_special_awards[] = $r;
+}
+
+if($round_divisional1 == NULL) {
+ echo "No divisional1 round defined! Aborting!\n";
+ exit;
+}
+
$jdiv = array();
TRACE("Loading Judging Division Configuration and Projects...\n");
$q=mysql_query("SELECT * FROM judges_jdiv");
@@ -280,8 +368,7 @@ while($r=mysql_fetch_object($q)) {
}
$keys = array_keys($jdiv);
-for($k=0; $kid;
+ print(" $id");
+ /* Clean out the judges_teams_link */
+ mysql_query("DELETE FROM judges_teams_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
+ print mysql_error();
+ /* Awards */
+ mysql_query("DELETE FROM judges_teams_awards_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
+ print mysql_error();
+ /* Timeslots */
+ mysql_query("DELETE FROM judges_teams_timeslots_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
+ print mysql_error();
+ /* Timeslots projects */
+ mysql_query("DELETE FROM judges_teams_timeslots_projects_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
+ print mysql_error();
+}
+echo "\n";
+
+/* Finally, delete all the autocreated judges teams */
+mysql_query("DELETE FROM judges_teams WHERE autocreate_type_id=1 AND year={$config['FAIRYEAR']}");
+print mysql_error();
+
+/* Also delete any judges_teams_link that link to teams that dont exist, just
+ * in case */
+$q=mysql_query("SELECT judges_teams_link.id, judges_teams.id AS judges_teams_id
+ FROM judges_teams_link
+ LEFT JOIN judges_teams ON judges_teams_link.judges_teams_id=judges_teams.id
+ WHERE judges_teams_link.year={$config['FAIRYEAR']}");
+$n=0;
+while($r=mysql_fetch_object($q)) {
+ if(!$r->judges_teams_id) {
+ mysql_query("DELETE FROM judges_teams_link WHERE id='$r->id'");
+ $n++;
+ }
+}
+print("Deleted $n orphaned team linkings\n");
+TRACE(" Done.\n");
+
+
+set_status("Loading Judges");
+
+$judges = judges_load_all();
+
+foreach($judges as &$j) {
+ if($j['judge_active'] == 'no') {
+ TRACE(" {$j['name']} has their judge profile deactivated, skipping.\n");
+ unset($judges[$j['id']]);
+ continue;
+ }
+ if($j['judge_complete'] == 'no') {
+ TRACE(" {$j['name']} hasn't completed their judge profile, skipping.\n");
+ unset($judges[$j['id']]);
+ continue;
+ }
+
+ $q = mysql_query("SELECT users_id FROM judges_teams_link WHERE
+ users_id='{$j['id']}'
+ AND year='{$config['FAIRYEAR']}'");
+ if(mysql_num_rows($q) != 0) {
+ TRACE(" {$j['name']} is already on a judging team, skipping.\n");
+ unset($judges[$j['id']]);
+ continue;
+ }
+
+ /* Load the judge time availability */
+ $q = mysql_query("SELECT * FROM judges_availability WHERE users_id='{$j['id']}' ORDER BY `start`");
+ if(mysql_num_rows($q) == 0) {
+ TRACE(" {$j['name']} hasn't selected any time availability, POTENTIAL BUG (they shouldn't be marked as complete).\n");
+ TRACE(" Ignoring this judge.\n");
+ unset($judges[$j['id']]);
+ continue;
+ }
+ while($r = mysql_fetch_assoc($q)) {
+ $j['availability'][] = $r;
+ }
+
+ /* Load special award preferences */
+ $q = mysql_query("SELECT award_awards.id,award_awards.name FROM
+ judges_specialaward_sel,award_awards
+ WHERE
+ award_awards.id=judges_specialaward_sel.award_awards_id
+ AND judges_specialaward_sel.users_id='{$j['id']}'
+ AND award_awards.year='{$config['FAIRYEAR']}'");
+ echo mysql_error();
+
+ if($j['special_award_only'] == 'yes') {
+ TRACE(" {$j['name']} is a special awards only.\n");
+ /* Find their special award id */
+ if(mysql_num_rows($q) == 0) {
+ TRACE(" NO special award selected! (removing special award only request)\n");
+ $j['special_award_only'] = 'no';
+ } else if(mysql_num_rows($q) > 1) {
+ TRACE(" More than ONE special award selected (removing special award only request):\n");
+ $j['special_award_only'] = 'no';
+
+ }
+ }
+
+ $j['special_awards'] = array();
+ while($r = mysql_fetch_object($q)) {
+ TRACE(" {$r->name}\n");
+ /* Add them to the SA judge list (modify the actual list, not
+ * $j, which is a copy */
+ $j['special_awards'][] = $r->id;
+ }
+}
+
+
+TRACE("Loaded ".count($judges)." judges\n");
+$jteam[0]['max_judges'] = count($judges);
+
+
+/* Load the numbers for any user-defined judge teams that already exist,
+ * these numbers will be off-limits for auto-assigning numbers */
+$q = mysql_query("SELECT * FROM judges_teams WHERE year={$config['FAIRYEAR']}");
+$used_judges_teams_numbers = array();
+while($i = mysql_fetch_assoc($q)) {
+ $used_judges_teams_numbers[] = $i['num'];
+}
+echo "The following judge team numbers are already used: \n";
+print_r($used_judges_teams_numbers);
+
+$next_judges_teams_number_try = 1;
+/* A function to get the next available number */
+function next_judges_teams_number()
+{
+ global $used_judges_teams_numbers;
+ global $next_judges_teams_number_try;
+
+ while(1) {
+ if(!in_array($next_judges_teams_number_try, $used_judges_teams_numbers)) break;
+
+ $next_judges_teams_number_try++;
+ }
+ $r = $next_judges_teams_number_try;
+ $next_judges_teams_number_try++;
+ return $r;
+}
+
+function judge_team_create($num, $name)
+{
+ global $config;
+ $name = mysql_escape_string($name);
+ mysql_query("INSERT INTO judges_teams (num,name,autocreate_type_id,year)
+ VALUES ('$num','$name','1','{$config['FAIRYEAR']}')");
+ $id = mysql_insert_id();
+ return $id;
+}
+
+function judge_team_add_judge($team_id, $users_id)
+{
+ global $config, $judges;
+ mysql_query("INSERT INTO judges_teams_link
+ (users_id,judges_teams_id,captain,year)
+ VALUES ('$users_id','$team_id','{$judges[$users_id]['willing_chair']}',
+ '{$config['FAIRYEAR']}')");
+}
+
+/****************************************************************************
+ * Round 1 Divisional Scheduling
+ * - Compute required divisional judge teams
+ * - Delete existing ones
+ * - Anneal Projects to Teams
+ * - Anneal Judtes to Projects
+ *
+ ***************************************************************************/
+
set_status("Computing required judging teams");
TRACE(" Each judging team may judge {$config['max_projects_per_team']} projects\n");
TRACE(" Each project must be judged {$config['times_judged']} times\n");
$keys = array_keys($jdiv);
-for($k=0; $kbucket[$x];
$jteam[$jteam_id]['sub'] = $x;
$jteam[$jteam_id]['jdiv_id'] = $jdiv_id;
@@ -370,8 +628,7 @@ for($k=0; $kid;
- print(" $id");
- /* Clean out the judges_teams_link */
- mysql_query("DELETE FROM judges_teams_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
- print mysql_error();
- /* Awards */
- mysql_query("DELETE FROM judges_teams_awards_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
- print mysql_error();
- /* Timeslots */
- mysql_query("DELETE FROM judges_teams_timeslots_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
- print mysql_error();
- /* Timeslots projects */
- mysql_query("DELETE FROM judges_teams_timeslots_projects_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
- print mysql_error();
-}
-/* Finally, delete all the autocreated judges teams */
-mysql_query("DELETE FROM judges_teams WHERE autocreate_type_id=1 AND year={$config['FAIRYEAR']}");
-print mysql_error();
-
-/* Also delete any judges_teams_link that link to teams that dont exist */
-$q=mysql_query("SELECT judges_teams_link.id, judges_teams.id AS judges_teams_id FROM judges_teams_link LEFT JOIN judges_teams ON judges_teams_link.judges_teams_id=judges_teams.id WHERE judges_teams_link.year={$config['FAIRYEAR']}");
-$n=0;
-while($r=mysql_fetch_object($q)) {
- if(!$r->judges_teams_id) {
- mysql_query("DELETE FROM judges_teams_link WHERE id='$r->id'");
- $n++;
- }
-}
-print("Deleted $n orphaned team linkings\n");
-TRACE(" Done.\n");
-
-set_status("Loading Judges");
-
-$judges = judges_load_all();
-$sa_judges = array();
-
+TRACE("Finding judges available for round1 divisional\n");
+$div1_judge_ids = array();
foreach($judges as $j) {
- $q = mysql_query("SELECT users_id FROM judges_teams_link WHERE
- users_id='{$j['id']}'
- AND year='{$config['FAIRYEAR']}'");
- if(mysql_num_rows($q) != 0) {
- TRACE(" {$j['name']} is already on a judging team, skipping.\n");
- unset($judges[$j['id']]);
- continue;
- }
+ if(judge_available_for_round($j, $round_divisional1) == false) continue;
+ if($j['special_award_only'] == 'yes') continue;
- if($j['special_award_only'] == 'yes') {
- TRACE("Judge {$j['name']} is a special awards only.\n");
- /* Find their special award id */
- $q = mysql_query("SELECT award_awards.id,award_awards.name FROM
- judges_specialaward_sel,award_awards
- WHERE
- award_awards.id=judges_specialaward_sel.award_awards_id
- AND judges_specialaward_sel.users_id='{$j['id']}'
- AND award_awards.year='{$config['FAIRYEAR']}'");
- echo mysql_error();
- if(mysql_num_rows($q) == 0) {
- TRACE(" - NO special award selected! (removing special award only request)\n");
- $judges[$j['id']]['special_award_only'] = 'no';
- } else if(mysql_num_rows($q) > 1) {
- TRACE(" - More than ONE special award selected (removing special award only request):\n");
- $judges[$j['id']]['special_award_only'] = 'no';
- }
- }
-
- $judges[$j['id']]['special_awards'] = array();
- while($r = mysql_fetch_object($q)) {
- TRACE(" {$r->name}\n");
- /* Add them to the SA judge list (modify the actual list, not
- * $j, which is a copy */
- $judges[$j['id']]['special_awards'][] = $r->id;
- }
-
- if($j['special_award_only'] == 'yes') {
- /* Add to sa judge list, remove from the eligible judge list */
- $sa_judges[$j['id']] = $judges[$j['id']];
- unset($judges[$j['id']]);
- }
+ /* If we get here, the judge is ok for div1 */
+ $div1_judge_ids[] = $j['id'];
}
-TRACE("Loaded ".count($judges)." judges, and ".count($sa_judges)." special-award-only judges.\n");
-$jteam[0]['max_judges'] = count($judges);
-
+TRACE(count($div1_judge_ids)." judges available for round1 divisional\n");
function judges_to_teams_update($progress, $total)
{
@@ -488,48 +666,16 @@ function judges_to_teams_update($progress, $total)
}
set_status("Assigning Judges to Teams");
-$judge_ids = array_keys($judges);
$e = $config['effort'];
-$a = new annealer(count($jteam), 25, $e, 0.98, judges_cost_function, $judge_ids);
+$a = new annealer(count($jteam), 25, $e, 0.98, judges_cost_function, $div1_judge_ids);
$a->set_update_callback(judges_to_teams_update);
$a->anneal();
-function pr_judge(&$jt, $jid)
-{
- global $judges;
- $j =& $judges[$jid];
- print(" - {$j['name']} (".join(' ', $j['languages']).')');
- print("(");
- for($x=0; $xmax;
-
-TRACE("Max Judging Team Number is currently $max_jteam_num\n");
-
for($x=1;$xbucket_cost[$x]} ");
+ print("Judging Team {$t['num']}: cost={$a->bucket_cost[$x]} ");
$lang_array = $t['langs'];
asort($lang_array);
$langstr = implode(' ', $lang_array);
@@ -541,47 +687,49 @@ for($x=1;$xbucket[$x];
for($y=0; $ybucket[0];
for($y=0; $ypick_move();
-
- /* See if $b1,$i1 is movable */
- $id1 = $a->bucket[$b1][$i1];
- $j1 =& $sa_judges[$id1];
-// print("J1:");
-// print_r($j1);
- if($j1['sa_only'] == 'yes') continue;
-
- if($i2 != -1) {
- $id2 = $a->bucket[$b2][$i2];
- $j2 =& $sa_judges[$id2];
-// print("J2:");
-// print_r($j2);
- if($j2['sa_only'] == 'yes') continue;
- }
-
- return array($b1, $i1, $b2, $i2);
- }
-}
-
if($config['scheduler_enable_sa_scheduling'] == 'yes') {
+ TRACE("Finding judges for special award round(s)\n");
+ foreach($round_special_awards as &$r) {
+ $r['available_judge_ids'] = array();
+ }
+
+ $total_judges = 0;
+ foreach($judges as &$j) {
+ foreach($round_special_awards as &$r) {
+ if(judge_available_for_round($j, $r) == true) {
+ $r['available_judge_ids'][] = $j['id'];
+ $total_judges++; /* It's ok to count the same judge twice */
+ }
+ }
+ }
+ unset($j);
+ unset($r);
+
set_status("Creating Special Award Judging Teams (one team per award)");
+
+ /* Load special awards */
$q = "SELECT award_awards.name,award_awards.id FROM award_awards,award_types
WHERE
award_awards.year='{$config['FAIRYEAR']}'
@@ -727,105 +918,214 @@ if($config['scheduler_enable_sa_scheduling'] == 'yes') {
$sa_jteam[0]['max_judges'] = 0;
$sa_jteam[0]['award_ids'] = array();
- /* Reload the jteam_id */
- $jteam_id = count($jteam);
-
$x=1;
+ $required_judges = 0;
while($i = mysql_fetch_object($r)) {
$projects = getProjectsNominatedForSpecialAward($i->id);
- $max_jteam_num++; /* Pre-increment before using */
-
+ /* Construct an internal team for annealing, and create
+ * a DB team too */
$pids = array_keys($projects);
- $sa_jteam[$x]['id'] = $jteam_id;
+ $sa_jteam[$x]['num'] = next_judges_teams_number();
+ $sa_jteam[$x]['id'] = judge_team_create($sa_jteam[$x]['num'], $i->name);
+ /* Note, we use $x instead of the ID, because the DB id could be zero. */
$sa_jteam[$x]['projects'] = $pids;
+ $sa_jteam[$x]['round'] = NULL;
$sa_jteam[$x]['sub'] = 0;
$sa_jteam[$x]['langs'] = array();
$min = floor(count($pids) / $config['projects_per_special_award_judge']) + 1;
$sa_jteam[$x]['min_judges'] = $min;
$sa_jteam[$x]['max_judges'] = $min;
$sa_jteam[$x]['award_ids'] = array($i->id);
+ $sa_jteam[$x]['name'] = $i->name;
- $tn = "{$i->name}";
- /* Write this team to the DB */
- mysql_query("INSERT INTO judges_teams (num,name,autocreate_type_id,year)
- VALUES ('$max_jteam_num','".mysql_escape_string($tn)."','1','{$config['FAIRYEAR']}')");
- $sa_jteam[$x]['id'] = mysql_insert_id();
-
- /* Link the award to this team */
+ $required_judges += $min;
+
+ /* Link the award to this team */
mysql_query("INSERT INTO judges_teams_awards_link (award_awards_id,judges_teams_id,year)
VALUES ('{$i->id}','{$sa_jteam[$x]['id']}','{$config['FAIRYEAR']}')");
- TRACE("Created Team: $tn {$sa_jteam[$x]['id']}\n");
- $jteam_id++;
+ TRACE("Created Team: {$i->name}, $min judges needed (db id:{$sa_jteam[$x]['id']}) \n");
$x++;
}
+ TRACE("Total Judges: $total_judges, Required: $required_judges\n");
/* ====================================================================*/
- set_status("Assigning Judges to Special Award Teams\n");
+ set_status("Assigning Special Award Teams to Special Award Round(s)\n");
+ /* Compute how many judges each round needs based on the total number
+ * of needed judges, e.g. if SAround1 has 10 judges available and SAround2
+ * has 20 judges available, and we total need 90 judges, then we
+ * want to assign jteams so that SAround1 has 30 slots, and SAround2 has
+ * 60 to balance the deficit */
+ foreach($round_special_awards as &$r) {
+ $x = count($r['available_judge_ids']);
+ $target = ($x * $required_judges) / $total_judges;
+ $r['target_judges'] = $target;
+ TRACE("Round {$r['name']} should be assigned $target judge timeslots\n");
- $judge_ids = array_keys($sa_judges);
- $e = $config['effort'];
- $a = new annealer(count($sa_jteam), 25, $e, 0.98, judges_sa_cost_function, $judge_ids);
- //$a->set_update_callback(judges_to_teams_update);
- //$a->set_pick_move(judges_sa_pick_move);
- $a->anneal();
+ /* Setup for the next step, always add special award
+ * judge team 0 to ALL rounds */
+ $r['jteam_ids'] = array(0);
+ $r['assigned_judges'] = 0;
+ }
+ unset($r);
- $x=0;
- unset($t);
- unset($tid);
- foreach($sa_jteam as $tid => $t) {
- if($tid == 0) {
+ /* ====================================================================*/
+ /* Scan the list of special awards, check each special award to see if
+ * it has special award only judges, we want those special awards pre-assigned
+ * to rounds where ALL SA-only judges are available, or, as best we can. */
+ foreach($sa_jteam as $x=>&$jt) {
+ if($x == 0) continue;
+
+ $sa_judges = array();
+ foreach($round_special_awards as $i=>$r) {
+ $sa_round_count[$i] = 0;
+ }
+
+ foreach($jt['award_ids'] as $aid) {
+ foreach($judges as $jid=>$j) {
+ if($j['special_award_only'] == 'no') continue;
+ if(in_array($aid, $j['special_awards'])) {
+ $sa_judges[] = $jid;
+ foreach($round_special_awards as $i=>$r) {
+// TRACE("Checking {$j['name']} in round {$r['name']}\n");
+ if(judge_available_for_round($j, $r)) {
+// TRACE(" yes, round $i ++\n");
+ $sa_round_count[$i]++;
+
+ }
+ }
+ }
+ }
+
+ }
+
+ /* If there are no SA-only judges, skip the pre-assignment */
+ if(count($sa_judges) == 0) continue;
+
+ /* There are count($sa_judges), find the round
+ * with the highest count */
+ $highest_count = 0;
+ $highest_offset = -1;
+ foreach($round_special_awards as $i=>$r) {
+ if($sa_round_count[$i] > $highest_count || $highest_offset == -1) {
+ $highest_count = $sa_round_count[$i];
+ $highest_offset = $i;
+ }
+ }
+ /* Assign this jteam to that round */
+ $round_special_awards[$highest_offset]['jteam_ids'][] = $x;
+ $round_special_awards[$highest_offset]['assigned_judges'] += $jt['min_judges'];
+ TRACE("Pre-assigning Team {$jt['name']} to Round {$round_special_awards[$highest_offset]['name']}\n");
+ $jt['assigned'] = true;
+ }
+ unset($jt);
+
+ /* Use a greedy algorithm to assign the remaining jteams. First sort
+ * the teams by the number of judges needed so those can be assigned
+ * first */
+ function sa_cmp($a, $b) {
+ return $b['min_judges'] - $a['min_judges'];
+ }
+ uasort($sa_jteam, 'sa_cmp');
+
+ foreach($sa_jteam as $x=>$jt) {
+ if($x == 0) continue;
+ if($jt['assigned'] == true) continue;
+
+ $highest = 0;
+ $highest_offset = -1;
+ /* Find the round with the highest missing judges, this works
+ * even if the $p computation is negative */
+ foreach($round_special_awards as $o=>$r) {
+ $p = $r['target_judges'] - $r['assigned_judges'];
+// TRACE(" Round {$r['name']} p=$p\n");
+ if($highest_offset == -1 || $p > $highest) {
+ $highest = $p;
+ $highest_offset = $o;
+ }
+ }
+ /* Assign this jteam id to the special award round */
+ $round_special_awards[$highest_offset]['jteam_ids'][] = $x;
+ $round_special_awards[$highest_offset]['assigned_judges'] += $jt['min_judges'];
+ }
+
+ /* Now, anneal in each special award round */
+ foreach($round_special_awards as $r) {
+ set_status("Assigning Judges in round {$r['name']}\n");
+
+ $current_jteam_ids = $r['jteam_ids'];
+ $judge_ids = $r['available_judge_ids'];
+ $e = $config['effort'];
+ $a = new annealer(count($r['jteam_ids']), 25, $e, 0.98,
+ judges_sa_cost_function, $judge_ids);
+ //$a->set_update_callback(judges_to_teams_update);
+ //$a->set_pick_move(judges_sa_pick_move);
+ $a->anneal();
+
+ $x=0;
+
+ unset($t);
+ unset($tid);
+ foreach($r['jteam_ids'] as $tid) {
+ if($tid == 0) {
+ $x++;
+ continue;
+ }
+
+ $t = &$sa_jteam[$tid];
+
+ print("Judging Team {$t['id']} \"{$t['name']}\": cost={$a->bucket_cost[$x]} #=({$t['min_judges']},{$t['max_judges']}) ");
+
+ // print("langs=(");
+ /* $langstr="";
+ for($y=0; $ybucket[$x];
+ foreach($a->bucket[$x] as $jid) {
+ // pr_judge($t, $ids[$y]);
+
+ $j = &$judges[$jid];
+ print(" - {$j['name']}\n");
+
+ /* Link Judges to the judging team we just inserted */
+ judge_team_add_judge($t['id'], $jid);
+ }
$x++;
- continue;
}
-
- print("Judging Team {$t['id']}: cost={$a->bucket_cost[$x]} #=({$t['min_judges']},{$t['max_judges']}) ");
-
- // print("langs=(");
- /* $langstr="";
- for($y=0; $ybucket[$x];
- for($y=0; $y$r->id,
- "date"=>$r->date,
- "starttime"=>substr($r->starttime,0,-3),
- "endtime"=>substr($r->endtime,0,-3));
+ "date"=>$r->date,
+ "starttime"=>substr($r->starttime,0,-3),
+ "endtime"=>substr($r->endtime,0,-3));
print(" ".$available_timeslots[$x]['starttime']." -> ".
$available_timeslots[$x]['endtime']."\n");
$x++;
@@ -968,13 +1268,19 @@ for($k=0; $k<$keys_count; $k++) {
$a->set_pick_move(timeslot_pick_move);
$a->anneal();
+ printf(" ");
+ for($y=0;$y<$n_timeslots; $y++) {
+ printf("%4d ", $y+1);
+ }
+ printf("\n");
+
for($x=0; $xbucket[$y][$x];
- TRACE(($y+1).":$jteam_id ");
+ printf("%4d ", $jteam[$jteam_id]['id']);
if($jteam_id == 0) continue;
@@ -992,7 +1298,7 @@ for($k=0; $k<$keys_count; $k++) {
" '$pid', '{$config['FAIRYEAR']}')");
}
- TRACE("\n");
+ printf("\n");
}
}