Advertisement
Guest User

Untitled

a guest
Jan 7th, 2024
218
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 6.36 KB | Source Code | 0 0
  1. using Tools;
  2.  
  3. namespace Solutions
  4. {
  5.   public class Day17
  6.   {
  7.  
  8.     public enum Direction
  9.     {
  10.       UP,
  11.       DOWN,
  12.       LEFT,
  13.       RIGHT
  14.     }
  15.  
  16.     class Node
  17.     {
  18.       public int x { get; }
  19.       public int y { get; }
  20.       public int heatloss { get; }
  21.       public Node(int x, int y, int h)
  22.       {
  23.         this.x = x;
  24.         this.y = y;
  25.         heatloss = h;
  26.       }
  27.  
  28.       public override string ToString()
  29.       {
  30.         return $"{x},{y}: {heatloss}";
  31.       }
  32.     }
  33.     List<string> strings = new List<string>();
  34.     List<List<Node>> grid = new();
  35.  
  36.     public Day17(string fileName)
  37.     {
  38.       strings = FileReader.AsStringArray(fileName).ToList();
  39.       foreach ((string line, int x) in strings.WithIndex())
  40.       {
  41.         List<Node> newLine = new();
  42.         foreach ((char number, int y) in line.ToArray().WithIndex())
  43.         {
  44.           newLine.Add(new Node(x, y, int.Parse(number.ToString())));
  45.         }
  46.         grid.Add(newLine);
  47.       }
  48.     }
  49.  
  50.     public int PartOne()
  51.     {
  52.       (int x, int y) start = (0, 0);
  53.       (int x, int y) end = (grid.Count - 1, grid.First().Count - 1);
  54.       Dictionary<string, Node?> pathRecord = AStarPath(grid[start.x][start.y], grid[end.x][end.y]);
  55.  
  56.       // Starting from end, work way backwards to build path.
  57.       List<Node> path = new List<Node>();
  58.       Node? current = pathRecord[$"{end.x},{end.y}"];
  59.       while (current != null)
  60.       {
  61.         Console.WriteLine(current.ToString());
  62.         path.Add(current);
  63.         current = pathRecord[$"{current.x},{current.y}"];
  64.       }
  65.       // Console.WriteLine(pathRecord[$"12,12"]);
  66.       return path.Aggregate(0, (acc, curr) => acc + curr.heatloss);
  67.     }
  68.  
  69.     public int PartTwo()
  70.     {
  71.       return -1;
  72.     }
  73.  
  74.     private Dictionary<string, Node?> AStarPath(Node start, Node end)
  75.     {
  76.       // Set up priority queue for A* algorithm
  77.       // High priority is bad, comes later
  78.       PriorityQueue<Node, int> frontier = new PriorityQueue<Node, int>();
  79.       frontier.Enqueue(start, grid[start.x][start.y].heatloss);
  80.       // Dictionary keeps track of where we came from
  81.       Dictionary<string, Node?> cameFrom = new Dictionary<string, Node?>();
  82.       cameFrom[$"{start.x},{start.y}"] = null;
  83.       // Dictionary keeps track of cost so far per node
  84.       Dictionary<string, int> costSoFar = new Dictionary<string, int>();
  85.       costSoFar[$"{start.x},{start.y}"] = 0;
  86.  
  87.       while (frontier.Count > 0)
  88.       {
  89.         Node current = frontier.Dequeue();
  90.  
  91.         // if (start.x == end.x && start.y == end.y)
  92.         // {
  93.         //   cameFrom[$"{end.x},{end.y}"] = current;
  94.         //   return cameFrom;
  95.         // }
  96.  
  97.         // Get all neighbouring nodes
  98.         List<Node> neighbours = GetNeighbours(current);
  99.         // For each node, determine priority and add to frontier
  100.         foreach (Node next in neighbours)
  101.         {
  102.           // New cost is what we've done so far plus the heat cost of next Node
  103.           int newCost = costSoFar[$"{current.x},{current.y}"] + next.heatloss;
  104.           // Check if we've already moved three consecutive spaces
  105.           int consecutiveMoves = ConsecutiveMoves(current, cameFrom[$"{current.x},{current.y}"], cameFrom);
  106.           // Console.WriteLine($"Consecutive moves: {consecutiveMoves}");
  107.           const int MAX_CONSECUTIVE_MOVES = 3;
  108.           // If we haven't already done 3 consecutive moves in this direction... then
  109.           // If the next node isn't the node we just came from... then
  110.           // If we haven't visited this node, or if this new cost is better than a previous route to this node
  111.           if (
  112.             consecutiveMoves < MAX_CONSECUTIVE_MOVES &&
  113.             (cameFrom[$"{current.x},{current.y}"] == null || !(next.x == cameFrom[$"{current.x},{current.y}"].x && next.y == cameFrom[$"{current.x},{current.y}"].y)) &&
  114.             (!costSoFar.ContainsKey($"{next.x},{next.y}") || newCost < costSoFar[$"{next.x},{next.y}"])
  115.           )
  116.           {
  117.             // Set the cost
  118.             costSoFar[$"{next.x},{next.y}"] = newCost;
  119.             // Determine priority and store in queue
  120.             int priority = newCost; //+ ConsecutiveMovePenalty(next, end);
  121.             frontier.Enqueue(next, priority);
  122.             // Mark that the next node came from the current node
  123.             cameFrom[$"{next.x},{next.y}"] = current;
  124.           }
  125.         }
  126.       }
  127.       return cameFrom;
  128.     }
  129.  
  130.     private int ConsecutiveMoves(Node current, Node? previous, Dictionary<string, Node?> cameFrom, Direction? d = null)
  131.     {
  132.       // Console.WriteLine($"Current: {current.ToString()}");
  133.       // Console.WriteLine($"previous: {(previous != null ? previous.ToString() : "null")}");
  134.       // Console.WriteLine(d.ToString());
  135.  
  136.       if (previous == null) return 0;
  137.       // What direction are we coming from?
  138.       if (current.x - previous.x == 1 && (d == null || d == Direction.UP))
  139.       {
  140.         // up
  141.         return 1 + ConsecutiveMoves(previous, cameFrom[$"{previous.x},{previous.y}"], cameFrom, Direction.UP);
  142.       }
  143.       else if (current.x - previous.x == -1 && (d == null || d == Direction.DOWN))
  144.       {
  145.         // down
  146.         return 1 + ConsecutiveMoves(previous, cameFrom[$"{previous.x},{previous.y}"], cameFrom, Direction.DOWN);
  147.       }
  148.       else if (current.y - previous.y == 1 && (d == null || d == Direction.LEFT))
  149.       {
  150.         // left
  151.         return 1 + ConsecutiveMoves(previous, cameFrom[$"{previous.x},{previous.y}"], cameFrom, Direction.LEFT);
  152.       }
  153.       else if (current.y - previous.y == -1 && (d == null || d == Direction.RIGHT))
  154.       {
  155.         // right
  156.         return 1 + ConsecutiveMoves(previous, cameFrom[$"{previous.x},{previous.y}"], cameFrom, Direction.RIGHT);
  157.       }
  158.       return 0;
  159.     }
  160.  
  161.     private List<Node> GetNeighbours(Node current)
  162.     {
  163.       List<Node> neighbours = new List<Node>();
  164.       // above
  165.       if (current.x > 0)
  166.       {
  167.         neighbours.Add(grid[current.x - 1][current.y]);
  168.       }
  169.       // below
  170.       if (current.x < grid.Count - 1)
  171.       {
  172.         neighbours.Add(grid[current.x + 1][current.y]);
  173.       }
  174.       // left
  175.       if (current.y > 0)
  176.       {
  177.         neighbours.Add(grid[current.x][current.y - 1]);
  178.       }
  179.       // right
  180.       if (current.y < grid.First().Count - 1)
  181.       {
  182.         neighbours.Add(grid[current.x][current.y + 1]);
  183.       }
  184.       return neighbours;
  185.     }
  186.   }
  187. }
  188.  
  189.  
  190.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement