diff --git a/Program.cs b/Program.cs index 8128f0d..62ea7e6 100644 --- a/Program.cs +++ b/Program.cs @@ -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 [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? levels; + try + { + var json = File.ReadAllText(inputPath); + levels = JsonSerializer.Deserialize>(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(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 Trim(List 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(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 Goals); internal readonly record struct VerificationResult(int Id, bool Success, int? Moves, int? Pushes, int? Turns, string? ErrorMessage);