2006-01-03 02:08:23 +00:00
< ?
/*
This file is part of the 'Science Fair In A Box' project
SFIAB Website : http :// www . sfiab . ca
Copyright ( C ) 2005 Sci - Tech Ontario Inc < info @ scitechontario . org >
Copyright ( C ) 2005 James Grant < james @ lightbox . org >
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation , version 2.
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; see the file COPYING . If not , write to
the Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
Boston , MA 02111 - 1307 , USA .
*/
?>
< ?
require ( " ../common.inc.php " );
auth_required ( 'admin' );
function TRACE ()
{
}
2006-01-16 04:58:43 +00:00
//function TRACE($str) { print($str); }
2006-01-03 02:08:23 +00:00
function TRACE_R ()
{
}
/* Given the team data , pick a judge , then pick a new team
* for them */
function pick_random_move ( & $team )
{
//TRACE_R($team);
/* Pick 2 random teams*/
$tms = count ( $team );
while ( 1 ) {
$t1 = rand ( 0 , $tms - 1 );
if ( count ( $team [ $t1 ][ 'judges' ]) > 0 ) break ;
}
$t2 = rand ( 0 , $tms - 2 );
if ( $t2 >= $t1 ) $t2 ++ ;
/* Pick a judge on team1 */
$j1 = rand ( 0 , count ( $team [ $t1 ][ 'judges' ]) - 1 );
/* Pick a judge or the empty slot on team2 */
$j2 = rand ( 0 , count ( $team [ $t2 ][ 'judges' ]));
if ( $j2 == count ( $team [ $t2 ][ 'judges' ])) {
$j2 = - 1 ;
}
2006-01-16 04:58:43 +00:00
TRACE ( " Random move: ( $t1 , $j1 ) ( $t2 , $j2 ) \n " );
2006-01-03 02:08:23 +00:00
TRACE_R ( $team [ $t1 ][ 'judges' ]);
2006-01-16 04:58:43 +00:00
TRACE ( " T2: \n " );
2006-01-03 02:08:23 +00:00
TRACE_R ( $team [ $t2 ][ 'judges' ]);
2006-01-16 04:58:43 +00:00
TRACE ( " \n " );
2006-01-03 02:08:23 +00:00
/* The move is team1,judge1 <==> team2,judge2 */
return array ( $t1 , $j1 , $t2 , $j2 );
}
/* The cost function is :
+ 200 * each judge below the min for each team
+ 100 * each judge above the max for each team
+ - 20 * each level of preference away from the
max level for each judge
( ex : if a judge has selected LS -> 2 , PS -> 0 , CS ->- 1
then matching that judge with a :
LS = - 40 ,
PS = 0 ,
CS = - 20 ,
else = 0
)
*/
/* Compute the cost of adding a judge to a team */
function compute_team_cost ( & $teams , & $judges , $team_id )
{
$cost = 0 ;
$t =& $teams [ $team_id ];
/* Compute the over max / under min costs */
$c = count ( $t [ 'judges' ]);
$min = ( $c < $t [ 'min' ]) ? $t [ 'min' ] - $c : 0 ;
$max = ( $c > $t [ 'max' ]) ? $c - $t [ 'max' ] : 0 ;
$cost += $min * 20 ;
$cost += $max * 10 ;
2006-01-16 04:58:43 +00:00
// TRACE("Under min=$min, over max=$max\n");
2006-01-03 02:08:23 +00:00
/* For each judge on the team, score their preferences */
reset ( $t [ 'judges' ]);
while ( list ( $key , $judge_id ) = each ( $t [ 'judges' ]) ) {
$j = $judges [ $judge_id ];
/* Get the division , and see where it fits with this
* judges preferences */
$dpref = $j [ 'divprefs' ][ $t [ 'division' ]];
$cpref = $j [ 'catprefs' ][ $t [ 'category' ]];
2006-01-16 04:58:43 +00:00
// TRACE("Judge $judge_id cp=$cpref, dp=$dpref\n");
2006-01-03 02:08:23 +00:00
$cost += 2 * ( - $dpref + 2 );
$cost += 2 * ( - $cpref + 2 );
}
2006-01-16 04:58:43 +00:00
TRACE ( " Team $team_id , cost is $cost\n " );
2006-01-03 02:08:23 +00:00
return $cost ;
}
function compute_delta_cost ( & $teams , & $judges , $move )
{
list ( $tid1 , $jidx1 , $tid2 , $jidx2 ) = $move ;
$t1 =& $teams [ $tid1 ];
$t2 =& $teams [ $tid2 ];
$ja1 = $t1 [ 'judges' ];
$ja2 = $t2 [ 'judges' ];
/* Turn the indexes into judge IDs */
$jid1 = $t1 [ 'judges' ][ $jidx1 ];
if ( $jidx2 == - 1 )
$jid2 = - 1 ;
else
$jid2 = $t2 [ 'judges' ][ $jidx2 ];
$cost = 0 ;
/* Make new arrays for each judge list */
$nj1 = array ();
for ( $x = 0 ; $x < count ( $t1 [ 'judges' ]); $x ++ ) {
$id = $t1 [ 'judges' ][ $x ];
if ( $x == $jidx1 ) {
/* This is the index of the judge we ' re
* supposed to swap ( or remove ) */
if ( $jid2 != - 1 ) $nj1 [] = $jid2 ;
} else {
$nj1 [] = $id ;
}
}
$t1 [ 'judges' ] = $nj1 ;
$nj2 = array ();
for ( $x = 0 ; $x < count ( $t2 [ 'judges' ]); $x ++ ) {
$id = $t2 [ 'judges' ][ $x ];
if ( $x == $jidx2 ) {
/* if jidx2 is - 1 we ' ll never get here , else
* we might , meaning that this is where
* we want to swap in the value from the first
* array */
$nj2 [] = $jid1 ;
} else {
$nj2 [] = $id ;
}
}
/* jidx2 may be - 1 meaning that something was removed
* from the first array , but not added back to this one ,
* do that now so we don ' t lose judges */
if ( $jidx2 == - 1 ) $nj2 [] = $jid1 ;
$t2 [ 'judges' ] = $nj2 ;
/* Recompute the costs */
$cost -= $t1 [ 'cost' ];
$cost -= $t2 [ 'cost' ];
$c1 = compute_team_cost ( $teams , $judges , $tid1 );
$c2 = compute_team_cost ( $teams , $judges , $tid2 );
$cost += $c1 + $c2 ;
2006-01-16 04:58:43 +00:00
TRACE ( " Team $tid1 cost { $t1 [ 'cost' ] } -> $c1\n " );
TRACE ( " Team $tid2 cost { $t2 [ 'cost' ] } -> $c2\n " );
TRACE ( " Delta = $cost\n " );
2006-01-03 02:08:23 +00:00
$t1 [ 'judges' ] = $ja1 ;
$t2 [ 'judges' ] = $ja2 ;
return array ( $cost , array ( $nj1 , $c1 , $nj2 , $c2 )) ;
}
function record_move ( & $teams , $move , $movedata )
{
list ( $tid1 , $jidx1 , $tid2 , $jidx2 ) = $move ;
list ( $judges1 , $c1 , $judges2 , $c2 ) = $movedata ;
$t1 =& $teams [ $tid1 ];
$t2 =& $teams [ $tid2 ];
// TRACE_R($t1);
$t1 [ 'judges' ] = $judges1 ;
$t1 [ 'cost' ] = $c1 ;
$t2 [ 'judges' ] = $judges2 ;
$t2 [ 'cost' ] = $c2 ;
TRACE ( " T1: " );
TRACE_R ( $t1 [ 'judges' ]);
2006-01-16 04:58:43 +00:00
TRACE ( " \n T2: " );
2006-01-03 02:08:23 +00:00
TRACE_R ( $t2 [ 'judges' ]);
2006-01-16 04:58:43 +00:00
TRACE ( " \n " );
2006-01-03 02:08:23 +00:00
// TRACE_R($t1);
// TRACE_R($t2);
}
/* Inputs to annealer :
* - data [ 'min_per_team' ]
* - 'max_per_team'
* - 'teams' [ division ][ category ] = number of teams
*
* - judges
*/
2006-01-16 04:58:43 +00:00
function judges_assign_anneal ( $divisions , $categories , $languages , $judges , & $team , $data )
2006-01-03 02:08:23 +00:00
{
2006-01-16 04:58:43 +00:00
$num_teams = count ( $team );
$x = 0 ;
2006-01-03 02:08:23 +00:00
2006-01-16 04:58:43 +00:00
TRACE ( " Input: $num_teams juding teams \n " );
if ( $num_teams <= 0 ) return ;
2006-01-03 02:08:23 +00:00
/* Inital assignment of judges to teams */
reset ( $judges );
while ( list ( $j , $ji ) = each ( $judges )) {
$team [ $x % $num_teams ][ 'judges' ][] = $j ;
$x ++ ;
}
/* Compute inital costs */
$current_cost = 0 ;
// reset($team);
2006-01-16 04:58:43 +00:00
for ( $x = 0 ; $x < $num_teams ; $x ++ ) {
2006-01-03 02:08:23 +00:00
$t =& $team [ $x ];
$t [ 'cost' ] = compute_team_cost ( $team , $judges , $x );
$current_cost += $t [ 'cost' ];
}
/* Anneal */
$temperature = 25.0 ;
while ( 1 ) {
2006-01-16 04:58:43 +00:00
$moves = 500 ;
2006-01-03 02:08:23 +00:00
for ( $m = 0 ; $m < $moves ; $m ++ ) {
/* Pick 2 moves at random */
$move = pick_random_move ( $team );
/* See what the new cost is compared to the old */
list ( $delta_c , $movedata ) =
compute_delta_cost ( $team , $judges , $move );
$r = floatval ( rand ()) / floatval ( getrandmax ());
/* Decide if we want to keep it */
$e = exp ( - $delta_c / $temperature );
2006-01-16 04:58:43 +00:00
TRACE ( " r= $r , exp= $e\n " );
2006-01-03 02:08:23 +00:00
if ( $r < exp ( - $delta_c / $temperature )) {
/* Yes, we do, record the move */
record_move ( $team , $move , $movedata );
$current_cost += $delta_c ;
$n_accepted ++ ;
if ( $current_cost < $best_cost )
$best_cost = $current_cost ;
2006-01-16 04:58:43 +00:00
TRACE ( " Move accepted, cost= $current_cost\n " );
2006-01-03 02:08:23 +00:00
} else {
2006-01-16 04:58:43 +00:00
TRACE ( " Move rejected \n " );
2006-01-03 02:08:23 +00:00
}
}
2006-01-16 04:58:43 +00:00
TRACE ( " Cost is $current_cost\n " );
2006-01-03 02:08:23 +00:00
$temperature *= 0.9 ;
if ( $temperature <= 0.05 ) break ;
}
}
$q = mysql_query ( " SELECT * FROM projectdivisions WHERE year=' " . $config [ 'FAIRYEAR' ] . " ' ORDER BY id " );
while ( $r = mysql_fetch_object ( $q ))
$div [ $r -> id ] = $r -> division ;
$q = mysql_query ( " SELECT * FROM projectcategories WHERE year=' " . $config [ 'FAIRYEAR' ] . " ' ORDER BY id " );
while ( $r = mysql_fetch_object ( $q ))
$cat [ $r -> id ] = $r -> category ;
2006-01-16 04:58:43 +00:00
$langr = array ();
$q = mysql_query ( " SELECT * FROM languages WHERE active='Y' " );
while ( $r = mysql_fetch_object ( $q ))
$langr [] = $r -> lang ;
2006-01-03 02:08:23 +00:00
$configq = mysql_query ( " SELECT * FROM judges_schedulerconfig WHERE year=' " . $config [ 'FAIRYEAR' ] . " ' " );
$data = array ();
while ( $configr = mysql_fetch_object ( $configq ))
$data [ $configr -> var ] = $configr -> val ;
$data [ 'teams' ] = array ();
2006-01-16 04:58:43 +00:00
$data [ 'projects' ] = array ();
$jdivisions = array ();
/* Load a list of all projects , and build an array of projects in each
* category */
// print_r($langr);
$q = mysql_query ( " SELECT * FROM projects WHERE year=' " . $config [ 'FAIRYEAR' ] . " ' " );
while ( $r = mysql_fetch_object ( $q )) {
$d_id = $r -> projectdivisions_id ;
$c_id = $r -> projectcategories_id ;
$l_id = $r -> language ;
$data [ 'projects' ][ $r -> id ][ 'timetable' ] = array ();
if ( ! in_array ( $l_id , $langr )) $l_id = 'en' ;
$jdivisions [ $d_id ][ $c_id ][ $l_id ][] = $r -> id ;
2006-01-03 02:08:23 +00:00
2006-01-16 04:58:43 +00:00
TRACE ( " Found project id { $r -> id } in $d_id , $c_id , $l_id\n\n " );
}
$t = 0 ;
$max_ts = 0 ;
2006-01-03 02:08:23 +00:00
foreach ( $div AS $d_id => $d_val )
{
foreach ( $cat AS $c_id => $c_val )
{
2006-01-16 04:58:43 +00:00
foreach ( $langr AS $l_id ) {
$num = count ( $jdivisions [ $d_id ][ $c_id ][ $l_id ]);
if ( $num <= 0 ) continue ;
$numteams = ceil ( $num / $data [ 'max_projects_per_team' ] * $data [ 'num_times_judged' ]);
if ( $numteams < $data [ 'num_times_judged' ])
$numteams = $data [ 'num_times_judged' ];
TRACE ( " Judging teams for $d_id , $c_id , $l_id is $numteams\n\n " );
$start_t = $t ;
for ( $x = 0 ; $x < $numteams ; $x ++ ) {
$team [ $t ][ 'division' ] = $d_id ;
$team [ $t ][ 'category' ] = $c_id ;
$team [ $t ][ 'language' ] = $l_id ;
$team [ $t ][ 'min' ] = $data [ 'min_judges_per_team' ];
$team [ $t ][ 'max' ] = $data [ 'max_judges_per_team' ];
$team [ $t ][ 'judges' ] = array ();
$t ++ ;
}
TRACE ( " Created judging teams $start_t -> " . ( $t - 1 ) . " \n \n " );
TRACE ( " Need to assign these teams to $num projects: " );
// print_r($jdivisions[$d_id][$c_id][$l_id]);
TRACE ( " \n \n " );
/* We just created teams $start_t -> $t - 1 , now we can assign which projects
* they judge in different timeslots */
/* Each project must be judged $data [ 'num_times_judged' ], and each team is
* allowed to judge $data [ 'num_times_judged' ] projects */
$x = 0 ; /* Cycles over 0 -> $num */
$ts = 1 ; /* Current timeslot , increment when all judging teams are assinged */
$j = 0 ; /* Cycles over o0 -> $numteams */
$teams_at_this_ts = array ();
$num_done = 0 ;
while ( 1 ) {
TRACE ( " x= $x " );
if ( $x == $num ) $x = 0 ;
/* Get the project id we want to look at */
$p = $jdivisions [ $d_id ][ $c_id ][ $l_id ][ $x ];
TRACE ( " project= $p \n \n " );
TRACE ( " This projects has " . ( count ( $data [ 'projects' ][ $p ][ 'timetable' ])) . " judging teams \n " );
/* See if this project needs more judges */
if ( count ( $data [ 'projects' ][ $p ][ 'timetable' ]) == $data [ 'num_times_judged' ]) {
/* No, this project doesn't need any more judging teams */
TRACE ( " This project doesn't need more teams, skipping \n \n " );
// print_r($data['projects'][$p]['timetable']);
TRACE ( " \n \n " );
if ( $data [ 'projects' ][ $p ][ 'timetable_done' ] != 1 ) {
$data [ 'projects' ][ $p ][ 'timetable_done' ] = 1 ;
$num_done ++ ;
if ( $num_done == $num ) break ;
}
$x ++ ;
continue ;
}
/* Find a judging team to assign */
TRACE ( " Starting at team= $j\n\n " );
while ( 1 ) {
TRACE ( " j= $j " );
if ( $j == $numteams ) $j = 0 ;
$jteam = $j + $start_t ;
TRACE ( " team= $jteam\n\n " );
if ( in_array ( $jteam , $teams_at_this_ts )) {
$j ++ ;
continue ;
}
if ( ! in_array ( $jteam , $data [ 'projects' ][ $p ][ 'timetable' ])) {
/* Add this juding team to the timetable */
TRACE ( " Project $p , timeslot $ts = judging team $jteam\n\n " );
$data [ 'projects' ][ $p ][ 'timetable' ][ $ts ] = $jteam ;
$teams_at_this_ts [] = $jteam ;
$j ++ ;
break ;
}
$j ++ ;
if ( $j == $numteams ) $j = 0 ;
}
$x ++ ;
if ( count ( $teams_at_this_ts ) == $numteams ) {
$ts ++ ;
$teams_at_this_ts = array ();
if ( $ts > $max_ts ) $max_ts = $ts ;
}
}
}
2006-01-03 02:08:23 +00:00
}
}
2006-01-16 04:58:43 +00:00
//print_r($data['projects']);
TRACE ( " Teams: " . count ( $team ) . " \n " );
//print_r($team);
TRACE ( " \n " );
2006-01-03 02:08:23 +00:00
2006-01-16 04:58:43 +00:00
$q = mysql_query ( " SELECT judges.* FROM judges,judges_years WHERE complete='yes' AND judges_years.year=' { $config [ 'FAIRYEAR' ] } ' AND judges_years.judges_id=judges.id " );
2006-01-03 02:08:23 +00:00
$judges = array ();
2006-01-16 04:58:43 +00:00
2006-01-03 02:08:23 +00:00
while ( $r = mysql_fetch_object ( $q ))
{
unset ( $divprefs );
unset ( $catprefs );
//get category preferences
$q2 = mysql_query ( " SELECT * FROM judges_catpref WHERE judges_id=' $r->id ' AND year=' " . $config [ 'FAIRYEAR' ] . " ' ORDER BY projectcategories_id " );
$catprefs = array ();
while ( $r2 = mysql_fetch_object ( $q2 ))
$catprefs [ $r2 -> projectcategories_id ] = $r2 -> rank ;
//get division preferences
$q2 = mysql_query ( " SELECT * FROM judges_expertise WHERE judges_id=' $r->id ' AND year=' " . $config [ 'FAIRYEAR' ] . " ' AND projectsubdivisions_id IS NULL ORDER BY projectdivisions_id " );
//the areas of expertise are ranked from 1 to 5, and we need them as -2,-1,0,1,2 so we simply subtract 3
$divprefs = array ();
while ( $r2 = mysql_fetch_object ( $q2 ))
$divprefs [ $r2 -> projectdivisions_id ] = $r2 -> val - 3 ;
$judges [] = array (
" judges_id " => " $r->id " ,
" name " => " $r->firstname $r->lastname " ,
" years_school " => $r -> years_school ,
" years_regional " => $r -> years_regional ,
" years_national " => $r -> years_national ,
" willing_chair " => $r -> willing_chair ,
" divprefs " => $divprefs ,
" catprefs " => $catprefs
);
}
2006-01-16 04:58:43 +00:00
//print_r($judges);
2006-01-03 02:08:23 +00:00
//echo nl2br(TRACE_R($judges, true));
2006-01-16 04:58:43 +00:00
judges_assign_anneal ( $div , $cat , $langr , $judges , $team , $data );
2006-01-03 02:08:23 +00:00
2006-01-16 04:58:43 +00:00
//print_r( $team);
2006-01-03 02:08:23 +00:00
$teamnums = array ();
send_header ( " Judging teams automatic scheduler " );
echo i18n ( " Judging teams successfully created. You can review the teams and modify as desired using the following links " );
echo " <br /> " ;
echo " <br /> " ;
echo " <a href= \" judges_teams.php \" > " . i18n ( " Manage Judge Teams " ) . " </a> " ;
echo " <br /> " ;
echo " <a href= \" judges_teams_members.php \" > " . i18n ( " Manage Judge Members " ) . " </a> " ;
echo " <br /> " ;
echo " <br /> " ;
2006-01-16 04:58:43 +00:00
print ( " Project Timeslots:<br> \n " );
print ( " <table border=1><tr><td>Project ID</td> " );
for ( $x = 0 ; $x < $max_ts ; $x ++ ) {
print ( " <td>Slot " . ( $x + 1 ) . " </td> " );
}
print ( " </tr> " );
while ( list ( $proj_id , $projinfo ) = each ( $data [ 'projects' ] )) {
print ( " <tr><td> $proj_id </td> " );
$last_slot = 1 ;
while ( list ( $slot , $jteam ) = each ( $projinfo [ 'timetable' ]) ) {
while ( $last_slot != $slot ) {
print ( " <td> </td> " );
$last_slot ++ ;
}
print ( " <td> " . ( $jteam + 1 ) . " </td> " );
$last_slot ++ ;
}
print ( " </tr> " );
}
print ( " </table> " );
2006-01-03 02:08:23 +00:00
$totalcost = 0 ;
2006-01-16 04:58:43 +00:00
while ( list ( $tn , $t ) = each ( $team )) {
2006-01-03 02:08:23 +00:00
//team numbers start with 0 in the annealer, but we want them to start at 1, so just tn++ here.
$tn ++ ;
print ( " Team $tn : ( { $div [ $t [ 'division' ]] } ( { $t [ 'division' ] } ), { $cat [ $t [ 'category' ]] } ( { $t [ 'category' ] } )) " .
2006-01-16 04:58:43 +00:00
" (cost: { $t [ 'cost' ] } )<br> \n " );
2006-01-03 02:08:23 +00:00
$totalcost += $t [ 'cost' ];
if ( ! $teamnums [ $t [ 'division' ]][ $t [ 'category' ]]) $teamnums [ $t [ 'division' ]][ $t [ 'category' ]] = 1 ;
else $teamnums [ $t [ 'division' ]][ $t [ 'category' ]] ++ ;
mysql_query ( " INSERT INTO judges_teams (num,name,autocreate_type_id,year) VALUES (' $tn ','(Divisional) { $div [ $t [ 'division' ]] } - { $cat [ $t [ 'category' ]] } # { $teamnums [ $t [ 'division' ]][ $t [ 'category' ]] } ','1',' { $config [ 'FAIRYEAR' ] } ') " );
$team_id = mysql_insert_id ();
while ( list ( $key , $j ) = each ( $t [ 'judges' ]) ) {
$judge = $judges [ $j ];
2006-01-16 04:58:43 +00:00
print ( " { $judge [ 'name' ] } <br> \n " );
2006-01-03 02:08:23 +00:00
mysql_query ( " INSERT INTO judges_teams_link (judges_id,judges_teams_id,captain,year) VALUES (' { $judge [ 'judges_id' ] } ',' $team_id ',' { $judge [ 'willing_chair' ] } ',' { $config [ 'FAIRYEAR' ] } ') " );
}
//and finally, link the team to the award
$q = mysql_query ( " SELECT award_awards.id FROM
award_awards ,
award_awards_projectcategories ,
award_awards_projectdivisions
WHERE
award_awards . year = '{$config[' FAIRYEAR ']}'
AND award_awards . id = award_awards_projectcategories . award_awards_id
AND award_awards . id = award_awards_projectdivisions . award_awards_id
AND award_awards_projectcategories . projectcategories_id = '{$t[' category ']}'
AND award_awards_projectdivisions . projectdivisions_id = '{$t[' division ']}'
AND award_awards . award_types_id = '1'
" );
if ( mysql_num_rows ( $q ) != 1 )
{
echo error ( i18n ( " Cannot find award for %1 - %2 " , array ( $cat [ $t [ 'category' ]], $div [ $t [ 'division' ]])));
}
else
{
$r = mysql_fetch_object ( $q );
mysql_query ( " INSERT INTO judges_teams_awards_link (award_awards_id,judges_teams_id,year) VALUES (' $r->id ',' $team_id ',' { $config [ 'FAIRYEAR' ] } ') " );
}
}
echo " <br /> " ;
echo " <br /> " ;
echo " <b>Total 'cost' for all teams: $totalcost </b> " ;
send_footer ();
?>