| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- namespace Day21;
- public class Map
- {
- private readonly int _width;
- private readonly int _height;
- private readonly Tile[,] _map;
- private readonly Tile? _start;
- public Map(int width, int height, IEnumerable<Tile> tiles)
- {
- _width = width;
- _height = height;
- _map = new Tile[width, height];
- var i = 0;
- foreach (var t in tiles)
- {
- if (t.IsStart)
- {
- _start = t;
- }
- t.Position = new Vec(i % width, i / width);
- _map[t.Position.X, t.Position.Y] = t;
- i++;
- }
- if (_start == null)
- {
- throw new Exception("No start tile found");
- }
- }
- public void Print(ISet<Vec>? mark = null)
- {
- for (int y = 0; y < _height; y++)
- {
- for (int x = 0; x < _width; x++)
- {
- if (mark != null && mark.Contains(new Vec(x, y)))
- {
- Console.Write('O');
- }
- else
- {
- Console.Write(_map[x, y]);
- }
- }
- Console.WriteLine();
- }
- Console.WriteLine();
- }
- public long ReachableTiles(long steps, Vec? start = null, bool print = false)
- {
- start ??= _start!.Position;
- var reachable = new HashSet<Vec>();
- reachable.Add(start.Value);
-
- for (int i = 0; i < steps; i++)
- {
- var next = new HashSet<Vec>();
- foreach (var p in reachable)
- {
- foreach (var neighbor in Surrounding(p).Where(n => At(n)?.IsWalkable ?? false))
- {
- next.Add(neighbor);
- }
- }
- reachable = next;
- }
- if (print)
- {
- Print(reachable);
- }
- return reachable.Count;
- }
- public long ReachableLarge(long steps)
- {
- var fullMap = PassableTileCount();
- Console.WriteLine($"Width: {_width} | Height: {_height}");
- Console.WriteLine($"Odd Reachable: {fullMap.Odd} | Even Reachable: {fullMap.Even}");
- var even = steps % 2 == 0;
- Console.WriteLine($"Step count is even: {even}");
-
- var count = 0L;
- var fullChunks = ((steps - (_width / 2)) / _width) - 1;
- Console.WriteLine($"Full chunks after center: {fullChunks}");
-
- // round UP to the nearest multiple of 2
- count += Square(fullChunks / 2 * 2 + 1) * fullMap.Odd;
- // round DOWN to the nearest multiple of 2
- count += Square((fullChunks + 1) / 2 * 2) * fullMap.Even;
- var majorSteps = (long)(_width + _width / 2 - 1);
- var minorSteps = (long)(_width / 2 - 1);
- var cornerT = ReachableTiles(_height - 1, new Vec(_width / 2, _height - 1));
- count += cornerT;
- var cornerB = ReachableTiles(_height - 1, new Vec(_width / 2, 0));
- count += cornerB;
- var cornerR = ReachableTiles(_width - 1, new Vec(_width - 1, _height / 2));
- count += cornerR;
- var cornerL = ReachableTiles(_width - 1, new Vec(0, _height / 2));
- count += cornerL;
- Console.WriteLine($"Corners: {cornerT} {cornerR} {cornerB} {cornerL}");
- var largeBL = ReachableTiles(majorSteps, new Vec(0, _height - 1));
- count += fullChunks * largeBL;
- var smallBL = ReachableTiles(minorSteps, new Vec(0, _height - 1));
- count += (fullChunks + 1) * smallBL;
- var largeTL = ReachableTiles(majorSteps, new Vec(0, 0));
- count += fullChunks * largeTL;
- var smallTL = ReachableTiles(minorSteps, new Vec(0, 0));
- count += (fullChunks + 1) * smallTL;
- var largeTR = ReachableTiles(majorSteps, new Vec(_width - 1, 0));
- count += fullChunks * largeTR;
- var smallTR = ReachableTiles(minorSteps, new Vec(_width - 1, 0));
- count += (fullChunks + 1) * smallTR;
- var largeBR = ReachableTiles(majorSteps, new Vec(_width - 1, _height - 1));
- count += fullChunks * largeBR;
- var smallBR = ReachableTiles(minorSteps, new Vec(_width - 1, _height - 1));
- count += (fullChunks + 1) * smallBR;
- Console.WriteLine($"Small Diagonals: {smallTL} {smallTR} {smallBL} {smallBR}");
- Console.WriteLine($"Large Diagonals: {largeTL} {largeTR} {largeBL} {largeBR}");
- return count;
- }
- private IEnumerable<Vec> Surrounding(Vec middle)
- {
- yield return middle.Add(ScreenSpace.Up);
- yield return middle.Add(ScreenSpace.Right);
- yield return middle.Add(ScreenSpace.Down);
- yield return middle.Add(ScreenSpace.Left);
- }
- private Tile? At(Vec p)
- {
- if (p.X < 0 || p.X >= _width || p.Y < 0 || p.Y >= _height)
- {
- return null;
- }
- return _map[p.X, p.Y];
- }
- private long Square(long num)
- {
- return num * num;
- }
-
- private (long Even, long Odd) PassableTileCount()
- {
- // Note: there is ONE single unreachable tile that is not a rock, but surrounded by rocks, so we have to
- // "fill" to find the correct numbers
- return (ReachableTiles(_width + 1), ReachableTiles(_width));
- }
- }
|