namespace Day22; public class BrickStack { private readonly List _bricks = new List(); private Dictionary _space = new Dictionary(); private Vec _min = Vec.Zero; private Vec _max = Vec.Zero; public BrickStack() { } public void Add(Brick brick) { _bricks.Add(brick); _min = Min(_min, brick.Start, brick.End); _max = Max(_max, brick.Start, brick.End); } public void Populate() { _space = new Dictionary(); foreach (var b in _bricks.OrderBy(x => x.MinZ)) { foreach (var p in b.Points()) { if (!_space.TryAdd(p, b)) { throw new Exception("Unexpected overlap during populate phase"); } } } } public void Collapse() { foreach (var b in _bricks.OrderBy(x => x.MinZ)) { var newZ = 1L; foreach (var p in b.Points()) { var below = p; while (below.Z > newZ - 1) { var target = _space.GetValueOrDefault(below); if (target == null || target == b) { below = below.Add(Vec.Down); } else { if (below.Z + 1 > newZ) { newZ = below.Z + 1; } break; } } } if (newZ < b.MinZ) { MoveDown(b, b.MinZ - newZ); } } } public void Print() { for (var y = _min.Y; y <= _max.Y; y++) { for (var z = _min.Z; z <= Math.Min(_max.Z, 19); z++) { for (var x = _min.X; x <= _max.X; x++) { var brick = _space.GetValueOrDefault(new Vec(x, y, z)); if (brick != null) { Console.Write(brick.Label); } else { Console.Write('.'); } } Console.Write(' '); } Console.WriteLine(); } } public long CountSafeRemoval() { var count = 0L; foreach (var b in _bricks.OrderBy(x => x.MinZ)) { var above = BricksAbove(b).ToList(); if (above.Count == 0 || above.All(a => BricksBelow(a).Count() >= 2)) { count++; } } return count; } private Vec Min(params Vec[] vectors) { var x = vectors[0].X; var y = vectors[0].Y; var z = vectors[0].Z; foreach (var v in vectors.Skip(1)) { x = Math.Min(x, v.X); y = Math.Min(y, v.Y); z = Math.Min(z, v.Z); } return new Vec(x, y, z); } private Vec Max(params Vec[] vectors) { var x = vectors[0].X; var y = vectors[0].Y; var z = vectors[0].Z; foreach (var v in vectors.Skip(1)) { x = Math.Max(x, v.X); y = Math.Max(y, v.Y); z = Math.Max(z, v.Z); } return new Vec(x, y, z); } private void MoveDown(Brick brick, long count) { foreach (var p in brick.Points()) { _space.Remove(p); } brick.MoveDown(count); foreach (var p in brick.Points()) { if (!_space.TryAdd(p, brick)) { throw new Exception("Unexpected overlap during collapse phase"); } } } private IEnumerable BricksAbove(Brick brick) { return brick.Points() .Select(p => _space.GetValueOrDefault(p.Add(Vec.Up))) .Where(b => b != null && b != brick) .Cast() .Distinct(); } private IEnumerable BricksBelow(Brick brick) { return brick.Points() .Select(p => _space.GetValueOrDefault(p.Add(Vec.Down))) .Where(b => b != null && b != brick) .Cast() .Distinct(); } }