Day 18 solution

ivec3 is a copy of ivec2 with a third dimension added and a few new utility methods (chiefly: MinElement, GetNeighbors, IsTouching).
This commit is contained in:
2022-12-18 11:11:37 -06:00
parent 09f866c9a3
commit 818e9478e1
6 changed files with 2989 additions and 100 deletions

View File

@ -74,6 +74,8 @@
<EmbeddedResource Include="inputs\17a.txt" /> <EmbeddedResource Include="inputs\17a.txt" />
<EmbeddedResource Include="inputs\18.txt" /> <EmbeddedResource Include="inputs\18.txt" />
<None Remove="inputs\19.txt" /> <None Remove="inputs\19.txt" />
<EmbeddedResource Include="inputs\18a.txt" />
<EmbeddedResource Include="inputs\18b.txt" />
<EmbeddedResource Include="inputs\19.txt" /> <EmbeddedResource Include="inputs\19.txt" />
<None Remove="inputs\20.txt" /> <None Remove="inputs\20.txt" />
<EmbeddedResource Include="inputs\20.txt" /> <EmbeddedResource Include="inputs\20.txt" />

File diff suppressed because it is too large Load Diff

2
inputs/18a.txt Normal file
View File

@ -0,0 +1,2 @@
1,1,1
2,1,1

13
inputs/18b.txt Normal file
View File

@ -0,0 +1,13 @@
2,2,2
1,2,2
3,2,2
2,1,2
2,3,2
2,2,1
2,2,3
2,2,4
2,2,6
1,2,5
3,2,5
2,1,5
2,3,5

57
src/18.cs Normal file
View File

@ -0,0 +1,57 @@
using aoc2022.Util;
namespace aoc2022;
internal class Day18 : Day
{
private readonly List<ivec3> cubes = new();
internal override void Parse()
{
cubes.AddRange(Parsing.ReadAllLines("18").Select(ivec3.Parse));
}
internal override string Part1()
{
var totalSides = 6 * cubes.Count;
var coveredSides = cubes.Sum(c => cubes.Count(c2 => c != c2 && c.IsTouching(c2)));
return $"Exposed faces: <+white>{totalSides - coveredSides}";
}
internal override string Part2()
{
var set = new HashSet<ivec3>(cubes);
var minv = cubes.Min(c => c.MinElement) - 1;
var maxv = cubes.Max(c => c.MaxElement) + 1;
var min = new ivec3(minv, minv, minv);
var max = new ivec3(maxv, maxv, maxv);
HashSet<ivec3> visited = new();
Queue<ivec3> q = new();
var total = 0;
q.Enqueue(min);
visited.Add(min);
while (q.Count > 0)
{
var v = q.Dequeue();
foreach (var neighbor in v.GetNeighbors(min, max))
{
if (visited.Contains(neighbor))
{
continue;
}
if (set.Contains(neighbor))
{
total++;
continue;
}
visited.Add(neighbor);
q.Enqueue(neighbor);
}
}
return $"Exterior exposed faces: <+white>{total}";
}
}

173
src/Util/Vec3.cs Normal file
View File

@ -0,0 +1,173 @@
namespace aoc2022.Util;
public struct ivec3 : IEquatable<ivec3>, IComparable<ivec3>, IComparable
{
public long x = 0;
public long y = 0;
public long z = 0;
public static readonly ivec3 ZERO = new ivec3(0, 0, 0);
public static readonly ivec3 ONE = new ivec3(1, 1, 1);
public static readonly ivec3 LEFT = new ivec3(-1, 0, 0);
public static readonly ivec3 RIGHT = new ivec3(1, 0, 0);
public static readonly ivec3 UP = new ivec3(0, -1, 0);
public static readonly ivec3 DOWN = new ivec3(0, 1, 0);
public static readonly ivec3 FORWARD = new ivec3(0, 0, 1);
public static readonly ivec3 BACKWARD = new ivec3(0, 0, -1);
public static readonly ivec3[] DIRECTIONS = {LEFT, RIGHT, UP, DOWN, FORWARD, BACKWARD};
public ivec3(long xv, long yv, long zv)
{
x = xv;
y = yv;
z = zv;
}
public bool IsZero() => x == 0 && y == 0 && z == 0;
public long Sum => x + y + z;
public long Product => x * y * z;
public long MaxElement => System.Math.Max(x, System.Math.Max(y, z));
public long MinElement => System.Math.Min(x, System.Math.Min(y, z));
public ivec3 GetRotatedLeft() => new ivec3(y, -x, z);
public void RotateLeft() => this = GetRotatedLeft();
public ivec3 GetRotatedRight() => new ivec3(-y, x, z);
public void RotateRight() => this = GetRotatedRight();
public long Dot(ivec3 v) => (x * v.x) + (y * v.y) + (z * v.z);
public long LengthSquared => (x * x) + (y * y) + (z * z);
public float Length => MathF.Sqrt(LengthSquared);
public long ManhattanDistance => Abs(this).Sum;
public long ManhattanDistanceTo(ivec3 other) => System.Math.Abs(x - other.x) + System.Math.Abs(y - other.y) + System.Math.Abs(z - other.z);
public bool IsTouching(ivec3 other) => ManhattanDistanceTo(other) == 1;
public IEnumerable<ivec3> GetNeighbors(ivec3? min = null, ivec3? max = null)
{
foreach (var d in DIRECTIONS)
{
var n = this + d;
if ((min == null || n >= min) && (max == null || n <= max))
{
yield return n;
}
}
}
public ivec3 GetBestDirectionTo(ivec3 p)
{
ivec3 diff = p - this;
if (diff.IsZero())
{
return ZERO;
}
ivec3 dir = diff / Abs(diff).MaxElement;
return Sign(dir);
}
// get a point in the 8 cells around me closest to p
public ivec3 GetNearestNeighbor(ivec3 p)
{
ivec3 dir = GetBestDirectionTo(p);
return this + dir;
}
public long this[long i] => (i == 0) ? x : (i == 1) ? y : z;
public static ivec3 operator +(ivec3 v) => v;
public static ivec3 operator -(ivec3 v) => new ivec3(-v.x, -v.y, -v.z);
public static ivec3 operator +(ivec3 a, ivec3 b) => new ivec3(a.x + b.x, a.y + b.y, a.z + b.z);
public static ivec3 operator -(ivec3 a, ivec3 b) => new ivec3(a.x - b.x, a.y - b.y, a.z - b.z);
public static ivec3 operator *(ivec3 a, ivec3 b) => new ivec3(a.x * b.x, a.y * b.y, a.z * b.z);
public static ivec3 operator *(long a, ivec3 v) => new ivec3(a * v.x, a * v.y, a * v.z);
public static ivec3 operator *(ivec3 v, long a) => new ivec3(a * v.x, a * v.y, a * v.z);
public static ivec3 operator /(ivec3 v, long a) => new ivec3(v.x / a, v.y / a, v.z / a);
public static bool operator ==(ivec3 a, ivec3 b) => (a.x == b.x) && (a.y == b.y) && (a.z == b.z);
public static bool operator !=(ivec3 a, ivec3 b) => (a.x != b.x) || (a.y != b.y) || (a.z != b.z);
public static bool operator <(ivec3 a, ivec3 b) => (a.x < b.x) && (a.y < b.y) && (a.z < b.z);
public static bool operator <=(ivec3 a, ivec3 b) => (a.x <= b.x) && (a.y <= b.y) && (a.z <= b.z);
public static bool operator >(ivec3 a, ivec3 b) => (a.x > b.x) && (a.y > b.y) && (a.z > b.z);
public static bool operator >=(ivec3 a, ivec3 b) => (a.x >= b.x) && (a.y >= b.y) && (a.z >= b.z);
public bool Equals(ivec3 other)
{
return x == other.x && y == other.y && z == other.z;
}
public override bool Equals(object? obj)
{
return obj is ivec3 other && Equals(other);
}
public int CompareTo(ivec3 other)
{
if (this < other)
{
return -1;
}
if (this > other)
{
return 1;
}
return 0;
}
public int CompareTo(object? obj)
{
if (ReferenceEquals(null, obj)) return 1;
return obj is ivec3 other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(ivec3)}");
}
public static ivec3 Sign(ivec3 v) => new ivec3(System.Math.Sign(v.x), System.Math.Sign(v.y), System.Math.Sign(v.z));
public static ivec3 Min(ivec3 a, ivec3 b) => new ivec3(System.Math.Min(a.x, b.x), System.Math.Min(a.y, b.y), System.Math.Min(a.z, b.z));
public static ivec3 Max(ivec3 a, ivec3 b) => new ivec3(System.Math.Max(a.x, b.x), System.Math.Max(a.y, b.y), System.Math.Max(a.z, b.z));
public static ivec3 Clamp(ivec3 v, ivec3 lh, ivec3 rh) => Min(rh, Max(lh, v));
public static ivec3 Abs(ivec3 v) => new ivec3(System.Math.Abs(v.x), System.Math.Abs(v.y), System.Math.Abs(v.z));
public static ivec3 Mod(ivec3 val, long den)
{
long x = val.x % den;
long y = val.y % den;
long z = val.z % den;
if (x < 0)
{
x += den;
}
if (y < 0)
{
y += den;
}
if (z < 0)
{
z += den;
}
return new ivec3(x, y, z);
}
public static long Dot(ivec3 a, ivec3 b) => (a.x * b.x) + (a.y * b.y) + (a.z * b.z);
public static ivec3 Parse(string s)
{
string[] parts = s.Split(',', 3);
long x = long.Parse(parts[0]);
long y = long.Parse(parts[1]);
long z = long.Parse(parts[2]);
return new ivec3(x, y, z);
}
public override int GetHashCode() => HashCode.Combine(x, y, z);
public override string ToString() => $"{x},{y},{z}";
}