feat: solved day 14 part 1

This commit is contained in:
Stevan Freeborn
2024-12-15 13:50:34 -06:00
parent abfa9e50ba
commit bfff85956e
3 changed files with 251 additions and 2 deletions
+73
View File
@@ -0,0 +1,73 @@
namespace RestroomRedoubt.Tests;
public class RobotTests
{
[Test]
[MethodDataSource(nameof(FromTestCases))]
public async Task From_WhenCalledWithInput_ItShouldReturnExpectedRobot(FromTestCase testCase)
{
var expected = Robot.From(
testCase.ExpectedPosition.X,
testCase.ExpectedPosition.Y,
testCase.ExpectedVelocity.X,
testCase.ExpectedVelocity.Y
);
var result = Robot.From(testCase.Input);
await Assert.That(result).IsEqualTo(expected);
}
[Test]
[MethodDataSource(nameof(MoveTestCases))]
public async Task Move_WhenCalled_ItShouldMoveRobotToExpectedPosition(MoveTestCase testCase)
{
var robot = Robot.From(
testCase.CurrentPosition.X,
testCase.CurrentPosition.Y,
testCase.Velocity.X,
testCase.Velocity.Y
);
var result = robot.Move(testCase.Area.Width, testCase.Area.Height);
await Assert.That(result).IsEqualTo(testCase.ExpectedNextPosition);
}
public static IEnumerable<Func<MoveTestCase>> MoveTestCases()
{
var testVelocity = (2, -3);
var testArea = (11, 7);
yield return () => new((2, 4), testVelocity, testArea, (4, 1));
yield return () => new((4, 1), testVelocity, testArea, (6, 5));
yield return () => new((6, 5), testVelocity, testArea, (8, 2));
yield return () => new((8, 2), testVelocity, testArea, (10, 6));
yield return () => new((10, 6), testVelocity, testArea, (1, 3));
}
public record MoveTestCase(
(int X, int Y) CurrentPosition,
(int X, int Y) Velocity,
(int Width, int Height) Area,
(int X, int Y) ExpectedNextPosition
);
public static IEnumerable<Func<FromTestCase>> FromTestCases()
{
yield return () => new("p=0,4 v=3,-3", (0, 4), (3, -3));
yield return () => new("p=6,3 v=-1,-3", (6, 3), (-1, -3));
yield return () => new("p=10,3 v=-1,2", (10, 3), (-1, 2));
yield return () => new("p=2,0 v=2,-1", (2, 0), (2, -1));
yield return () => new("p=0,0 v=1,3", (0, 0), (1, 3));
yield return () => new("p=3,0 v=-2,-2", (3, 0), (-2, -2));
yield return () => new("p=7,6 v=-1,-3", (7, 6), (-1, -3));
yield return () => new("p=3,0 v=-1,-2", (3, 0), (-1, -2));
yield return () => new("p=9,3 v=2,3", (9, 3), (2, 3));
yield return () => new("p=7,3 v=-1,2", (7, 3), (-1, 2));
yield return () => new("p=2,4 v=2,-3", (2, 4), (2, -3));
yield return () => new("p=9,5 v=-3,-3", (9, 5), (-3, -3));
}
public record FromTestCase(string Input, (int X, int Y) ExpectedPosition, (int X, int Y) ExpectedVelocity);
}
@@ -0,0 +1,42 @@
namespace RestroomRedoubt.Tests;
public class SimulationTests
{
private readonly string[] _exampleInput = [
"p=0,4 v=3,-3",
"p=6,3 v=-1,-3",
"p=10,3 v=-1,2",
"p=2,0 v=2,-1",
"p=0,0 v=1,3",
"p=3,0 v=-2,-2",
"p=7,6 v=-1,-3",
"p=3,0 v=-1,-2",
"p=9,3 v=2,3",
"p=7,3 v=-1,2",
"p=2,4 v=2,-3",
"p=9,5 v=-3,-3",
];
private async Task<string[]> GetPuzzleInput()
{
return await File.ReadAllLinesAsync(Path.Combine(AppContext.BaseDirectory, "INPUT.txt"));
}
[Test]
public async Task Run_WhenCalledWithPuzzleInput_ItShouldReturnExpectedValue()
{
var input = await GetPuzzleInput();
var result = Simulation.From(101, 103, input).Run(100);
await Assert.That(result).IsEqualTo(224554908);
}
[Test]
public async Task Run_WhenCalledWithExampleInput_ItShouldReturnExpectedValue()
{
var result = Simulation.From(11, 7, _exampleInput).Run(100);
await Assert.That(result).IsEqualTo(12);
}
}
+136 -2
View File
@@ -1,4 +1,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
if (args.Length is 0) if (args.Length is 0)
{ {
@@ -18,7 +20,139 @@ var input = await File.ReadAllLinesAsync(args[0]);
var stopwatch = new Stopwatch(); var stopwatch = new Stopwatch();
stopwatch.Start(); stopwatch.Start();
// TODO: Implement solution var result = Simulation.From(101, 103, input).Run(100);
stopwatch.Stop(); stopwatch.Stop();
Console.WriteLine($". ({stopwatch.ElapsedMilliseconds}ms)"); Console.WriteLine($"The safety factor will be {result}. ({stopwatch.ElapsedMilliseconds}ms)");
class Simulation
{
private readonly List<Robot> _robots;
private readonly int _width;
private readonly int _height;
private readonly List<Quadrant> _quadrants;
private Simulation(int width, int height, List<Robot> robots)
{
_width = width;
_robots = robots;
_height = height;
var middleColumn = _width / 2;
var middleRow = _height / 2;
var topLeftQuadrant = new Quadrant(0, middleColumn - 1, 0, middleRow - 1);
var topRightQuadrant = new Quadrant(middleColumn + 1, _width - 1, 0, middleRow - 1);
var bottomLeftQuadrant = new Quadrant(0, middleColumn - 1, middleRow + 1, _height - 1);
var bottomRightQuadrant = new Quadrant(middleColumn + 1, _width - 1, middleRow + 1, _height - 1);
_quadrants = [topLeftQuadrant, topRightQuadrant, bottomLeftQuadrant, bottomRightQuadrant];
}
public static Simulation From(int width, int height, string[] input) => new(width, height, input.Select(Robot.From).ToList());
public int Run(int times)
{
for (int i = 0; i < times; i++)
{
foreach (var robot in _robots)
{
robot.Move(_width, _height);
}
Debug(i);
}
return _quadrants
.Select(quadrant => _robots.Count(r => r.IsIn(quadrant)))
.Aggregate(1, (current, count) => current * count);
}
private void Debug(int time)
{
var lines = new StringBuilder();
for (var currentColumn = 0; currentColumn < _width; currentColumn++)
{
var row = new StringBuilder();
for (int currentRow = 0; currentRow < _height; currentRow++)
{
if (_robots.Any(r => r.IsIn(currentColumn, currentRow)))
{
row.Append('R');
}
else
{
row.Append('.');
}
}
lines.AppendLine(row.ToString());
}
File.WriteAllText(Path.Combine(AppContext.BaseDirectory, $"OUTPUT_{time}.txt"), lines.ToString());
}
}
partial record Robot
{
private int PositionX { get; set; }
private int PositionY { get; set; }
private int VelocityX { get; }
private int VelocityY { get; }
private Robot(int positionX, int positionY, int velocityX, int velocityY)
{
PositionX = positionX;
PositionY = positionY;
VelocityX = velocityX;
VelocityY = velocityY;
}
public static Robot From(
int positionX,
int positionY,
int velocityX,
int velocityY
) => new(positionX, positionY, velocityX, velocityY);
public static Robot From(string input)
{
var matches = RobotRegex().Match(input);
if (matches.Groups.Count is not 5)
{
throw new ArgumentException("The given robot input is missing values");
}
return new(
int.Parse(matches.Groups[1].Value),
int.Parse(matches.Groups[2].Value),
int.Parse(matches.Groups[3].Value),
int.Parse(matches.Groups[4].Value)
);
}
public (int X, int Y) Move(int MaxX, int MaxY)
{
PositionX = (PositionX + VelocityX + MaxX) % MaxX;
PositionY = (PositionY + VelocityY + MaxY) % MaxY;
return (PositionX, PositionY);
}
public bool IsIn(Quadrant quadrant)
{
return PositionX >= quadrant.MinX &&
PositionX <= quadrant.MaxX &&
PositionY >= quadrant.MinY &&
PositionY <= quadrant.MaxY;
}
public bool IsIn(int x, int y) => PositionX == x && PositionY == y;
[GeneratedRegex(@"p=(-?\d+),(-?\d+) v=(-?\d+),(-?\d+)")]
private static partial Regex RobotRegex();
}
record Quadrant(int MinX, int MaxX, int MinY, int MaxY);