WIP AdventOfCode 2023 Day24 Part2
This commit is contained in:
@@ -72,6 +72,44 @@ However, you'll need to search a much larger test area if you want to see if any
|
||||
|
||||
Considering only the X and Y axes, check all pairs of hailstones' future paths for intersections. How many of these intersections occur within the test area?
|
||||
|
||||
--- Part Two ---
|
||||
|
||||
Upon further analysis, it doesn't seem like any hailstones will naturally collide. It's up to you to fix that!
|
||||
|
||||
You find a rock on the ground nearby. While it seems extremely unlikely, if you throw it just right, you should be able to hit every hailstone in a single throw!
|
||||
|
||||
You can use the probably-magical winds to reach any integer position you like and to propel the rock at any integer velocity. Now including the Z axis in your calculations, if you throw the rock at time 0, where do you need to be so that the rock perfectly collides with every hailstone? Due to probably-magical inertia, the rock won't slow down or change direction when it collides with a hailstone.
|
||||
|
||||
In the example above, you can achieve this by moving to position 24, 13, 10 and throwing the rock at velocity -3, 1, 2. If you do this, you will hit every hailstone as follows:
|
||||
|
||||
Hailstone: 19, 13, 30 @ -2, 1, -2
|
||||
Collision time: 5
|
||||
Collision position: 9, 18, 20
|
||||
|
||||
Hailstone: 18, 19, 22 @ -1, -1, -2
|
||||
Collision time: 3
|
||||
Collision position: 15, 16, 16
|
||||
|
||||
Hailstone: 20, 25, 34 @ -2, -2, -4
|
||||
Collision time: 4
|
||||
Collision position: 12, 17, 18
|
||||
|
||||
Hailstone: 12, 31, 28 @ -1, -2, -1
|
||||
Collision time: 6
|
||||
Collision position: 6, 19, 22
|
||||
|
||||
Hailstone: 20, 19, 15 @ 1, -5, -3
|
||||
Collision time: 1
|
||||
Collision position: 21, 14, 12
|
||||
|
||||
Above, each hailstone is identified by its initial position and its velocity. Then, the time and position of that hailstone's collision with your rock are given.
|
||||
|
||||
After 1 nanosecond, the rock has exactly the same position as one of the hailstones, obliterating it into ice dust! Another hailstone is smashed to bits two nanoseconds after that. After a total of 6 nanoseconds, all of the hailstones have been destroyed.
|
||||
|
||||
So, at time 0, the rock needs to be at X position 24, Y position 13, and Z position 10. Adding these three coordinates together produces 47. (Don't add any coordinates from the rock's velocity.)
|
||||
|
||||
Determine the exact position and velocity the rock needs to have at time 0 so that it perfectly collides with every hailstone. What do you get if you add up the X, Y, and Z coordinates of that initial position?
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@@ -88,10 +126,15 @@ public class Day24 : IDay
|
||||
{
|
||||
for (int j = i + 1; j < hails.Length; j++)
|
||||
{
|
||||
(bool intersects, double s, double t, Vector3D point) = hails[i].Intersect2D(hails[j]);
|
||||
if (intersects && s > 0 && t > 0 && point.X >= min && point.X <= max && point.Y >= min && point.Y <= max)
|
||||
(bool intersects, double s, double t) = hails[i].Intersect2D(hails[j]);
|
||||
if (intersects && s > 0 && t > 0)
|
||||
{
|
||||
count++;
|
||||
long x = (long)(hails[i].Position.X + t * hails[i].Velocity.X);
|
||||
long y = (long)(hails[i].Position.Y + t * hails[i].Velocity.Y);
|
||||
if (x >= min && x <= max && y >= min && y <= max)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,10 +143,207 @@ public class Day24 : IDay
|
||||
|
||||
public string ResolvePart2(string[] inputs)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
string result = ResolvePart2_SolutionInDataSet(inputs);
|
||||
if (string.IsNullOrEmpty(result) == false)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result = ResolvePart2_BruteForceXY(inputs);
|
||||
if (string.IsNullOrEmpty(result) == false)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public struct Vector3D
|
||||
|
||||
public string ResolvePart2_BruteForceXY(string[] inputs)
|
||||
{
|
||||
Hail[] hails = inputs.Select(input => new Hail(input)).ToArray();
|
||||
|
||||
// Brute force solution
|
||||
long absMax = hails
|
||||
.Select(h => new[] { h.Velocity.X, h.Velocity.Y, h.Velocity.Z, })
|
||||
.SelectMany(x => x).Select(Math.Abs)
|
||||
.Max();
|
||||
Console.WriteLine($"Velocity AbsMax: {absMax}");
|
||||
long minV = -absMax;
|
||||
long maxV = absMax;
|
||||
Vector3D? collisionPoint = null;
|
||||
for (long vy = minV; vy < maxV && collisionPoint == null; vy++)
|
||||
{
|
||||
for (long vx = minV; vx < maxV && collisionPoint == null; vx++)
|
||||
{
|
||||
Vector3D newVelocity0 = new(
|
||||
x: hails[0].Velocity.X - vx,
|
||||
y: hails[0].Velocity.Y - vy,
|
||||
z: hails[0].Velocity.Z);
|
||||
Hail shiftedHail0 = new(hails[0].Position, newVelocity0);
|
||||
Vector3D newVelocity1 = new(
|
||||
x: hails[1].Velocity.X - vx,
|
||||
y: hails[1].Velocity.Y - vy,
|
||||
z: hails[1].Velocity.Z);
|
||||
Hail shiftedHail1 = new(hails[1].Position, newVelocity1);
|
||||
(bool intersects, double s, double t) = shiftedHail0.Intersect2D(shiftedHail1);
|
||||
if (intersects == false || s < 0.0f || t < 0.0) { continue; }
|
||||
|
||||
long x = (long)(shiftedHail0.Position.X + t * shiftedHail0.Velocity.X);
|
||||
long y = (long)(shiftedHail0.Position.Y + t * shiftedHail0.Velocity.Y);
|
||||
Vector3D firstCollisionPoint = new(x, y, 0);
|
||||
//Console.WriteLine($"Possible Rock: {vx} {vy} | {firstCollisionPoint.X}, {firstCollisionPoint.Y}, {firstCollisionPoint.Z} @ {vx}, {vy}, ?? | {s} {t}");
|
||||
Hail[] shiftedHails = hails.Select(h =>
|
||||
{
|
||||
Vector3D newVelocity = new(
|
||||
x: h.Velocity.X - vx,
|
||||
y: h.Velocity.Y - vy,
|
||||
z: h.Velocity.Z);
|
||||
Hail newHail = new(h.Position, newVelocity);
|
||||
return newHail;
|
||||
}).ToArray();
|
||||
Vector3D currentCollisionPoint = firstCollisionPoint;
|
||||
bool allCollided = true;
|
||||
for (int i = 2; i < shiftedHails.Length; i++)
|
||||
{
|
||||
(bool intersectsIter, double sIter, double tIter) = shiftedHails[0].Intersect2D(shiftedHails[i]);
|
||||
long xIter = (long)(shiftedHail0.Position.X + tIter * shiftedHail0.Velocity.X);
|
||||
long yIter = (long)(shiftedHail0.Position.Y + tIter * shiftedHail0.Velocity.Y);
|
||||
Vector3D collisionPointIter = new(xIter, yIter, 0);
|
||||
if (
|
||||
intersectsIter &&
|
||||
currentCollisionPoint.Cmp2D(collisionPointIter) &&
|
||||
true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
allCollided = false;
|
||||
//Console.WriteLine($"... Invalid rock: {i}");
|
||||
break;
|
||||
}
|
||||
if (allCollided)
|
||||
{
|
||||
long xCollision = (long)(hails[0].Position.X + t * hails[0].Velocity.X);
|
||||
long yCollision = (long)(hails[0].Position.Y + t * hails[0].Velocity.Y);
|
||||
long zCollision = (long)(hails[0].Position.Z + t * hails[0].Velocity.Z);
|
||||
Console.WriteLine($"Possible Rock: {xCollision}, {yCollision}, {zCollision} @ {vx}, {vy}, ?? | {t}");
|
||||
collisionPoint = new Vector3D(xCollision, yCollision, zCollision);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (collisionPoint == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return (collisionPoint.Value.X + collisionPoint.Value.Y + collisionPoint.Value.Z).ToString();
|
||||
}
|
||||
|
||||
private string ResolvePart2_BruteForceXYZ(string[] inputs)
|
||||
{
|
||||
Hail[] hails = inputs.Select(input => new Hail(input)).ToArray();
|
||||
|
||||
// Brute force solution
|
||||
long absMax = hails
|
||||
.Select(h => new[] { h.Velocity.X, h.Velocity.Y, h.Velocity.Z, })
|
||||
.SelectMany(x => x).Select(Math.Abs)
|
||||
.Max();
|
||||
Console.WriteLine($"Velocity AbsMax: {absMax}");
|
||||
long minV = -absMax;
|
||||
long maxV = absMax;
|
||||
Vector3D? collisionPoint = null;
|
||||
for (long vz = minV; vz < maxV && collisionPoint == null; vz++)
|
||||
{
|
||||
Console.WriteLine($"Brute forcing Z layer {vz}");
|
||||
for (long vy = minV; vy < maxV && collisionPoint == null; vy++)
|
||||
{
|
||||
for (long vx = minV; vx < maxV && collisionPoint == null; vx++)
|
||||
{
|
||||
Vector3D newVelocity0 = new(
|
||||
x: hails[0].Velocity.X - vx,
|
||||
y: hails[0].Velocity.Y - vy,
|
||||
z: hails[0].Velocity.Z - vz);
|
||||
Hail shiftedHail0 = new(hails[0].Position, newVelocity0);
|
||||
Vector3D newVelocity1 = new(
|
||||
x: hails[1].Velocity.X - vx,
|
||||
y: hails[1].Velocity.Y - vy,
|
||||
z: hails[1].Velocity.Z - vz);
|
||||
Hail shiftedHail1 = new(hails[1].Position, newVelocity1);
|
||||
(bool intersects, double t, Vector3D? firstCollisionPoint) = shiftedHail0.Intersect3D(shiftedHail1, sameT: false);
|
||||
if (intersects == false || firstCollisionPoint == null || t < 0.0) { continue; }
|
||||
|
||||
//Console.WriteLine($"Possible Rock: {firstCollisionPoint.Value.X}, {firstCollisionPoint.Value.Y}, {firstCollisionPoint.Value.Z} @ {vx}, {vy}, {vz} | {t}");
|
||||
Hail[] shiftedHails = hails.Select(h =>
|
||||
{
|
||||
Vector3D newVelocity = new(
|
||||
x: h.Velocity.X - vx,
|
||||
y: h.Velocity.Y - vy,
|
||||
z: h.Velocity.Z - vz);
|
||||
Hail newHail = new(h.Position, newVelocity);
|
||||
return newHail;
|
||||
}).ToArray();
|
||||
Vector3D currentCollisionPoint = firstCollisionPoint.Value;
|
||||
bool allCollided = true;
|
||||
for (int i = 2; i < shiftedHails.Length; i++)
|
||||
{
|
||||
(bool intersectsIter, double tIter, Vector3D? collisionPointIter) = shiftedHails[0].Intersect3D(shiftedHails[i], sameT: false);
|
||||
if (
|
||||
intersectsIter &&
|
||||
tIter >= -0.001f &&
|
||||
collisionPointIter != null &&
|
||||
currentCollisionPoint.Cmp3D(collisionPointIter.Value) &&
|
||||
true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
allCollided = false;
|
||||
//Console.WriteLine($"... Invalid rock: {i}");
|
||||
break;
|
||||
}
|
||||
if (allCollided)
|
||||
{
|
||||
collisionPoint = currentCollisionPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return collisionPoint != null
|
||||
? (collisionPoint.Value.X + collisionPoint.Value.Y + collisionPoint.Value.Z).ToString()
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
private string ResolvePart2_SolutionInDataSet(string[] inputs)
|
||||
{
|
||||
Hail[] hails = inputs.Select(input => new Hail(input)).ToArray();
|
||||
|
||||
// Check if any hail is doing the same as the rock
|
||||
Hail? bestMatch = null;
|
||||
for (int i = 0; i < hails.Length; i++)
|
||||
{
|
||||
bool allCollided = true;
|
||||
for (int j = 0; j < hails.Length; j++)
|
||||
{
|
||||
if (i == j) { continue; }
|
||||
(bool intersects, _, _) = hails[i].Intersect3D(hails[j]);
|
||||
if (intersects) { continue; }
|
||||
|
||||
allCollided = false;
|
||||
break;
|
||||
}
|
||||
if (allCollided)
|
||||
{
|
||||
bestMatch = hails[i];
|
||||
}
|
||||
}
|
||||
if (bestMatch != null)
|
||||
{
|
||||
return (bestMatch.Value.Position.X + bestMatch.Value.Position.Y + bestMatch.Value.Position.Z).ToString();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public readonly struct Vector3D
|
||||
{
|
||||
public readonly long X;
|
||||
public readonly long Y;
|
||||
@@ -115,6 +355,16 @@ public class Day24 : IDay
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
public bool Cmp2D(Vector3D other)
|
||||
{
|
||||
return X == other.X && Y == other.Y;
|
||||
}
|
||||
|
||||
public bool Cmp3D(Vector3D other)
|
||||
{
|
||||
return X == other.X && Y == other.Y && Z == other.Z;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct Hail
|
||||
@@ -122,6 +372,12 @@ public class Day24 : IDay
|
||||
public readonly Vector3D Position;
|
||||
public readonly Vector3D Velocity;
|
||||
|
||||
public Hail(Vector3D position, Vector3D velocity)
|
||||
{
|
||||
Position = position;
|
||||
Velocity = velocity;
|
||||
}
|
||||
|
||||
public Hail(string input)
|
||||
{
|
||||
string[] parts = input.Split(" @ ");
|
||||
@@ -137,24 +393,43 @@ public class Day24 : IDay
|
||||
z: Convert.ToInt64(strVelocity[2]));
|
||||
}
|
||||
|
||||
public (bool intersects, double s, double t, Vector3D point) Intersect2D(Hail other)
|
||||
public (bool intersects, double s, double t) Intersect2D(Hail other)
|
||||
{
|
||||
long s_Div = (-other.Velocity.X * Velocity.Y + Velocity.X * other.Velocity.Y);
|
||||
if (s_Div == 0)
|
||||
{
|
||||
return (false, 0, 0, new Vector3D());
|
||||
return (false, 0, 0);
|
||||
}
|
||||
double s = (-Velocity.Y * (Position.X - other.Position.X) + Velocity.X * (Position.Y - other.Position.Y)) / (double)s_Div;
|
||||
|
||||
long t_Div = (-other.Velocity.X * Velocity.Y + Velocity.X * other.Velocity.Y);
|
||||
if (t_Div == 0)
|
||||
{
|
||||
return (false, 0, 0, new Vector3D());
|
||||
return (false, 0, 0);
|
||||
}
|
||||
double t = (other.Velocity.X * (Position.Y - other.Position.Y) - other.Velocity.Y * (Position.X - other.Position.X)) / (double)t_Div;
|
||||
|
||||
Vector3D intersection = new((long)(Position.X + t * Velocity.X), (long)(Position.Y + t * Velocity.Y), 0);
|
||||
return (true, s, t, intersection);
|
||||
return (true, s, t);
|
||||
}
|
||||
|
||||
public (bool intersects, double t, Vector3D? point) Intersect3D(Hail other, bool sameT = true)
|
||||
{
|
||||
(bool intersects, double s, double t) = Intersect2D(other);
|
||||
if (intersects == false || (sameT && Math.Abs(s - t) > 0.001))
|
||||
{
|
||||
return (false, 0, null);
|
||||
}
|
||||
|
||||
long zThis = (long)(Position.Z + t * Velocity.Z);
|
||||
long zOther = (long)(other.Position.Z + s * other.Velocity.Z);
|
||||
if (Math.Abs(zThis - zOther) > 0.001)
|
||||
{
|
||||
return (false, 0, null);
|
||||
}
|
||||
long yThis = (long)(Position.Y + t * Velocity.Y);
|
||||
long xThis = (long)(Position.X + t * Velocity.X);
|
||||
|
||||
return (true, t, new Vector3D(xThis, yThis, zThis));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user