Advertisement
mgla

Advent of Code 2023 - Day 24

Dec 24th, 2023 (edited)
1,672
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 5.19 KB | None | 0 0
  1. using Microsoft.Z3;
  2.  
  3. var input = File.ReadAllLines("input.txt");
  4.  
  5. var min = 200000000000000.0;
  6. var max = 400000000000000.0;
  7.  
  8. var hails = new List<Hail>();
  9.  
  10. foreach (var line in input)
  11. {
  12.     var split = line.Split('@', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
  13.    
  14.     var position = split[0].Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
  15.         .Select(double.Parse).ToArray();
  16.     var velocity = split[1].Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
  17.         .Select(double.Parse).ToArray();
  18.  
  19.     var hail = new Hail(
  20.         new Position(position[0], position[1], position[2]),
  21.         new Position(velocity[0], velocity[1], velocity[2]));
  22.    
  23.     var next = hail.Move();
  24.    
  25.     // We calculate the line equation for the hail's path and store it in the hail object.
  26.     var slope = (next.Position.Y - hail.Position.Y) / (next.Position.X - hail.Position.X);
  27.     var intercept = next.Position.Y - slope * next.Position.X;
  28.  
  29.     hails.Add(hail with { Slope = slope, Intersect = intercept });
  30. }
  31.  
  32. var part1 = 0;
  33. var visited = new HashSet<Hail>();
  34. const int b = -1;
  35. foreach (var first in hails)
  36. {
  37.     visited.Add(first);
  38.     var a1 = first.Slope;
  39.     var c1 = first.Intersect;
  40.     var x1 = first.Position.X;
  41.     var y1 = first.Position.Y;
  42.     var vx1 = first.Velocity.X;
  43.     var vy1 = first.Velocity.Y;
  44.  
  45.     foreach (var second in hails.Where(h => !visited.Contains(h)))
  46.     {
  47.         var a2 = second.Slope;
  48.         var c2 = second.Intersect;
  49.         var x2 = second.Position.X;
  50.         var y2 = second.Position.Y;
  51.         var vx2 = second.Velocity.X;
  52.         var vy2 = second.Velocity.Y;
  53.  
  54.         if (a1 == a2)
  55.         {
  56.             continue; // Parallel
  57.         }
  58.        
  59.         var x = (b * c2 - b * c1) / (a1 * b - a2 * b);
  60.         var y = (a2 * c1 - a1 * c2) / (a1 * b - a2 * b);
  61.        
  62.         if (x - x1 < 0 != vx1 < 0 ||
  63.             y - y1 < 0 != vy1 < 0)
  64.         {
  65.             continue; // Opposite to first's direction
  66.         }
  67.        
  68.         if (x - x2 < 0 != vx2 < 0 ||
  69.             y - y2 < 0 != vy2 < 0)
  70.         {
  71.             continue; // Opposite to second's direction
  72.         }
  73.                
  74.         if (new Position(x, y).InBounds(min, max))
  75.         {
  76.             part1++;
  77.         }
  78.     }
  79. }
  80.  
  81. Console.WriteLine($"Part 1: {part1}");
  82. Console.WriteLine($"Part 2: {Solve(hails)}");
  83. return;
  84.  
  85. // Using Z3 to solve the system of equations. I wanted to do it without using any 3rd party libraries, but I gave up.
  86. // Used this: https://github.com/Z3Prover/z3
  87. static long Solve(List<Hail> hails)
  88. {
  89.     var ctx = new Context();
  90.     var solver = ctx.MkSolver();
  91.  
  92.     // Coordinates of the stone
  93.     var x = ctx.MkIntConst("x");
  94.     var y = ctx.MkIntConst("y");
  95.     var z = ctx.MkIntConst("z");
  96.  
  97.     // Velocity of the stone
  98.     var vx = ctx.MkIntConst("vx");
  99.     var vy = ctx.MkIntConst("vy");
  100.     var vz = ctx.MkIntConst("vz");
  101.  
  102.     // For each iteration, we will add 3 new equations and one new condition to the solver.
  103.     // We want to find 9 variables (x, y, z, vx, vy, vz, t0, t1, t2) that satisfy all the equations, so a system of 9 equations is enough.
  104.     for (var i = 0; i < 3; i++)
  105.     {
  106.         var t = ctx.MkIntConst($"t{i}"); // time for the stone to reach the hail
  107.         var hail = hails[i];
  108.  
  109.         var px = ctx.MkInt(Convert.ToInt64(hail.Position.X));
  110.         var py = ctx.MkInt(Convert.ToInt64(hail.Position.Y));
  111.         var pz = ctx.MkInt(Convert.ToInt64(hail.Position.Z));
  112.        
  113.         var pvx = ctx.MkInt(Convert.ToInt64(hail.Velocity.X));
  114.         var pvy = ctx.MkInt(Convert.ToInt64(hail.Velocity.Y));
  115.         var pvz = ctx.MkInt(Convert.ToInt64(hail.Velocity.Z));
  116.        
  117.         var xLeft = ctx.MkAdd(x, ctx.MkMul(t, vx)); // x + t * vx
  118.         var yLeft = ctx.MkAdd(y, ctx.MkMul(t, vy)); // y + t * vy
  119.         var zLeft = ctx.MkAdd(z, ctx.MkMul(t, vz)); // z + t * vz
  120.  
  121.         var xRight = ctx.MkAdd(px, ctx.MkMul(t, pvx)); // px + t * pvx
  122.         var yRight = ctx.MkAdd(py, ctx.MkMul(t, pvy)); // py + t * pvy
  123.         var zRight = ctx.MkAdd(pz, ctx.MkMul(t, pvz)); // pz + t * pvz
  124.  
  125.         solver.Add(t >= 0); // time should always be positive - we don't want solutions for negative time
  126.         solver.Add(ctx.MkEq(xLeft, xRight)); // x + t * vx = px + t * pvx
  127.         solver.Add(ctx.MkEq(yLeft, yRight)); // y + t * vy = py + t * pvy
  128.         solver.Add(ctx.MkEq(zLeft, zRight)); // z + t * vz = pz + t * pvz
  129.     }
  130.  
  131.     solver.Check();
  132.     var model = solver.Model;
  133.  
  134.     var rx = model.Eval(x);
  135.     var ry = model.Eval(y);
  136.     var rz = model.Eval(z);
  137.  
  138.     return Convert.ToInt64(rx.ToString()) + Convert.ToInt64(ry.ToString()) + Convert.ToInt64(rz.ToString());
  139. }
  140.  
  141. internal record Position(double X, double Y, double Z = 0)
  142. {
  143.     public Position Move(double x, double y, double z) => new(X + x, Y + y, Z + z);
  144.     public bool InBounds(double min, double max) => X >= min && X <= max && Y >= min && Y <= max;
  145. }
  146.  
  147. internal record Hail(Position Position, Position Velocity, double Slope = 0, double Intersect = 0)
  148. {
  149.     public Hail Move() => this with { Position = Position.Move(Velocity.X, Velocity.Y, Velocity.Z) };
  150. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement