DIR : /home/kozerus/public_html/dgs/tournaments/roundrobin/define_pools.php
/home/kozerus/public_html/dgs/tournaments/roundrobin
<?php
/*
Dragon Go Server
Copyright (C) 2001- Erik Ouchterlony, Jens-Uwe Gaspar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
$TranslateGroups[] = "Tournament";
chdir('../..');
require_once 'include/std_functions.php';
require_once 'include/gui_functions.php';
require_once 'include/form_functions.php';
require_once 'include/table_columns.php';
require_once 'tournaments/include/tournament_cache.php';
require_once 'tournaments/include/tournament_factory.php';
require_once 'tournaments/include/tournament_helper.php';
require_once 'tournaments/include/tournament_log_helper.php';
require_once 'tournaments/include/tournament_participant.php';
require_once 'tournaments/include/tournament_pool.php';
require_once 'tournaments/include/tournament_round.php';
require_once 'tournaments/include/tournament_round_status.php';
require_once 'tournaments/include/tournament_status.php';
require_once 'tournaments/include/tournament_utils.php';
$GLOBALS['ThePage'] = new Page('TournamentPoolDefine');
{
connect2mysql();
$logged_in = who_is_logged( $player_row, LOGIN_DEFAULT_OPTS_TDIR_OPS );
if ( !$logged_in )
error('login_if_not_logged_in', 'Tournament.roundrobin.define_pools');
if ( !ALLOW_TOURNAMENTS )
error('feature_disabled', 'Tournament.roundrobin.define_pools');
$my_id = $player_row['ID'];
if ( $my_id <= GUESTS_ID_MAX )
error('not_allowed_for_guest', 'Tournament.roundrobin.define_pools');
$page = "define_pools.php";
/* Actual REQUEST calls used:
NOTE: used for round-robin-tournaments
NOTE: used for league-tournaments (without pool-winners)
tid= : define tournament pools
t_preview&tid= : preview for tournament-pool-save
t_save&tid= : update (replace) tournament-round pool-info in database
t_suggest&tid= : show suggestions for pool-info
t_cancel&tid= : cancel editing, reload page
...&addpool=1 : add one pool (if possible)
...&delpool=1 : delete last pool (if possible)
*/
$tid = (int) @$_REQUEST['tid'];
if ( $tid < 0 ) $tid = 0;
$tourney = TournamentCache::load_cache_tournament( 'Tournament.define_pools.find_tournament', $tid );
$tstatus = new TournamentStatus( $tourney );
$ttype = TournamentFactory::getTournament($tourney->WizardType);
if ( !$ttype->need_rounds )
error('tournament_edit_rounds_not_allowed', "Tournament.define_pools.need_rounds($tid)");
$is_league = ( $tourney->Type == TOURNEY_TYPE_LEAGUE );
// create/edit allowed?
$allow_edit_tourney = TournamentHelper::allow_edit_tournaments($tourney, $my_id);
if ( !$allow_edit_tourney )
error('tournament_edit_not_allowed', "Tournament.define_pools.edit_tournament($tid,$my_id)");
// load existing T-round
$round = $tourney->CurrentRound;
$tround = TournamentCache::load_cache_tournament_round( 'Tournament.define_pools', $tid, $round );
$trstatus = new TournamentRoundStatus( $tourney, $tround );
if ( @$_REQUEST['t_cancel'] )
jump_to("tournaments/roundrobin/define_pools.php?tid=$tid".URI_AMP."round=$round");
// init
$old_poolcount = $tround->Pools;
$errors = $tstatus->check_edit_status( TournamentPool::get_edit_tournament_status() );
$errors = array_merge( $errors, $trstatus->check_edit_round_status( TROUND_STATUS_POOL ) );
if ( !TournamentUtils::isAdmin() && $tourney->isFlagSet(TOURNEY_FLAG_LOCK_ADMIN) )
$errors[] = $tourney->buildAdminLockText();
$reg_count = TournamentParticipant::count_tournament_participants( $tid, TP_STATUS_REGISTER, $round, /*NextR*/true );
$min_pool_count = min( TROUND_MAX_POOLCOUNT, TournamentUtils::calc_pool_count($reg_count, $tround->MaxPoolSize) );
$max_pool_count = min( TROUND_MAX_POOLCOUNT, TournamentUtils::calc_pool_count($reg_count, $tround->MinPoolSize) );
// check + parse edit-form (notes)
$old_tround = clone $tround;
list( $vars, $edits, $input_errors ) = parse_edit_form( $tround, $is_league );
$errors = array_merge( $errors, $input_errors, $tround->check_round_properties($tourney->Type) );
$adjust_pool = '';
$tier = 1;
if ( @$_REQUEST['t_save'] || @$_REQUEST['t_preview'] )
{
// NOTE: add/del-pools checkboxes don't show up under 'edits'
if ( $tround->Status == TROUND_STATUS_POOL && $old_poolcount > 0 && count($edits) == 0 )
{
if ( $vars['addpool'] )
{
$adjust_pool = '+1';
$tround->Pools++;
$edits[] = T_('Pool Count') . $adjust_pool;
}
elseif ( $vars['delpool'] )
{
$adjust_pool = '-1';
if ( TournamentPool::exists_tournament_tier_pool($tid, $round, $tier, $tround->Pools) )
$errors[] = sprintf( T_('Last Pool [%s] must be empty to allow deletion!'), $tround->Pools );
else
{
$tround->Pools--;
$edits[] = T_('Pool Count') . $adjust_pool;
}
}
}
else
{
if ( TournamentPool::exists_tournament_tier_pool($tid, $round, 0, -1) )
$errors[] = T_('Pool parameters can only be directly changed if all pools have been removed!');
}
}
// ---------- Process actions ------------------------------------------------
$arr_sugg_order = array(
1 => T_('Pool Size#tpool_sugg_order'),
2 => T_('Best Pool Distribution#tpool_sugg_order'),
);
$sugg_order_val = get_request_arg('sugg_order', 1);
$ttable = null;
if ( @$_REQUEST['t_suggest'] || @$_REQUEST['t_save'] || @$_REQUEST['t_preview'] )
{
$games_factor = TournamentHelper::determine_games_factor( $tid );
if ( !$is_league )
$ttable = make_suggestions_table( $tround, $reg_count, $errors, $games_factor,
$tround->PoolSize, $tround->Pools );
}
else
$games_factor = 1;
// save tournament-round-object with values from edit-form
if ( @$_REQUEST['t_save'] && count($errors) == 0 )
{
$tround->update();
TournamentLogHelper::log_define_tournament_pools( $tid, $allow_edit_tourney, $edits, $old_tround, $tround );
jump_to("tournaments/roundrobin/define_pools.php?tid=$tid".URI_AMP."round=$round".URI_AMP
. "sysmsg=". urlencode(T_('Tournament Round saved!')) );
}
// --------------- Tournament-Pools EDIT form --------------------
$tform = new Form( 'tournament', $page, FORM_GET );
$tform->add_hidden( 'tid', $tid );
$tform->add_row( array(
'DESCRIPTION', T_('Tournament ID'),
'TEXT', $tourney->build_info() ));
if ( !$is_league )
{
$tform->add_row( array(
'DESCRIPTION', T_('Tournament Round'),
'TEXT', $tourney->formatRound(), ));
}
TournamentUtils::show_tournament_flags( $tform, $tourney );
$tform->add_row( array(
'DESCRIPTION', T_('Round Status#tourney'),
'TEXT', TournamentRound::getStatusText($tround->Status), ));
if ( count($errors) )
{
$tform->add_row( array( 'HR' ));
$tform->add_row( array(
'DESCRIPTION', T_('Error'),
'TEXT', buildErrorListString(T_('There are some errors'), $errors) ));
}
$tform->add_row( array( 'HR' ));
$disabled = ( $is_league ) ? 'disabled=1' : '';
$tform->add_row( array(
'DESCRIPTION', T_('Base Pool Size'),
'TEXTINPUTX', 'pool_size', 4, 3, $vars['pool_size'], $disabled,
'TEXT', build_range_text( $tround->MinPoolSize, $tround->MaxPoolSize ), ));
$tform->add_row( array(
'DESCRIPTION', T_('Pool Count'),
'TEXTINPUTX', 'pool_count', 4, 4, $vars['pool_count'], $disabled,
'TEXT', ( $adjust_pool ? "<b>$adjust_pool</b>" . SMALL_SPACING : ''),
'TEXT', build_range_text( $min_pool_count, $max_pool_count ), ));
if ( $tround->Status == TROUND_STATUS_POOL && $old_poolcount > 0 )
{
if ( $old_poolcount < $max_pool_count )
$tform->add_row( array(
'TAB',
'CHECKBOX', 'addpool', 1, T_('Add Pool (possible if no violation)'), $vars['addpool'], ));
if ( $old_poolcount > $min_pool_count - 1 )
$tform->add_row( array(
'TAB',
'CHECKBOX', 'delpool', 1, T_('Delete last Pool (possible if pool empty and no violation)'), $vars['delpool'], ));
}
if ( !$is_league )
{
$tform->add_empty_row();
$tform->add_row( array(
'DESCRIPTION', T_('Pool Winner Ranks'),
'TEXTINPUTX', 'poolwinner_ranks', 3, 3, $vars['poolwinner_ranks'], $disabled, ));
}
$tform->add_empty_row();
$tform->add_row( array(
'DESCRIPTION', T_('Unsaved edits'),
'TEXT', span('TWarning', implode(', ', $edits), '[%s]'), ));
$tform->add_row( array(
'TAB', 'CELL', 1, '', // align submit-buttons
'SUBMITBUTTON', 't_save', T_('Save Pools'),
'TEXT', SMALL_SPACING,
'SUBMITBUTTON', 't_preview', T_('Preview'),
'TEXT', SMALL_SPACING,
'SUBMITBUTTON', 't_cancel', T_('Cancel'), ));
if ( !$is_league )
{
$tform->add_empty_row();
$tform->add_row( array(
'CELL', 2, '',
'TEXT', T_('Order by') . ' ',
'SELECTBOX', 'sugg_order', 1, $arr_sugg_order, $sugg_order_val, false,
'TEXT', SMALL_SPACING,
'SUBMITBUTTON', 't_suggest', T_('Suggest Pool Parameters'), ));
}
$title = T_('Tournament Pools Setup');
start_page( $title, true, $logged_in, $player_row );
echo "<h3 class=Header>$title</h3>\n";
$tform->echo_string();
if ( !$is_league && !is_null($ttable) )
{
section( 'suggestions', T_('Pool Suggestions') );
echo "<p>\n",
sprintf( T_('The table shows sample distributions for the %s registered users of round #%s for allowed pool-sizes.#tpool'),
"<b>$reg_count</b>", $round ), ":<br>\n",
( $games_factor > 1
? '( ' . sprintf( T_('"Games Count" is based on a factor of %s games per challenge!#tpool'), $games_factor ) . ' )'
: '' ), "<br>\n",
$ttable->make_table(),
"<br>\n";
echo_notes( 'defineTPoolsTable', T_('Legend for pool suggestions'), array(
sprintf( T_('%s = maximal user capacity for chosen pool-size and count'), stripLF(T_("User\nCapacity#tpool_sugg")) ),
sprintf( T_('%s = number of missing users to only have full pools'), stripLF(T_("User\nDiff#tpool_sugg")) ),
sprintf( T_('%s = best equally shared round-robin pool distribution for all %s registered users'),
stripLF(T_("Best Pool\nDistribution#tpool_sugg")), $reg_count )
. "\n" . T_('P x S : P = number of pools, S = pool-size; P1 + P2 = chosen pool-count'),
sprintf( T_('%s = max. number of games per user (calculated for largest pool of distribution)'), stripLF(T_("User\nGames#tpool_sugg")) ),
sprintf( T_('%s = number of games for best pool distribution'), stripLF(T_("Games\nCount#tpool_sugg")) ),
null,
T_("Recommended distributions are the suggestions with a minimal user-diff\nand a minimum of pools, that are smaller than the chosen pool-size."),
T_('You are free to use other combinations if they are valid.'),
T_('The current parameter choice by tournament director is included and the line is marked if valid.')
. "\n"
. T_('Eventually the pool-size is adjusted in order to find a good pool distribution.'),
), false);
}
$menu_array = array();
$menu_array[T_('Tournament info')] = "tournaments/view_tournament.php?tid=$tid";
$menu_array[T_('Define pools')] =
array( 'url' => "tournaments/roundrobin/define_pools.php?tid=$tid", 'class' => 'TAdmin' );
$menu_array[T_('Create pools')] =
array( 'url' => "tournaments/roundrobin/create_pools.php?tid=$tid", 'class' => 'TAdmin' );
$menu_array[T_('Edit pools')] =
array( 'url' => "tournaments/roundrobin/edit_pools.php?tid=$tid", 'class' => 'TAdmin' );
$menu_array[T_('Manage tournament')] =
array( 'url' => "tournaments/manage_tournament.php?tid=$tid", 'class' => 'TAdmin' );
end_page(@$menu_array);
}//main
function stripLF( $str )
{
return str_replace( array( "\r\n", "\r", "\n" ), ' ', $str );
}
// return [ vars-hash, edits-arr, errorlist ]
function parse_edit_form( TournamentRound &$trd, $is_league )
{
$edits = array();
$errors = array();
$is_posted = ( @$_REQUEST['t_save'] || @$_REQUEST['t_preview'] || @$_REQUEST['t_suggest'] );
// read from props or set defaults
$vars = array(
'pool_size' => $trd->PoolSize,
'pool_count' => $trd->Pools,
'addpool' => 0,
'delpool' => 0,
'poolwinner_ranks' => $trd->PoolWinnerRanks,
);
// copy to determine edit-changes
$old_vals = array_merge( array(), $vars );
// read URL-vals into vars
foreach ( $vars as $key => $val )
{
if ( !$is_league || !preg_match("/^(pool_size|pool_count|poolwinner_ranks)$/", $key) )
$vars[$key] = get_request_arg( $key, $val );
}
// handle checkboxes having no key/val in _POST-hash
if ( $is_posted )
{
foreach ( array('addpool','delpool') as $key )
$vars[$key] = get_request_arg( $key, false );
}
// parse URL-vars
if ( $is_posted )
{
global $min_pool_count, $max_pool_count;
$new_value = $vars['pool_size'];
if ( TournamentUtils::isNumberOrEmpty($new_value) && $new_value >= $trd->MinPoolSize && $new_value <= $trd->MaxPoolSize )
$trd->PoolSize = $new_value;
else
$errors[] = sprintf( T_('Expecting number for %s in range %s.'), T_('Pool Size'),
build_range_text($trd->MinPoolSize, $trd->MaxPoolSize) );
$new_value = $vars['pool_count'];
if ( TournamentUtils::isNumberOrEmpty($new_value) && $new_value >= $min_pool_count && $new_value <= $max_pool_count )
$trd->Pools = $new_value;
else
$errors[] = sprintf( T_('Expecting number for %s in range %s.'), T_('Pool Count'),
build_range_text( $min_pool_count, $max_pool_count ) );
if ( $vars['addpool'] && $vars['delpool'] )
$errors[] = T_('Adding and deleting pool are mutual exclusive actions: Choose only one.');
$new_value = $vars['poolwinner_ranks'];
if ( TournamentUtils::isNumberOrEmpty($new_value) && $new_value >= 0 && $new_value <= $trd->MaxPoolSize )
$trd->PoolWinnerRanks = $new_value;
else
{
$min_poolwinner_ranks = ( $trd->Status == TROUND_STATUS_INIT ) ? 0 : 1;
$errors[] = sprintf( T_('Expecting number for %s in range %s.'), T_('Pool Winner Ranks'),
build_range_text( $min_poolwinner_ranks, $trd->MaxPoolSize ) );
}
// determine edits
if ( $old_vals['pool_size'] != $trd->PoolSize ) $edits[] = T_('Pool Size');
if ( $old_vals['pool_count'] != $trd->Pools ) $edits[] = T_('Pool Count');
if ( $old_vals['poolwinner_ranks'] != $trd->PoolWinnerRanks ) $edits[] = T_('Pool Winner Ranks');
}
return array( $vars, array_unique($edits), $errors );
}//parse_edit_form
// return array( 0:$pool_size, 1:$pool_count, 2:$user_capacity, 3:$pool_size_base,
// 4:$pool_count_remain, 5:$pool_count_base, 6:$max_games_per_user 7:$games_count,
// 8:$distribution, 9:$user_choice )
// for given pool-size and pool-count
// param $games_factor : games per challenge = factor for games-count (e.g. 2 for double-htype tourney-rules)
// param $user_choice : 1=parameter-choice by user, 0=suggested or calculated choice (default)
function calc_suggestion( $reg_count, $pool_size, $pool_count, $games_factor, $user_choice=0 )
{
$user_capacity = $pool_size * $pool_count;
$pool_size_base = ($pool_count > 0) ? floor( $reg_count / $pool_count ) : 1;
$pool_count_remain = $reg_count - $pool_size_base * $pool_count;
$pool_count_base = $pool_count - $pool_count_remain;
$games_count = $pool_count_base * TournamentUtils::calc_pool_games( $pool_size_base, $games_factor )
+ $pool_count_remain * TournamentUtils::calc_pool_games( $pool_size_base + 1, $games_factor );
if ( $pool_count_remain > 0 )
{
$max_games_per_user = $games_factor * $pool_size_base;
$distribution = sprintf( '%d x %d + %d x %d', //==reg_count, PC_base + PC_remain = pool_count
$pool_count_base, $pool_size_base, $pool_count_remain, $pool_size_base + 1 );
}
else
{
$max_games_per_user = $games_factor * ( $pool_size_base - 1 );
$distribution = sprintf( '%d x %d', $pool_count_base, $pool_size_base );
}
return array( $pool_size, $pool_count, $user_capacity, $pool_size_base,
$pool_count_remain, $pool_count_base, $max_games_per_user, $games_count, $distribution, $user_choice );
}//calc_suggestion
function make_suggestions_table( TournamentRound $tround, $reg_count, array &$errors, $games_factor, $user_pool_size=0, $user_pool_count=0 )
{
$arr_check = array();
$arr_uniq = array();
if ( $user_pool_size > 0 && $user_pool_count > 0 )
{
$arr_sugg = calc_suggestion( $reg_count, $user_pool_size, $user_pool_count, $games_factor, 1 );
$arr_uniq["$user_pool_size:$user_pool_count"] = 1;
$arr_check[] = $arr_sugg;
// check if extra-suggestion makes better choice
$new_user_pool_size = $user_pool_size;
if ( $arr_sugg[4] > 0 && $arr_sugg[3] + 1 != $user_pool_size )
$new_user_pool_size = $arr_sugg[3] + 1;
elseif ( $arr_sugg[4] == 0 && $arr_sugg[3] != $user_pool_size )
$new_user_pool_size = $arr_sugg[3];
if ( $new_user_pool_size != $user_pool_size )
{
$arr_sugg = calc_suggestion( $reg_count, $new_user_pool_size, $user_pool_count, $games_factor );
$arr_uniq["$new_user_pool_size:$user_pool_count"] = 1;
$arr_check[] = $arr_sugg;
}
}
for ( $pool_size = $tround->MinPoolSize; $pool_size <= $tround->MaxPoolSize; $pool_size++ )
{
$pool_count = TournamentUtils::calc_pool_count( $reg_count, $pool_size );
$key_uniq = "$pool_size:$pool_count";
if ( !isset($arr_uniq[$key_uniq]) )
$arr_check[] = calc_suggestion( $reg_count, $pool_size, $pool_count, $games_factor );
$arr_uniq[$key_uniq] = 1;
}
unset($arr_uniq);
global $page;
$table = new Table( 'TPoolSuggestions', $page, null, '',
TABLE_NO_SORT|TABLE_NO_HIDE|TABLE_NO_PAGE|TABLE_NO_SIZE );
// add_tablehead($nr, $descr, $attbs=null, $mode=TABLE_NO_HIDE|TABLE_NO_SORT, $sortx='')
$table->add_tablehead( 1, T_("Pool\nSize#tpool_sugg"), 'NumberC');
$table->add_tablehead( 2, T_("Pool\nCount#tpool_sugg"), 'NumberC');
$table->add_tablehead( 3, T_("User\nCapacity#tpool_sugg"), 'NumberC');
$table->add_tablehead( 4, T_("User\nDiff#tpool_sugg"), 'NumberC');
$table->add_tablehead( 5, T_("Best Pool\nDistribution#tpool_sugg"), 'Right');
$table->add_tablehead( 6, T_("User\nGames#tpool_sugg"), 'Number');
$table->add_tablehead( 7, T_("Games\nCount#tpool_sugg"), 'Number');
$table->add_tablehead( $idx=8, T_('Suggestion Note#tpool_sugg'), 'Note');
$arr_best = array();
foreach ( $arr_check as $arr_item )
{
list( $pool_size, $pool_count, $user_capacity, $pool_size_base, $pool_count_remain,
$pool_count_base, $max_games_per_user, $games_count, $distribution, $user_choice ) = $arr_item;
$user_diff = $user_capacity - $reg_count;
$row_str = array(
1 => $pool_size,
2 => $pool_count,
3 => $user_capacity,
4 => $user_diff,
5 => $distribution,
6 => $max_games_per_user,
7 => $games_count,
$idx => '',
);
// check for violations
$pool_size_check = ( $pool_count_remain > 0 ) ? $pool_size_base + 1 : $pool_size_base;
$violation = '';
if ( $pool_size_check < $tround->MinPoolSize )
{
$violation = T_('Violates min. pool-size on slicing!');
if ( $user_choice )
$errors[] = sprintf( T_('Pool count [%s] is too large, the min. pool size [%s] would be violated on slicing.'),
$pool_count, $tround->MinPoolSize );
}
elseif ( $pool_size_check > $tround->MaxPoolSize )
{
$violation = T_('Violates max. pool-size!');
if ( $user_choice )
$errors[] = sprintf( T_('Pool count [%s] is too small, the max. pool size [%s] would be violated.'),
$pool_count, $tround->MaxPoolSize );
}
elseif ( $user_capacity < $reg_count )
{
$violation = T_('Pool-size or count too small!');
if ( $user_choice )
$errors[] = sprintf( T_('Either the pool size [%s] or pool count [%s] is too small to cover all %s registered users.'),
$pool_size, $pool_count, $reg_count );
}
$extra_class = '';
$arr_note = array();
if ( $user_choice )
{
$extra_class .= ' UserChoice';
$arr_note[] = T_('TD-Choice#tpool_sugg');
}
if ( $violation )
{
$extra_class .= ' Violation';
$arr_note[] = $violation;
}
if ( count($arr_note) )
$row_str[$idx] = implode(', ', $arr_note);
if ( $extra_class )
$row_str['extra_class'] = trim($extra_class);
if ( $pool_count_remain > 0 )
{
$r1 = $pool_count_base * $pool_size_base;
$r2 = $pool_count_remain * ( $pool_size_base + 1 );
$ratio = min($r1,$r2) / max($r1,$r2);
}
else
$ratio = 0;
$arr_best[] = array( $row_str, $user_diff, $ratio, $pool_size, $pool_count, (bool)$violation );
}
// find best suggestion (ordered by: min. user-diff, slice-ratio, pool-size)
usort( $arr_best, 'compare_suggestions' );
$row_idx = 0;
$break_val = 0;
foreach ( $arr_best as $arr )
{
list(, $user_diff, $ratio, $pool_size, $pool_count, $has_violation ) = $arr;
$row_str =& $arr_best[$row_idx++][0];
if ( $has_violation )
continue;
$stop_val = ( $user_diff + 1 ) * ( $ratio + 1 );
if ( $break_val > 0 && $break_val != $stop_val )
break;
$break_val = $stop_val;
$row_str[$idx] = concat_str( $row_str[$idx], ', ', T_('Recommended distribution!#tpool_sugg') );
$row_str['extra_class'] = concat_str( (string)@$row_str['extra_class'], ' ' , 'Best' );
}
global $sugg_order_val;
if ( $sugg_order_val == 1 ) // order by pool-size
usort( $arr_best, 'compare_pool_parameters' );
foreach ( $arr_best as $arr )
$table->add_row( $arr[0] );
return $table;
}//make_suggestions_table
// $a/$b are arrays with key/vals: 0:row_str, 1:user_diff, 2:ratio, 3:pool_size, 4:pool_count, 5:has_violation
function compare_pool_parameters( array $a, array $b )
{
if ( $a[3] == $b[3] ) // pool-size
return ($a[5] > $b[5]) ? -1 : 1; // pool-count
else
return ($a[3] < $b[3]) ? -1 : 1;
}
// $a/$b are arrays with key/vals: 0:row_str, 1:user_diff, 2:ratio, 3:pool_size, 4:pool_count, 5:has_violation
function compare_suggestions( array $a, array $b )
{
if ( $a[1] == $b[1] ) // user-diff
{
if ( $a[2] == $b[2] ) // ratio1
return ($a[3] < $b[3]) ? -1 : 1; // pool-size
else
return ($a[2] < $b[2]) ? -1 : 1;
}
else
return ($a[1] < $b[1]) ? -1 : 1;
}
?>
//
koh5_pano
Drag mouse to navigate.
Navigation
- Left/Right Mouse drag: Changes camera heading.
- Up/Sown Mouse drag: Changes camera pitch.
- Scroll wheel: Changes camera field of view.
- I-Key: Displays Info panel with canvas size, image size and FPS.
17.Aug.2010, Martin Wengenmayer