Add Day 6
This commit is contained in:
parent
0111ee1176
commit
367cd56a42
9 changed files with 345 additions and 0 deletions
11
.vscode/launch.json
vendored
11
.vscode/launch.json
vendored
|
|
@ -58,6 +58,17 @@
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
"console": "internalConsole"
|
"console": "internalConsole"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aoc-2024-06 Debug",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "dotnet: build aoc-2024-06",
|
||||||
|
"program": "${workspaceFolder}/artifacts/bin/aoc-2024-06/debug/aoc-2024-06.dll",
|
||||||
|
"args": ["part-two", "data/2024/06/dev.txt"],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"console": "internalConsole"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
8
.vscode/tasks.json
vendored
8
.vscode/tasks.json
vendored
|
|
@ -42,6 +42,14 @@
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"problemMatcher": [],
|
"problemMatcher": [],
|
||||||
"label": "dotnet: build aoc-2024-05"
|
"label": "dotnet: build aoc-2024-05"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "dotnet",
|
||||||
|
"task": "build aoc-2024-06.csproj",
|
||||||
|
"file": "src/2024/06/aoc-2024-06.csproj",
|
||||||
|
"group": "build",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"label": "dotnet: build aoc-2024-06"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aoc-2024-04", "src\2024\04\
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aoc-2024-05", "src\2024\05\aoc-2024-05.csproj", "{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aoc-2024-05", "src\2024\05\aoc-2024-05.csproj", "{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aoc-2024-06", "src\2024\06\aoc-2024-06.csproj", "{EEC6EF36-EE16-4DB4-9AE8-CF0234751458}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -44,6 +46,10 @@ Global
|
||||||
{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}.Release|Any CPU.Build.0 = Release|Any CPU
|
{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EEC6EF36-EE16-4DB4-9AE8-CF0234751458}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EEC6EF36-EE16-4DB4-9AE8-CF0234751458}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EEC6EF36-EE16-4DB4-9AE8-CF0234751458}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EEC6EF36-EE16-4DB4-9AE8-CF0234751458}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{FAF70800-E2C3-4AD7-B433-86C1F20380F7} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
|
{FAF70800-E2C3-4AD7-B433-86C1F20380F7} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
|
||||||
|
|
@ -51,5 +57,6 @@ Global
|
||||||
{D6DAA79C-6B82-46E7-90CD-73D0A3196B8A} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
|
{D6DAA79C-6B82-46E7-90CD-73D0A3196B8A} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
|
||||||
{49021FCE-1CA9-430F-99B2-8E5C14A48394} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
|
{49021FCE-1CA9-430F-99B2-8E5C14A48394} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
|
||||||
{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
|
{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
|
||||||
|
{EEC6EF36-EE16-4DB4-9AE8-CF0234751458} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
68
src/2024/06/Guard.cs
Normal file
68
src/2024/06/Guard.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
class Guard : IUpdate
|
||||||
|
{
|
||||||
|
private readonly Map _map;
|
||||||
|
private readonly HashSet<(Vector2I, char)> _path = [];
|
||||||
|
|
||||||
|
public char Direction { get; set; }
|
||||||
|
|
||||||
|
public Vector2I Position { get; set; }
|
||||||
|
|
||||||
|
public Guard(Map map)
|
||||||
|
{
|
||||||
|
_map = map;
|
||||||
|
map.RegisterOnUpdate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool? Step()
|
||||||
|
{
|
||||||
|
if (!_path.Add((Position, Direction)))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var test = NextPosition();
|
||||||
|
if (!_map.IsValid(test))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (_map.IsOccupied(test))
|
||||||
|
{
|
||||||
|
Direction = Turn();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Position = test;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
var tmpPosition = Position;
|
||||||
|
if (Step() is not true)
|
||||||
|
{
|
||||||
|
_map.UnregisterOnUpdate(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_map.Move(tmpPosition, Position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2I NextPosition()
|
||||||
|
{
|
||||||
|
return Position + Program.ToVector(Direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private char Turn()
|
||||||
|
{
|
||||||
|
return Direction switch
|
||||||
|
{
|
||||||
|
'^' => '>',
|
||||||
|
'>' => 'v',
|
||||||
|
'v' => '<',
|
||||||
|
'<' => '^',
|
||||||
|
_ => throw new InvalidOperationException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/2024/06/IUpdate.cs
Normal file
4
src/2024/06/IUpdate.cs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
interface IUpdate
|
||||||
|
{
|
||||||
|
void Update();
|
||||||
|
}
|
||||||
145
src/2024/06/Map.cs
Normal file
145
src/2024/06/Map.cs
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO.MemoryMappedFiles;
|
||||||
|
|
||||||
|
class Map
|
||||||
|
{
|
||||||
|
delegate void UpdateHandler();
|
||||||
|
|
||||||
|
private readonly ArrayList _entities = [];
|
||||||
|
private (char, object?)[]? _map;
|
||||||
|
private UpdateHandler? _updater;
|
||||||
|
|
||||||
|
public int Width { get; private set; }
|
||||||
|
|
||||||
|
public int Height { get; private set; }
|
||||||
|
|
||||||
|
public void RegisterOnUpdate(IUpdate instance)
|
||||||
|
{
|
||||||
|
_updater += instance.Update;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnregisterOnUpdate(IUpdate instance)
|
||||||
|
{
|
||||||
|
_updater -= instance.Update;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T? FindEntity<T>()
|
||||||
|
{
|
||||||
|
return _entities.OfType<T>().FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Update()
|
||||||
|
{
|
||||||
|
if (_updater is not { } updatee)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatee();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load(string file)
|
||||||
|
{
|
||||||
|
using var fileView = MemoryMappedFile.CreateFromFile(file, FileMode.Open);
|
||||||
|
using var accessor = fileView.CreateViewAccessor();
|
||||||
|
(Width, Height, var stride) = Parse(accessor);
|
||||||
|
_map = new (char, object?)[Width * Height];
|
||||||
|
for (int x = 0; x < Width; x++)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < Height; y++)
|
||||||
|
{
|
||||||
|
object? entity = null;
|
||||||
|
var tile = (char)accessor.ReadByte(y * stride + x);
|
||||||
|
if (tile is '^' or '>' or '<' or 'v')
|
||||||
|
{
|
||||||
|
_entities.Add(entity = new Guard(this)
|
||||||
|
{
|
||||||
|
Position = new(x, y),
|
||||||
|
Direction = tile
|
||||||
|
});
|
||||||
|
tile = '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
SetTile(x, y, tile, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static (int Width, int Height, int Stride) 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, (int)((map.Capacity + width.Value) / stride!.Value), stride.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid(Vector2I position) => IsValid(position.X, position.Y);
|
||||||
|
|
||||||
|
public bool IsValid(int x, int y) => x >= 0 && x < Width && y >= 0 && y < Height;
|
||||||
|
|
||||||
|
public bool IsOccupied(Vector2I position) => IsOccupied(position.X, position.Y);
|
||||||
|
|
||||||
|
public bool IsOccupied(int x, int y)
|
||||||
|
{
|
||||||
|
EnsureMap();
|
||||||
|
if (!IsValid(x, y))
|
||||||
|
{
|
||||||
|
throw new Exception("(x, y)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _map[(y * Width) + x] is { Item2: not null } or { Item1: '#' };
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTile(Vector2I position, char? tile, object? entity) => SetTile(position.X, position.Y, tile, entity);
|
||||||
|
|
||||||
|
public void Move(Vector2I from, Vector2I to)
|
||||||
|
{
|
||||||
|
EnsureMap();
|
||||||
|
if (!IsValid(from))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(null, nameof(from));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValid(to))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(null, nameof(to));
|
||||||
|
}
|
||||||
|
|
||||||
|
ref (char, object?) fromTile = ref _map[from.Y * Width + from.X];
|
||||||
|
_map[to.Y * Width + to.X].Item2 = fromTile.Item2;
|
||||||
|
fromTile.Item2 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTile(int x, int y, char? tile, object? entity)
|
||||||
|
{
|
||||||
|
EnsureMap();
|
||||||
|
ref (char, object?) value = ref _map[(y * Width) + x];
|
||||||
|
value = value with
|
||||||
|
{
|
||||||
|
Item1 = tile ?? value.Item1,
|
||||||
|
Item2 = entity ?? value.Item2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_map))]
|
||||||
|
private void EnsureMap() => ArgumentNullException.ThrowIfNull(_map);
|
||||||
|
}
|
||||||
83
src/2024/06/Program.cs
Normal file
83
src/2024/06/Program.cs
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
using ConsoleAppFramework;
|
||||||
|
|
||||||
|
partial class Program
|
||||||
|
{
|
||||||
|
private static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var builder = ConsoleApp.Create();
|
||||||
|
builder.Add("part-one", static ([Argument, FileExists] string file) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Result: {ReadFile(file, false)}");
|
||||||
|
});
|
||||||
|
builder.Add("part-two", static ([Argument, FileExists] string file) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Result: {ReadFile(file, true)}");
|
||||||
|
});
|
||||||
|
builder.Run(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Vector2I ToVector(char direction)
|
||||||
|
{
|
||||||
|
return direction switch
|
||||||
|
{
|
||||||
|
'^' => new(0, -1),
|
||||||
|
'>' => new(1, 0),
|
||||||
|
'<' => new(-1, 0),
|
||||||
|
'v' => new(0, 1),
|
||||||
|
_ => throw new InvalidOperationException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReadFile(string file, bool findLoops)
|
||||||
|
{
|
||||||
|
Map map = new();
|
||||||
|
map.Load(file);
|
||||||
|
Guard guard = map.FindEntity<Guard>()!;
|
||||||
|
var guardStartDirection = guard.Direction;
|
||||||
|
var guardStartPosition = guard.Position;
|
||||||
|
HashSet<Vector2I> uniquePositions = [guardStartPosition];
|
||||||
|
while (map.Update())
|
||||||
|
{
|
||||||
|
uniquePositions.Add(guard.Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!findLoops)
|
||||||
|
{
|
||||||
|
return uniquePositions.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<Vector2I> uniqueObstacles = [];
|
||||||
|
foreach (var position in uniquePositions)
|
||||||
|
{
|
||||||
|
if (position == guardStartPosition)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Guard validator = new(map)
|
||||||
|
{
|
||||||
|
Position = guardStartPosition,
|
||||||
|
Direction = guardStartDirection
|
||||||
|
};
|
||||||
|
map.SetTile(position, '#', null);
|
||||||
|
bool? result;
|
||||||
|
while ((result = validator.Step()) is true)
|
||||||
|
{ }
|
||||||
|
map.SetTile(position, '.', null);
|
||||||
|
|
||||||
|
if (result is null)
|
||||||
|
{
|
||||||
|
uniqueObstacles.Add(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniqueObstacles.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileExistsAttribute : ValidationAttribute
|
||||||
|
{
|
||||||
|
public override bool IsValid(object? value) => value is string path && File.Exists(path);
|
||||||
|
}
|
||||||
8
src/2024/06/Vector2I.cs
Normal file
8
src/2024/06/Vector2I.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
record struct Vector2I(int X, int Y) : IAdditionOperators<Vector2I, Vector2I, Vector2I>, ISubtractionOperators<Vector2I, Vector2I, Vector2I>
|
||||||
|
{
|
||||||
|
public static Vector2I operator +(Vector2I left, Vector2I right) => new(left.X + right.X, left.Y + right.Y);
|
||||||
|
|
||||||
|
public static Vector2I operator -(Vector2I left, Vector2I right) => new(left.X - right.X, left.Y - right.Y);
|
||||||
|
}
|
||||||
11
src/2024/06/aoc-2024-06.csproj
Normal file
11
src/2024/06/aoc-2024-06.csproj
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<RootNamespace>aoc_2024_06</RootNamespace>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue