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 ()
{
}
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 ;
}
TRACE ( " Random move: ( $t1 , $j1 ) ( $t2 , $j2 )<br> " );
TRACE_R ( $team [ $t1 ][ 'judges' ]);
TRACE ( " <br>T2: " );
TRACE_R ( $team [ $t2 ][ 'judges' ]);
TRACE ( " <br> " );
/* 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 ;
// TRACE("Under min=$min, over max=$max<br>");
/* 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' ]];
// TRACE("Judge $judge_id cp=$cpref, dp=$dpref<br>");
$cost += 2 * ( - $dpref + 2 );
$cost += 2 * ( - $cpref + 2 );
}
TRACE ( " Team $team_id , cost is $cost <br> " );
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 ;
TRACE ( " Team $tid1 cost { $t1 [ 'cost' ] } -> $c1 <br> " );
TRACE ( " Team $tid2 cost { $t2 [ 'cost' ] } -> $c2 <br> " );
TRACE ( " Delta = $cost <br> " );
$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' ]);
TRACE ( " <br>T2: " );
TRACE_R ( $t2 [ 'judges' ]);
TRACE ( " <br> " );
// TRACE_R($t1);
// TRACE_R($t2);
}
/* Inputs to annealer :
* - data [ 'min_per_team' ]
* - 'max_per_team'
* - 'teams' [ division ][ category ] = number of teams
*
* - judges
*/
function judges_assign_anneal ( $divisions , $categories , $judges , $data )
{
/* Create an array to hold the team data */
$team = array ();
$t = 0 ;
reset ( $data [ 'teams' ]);
while ( list ( $div , $c ) = each ( $data [ 'teams' ]) ){
reset ( $c );
while ( list ( $cat , $num ) = each ( $c ) ) {
for ( $x = 0 ; $x < $num ; $x ++ ) {
$team [ $t ][ 'division' ] = $div ;
$team [ $t ][ 'category' ] = $cat ;
$team [ $t ][ 'min' ] = $data [ 'min_judges_per_team' ];
$team [ $t ][ 'max' ] = $data [ 'max_judges_per_team' ];
$team [ $t ][ 'judges' ] = array ();
$t ++ ;
}
}
}
$num_teams = $t ;
$x = 0 ;
/* 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);
for ( $x = 0 ; $x < count ( $team ); $x ++ ) {
$t =& $team [ $x ];
$t [ 'cost' ] = compute_team_cost ( $team , $judges , $x );
$current_cost += $t [ 'cost' ];
}
/* Anneal */
$temperature = 25.0 ;
while ( 1 ) {
$moves = 1000 ;
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 );
TRACE ( " r= $r , exp= $e <br> " );
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 ;
TRACE ( " Move accepted, cost= $current_cost <br> " );
} else {
TRACE ( " Move rejected<br> " );
}
}
TRACE ( " Cost is $current_cost <br> " );
$temperature *= 0.9 ;
if ( $temperature <= 0.05 ) break ;
}
return $team ;
}
$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 ;
$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 ();
foreach ( $div AS $d_id => $d_val )
{
foreach ( $cat AS $c_id => $c_val )
{
$numq = mysql_query ( " SELECT COUNT(id) AS num FROM projects WHERE projectcategories_id=' $c_id ' AND projectdivisions_id=' $d_id ' " );
$numr = mysql_fetch_object ( $numq );
$numteams = ceil ( $numr -> num / $data [ 'max_projects_per_team' ] * $data [ 'num_times_judged' ]);
$data [ 'teams' ][ $d_id ][ $c_id ] = $numteams ;
}
}
/*
$data = array ( 'min_per_team' => 2 ,
'max_per_team' => 4 ,
'teams' => array () );
$data [ 'teams' ][ 'S' ][ 'LS' ] = 4 ;
$data [ 'teams' ][ 'S' ][ 'PS' ] = 4 ;
$data [ 'teams' ][ 'S' ][ 'Case' ] = 3 ;
$data [ 'teams' ][ 'S' ][ 'CS' ] = 2 ;
$data [ 'teams' ][ 'I' ][ 'LS' ] = 3 ;
$data [ 'teams' ][ 'I' ][ 'PS' ] = 3 ;
$data [ 'teams' ][ 'I' ][ 'Case' ] = 2 ;
$data [ 'teams' ][ 'I' ][ 'CS' ] = 1 ;
$data [ 'teams' ][ 'J' ][ 'LS' ] = 2 ;
$data [ 'teams' ][ 'J' ][ 'PS' ] = 2 ;
$data [ 'teams' ][ 'J' ][ 'Case' ] = 1 ;
$data [ 'teams' ][ 'J' ][ 'CS' ] = 4 ;
*/
2006-01-03 02:44:26 +00:00
$q = mysql_query ( " SELECT * FROM judges 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 ();
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
);
}
//echo nl2br(TRACE_R($judges, true));
$teams = judges_assign_anneal ( $div , $cat , $judges , $data );
//TRACE_R( $teams);
$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 /> " ;
$totalcost = 0 ;
while ( list ( $tn , $t ) = each ( $teams )) {
//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' ] } )) " .
" (cost: { $t [ 'cost' ] } )<br> " );
$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 ];
print ( " { $judge [ 'name' ] } <br> " );
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 ();
?>