Skip to content

Commit

Permalink
day 6
Browse files Browse the repository at this point in the history
  • Loading branch information
mazharenko committed Dec 6, 2024
1 parent 27be90c commit 7f190e5
Show file tree
Hide file tree
Showing 6 changed files with 329 additions and 27 deletions.
43 changes: 32 additions & 11 deletions src/aoc/Common/Maps/M.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
using MoreLinq;
using System.Diagnostics.CodeAnalysis;
using aoc.Common.Points;
using MoreLinq;

namespace aoc.Common.Maps;

// todo separate wrapper type?
public static class M
{
public static T[,] Init<T>(int height, int width, Func<int, int, T> initializer)
public static T[,] Init<T>(int height, int width, Func<V<int>, T> initializer)
{
var map = new T[height, width];

for (var i = 0; i < height; i++)
for (var j = 0; j < width; j++)
map[i, j] = initializer(i, j);
map[i, j] = initializer(V.Create(i, j));

return map;
}
Expand All @@ -27,22 +29,41 @@ public static class M
return map;
}

public static U[,] Map<T, U>(T[,] source, Func<int, int, T, U> mapper)
public static U[,] Map<T, U>(this T[,] source, Func<V<int>, T, U> mapper)
{
return Init(source.GetLength(0), source.GetLength(1), (i, j) => mapper(i, j, source[i, j]));
return Init(source.GetLength(0), source.GetLength(1), p => mapper(p, source.At(p)));
}

public static void Iter<T>(this T[,] source, Action<int, int, T> action)
public static void Iter<T>(this T[,] source, Action<V<int>, T> action)
{
source.AsEnumerable().ForEach((x) => action(x.i, x.j, x.element));
source.AsEnumerable().ForEach((x) => action(x.point, x.element));
}

// todo: separate type can implement IEnumerable
public static IEnumerable<(int i, int j, T element)> AsEnumerable<T>(this T[,] source)
public static IEnumerable<(V<int> point, T element)> AsEnumerable<T>(this T[,] source)
{
for (var i = 0; i < source.Height(); i++)
for (var j = 0; j < source.Width(); j++)
yield return (i, j, source[i, j]);
yield return (V.Create(i, j), source[i, j]);
}

public static T At<T>(this T[,] source, V<int> point)
{
return source[point.X, point.Y];
}

// todo: maybe?
public static bool TryAt<T>(this T[,] source, V<int> point, [MaybeNullWhen(false)] out T element)
{
if (point.X < 0 || point.X >= source.Height()
|| point.Y < 0 || point.Y >= source.Width())
{
element = default;
return false;
}

element = source[point.X, point.Y];
return true;
}

public static int Height<T>(this T[,] map) => map.GetLength(0);
Expand Down Expand Up @@ -79,10 +100,10 @@ public static bool CompareFromSafe<T>(this T[,] map1, int iFrom, int jFrom, T[,]
}

public static T[,] RotateCw<T>(this T[,] map)
=> Init(map.Width(), map.Height(), (i, j) => map[map.Height() - j - 1, i]);
=> Init(map.Width(), map.Height(), p => map[map.Height() - p.Y - 1, p.X]);

public static T[,] RotateCcw<T>(this T[,] map)
=> Init(map.Width(), map.Height(), (i, j) => map[j, map.Width() - i - 1]);
=> Init(map.Width(), map.Height(), p => map[p.Y, map.Width() - p.X - 1]);

// todo slice
// separate type can get an indexer for slice
Expand Down
73 changes: 73 additions & 0 deletions src/aoc/Common/Points/V.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.Numerics;

namespace aoc.Common.Points;

public static class V
{
public static V<T> Create<T>(T x, T y) where T : INumber<T>
{
return new V<T>(x, y);
}
public static V<T> Dir<T>(this V<T> p) where T : INumber<T>
{
return Create(T.CreateChecked(T.Sign(p.X)), T.CreateChecked(T.Sign(p.Y)));
}

public static V<T> RotateCw<T>(this V<T> v) where T : INumber<T>
=> Create(v.Y, -v.X);

public static V<T> RotateCcw<T>(this V<T> v) where T : INumber<T>
=> Create(-v.Y, v.X);
}

public static class Directions
{
public static V<int> Down() => new(1, 0);

public static V<int> Up() => new(-1, 0);

public static V<int> Right() => new(0, 1);

public static V<int> Left() => new(0, -1);
}

public record struct V<T>(T X, T Y) where T : INumber<T>
{
public V((T X, T Y) tuple) : this(tuple.X, tuple.Y)
{
}

public static V<T> operator +(V<T> v1, V<T> v2)
{
return new V<T>(v1.X + v2.X, v1.Y + v2.Y);
}

public static V<T> operator -(V<T> v1, V<T> v2)
{
return new V<T>(v1.X - v2.X, v1.Y - v2.Y);
}

public static V<T> operator +(V<T> v1, V<int> v2)
{
return new V<T>(v1.X + T.CreateChecked(v2.X), v1.Y + T.CreateChecked(v2.Y));
}

public static V<T> operator -(V<T> v1, V<int> v2)
{
return new V<T>(v1.X - T.CreateChecked(v2.X), v1.Y - T.CreateChecked(v2.Y));
}

public static V<T> operator *(V<T> v1, T n)
{
return new V<T>(v1.X * n, v1.Y * n);
}

public static V<T> operator *(V<T> v1, int n)
{
return new V<T>(v1.X * T.CreateChecked(n), v1.Y * T.CreateChecked(n));
}

public static V<T> Zero { get; } = new(T.Zero, T.Zero);
public static V<T> One { get; } = new(T.One, T.One);

}
2 changes: 1 addition & 1 deletion src/aoc/Year2024/Day04/Day04.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal static int CountFormations(char[,] input, char[][,] formations)
{
return input.AsEnumerable()
.Sum(x =>
formations.Count(xmas => input.CompareFromSafe(x.i, x.j, xmas, (c, xmasc) => xmasc == '.' || xmasc == c))
formations.Count(xmas => input.CompareFromSafe(x.point.X, x.point.Y, xmas, (c, xmasc) => xmasc == '.' || xmasc == c))
);
}

Expand Down
106 changes: 91 additions & 15 deletions src/aoc/Year2024/Day06/Day06.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,96 @@
using aoc.Common.Maps;
using aoc.Common.Points;
using MoreLinq;
using V = aoc.Common.Points.V<int>;

namespace aoc.Year2024.Day06;

internal partial class Day06
{
internal partial class Part1
{
public string Solve(string input)
{
throw new NotImplementedException();
}
}

internal partial class Part2
{
public string Solve(string input)
{
throw new NotImplementedException();
}
}
internal partial class Part1
{
private readonly Example example = new(
"""
....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#...
""", 41);

public int Solve(char[,] input)
{
var (guardPosition, _) = input.AsEnumerable().Single(x => x.element == '^');

return WalkUntilOutside(input, guardPosition, Directions.Up())
.Select(x => x.pos)
.Distinct()
.Count();
}


public char[,] Parse(string input)
{
return Character.In('.', '#', '^').Map().Parse(input);
}
}

internal partial class Part2
{
private readonly Example example = new(
"""
....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#...
""", 6);


public int Solve(char[,] input)
{
var (guardPosition, _) = input.AsEnumerable().Single(x => x.element == '^');
var originalVisited =
WalkUntilOutside(input, guardPosition, Directions.Up())
.Select(x => x.pos).Distinct();
return originalVisited.Count(obstacleCandidate =>
{
var copy = input.Map((p, cell) => p == obstacleCandidate ? '#' : cell);
return
WalkUntilOutside(copy, guardPosition, Directions.Up())
// enumerable returned will sometimes be infinite and looped.
// as soon as we discover a duplicate, we go continue to the next obstacle candidate.
// Duplicates is documented to be deferred and not populate the whole sequence.
.Duplicates().Any();
});
}

public char[,] Parse(string input)
{
return Character.In('.', '#', '^').Map().Parse(input);
}
}

private static IEnumerable<(V pos, V dir)> WalkUntilOutside(char[,] map, V pos, V dir)
{
while (true)
{
yield return (pos, dir);
if (!map.TryAt(pos + dir, out var nextCell)) break;
if (nextCell is '#')
dir = dir.RotateCw();
else
pos += dir;
}
}
}
2 changes: 2 additions & 0 deletions tests/AoC.Tests/InputTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ private static IEnumerable<PartInputCaseData> GetCases()
yield return new PartInputCaseData(4, 2, "2000");
yield return new PartInputCaseData(5, 1, "5639");
yield return new PartInputCaseData(5, 2, "5273");
yield return new PartInputCaseData(6, 1, "4722");
yield return new PartInputCaseData(6, 2, "1602");
}
}
Loading

0 comments on commit 7f190e5

Please sign in to comment.