그리드 트리밍/검증 리포트 확장

This commit is contained in:
JiWoong Sul
2025-11-24 17:47:45 +09:00
parent 629b74888c
commit 71955f3367

View File

@@ -101,6 +101,28 @@ internal static class Program
public static void Main(string[] args)
{
// 트리밍 모드: --trim <입력 json> [출력 json] [startId] [endId]
if (args.Length > 0 && args[0].Equals("--trim", StringComparison.OrdinalIgnoreCase))
{
if (args.Length < 2)
{
Console.Error.WriteLine("사용법: dotnet run -- --trim <입력 json> [출력 json] [startId] [endId]");
return;
}
var input = args[1];
var output = args.Length >= 3 && !args[2].StartsWith("--", StringComparison.Ordinal) ? args[2] : "trimmed_output.json";
var argOffset = args.Length >= 3 && !args[2].StartsWith("--", StringComparison.Ordinal) ? 3 : 2;
int? trimStart = null;
int? trimEnd = null;
if (args.Length > argOffset && int.TryParse(args[argOffset], out var ts)) trimStart = ts;
if (args.Length > argOffset + 1 && int.TryParse(args[argOffset + 1], out var te)) trimEnd = te;
RunTrim(input, output, trimStart, trimEnd);
return;
}
// 검증 모드: --verify <json 경로> <id> [endId]
if (args.Length > 0 && args[0].Equals("--verify", StringComparison.OrdinalIgnoreCase))
{
@@ -261,6 +283,71 @@ internal static class Program
Console.WriteLine($"[verify] 완료: 성공 {ok}/{targets.Count} (범위 {startId}~{endId})");
}
private static void RunTrim(string inputPath, string outputPath, int? startId, int? endId)
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
WriteIndented = true
};
if (!File.Exists(inputPath))
{
Console.Error.WriteLine($"파일을 찾을 수 없습니다: {inputPath}");
return;
}
List<GeneratedLevel>? levels;
try
{
var json = File.ReadAllText(inputPath);
levels = JsonSerializer.Deserialize<List<GeneratedLevel>>(json, options);
}
catch (Exception ex)
{
Console.Error.WriteLine($"[trim] JSON을 읽거나 파싱하는 데 실패했습니다: {ex.Message}");
return;
}
if (levels == null || levels.Count == 0)
{
Console.Error.WriteLine("[trim] 레벨 데이터가 없습니다.");
return;
}
if (startId.HasValue && endId.HasValue && startId > endId)
{
(startId, endId) = (endId, startId);
}
var trimmed = new List<GeneratedLevel>(levels.Count);
foreach (var level in levels)
{
if (startId.HasValue && level.Id < startId.Value) { trimmed.Add(level); continue; }
if (endId.HasValue && level.Id > endId.Value) { trimmed.Add(level); continue; }
var newGrid = LevelTrimmer.Trim(level.Grid);
trimmed.Add(new GeneratedLevel
{
Id = level.Id,
Grid = newGrid,
LowestPush = level.LowestPush,
PushLimit = level.PushLimit
});
}
try
{
var json = JsonSerializer.Serialize(trimmed, options);
File.WriteAllText(outputPath, json);
Console.WriteLine($"[trim] 완료: {inputPath} -> {outputPath} (대상 id {startId ?? int.MinValue}~{endId ?? int.MaxValue})");
}
catch (Exception ex)
{
Console.Error.WriteLine($"[trim] 저장 실패: {ex.Message}");
}
}
private static LevelBandConfig[] LoadLevelBands()
{
try
@@ -745,6 +832,39 @@ internal sealed class LevelGenerator
}
}
internal static class LevelTrimmer
{
// 그리드에서 바깥쪽이 전부 '0'인 행/열을 제거해 압축한다.
public static List<string> Trim(List<string> grid)
{
if (grid.Count == 0) return grid;
var height = grid.Count;
var width = grid.Max(r => r.Length);
if (width == 0) return grid;
// 패딩하여 동일 너비로 정규화
var padded = grid.Select(r => r.PadRight(width, '0')).ToArray();
// 유지할 행/열 계산
var keepRows = Enumerable.Range(0, height)
.Where(y => padded[y].Any(ch => ch != '0'))
.ToList();
var keepCols = Enumerable.Range(0, width)
.Where(x => padded.Any(row => row[x] != '0'))
.ToList();
if (keepRows.Count == 0 || keepCols.Count == 0) return grid;
var trimmed = new List<string>(keepRows.Count);
foreach (var y in keepRows)
{
var chars = keepCols.Select(x => padded[y][x]).ToArray();
trimmed.Add(new string(chars));
}
return trimmed;
}
}
internal readonly record struct ParsedLevel(Board Board, int Player, int[] Boxes, HashSet<int> Goals);
internal readonly record struct VerificationResult(int Id, bool Success, int? Moves, int? Pushes, int? Turns, string? ErrorMessage);