Skip to content

Commit

Permalink
2023 Day22 refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
smabuk committed Dec 22, 2023
1 parent 6dda765 commit d677cce
Showing 1 changed file with 65 additions and 107 deletions.
172 changes: 65 additions & 107 deletions Solutions/2023/Day22.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,154 +7,112 @@
[Description("Sand Slabs")]
public sealed partial class Day22 {

public static string Part1(string[] input, params object[]? args) => Solution1(input).ToString();
public static string Part2(string[] input, params object[]? args) => Solution2(input).ToString();

private static int Solution1(string[] input) {
List<Brick> bricks = [.. input.As<Brick>()];
List<Brick> orderedBricks = [..bricks.OrderBy(brick => brick.Start.Z)];

for (int i = 0; i < bricks.Count; i++) {
Brick brick = orderedBricks[i];
List<Brick> bricksBelow = orderedBricks[0..i];
int newZ = bricksBelow
.Where(b => (brick.Start.X, brick.End.X).TryGetOverlap((b.Start.X, b.End.X), out _)
&& (brick.Start.Y, brick.End.Y).TryGetOverlap((b.Start.Y, b.End.Y), out _))
.DefaultIfEmpty(new Brick(new(0,0,0), new(0,0,0)))
.Max(b => b.End.Z) + 1;
orderedBricks[i] = brick with { Start = new(brick.Start.X, brick.Start.Y, newZ),
End = new(brick.End.X, brick.End.Y, newZ + brick.End.Z - brick.Start.Z)};
}
[Init]
public static void Init(string[] input, params object[]? args) => LoadBricks(input);
public static string Part1(string[] input, params object[]? args) => Solution1().ToString();
public static string Part2(string[] input, params object[]? args) => Solution2().ToString();

private static List<Brick> _bricks = [];
private static List<string> _bricksWillFall = [];
private static Dictionary<string, (HashSet<string> SupportedBy, HashSet<string> Supporting)> _aboveAndBelow = [];

private static void LoadBricks(string[] input)
{
_bricks = AllowBricksToFall([.. input.As<Brick>().OrderBy(brick => brick.Start.Z)]);
_aboveAndBelow = CalculateSupportingStructure(_bricks);
_bricksWillFall = WhichBricksWillFall(_bricks, _aboveAndBelow);
}

Dictionary<string, (HashSet<string> SupportedBy, HashSet<string> Supporting)> aboveAndBelow = [];
private static int Solution1() => _bricks.Count - _bricksWillFall.Count;

private static int Solution2() {
HashSet<string> disintegratedOrFallen;

for (int i = 0; i < bricks.Count; i++) {
Brick brick = orderedBricks[i];
List<Brick> bricksAbove = orderedBricks[(i+1)..];
HashSet<string> supporting = [..orderedBricks
.Where(b => brick.End.Z == b.Start.Z - 1
&& (brick.Start.X, brick.End.X).TryGetOverlap((b.Start.X, b.End.X), out _)
&& (brick.Start.Y, brick.End.Y).TryGetOverlap((b.Start.Y, b.End.Y), out _))
.Select(b => b.Name)];
HashSet<string> supportedBy = [..orderedBricks
.Where(b => brick.Start.Z == b.End.Z + 1
&& (brick.Start.X, brick.End.X).TryGetOverlap((b.Start.X, b.End.X), out _)
&& (brick.Start.Y, brick.End.Y).TryGetOverlap((b.Start.Y, b.End.Y), out _))
.Select(b => b.Name)];
aboveAndBelow[brick.Name] = (supportedBy, supporting);
int count = 0;
foreach (string brickName in _bricksWillFall) {
disintegratedOrFallen = [brickName];
count += CountFallersAbove(brickName);
}

int count = 0;
for (int i = 0; i < orderedBricks.Count; i++) {
Brick brick = orderedBricks[i];
if (aboveAndBelow[brick.Name].Supporting.Count == 0) {
count++;
continue;
}
bool ok = true;
foreach (string aboveName in aboveAndBelow[brick.Name].Supporting) {
if (aboveAndBelow[aboveName].SupportedBy.Count == 1) {
ok = false;
break;
return count;

int CountFallersAbove(string brickName)
{
int count = 0;
foreach (string name in _aboveAndBelow[brickName].Supporting) {
if (IsNotSupportedByAnyBricks(name)) {
_ = disintegratedOrFallen.Add(name);
count += 1 + CountFallersAbove(name);
}
}
if (ok) {
count++;
}
}

return count;
}
return count;

private static int Solution2(string[] input) {
List<Brick> bricks = [.. input.As<Brick>()];
List<Brick> orderedBricks = [.. bricks.OrderBy(brick => brick.Start.Z)];
bool IsNotSupportedByAnyBricks(string name) => !_aboveAndBelow[name].SupportedBy.Except(disintegratedOrFallen).Any();
}
}

private static List<Brick> AllowBricksToFall(List<Brick> bricks)
{
for (int i = 0; i < bricks.Count; i++) {
Brick brick = orderedBricks[i];
List<Brick> bricksBelow = orderedBricks[0..i];
int newZ = bricksBelow
.Where(b => (brick.Start.X, brick.End.X).TryGetOverlap((b.Start.X, b.End.X), out _)
&& (brick.Start.Y, brick.End.Y).TryGetOverlap((b.Start.Y, b.End.Y), out _))
Brick brick = bricks[i];
int newZ = bricks[0..i]
.Where(brick.XYOverlaps)
.DefaultIfEmpty(new Brick(new(0, 0, 0), new(0, 0, 0)))
.Max(b => b.End.Z) + 1;
orderedBricks[i] = brick with
bricks[i] = brick with
{
Start = new(brick.Start.X, brick.Start.Y, newZ),
End = new(brick.End.X, brick.End.Y, newZ + brick.End.Z - brick.Start.Z)
End = new(brick.End.X, brick.End.Y, newZ + brick.End.Z - brick.Start.Z)
};
}

Dictionary<string, (HashSet<string> SupportedBy, HashSet<string> Supporting)> aboveAndBelow = [];
return bricks;
}


private static Dictionary<string, (HashSet<string> SupportedBy, HashSet<string> Supporting)> CalculateSupportingStructure(List<Brick> bricks)
{
Dictionary<string, (HashSet<string> SupportedBy, HashSet<string> Supporting)> aboveAndBelow = [];
for (int i = 0; i < bricks.Count; i++) {
Brick brick = orderedBricks[i];
List<Brick> bricksAbove = orderedBricks[(i + 1)..];
HashSet<string> supporting = [..orderedBricks
.Where(b => brick.End.Z == b.Start.Z - 1
&& (brick.Start.X, brick.End.X).TryGetOverlap((b.Start.X, b.End.X), out _)
&& (brick.Start.Y, brick.End.Y).TryGetOverlap((b.Start.Y, b.End.Y), out _))
Brick brick = bricks[i];
HashSet<string> supportedBy = [..bricks
.Where(b => brick.Start.Z == b.End.Z + 1 && brick.XYOverlaps(b))
.Select(b => b.Name)];
HashSet<string> supportedBy = [..orderedBricks
.Where(b => brick.Start.Z == b.End.Z + 1
&& (brick.Start.X, brick.End.X).TryGetOverlap((b.Start.X, b.End.X), out _)
&& (brick.Start.Y, brick.End.Y).TryGetOverlap((b.Start.Y, b.End.Y), out _))
HashSet<string> supporting = [..bricks
.Where(b => brick.End.Z == b.Start.Z - 1 && brick.XYOverlaps(b))
.Select(b => b.Name)];
aboveAndBelow[brick.Name] = (supportedBy, supporting);
}

List<string> bricksWontFall = [];
return aboveAndBelow;
}

private static List<string> WhichBricksWillFall(List<Brick> bricks, Dictionary<string, (HashSet<string> SupportedBy, HashSet<string> Supporting)> aboveAndBelow)
{
List<string> bricksWillFall = [];
for (int i = 0; i < orderedBricks.Count; i++) {
Brick brick = orderedBricks[i];
foreach (Brick brick in bricks) {
if (aboveAndBelow[brick.Name].Supporting.Count == 0) {
bricksWontFall.Add(brick.Name);
continue;
}
bool ok = true;

foreach (string aboveName in aboveAndBelow[brick.Name].Supporting) {
if (aboveAndBelow[aboveName].SupportedBy.Count == 1) {
ok = false;
bricksWillFall.Add(brick.Name);
break;
}
}
if (ok) {
bricksWontFall.Add(brick.Name);
} else {
bricksWillFall.Add(brick.Name);
}
}

HashSet<string> seen = [];
int count = 0;
foreach (string brickName in bricksWillFall) {
seen = [brickName];
count += CountFallersAbove(brickName);
}

return count;

int CountFallersAbove(string brickName)
{
int count = 0;
foreach (string name in aboveAndBelow[brickName].Supporting) {
int supportedByCount = aboveAndBelow[name].SupportedBy.Except(seen).Count();
if (supportedByCount < 1 && !seen.Contains(name)) {
_ = seen.Add(name);
count++;
count += CountFallersAbove(name);
}
}
return count;
}
return bricksWillFall;
}


private sealed record Brick(Point3d Start, Point3d End) : IParsable<Brick> {
public string Name => $"({Start.X},{Start.Y},{Start.Z})-({End.X},{End.Y},{End.Z})";

public bool XYOverlaps(Brick otherBrick) =>
(Start.X, End.X).TryGetOverlap((otherBrick.Start.X, otherBrick.End.X), out _)
&& (Start.Y, End.Y).TryGetOverlap((otherBrick.Start.Y, otherBrick.End.Y), out _);

public static Brick Parse(string s, IFormatProvider? provider)
{
string[] tokens = s.TrimmedSplit('~');
Expand Down

0 comments on commit d677cce

Please sign in to comment.