BoulderMap.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. using System.Text;
  2. namespace Day14;
  3. public class BoulderMap
  4. {
  5. private readonly char[][] _lines;
  6. private readonly IAxis _north;
  7. private readonly IAxis _east;
  8. private readonly IAxis _south;
  9. private readonly IAxis _west;
  10. private readonly Dictionary<string, int> _keyMap = new Dictionary<string, int>();
  11. public BoulderMap(IList<string> lines)
  12. {
  13. _lines = lines.Select(l => l.ToCharArray()).ToArray();
  14. _north = new NorthSouth(_lines, 1);
  15. _east = new EastWest(_lines, -1);
  16. _south = new NorthSouth(_lines, -1);
  17. _west = new EastWest(_lines, 1);
  18. }
  19. public int CalculateMaxNorthLoad()
  20. {
  21. return CalculateDirectionalLoad(_north);
  22. }
  23. private int CalculateDirectionalLoad(IAxis axis)
  24. {
  25. var load = 0;
  26. foreach (var s in axis.SecondaryRange)
  27. {
  28. var free = axis.PrimaryRange.First();
  29. foreach (var p in axis.PrimaryRange)
  30. {
  31. switch (axis.Get(p, s))
  32. {
  33. case 'O':
  34. if (p != free)
  35. {
  36. axis.Swap(p, free, s);
  37. }
  38. load += axis.NorthLoad(free, s);
  39. free += axis.Delta;
  40. break;
  41. case '#':
  42. free = p + axis.Delta;
  43. break;
  44. }
  45. }
  46. }
  47. // foreach (var l in _lines)
  48. // {
  49. // Console.WriteLine(l);
  50. // }
  51. // Console.WriteLine();
  52. return load;
  53. }
  54. public int Cycle(int n)
  55. {
  56. var loads = new List<int>();
  57. for (var i = 1; i <= n; i++)
  58. {
  59. CalculateDirectionalLoad(_north);
  60. CalculateDirectionalLoad(_west);
  61. CalculateDirectionalLoad(_south);
  62. loads.Add(CalculateDirectionalLoad(_east));
  63. var key = GetKey();
  64. Console.WriteLine($"Key: {key} => {loads.Last()}");
  65. if (_keyMap.TryGetValue(key, out int value))
  66. {
  67. Console.WriteLine($"CYCLE: @{i} from {value}");
  68. var offset = value;
  69. var cycle = i - value;
  70. var remainder = (1000000000 - offset) % cycle;
  71. return loads[loads.Count - (cycle - remainder) - 1];
  72. }
  73. _keyMap[key] = i;
  74. }
  75. return -1;
  76. }
  77. private string GetKey()
  78. {
  79. var result = new StringBuilder();
  80. var start = 0;
  81. var run = 0;
  82. for (var y = 0; y < _lines.Length; y++)
  83. {
  84. for (var x = 0; x < _lines[0].Length; x++)
  85. {
  86. if (_lines[y][x] == 'O')
  87. {
  88. run++;
  89. }
  90. else
  91. {
  92. if (run > 0)
  93. {
  94. result.Append(start);
  95. result.Append(':');
  96. result.Append(run);
  97. result.Append('|');
  98. run = 0;
  99. }
  100. start = y * _lines[0].Length + x;
  101. }
  102. }
  103. }
  104. if (run > 0)
  105. {
  106. result.Append(start);
  107. result.Append(':');
  108. result.Append(run);
  109. result.Append('|');
  110. }
  111. return result.ToString();
  112. }
  113. private interface IAxis
  114. {
  115. int Delta { get; }
  116. List<int> PrimaryRange { get; }
  117. List<int> SecondaryRange { get; }
  118. char Get(int primary, int secondary);
  119. void Swap(int primaryA, int primaryB, int secondary);
  120. int NorthLoad(int primary, int secondary);
  121. }
  122. private class NorthSouth : IAxis
  123. {
  124. private readonly char[][] _lines;
  125. public int Delta { get; }
  126. public List<int> PrimaryRange { get; }
  127. public List<int> SecondaryRange { get; }
  128. public NorthSouth(char[][] lines, int delta)
  129. {
  130. Delta = delta;
  131. _lines = lines;
  132. PrimaryRange = Enumerable.Range(0, _lines.Length).ToList();
  133. if (delta < 0)
  134. {
  135. PrimaryRange.Reverse();
  136. }
  137. SecondaryRange = Enumerable.Range(0, _lines[0].Length).ToList();
  138. }
  139. public char Get(int primary, int secondary)
  140. {
  141. return _lines[primary][secondary];
  142. }
  143. public void Swap(int primaryA, int primaryB, int secondary)
  144. {
  145. (_lines[primaryA][secondary], _lines[primaryB][secondary]) = (_lines[primaryB][secondary], _lines[primaryA][secondary]);
  146. }
  147. public int NorthLoad(int primary, int secondary)
  148. {
  149. return Delta > 0 ? PrimaryRange.Count - primary : primary;
  150. }
  151. }
  152. private class EastWest : IAxis
  153. {
  154. private readonly char[][] _lines;
  155. public int Delta { get; }
  156. public List<int> PrimaryRange { get; }
  157. public List<int> SecondaryRange { get; }
  158. public EastWest(char[][] lines, int delta)
  159. {
  160. Delta = delta;
  161. _lines = lines;
  162. PrimaryRange = Enumerable.Range(0, _lines[0].Length).ToList();
  163. if (delta < 0)
  164. {
  165. PrimaryRange.Reverse();
  166. }
  167. SecondaryRange = Enumerable.Range(0, _lines.Length).ToList();
  168. }
  169. public char Get(int primary, int secondary)
  170. {
  171. return _lines[secondary][primary];
  172. }
  173. public void Swap(int primaryA, int primaryB, int secondary)
  174. {
  175. (_lines[secondary][primaryA], _lines[secondary][primaryB]) = (_lines[secondary][primaryB], _lines[secondary][primaryA]);
  176. }
  177. public int NorthLoad(int primary, int secondary)
  178. {
  179. return SecondaryRange.Count - secondary;
  180. }
  181. }
  182. }