update polygon package parsing & testing
This commit is contained in:
181
src/LiquidCode.Tester.Worker/Services/AnswerGenerationService.cs
Normal file
181
src/LiquidCode.Tester.Worker/Services/AnswerGenerationService.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
namespace LiquidCode.Tester.Worker.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for generating answer files by running the main solution from Polygon package
|
||||
/// </summary>
|
||||
public class AnswerGenerationService
|
||||
{
|
||||
private readonly ICompilationServiceFactory _compilationFactory;
|
||||
private readonly IExecutionServiceFactory _executionFactory;
|
||||
private readonly ILogger<AnswerGenerationService> _logger;
|
||||
|
||||
public AnswerGenerationService(
|
||||
ICompilationServiceFactory compilationFactory,
|
||||
IExecutionServiceFactory executionFactory,
|
||||
ILogger<AnswerGenerationService> logger)
|
||||
{
|
||||
_compilationFactory = compilationFactory;
|
||||
_executionFactory = executionFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<bool> GenerateAnswersAsync(
|
||||
PolygonProblemDescriptor descriptor,
|
||||
string workingDirectory,
|
||||
List<string> inputFilePaths,
|
||||
List<string> answerFilePaths)
|
||||
{
|
||||
if (string.IsNullOrEmpty(descriptor.MainSolutionPath))
|
||||
{
|
||||
_logger.LogWarning("No main solution specified, cannot generate answers");
|
||||
return false;
|
||||
}
|
||||
|
||||
var solutionPath = Path.Combine(workingDirectory, descriptor.MainSolutionPath);
|
||||
if (!File.Exists(solutionPath))
|
||||
{
|
||||
_logger.LogWarning("Main solution file not found: {Path}", solutionPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine language and version from solution type
|
||||
var (language, version) = ParseSolutionType(descriptor.MainSolutionType ?? "");
|
||||
if (language == null)
|
||||
{
|
||||
_logger.LogWarning("Unsupported solution type: {Type}", descriptor.MainSolutionType);
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Generating answers using {Language} {Version} solution: {Path}",
|
||||
language, version, descriptor.MainSolutionPath);
|
||||
|
||||
try
|
||||
{
|
||||
// Read solution source code
|
||||
var sourceCode = await File.ReadAllTextAsync(solutionPath);
|
||||
|
||||
// Get compilation service
|
||||
var compilationService = _compilationFactory.GetCompilationService(language);
|
||||
var executionService = _executionFactory.GetExecutionService(language);
|
||||
|
||||
// Compile solution
|
||||
_logger.LogInformation("Compiling main solution...");
|
||||
var compilationResult = await compilationService.CompileAsync(
|
||||
sourceCode,
|
||||
Path.GetDirectoryName(solutionPath)!,
|
||||
version);
|
||||
|
||||
if (!compilationResult.Success)
|
||||
{
|
||||
_logger.LogError("Failed to compile main solution: {Error}", compilationResult.CompilerOutput);
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Main solution compiled successfully");
|
||||
|
||||
// Generate answers for each test
|
||||
int generatedCount = 0;
|
||||
for (int i = 0; i < inputFilePaths.Count; i++)
|
||||
{
|
||||
var inputPath = inputFilePaths[i];
|
||||
var answerPath = answerFilePaths[i];
|
||||
|
||||
if (!File.Exists(inputPath))
|
||||
{
|
||||
_logger.LogWarning("Input file not found: {Path}", inputPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Generating answer {Index}/{Total}: {AnswerPath}",
|
||||
i + 1, inputFilePaths.Count, answerPath);
|
||||
|
||||
// Execute solution with input
|
||||
var executionResult = await executionService.ExecuteAsync(
|
||||
compilationResult.ExecutablePath!,
|
||||
inputPath,
|
||||
descriptor.TimeLimitMs * 2, // Give extra time for answer generation
|
||||
descriptor.MemoryLimitMb * 2);
|
||||
|
||||
if (!executionResult.Success || executionResult.RuntimeError)
|
||||
{
|
||||
_logger.LogWarning("Failed to generate answer for {InputPath}: {Error}",
|
||||
inputPath, executionResult.ErrorMessage);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Save output as answer file
|
||||
var answerDir = Path.GetDirectoryName(answerPath);
|
||||
if (!string.IsNullOrEmpty(answerDir) && !Directory.Exists(answerDir))
|
||||
{
|
||||
Directory.CreateDirectory(answerDir);
|
||||
}
|
||||
|
||||
await File.WriteAllTextAsync(answerPath, executionResult.Output);
|
||||
generatedCount++;
|
||||
|
||||
_logger.LogDebug("Generated answer {Index}/{Total}", i + 1, inputFilePaths.Count);
|
||||
}
|
||||
|
||||
_logger.LogInformation("Generated {Count} answer files out of {Total} tests",
|
||||
generatedCount, inputFilePaths.Count);
|
||||
|
||||
return generatedCount > 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error generating answers");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private (string? language, string version) ParseSolutionType(string solutionType)
|
||||
{
|
||||
// Polygon solution types: python.2, python.3, cpp.g++17, cpp.g++20, java7, java8, etc.
|
||||
if (string.IsNullOrEmpty(solutionType))
|
||||
{
|
||||
return (null, "");
|
||||
}
|
||||
|
||||
if (solutionType.StartsWith("python."))
|
||||
{
|
||||
var parts = solutionType.Split('.');
|
||||
var version = parts.Length > 1 ? parts[1] : "3";
|
||||
return ("python", $"3.{version}"); // Map python.3 -> 3.3, python.2 -> 3.2 (approx)
|
||||
}
|
||||
|
||||
if (solutionType.StartsWith("cpp."))
|
||||
{
|
||||
// cpp.g++17, cpp.g++20, cpp.g++14
|
||||
if (solutionType.Contains("++20"))
|
||||
return ("cpp", "20");
|
||||
if (solutionType.Contains("++17"))
|
||||
return ("cpp", "17");
|
||||
if (solutionType.Contains("++14"))
|
||||
return ("cpp", "14");
|
||||
return ("cpp", "17"); // Default to C++17
|
||||
}
|
||||
|
||||
if (solutionType.StartsWith("java"))
|
||||
{
|
||||
// java7, java8, java11
|
||||
if (solutionType.Contains("11"))
|
||||
return ("java", "11");
|
||||
if (solutionType.Contains("8"))
|
||||
return ("java", "8");
|
||||
return ("java", "11"); // Default to Java 11
|
||||
}
|
||||
|
||||
if (solutionType.StartsWith("csharp"))
|
||||
{
|
||||
return ("csharp", "9"); // Default to C# 9
|
||||
}
|
||||
|
||||
if (solutionType.StartsWith("kotlin"))
|
||||
{
|
||||
return ("kotlin", "1.9"); // Default to Kotlin 1.9
|
||||
}
|
||||
|
||||
_logger.LogWarning("Unknown solution type: {Type}", solutionType);
|
||||
return (null, "");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user