public async Task <JudgeResult> JudgeAsync(BuildOption buildOption, JudgeOption judgeOption) { _workingdir = Path.Combine(Path.GetTempPath(), "hjudgeTest", judgeOption.GuidStr); Directory.CreateDirectory(_workingdir); var result = new JudgeResult { JudgePoints = new List <JudgePoint>() }; try { if (judgeOption.AnswerPoint != null) { return(await AnswerJudgeAsync(buildOption, judgeOption)); } var fileName = Path.Combine(_workingdir, $"{buildOption.SubmitFileName}{buildOption.ExtensionName}"); File.WriteAllText(fileName, buildOption.Source, Encoding.UTF8); if (buildOption.StaticCheckOption != null) { var logs = await StaticCheck(buildOption.StaticCheckOption); result.StaticCheckLog = logs; } if (buildOption.CompilerOption != null) { var(isSucceeded, logs) = await Compile(buildOption.CompilerOption, judgeOption.ExtraFiles); result.CompileLog = logs; if (!isSucceeded) { result.JudgePoints = Enumerable.Repeat( new JudgePoint { ResultType = ResultCode.Compile_Error }, judgeOption.DataPoints.Count) .ToList(); return(result); } } for (var i = 0; i < judgeOption.DataPoints.Count; i++) { var point = new JudgePoint(); if (!File.Exists(judgeOption.RunOption?.Exec)) { point.ResultType = ResultCode.Compile_Error; point.ExtraInfo = "Cannot find compiled executable file"; result.JudgePoints.Add(point); continue; } try { try { File.Copy(judgeOption.DataPoints[i].StdInFile.Replace("${index}", (i + 1).ToString()).Replace("${index0}", i.ToString()), Path.Combine(_workingdir, judgeOption.InputFileName), true); } catch { throw new InvalidOperationException("Unable to find standard input file"); } var strErrFile = Path.Combine(_workingdir, $"stderr_{judgeOption.GuidStr}.dat"); var inputFile = Path.Combine(_workingdir, judgeOption.InputFileName); var outputFile = Path.Combine(_workingdir, judgeOption.OutputFileName); var param = new { judgeOption.RunOption.Exec, judgeOption.RunOption.Args, WorkingDir = _workingdir, StdErrRedirectFile = strErrFile, InputFile = inputFile, OutputFile = outputFile, judgeOption.DataPoints[i].TimeLimit, judgeOption.DataPoints[i].MemoryLimit, IsStdIO = judgeOption.UseStdIO, judgeOption.ActiveProcessLimit }; var ret = new StringBuilder(256); if (Execute(JsonConvert.SerializeObject(param), ret)) { point = JsonConvert.DeserializeObject <JudgePoint>(ret.ToString()?.Trim() ?? "{}"); } else { throw new Exception("Unable to execute target program"); } try { File.Copy(judgeOption.DataPoints[i].StdOutFile.Replace("${index}", (i + 1).ToString()).Replace("${index0}", i.ToString()), Path.Combine(_workingdir, $"answer_{judgeOption.GuidStr}.dat"), true); } catch { throw new InvalidOperationException("Unable to find standard output file"); } if (point.ResultType == ResultCode.Runtime_Error) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Enum.IsDefined(typeof(WindowsExceptionCode), (uint)point.ExitCode)) { point.ExtraInfo = Enum.GetName(typeof(WindowsExceptionCode), (uint)point.ExitCode); } else if (Enum.IsDefined(typeof(LinuxExceptionCode), point.ExitCode - 128)) { point.ExtraInfo = Enum.GetName(typeof(LinuxExceptionCode), point.ExitCode - 128); } else { point.ExtraInfo = "UNKNOWN_EXCEPTION"; } point.Score = 0; } else { if (judgeOption.StandardErrorBehavior != StdErrBehavior.Ignore) { try { var stderr = File.ReadAllText(strErrFile).Trim() .Replace(_workingdir, "...") .Replace(_workingdir.Replace("/", "\\"), "...");; if (!string.IsNullOrWhiteSpace(stderr.Replace("\n", string.Empty).Replace("\r", string.Empty).Replace("\t", string.Empty).Trim())) { switch (judgeOption.StandardErrorBehavior) { case StdErrBehavior.TreatAsCompileError: point.ResultType = ResultCode.Compile_Error; point.ExtraInfo = stderr; break; case StdErrBehavior.TreatAsRuntimeError: point.ResultType = ResultCode.Runtime_Error; point.ExtraInfo = stderr; break; } point.Score = 0; } } catch { //ignored } } if (point.ResultType == ResultCode.Accepted) { var(resultType, percentage, extraInfo) = point.ResultType == ResultCode.Accepted ? await CompareAsync(fileName, inputFile, Path.Combine(_workingdir, $"answer_{judgeOption.GuidStr}.dat"), outputFile, judgeOption) : (point.ResultType, 0, point.ExtraInfo); point.ExtraInfo = extraInfo; point.ResultType = resultType; point.Score = percentage * judgeOption.DataPoints[i].Score; } } } catch (Exception ex) { point.ExtraInfo = ex.Message; if (ex is InvalidOperationException) { point.ResultType = ResultCode.Problem_Config_Error; } else { point.ResultType = ResultCode.Unknown_Error; } } result.JudgePoints.Add(point); } } finally { try { Directory.Delete(_workingdir, true); } catch { /* ignored */ } } return(result); }
private async Task <(ResultCode Result, float Percentage, string ExtraInfo)> CompareAsync(string sourceFile, string stdInputFile, string stdOutputFile, string outputFile, JudgeOption judgeOption, bool isAnswerJudge = false) { if (!File.Exists(outputFile)) { return(ResultCode.Output_File_Error, 0, string.Empty); } if (judgeOption.SpecialJudgeOption != null) { var argsBuilder = new StringBuilder(); if (judgeOption.SpecialJudgeOption.UseSourceFile) { argsBuilder.Append($" \"{sourceFile}\""); } if (judgeOption.SpecialJudgeOption.UseOutputFile) { argsBuilder.Append($" \"{outputFile}\""); } if (judgeOption.SpecialJudgeOption.UseStdInputFile) { argsBuilder.Append($" \"{stdInputFile}\""); } if (judgeOption.SpecialJudgeOption.UseStdOutputFile) { argsBuilder.Append($" \"{stdOutputFile}\""); } using (var judge = new Process { StartInfo = new ProcessStartInfo { FileName = judgeOption.SpecialJudgeOption.Exec, Arguments = argsBuilder.ToString(), ErrorDialog = false, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, StandardOutputEncoding = Encoding.UTF8 } }) { await Task.Delay(100); try { judge.Start(); } catch (Exception ex) { return(ResultCode.Special_Judge_Error, 0, ex.Message); } if (judge.WaitForExit(60 * 1000)) { try { judge.Kill(); } catch { /* ignored */ } } var(error, output) = (await judge.StandardError.ReadToEndAsync(), await judge.StandardOutput.ReadToEndAsync()); if (judge.ExitCode != 0) { return(ResultCode.Special_Judge_Error, 0, null); } try { var percentage = Convert.ToSingle(output.Trim()); return( Math.Abs(percentage - 1f) < 0.001 ? ResultCode.Accepted : ResultCode.Wrong_Answer, percentage, error); } catch { return(ResultCode.Special_Judge_Error, 0, null); } } } StreamReader std = null, act = null; var retryTimes = 0; do { try { std = new StreamReader(new FileStream(stdOutputFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Encoding.UTF8); } catch (Exception ex) { std?.Dispose(); std = null; retryTimes++; if (retryTimes > 100) { return(ResultCode.Unknown_Error, 0, ex.Message); } await Task.Delay(100); } } while (std == null); retryTimes = 0; do { try { act = new StreamReader(new FileStream(outputFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Encoding.UTF8, true); } catch (Exception ex) { act?.Dispose(); act = null; retryTimes++; if (retryTimes > 100) { std?.Dispose(); return(ResultCode.Unknown_Error, 0, ex.Message); } await Task.Delay(100); } } while (act == null); var line = 0; var result = new JudgePoint { ResultType = ResultCode.Accepted }; while (!act.EndOfStream || !std.EndOfStream) { string actline = null, stdline = null; if (!std.EndOfStream) { stdline = std.ReadLine(); } if (!act.EndOfStream) { actline = act.ReadLine(); } line++; if (judgeOption.ComparingOption?.IgnoreLineTailWhiteSpaces ?? true) { if (!string.IsNullOrEmpty(stdline)) { stdline = stdline.TrimEnd(); } if (!string.IsNullOrEmpty(actline)) { actline = actline.TrimEnd(); } } if (judgeOption.ComparingOption?.IgnoreTextTailLineFeeds ?? true) { if (stdline == null) { stdline = string.Empty; } if (actline == null) { actline = string.Empty; } } if (stdline != actline) { if (!isAnswerJudge) { result.ExtraInfo = $"Line {line}: \nexpect: {stdline?.Substring(0, 64 < (stdline?.Length ?? 0) ? 64 : stdline?.Length ?? 0) ?? "<nothing>"}{((stdline?.Length ?? 0) > 64 ? "..." : string.Empty)} \noutput: {actline?.Substring(0, 64 < (actline?.Length ?? 0) ? 64 : actline?.Length ?? 0) ?? "<nothing>"}{((actline?.Length ?? 0) > 64 ? "..." : string.Empty)}"; } if ((stdline?.Replace(" ", string.Empty) ?? string.Empty) == (actline?.Replace(" ", string.Empty) ?? string.Empty)) { result.ResultType = ResultCode.Presentation_Error; } else { result.ResultType = ResultCode.Wrong_Answer; break; } } } std.Dispose(); act.Dispose(); return(result.ResultType, result.ResultType == ResultCode.Accepted ? 1 : 0, result.ExtraInfo); }