feat: solved day 13 part 1 and part 2
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
namespace ClawContraption.Tests;
|
||||
|
||||
public class MachineTests
|
||||
{
|
||||
private async Task<List<string[]>> GetPuzzleInput()
|
||||
{
|
||||
var input = await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "INPUT.txt"));
|
||||
|
||||
return input
|
||||
.Split($"{Environment.NewLine}{Environment.NewLine}")
|
||||
.Select(s => s.Split(Environment.NewLine))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SolutionPartOne_WhenGivenPuzzleInput_ItShouldReturnExpectedResult()
|
||||
{
|
||||
var input = await GetPuzzleInput();
|
||||
var machines = input.Select(s => Machine.From(s, 100));
|
||||
var result = machines.Sum(m => m.TokenCost);
|
||||
|
||||
await Assert.That(result).IsEqualTo(28262);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SolutionPartTwo_WhenGivenPuzzleInput_ItShouldReturnExpectedResult()
|
||||
{
|
||||
var input = await GetPuzzleInput();
|
||||
var machines = input.Select(s => Machine.From(s, maxPresses: null, 10_000_000_000_000));
|
||||
var result = machines.Sum(m => m.TokenCost);
|
||||
|
||||
await Assert.That(result).IsEqualTo(101406661266314);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[MethodDataSource(nameof(FromTestCases))]
|
||||
public async Task From_WhenCalledWithInput_ItShouldReturnExpectedMachine(FromTestCase testCase)
|
||||
{
|
||||
var expectedMachine = Machine.From(
|
||||
buttonA: new(testCase.ExpectedButtonAValues.X, testCase.ExpectedButtonAValues.Y),
|
||||
buttonB: new(testCase.ExpectedButtonBValues.X, testCase.ExpectedButtonBValues.Y),
|
||||
prizeLocation: new(
|
||||
testCase.ExpectedPrizeValues.X + testCase.PrizeLocationOffset,
|
||||
testCase.ExpectedPrizeValues.Y + testCase.PrizeLocationOffset
|
||||
),
|
||||
maxPresses: testCase.MaxPresses
|
||||
);
|
||||
|
||||
var machine = Machine.From(testCase.MachineInput, testCase.MaxPresses, testCase.PrizeLocationOffset);
|
||||
|
||||
await Assert.That(machine).IsEquivalentTo(expectedMachine);
|
||||
await Assert.That(machine.TokenCost).IsEqualTo(testCase.ExpectedTokenCost);
|
||||
}
|
||||
|
||||
public static IEnumerable<Func<FromTestCase>> FromTestCases()
|
||||
{
|
||||
yield return () => new(
|
||||
[
|
||||
"Button A: X+94, Y+34",
|
||||
"Button B: X+22, Y+67",
|
||||
"Prize: X=8400, Y=5400",
|
||||
],
|
||||
0,
|
||||
100,
|
||||
(94, 34),
|
||||
(22, 67),
|
||||
(8400, 5400),
|
||||
280
|
||||
);
|
||||
|
||||
yield return () => new(
|
||||
[
|
||||
"Button A: X+26, Y+66",
|
||||
"Button B: X+67, Y+21",
|
||||
"Prize: X=12748, Y=12176",
|
||||
],
|
||||
0,
|
||||
100,
|
||||
(26, 66),
|
||||
(67, 21),
|
||||
(12748, 12176),
|
||||
0
|
||||
);
|
||||
|
||||
yield return () => new(
|
||||
[
|
||||
"Button A: X+17, Y+86",
|
||||
"Button B: X+84, Y+37",
|
||||
"Prize: X=7870, Y=6450",
|
||||
],
|
||||
0,
|
||||
100,
|
||||
(17, 86),
|
||||
(84, 37),
|
||||
(7870, 6450),
|
||||
200
|
||||
);
|
||||
|
||||
yield return () => new(
|
||||
[
|
||||
"Button A: X+69, Y+23",
|
||||
"Button B: X+27, Y+71",
|
||||
"Prize: X=18641, Y=10279",
|
||||
],
|
||||
0,
|
||||
100,
|
||||
(69, 23),
|
||||
(27, 71),
|
||||
(18641, 10279),
|
||||
0
|
||||
);
|
||||
|
||||
// part 2 cases
|
||||
yield return () => new(
|
||||
[
|
||||
"Button A: X+94, Y+34",
|
||||
"Button B: X+22, Y+67",
|
||||
"Prize: X=8400, Y=5400",
|
||||
],
|
||||
10_000_000_000_000,
|
||||
null,
|
||||
(94, 34),
|
||||
(22, 67),
|
||||
(8400, 5400),
|
||||
0
|
||||
);
|
||||
|
||||
yield return () => new(
|
||||
[
|
||||
"Button A: X+26, Y+66",
|
||||
"Button B: X+67, Y+21",
|
||||
"Prize: X=12748, Y=12176",
|
||||
],
|
||||
10_000_000_000_000,
|
||||
null,
|
||||
(26, 66),
|
||||
(67, 21),
|
||||
(12748, 12176),
|
||||
459236326669
|
||||
);
|
||||
|
||||
yield return () => new(
|
||||
[
|
||||
"Button A: X+17, Y+86",
|
||||
"Button B: X+84, Y+37",
|
||||
"Prize: X=7870, Y=6450",
|
||||
],
|
||||
10_000_000_000_000,
|
||||
null,
|
||||
(17, 86),
|
||||
(84, 37),
|
||||
(7870, 6450),
|
||||
0
|
||||
);
|
||||
|
||||
yield return () => new(
|
||||
[
|
||||
"Button A: X+69, Y+23",
|
||||
"Button B: X+27, Y+71",
|
||||
"Prize: X=18641, Y=10279",
|
||||
],
|
||||
10_000_000_000_000,
|
||||
null,
|
||||
(69, 23),
|
||||
(27, 71),
|
||||
(18641, 10279),
|
||||
416082282239
|
||||
);
|
||||
}
|
||||
|
||||
public record FromTestCase(
|
||||
string[] MachineInput,
|
||||
long PrizeLocationOffset,
|
||||
int? MaxPresses,
|
||||
(int X, int Y) ExpectedButtonAValues,
|
||||
(int X, int Y) ExpectedButtonBValues,
|
||||
(int X, int Y) ExpectedPrizeValues,
|
||||
decimal ExpectedTokenCost
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace ClawContraption;
|
||||
|
||||
record Button(decimal XMovement, decimal YMovement);
|
||||
@@ -0,0 +1,111 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ClawContraption;
|
||||
|
||||
partial class Machine
|
||||
{
|
||||
private readonly int? _maxPresses;
|
||||
private readonly Button _buttonA;
|
||||
private readonly Button _buttonB;
|
||||
private readonly PrizeLocation _prizeLocation;
|
||||
private decimal NumberOfButtonBPresses =>
|
||||
((_prizeLocation.Y * _buttonA.XMovement) - (_prizeLocation.X * _buttonA.YMovement)) / ((_buttonB.YMovement * _buttonA.XMovement) - (_buttonB.XMovement * _buttonA.YMovement));
|
||||
|
||||
private decimal NumberOfButtonAPresses =>
|
||||
(_prizeLocation.X - (_buttonB.XMovement * NumberOfButtonBPresses)) / _buttonA.XMovement;
|
||||
|
||||
private bool HasSolution => CheckForSolution();
|
||||
|
||||
public decimal TokenCost => HasSolution ? (NumberOfButtonBPresses * 1) + (NumberOfButtonAPresses * 3) : 0;
|
||||
|
||||
private Machine(Button buttonA, Button buttonB, PrizeLocation prizeLocation, int? maxPresses = null)
|
||||
{
|
||||
_buttonA = buttonA;
|
||||
_buttonB = buttonB;
|
||||
_prizeLocation = prizeLocation;
|
||||
_maxPresses = maxPresses;
|
||||
}
|
||||
|
||||
internal static Machine From(Button buttonA, Button buttonB, PrizeLocation prizeLocation, int? maxPresses) =>
|
||||
new(buttonA, buttonB, prizeLocation, maxPresses);
|
||||
|
||||
public static Machine From(string[] input, int? maxPresses = null, long prizeLocationOffset = 0)
|
||||
{
|
||||
if (input.Length is not 3)
|
||||
{
|
||||
throw new ApplicationException("Input describing machine is not valid");
|
||||
}
|
||||
|
||||
var buttonAInput = input[0];
|
||||
var buttonBInput = input[1];
|
||||
var prizeInput = input[2];
|
||||
|
||||
var buttonAMatches = ButtonARegex().Match(buttonAInput);
|
||||
var buttonBMatches = ButtonBRegex().Match(buttonBInput);
|
||||
var prizeMatches = PrizeRegex().Match(prizeInput);
|
||||
|
||||
if (buttonAMatches.Groups.Count is not 3)
|
||||
{
|
||||
throw new ApplicationException("input for button A missing values");
|
||||
}
|
||||
|
||||
if (buttonBMatches.Groups.Count is not 3)
|
||||
{
|
||||
throw new ApplicationException("input for button B missing values");
|
||||
}
|
||||
|
||||
if (prizeMatches.Groups.Count is not 3)
|
||||
{
|
||||
throw new ApplicationException("input for prize missing values");
|
||||
}
|
||||
|
||||
var buttonA = new Button(
|
||||
int.Parse(buttonAMatches.Groups[1].Value),
|
||||
int.Parse(buttonAMatches.Groups[2].Value)
|
||||
);
|
||||
|
||||
var buttonB = new Button(
|
||||
int.Parse(buttonBMatches.Groups[1].Value),
|
||||
int.Parse(buttonBMatches.Groups[2].Value)
|
||||
);
|
||||
|
||||
var prize = new PrizeLocation(
|
||||
int.Parse(prizeMatches.Groups[1].Value) + prizeLocationOffset,
|
||||
int.Parse(prizeMatches.Groups[2].Value) + prizeLocationOffset
|
||||
);
|
||||
|
||||
return new(buttonA, buttonB, prize, maxPresses);
|
||||
}
|
||||
|
||||
private bool CheckForSolution()
|
||||
{
|
||||
if (Math.Floor(NumberOfButtonAPresses) != NumberOfButtonAPresses)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Math.Floor(NumberOfButtonBPresses) != NumberOfButtonBPresses)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_maxPresses.HasValue && NumberOfButtonAPresses > _maxPresses)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_maxPresses.HasValue && NumberOfButtonBPresses > _maxPresses)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"Button A: X\+(\d+), Y\+(\d+)")]
|
||||
private static partial Regex ButtonARegex();
|
||||
[GeneratedRegex(@"Button B: X\+(\d+), Y\+(\d+)")]
|
||||
private static partial Regex ButtonBRegex();
|
||||
[GeneratedRegex(@"Prize: X=(\d+), Y=(\d+)")]
|
||||
private static partial Regex PrizeRegex();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace ClawContraption;
|
||||
|
||||
record PrizeLocation(decimal X, decimal Y);
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
using ClawContraption;
|
||||
|
||||
if (args.Length is 0)
|
||||
{
|
||||
Console.WriteLine("Please provide a path to the input file.");
|
||||
@@ -13,12 +15,21 @@ if (File.Exists(args[0]) is false)
|
||||
}
|
||||
|
||||
var isPart2 = args.Length is 2 && args[1] is "part2";
|
||||
var input = await File.ReadAllLinesAsync(args[0]);
|
||||
var input = await File.ReadAllTextAsync(args[0]);
|
||||
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
// TODO: Implement solution
|
||||
var offset = isPart2 ? 10_000_000_000_000 : 0;
|
||||
int? maxPresses = isPart2 ? null : 100;
|
||||
|
||||
var machines = input
|
||||
.Split($"{Environment.NewLine}{Environment.NewLine}")
|
||||
.Select(s => s.Split(Environment.NewLine))
|
||||
.Select(s => Machine.From(s, maxPresses, offset))
|
||||
.ToList();
|
||||
|
||||
var result = machines.Sum(m => m.TokenCost);
|
||||
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine($". ({stopwatch.ElapsedMilliseconds}ms)");
|
||||
Console.WriteLine($"The fewest tokens to when all possible games is {result}. ({stopwatch.ElapsedMilliseconds}ms)");
|
||||
@@ -56,3 +56,4 @@ dotnet build
|
||||
| 10 | [Problem](./10/PROBLEM.md) | [Solution](./10/HoofIt/) | I recognized the solution for this based on pattern from last year. Solved using BFS. |
|
||||
| 11 | [Problem](./11/PROBLEM.md) | [Solution](./11/PlutonianPebbles/) | Dictionary > List |
|
||||
| 12 | [Problem](./12/PROBLEM.md) | [Solution](./12/GardenGroups/) | CORNERS == SIDES |
|
||||
| 13 | [Problem](./13/PROBLEM.md) | [Solution](./13/ClawContraption/) | Yay for algebra |
|
||||
|
||||
Reference in New Issue
Block a user