Initial Commit
This commit is contained in:
344
2022/Day18CSharp/Program.cs
Normal file
344
2022/Day18CSharp/Program.cs
Normal file
@@ -0,0 +1,344 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
Part01();
|
||||
|
||||
void Part01()
|
||||
{
|
||||
Console.WriteLine("!! === Part 01 === !!");
|
||||
|
||||
Console.WriteLine(" === Example Simple ===");
|
||||
var exampleSimpleData = new ProblemData("example-input-simple.txt");
|
||||
exampleSimpleData.Print();
|
||||
|
||||
Console.WriteLine(" === Example ===");
|
||||
var exampleData = new ProblemData("example-input.txt");
|
||||
exampleData.Print();
|
||||
|
||||
Console.WriteLine(" === Puzzle ===");
|
||||
var puzzleData = new ProblemData("puzzle-input.txt");
|
||||
puzzleData.Print();
|
||||
}
|
||||
|
||||
struct Vertex
|
||||
: IComparable
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
public int Z;
|
||||
|
||||
public Vertex() : this(int.MinValue, int.MinValue, int.MinValue) { }
|
||||
|
||||
public Vertex(int x, int y, int z)
|
||||
{
|
||||
this.X = x;
|
||||
this.Y = y;
|
||||
this.Z = z;
|
||||
}
|
||||
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
if (obj == null) throw new InvalidCastException();
|
||||
var other = (Vertex)obj;
|
||||
|
||||
if (this.X != other.X)
|
||||
{
|
||||
return this.X.CompareTo(other.X);
|
||||
}
|
||||
else if (this.Y != other.Y)
|
||||
{
|
||||
return this.Y.CompareTo(other.Y);
|
||||
}
|
||||
else if (this.Z != other.Z)
|
||||
{
|
||||
return this.Z.CompareTo(other.Z);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
var other = (Vertex)obj;
|
||||
return this.CompareTo(other) == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Cube
|
||||
{
|
||||
public Cube(int x, int y, int z)
|
||||
{
|
||||
init(x, y, z);
|
||||
}
|
||||
|
||||
public Cube(string data)
|
||||
{
|
||||
var split = data.Split(',');
|
||||
|
||||
init(int.Parse(split[0]), int.Parse(split[1]), int.Parse(split[2]));
|
||||
}
|
||||
|
||||
private void init(int x, int y, int z)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
|
||||
List<Vertex> vertices = new List<Vertex>();
|
||||
|
||||
// Create the verts (we'll store CCW)
|
||||
for (int d = 0; d < 2; d++)
|
||||
{
|
||||
vertices.Add(new Vertex(x, y, z + d));
|
||||
vertices.Add(new Vertex(x+1, y, z + d));
|
||||
vertices.Add(new Vertex(x+1, y+1, z + d));
|
||||
vertices.Add(new Vertex(x, y+1, z + d));
|
||||
}
|
||||
|
||||
Vertices = vertices.ToArray();
|
||||
}
|
||||
|
||||
public Vertex[] Vertices { get; private set; } = new Vertex[0];
|
||||
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int Z { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("Cube at {0},{1},{2}", X, Y, Z);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
var other = (Cube)obj;
|
||||
return X == other.X && Y == other.Y && Z == other.Z;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return X ^ Y ^ Z;
|
||||
}
|
||||
|
||||
public bool IsAdjacent(Cube c)
|
||||
{
|
||||
var faceAdjacents = new Cube[]
|
||||
{
|
||||
// Directly to the sides
|
||||
new Cube(c.X+1, c.Y, c.Z),
|
||||
new Cube(c.X, c.Y+1, c.Z),
|
||||
new Cube(c.X, c.Y, c.Z+1),
|
||||
new Cube(c.X-1, c.Y, c.Z),
|
||||
new Cube(c.X, c.Y-1, c.Z),
|
||||
new Cube(c.X, c.Y, c.Z-1),
|
||||
};
|
||||
|
||||
foreach (var f in faceAdjacents)
|
||||
{
|
||||
if (f.Equals(this)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Cube[] Adjacents
|
||||
{
|
||||
get
|
||||
{
|
||||
var faceAdjacents = new Cube[]
|
||||
{
|
||||
// Directly to the sides
|
||||
new Cube(this.X+1, this.Y, this.Z),
|
||||
new Cube(this.X, this.Y+1, this.Z),
|
||||
new Cube(this.X, this.Y, this.Z+1),
|
||||
new Cube(this.X-1, this.Y, this.Z),
|
||||
new Cube(this.X, this.Y-1, this.Z),
|
||||
new Cube(this.X, this.Y, this.Z-1),
|
||||
};
|
||||
|
||||
return faceAdjacents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ProblemData
|
||||
{
|
||||
public HashSet<Cube> Cubes { get; }
|
||||
public HashSet<Cube> AirCubes { get; private set; } = new HashSet<Cube>();
|
||||
|
||||
public ProblemData(string filename)
|
||||
{
|
||||
Cubes = new HashSet<Cube>();
|
||||
|
||||
using (StreamReader reader = System.IO.File.OpenText(filename))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (line == null) throw new InvalidDataException();
|
||||
|
||||
var newCube = new Cube(line);
|
||||
if (Cubes.Contains(newCube)) throw new InvalidDataException();
|
||||
Cubes.Add(newCube);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetExposedSides(Cube c, HashSet<Cube> cubes)
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
var faceAdjacents = new Cube[]
|
||||
{
|
||||
// Directly to the sides
|
||||
new Cube(c.X+1, c.Y, c.Z),
|
||||
new Cube(c.X, c.Y+1, c.Z),
|
||||
new Cube(c.X, c.Y, c.Z+1),
|
||||
new Cube(c.X-1, c.Y, c.Z),
|
||||
new Cube(c.X, c.Y-1, c.Z),
|
||||
new Cube(c.X, c.Y, c.Z-1),
|
||||
};
|
||||
|
||||
var angledAdjacents = new Cube[]
|
||||
{
|
||||
new Cube(c.X+1, c.Y, c.Z-1), // Right and closer to camera
|
||||
new Cube(c.X-1, c.Y, c.Z-1), // Left and closer to camera
|
||||
new Cube(c.X+1, c.Y, c.Z+1), // Right and farther from camera
|
||||
new Cube(c.X-1, c.Y, c.Z+1), // Left and farther from camera
|
||||
new Cube(c.X, c.Y+1, c.Z-1), // Up and closer to the camera
|
||||
new Cube(c.X, c.Y+1, c.Z+1), // Up and farther from camera
|
||||
new Cube(c.X, c.Y-1, c.Z-1), // Down and closer to camera
|
||||
new Cube(c.X, c.Y-1, c.Z+1), // Down and farther from camera
|
||||
};
|
||||
|
||||
foreach (var adjacent in faceAdjacents)
|
||||
{
|
||||
if (cubes.Contains(adjacent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public int SurfaceArea
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSurfaceArea(Cubes);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetSurfaceArea(HashSet<Cube> cubes)
|
||||
{
|
||||
int area = 0;
|
||||
foreach (var c in cubes)
|
||||
{
|
||||
area += GetExposedSides(c, cubes);
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
public int ExteriorSurfaceArea
|
||||
{
|
||||
get
|
||||
{
|
||||
HashSet<Cube> exterior = new HashSet<Cube>();
|
||||
|
||||
FillExterior(new Cube(Bounds[0].X, Bounds[0].Y, Bounds[0].Z), Cubes, exterior);
|
||||
|
||||
int area = 0;
|
||||
foreach (var cube in Cubes)
|
||||
{
|
||||
foreach (var adjacent in cube.Adjacents)
|
||||
{
|
||||
if (exterior.Contains(adjacent)) area++;
|
||||
}
|
||||
}
|
||||
|
||||
return area;
|
||||
}
|
||||
}
|
||||
|
||||
public void FillExterior(Cube startingCube, HashSet<Cube> dropletCubes, HashSet<Cube> exterior)
|
||||
{
|
||||
List<Cube> toTest = new List<Cube>();
|
||||
|
||||
toTest.Add(startingCube);
|
||||
|
||||
do
|
||||
{
|
||||
var c = toTest[0];
|
||||
toTest.RemoveAt(0);
|
||||
|
||||
if (c.X >= Bounds[0].X - 1 && c.Y >= Bounds[0].Y - 1 && c.Z >= Bounds[0].Z - 1
|
||||
&& c.X <= Bounds[1].X + 1 && c.Y <= Bounds[1].Y + 1 && c.Z <= Bounds[1].Z + 1
|
||||
&& !dropletCubes.Contains(c)
|
||||
&& !exterior.Contains(c))
|
||||
{
|
||||
exterior.Add(c);
|
||||
foreach (var face in c.Adjacents)
|
||||
{
|
||||
// C# doesnt support tail recursion innately
|
||||
//FillExterior(face, dropletCubes, exterior);
|
||||
toTest.Add(face);
|
||||
}
|
||||
}
|
||||
} while (toTest.Count > 0);
|
||||
}
|
||||
|
||||
private bool HasCubeBelow(Cube c)
|
||||
{
|
||||
return Cubes.Contains(new Cube(c.X, c.Y-1, c.Z));
|
||||
}
|
||||
|
||||
public Vertex[] Bounds
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mBounds.Length == 0)
|
||||
{
|
||||
Vertex bottomLeft = new Vertex(int.MaxValue, int.MaxValue, int.MaxValue);
|
||||
Vertex topRight = new Vertex(int.MinValue,int.MinValue,int.MinValue);
|
||||
|
||||
foreach (var c in Cubes)
|
||||
{
|
||||
bottomLeft.X = int.Min(c.X, bottomLeft.X);
|
||||
bottomLeft.Y = int.Min(c.Y, bottomLeft.Y);
|
||||
bottomLeft.Z = int.Min(c.Z, bottomLeft.Z);
|
||||
|
||||
topRight.X = int.Max(c.X, topRight.X);
|
||||
topRight.Y = int.Max(c.Y, topRight.Y);
|
||||
topRight.Z = int.Max(c.Z, topRight.Z);
|
||||
}
|
||||
|
||||
mBounds = new Vertex[] { bottomLeft, topRight };
|
||||
}
|
||||
|
||||
return mBounds;
|
||||
}
|
||||
}
|
||||
|
||||
private Vertex[] mBounds = new Vertex[0];
|
||||
|
||||
public void Print()
|
||||
{
|
||||
Console.WriteLine("Cubes {0} with surface area {1} and {2} exterior area", Cubes.Count, SurfaceArea, ExteriorSurfaceArea);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user