Map.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. namespace Day21;
  2. public class Map
  3. {
  4. private readonly int _width;
  5. private readonly int _height;
  6. private readonly Tile[,] _map;
  7. private readonly Tile? _start;
  8. public Map(int width, int height, IEnumerable<Tile> tiles)
  9. {
  10. _width = width;
  11. _height = height;
  12. _map = new Tile[width, height];
  13. var i = 0;
  14. foreach (var t in tiles)
  15. {
  16. if (t.IsStart)
  17. {
  18. _start = t;
  19. }
  20. t.Position = new Vec(i % width, i / width);
  21. _map[t.Position.X, t.Position.Y] = t;
  22. i++;
  23. }
  24. if (_start == null)
  25. {
  26. throw new Exception("No start tile found");
  27. }
  28. }
  29. public void Print(ISet<Vec>? mark = null)
  30. {
  31. for (int y = 0; y < _height; y++)
  32. {
  33. for (int x = 0; x < _width; x++)
  34. {
  35. if (mark != null && mark.Contains(new Vec(x, y)))
  36. {
  37. Console.Write('O');
  38. }
  39. else
  40. {
  41. Console.Write(_map[x, y]);
  42. }
  43. }
  44. Console.WriteLine();
  45. }
  46. Console.WriteLine();
  47. }
  48. public long ReachableTiles(long steps, Vec? start = null, bool print = false)
  49. {
  50. start ??= _start!.Position;
  51. var reachable = new HashSet<Vec>();
  52. reachable.Add(start.Value);
  53. for (int i = 0; i < steps; i++)
  54. {
  55. var next = new HashSet<Vec>();
  56. foreach (var p in reachable)
  57. {
  58. foreach (var neighbor in Surrounding(p).Where(n => At(n)?.IsWalkable ?? false))
  59. {
  60. next.Add(neighbor);
  61. }
  62. }
  63. reachable = next;
  64. }
  65. if (print)
  66. {
  67. Print(reachable);
  68. }
  69. return reachable.Count;
  70. }
  71. public long ReachableLarge(long steps)
  72. {
  73. var fullMap = PassableTileCount();
  74. Console.WriteLine($"Width: {_width} | Height: {_height}");
  75. Console.WriteLine($"Odd Reachable: {fullMap.Odd} | Even Reachable: {fullMap.Even}");
  76. var even = steps % 2 == 0;
  77. Console.WriteLine($"Step count is even: {even}");
  78. var count = 0L;
  79. var fullChunks = ((steps - (_width / 2)) / _width) - 1;
  80. Console.WriteLine($"Full chunks after center: {fullChunks}");
  81. // round UP to the nearest multiple of 2
  82. count += Square(fullChunks / 2 * 2 + 1) * fullMap.Odd;
  83. // round DOWN to the nearest multiple of 2
  84. count += Square((fullChunks + 1) / 2 * 2) * fullMap.Even;
  85. var majorSteps = (long)(_width + _width / 2 - 1);
  86. var minorSteps = (long)(_width / 2 - 1);
  87. var cornerT = ReachableTiles(_height - 1, new Vec(_width / 2, _height - 1));
  88. count += cornerT;
  89. var cornerB = ReachableTiles(_height - 1, new Vec(_width / 2, 0));
  90. count += cornerB;
  91. var cornerR = ReachableTiles(_width - 1, new Vec(_width - 1, _height / 2));
  92. count += cornerR;
  93. var cornerL = ReachableTiles(_width - 1, new Vec(0, _height / 2));
  94. count += cornerL;
  95. Console.WriteLine($"Corners: {cornerT} {cornerR} {cornerB} {cornerL}");
  96. var largeBL = ReachableTiles(majorSteps, new Vec(0, _height - 1));
  97. count += fullChunks * largeBL;
  98. var smallBL = ReachableTiles(minorSteps, new Vec(0, _height - 1));
  99. count += (fullChunks + 1) * smallBL;
  100. var largeTL = ReachableTiles(majorSteps, new Vec(0, 0));
  101. count += fullChunks * largeTL;
  102. var smallTL = ReachableTiles(minorSteps, new Vec(0, 0));
  103. count += (fullChunks + 1) * smallTL;
  104. var largeTR = ReachableTiles(majorSteps, new Vec(_width - 1, 0));
  105. count += fullChunks * largeTR;
  106. var smallTR = ReachableTiles(minorSteps, new Vec(_width - 1, 0));
  107. count += (fullChunks + 1) * smallTR;
  108. var largeBR = ReachableTiles(majorSteps, new Vec(_width - 1, _height - 1));
  109. count += fullChunks * largeBR;
  110. var smallBR = ReachableTiles(minorSteps, new Vec(_width - 1, _height - 1));
  111. count += (fullChunks + 1) * smallBR;
  112. Console.WriteLine($"Small Diagonals: {smallTL} {smallTR} {smallBL} {smallBR}");
  113. Console.WriteLine($"Large Diagonals: {largeTL} {largeTR} {largeBL} {largeBR}");
  114. return count;
  115. }
  116. private IEnumerable<Vec> Surrounding(Vec middle)
  117. {
  118. yield return middle.Add(ScreenSpace.Up);
  119. yield return middle.Add(ScreenSpace.Right);
  120. yield return middle.Add(ScreenSpace.Down);
  121. yield return middle.Add(ScreenSpace.Left);
  122. }
  123. private Tile? At(Vec p)
  124. {
  125. if (p.X < 0 || p.X >= _width || p.Y < 0 || p.Y >= _height)
  126. {
  127. return null;
  128. }
  129. return _map[p.X, p.Y];
  130. }
  131. private long Square(long num)
  132. {
  133. return num * num;
  134. }
  135. private (long Even, long Odd) PassableTileCount()
  136. {
  137. // Note: there is ONE single unreachable tile that is not a rock, but surrounded by rocks, so we have to
  138. // "fill" to find the correct numbers
  139. return (ReachableTiles(_width + 1), ReachableTiles(_width));
  140. }
  141. }