Files
hustoj/web/reinfo.php
T

369 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
$cache_time = 10;
$OJ_CACHE_SHARE = false;
require_once('./include/cache_start.php');
require_once('./include/db_info.inc.php');
require_once('./include/setlang.php');
$view_title = "Welcome To Online Judge";
if(!isset($_SESSION[$OJ_NAME.'_'.'user_id'])){
header("location:loginpage.php");
exit(0);
}
require_once("./include/const.inc.php");
require_once("./include/llm_guidance.inc.php");
if(!isset($_GET['sid'])){
echo "No such code!\n";
require_once("oj-footer.php");
exit(0);
}
function is_valid($str2){
global $_SESSION,$OJ_NAME,$OJ_FRIENDLY_LEVEL;
if(isset($_SESSION[$OJ_NAME.'_'.'source_browser'])) return true;
if($OJ_FRIENDLY_LEVEL>3) return true;
// 如果希望能让任何人都查看对比和RE,放开行首注释,并设定$OJ_SHOW_DIFF=true;
// return true;
$n = strlen($str2);
$str = str_split($str2);
$m = 1;
for($i=0; $i<$n; $i++){
if(is_numeric($str[$i]))
$m++;
}
return $n/$m>3;
}
if(!isset($_SESSION[$OJ_NAME.'_'.'user_id'])){
$view_errors = $MSG_WARNING_ACCESS_DENIED ;
require("template/".$OJ_TEMPLATE."/error.php");
exit(0);
}
$ok = false;
$id = strval(intval($_GET['sid']));
$sql = "SELECT * FROM `solution` WHERE `solution_id`=?";
$result = pdo_query($sql,$id);
$row = $result[0];
$lang = $row['language'];
$contest_id = intval($row['contest_id']);
$problem_id = intval($row['problem_id']);
$problem_num = intval($row['num']);
$view_resubmit_url = "submitpage.php?id=".$problem_id."&sid=".intval($id);
$view_problem_url = "problem.php?id=".$problem_id;
if($contest_id > 0){
$view_resubmit_url = "submitpage.php?cid=".$contest_id."&pid=".$problem_num."&sid=".intval($id);
$view_problem_url = "problem.php?cid=".$contest_id."&pid=".$problem_num;
}
$isRE = $row['result']==10;
$isAC = $row['result']==4 ;
$mark=$row['pass_rate']*100;
if($isAC) $mark=100;
if((isset($_SESSION[$OJ_NAME.'_'.'user_id']) && $row && (strval($row['user_id'])===strval($_SESSION[$OJ_NAME.'_'.'user_id'])))||isset($_SESSION[$OJ_NAME.'_'.'source_browser']))
{
$ok = true;
}
// Compile Error has its own page with compiler diagnostics and source-line
// annotations. Redirect after the ownership check so submit-page polling and
// status-page clicks end up on the same safe CE view.
if ($ok && intval($row['result']) === 11) {
header("Location: ceinfo.php?sid=" . intval($id));
exit(0);
}
$spj=pdo_query("select spj from problem where problem_id=?",$row['problem_id']);
if(!empty($spj)&&$spj[0][0]==2 && $OJ_HIDE_RIGHT_ANSWER && !isset($_SESSION[$OJ_NAME.'_'.'source_browser']) ){
$view_errors = "<h1>$MSG_MARK:$mark</h1><br>";
$ok = false;
}
/**
* 把 N 个相同测试点的 RE 折叠为 "1.in~5.in: Aborted (共 5 个测试点)"
*/
function dedup_runtimeinfo($text, &$dedup_count) {
$lines = explode("\n", $text);
$buckets = array(); // reason => [testcase, ...]
$other = array();
foreach ($lines as $line) {
$line = trim($line);
if ($line === "") continue;
// 形如 "data/1001/1.in:Aborted" 或 "1.in: Segmentation fault"
if (preg_match('/^(?:data\/\d+\/)?([^\s:]+(?:[.:][^\s:]+)*?):\s*(.+)$/', $line, $m)) {
$tc = $m[1];
$reason = trim($m[2]);
$buckets[$reason][] = $tc;
} else {
$other[] = $line;
}
}
$out = array();
foreach ($other as $o) $out[] = $o;
foreach ($buckets as $reason => $tcs) {
$count = count($tcs);
if ($count == 1) {
$out[] = $tcs[0] . ": " . $reason;
} else {
$first = $tcs[0];
$last = $tcs[$count - 1];
$range = ($first === $last) ? $first : ($first . "~" . $last);
$out[] = $range . ": " . $reason . " (共 " . $count . " 个测试点)";
$dedup_count += ($count - 1);
}
}
return implode("\n", $out);
}
/**
* 把 runtimeinfo.error 解析为结构化的 diff blocks
* 返回 [['name' => 'test1.out', 'expected' => [...], 'yours' => [...], 'full_mode' => bool], ...]
* 解析失败返回空数组
*/
function parse_diff_blocks($text) {
$blocks = array();
if (empty($text)) return $blocks;
// 先按 "\n\n" 切分(simple mode 每个测试点之间有空行)
$chunks = preg_split('/\n\s*\n/', $text);
foreach ($chunks as $chunk) {
if (trim($chunk) === '') continue;
$chunk = trim($chunk, "\r\n");
// 格式 1: 整段就是一个 =====[name]===== 块(包含表头+数据+结尾 ====)
// 形如 "========[test1.out]========\nExpected | Yours\nFail | Fall\n=============================="
if (preg_match('/^=+\[([^\]]+)\]\=+(.+?)\n=+\s*$/s', $chunk, $m)) {
$name = trim($m[1]);
$body = $m[2];
if (llm_guidance_testcase_base($name) === null) continue;
// OJ_FULL_DIFF also contains hidden test input. It is never interpreted
// as an Expected/Yours block; normal users fall back to focused guidance.
if (llm_guidance_has_full_diff_sections($chunk)) continue;
// body 形如 "\nExpected | Yours\nFail | Fall"
// 去掉首尾的换行符
$body = trim($body, "\n\r");
$rows = llm_guidance_parse_diff_rows($body);
$expected = $rows['expected'];
$yours = $rows['yours'];
if (!empty($expected)) {
$blocks[] = array(
'name' => $name,
'expected' => $expected,
'yours' => $yours,
'full_mode' => true,
);
}
continue;
}
// 格式 2: simple mode "test1.out\n--\n|Expected|Yours\n|--|--\n|row1|row2"
if (preg_match('/^([^\n=|]+)\n--\n([\s\S]+)$/', $chunk, $m)) {
$name = trim($m[1]);
$body = $m[2];
if (llm_guidance_testcase_base($name) === null) continue;
$rows = llm_guidance_parse_diff_rows($body);
$expected = $rows['expected'];
$yours = $rows['yours'];
if (!empty($expected)) {
$blocks[] = array(
'name' => $name,
'expected' => $expected,
'yours' => $yours,
'full_mode' => false,
);
}
}
}
return $blocks;
}
$is_compile_error = intval($row['result']) == 11;
$view_reinfo = "";
$view_reinfo_raw = ""; // 原始(管理员/有权限者看)
$view_reinfo_summary = ""; // 折叠后(普通用户看)
$view_reinfo_dedup_count = 0; // 折叠掉的测试点数
$verdict_color_class = "blue"; // semantic ui color: green/red/orange/yellow/grey
$result_icon = "info circle";
$verdict_tip = "";
if($ok){
if(strval($row['user_id'])!==strval($_SESSION[$OJ_NAME.'_'.'user_id'])){
$view_mail_link= "<a href='mail.php?to_user=".htmlentities($row['user_id'],ENT_QUOTES,"UTF-8")."&title=$MSG_SUBMIT $id'>Mail the auther</a>";
}
$sql = $is_compile_error
? "SELECT `error` FROM `compileinfo` WHERE `solution_id`=?"
: "SELECT `error` FROM `runtimeinfo` WHERE `solution_id`=?";
$rt = pdo_query($sql,$id);
$raw_error = "";
if(isset($rt[0])){
$raw_error = $rt[0]['error'];
// 统一换行符:先替换 \r\n,再替换单独的 \r
$raw_error = str_replace(array("\r\n", "\r"), "\n", $raw_error);
}
// === 阶段 1:基于 solution.result 给"主解释"(永远非空)===
$result_code = intval($row['result']);
$is_admin_session = isset($_SESSION[$OJ_NAME."_administrator"]);
$is_source_browser = isset($_SESSION[$OJ_NAME.'_'.'source_browser']);
// 颜色 + 图标
switch ($result_code) {
case 4: $verdict_color_class = "green"; $result_icon = "check circle"; break;
case 5: // PE
case 6: $verdict_color_class = "red"; $result_icon = "times circle"; break;
case 7: // TLE
case 8: // MLE
case 9: $verdict_color_class = "yellow";$result_icon = "hourglass end"; break;
case 10: $verdict_color_class = "red"; $result_icon = "bug"; break;
case 11: $verdict_color_class = "grey"; $result_icon = "code"; break;
case 13: $verdict_color_class = "blue"; $result_icon = "play circle"; break;
default: $verdict_color_class = "blue"; $result_icon = "info circle";
}
// 友好提示:仅保留标题性文字,具体修复建议交给 AI 点评
switch ($result_code) {
case 4: // AC
$verdict_tip = "本次提交通过了所有测试点";
break;
case 5: // PE
$verdict_tip = "输出与预期基本一致但格式不符(Presentation Error";
break;
case 6: // WA
$verdict_tip = "输出与预期不符(Wrong Answer";
break;
case 7: // TLE
$verdict_tip = $MSG_HINT_TLE;
break;
case 8: // MLE
$verdict_tip = $MSG_HINT_MLE;
break;
case 9: // OLE
$verdict_tip = $MSG_HINT_OLE;
break;
case 10: // RE
$verdict_tip = "程序运行中发生错误(Runtime Error";
break;
case 11: // CE
$verdict_tip = "编译错误(Compile Error)。请查看下方编译信息。";
break;
case 13: // TR (test run)
$verdict_tip = "测试运行完成(Test Run";
break;
default:
$verdict_tip = "正在评判,请稍候刷新";
}
// === 阶段 3:原始错误处理(syscall 提示 vs 占位符修复)===
$view_reinfo_raw = $raw_error;
if (trim($raw_error) === "") {
// TLE/MLE/OLE 时 runtimeinfo 通常为空
$view_reinfo_summary = "";
} elseif ($is_compile_error) {
$view_reinfo_summary = $raw_error;
} else {
// 含 "PASS" 不再显示 "error3" 占位
// 含 "php" 不再显示 "error2" 占位(用 generic 提示)
if (strpos($raw_error, "php") !== false) {
$view_reinfo_summary = $MSG_RUNTIME_GENERIC_ERROR;
$view_reinfo_raw = $raw_error; // 管理员仍能看
}
// 含 "judge/" 区分:是否是 syscall 错误
elseif (strpos($raw_error, "judge/") !== false
&& strpos($raw_error, "CALLID") === false
&& strpos($raw_error, "Forbidden system call") === false) {
$view_reinfo_summary = "潜在的数组或指针越界,请检查代码。";
}
// 默认:原文 + 去重
else {
$view_reinfo_summary = $raw_error;
// 多测试点去重:按 "infile: error" 模式分组
$view_reinfo_summary = dedup_runtimeinfo($view_reinfo_summary, $view_reinfo_dedup_count);
}
}
$view_reinfo = $view_reinfo_summary;
$show_raw = $is_admin_session || $is_source_browser;
$unsafe_full_diff = llm_guidance_has_full_diff_sections($raw_error);
// 尝试解析为结构化的 diff 数据(用于高完成度 WA/PE 的对比展示)。
// Full diff 同时携带隐藏输入,普通页面不解析也不回显。
$diff_blocks = $unsafe_full_diff ? array() : parse_diff_blocks($raw_error);
// 完成度只使用学生自己的源码和判题统计;不查询参考答案。
$source_result = pdo_query("SELECT `source` FROM `source_code_user` WHERE `solution_id`=?", $id);
$student_source = (!empty($source_result) && $source_result !== -1)
? $source_result[0]['source']
: "";
$llm_guidance = llm_guidance_assess($student_source, $result_code, floatval($row['pass_rate']));
$llm_guidance = llm_guidance_refine_with_output_attempt(
$llm_guidance,
$result_code,
llm_guidance_output_attempt($unsafe_full_diff ? '' : $raw_error)
);
if ($llm_guidance['show_output_diff'] && empty($diff_blocks)) {
$llm_guidance['mode'] = 'focused_hint';
$llm_guidance['show_output_diff'] = false;
}
$llm_guidance_auto_fetch = true;
// 管理员或 source_browser 永远看完整原文。普通学生的 WA/PE
// 只看经过策略授权的结构化 diff,不再从原始折叠区旁路读取。
if (!$show_raw && ($result_code === 5 || $result_code === 6)) {
if ($llm_guidance['show_output_diff'] && !empty($diff_blocks)) {
$view_reinfo_summary = "已在下方显示本次错误测试点的结构化输出差异。";
} else {
// 低/中完成度不能通过重复白卷提交套取隐藏测试点答案。
$diff_blocks = array();
$view_reinfo_summary = "当前提交完成度不足,暂不显示隐藏测试点的期望输出。请先补齐程序,再重新提交。";
}
}
if ($show_raw) {
$view_reinfo = $view_reinfo_raw;
} else {
$view_reinfo = $view_reinfo_summary;
}
$llm_guidance_has_output_diff = !empty($diff_blocks);
$is_owner_session = isset($_SESSION[$OJ_NAME.'_'.'user_id'])
&& strval($row['user_id']) === strval($_SESSION[$OJ_NAME.'_'.'user_id']);
$download_hidden_spj = !empty($spj)
&& intval($spj[0][0]) === 2
&& !empty($OJ_HIDE_RIGHT_ANSWER);
$download_diff_enabled = !isset($OJ_SHOW_DIFF) || $OJ_SHOW_DIFF;
$llm_guidance_download_allowed = $is_admin_session
|| ($is_owner_session
&& $download_diff_enabled
&& !$download_hidden_spj
&& !$unsafe_full_diff
&& !empty($llm_guidance['show_output_diff']));
}
// 不是本人的提交,且不是 source_browser
else{
if($spj[0][0]!=2) $view_errors = $MSG_WARNING_ACCESS_DENIED;
require("template/".$OJ_TEMPLATE."/error.php");
exit(0);
}
/////////////////////////Template
if(!$is_compile_error && !isset($_SESSION[$OJ_NAME.'_'.'source_browser']) && $OJ_SHOW_DIFF==false){
$view_errors = $MSG_WARNING_ACCESS_DENIED;
require("template/".$OJ_TEMPLATE."/error.php");
exit(0);
}
else{
require("template/".$OJ_TEMPLATE."/reinfo.php");
}
/////////////////////////Common foot
if(file_exists('./include/cache_end.php')){
require_once('./include/cache_end.php');
}
?>