Skip to content

Commit

Permalink
Refactor day 15 solution
Browse files Browse the repository at this point in the history
  • Loading branch information
mMosiur committed Aug 13, 2024
1 parent 0dbfa69 commit e11bcd8
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 90 deletions.
63 changes: 9 additions & 54 deletions Day15 - Lens Library/Day15Solver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ public sealed class Day15Solver : DaySolver
public override int Day => 15;
public override string Title => "Lens Library";

private readonly InputReader _inputReader;
private readonly IReadOnlyList<string> _initializationSequence;

public Day15Solver(Day15SolverOptions options) : base(options)
{
_initializationSequence = InputReader.ReadInitializationSequence(Input);
_inputReader = new(options);
_initializationSequence = _inputReader.ReadInitializationSequence(Input);
}

public Day15Solver(Action<Day15SolverOptions> configure)
Expand All @@ -32,61 +34,14 @@ public override string SolvePart1()

public override string SolvePart2()
{
var initializationSteps = _initializationSequence
.Select(InitializationStep.Parse)
.ToList();
var boxes = new List<Lens>?[256];
foreach (var step in initializationSteps)
var lensConfigurator = new LensConfigurator();
foreach (string stepString in _initializationSequence)
{
int labelHash = HolidayAsciiStringHelper.Hash(step.Label);
var box = boxes[labelHash];
if (box is null)
{
box = new(1);
boxes[labelHash] = box;
}

switch (step.Operation)
{
case InitializationOperationType.Remove:
// find lens with the same label and remove it
box.RemoveAll(lens => lens.Label == step.Label);
break;
case InitializationOperationType.Insert:
{
var lens = box.Find(l => l.Label == step.Label);
if (lens is null)
{
lens = new(step.Label);
box.Add(lens);
}

lens.FocalLength = step.Parameter;
break;
}
default:
throw new InvalidOperationException($"Invalid operation type '{step.Operation}'.");
}
}

int focusingPowerSum = 0;
for (int boxNumber = 0; boxNumber < boxes.Length; boxNumber++)
{
var box = boxes[boxNumber];
if (box is null) continue;
for (int slotNumber = 0; slotNumber < box.Count; slotNumber++)
{
var lens = box[slotNumber];
focusingPowerSum += (boxNumber + 1) * (slotNumber + 1) * lens.FocalLength;
}
var step = _inputReader.ParseStep(stepString);
lensConfigurator.PerformStep(step);
}

return focusingPowerSum.ToString();
int totalFocusingPower = lensConfigurator.CalculateFocusingPower();
return totalFocusingPower.ToString();
}
}

internal class Lens(string label)
{
public string Label { get; } = label;
public int FocalLength { get; set; }
}
3 changes: 3 additions & 0 deletions Day15 - Lens Library/Day15SolverOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ namespace AdventOfCode.Year2023.Day15;

public sealed class Day15SolverOptions : DaySolverOptions
{
public char SequenceSeparator { get; set; } = ',';
public char RemoveOperationChar { get; set; } = '-';
public char InsertOperationChar { get; set; } = '=';
}
2 changes: 1 addition & 1 deletion Day15 - Lens Library/HolidayAsciiStringHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace AdventOfCode.Year2023.Day15;
namespace AdventOfCode.Year2023.Day15;

internal static class HolidayAsciiStringHelper
{
Expand Down
8 changes: 8 additions & 0 deletions Day15 - Lens Library/InitializationOperationType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace AdventOfCode.Year2023.Day15;

internal enum InitializationOperationType
{
None = 0,
Remove,
Insert,
}
8 changes: 0 additions & 8 deletions Day15 - Lens Library/InitializationSequence.cs

This file was deleted.

8 changes: 8 additions & 0 deletions Day15 - Lens Library/InitializationStep.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace AdventOfCode.Year2023.Day15;

internal readonly struct InitializationStep(string label, InitializationOperationType operation, int parameter = default)
{
public string Label { get; } = label;
public InitializationOperationType Operation { get; } = operation;
public int Parameter { get; } = parameter;
}
55 changes: 28 additions & 27 deletions Day15 - Lens Library/InputReader.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,51 @@
using AdventOfCode.Common.SpanExtensions;
using AdventOfCode.Common.SpanExtensions;

namespace AdventOfCode.Year2023.Day15;

internal static class InputReader
internal sealed class InputReader(Day15SolverOptions options)
{
public static List<string> ReadInitializationSequence(string input)
private readonly char _sequenceSeparator = options.SequenceSeparator;
private readonly char _removeOperationChar = options.RemoveOperationChar;
private readonly char _insertOperationChar = options.InsertOperationChar;


public List<string> ReadInitializationSequence(string input)
{
var sequence = new List<string>(input.AsSpan().Count(',') + 1);
foreach (var sequenceSpan in input.AsSpan().Trim().Split(','))
var sequence = new List<string>(input.AsSpan().Count(_sequenceSeparator) + 1);
foreach (var sequenceSpan in input.AsSpan().Trim().Split(_sequenceSeparator))
{
sequence.Add(sequenceSpan.Trim().ToString());
}

return sequence;
}
}

internal readonly struct InitializationStep(string label, InitializationOperationType operation, int parameter = default)
{
public string Label { get; } = label;
public InitializationOperationType Operation { get; } = operation;
public int Parameter { get; } = parameter;

public static InitializationStep Parse(string line) => Parse(line.AsSpan());
public InitializationStep ParseStep(string line) => ParseStep(line.AsSpan());

public static InitializationStep Parse(ReadOnlySpan<char> line)
public InitializationStep ParseStep(ReadOnlySpan<char> line)
{
line = line.Trim();
int operationCharIndex = line.LastIndexOfAny('-', '=');
InitializationOperationType operation = line[operationCharIndex] switch
{
'-' => InitializationOperationType.Remove,
'=' => InitializationOperationType.Insert,
_ => throw new InvalidOperationException("Invalid operation character.")
};
int operationCharIndex = line.LastIndexOfAny(_removeOperationChar, '=');
InitializationOperationType operation = ParseOperation(line[operationCharIndex]);
var labelSpan = line[..operationCharIndex];
string label = labelSpan.ToString();
var parameterSpan = line[(operationCharIndex + 1)..];
int parameter = parameterSpan.IsEmpty ? default : int.Parse(parameterSpan);
return new(label, operation, parameter);
}
}

internal enum InitializationOperationType
{
None,
Remove,
Insert,
private InitializationOperationType ParseOperation(char operationChar)
{
if (operationChar == _removeOperationChar)
{
return InitializationOperationType.Remove;
}

if (operationChar == _insertOperationChar)
{
return InitializationOperationType.Insert;
}

throw new InvalidOperationException("Invalid operation character.");
}
}
73 changes: 73 additions & 0 deletions Day15 - Lens Library/LensConfigurator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace AdventOfCode.Year2023.Day15;

internal sealed class LensConfigurator
{
private readonly List<Lens>?[] _boxes = new List<Lens>?[256];

private List<Lens> GetBox(int boxNumber)
{
var box = _boxes[boxNumber];
if (box is null)
{
box = new(1);
_boxes[boxNumber] = box;
}

return box;
}

public void PerformStep(InitializationStep step)
{
int labelHash = HolidayAsciiStringHelper.Hash(step.Label);
var box = GetBox(labelHash);

switch (step.Operation)
{
case InitializationOperationType.Remove:
box.RemoveAll(lens => lens.Label == step.Label);
break;
case InitializationOperationType.Insert:
{
var lens = box.Find(l => l.Label == step.Label);
if (lens is null)
{
lens = new(step.Label);
box.Add(lens);
}

lens.FocalLength = step.Parameter;
break;
}
default:
throw new InvalidOperationException($"Invalid operation to perform: {step.Operation}.");
}
}

public int CalculateFocusingPower()
{
int totalFocusingPower = 0;
for (int boxNumber = 0; boxNumber < _boxes.Length; boxNumber++)
{
var box = _boxes[boxNumber];
if (box is null) continue;
for (int slotNumber = 0; slotNumber < box.Count; slotNumber++)
{
var lens = box[slotNumber];
totalFocusingPower += CalculateSingleLensFocusingPower(boxNumber, slotNumber, lens);
}
}

return totalFocusingPower;
}

private static int CalculateSingleLensFocusingPower(int boxNumber, int slotNumber, Lens lens)
{
return (boxNumber + 1) * (slotNumber + 1) * lens.FocalLength;
}

private sealed class Lens(string label)
{
public string Label { get; } = label;
public int FocalLength { get; set; }
}
}

0 comments on commit e11bcd8

Please sign in to comment.