Files
advent-of-code/AdventOfCode C#/AOC.Tests/Y2025/Day01.cs
T
2025-12-01 17:49:57 +00:00

166 lines
4.2 KiB
C#

namespace AOC.Tests.Y2025;
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class Day01
{
[SetUp]
public void Setup()
{
realData = File.ReadAllLines(Path.Combine(TestContext.CurrentContext.TestDirectory, "Y2025", "Data",
$"{GetThisClassName()}.dat"));
}
protected string GetThisClassName() { return GetType().Name; }
private string[] realData;
private int TurnDial(int startNumber, char direction, int clicks)
{
int delta = direction switch
{
'R' => clicks,
'L' => -clicks,
_ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null)
};
int finalNumber = (startNumber + delta) % 100;
if (finalNumber < 0)
{
finalNumber += 100;
}
return finalNumber;
}
private int CountZeroHits(int startNumber, char direction, int clicks)
{
if (clicks <= 0)
{
return 0;
}
int firstHit;
if (direction == 'R')
{
// Position after k clicks: (startNumber + k) mod 100 == 0
// => k ≡ -startNumber ≡ 100 - startNumber (mod 100)
firstHit = (100 - startNumber) % 100;
if (firstHit == 0)
{
firstHit = 100; // don't count "k = 0", we only care about actual clicks
}
}
else if (direction == 'L')
{
// Position after k clicks: (startNumber - k) mod 100 == 0
// => k ≡ startNumber (mod 100)
firstHit = startNumber % 100;
if (firstHit == 0)
{
firstHit = 100;
}
}
else
{
throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
}
if (firstHit > clicks)
{
return 0; // we never reach 0 within this rotation
}
// After the first hit, every extra 100 clicks we hit 0 again
int remaining = clicks - firstHit;
return 1 + remaining / 100;
}
private int HandleRotations(string[] lines, bool isPart2 = false)
{
int startNumber = 50;
int zerosCount = 0;
foreach (string line in lines)
{
// L or R, then a number to EOL
char direction = line[0];
int clicks = int.Parse(line.Substring(1));
// The dial is a circle, turning the dial left from 0 one click makes it point at 99.
// Similarly, turning the dial right from 99 one click makes it point at 0.
if (isPart2)
{
// Count every click that lands on 0 (during + at end of rotation).
zerosCount += CountZeroHits(startNumber, direction, clicks);
}
// For the final dial position ignore full rotations.
int clicksLessFullRotations = clicks % 100;
int finalNumber = TurnDial(startNumber, direction, clicksLessFullRotations);
if (!isPart2 && finalNumber == 0)
{
zerosCount++;
}
startNumber = finalNumber;
}
return zerosCount;
}
[TestCase(@"L68
L30
R48
L5
R60
L55
L1
L99
R14
L82", 3)]
[TestCase(null, 1066)] // The actual answer
public void Part1(string? input, int? expected)
{
// string[] lines = input != null ? new[] { input } : realData;
string[] lines = input != null ? input.Split("\n") : realData;
int result = HandleRotations(lines);
if (expected != null)
{
Assert.That(result, Is.EqualTo(expected.Value));
}
Console.WriteLine($"Part 1: {result}");
}
[TestCase(@"L68
L30
R48
L5
R60
L55
L1
L99
R14
L82", 6)]
[TestCase(null, 6223)] // The actual answer
public void Part2(string? input, int? expected)
{
// string[] lines = input != null ? new[] { input } : realData;
string[] lines = input != null ? input.Split("\n") : realData;
int result = HandleRotations(lines, true);
if (expected != null)
{
Assert.That(result, Is.EqualTo(expected.Value));
}
Console.WriteLine($"Part 2: {result}");
}
}