Files

312 lines
11 KiB
PHP
Raw Permalink 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) {
return llm_guidance_parse_diff_blocks($text);
}
$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 是原评测器输出的一个视图,不受 AI 完成度策略控制。
// Full diff 的原文仍按原有 OJ_SHOW_DIFF/权限规则展示,但不会被二次解析。
$diff_blocks = $unsafe_full_diff ? array() : parse_diff_blocks($raw_error);
// AI 完成度只决定辅导方式,不修改评测器原文、diff 或下载权限。
$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'] = 'judge_output';
$llm_guidance['show_output_diff'] = false;
}
$llm_guidance_auto_fetch = true;
if ($show_raw) {
$view_reinfo = $view_reinfo_raw;
} elseif ($unsafe_full_diff) {
// Full mode includes hidden testcase input/expected output. Preserve the
// judge structure and the student's output, but redact protected data.
$view_reinfo = llm_guidance_redact_full_diff($view_reinfo_raw);
} else {
// AI guidance is additive: ordinary judge/compiler/runtime text remains
// byte-for-byte visible instead of being replaced by an AI policy message.
$view_reinfo = $view_reinfo_raw;
}
$view_reinfo_dedup_count = 0;
$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;
$testcase_download_allowed = $is_admin_session
|| ($is_owner_session
&& $download_diff_enabled
&& !$download_hidden_spj
&& !$unsafe_full_diff
&& ($result_code === 5 || $result_code === 6));
}
// 不是本人的提交,且不是 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');
}
?>