Skip to content

Commit 974c3b6

Browse files
committed
Solve 2024 day 14
1 parent d6649a3 commit 974c3b6

File tree

3 files changed

+669
-0
lines changed

3 files changed

+669
-0
lines changed

2024.Tests/Day14Tests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Basje.AdventOfCode.Y2024.D14;
2+
3+
namespace Basje.AdventOfCode.Y2024.Tests;
4+
5+
public static class Day14Tests
6+
{
7+
[Fact]
8+
public static void Part1()
9+
{
10+
var solution = new Day14();
11+
const string input = """
12+
p=0,4 v=3,-3
13+
p=6,3 v=-1,-3
14+
p=10,3 v=-1,2
15+
p=2,0 v=2,-1
16+
p=0,0 v=1,3
17+
p=3,0 v=-2,-2
18+
p=7,6 v=-1,-3
19+
p=3,0 v=-1,-2
20+
p=9,3 v=2,3
21+
p=7,3 v=-1,2
22+
p=2,4 v=2,-3
23+
p=9,5 v=-3,-3
24+
""";
25+
26+
var answer = solution.SolvePart1(input);
27+
28+
answer.Should().Be(12);
29+
}
30+
}

2024/D14/Day14.cs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
namespace Basje.AdventOfCode.Y2024.D14;
2+
3+
public class Day14 : Solution<((int X, int Y) Position, (int X, int Y) Velocity)[]>
4+
{
5+
protected override ((int X, int Y) Position, (int X, int Y) Velocity)[] ParseInput(string input)
6+
{
7+
return input
8+
.PerLine()
9+
.IgnoreEmptyLines()
10+
.Select(line =>
11+
line.Replace("p=", "")
12+
.Split(" v=")
13+
.IgnoreEmptyLines()
14+
.ToArray()
15+
)
16+
.Select(line => (
17+
Position: line[0].Split(",").Select(int.Parse).ToArray(),
18+
Velocity: line[1].Split(",").Select(int.Parse).ToArray())
19+
)
20+
.Select(line => (
21+
Position: (X: line.Position[0], Y: line.Position[1]),
22+
Velocity: (X: line.Velocity[0], Y: line.Velocity[1]))
23+
)
24+
.ToArray();
25+
}
26+
27+
protected override object SolvePart1(((int X, int Y) Position, (int X, int Y) Velocity)[] list)
28+
{
29+
var height = list.Select(robot => robot.Position.Y).Max() + 1;
30+
var width = list.Select(robot => robot.Position.X).Max() + 1;
31+
32+
List<(int X, int Y)> robots = [];
33+
34+
foreach (var robot in list)
35+
{
36+
var newPosition = (
37+
X: robot.Position.X + 100 * robot.Velocity.X,
38+
Y: robot.Position.Y + 100 * robot.Velocity.Y);
39+
40+
newPosition = (X: newPosition.X.Modulo(width), Y: newPosition.Y.Modulo(height));
41+
42+
robots.Add(newPosition);
43+
}
44+
45+
robots.RemoveAll(robot => robot.X == width / 2 || robot.Y == height / 2);
46+
47+
var nw = robots.Count(robot => robot.X <= width / 2 && robot.Y <= height / 2);
48+
var ne = robots.Count(robot => robot.X > width / 2 && robot.Y <= height / 2);
49+
var sw = robots.Count(robot => robot.X <= width / 2 && robot.Y > height / 2);
50+
var se = robots.Count(robot => robot.X > width / 2 && robot.Y > height / 2);
51+
52+
return nw * ne * sw * se;
53+
}
54+
55+
protected override object SolvePart2(((int X, int Y) Position, (int X, int Y) Velocity)[] list)
56+
{
57+
var height = list.Select(robot => robot.Position.Y).Max() + 1;
58+
var width = list.Select(robot => robot.Position.X).Max() + 1;
59+
60+
var treeFound = false;
61+
var i = 1;
62+
// var key = '.';
63+
64+
while (!treeFound)
65+
{
66+
67+
List<(int X, int Y)> robots = [];
68+
69+
foreach (var robot in list)
70+
{
71+
var newPosition = (
72+
X: robot.Position.X + i * robot.Velocity.X,
73+
Y: robot.Position.Y + i * robot.Velocity.Y);
74+
75+
newPosition = (X: newPosition.X.Modulo(width), Y: newPosition.Y.Modulo(height));
76+
77+
robots.Add(newPosition);
78+
}
79+
80+
// First wrote a visualiser. Then looked through the output and
81+
// noticed that sometimes the robots seemed to cluster. Based on
82+
// clustering I played with values of number of robots on the same
83+
// line. I visually found tree, after which I updated the search
84+
// criteria.
85+
treeFound = robots
86+
.GroupBy(robot => robot.Y)
87+
.Count(line => line.Count() >= 31) == 2
88+
&& robots
89+
.GroupBy(robot => robot.X)
90+
.Count(column => column.Count() >= 33) == 2;
91+
92+
// if (treeFound)
93+
// {
94+
// Console.WriteLine($"i: {i}");
95+
// robots.Print(width, height);
96+
// key = Console.ReadKey().KeyChar;
97+
// }
98+
99+
i++;
100+
}
101+
return i;
102+
}
103+
}
104+
105+
internal static class Day14Extensions
106+
{
107+
// Apparently the % operator in C# is not an actual modulo, but the remainder.
108+
// So we need to make a correct modulo that works with negative numbers too.
109+
// Source: https://stackoverflow.com/a/6400477
110+
internal static int Modulo(this int a, double b)
111+
{
112+
return (int)Math.Round(a - b * Math.Floor(a / b));
113+
}
114+
115+
internal static void Print(this List<(int X, int Y)> robots, int width, int height)
116+
{
117+
for (var y = 0; y < height; y++)
118+
{
119+
for (var x = 0; x < width; x++)
120+
{
121+
var character = '.';
122+
if (robots.Contains((X: x, Y: y)))
123+
{
124+
character = 'X';
125+
}
126+
Console.Write(character);
127+
if (x == width - 1)
128+
{
129+
Console.WriteLine();
130+
}
131+
}
132+
133+
if (y == height - 1)
134+
{
135+
Console.WriteLine();
136+
}
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)