using System.Text.RegularExpressions; namespace Trebuchet; public class Program { public async static Task Main(string[] args) { if (args.Length is 0) { Console.WriteLine("Please provide a path to the input file."); return 1; } if (File.Exists(args[0]) is false) { Console.WriteLine("The provided file does not exist."); return 2; } IPuzzleSolver puzzleSolver = args.Length > 1 && args[1] == "2" ? new PartTwoPuzzleSolver() : new PartOnePuzzleSolver(); var input = await File.ReadAllLinesAsync(args[0]); var result = puzzleSolver.SumCalibrationValues(input); Console.WriteLine($"The sum of all calibration values is {result}."); return 0; } } /// /// Interface for puzzle solvers. /// public interface IPuzzleSolver { /// /// Sums all calibration values. /// /// The lines of the input file. /// The sum of all calibration values. public int SumCalibrationValues(string[] lines); /// /// Gets the calibration value for a given line. /// /// The line to get the calibration value for. /// The calibration value for the given line. public int GetCalibrationValue(string line); } /// /// Puzzle solver for part one. /// /// public class PartOnePuzzleSolver : IPuzzleSolver { public int SumCalibrationValues(string[] lines) => lines.Sum(GetCalibrationValue); public int GetCalibrationValue(string line) { if (line.Length is 0) { return 0; } var numbers = line .Where(char.IsDigit) .Select(char.ToString) .ToList(); var firstNumber = numbers.First(); var lastNumber = numbers.Last(); return int.Parse(firstNumber + lastNumber); } } /// /// Puzzle solver for part two. /// /// public class PartTwoPuzzleSolver : IPuzzleSolver { private readonly Dictionary _validDigitsAsWords = new() { { "one", "1" }, { "two", "2" }, { "three", "3" }, { "four", "4" }, { "five", "5" }, { "six", "6" }, { "seven", "7" }, { "eight", "8" }, { "nine", "9" }, }; private bool HasValidDigitAsWord(string word, out string match) { var matchResult = Regex.Match(word, _validDigitsAsWords.Keys.Aggregate((a, b) => @$"{a}|{b}")); match = matchResult.Value; return matchResult.Success; } public int SumCalibrationValues(string[] lines) => lines.Sum(GetCalibrationValue); public int GetCalibrationValue(string line) { if (line.Length is 0) { return 0; } var currentWord = string.Empty; var numbers = new List(); foreach (var character in line) { currentWord += character; if (char.IsDigit(character)) { numbers.Add(character.ToString()); currentWord = string.Empty; } if (HasValidDigitAsWord(currentWord, out var match)) { // The last character of the match could be the // first character of the next valid digit as word // so we want to preserve it for the next iteration. numbers.Add(_validDigitsAsWords[match]); currentWord = currentWord.Replace(match[..^1], string.Empty); } } var firstNumber = numbers.First(); var lastNumber = numbers.Last(); return int.Parse(firstNumber + lastNumber); } }