레벨 밸런스 1000까지 확장 및 박스 상한 규칙 적용
This commit is contained in:
47
Program.cs
47
Program.cs
@@ -10,6 +10,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text;
|
||||
|
||||
internal static class Program
|
||||
@@ -332,7 +333,8 @@ internal static class Program
|
||||
Id = level.Id,
|
||||
Grid = newGrid,
|
||||
LowestPush = level.LowestPush,
|
||||
PushLimit = level.PushLimit
|
||||
PushLimit = level.PushLimit,
|
||||
MoveCount = level.MoveCount
|
||||
});
|
||||
}
|
||||
|
||||
@@ -395,7 +397,8 @@ internal static class Program
|
||||
PocketMaxRadius = input.PocketMaxRadius,
|
||||
MaskPadMin = padMin,
|
||||
MaskPadMax = padMax,
|
||||
ShapeMasks = masks
|
||||
ShapeMasks = masks,
|
||||
ApplyTransforms = input.ApplyTransforms
|
||||
};
|
||||
}
|
||||
|
||||
@@ -423,6 +426,21 @@ internal static class Program
|
||||
case "large":
|
||||
AddMasks(resolved, seen, MaskLibrary.Large);
|
||||
break;
|
||||
case "tall8":
|
||||
AddMasks(resolved, seen, MaskLibrary.Tall8);
|
||||
break;
|
||||
case "tall9":
|
||||
AddMasks(resolved, seen, MaskLibrary.Tall9);
|
||||
break;
|
||||
case "tall10":
|
||||
AddMasks(resolved, seen, MaskLibrary.Tall10);
|
||||
break;
|
||||
case "tall11":
|
||||
AddMasks(resolved, seen, MaskLibrary.Tall11);
|
||||
break;
|
||||
case "tall12":
|
||||
AddMasks(resolved, seen, MaskLibrary.Tall12);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -477,7 +495,7 @@ internal sealed class LevelGenerator
|
||||
if (band.ShapeMasksExpanded == null || band.ShapeMasksExpanded.Count == 0)
|
||||
{
|
||||
var baseMasks = band.ShapeMasks.Count > 0 ? band.ShapeMasks : MaskLibrary.Microban;
|
||||
band.ShapeMasksExpanded = MaskLibrary.ExpandWithTransforms(baseMasks, band.MaskPadMin, band.MaskPadMax);
|
||||
band.ShapeMasksExpanded = MaskLibrary.ExpandWithTransforms(baseMasks, band.MaskPadMin, band.MaskPadMax, band.ApplyTransforms);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,7 +612,7 @@ internal sealed class LevelGenerator
|
||||
{
|
||||
var failReasons = _trace ? new Dictionary<string, int>() : null;
|
||||
var baseMasks = band.ShapeMasks.Count > 0 ? band.ShapeMasks : MaskLibrary.Microban;
|
||||
band.ShapeMasksExpanded ??= MaskLibrary.ExpandWithTransforms(baseMasks, band.MaskPadMin, band.MaskPadMax);
|
||||
band.ShapeMasksExpanded ??= MaskLibrary.ExpandWithTransforms(baseMasks, band.MaskPadMin, band.MaskPadMax, band.ApplyTransforms);
|
||||
var pockets = ResolvePockets(band);
|
||||
|
||||
var relaxSteps = overrideRelaxSteps ?? _tuning.RelaxationSteps;
|
||||
@@ -627,7 +645,7 @@ internal sealed class LevelGenerator
|
||||
var mask = MaskLibrary.CreateVariant(
|
||||
MaskLibrary.PickRandom(rng, band.ShapeMasksExpanded),
|
||||
rng,
|
||||
_tuning.ApplyMaskTransforms,
|
||||
_tuning.ApplyMaskTransforms && band.ApplyTransforms,
|
||||
_tuning.MaskWallJitter);
|
||||
|
||||
var canvas = LayoutFactory.FromMask(mask, rng, _tuning, pockets);
|
||||
@@ -723,7 +741,8 @@ internal sealed class LevelGenerator
|
||||
Id = id,
|
||||
Grid = lines,
|
||||
LowestPush = solve.Pushes,
|
||||
PushLimit = pushLimit
|
||||
PushLimit = pushLimit,
|
||||
MoveCount = solve.Moves
|
||||
};
|
||||
return true;
|
||||
}
|
||||
@@ -765,6 +784,7 @@ internal sealed class LevelGenerator
|
||||
PocketMaxRadius = src.PocketMaxRadius,
|
||||
MaskPadMin = src.MaskPadMin,
|
||||
MaskPadMax = src.MaskPadMax,
|
||||
ApplyTransforms = src.ApplyTransforms,
|
||||
ShapeMasks = src.ShapeMasks.ToList(),
|
||||
ShapeMasksExpanded = src.ShapeMasksExpanded?.ToList()
|
||||
};
|
||||
@@ -824,7 +844,8 @@ internal sealed class LevelGenerator
|
||||
Id = level.Id,
|
||||
Grid = trimmed,
|
||||
LowestPush = level.LowestPush,
|
||||
PushLimit = level.PushLimit
|
||||
PushLimit = level.PushLimit,
|
||||
MoveCount = level.MoveCount
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -953,10 +974,20 @@ internal static class LevelVerifier
|
||||
|
||||
internal sealed class GeneratedLevel
|
||||
{
|
||||
[JsonPropertyOrder(0)]
|
||||
public int Id { get; init; }
|
||||
|
||||
[JsonPropertyOrder(1)]
|
||||
public List<string> Grid { get; init; } = new();
|
||||
|
||||
[JsonPropertyOrder(2)]
|
||||
public int LowestPush { get; init; }
|
||||
|
||||
[JsonPropertyOrder(3)]
|
||||
public int PushLimit { get; init; }
|
||||
|
||||
[JsonPropertyOrder(4)]
|
||||
public int MoveCount { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class LevelBandConfig
|
||||
@@ -978,6 +1009,7 @@ internal sealed class LevelBandConfig
|
||||
public int PocketMaxRadius { get; set; } = -1;
|
||||
public int MaskPadMin { get; set; } = -1;
|
||||
public int MaskPadMax { get; set; } = 1;
|
||||
public bool ApplyTransforms { get; set; } = true;
|
||||
public List<string[]> ShapeMasks { get; set; } = new();
|
||||
public List<string[]>? ShapeMasksExpanded { get; set; }
|
||||
}
|
||||
@@ -1006,6 +1038,7 @@ internal sealed class LevelBandJson
|
||||
public int PocketMaxRadius { get; set; } = -1;
|
||||
public int MaskPadMin { get; set; } = -1;
|
||||
public int MaskPadMax { get; set; } = 1;
|
||||
public bool ApplyTransforms { get; set; } = true;
|
||||
public List<string> MaskSets { get; set; } = new();
|
||||
public int MaskTake { get; set; } = 0;
|
||||
}
|
||||
|
||||
@@ -48,13 +48,15 @@ dotnet run -- --trim <입력 json> [출력 json] [startId] [endId]
|
||||
"000000000"
|
||||
],
|
||||
"lowestPush": 5,
|
||||
"pushLimit": 7
|
||||
"pushLimit": 7,
|
||||
"moveCount": 9
|
||||
}
|
||||
]
|
||||
```
|
||||
- `grid`: 문자열 배열(행). `0`은 외부 void, `#`는 벽, `.`은 바닥, `G/$/@`는 목표/박스/플레이어.
|
||||
- `lowestPush`: 솔버가 계산한 최소 푸시 수.
|
||||
- `pushLimit`: 최소 푸시에 여유 패딩을 더한 제한 값.
|
||||
- `moveCount`: 솔버가 계산한 최소 이동 수(걷기 + 푸시 포함).
|
||||
|
||||
## 메모
|
||||
- 외벽은 항상 `#`로 둘러지며, `0`은 외부에서만 사용됩니다.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
3664
levelbalance.json
3664
levelbalance.json
File diff suppressed because it is too large
Load Diff
213
mask_library.cs
213
mask_library.cs
@@ -352,13 +352,221 @@ internal static class MaskLibrary
|
||||
}
|
||||
};
|
||||
|
||||
// 세로 확장형 마스크(폭 8~12, 회전 없이 사용)
|
||||
public static readonly List<string[]> Tall8 = new()
|
||||
{
|
||||
new[]{
|
||||
"########",
|
||||
"#......#",
|
||||
"#.####.#",
|
||||
"#.#..#.#",
|
||||
"#.#..#.#",
|
||||
"#.####.#",
|
||||
"#......#",
|
||||
"########"
|
||||
},
|
||||
new[]{
|
||||
"########",
|
||||
"#..##..#",
|
||||
"#..##..#",
|
||||
"#......#",
|
||||
"#.####.#",
|
||||
"#.#..#.#",
|
||||
"#......#",
|
||||
"###..###",
|
||||
"########"
|
||||
},
|
||||
new[]{
|
||||
"########",
|
||||
"#......#",
|
||||
"##.##.##",
|
||||
"#......#",
|
||||
"#.####.#",
|
||||
"#......#",
|
||||
"##.##.##",
|
||||
"#......#",
|
||||
"########"
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly List<string[]> Tall9 = new()
|
||||
{
|
||||
new[]{
|
||||
"#########",
|
||||
"#.......#",
|
||||
"#.#####.#",
|
||||
"#.#...#.#",
|
||||
"#.#.#.#.#",
|
||||
"#.#...#.#",
|
||||
"#.#####.#",
|
||||
"#.......#",
|
||||
"#########"
|
||||
},
|
||||
new[]{
|
||||
"#########",
|
||||
"#...#...#",
|
||||
"#...#...#",
|
||||
"###.#.###",
|
||||
"#.......#",
|
||||
"#.#####.#",
|
||||
"#.#...#.#",
|
||||
"#.#...#.#",
|
||||
"#...#...#",
|
||||
"#########"
|
||||
},
|
||||
new[]{
|
||||
"#########",
|
||||
"#.......#",
|
||||
"#.###.#.#",
|
||||
"#.#.#.#.#",
|
||||
"#.#.#.#.#",
|
||||
"#.#.#.#.#",
|
||||
"#.###.#.#",
|
||||
"#.......#",
|
||||
"#.###.#.#",
|
||||
"#.......#",
|
||||
"#########"
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly List<string[]> Tall10 = new()
|
||||
{
|
||||
new[]{
|
||||
"##########",
|
||||
"#........#",
|
||||
"##.####.##",
|
||||
"#..#..#..#",
|
||||
"#..####..#",
|
||||
"#........#",
|
||||
"###.##.###",
|
||||
"#........#",
|
||||
"##.####.##",
|
||||
"##########"
|
||||
},
|
||||
new[]{
|
||||
"##########",
|
||||
"#...##...#",
|
||||
"#...##...#",
|
||||
"###.##.###",
|
||||
"#........#",
|
||||
"#.######.#",
|
||||
"#.#....#.#",
|
||||
"#.######.#",
|
||||
"#........#",
|
||||
"###.##.###",
|
||||
"#...##...#",
|
||||
"##########"
|
||||
},
|
||||
new[]{
|
||||
"##########",
|
||||
"#........#",
|
||||
"#.######.#",
|
||||
"#.#....#.#",
|
||||
"#.#.##.#.#",
|
||||
"#.#.##.#.#",
|
||||
"#.#....#.#",
|
||||
"#.######.#",
|
||||
"#........#",
|
||||
"###.##.###",
|
||||
"#........#",
|
||||
"#.######.#",
|
||||
"##########"
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly List<string[]> Tall11 = new()
|
||||
{
|
||||
new[]{
|
||||
"###########",
|
||||
"#.........#",
|
||||
"#.#######.#",
|
||||
"#.#.....#.#",
|
||||
"#.#.###.#.#",
|
||||
"#.#.#.#.#.#",
|
||||
"#.#.###.#.#",
|
||||
"#.#.....#.#",
|
||||
"#.#######.#",
|
||||
"#.........#",
|
||||
"###.###.###",
|
||||
"#.........#",
|
||||
"#.#######.#",
|
||||
"#.#.....#.#",
|
||||
"#.#.###.#.#",
|
||||
"###########"
|
||||
},
|
||||
new[]{
|
||||
"###########",
|
||||
"#....#....#",
|
||||
"#....#....#",
|
||||
"###.#.#.###",
|
||||
"#.........#",
|
||||
"#.#######.#",
|
||||
"#.#.....#.#",
|
||||
"#.#.###.#.#",
|
||||
"#.#.#.#.#.#",
|
||||
"#.#.###.#.#",
|
||||
"#.#.....#.#",
|
||||
"#.#######.#",
|
||||
"#.........#",
|
||||
"###.#.#.###",
|
||||
"#....#....#",
|
||||
"#....#....#",
|
||||
"###.....###",
|
||||
"###########"
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly List<string[]> Tall12 = new()
|
||||
{
|
||||
new[]{
|
||||
"############",
|
||||
"#..........#",
|
||||
"#.########.#",
|
||||
"#.#......#.#",
|
||||
"#.#.####.#.#",
|
||||
"#.#.#..#.#.#",
|
||||
"#.#.#..#.#.#",
|
||||
"#.#.####.#.#",
|
||||
"#.#......#.#",
|
||||
"#.########.#",
|
||||
"#..........#",
|
||||
"###.####.###",
|
||||
"#..........#",
|
||||
"#.########.#",
|
||||
"#.#......#.#",
|
||||
"#.#.####.#.#",
|
||||
"#..........#",
|
||||
"############"
|
||||
},
|
||||
new[]{
|
||||
"############",
|
||||
"#....##....#",
|
||||
"#....##....#",
|
||||
"###.####.###",
|
||||
"#..........#",
|
||||
"#.########.#",
|
||||
"#.#......#.#",
|
||||
"#.#.####.#.#",
|
||||
"#.#.#..#.#.#",
|
||||
"#.#.####.#.#",
|
||||
"#.#......#.#",
|
||||
"#.########.#",
|
||||
"#..........#",
|
||||
"###.####.###",
|
||||
"#....##....#",
|
||||
"#....##....#",
|
||||
"###......###",
|
||||
"############"
|
||||
}
|
||||
};
|
||||
|
||||
public static string[] PickRandom(Random rng, List<string[]> masks)
|
||||
{
|
||||
if (masks.Count == 0) throw new System.InvalidOperationException("No masks provided.");
|
||||
return masks[rng.Next(masks.Count)];
|
||||
}
|
||||
|
||||
public static List<string[]> ExpandWithTransforms(IEnumerable<string[]> baseMasks, int padMin = -1, int padMax = 1)
|
||||
public static List<string[]> ExpandWithTransforms(IEnumerable<string[]> baseMasks, int padMin = -1, int padMax = 1, bool allowTransforms = true)
|
||||
{
|
||||
var seen = new HashSet<string>();
|
||||
var output = new List<string[]>();
|
||||
@@ -367,7 +575,8 @@ internal static class MaskLibrary
|
||||
var seeds = ScaleVariants(mask, padMin, padMax);
|
||||
foreach (var seed in seeds)
|
||||
{
|
||||
foreach (var variant in Variants(seed))
|
||||
var variants = allowTransforms ? Variants(seed) : new[] { seed };
|
||||
foreach (var variant in variants)
|
||||
{
|
||||
var key = CanonicalKey(variant);
|
||||
if (seen.Add(key))
|
||||
|
||||
@@ -1 +1 @@
|
||||
7886d3225e6b0aa6234a1c3da555a0a6b7dccd0b
|
||||
c99ea306e8f2077bc6d6b8c28e9c7dd567fe8bf1
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user