신규등록
This commit is contained in:
414
mask_library.cs
Normal file
414
mask_library.cs
Normal file
@@ -0,0 +1,414 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
internal static class MaskLibrary
|
||||
{
|
||||
public static readonly List<string[]> Microban = new()
|
||||
{
|
||||
new[]{
|
||||
"0000000",
|
||||
"0####00",
|
||||
"0#..#00",
|
||||
"0#..###",
|
||||
"0#....#",
|
||||
"0######",
|
||||
"0000000"
|
||||
},
|
||||
new[]{
|
||||
"0000000",
|
||||
"0#####0",
|
||||
"0#...#0",
|
||||
"0#.#.#0",
|
||||
"0#...#0",
|
||||
"0#####0",
|
||||
"0000000"
|
||||
},
|
||||
new[]{
|
||||
"0000000",
|
||||
"0###000",
|
||||
"0#.#000",
|
||||
"0#.#000",
|
||||
"0#..###",
|
||||
"0#####0",
|
||||
"0000000"
|
||||
},
|
||||
new[]{
|
||||
"00000000",
|
||||
"0######0",
|
||||
"0#....#0",
|
||||
"0###..#0",
|
||||
"000#..#0",
|
||||
"000####0",
|
||||
"00000000"
|
||||
},
|
||||
new[]{
|
||||
"000000000",
|
||||
"00#####00",
|
||||
"00#...#00",
|
||||
"0##...##0",
|
||||
"0#.....#0",
|
||||
"0##...##0",
|
||||
"00#...#00",
|
||||
"00#####00",
|
||||
"000000000"
|
||||
},
|
||||
new[]{
|
||||
"00000000",
|
||||
"0######0",
|
||||
"0#....#0",
|
||||
"0#.####0",
|
||||
"0#....#0",
|
||||
"0###..#0",
|
||||
"000####0",
|
||||
"00000000"
|
||||
},
|
||||
new[]{
|
||||
"00000000",
|
||||
"0####000",
|
||||
"0#..###0",
|
||||
"0#....#0",
|
||||
"0###..#0",
|
||||
"000####0",
|
||||
"00000000"
|
||||
},
|
||||
new[]{
|
||||
"0000000",
|
||||
"0#####0",
|
||||
"0#...#0",
|
||||
"0#...#0",
|
||||
"0#...#0",
|
||||
"0###.#0",
|
||||
"000###0",
|
||||
"0000000"
|
||||
},
|
||||
new[]{
|
||||
"0000000",
|
||||
"0####00",
|
||||
"0#..#00",
|
||||
"0#..###",
|
||||
"0#..#.#",
|
||||
"0####.#",
|
||||
"000000#"
|
||||
},
|
||||
new[]{
|
||||
"00000000",
|
||||
"0######0",
|
||||
"0#....#0",
|
||||
"0#.#..#0",
|
||||
"0#.#.##0",
|
||||
"0#...#00",
|
||||
"0#####00",
|
||||
"00000000"
|
||||
},
|
||||
new[]{
|
||||
"0000000",
|
||||
"0#####0",
|
||||
"0#...#0",
|
||||
"###.###",
|
||||
"0#...#0",
|
||||
"0#...#0",
|
||||
"0#####0"
|
||||
},
|
||||
new[]{
|
||||
"0000000000",
|
||||
"00#######0",
|
||||
"00#.....#0",
|
||||
"###.###.#0",
|
||||
"0#.....#0",
|
||||
"0#######0",
|
||||
"000000000"
|
||||
},
|
||||
// 13: 얇은 리본형
|
||||
new[]{
|
||||
"000000000",
|
||||
"00####000",
|
||||
"00#..###0",
|
||||
"0##..#.#0",
|
||||
"0#....#00",
|
||||
"0##..###0",
|
||||
"00####000",
|
||||
"000000000"
|
||||
},
|
||||
// 14: 작은 도넛
|
||||
new[]{
|
||||
"0000000",
|
||||
"0#####0",
|
||||
"0#...#0",
|
||||
"0#.#.#0",
|
||||
"0#...#0",
|
||||
"0#####0",
|
||||
"0000000"
|
||||
},
|
||||
// 15: ㄴ자 계단형
|
||||
new[]{
|
||||
"00000000",
|
||||
"0####000",
|
||||
"0#..###0",
|
||||
"0#....#0",
|
||||
"0###..#0",
|
||||
"000#..#0",
|
||||
"000####0",
|
||||
"00000000"
|
||||
},
|
||||
// 16: 짧은 S자 복도
|
||||
new[]{
|
||||
"00000000",
|
||||
"0####000",
|
||||
"0#..###0",
|
||||
"0#..#.#0",
|
||||
"0###..#0",
|
||||
"000####0",
|
||||
"00000000"
|
||||
},
|
||||
// 17: 코너 방 + 넓은 홀
|
||||
new[]{
|
||||
"000000000",
|
||||
"00#####00",
|
||||
"00#...#00",
|
||||
"0##.#.##0",
|
||||
"0#.....#0",
|
||||
"0#.#.###0",
|
||||
"0#...#000",
|
||||
"0#####000",
|
||||
"000000000"
|
||||
},
|
||||
// 18: 긴 복도 + 옆 포켓
|
||||
new[]{
|
||||
"0000000000",
|
||||
"00#######0",
|
||||
"00#.....#0",
|
||||
"0##.###.#0",
|
||||
"0#.....#00",
|
||||
"0#.#.###00",
|
||||
"0#.....#00",
|
||||
"0#######00",
|
||||
"0000000000"
|
||||
},
|
||||
// 19: 두꺼운 U자 (안쪽 공간 넓음)
|
||||
new[]{
|
||||
"000000000",
|
||||
"0#######0",
|
||||
"0#.....#0",
|
||||
"0#.....#0",
|
||||
"0#.....#0",
|
||||
"0#..#..#0",
|
||||
"0######0",
|
||||
"000000000"
|
||||
},
|
||||
// 20: 작은 십자 변형 (팔 길이 짧음)
|
||||
new[]{
|
||||
"000000000",
|
||||
"00###0000",
|
||||
"00#.#0000",
|
||||
"0###.###0",
|
||||
"0#.....#0",
|
||||
"0###.###0",
|
||||
"000#.#000",
|
||||
"000###000",
|
||||
"000000000"
|
||||
}
|
||||
};
|
||||
|
||||
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, bool includeScaled = true)
|
||||
{
|
||||
var seen = new HashSet<string>();
|
||||
var output = new List<string[]>();
|
||||
foreach (var mask in baseMasks)
|
||||
{
|
||||
var seeds = includeScaled ? ScaleVariants(mask) : new List<string[]> { mask };
|
||||
foreach (var seed in seeds)
|
||||
{
|
||||
foreach (var variant in Variants(seed))
|
||||
{
|
||||
var key = CanonicalKey(variant);
|
||||
if (seen.Add(key))
|
||||
{
|
||||
output.Add(variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public static string[] CreateVariant(string[] mask, Random rng, bool applyTransforms, int wallJitter)
|
||||
{
|
||||
var candidates = applyTransforms ? Variants(mask).ToList() : new List<string[]> { mask };
|
||||
var picked = candidates[rng.Next(candidates.Count)];
|
||||
|
||||
if (wallJitter <= 0)
|
||||
{
|
||||
return picked;
|
||||
}
|
||||
|
||||
var jittered = ApplyWallJitter(picked, rng, wallJitter);
|
||||
return jittered;
|
||||
}
|
||||
|
||||
private static IEnumerable<string[]> Variants(string[] mask)
|
||||
{
|
||||
var current = mask;
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
yield return current;
|
||||
yield return FlipHorizontal(current);
|
||||
current = Rotate90(current);
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] Rotate90(string[] mask)
|
||||
{
|
||||
var h = mask.Length;
|
||||
var w = mask.Max(r => r.Length);
|
||||
var arr = new char[h, w];
|
||||
for (var y = 0; y < h; y++)
|
||||
{
|
||||
var row = mask[y];
|
||||
for (var x = 0; x < w; x++)
|
||||
{
|
||||
arr[y, x] = x < row.Length ? row[x] : '0';
|
||||
}
|
||||
}
|
||||
|
||||
var rotated = new string[w];
|
||||
for (var y = 0; y < w; y++)
|
||||
{
|
||||
var chars = new char[h];
|
||||
for (var x = 0; x < h; x++)
|
||||
{
|
||||
chars[x] = arr[h - 1 - x, y];
|
||||
}
|
||||
rotated[y] = new string(chars);
|
||||
}
|
||||
return rotated;
|
||||
}
|
||||
|
||||
private static string[] FlipHorizontal(string[] mask)
|
||||
{
|
||||
return mask.Select(row => new string(row.Reverse().ToArray())).ToArray();
|
||||
}
|
||||
|
||||
private static string CanonicalKey(string[] mask) => string.Join("\n", mask);
|
||||
|
||||
private static string[] ApplyWallJitter(string[] mask, Random rng, int maxJitter)
|
||||
{
|
||||
var h = mask.Length;
|
||||
var w = mask.Max(r => r.Length);
|
||||
var grid = mask.Select(line => line.PadRight(w, '0').ToCharArray()).ToArray();
|
||||
|
||||
int changes = rng.Next(1, maxJitter + 1);
|
||||
var candidates = new List<(int x, int y)>();
|
||||
for (var y = 0; y < h; y++)
|
||||
{
|
||||
for (var x = 0; x < w; x++)
|
||||
{
|
||||
var c = grid[y][x];
|
||||
if (c == '0') continue;
|
||||
// avoid outermost void border flipping into wall
|
||||
if (y == 0 || x == 0 || y == h - 1 || x == w - 1) continue;
|
||||
candidates.Add((x, y));
|
||||
}
|
||||
}
|
||||
|
||||
Shuffle(candidates, rng);
|
||||
var applied = 0;
|
||||
foreach (var (x, y) in candidates)
|
||||
{
|
||||
if (applied >= changes) break;
|
||||
var c = grid[y][x];
|
||||
if (c == '#') grid[y][x] = '.';
|
||||
else if (c == '.') grid[y][x] = '#';
|
||||
else continue;
|
||||
applied++;
|
||||
}
|
||||
|
||||
var result = new string[h];
|
||||
for (var y = 0; y < h; y++)
|
||||
{
|
||||
result[y] = new string(grid[y]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void Shuffle<T>(IList<T> list, Random rng)
|
||||
{
|
||||
for (var i = list.Count - 1; i > 0; i--)
|
||||
{
|
||||
var j = rng.Next(i + 1);
|
||||
(list[i], list[j]) = (list[j], list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<string[]> ScaleVariants(string[] mask)
|
||||
{
|
||||
var scaled = new List<string[]>();
|
||||
scaled.Add(mask);
|
||||
var padded = Pad(mask, 1);
|
||||
if (padded != null) scaled.Add(padded);
|
||||
var trimmed = Pad(mask, -1);
|
||||
if (trimmed != null) scaled.Add(trimmed);
|
||||
return scaled;
|
||||
}
|
||||
|
||||
private static string[]? Pad(string[] mask, int delta)
|
||||
{
|
||||
if (delta == 0) return mask;
|
||||
var h = mask.Length;
|
||||
var w = mask.Max(r => r.Length);
|
||||
if (delta < 0)
|
||||
{
|
||||
if (h + delta * 2 < 3 || w + delta * 2 < 3) return null;
|
||||
var newH = h + delta * 2;
|
||||
var newW = w + delta * 2;
|
||||
var output = new string[newH];
|
||||
for (var y = 0; y < newH; y++)
|
||||
{
|
||||
var srcY = y - delta;
|
||||
if (srcY < 0 || srcY >= h)
|
||||
{
|
||||
output[y] = new string('0', newW);
|
||||
continue;
|
||||
}
|
||||
var row = mask[srcY];
|
||||
var src = row.Skip(delta).Take(newW).ToArray();
|
||||
if (src.Length < newW)
|
||||
{
|
||||
output[y] = new string(src).PadRight(newW, '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
output[y] = new string(src);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newH = h + delta * 2;
|
||||
var newW = w + delta * 2;
|
||||
var output = new string[newH];
|
||||
for (var y = 0; y < newH; y++)
|
||||
{
|
||||
if (y < delta || y >= h + delta)
|
||||
{
|
||||
output[y] = new string('0', newW);
|
||||
continue;
|
||||
}
|
||||
var row = mask[y - delta];
|
||||
var paddedRow = new string('0', delta) + row.PadRight(w, '0') + new string('0', delta);
|
||||
if (paddedRow.Length < newW)
|
||||
{
|
||||
paddedRow = paddedRow.PadRight(newW, '0');
|
||||
}
|
||||
output[y] = paddedRow;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user