首页 > 其他分享 >创新实训(七)——比赛(Contest)内部逻辑处理

创新实训(七)——比赛(Contest)内部逻辑处理

时间:2024-06-22 23:31:27浏览次数:26  
标签:rating 逻辑 竞赛 form contest Contest 实训 contests id

比赛部分

比赛部分包含比赛列表显示、单个比赛内部信息显示、比赛管理、比赛人员以及比赛报名页面这五个代码主要代码文件。此外在内部逻辑处理时还使用了modellib下的部分配置代码

比赛列表

比赛列表包含“正在进行或即将到来的比赛”和“已结束的比赛”这两部分具体代码实现解析如下

生成单个比赛信息

在php代码中定义echoContest,用于生成单行比赛的列表信息。该方法将封装并利用于生成长表格

// 生成单个比赛信息的方法封装
	function echoContest($contest) {
		global $myUser, $upcoming_contest_name, $upcoming_contest_href, $rest_second;
		
		// 比赛主要链接信息的HTML代码生成
		$contest_name_link = <<<EOD
<a href="/contest/{$contest['id']}">{$contest['name']}</a>
EOD;
		genMoreContestInfo($contest);
		// 根据比赛状态进行前端页面显示更改,添加contest_name_link的代码细节
		if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
			$cur_rest_second = $contest['start_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
			if ($cur_rest_second < $rest_second) {
				$upcoming_contest_name = $contest['name'];
				$upcoming_contest_href = "/contest/{$contest['id']}";
				$rest_second = $cur_rest_second;
			}
			if ($myUser != null && hasRegistered($myUser, $contest)) {
				$contest_name_link .= '<sup><a style="color:green">'.UOJLocale::get('contests::registered').'</a></sup>';
			} else {
				$contest_name_link .= '<sup><a style="color:red" href="/contest/'.$contest['id'].'/register">'.UOJLocale::get('contests::register').'</a></sup>';
			}
		} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) {
			$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::in progress').'</a></sup>';
		} elseif ($contest['cur_progress'] == CONTEST_PENDING_FINAL_TEST) {
			$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::pending final test').'</a></sup>';
		} elseif ($contest['cur_progress'] == CONTEST_TESTING) {
			$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::final testing').'</a></sup>';
		} elseif ($contest['cur_progress'] == CONTEST_FINISHED) {
			$contest_name_link .= '<sup><a style="color:grey" href="/contest/'.$contest['id'].'/standings">'.UOJLocale::get('contests::ended').'</a></sup>';
		}
		
		$last_hour = round($contest['last_min'] / 60, 2);
		
		// 打印单行比赛信息
		$click_zan_block = getClickZanBlock('C', $contest['id'], $contest['zan']);
		echo '<tr>';
		echo '<td>', $contest_name_link, '</td>';
		echo '<td>', '<a href="'.HTML::timeanddate_url($contest['start_time'], array('duration' => $contest['last_min'])).'">'.$contest['start_time_str'].'</a>', '</td>';
		echo '<td>', UOJLocale::get('hours', $last_hour), '</td>';
		echo '<td>', '<a href="/contest/'.$contest['id'].'/registrants"><span class="glyphicon glyphicon-user"></span> &times;'.$contest['player_num'].'</a>', '</td>';
		echo '<td>', '<div class="text-left">'.$click_zan_block.'</div>', '</td>';
		echo '</tr>';
	}

打印比赛列表

打印两个比赛列表的形式大同小异。以正在进行或即将到来的比赛为例,先整理表头的HTML代码,然后再利用html-lib.php的长列表方法将封装好的变量方法放入处理

    // 表头整理
	$table_header = '';
	$table_header .= '<tr>';
	$table_header .= '<th>'.UOJLocale::get('contests::contest name').'</th>';
	$table_header .= '<th style="width:15em;">'.UOJLocale::get('contests::start time').'</th>';
	$table_header .= '<th style="width:100px;">'.UOJLocale::get('contests::duration').'</th>';
	$table_header .= '<th style="width:100px;">'.UOJLocale::get('contests::the number of registrants').'</th>';
	$table_header .= '<th style="width:180px;">'.UOJLocale::get('appraisal').'</th>';
	$table_header .= '</tr>';
	// 生成长表格
	echoLongTable(array('*'), 'contests', "status != 'finished'", 'order by id desc', $table_header,
		echoContest,
		array('page_len' => 100)
	);

附:表格打印

html-lib.php中,使用echoLongTable方法对表格进行打印

// 重要代码:生成长表格的方法,适用于各大需要生成长表格(例如比赛列表、评测列表等)
function echoLongTable($col_names, $table_name, $cond, $tail, $header_row, $print_row, $config) {

	// 初始化分页配置
	$pag_config = $config;
	$pag_config['col_names'] = $col_names;
	$pag_config['table_name'] = $table_name;
	$pag_config['cond'] = $cond;
	$pag_config['tail'] = $tail;
	$pag = new Paginator($pag_config);

	// 设置表格样式
	$div_classes = isset($config['div_classes']) ? $config['div_classes'] : array('table-responsive');
	$table_classes = isset($config['table_classes']) ? $config['table_classes'] : array('table', 'table-bordered', 'table-hover', 'table-striped', 'table-text-center');
		
	// 打印表格样式
	echo '<div class="', join($div_classes, ' '), '">';
	echo '<table class="', join($table_classes, ' '), '">';
	echo '<thead>';
	echo $header_row;
	echo '</thead>';
	echo '<tbody>';

	// 打印表格内容
	foreach ($pag->get() as $idx => $row) {
		if (isset($config['get_row_index'])) {
			$print_row($row, $idx);
		} else {
			$print_row($row);
		}
	}
	if ($pag->isEmpty()) {
		echo '<tr><td colspan="233">'.UOJLocale::get('none').'</td></tr>';
	}

	// 打印表格结尾和分页控件
	echo '</tbody>';
	echo '</table>';
	echo '</div>';
	
	if (isset($config['print_after_table'])) {
		$fun = $config['print_after_table'];
		$fun();
	}
		
	echo $pag->pagination();
}

比赛倒计时

在即将到来的比赛下,显示其倒计时。比赛时间一到给出弹窗信息

	// 这段用来显示最近1天内最先要开始的比赛倒计时,时间一到就会显示弹窗
	if ($rest_second <= 86400) {
		echo <<<EOD
<div class="text-center bot-buffer-lg">
<div class="text-warning">$upcoming_contest_name 倒计时</div>
<div id="contest-countdown"></div>
<script type="text/javascript">
$('#contest-countdown').countdown($rest_second, function() {
	if (confirm('$upcoming_contest_name 已经开始了。是否要跳转到比赛页面?')) {
		window.location.href = "$upcoming_contest_href";
	} 
});
</script>
</div>
EOD;
	}

比赛内部信息

由于负责contest_inside.php的信息内部处理逻辑较多,这里只取部分主要内部逻辑处理的php代码进行解读

1. echoDashboard 函数

这个函数用于显示竞赛控制台,包括问题、公告和提交问题的表单。

function echoDashboard() {
	global $contest, $post_notice, $post_question, $reply_question;
	
	$myname = Auth::id();
	$contest_problems = DB::selectAll("select contests_problems.problem_id, best_ac_submissions.submission_id from contests_problems left join best_ac_submissions on contests_problems.problem_id = best_ac_submissions.problem_id and submitter = '{$myname}' where contest_id = {$contest['id']} order by contests_problems.problem_id asc");
	
	for ($i = 0; $i < count($contest_problems); $i++) {
		$contest_problems[$i]['problem'] = queryProblemBrief($contest_problems[$i]['problem_id']);
	}
	
	$contest_notice = DB::selectAll("select * from contests_notice where contest_id = {$contest['id']} order by time desc");
	
	if (Auth::check()) {
		$my_questions = DB::selectAll("select * from contests_asks where contest_id = {$contest['id']} and username = '{$myname}' order by post_time desc");
		$my_questions_pag = new Paginator([
			'data' => $my_questions
		]);
	} else {
		$my_questions_pag = null;
	}
	
	$others_questions_pag = new Paginator([
		'col_names' => array('*'),
		'table_name' => 'contests_asks',
		'cond' => "contest_id = {$contest['id']} and username != '{$myname}' and is_hidden = 0",
		'tail' => 'order by reply_time desc',
		'page_len' => 10
	]);
	
	uojIncludeView('contest-dashboard', [
		'contest' => $contest,
		'contest_notice' => $contest_notice,
		'contest_problems' => $contest_problems,
		'post_question' => $post_question,
		'my_questions_pag' => $my_questions_pag,
		'others_questions_pag' => $others_questions_pag
	]);
}
  • 从数据库中获取竞赛问题和用户的最佳提交,并查询每个问题的简要信息。
  • 获取竞赛公告,并按时间倒序排列。
  • 如果用户已登录,获取用户提交的问题并分页显示。
  • 获取其他用户提交的问题(公开的)并分页显示。
  • 包含一个视图文件 contest-dashboard,传递相关数据进行显示。

2. echoBackstage 函数

这个函数用于显示竞赛的后台管理页面,包括发布公告和回复问题的表单。

function echoBackstage() {
	global $contest, $post_notice, $reply_question;
	
	$questions_pag = new Paginator([
		'col_names' => array('*'),
		'table_name' => 'contests_asks',
		'cond' => "contest_id = {$contest['id']}",
		'tail' => 'order by post_time desc',
		'page_len' => 50
	]);
	
	if ($contest['cur_progress'] < CONTEST_TESTING) {
		$contest_data = queryContestData($contest, ['pre_final' => true]);
		calcStandings($contest, $contest_data, $score, $standings);
		
		$standings_data = [
			'contest' => $contest,
			'standings' => $standings,
			'score' => $score,
			'contest_data' => $contest_data
		];
	} else {
		$standings_data = null;
	}
	
	uojIncludeView('contest-backstage', [
		'contest' => $contest,
		'post_notice' => $post_notice,
		'reply_question' => $reply_question,
		'questions_pag' => $questions_pag,
		'standings_data' => $standings_data
	]);
}
  • 获取并分页显示所有竞赛问题。
  • 如果竞赛尚未进入最终测试阶段,计算竞赛排名数据。
  • 包含一个视图文件 contest-backstage,传递相关数据进行显示。

3. echoMySubmissions 函数

这个函数用于显示用户在竞赛中的提交记录,并提供一个选项来显示所有提交记录。

function echoMySubmissions() {
	global $contest, $myUser;

	$show_all_submissions_status = Cookie::get('show_all_submissions') !== null ? 'checked="checked" ' : '';
	$show_all_submissions = UOJLocale::get('contests::show all submissions');
	echo <<<EOD
		<div class="checkbox text-right">
			<label for="input-show_all_submissions"><input type="checkbox" id="input-show_all_submissions" $show_all_submissions_status/> $show_all_submissions</label>
		</div>
		<script type="text/javascript">
			$('#input-show_all_submissions').click(function() {
				if (this.checked) {
					$.cookie('show_all_submissions', '');
				} else {
					$.removeCookie('show_all_submissions');
				}
				location.reload();
			});
		</script>
EOD;
	if (Cookie::get('show_all_submissions') !== null) {
		echoSubmissionsList("contest_id = {$contest['id']}", 'order by id desc', array('judge_time_hidden' => ''), $myUser);
	} else {
		echoSubmissionsList("submitter = '{$myUser['username']}' and contest_id = {$contest['id']}", 'order by id desc', array('judge_time_hidden' => ''), $myUser);
	}
}
  • 提供一个复选框,用户可以选择是否显示所有提交记录。
  • 根据用户选择,显示用户自己的提交记录或所有提交记录。

4. echoStandings 函数

这个函数用于显示竞赛的排名情况。

function echoStandings() {
	global $contest;
	
	$contest_data = queryContestData($contest);
	calcStandings($contest, $contest_data, $score, $standings);
	
	uojIncludeView('contest-standings', [
		'contest' => $contest,
		'standings' => $standings,
		'score' => $score,
		'contest_data' => $contest_data
	]);
}
  • 获取竞赛数据并计算排名。
  • 包含一个视图文件 contest-standings,传递相关数据进行显示。

5. echoContestCountdown 函数

这个函数用于显示竞赛的倒计时。

function echoContestCountdown() {
	global $contest;
 	$rest_second = $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
 	$time_str = UOJTime::$time_now_str;
 	$contest_ends_in = UOJLocale::get('contests::contest ends in');
 	echo <<<EOD
 		<div class="card border-info">
 			<div class="card-header bg-info">
 				<h3 class="card-title">$contest_ends_in</h3>
 			</div>
 			<div class="card-body text-center countdown" data-rest="$rest_second"></div>
 		</div>
		<script type="text/javascript">
			checkContestNotice({$contest['id']}, '$time_str');
		</script>
EOD;
}
  • 计算剩余时间并显示倒计时卡片。
  • 包含一个 JavaScript 脚本用于实时更新倒计时。

6. echoContestJudgeProgress 函数

这个函数用于显示竞赛的判题进度。

function echoContestJudgeProgress() {
	global $contest;
	if ($contest['cur_progress'] < CONTEST_TESTING) {
		$rop = 0;
		$title = UOJLocale::get('contests::contest pending final test');
	} else {
		$total = DB::selectCount("select count(*) from submissions where contest_id = {$contest['id']}");
		$n_judged = DB::selectCount("select count(*) from submissions where contest_id = {$contest['id']} and status = 'Judged'");
		$rop = $total == 0 ? 100 : (int)($n_judged / $total * 100);
		$title = UOJLocale::get('contests::contest final testing');
	}
	echo <<<EOD
 		<div class="card border-info">
 			<div class="card-header bg-info">
 				<h3 class="card-title">$title</h3>
 			</div>
 			<div class="card-body">
				<div class="progress bot-buffer-no">
					<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="$rop" aria-valuemin="0" aria-valuemax="100" style="width: {$rop}%; min-width: 20px;">{$rop}%</div>
				

	</div>
				</div>
			</div>
		</div>
EOD;
}
  • 根据竞赛当前的进度,显示不同的判题进度条。
  • 包含一个进度条,显示已判题目的百分比。

7. echoContestFinished 函数

这个函数用于显示竞赛结束的状态。

function echoContestFinished() {
	$title = UOJLocale::get('contests::contest ended');
	echo <<<EOD
		<div class="card border-info">
			<div class="card-header bg-info">
				<h3 class="card-title">$title</h3>
			</div>
		</div>
EOD;
}
  • 显示一个简单的卡片,表明竞赛已结束。

附:比赛rating计算

这个评分计算逻辑实现了一种基于排名的评分系统,用于计算参赛者在比赛后的新评分(Rating)。下面是对这段代码的详细解释:

函数定义

function calcRating($standings, $K = 400) {
    $DELTA = 500;
  • calcRating 是一个计算评分的函数。
  • 参数 $standings 是比赛排名列表。
  • 参数 $K 是一个常数,用于控制评分变化的幅度,默认值为 400。
  • $DELTA 是一个常数,用于计算权重。

获取参赛人数和初始评分

$n = count($standings);
$rating = array();
for ($i = 0; $i < $n; ++$i) {
    $rating[$i] = $standings[$i][2][1];
}
  • $n 是参赛人数。
  • $rating 数组存储每个参赛者的初始评分。

计算排名和名次

$rank = array();
$foot = array();
for ($i = 0; $i < $n; ) {
    $j = $i;
    while ($j + 1 < $n && $standings[$j + 1][3] == $standings[$j][3]) {
        ++$j;
    }
    $our_rk = 0.5 * (($i + 1) + ($j + 1));
    while ($i <= $j) {
        $rank[$i] = $our_rk;
        $foot[$i] = $n - $rank[$i];
        $i++;
    }
}
  • rank 数组存储每个参赛者的实际排名。
  • foot 数组存储每个参赛者的反向排名(即排名从后往前数的位置)。
  • 这段代码计算并存储参赛者的实际排名,如果多个参赛者的排名相同,他们的实际排名取平均值。

计算权重和期望排名

$weight = array();
for ($i = 0; $i < $n; ++$i) {
    $weight[$i] = pow(7, $rating[$i] / $DELTA);
}
$exp = array_fill(0, $n, 0);
for ($i = 0; $i < $n; ++$i)
    for ($j = 0; $j < $n; ++$j)
        if ($j != $i) {
            $exp[$i] += $weight[$i] / ($weight[$i] + $weight[$j]);
        }
  • weight 数组存储每个参赛者的权重,基于其初始评分计算。
  • exp 数组存储每个参赛者的期望排名分数,基于所有参赛者之间的权重比计算。

计算新评分

$new_rating = array();
for ($i = 0; $i < $n; $i++) {
    $new_rating[$i] = $rating[$i];
    $new_rating[$i] += ceil($K * ($foot[$i] - $exp[$i]) / ($n - 1));
}
  • new_rating 数组存储每个参赛者的新评分。
  • 新评分的计算公式:new_rating = rating + K * (实际排名分数 - 期望排名分数) / (参赛人数 - 1),并取整。

调整新评分

for ($i = $n - 1; $i >= 0; $i--) {
    if ($i + 1 < $n && $standings[$i][3] != $standings[$i + 1][3]) {
        break;
    }
    if ($new_rating[$i] > $rating[$i]) {
        $new_rating[$i] = $rating[$i];
    }
}

for ($i = 0; $i < $n; $i++) {
    if ($new_rating[$i] < 0) {
        $new_rating[$i] = 0;
    }
}
  • 如果排名靠后的参赛者的新评分比初始评分高,将其调整为初始评分。
  • 确保所有新评分不低于零。

返回新评分

return $new_rating;
}
  • 返回计算后的新评分数组。

比赛后台信息管理

对于具有管理员权限的用户,需要一个比赛后台页面去处理比赛后台的信息。这里使用contest_manage.php去处理其中的页面逻辑和处理逻辑

这段代码是一个 PHP 文件,通常用于管理竞赛相关的设置。让我们逐段解读其内部逻辑和功能:

1. 引入必要的 PHP 库和函数

<?php
	requirePHPLib('form');
  • 使用 requirePHPLib 函数引入了一个名为 form 的 PHP 库,这个库可能包含了用于表单处理的函数和类。

2. 验证和查询竞赛信息

if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
	become404Page();
}
  • 首先,验证了从 GET 请求中获取的竞赛 ID 是否是一个无符号整数,并且查询对应的竞赛信息。
  • 如果竞赛 ID 不合法或者查询竞赛失败,则调用 become404Page() 函数,显示 404 页面。

3. 生成更多竞赛信息

genMoreContestInfo($contest);
  • 调用 genMoreContestInfo 函数,可能用于生成和展示更多关于竞赛的详细信息,但具体实现不在这段代码中。

4. 权限检查

if (!isSuperUser($myUser)) {
	become403Page();
}
  • 判断当前用户是否为超级用户(管理员)。如果不是,则调用 become403Page() 函数,显示 403 页面,表示权限不足。

5. 创建并处理比赛时间表单 time_form

$time_form = new UOJForm('time');
// 添加输入字段及其验证规则和处理逻辑
$time_form->runAtServer();
  • 创建了一个名为 time_form 的表单对象,用于编辑竞赛的开始时间、时长和名称。
  • 设定了各字段的初始值和验证函数,以及处理函数。

6. 创建和处理管理者表单 managers_form 和题目表单 problems_form

$managers_form = newAddDelCmdForm('managers', ...);
$problems_form = newAddDelCmdForm('problems', ...);
$managers_form->runAtServer();
$problems_form->runAtServer();
  • 创建了两个表单对象 managers_formproblems_form,用于添加和删除竞赛的管理员和题目。
  • 设定了验证函数和处理函数,并执行了表单处理操作。

7. 超级用户特有功能表单处理

if (isSuperUser($myUser)) {
	// 创建和处理额外的表单,如评分变化上限、比赛类型、排名版本等
}
  • 如果当前用户是超级用户,创建和处理了额外的表单,用于编辑评分变化上限、比赛类型和排名版本等竞赛的附加设置。

8. 运行表单处理逻辑

$time_form->runAtServer();
$managers_form->runAtServer();
$problems_form->runAtServer();
  • 最后,依次运行了所有表单对象的 runAtServer() 方法,处理提交的数据并更新数据库。

比赛报名人员信息显示

这段代码主要用于展示竞赛的报名参赛者信息,并提供一些管理功能。让我们逐段解读其功能和逻辑:

1. 处理未开始竞赛的逻辑

if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
    // 判断用户是否已经报名
    $iHasRegistered = $myUser != null && hasRegistered($myUser, $contest);

    if ($iHasRegistered) {
        // 创建取消报名的表单
        $unregister_form = new UOJForm('unregister');
        $unregister_form->handle = function() {
            global $myUser, $contest;
            DB::query("delete from contests_registrants where username = '{$myUser['username']}' and contest_id = {$contest['id']}");
            updateContestPlayerNum($contest);
        };
        $unregister_form->submit_button_config['class_str'] = 'btn btn-danger btn-xs';
        $unregister_form->submit_button_config['text'] = '取消报名';
        $unregister_form->succ_href = "/contests";
        $unregister_form->runAtServer();
    }
    
    if ($has_contest_permission) {
        // 创建重新计算参赛前 rating 的表单
        $pre_rating_form = new UOJForm('pre_rating');
        $pre_rating_form->handle = function() {
            global $contest;
            foreach (DB::selectAll("select * from contests_registrants where contest_id = {$contest['id']}") as $reg) {
                $user = queryUser($reg['username']);
                DB::update("update contests_registrants set user_rating = {$user['rating']} where contest_id = {$contest['id']} and username = '{$user['username']}'");
            }
        };
        $pre_rating_form->submit_button_config['align'] = 'right';
        $pre_rating_form->submit_button_config['class_str'] = 'btn btn-warning';
        $pre_rating_form->submit_button_config['text'] = '重新计算参赛前的 rating';
        $pre_rating_form->submit_button_config['smart_confirm'] = '';
        
        $pre_rating_form->runAtServer();
    }
}
  • 如果竞赛尚未开始 ($contest['cur_progress'] == CONTEST_NOT_STARTED),则执行以下逻辑:
    • 检查当前用户是否已经报名。
    • 如果已经报名,则创建一个用于取消报名的表单,并处理取消报名的逻辑。
    • 如果当前用户有管理竞赛的权限 ($has_contest_permission),则创建一个用于重新计算参赛前评分 (user_rating) 的表单,并处理相应逻辑。

2. 页面头部和竞赛标题展示

<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - ' . UOJLocale::get('contests::contest registrants')) ?>

<h1 class="text-center"><?= $contest['name'] ?></h1>
  • 使用 echoUOJPageHeader 函数显示页面的头部,包括竞赛的标题和“竞赛参赛者”等翻译后的本地化文本。

3. 根据竞赛状态显示报名信息

<?php if ($contest['cur_progress'] == CONTEST_NOT_STARTED): ?>
    <?php if ($iHasRegistered): ?>
        <div class="float-right">
            <?php $unregister_form->printHTML(); ?>
        </div>
        <div><a style="color:green">已报名</a></div>
    <?php else: ?>
        <div>当前尚未报名,您可以<a style="color:red" href="/contest/<?= $contest['id'] ?>/register">报名</a>。</div>
    <?php endif ?>
<div class="top-buffer-sm"></div>
<?php endif ?>
  • 如果竞赛尚未开始,根据用户是否已经报名显示不同的信息:
    • 如果已经报名,则显示取消报名按钮。
    • 如果尚未报名,则显示报名链接。

4. 显示参赛者列表

<?php
if ($show_ip) {
    // 如果有权限显示 IP 地址,则设置表头
    $header_row = '<tr><th>#</th><th>'.UOJLocale::get('username').'</th><th>remote_addr</th><th>rating</th></tr>';
    $ip_owner = array();
    foreach (DB::selectAll("select * from contests_registrants where contest_id = {$contest['id']} order by user_rating asc") as $reg) {
        $user = queryUser($reg['username']);
        $ip_owner[$user['remote_addr']] = $reg['username'];
    }
} else {
    // 否则只显示用户名和评分
    $header_row = '<tr><th>#</th><th>'.UOJLocale::get('username').'</th><th>rating</th></tr>';
}

echoLongTable(array('*'), 'contests_registrants', "contest_id = {$contest['id']}", 'order by user_rating desc',
    $header_row,
    function($contest, $num) {
        global $myUser;
        global $show_ip, $ip_owner;
        $user = queryUser($contest['username']);
        $user_link = getUserLink($contest['username'], $contest['user_rating']);
        if (!$show_ip) {
            echo '<tr>';
        } else {
            if ($ip_owner[$user['remote_addr']] != $user['username']) {
                echo '<tr class="danger">';
            } else {
                echo '<tr>';
            }
        }
        echo '<td>'.$num.'</td>';
        echo '<td>'.$user_link.'</td>';
        if ($show_ip) {
            echo '<td>'.$user['remote_addr'].'</td>';
        }
        echo '<td>'.$contest['user_rating'].'</td>';
        echo '</tr>';
    },
    array('page_len' => 100,
        'get_row_index' => '',
        'print_after_table' => function() {
            global $pre_rating_form;
            if (isset($pre_rating_form)) {
                $pre_rating_form->printHTML();
            }
        }
    )
);
?>
  • 使用 echoLongTable 函数显示参赛者列表,根据是否有权限显示 IP 地址决定表格的表头和样式。
  • 对每个参赛者生成一行表格数据,并根据权限高亮显示 IP 地址不匹配的参赛者。
  • 如果有重新计算评分的表单 ($pre_rating_form),则在表格后面显示该表单。

比赛报名页面

contest_registration.php实现了一个简单的竞赛报名页面,用户必须登录后才能进行报名操作。如果用户已经报名或具有管理权限,或者竞赛不是未开始状态,则会被重定向到竞赛列表页面。页面展示了比赛的规则,并提供了一个表单用于用户提交报名信息,成功报名后将跳转回竞赛列表页面。

1. 权限检查和重定向

if ($myUser == null) {
    redirectToLogin();
} elseif (hasContestPermission($myUser, $contest) || hasRegistered($myUser, $contest) || $contest['cur_progress'] != CONTEST_NOT_STARTED) {
    redirectTo('/contests');
}
  • 检查当前用户是否已登录 ($myUser == null),如果未登录,则重定向到登录页面。
  • 如果用户已经具有竞赛的管理权限或已经报名了该竞赛,或者竞赛当前不是未开始状态,则重定向到竞赛列表页面 (/contests)。

2. 处理报名表单

$register_form = new UOJForm('register');
$register_form->handle = function() {
    global $myUser, $contest;
    DB::query("insert into contests_registrants (username, user_rating, contest_id, has_participated) values ('{$myUser['username']}', {$myUser['rating']}, {$contest['id']}, 0)");
    updateContestPlayerNum($contest);
};
$register_form->submit_button_config['class_str'] = 'btn btn-primary';
$register_form->submit_button_config['text'] = '报名比赛';
$register_form->succ_href = "/contests";

$register_form->runAtServer();
  • 创建一个名为 register 的表单对象 UOJForm,用于用户报名操作。
  • 定义了表单的处理函数 ($register_form->handle),在用户点击提交时执行数据库操作,将用户信息插入到 contests_registrants 表中,并更新竞赛的参赛人数。
  • 设置提交按钮的样式和文本,并定义成功后的重定向链接为 /contests
  • 使用 runAtServer() 方法执行表单处理,处理用户的提交请求。

3. 页面头部

<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - 报名') ?>
  • 使用 echoUOJPageHeader 函数显示页面的头部,包括竞赛的标题和“报名”等文本。

4. 比赛规则展示

<h1 class="page-header">比赛规则</h1>
<ul>
    <!-- 比赛规则的具体内容 -->
</ul>
  • 展示了关于比赛的规则,以无序列表的形式呈现。

5. 显示报名表单

<?php $register_form->printHTML(); ?>
  • 使用 printHTML() 方法将报名表单的 HTML 代码输出到页面中,让用户可以填写和提交报名信息。

6. 页面底部

<?php echoUOJPageFooter() ?>
  • 使用 echoUOJPageFooter 函数显示页面的底部,包括版权信息等。

标签:rating,逻辑,竞赛,form,contest,Contest,实训,contests,id
From: https://www.cnblogs.com/hakurokawashiro/p/18262879

相关文章

  • UNIQUE VISION Programming Contest 2024 Summer (AtCoder Beginner Contest 359) 题
    点我看题A-CountTakahashi没啥好说的点击查看代码#include<bits/stdc++.h>#definerep(i,n)for(inti=0;i<n;++i)#definerepn(i,n)for(inti=1;i<=n;++i)#defineLLlonglong#definefifirst#definesesecond#definepbpush_back#definemprmake_pair......
  • AtCoder Beginner Contest 359 解题报告
    AtCoderBeginnerContest359吐槽:A-F还算正常,G题你tm给我放了个出过的板子(ABC340G)是几个意思啊???ASimulate.#include<bits/stdc++.h>usingnamespacestd;#definelllonglong#defineendl'\n'#definePBemplace_back#definePPBpop_back#defineMPmake_pai......
  • 数据宽度,逻辑运算
    一、数据宽度数据宽度指的是在计算机中表示数据时所占用的内存空间大小,它决定了数据在内存中的表示范围和精度。数据宽度通常以位(bit)或字节(Byte)为单位来衡量,不同的数据类型有不同的宽度要求。在计算机科学中,数据宽度是一个基本概念,它涉及到如何有效地在内存中存储和处理数据。位......
  • 计算机系统基础实训五—CacheLab实验
    实验目的与要求1、让学生更好地应用程序性能的优化方法;2、让学生更好地理解存储器层次结构在程序运行过程中所起的重要作用;3、让学生更好地理解高速缓存对程序性能的影响;实验原理与内容本实验将帮助您了解缓存对C程序性能的影响。实验由两部分组成。在第一部分中,您将编写......
  • [本科项目实训] 行动模拟实现 与 FastAPI 对应交互-V0
    to2024/06/01行动模拟该项目拟通过大语言模型分析拆解人类描述的任务,自动的调用行动库进行执行并完成内容的汇报。因而需要针对任务对大模型进行微调(比如本项目使用的p-tuningv2),行动模拟即针对微调结果将大模型与行动库进行对接,模拟实际场景下的函数输入输出。第一步,即加......
  • caddy 模块(module) caddyhttp Start启动逻辑分析
     ./modules/caddyhttp/app.gofunc(app*App)Start()error{//xx} Start方法属于一个自定义的App结构体,用于启动应用程序中的多个HTTP服务器实例。下面是对该方法的主要逻辑和关键步骤的详细分析:1.日志设置:首先,通过zap.NewStdLogAt创建一个兼容......
  • 【初阶数据结构】深入解析带头双向循环链表:探索底层逻辑
    ......
  • 0基础学C++ | 第03天 | 基础知识 |算术运算符 | 赋值运算符 | 比较运算符 | 逻辑运算
    前言前面已经讲了,数据类型以及求数据类型所占的空间0基础学C++|第02天|基础知识|sizeof关键字|浮点型|字符型|转义字符|字符串|布尔类型|数据的输入-CSDN博客,现在讲运算符算术运算符 作用:用于处理四则运算#include<iostream>usingnamespacestd;in......
  • 小程序项目业务逻辑回忆3
    1.用户激活票劵在我的票劵未激活票劵查询业务中,票劵是可以通过激活按钮进行激活的该业务涉及3个表users用户信息表\order_items门票订单关联表\signin_records签到信息表用户激活票劵时,首先使用该用户user_id创建signin_record表,signin_record表中门票类型ticket_type为"......
  • 头歌机器学习实训答案 第1关:集成学习常用算法详解
    任务描述本关任务:学习集成学习的基本概念以及常用算法并编程熟悉sklearn。相关知识为了完成本关任务,你需要掌握:1.个体与集成的概念,2.常用的集成学习算法。个体和集成集成学习(ensemblelearning)通过构建并结合多个学习器来完成学习任务,有时也被称为多分类器系统(multi-class......