Day 6: Wait For It
I have enjoyed doing this challenge immensely. It's been a lot of fun, predominantly because the instructions and requirements were so easy to follow, unlike some previous days.
Table of Contents
Link to Challenge
TLDR
Solution
Breakdown of Code Part 1
Part 2 Breakdown of Code Part 2
TLDR
You're given a time and distance to beat.
You can inform your boat to increase its speed for the race by making it wait to increase the speed at the beginning of the race (but this costs you time).
Once you've reached your preferred speed for the race (from boost), you have the remaining time to get as far as you can to beat the distance (world record).
You are required to calculate the number of combinations of boost time/race time, in which you can beat the world record.
Solution
This was a straight brute force on the combinations, no real clever maths equations, (I'm not that good at Maths).
using System.Text.RegularExpressions;
string[] data = File.ReadAllText("./puzzle.txt").Split("\n");
List<Boat> boats = Boat.ParseMultipleBoats(data);
Boat boat = Boat.ParseBoat(data);
long Part1() => boats
.Select(boat => boat.FindPossibilities())
.Aggregate((x, y) => x * y);
long Part2() => boat.FindPossibilities();
Console.WriteLine($"Part 1: {Part1()}");
Console.WriteLine($"Part 2: {Part2()}");
record Boat(long Time, long Distance)
{
bool CanBeat(long boost)
{
var timeRemaining = Time - boost;
return (timeRemaining * boost) > Distance;
}
public long FindPossibilities() =>
Enumerable.Range(1, (int)Distance)
.Where(x => CanBeat(x) == true)
.Count();
public static Boat ParseBoat(string[] lines)
{
string time = "";
string distance = "";
if (lines.Length >= 2)
{
string times = lines[0].Trim();
string distances = lines[1].Trim();
time = string.Join("", Regex
.Matches(times.Trim(), @"\d+")
.Select(match => match.Value)
);
distance = string.Join("", Regex
.Matches(distances.Trim(), @"\d+")
.Select(match => match.Value)
);
}
return new Boat(long.Parse(time), long.Parse(distance));
}
public static List<Boat> ParseMultipleBoats(string[] lines)
{
List<Boat> boats = new();
if (lines.Length >= 2)
{
string[] times = lines[0].Split(' ', StringSplitOptions.RemoveEmptyEntries);
string[] distance = lines[1].Split(' ', StringSplitOptions.RemoveEmptyEntries);
for (int i = 1; i < times.Length; i++)
{
boats.Add(new Boat(long.Parse(times[i]), long.Parse(distance[i])));
}
}
return boats;
}
}
Breakdown of Code
*Part 1 *
Boat object - this is the record which will store our methods, and details (time, distance).
We parse the lines of the text file (luckily for us there wasn't too much today oddly). Into boats, and store their time and distance for each one, using the ParseBoats
method.
We also do this for the ParseBoat
method, to get the single boat (Part 2), we'll get to that later.
FindPossibilities - is the brute force element of the solution.
Let's break this down further:
public long FindPossibilities() =>
Enumerable.Range(1, (int)Distance)
.Where(x => CanBeat(x) == true)
.Count();
- Create a range of numbers from 1 (we discount 0 as we've been told in the instructions that 0 means no speed/distance is covered) to Distance.
Example
1...5 => 1,2,3,4,5
We filter out the numbers that
CanBeat
the distance the boat set for the World Record. We do this using the CanBeat Function
CanBeat
- this method simply applies some Math in order to work out if the time remaining * the speed the boat has accrued from waiting boost, is GREATER THAN the World Record distance.We count the number of entries from the range, that were returned true from CanBeat.
Then all we have to do is run an Aggregate
extension function on the count for each boat.
What is the Aggregate function?
It's a method that iteratively applies a specified function to the elements of a list, accumulating a result. You can also provide a starting value, which can be added to the first value. If you do not provide a starting point, the first two values are ran against the lambda function and this forms the accumulator value.
It takes a lambda function, combining elements and the accumulated result.
Example:
var numbers = [1,2,3,4];
var output = numbers.Aggregate((x,y) => x + y);
//Output = 10
1st Iteration x = 1, y = 2 , x + y = 3
2nd Iteration x = 3, y = 3, x + y = 6
3rd Iteration x = 6, y = 4, x + y = 10
Result = 10
Solved - That's Part 1
Part 2
This is the same as Part 1 - however now the 3 columns for th initial part, have all been combined to make 1 large time and distance, meaning the combination total is a lot bigger.
However now, we only need to know the answer of this combination count. Therefore there wasn't much to change to our brute force solution.
Simply a case of parsing the 2 lines of text as 1 boat rather than multiple boats, and running it through our pre-existing flow, removing the need to aggregate the counts.
Solved - Part 2