using System.Xml.Linq; using LiquidCode.Tester.Common.Models; namespace LiquidCode.Tester.Worker.Services; /// /// Parser for Polygon problem.xml format /// public class PolygonProblemXmlParser { private readonly ILogger _logger; public PolygonProblemXmlParser(ILogger 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; } } } /// /// Descriptor parsed from problem.xml /// 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; } }