1
0
Fork 0
AdventOfCode/src/core/SymbolGrid.cs

127 lines
2.4 KiB
C#
Raw Normal View History

2024-12-25 02:03:42 +01:00
using System.Diagnostics.CodeAnalysis;
using System.IO.MemoryMappedFiles;
using System.Numerics;
namespace core;
public class SymbolGrid
{
private char[]? _map;
[DisallowNull]
public char? this[int x, int y]
{
get
{
EnsureMap();
return IsValid(x, y) ? _map[y * Height + x] : null;
}
set
{
EnsureMap();
if (IsValid(x, y))
{
_map[y * Height + x] = value.Value;
}
}
}
[DisallowNull]
public char? this[Vector2I i]
{
get => this[i.X, i.Y];
set => this[i.X, i.Y] = value;
}
public int Width { get; private set; }
public int Height { get; private set; }
public void Load(string file)
{
using var fileView = MemoryMappedFile.CreateFromFile(file, FileMode.Open);
using var accessor = fileView.CreateViewAccessor();
(Width, var stride, Height) = Parse(accessor);
_map = new char[Width * Height];
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
_map[y * Height + x] = (char)accessor.ReadByte(y * stride + x);
}
}
static (int Width, int Stride, int Height) Parse(MemoryMappedViewAccessor map)
{
bool hasNewLine = false;
int? width = null, stride = null;
for (int i = 0; i < map.Capacity; i++)
{
if ((char)map.ReadByte(i) is '\r' or '\n')
{
if (!hasNewLine)
{
width = i;
}
hasNewLine = true;
}
else if (hasNewLine)
{
stride = i;
break;
}
}
return (width!.Value, stride!.Value, (int)((map.Capacity + width.Value) / stride.Value));
}
}
public SymbolEnumerator GetEnumerator() => new(this);
public bool IsValid(Vector2I p) => IsValid(p.X, p.Y);
public bool IsValid(int x, int y)
{
EnsureMap();
return x >= 0 && x < Width && y >= 0 && y < Height;
}
public ref struct SymbolEnumerator
{
private readonly SymbolGrid _grid;
private readonly int _length;
private int? _current;
internal SymbolEnumerator(SymbolGrid grid)
{
_grid = grid;
_length = grid.Width * grid.Height;
}
public (Vector2I Position, char Symbol) Current { get; private set; }
public bool MoveNext()
{
_current = _current.HasValue ? _current + 1 : 0;
if (_current == _length)
{
return false;
}
Current = (_grid.ToVector(_current.Value), _grid._map![_current.Value]);
return true;
}
}
private Vector2I ToVector(int index) => new()
{
Y = index / Height,
X = index % Height
};
[MemberNotNull(nameof(_map))]
private void EnsureMap() => ArgumentNullException.ThrowIfNull(_map);
}