Files
LiquidCode.Tester/src/LiquidCode.Tester.Worker/Services/PolygonProblemXmlParser.cs
2025-10-28 22:01:35 +04:00

142 lines
5.2 KiB
C#

using System.Xml.Linq;
using LiquidCode.Tester.Common.Models;
namespace LiquidCode.Tester.Worker.Services;
/// <summary>
/// Parser for Polygon problem.xml format
/// </summary>
public class PolygonProblemXmlParser
{
private readonly ILogger<PolygonProblemXmlParser> _logger;
public PolygonProblemXmlParser(ILogger<PolygonProblemXmlParser> logger)
{
_logger = logger;
}
public PolygonProblemDescriptor? ParseProblemXml(string xmlPath)
{
try
{
var doc = XDocument.Load(xmlPath);
var problem = doc.Element("problem");
if (problem == null)
{
_logger.LogWarning("Invalid problem.xml: root 'problem' element not found");
return null;
}
var judging = problem.Element("judging");
if (judging == null)
{
_logger.LogWarning("No 'judging' section found in problem.xml");
return null;
}
var testset = judging.Element("testset");
if (testset == null)
{
_logger.LogWarning("No 'testset' section found in problem.xml");
return null;
}
var descriptor = new PolygonProblemDescriptor
{
ShortName = problem.Attribute("short-name")?.Value ?? "unknown",
Revision = int.TryParse(problem.Attribute("revision")?.Value, out var rev) ? rev : 0
};
// Parse time limit (in milliseconds)
var timeLimitText = testset.Element("time-limit")?.Value;
if (int.TryParse(timeLimitText, out var timeLimit))
{
descriptor.TimeLimitMs = timeLimit;
}
// Parse memory limit (in bytes)
var memoryLimitText = testset.Element("memory-limit")?.Value;
if (long.TryParse(memoryLimitText, out var memoryLimit))
{
descriptor.MemoryLimitMb = (int)(memoryLimit / (1024 * 1024)); // Convert bytes to MB
}
// Parse test count
var testCountText = testset.Element("test-count")?.Value;
if (int.TryParse(testCountText, out var testCount))
{
descriptor.TestCount = testCount;
}
// Parse path patterns
descriptor.InputPathPattern = testset.Element("input-path-pattern")?.Value ?? "tests/%02d";
descriptor.AnswerPathPattern = testset.Element("answer-path-pattern")?.Value ?? "tests/%02d.a";
// Parse solutions to find main solution
var assets = problem.Element("assets");
if (assets != null)
{
var solutions = assets.Element("solutions");
if (solutions != null)
{
// Try to find main solution first
var mainSolution = solutions.Elements("solution")
.FirstOrDefault(s => s.Attribute("tag")?.Value == "main");
// If no main solution, try to find any accepted solution
if (mainSolution == null)
{
mainSolution = solutions.Elements("solution")
.FirstOrDefault(s => s.Attribute("tag")?.Value == "accepted");
}
if (mainSolution != null)
{
var source = mainSolution.Element("source");
if (source != null)
{
descriptor.MainSolutionPath = source.Attribute("path")?.Value;
descriptor.MainSolutionType = source.Attribute("type")?.Value;
_logger.LogInformation("Found main solution: {Path} (type: {Type})",
descriptor.MainSolutionPath, descriptor.MainSolutionType);
}
}
else
{
_logger.LogWarning("No main or accepted solution found in problem.xml");
}
}
}
_logger.LogInformation(
"Parsed problem.xml: {ShortName} (rev {Revision}), {TestCount} tests, TL={TimeLimit}ms, ML={MemoryLimit}MB",
descriptor.ShortName, descriptor.Revision, descriptor.TestCount, descriptor.TimeLimitMs, descriptor.MemoryLimitMb);
return descriptor;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to parse problem.xml at {Path}", xmlPath);
return null;
}
}
}
/// <summary>
/// Descriptor parsed from problem.xml
/// </summary>
public class PolygonProblemDescriptor
{
public string ShortName { get; set; } = string.Empty;
public int Revision { get; set; }
public int TimeLimitMs { get; set; } = 2000;
public int MemoryLimitMb { get; set; } = 256;
public int TestCount { get; set; }
public string InputPathPattern { get; set; } = "tests/%02d";
public string AnswerPathPattern { get; set; } = "tests/%02d.a";
public string? MainSolutionPath { get; set; }
public string? MainSolutionType { get; set; }
}