diff --git a/.drone.yml b/.drone.yml index 0300278..e410fa9 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,7 +4,7 @@ name: run steps: - name: run - image: mcr.microsoft.com/dotnet/sdk:latest + image: mcr.microsoft.com/dotnet/sdk:6.0 commands: - dotnet test - dotnet run --project aoc2020/aoc2020.csproj diff --git a/LICENSE b/LICENSE index 204b93d..cca1ab9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -MIT License Copyright (c) +MIT License Copyright (c) 2020 Ben Harris Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/aoc2020.test/DayTests.cs b/aoc2020.test/DayTests.cs index ce64713..f11f375 100644 --- a/aoc2020.test/DayTests.cs +++ b/aoc2020.test/DayTests.cs @@ -1,62 +1,60 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace aoc2020.test +namespace aoc2020.test; + +[TestClass] +public class DayTests { - [TestClass] - public class DayTests + [DataTestMethod] + [DataRow(typeof(Day01), "751776", "42275090")] + [DataRow(typeof(Day02), "556", "605")] + [DataRow(typeof(Day03), "189", "1718180100")] + [DataRow(typeof(Day04), "247", "145")] + [DataRow(typeof(Day05), "878", "504")] + [DataRow(typeof(Day06), "6273", "3254")] + [DataRow(typeof(Day07), "169", "82372")] + [DataRow(typeof(Day08), "1654", "833")] + [DataRow(typeof(Day09), "138879426", "23761694")] + [DataRow(typeof(Day10), "1980", "4628074479616")] + [DataRow(typeof(Day11), "2303", "2057")] + [DataRow(typeof(Day12), "1710", "62045")] + [DataRow(typeof(Day13), "171", "539746751134958")] + [DataRow(typeof(Day14), "17481577045893", "4160009892257")] + [DataRow(typeof(Day15), "257", "8546398")] + [DataRow(typeof(Day16), "19093", "5311123569883")] + // [DataRow(typeof(Day17), "293", "1816")] + [DataRow(typeof(Day18), "12918250417632", "171259538712010")] + [DataRow(typeof(Day19), "160", "357")] + [DataRow(typeof(Day20), "21599955909991", "")] + [DataRow(typeof(Day21), "", "")] + [DataRow(typeof(Day22), "", "")] + [DataRow(typeof(Day23), "", "")] + [DataRow(typeof(Day24), "", "")] + [DataRow(typeof(Day25), "", "")] + public void CheckAllDays(Type dayType, string part1, string part2) { - [DataTestMethod] - [DataRow(typeof(Day01), "751776", "42275090")] - [DataRow(typeof(Day02), "556", "605")] - [DataRow(typeof(Day03), "189", "1718180100")] - [DataRow(typeof(Day04), "247", "145")] - [DataRow(typeof(Day05), "878", "504")] - [DataRow(typeof(Day06), "6273", "3254")] - [DataRow(typeof(Day07), "169", "82372")] - [DataRow(typeof(Day08), "1654", "833")] - [DataRow(typeof(Day09), "138879426", "23761694")] - [DataRow(typeof(Day10), "1980", "4628074479616")] - [DataRow(typeof(Day11), "2303", "2057")] - [DataRow(typeof(Day12), "1710", "62045")] - [DataRow(typeof(Day13), "171", "539746751134958")] - [DataRow(typeof(Day14), "17481577045893", "4160009892257")] - [DataRow(typeof(Day15), "257", "8546398")] - [DataRow(typeof(Day16), "19093", "5311123569883")] - // [DataRow(typeof(Day17), "293", "1816")] - [DataRow(typeof(Day18), "12918250417632", "171259538712010")] - [DataRow(typeof(Day19), "160", "357")] - [DataRow(typeof(Day20), "21599955909991", "")] - [DataRow(typeof(Day21), "", "")] - [DataRow(typeof(Day22), "", "")] - [DataRow(typeof(Day23), "", "")] - [DataRow(typeof(Day24), "", "")] - [DataRow(typeof(Day25), "", "")] - public void CheckAllDays(Type dayType, string part1, string part2) - { - // create day instance - var s = Stopwatch.StartNew(); - var day = (Day) Activator.CreateInstance(dayType); - s.Stop(); - Assert.IsNotNull(day, "failed to create day object"); - Console.WriteLine($"{s.ScaleMilliseconds()}ms elapsed in constructor"); + // create day instance + var s = Stopwatch.StartNew(); + var day = Activator.CreateInstance(dayType) as Day; + s.Stop(); + Assert.IsNotNull(day, "failed to create day object"); + Console.WriteLine($"{s.ScaleMilliseconds()}ms elapsed in constructor"); - // part 1 - s.Reset(); - s.Start(); - var part1Actual = day.Part1(); - s.Stop(); - Console.WriteLine($"{s.ScaleMilliseconds()}ms elapsed in part1"); - Assert.AreEqual(part1, part1Actual, $"Incorrect answer for Day {day.DayNumber} Part1"); + // part 1 + s.Reset(); + s.Start(); + var part1Actual = day.Part1(); + s.Stop(); + Console.WriteLine($"{s.ScaleMilliseconds()}ms elapsed in part1"); + Assert.AreEqual(part1, part1Actual, $"Incorrect answer for Day {day.DayNumber} Part1"); - // part 2 - s.Reset(); - s.Start(); - var part2Actual = day.Part2(); - s.Stop(); - Console.WriteLine($"{s.ScaleMilliseconds()}ms elapsed in part2"); - Assert.AreEqual(part2, part2Actual, $"Incorrect answer for Day {day.DayNumber} Part2"); - } + // part 2 + s.Reset(); + s.Start(); + var part2Actual = day.Part2(); + s.Stop(); + Console.WriteLine($"{s.ScaleMilliseconds()}ms elapsed in part2"); + Assert.AreEqual(part2, part2Actual, $"Incorrect answer for Day {day.DayNumber} Part2"); } -} \ No newline at end of file +} diff --git a/aoc2020.test/aoc2020.test.csproj b/aoc2020.test/aoc2020.test.csproj index cabfef9..98a018e 100644 --- a/aoc2020.test/aoc2020.test.csproj +++ b/aoc2020.test/aoc2020.test.csproj @@ -1,9 +1,10 @@ - + - net5.0 - - false + net6.0 + enable + enable + false diff --git a/aoc2020/Day.cs b/aoc2020/Day.cs index a8f0b3b..77a272c 100644 --- a/aoc2020/Day.cs +++ b/aoc2020/Day.cs @@ -1,48 +1,44 @@ -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; -namespace aoc2020 +namespace aoc2020; + +public abstract class Day { - public abstract class Day + protected Day(int dayNumber, string puzzleName) { - protected Day(int dayNumber, string puzzleName) - { - DayNumber = dayNumber; - PuzzleName = puzzleName; - } - - public int DayNumber { get; } - public string PuzzleName { get; } - - protected IEnumerable Input => - File.ReadLines(FileName); - - protected string FileName => - Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"input/day{DayNumber,2:00}.in"); - - public abstract string Part1(); - public abstract string Part2(); - - public void AllParts(bool verbose = true) - { - Console.WriteLine($"Day {DayNumber,2}: {PuzzleName}"); - var s = Stopwatch.StartNew(); - var part1 = Part1(); - s.Stop(); - Console.Write($"Part1: {part1,-15} "); - Console.WriteLine(verbose ? $"{s.ScaleMilliseconds()}ms elapsed" : ""); - - s.Reset(); - - s.Start(); - var part2 = Part2(); - s.Stop(); - Console.Write($"Part2: {part2,-15} "); - Console.WriteLine(verbose ? $"{s.ScaleMilliseconds()}ms elapsed" : ""); - - Console.WriteLine(); - } + DayNumber = dayNumber; + PuzzleName = puzzleName; } -} \ No newline at end of file + + public int DayNumber { get; } + public string PuzzleName { get; } + + protected IEnumerable Input => + File.ReadLines(FileName); + + protected string FileName => + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"input/day{DayNumber,2:00}.in"); + + public abstract string Part1(); + public abstract string Part2(); + + public void AllParts(bool verbose = true) + { + Console.WriteLine($"Day {DayNumber,2}: {PuzzleName}"); + var s = Stopwatch.StartNew(); + var part1 = Part1(); + s.Stop(); + Console.Write($"Part1: {part1,-15} "); + Console.WriteLine(verbose ? $"{s.ScaleMilliseconds()}ms elapsed" : ""); + + s.Reset(); + + s.Start(); + var part2 = Part2(); + s.Stop(); + Console.Write($"Part2: {part2,-15} "); + Console.WriteLine(verbose ? $"{s.ScaleMilliseconds()}ms elapsed" : ""); + + Console.WriteLine(); + } +} diff --git a/aoc2020/Day01.cs b/aoc2020/Day01.cs index 96484e0..39f08df 100644 --- a/aoc2020/Day01.cs +++ b/aoc2020/Day01.cs @@ -1,35 +1,31 @@ -using System.Collections.Immutable; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 1: +/// +public sealed class Day01 : Day { - /// - /// Day 1: - /// - public sealed class Day01 : Day + private readonly ImmutableHashSet _entries; + + public Day01() : base(1, "Report Repair") { - private readonly ImmutableHashSet _entries; - - public Day01() : base(1, "Report Repair") - { - _entries = Input.Select(int.Parse).ToImmutableHashSet(); - } - - public override string Part1() - { - var entry = _entries.First(e => _entries.Contains(2020 - e)); - return $"{entry * (2020 - entry)}"; - } - - public override string Part2() - { - foreach (var i in _entries) - foreach (var j in _entries) - foreach (var k in _entries) - if (i + j + k == 2020) - return $"{i * j * k}"; - - return ""; - } + _entries = Input.Select(int.Parse).ToImmutableHashSet(); } -} \ No newline at end of file + + public override string Part1() + { + var entry = _entries.First(e => _entries.Contains(2020 - e)); + return $"{entry * (2020 - entry)}"; + } + + public override string Part2() + { + foreach (var i in _entries) + foreach (var j in _entries) + foreach (var k in _entries) + if (i + j + k == 2020) + return $"{i * j * k}"; + + return ""; + } +} diff --git a/aoc2020/Day02.cs b/aoc2020/Day02.cs index 1c06374..6513601 100644 --- a/aoc2020/Day02.cs +++ b/aoc2020/Day02.cs @@ -1,56 +1,52 @@ -using System.Collections.Immutable; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 2: +/// +public sealed class Day02 : Day { - /// - /// Day 2: - /// - public sealed class Day02 : Day + private readonly ImmutableList _passwords; + + public Day02() : base(2, "Password Philosophy") { - private readonly ImmutableList _passwords; - - public Day02() : base(2, "Password Philosophy") - { - _passwords = Input.Select(p => new Password(p)).ToImmutableList(); - } - - public override string Part1() - { - return $"{_passwords.Count(p => p.IsValid)}"; - } - - public override string Part2() - { - return $"{_passwords.Count(p => p.IsValidByIndex)}"; - } - - private class Password - { - public Password(string line) - { - var split = line.Split(": ", 2); - var split2 = split[0].Split(' ', 2); - var indices = split2[0].Split('-', 2); - I = int.Parse(indices[0]); - J = int.Parse(indices[1]); - C = char.Parse(split2[1]); - Value = split[1]; - } - - public bool IsValid => - Count >= I && Count <= J; - - public bool IsValidByIndex => - (Value[I - 1] == C) ^ (Value[J - 1] == C); - - private int Count => - Value.Count(p => p == C); - - private int I { get; } - private int J { get; } - private char C { get; } - private string Value { get; } - } + _passwords = Input.Select(p => new Password(p)).ToImmutableList(); } -} \ No newline at end of file + + public override string Part1() + { + return $"{_passwords.Count(p => p.IsValid)}"; + } + + public override string Part2() + { + return $"{_passwords.Count(p => p.IsValidByIndex)}"; + } + + private class Password + { + public Password(string line) + { + var split = line.Split(": ", 2); + var split2 = split[0].Split(' ', 2); + var indices = split2[0].Split('-', 2); + I = int.Parse(indices[0]); + J = int.Parse(indices[1]); + C = char.Parse(split2[1]); + Value = split[1]; + } + + public bool IsValid => + Count >= I && Count <= J; + + public bool IsValidByIndex => + (Value[I - 1] == C) ^ (Value[J - 1] == C); + + private int Count => + Value.Count(p => p == C); + + private int I { get; } + private int J { get; } + private char C { get; } + private string Value { get; } + } +} diff --git a/aoc2020/Day03.cs b/aoc2020/Day03.cs index dfc9ceb..00463ec 100644 --- a/aoc2020/Day03.cs +++ b/aoc2020/Day03.cs @@ -1,45 +1,42 @@ -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 3: +/// +public sealed class Day03 : Day { - /// - /// Day 3: - /// - public sealed class Day03 : Day + private readonly string[] _grid; + private readonly int _width; + + public Day03() : base(3, "Toboggan Trajectory") { - private readonly string[] _grid; - private readonly int _width; - - public Day03() : base(3, "Toboggan Trajectory") - { - _grid = Input.ToArray(); - _width = _grid[0].Length; - } - - private long CountSlope(int dx, int dy) - { - long hits = 0; - for (int x = 0, y = 0; y < _grid.Length; y += dy, x = (x + dx) % _width) - if (_grid[y][x] == '#') - hits++; - - return hits; - } - - public override string Part1() - { - return $"{CountSlope(3, 1)}"; - } - - public override string Part2() - { - var xSlopes = new[] {1, 3, 5, 7, 1}; - var ySlopes = new[] {1, 1, 1, 1, 2}; - - return xSlopes.Zip(ySlopes) - .Select(s => CountSlope(s.Item1, s.Item2)) - .Aggregate((acc, i) => acc * i) - .ToString(); - } + _grid = Input.ToArray(); + _width = _grid[0].Length; } -} \ No newline at end of file + + private long CountSlope(int dx, int dy) + { + long hits = 0; + for (int x = 0, y = 0; y < _grid.Length; y += dy, x = (x + dx) % _width) + if (_grid[y][x] == '#') + hits++; + + return hits; + } + + public override string Part1() + { + return $"{CountSlope(3, 1)}"; + } + + public override string Part2() + { + var xSlopes = new[] { 1, 3, 5, 7, 1 }; + var ySlopes = new[] { 1, 1, 1, 1, 2 }; + + return xSlopes.Zip(ySlopes) + .Select(s => CountSlope(s.First, s.Second)) + .Aggregate((acc, i) => acc * i) + .ToString(); + } +} diff --git a/aoc2020/Day04.cs b/aoc2020/Day04.cs index a85ad1a..8135801 100644 --- a/aoc2020/Day04.cs +++ b/aoc2020/Day04.cs @@ -1,188 +1,182 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 4: +/// +public sealed class Day04 : Day { - /// - /// Day 4: - /// - public sealed class Day04 : Day + private readonly List _passports; + + public Day04() : base(4, "Passport Processing") { - private readonly List _passports; + _passports = new List(); - public Day04() : base(4, "Passport Processing") + var a = new List(); + foreach (var line in Input) { - _passports = new List(); - - var a = new List(); - foreach (var line in Input) + if (line == "") { - if (line == "") - { - _passports.Add(Passport.Parse(a)); - a.Clear(); - continue; - } - - a.Add(line); + _passports.Add(Passport.Parse(a)); + a.Clear(); + continue; } - if (a.Any()) _passports.Add(Passport.Parse(a)); + a.Add(line); } - public override string Part1() + if (a.Any()) _passports.Add(Passport.Parse(a)); + } + + public override string Part1() + { + return $"{_passports.Count(p => p.IsValid)}"; + } + + public override string Part2() + { + return $"{_passports.Count(p => p.ExtendedValidation())}"; + } + + private class Passport + { + private string _byr; + private string _cid; + private string _ecl; + private string _eyr; + private string _hcl; + private string _hgt; + private string _iyr; + private string _pid; + + public bool IsValid => + _byr != null && + _iyr != null && + _eyr != null && + _hgt != null && + _hcl != null && + _ecl != null && + _pid != null; + + public bool ExtendedValidation() { - return $"{_passports.Count(p => p.IsValid)}"; - } + if (!IsValid) return false; - public override string Part2() - { - return $"{_passports.Count(p => p.ExtendedValidation())}"; - } - - private class Passport - { - private string _byr; - private string _cid; - private string _ecl; - private string _eyr; - private string _hcl; - private string _hgt; - private string _iyr; - private string _pid; - - public bool IsValid => - _byr != null && - _iyr != null && - _eyr != null && - _hgt != null && - _hcl != null && - _ecl != null && - _pid != null; - - public bool ExtendedValidation() + // birth year + if (int.TryParse(_byr, out var byr)) { - if (!IsValid) return false; - - // birth year - if (int.TryParse(_byr, out var byr)) - { - if (byr < 1920 || byr > 2002) - return false; - } - else - { + if (byr < 1920 || byr > 2002) return false; - } - - // issuance year - if (int.TryParse(_iyr, out var iyr)) - { - if (iyr < 2010 || iyr > 2020) - return false; - } - else - { - return false; - } - - // expiration year - if (int.TryParse(_eyr, out var eyr)) - { - if (eyr < 2020 || eyr > 2030) - return false; - } - else - { - return false; - } - - // height - if (_hgt.EndsWith("cm")) - { - var h = _hgt.Substring(0, 3); - if (int.TryParse(h, out var hgt)) - { - if (hgt < 150 || hgt > 193) - return false; - } - else - { - return false; - } - } - else if (_hgt.EndsWith("in")) - { - var h = _hgt.Substring(0, 2); - if (int.TryParse(h, out var hgt)) - { - if (hgt < 59 || hgt > 76) - return false; - } - else - { - return false; - } - } - else - { - return false; - } - - // hair color - if (!Regex.IsMatch(_hcl, "#[0-9a-f]{6}")) - return false; - - // eye color - if (!new[] {"amb", "blu", "brn", "gry", "grn", "hzl", "oth"}.Contains(_ecl)) - return false; - - // passport id - if (_pid.Length != 9) - return false; - - return true; + } + else + { + return false; } - public static Passport Parse(IEnumerable list) + // issuance year + if (int.TryParse(_iyr, out var iyr)) { - var passport = new Passport(); - foreach (var entry in string.Join(' ', list).Split(' ', StringSplitOptions.TrimEntries)) - { - var spl = entry.Split(':', 2); - switch (spl[0]) - { - case "byr": - passport._byr = spl[1]; - break; - case "iyr": - passport._iyr = spl[1]; - break; - case "eyr": - passport._eyr = spl[1]; - break; - case "hgt": - passport._hgt = spl[1]; - break; - case "hcl": - passport._hcl = spl[1]; - break; - case "ecl": - passport._ecl = spl[1]; - break; - case "pid": - passport._pid = spl[1]; - break; - case "cid": - passport._cid = spl[1]; - break; - } - } - - return passport; + if (iyr < 2010 || iyr > 2020) + return false; } + else + { + return false; + } + + // expiration year + if (int.TryParse(_eyr, out var eyr)) + { + if (eyr < 2020 || eyr > 2030) + return false; + } + else + { + return false; + } + + // height + if (_hgt.EndsWith("cm")) + { + var h = _hgt[..3]; + if (int.TryParse(h, out var hgt)) + { + if (hgt < 150 || hgt > 193) + return false; + } + else + { + return false; + } + } + else if (_hgt.EndsWith("in")) + { + var h = _hgt.Substring(0, 2); + if (int.TryParse(h, out var hgt)) + { + if (hgt < 59 || hgt > 76) + return false; + } + else + { + return false; + } + } + else + { + return false; + } + + // hair color + if (!Regex.IsMatch(_hcl, "#[0-9a-f]{6}")) + return false; + + // eye color + if (!new[] { "amb", "blu", "brn", "gry", "grn", "hzl", "oth" }.Contains(_ecl)) + return false; + + // passport id + if (_pid.Length != 9) + return false; + + return true; + } + + public static Passport Parse(IEnumerable list) + { + var passport = new Passport(); + foreach (var entry in string.Join(' ', list).Split(' ', StringSplitOptions.TrimEntries)) + { + var spl = entry.Split(':', 2); + switch (spl[0]) + { + case "byr": + passport._byr = spl[1]; + break; + case "iyr": + passport._iyr = spl[1]; + break; + case "eyr": + passport._eyr = spl[1]; + break; + case "hgt": + passport._hgt = spl[1]; + break; + case "hcl": + passport._hcl = spl[1]; + break; + case "ecl": + passport._ecl = spl[1]; + break; + case "pid": + passport._pid = spl[1]; + break; + case "cid": + passport._cid = spl[1]; + break; + } + } + + return passport; } } -} \ No newline at end of file +} diff --git a/aoc2020/Day05.cs b/aoc2020/Day05.cs index a872bdf..a5cb717 100644 --- a/aoc2020/Day05.cs +++ b/aoc2020/Day05.cs @@ -1,34 +1,29 @@ -using System; -using System.Collections.Immutable; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 5: +/// +public sealed class Day05 : Day { - /// - /// Day 5: - /// - public sealed class Day05 : Day + private readonly ImmutableHashSet _ids; + + public Day05() : base(5, "Binary Boarding") { - private readonly ImmutableHashSet _ids; - - public Day05() : base(5, "Binary Boarding") - { - _ids = Input - .Select(s => - Convert.ToInt32(s.Replace('F', '0').Replace('B', '1').Replace('L', '0').Replace('R', '1'), 2)) - .OrderBy(i => i) - .ToImmutableHashSet(); - } - - public override string Part1() - { - return $"{_ids.Last()}"; - } - - public override string Part2() - { - // arithmetic sum of full series - return $"{(_ids.Count + 1) * (_ids.First() + _ids.Last()) / 2 - _ids.Sum()}"; - } + _ids = Input + .Select(s => + Convert.ToInt32(s.Replace('F', '0').Replace('B', '1').Replace('L', '0').Replace('R', '1'), 2)) + .OrderBy(i => i) + .ToImmutableHashSet(); } -} \ No newline at end of file + + public override string Part1() + { + return $"{_ids.Last()}"; + } + + public override string Part2() + { + // arithmetic sum of full series + return $"{(_ids.Count + 1) * (_ids.First() + _ids.Last()) / 2 - _ids.Sum()}"; + } +} diff --git a/aoc2020/Day06.cs b/aoc2020/Day06.cs index 36fe86d..b3275e6 100644 --- a/aoc2020/Day06.cs +++ b/aoc2020/Day06.cs @@ -1,54 +1,50 @@ -using System.Collections.Generic; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 6: +/// +public sealed class Day06 : Day { - /// - /// Day 6: - /// - public sealed class Day06 : Day + private readonly int _countPart1; + private readonly int _countPart2; + + public Day06() : base(6, "Custom Customs") { - private readonly int _countPart1; - private readonly int _countPart2; - - public Day06() : base(6, "Custom Customs") + var alphabet = "abcedfghijklmnopqrstuvwxyz".ToCharArray(); + _countPart1 = 0; + _countPart2 = 0; + var s = new HashSet(); + var lines = new HashSet(); + foreach (var line in Input) { - var alphabet = "abcedfghijklmnopqrstuvwxyz".ToCharArray(); - _countPart1 = 0; - _countPart2 = 0; - var s = new HashSet(); - var lines = new HashSet(); - foreach (var line in Input) - { - if (line == "") - { - _countPart1 += s.Count; - _countPart2 += alphabet.Count(a => lines.All(l => l.Contains(a))); - s.Clear(); - lines.Clear(); - continue; - } - - foreach (var c in line) - s.Add(c); - lines.Add(line); - } - - if (s.Any()) + if (line == "") { _countPart1 += s.Count; _countPart2 += alphabet.Count(a => lines.All(l => l.Contains(a))); + s.Clear(); + lines.Clear(); + continue; } + + foreach (var c in line) + s.Add(c); + lines.Add(line); } - public override string Part1() + if (s.Any()) { - return $"{_countPart1}"; - } - - public override string Part2() - { - return $"{_countPart2}"; + _countPart1 += s.Count; + _countPart2 += alphabet.Count(a => lines.All(l => l.Contains(a))); } } -} \ No newline at end of file + + public override string Part1() + { + return $"{_countPart1}"; + } + + public override string Part2() + { + return $"{_countPart2}"; + } +} diff --git a/aoc2020/Day07.cs b/aoc2020/Day07.cs index 4e8603a..b747d91 100644 --- a/aoc2020/Day07.cs +++ b/aoc2020/Day07.cs @@ -1,61 +1,57 @@ -using System.Collections.Generic; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 7: +/// +public sealed class Day07 : Day { - /// - /// Day 7: - /// - public sealed class Day07 : Day + private readonly Dictionary> _rules; + + public Day07() : base(7, "Handy Haversacks") { - private readonly Dictionary> _rules; - - public Day07() : base(7, "Handy Haversacks") - { - _rules = Input.Select(rule => - { - var spl = rule.Split(" bags contain ", 2); - var outer = string.Join(' ', spl[0].Split(' ').Take(2)); - var inner = spl[1].Split(", ").Select(ParseQuantity).Where(i => i != null); - return (outer, inner); - }) - .ToDictionary(t => t.outer, t => t.inner); - } - - private static (int, string)? ParseQuantity(string arg) - { - if (arg == "no other bags.") return null; - var words = arg.Split(' '); - return (int.Parse(words[0]), string.Join(' ', words[1..3])); - } - - private int Weight(string node) - { - return 1 + _rules[node].Sum(i => i.Value.Item1 * Weight(i.Value.Item2)); - } - - public override string Part1() - { - // breadth-first search with Queue - var start = new Queue(new[] {"shiny gold"}); - var p = new HashSet(); - string node; - while (true) + _rules = Input.Select(rule => { - node = start.Dequeue(); - foreach (var (container, contained) in _rules) - if (contained.Any(i => i.HasValue && i.Value.Item2 == node) && p.Add(container)) - start.Enqueue(container); - - if (!start.Any()) break; - } - - return $"{p.Count}"; - } - - public override string Part2() - { - return $"{Weight("shiny gold") - 1}"; - } + var spl = rule.Split(" bags contain ", 2); + var outer = string.Join(' ', spl[0].Split(' ').Take(2)); + var inner = spl[1].Split(", ").Select(ParseQuantity).Where(i => i != null); + return (outer, inner); + }) + .ToDictionary(t => t.outer, t => t.inner); } -} \ No newline at end of file + + private static (int, string)? ParseQuantity(string arg) + { + if (arg == "no other bags.") return null; + var words = arg.Split(' '); + return (int.Parse(words[0]), string.Join(' ', words[1..3])); + } + + private int Weight(string node) + { + return 1 + _rules[node].Sum(i => i.Value.Item1 * Weight(i.Value.Item2)); + } + + public override string Part1() + { + // breadth-first search with Queue + var start = new Queue(new[] { "shiny gold" }); + var p = new HashSet(); + string node; + while (true) + { + node = start.Dequeue(); + foreach (var (container, contained) in _rules) + if (contained.Any(i => i.HasValue && i.Value.Item2 == node) && p.Add(container)) + start.Enqueue(container); + + if (!start.Any()) break; + } + + return $"{p.Count}"; + } + + public override string Part2() + { + return $"{Weight("shiny gold") - 1}"; + } +} diff --git a/aoc2020/Day08.cs b/aoc2020/Day08.cs index 8d3c982..9aa74c9 100644 --- a/aoc2020/Day08.cs +++ b/aoc2020/Day08.cs @@ -1,77 +1,74 @@ -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 8: +/// +public sealed class Day08 : Day { - /// - /// Day 8: - /// - public sealed class Day08 : Day + private readonly (string instruction, int value)[] _instructions; + private int _accumulator; + private int _currentInstruction; + + public Day08() : base(8, "Handheld Halting") { - private readonly (string instruction, int value)[] _instructions; - private int _accumulator; - private int _currentInstruction; + _instructions = Input.Select(ParseLine).ToArray(); + } - public Day08() : base(8, "Handheld Halting") + private static (string, int) ParseLine(string line) + { + var spl = line.Split(' ', 2); + return (spl[0], int.Parse(spl[1])); + } + + private bool Halts() + { + _accumulator = 0; + _currentInstruction = 0; + var visited = new bool[_instructions.Length + 1]; + + while (!visited[_currentInstruction] && _currentInstruction < _instructions.Length) { - _instructions = Input.Select(ParseLine).ToArray(); - } + visited[_currentInstruction] = true; - private static (string, int) ParseLine(string line) - { - var spl = line.Split(' ', 2); - return (spl[0], int.Parse(spl[1])); - } - - private bool Halts() - { - _accumulator = 0; - _currentInstruction = 0; - var visited = new bool[_instructions.Length + 1]; - - while (!visited[_currentInstruction] && _currentInstruction < _instructions.Length) + switch (_instructions[_currentInstruction].instruction) { - visited[_currentInstruction] = true; - - switch (_instructions[_currentInstruction].instruction) - { - case "acc": - _accumulator += _instructions[_currentInstruction].value; - break; - case "jmp": - _currentInstruction += _instructions[_currentInstruction].value; - continue; - } - - _currentInstruction++; + case "acc": + _accumulator += _instructions[_currentInstruction].value; + break; + case "jmp": + _currentInstruction += _instructions[_currentInstruction].value; + continue; } - return _currentInstruction == _instructions.Length; + _currentInstruction++; } - public override string Part1() - { - Halts(); - return $"{_accumulator}"; - } - - public override string Part2() - { - for (var i = 0; i < _instructions.Length; i++) - // swap each nop and jmp and check if the program halts - if (_instructions[i].instruction == "nop") - { - _instructions[i].instruction = "jmp"; - if (Halts()) break; - _instructions[i].instruction = "nop"; - } - else if (_instructions[i].instruction == "jmp") - { - _instructions[i].instruction = "nop"; - if (Halts()) break; - _instructions[i].instruction = "jmp"; - } - - return $"{_accumulator}"; - } + return _currentInstruction == _instructions.Length; } -} \ No newline at end of file + + public override string Part1() + { + Halts(); + return $"{_accumulator}"; + } + + public override string Part2() + { + for (var i = 0; i < _instructions.Length; i++) + // swap each nop and jmp and check if the program halts + if (_instructions[i].instruction == "nop") + { + _instructions[i].instruction = "jmp"; + if (Halts()) break; + _instructions[i].instruction = "nop"; + } + else if (_instructions[i].instruction == "jmp") + { + _instructions[i].instruction = "nop"; + if (Halts()) break; + _instructions[i].instruction = "jmp"; + } + + return $"{_accumulator}"; + } +} diff --git a/aoc2020/Day09.cs b/aoc2020/Day09.cs index 10eebe1..082b1d2 100644 --- a/aoc2020/Day09.cs +++ b/aoc2020/Day09.cs @@ -1,52 +1,49 @@ -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 9: +/// +public sealed class Day09 : Day { - /// - /// Day 9: - /// - public sealed class Day09 : Day + private readonly long[] _list; + private long _part1; + + public Day09() : base(9, "Encoding Error") { - private readonly long[] _list; - private long _part1; - - public Day09() : base(9, "Encoding Error") - { - _list = Input.Select(long.Parse).ToArray(); - } - - public override string Part1() - { - for (var i = 25; i < _list.Length - 25; i++) - { - var preamble = _list[(i - 25)..i]; - if (!preamble.Any(num1 => preamble.Any(num2 => num1 + num2 == _list[i]))) - { - _part1 = _list[i]; - return $"{_part1}"; - } - } - - return ""; - } - - public override string Part2() - { - for (var i = 0; i < _list.Length; i++) - { - long sum = 0; - for (var j = i; j < _list.Length; j++) - { - sum += _list[j]; - if (sum > _part1) break; - if (sum != _part1) continue; - - var subset = _list[i..(j + 1)].ToArray(); - return $"{subset.Min() + subset.Max()}"; - } - } - - return ""; - } + _list = Input.Select(long.Parse).ToArray(); } -} \ No newline at end of file + + public override string Part1() + { + for (var i = 25; i < _list.Length - 25; i++) + { + var preamble = _list[(i - 25)..i]; + if (!preamble.Any(num1 => preamble.Any(num2 => num1 + num2 == _list[i]))) + { + _part1 = _list[i]; + return $"{_part1}"; + } + } + + return ""; + } + + public override string Part2() + { + for (var i = 0; i < _list.Length; i++) + { + long sum = 0; + for (var j = i; j < _list.Length; j++) + { + sum += _list[j]; + if (sum > _part1) break; + if (sum != _part1) continue; + + var subset = _list[i..(j + 1)].ToArray(); + return $"{subset.Min() + subset.Max()}"; + } + } + + return ""; + } +} diff --git a/aoc2020/Day10.cs b/aoc2020/Day10.cs index 9b0421a..a61cc2c 100644 --- a/aoc2020/Day10.cs +++ b/aoc2020/Day10.cs @@ -1,59 +1,55 @@ -using System; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 10: +/// +public sealed class Day10 : Day { - /// - /// Day 10: - /// - public sealed class Day10 : Day + private readonly int[] _adapters; + private readonly long[] _memo; + + public Day10() : base(10, "Adapter Array") { - private readonly int[] _adapters; - private readonly long[] _memo; - - public Day10() : base(10, "Adapter Array") - { - var parsed = Input.Select(int.Parse).ToArray(); - // add socket and device to the list - _adapters = parsed.Concat(new[] {0, parsed.Max() + 3}).OrderBy(i => i).ToArray(); - _memo = new long[_adapters.Length]; - } - - private long Connections(int i) - { - if (i == _adapters.Length - 1) _memo[i] = 1; - if (_memo[i] > 0) return _memo[i]; - - for (var j = i + 1; j <= i + 3 && j < _adapters.Length; j++) - if (_adapters[j] - _adapters[i] <= 3) - _memo[i] += Connections(j); - - return _memo[i]; - } - - public override string Part1() - { - var ones = 0; - var threes = 0; - - for (var i = 0; i < _adapters.Length - 1; i++) - switch (_adapters[i + 1] - _adapters[i]) - { - case 1: - ones++; - break; - case 3: - threes++; - break; - default: throw new Exception("something went wrong"); - } - - return $"{ones * threes}"; - } - - public override string Part2() - { - return $"{Connections(0)}"; - } + var parsed = Input.Select(int.Parse).ToArray(); + // add socket and device to the list + _adapters = parsed.Concat(new[] { 0, parsed.Max() + 3 }).OrderBy(i => i).ToArray(); + _memo = new long[_adapters.Length]; } -} \ No newline at end of file + + private long Connections(int i) + { + if (i == _adapters.Length - 1) _memo[i] = 1; + if (_memo[i] > 0) return _memo[i]; + + for (var j = i + 1; j <= i + 3 && j < _adapters.Length; j++) + if (_adapters[j] - _adapters[i] <= 3) + _memo[i] += Connections(j); + + return _memo[i]; + } + + public override string Part1() + { + var ones = 0; + var threes = 0; + + for (var i = 0; i < _adapters.Length - 1; i++) + switch (_adapters[i + 1] - _adapters[i]) + { + case 1: + ones++; + break; + case 3: + threes++; + break; + default: throw new Exception("something went wrong"); + } + + return $"{ones * threes}"; + } + + public override string Part2() + { + return $"{Connections(0)}"; + } +} diff --git a/aoc2020/Day11.cs b/aoc2020/Day11.cs index a9f5e0f..2a532bd 100644 --- a/aoc2020/Day11.cs +++ b/aoc2020/Day11.cs @@ -1,91 +1,87 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 11: +/// +public sealed class Day11 : Day { - /// - /// Day 11: - /// - public sealed class Day11 : Day + public Day11() : base(11, "Seating System") { - public Day11() : base(11, "Seating System") + } + + public override string Part1() + { + var prev = new LifeGame(Input); + + while (true) + { + var next = prev.StepPart1(); + var same = true; + for (var i = 0; i < next.Grid.Length; i++) + if (!next.Grid[i].SequenceEqual(prev.Grid[i])) + { + same = false; + break; + } + + if (same) break; + prev = next; + } + + return $"{prev.TotalSeated}"; + } + + public override string Part2() + { + var prev = new LifeGame(Input); + while (true) + { + var next = prev.StepPart2(); + var same = true; + for (var i = 0; i < next.Grid.Length; i++) + if (!next.Grid[i].SequenceEqual(prev.Grid[i])) + { + same = false; + break; + } + + if (same) break; + prev = next; + } + + return $"{prev.TotalSeated}"; + } + + private class LifeGame + { + private int _h, _w; + public char[][] Grid; + + public LifeGame(IEnumerable input) + { + Grid = input.Select(line => line.ToCharArray()).ToArray(); + _h = Grid.Length; + _w = Grid[0].Length; + } + + private LifeGame() { } - public override string Part1() + public int TotalSeated => + Grid.Sum(l => l.Count(c => c == '#')); + + private void PrintBoard() { - var prev = new LifeGame(Input); - - while (true) - { - var next = prev.StepPart1(); - var same = true; - for (var i = 0; i < next.Grid.Length; i++) - if (!next.Grid[i].SequenceEqual(prev.Grid[i])) - { - same = false; - break; - } - - if (same) break; - prev = next; - } - - return $"{prev.TotalSeated}"; + Console.Clear(); + foreach (var line in Grid) + Console.WriteLine(line); } - public override string Part2() + public LifeGame StepPart1() { - var prev = new LifeGame(Input); - while (true) - { - var next = prev.StepPart2(); - var same = true; - for (var i = 0; i < next.Grid.Length; i++) - if (!next.Grid[i].SequenceEqual(prev.Grid[i])) - { - same = false; - break; - } - - if (same) break; - prev = next; - } - - return $"{prev.TotalSeated}"; - } - - private class LifeGame - { - private int _h, _w; - public char[][] Grid; - - public LifeGame(IEnumerable input) - { - Grid = input.Select(line => line.ToCharArray()).ToArray(); - _h = Grid.Length; - _w = Grid[0].Length; - } - - private LifeGame() - { - } - - public int TotalSeated => - Grid.Sum(l => l.Count(c => c == '#')); - - private void PrintBoard() - { - Console.Clear(); - foreach (var line in Grid) - Console.WriteLine(line); - } - - public LifeGame StepPart1() - { - var next = new LifeGame {_h = _h, _w = _w, Grid = Grid.Select(s => s.ToArray()).ToArray()}; - for (var y = 0; y < _h; y++) + var next = new LifeGame { _h = _h, _w = _w, Grid = Grid.Select(s => s.ToArray()).ToArray() }; + for (var y = 0; y < _h; y++) for (var x = 0; x < _w; x++) next.Grid[y][x] = Grid[y][x] switch { @@ -94,29 +90,29 @@ namespace aoc2020 _ => Grid[y][x] }; - // next.PrintBoard(); - return next; - } + // next.PrintBoard(); + return next; + } - private char At(int y, int x) - { - return x < 0 || y < 0 || x >= _w || y >= _h ? '.' : Grid[y][x]; - } + private char At(int y, int x) + { + return x < 0 || y < 0 || x >= _w || y >= _h ? '.' : Grid[y][x]; + } - private int CountAdjacent(int y, int x) + private int CountAdjacent(int y, int x) + { + return new[] { - return new[] - { At(y - 1, x - 1), At(y - 1, x + 0), At(y - 1, x + 1), At(y + 0, x - 1), At(y + 0, x + 1), At(y + 1, x - 1), At(y + 1, x + 0), At(y + 1, x + 1) }.Count(c => c == '#'); - } + } - public LifeGame StepPart2() - { - var next = new LifeGame {_h = _h, _w = _w, Grid = Grid.Select(s => s.ToArray()).ToArray()}; - for (var y = 0; y < _h; y++) + public LifeGame StepPart2() + { + var next = new LifeGame { _h = _h, _w = _w, Grid = Grid.Select(s => s.ToArray()).ToArray() }; + for (var y = 0; y < _h; y++) for (var x = 0; x < _w; x++) next.Grid[y][x] = Grid[y][x] switch { @@ -125,33 +121,32 @@ namespace aoc2020 _ => Grid[y][x] }; - // next.PrintBoard(); - return next; - } + // next.PrintBoard(); + return next; + } - private int CanSee(int y, int x) + private int CanSee(int y, int x) + { + return new[] { - return new[] - { TraceRay(y, x, -1, -1), TraceRay(y, x, -1, +0), TraceRay(y, x, -1, +1), TraceRay(y, x, +0, -1), TraceRay(y, x, +0, +1), TraceRay(y, x, +1, -1), TraceRay(y, x, +1, +0), TraceRay(y, x, +1, +1) }.Count(c => c == '#'); - } + } - private char TraceRay(int y, int x, int dy, int dx) + private char TraceRay(int y, int x, int dy, int dx) + { + y += dy; + x += dx; + while (y >= 0 && y < _h && x >= 0 && x < _w) { + if (Grid[y][x] != '.') return Grid[y][x]; y += dy; x += dx; - while (y >= 0 && y < _h && x >= 0 && x < _w) - { - if (Grid[y][x] != '.') return Grid[y][x]; - y += dy; - x += dx; - } - - return '.'; } + + return '.'; } } -} \ No newline at end of file +} diff --git a/aoc2020/Day12.cs b/aoc2020/Day12.cs index 151a9a0..02787ff 100644 --- a/aoc2020/Day12.cs +++ b/aoc2020/Day12.cs @@ -1,94 +1,89 @@ -using System; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 12: +/// +public sealed class Day12 : Day { - /// - /// Day 12: - /// - public sealed class Day12 : Day + public Day12() : base(12, "Rain Risk") { - public Day12() : base(12, "Rain Risk") - { - } - - private static void Swap(ref int x, ref int y) - { - var tmp = x; - x = y; - y = tmp; - } - - private (int x, int y, int sx, int sy) ProcessInstructions() - { - // start facing east - int x = 0, y = 0, dx = 1, dy = 0; - int sx = 0, sy = 0, waypointX = 10, waypointY = -1; - - foreach (var instruction in Input) - { - var value = int.Parse(instruction[1..]); - - switch (instruction[0]) - { - case 'N': - y -= value; - waypointY -= value; - break; - case 'S': - y += value; - waypointY += value; - break; - case 'E': - x += value; - waypointX += value; - break; - case 'W': - x -= value; - waypointX -= value; - break; - case 'L': - for (var i = 0; i < value / 90; ++i) - { - Swap(ref dx, ref dy); - Swap(ref waypointX, ref waypointY); - dy *= -1; - waypointY *= -1; - } - - break; - case 'R': - for (var i = 0; i < value / 90; ++i) - { - Swap(ref dx, ref dy); - Swap(ref waypointX, ref waypointY); - dx *= -1; - waypointX *= -1; - } - - break; - case 'F': - x += dx * value; - y += dy * value; - sx += waypointX * value; - sy += waypointY * value; - break; - default: throw new InvalidOperationException(nameof(instruction)); - } - } - - return (x, y, sx, sy); - } - - public override string Part1() - { - var (x, y, _, _) = ProcessInstructions(); - return $"{Math.Abs(x) + Math.Abs(y)}"; - } - - public override string Part2() - { - var (_, _, sx, sy) = ProcessInstructions(); - return $"{Math.Abs(sx) + Math.Abs(sy)}"; - } } -} \ No newline at end of file + + private static void Swap(ref int x, ref int y) + { + (y, x) = (x, y); + } + + private (int x, int y, int sx, int sy) ProcessInstructions() + { + // start facing east + int x = 0, y = 0, dx = 1, dy = 0; + int sx = 0, sy = 0, waypointX = 10, waypointY = -1; + + foreach (var instruction in Input) + { + var value = int.Parse(instruction[1..]); + + switch (instruction[0]) + { + case 'N': + y -= value; + waypointY -= value; + break; + case 'S': + y += value; + waypointY += value; + break; + case 'E': + x += value; + waypointX += value; + break; + case 'W': + x -= value; + waypointX -= value; + break; + case 'L': + for (var i = 0; i < value / 90; ++i) + { + Swap(ref dx, ref dy); + Swap(ref waypointX, ref waypointY); + dy *= -1; + waypointY *= -1; + } + + break; + case 'R': + for (var i = 0; i < value / 90; ++i) + { + Swap(ref dx, ref dy); + Swap(ref waypointX, ref waypointY); + dx *= -1; + waypointX *= -1; + } + + break; + case 'F': + x += dx * value; + y += dy * value; + sx += waypointX * value; + sy += waypointY * value; + break; + default: throw new InvalidOperationException(nameof(instruction)); + } + } + + return (x, y, sx, sy); + } + + public override string Part1() + { + var (x, y, _, _) = ProcessInstructions(); + return $"{Math.Abs(x) + Math.Abs(y)}"; + } + + public override string Part2() + { + var (_, _, sx, sy) = ProcessInstructions(); + return $"{Math.Abs(sx) + Math.Abs(sy)}"; + } +} diff --git a/aoc2020/Day13.cs b/aoc2020/Day13.cs index 94fd815..c4219d2 100644 --- a/aoc2020/Day13.cs +++ b/aoc2020/Day13.cs @@ -1,54 +1,51 @@ -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 13: +/// +public sealed class Day13 : Day { - /// - /// Day 13: - /// - public sealed class Day13 : Day + private readonly long[] _buses; + private readonly long _earliest; + private readonly string[] _fullSchedule; + + public Day13() : base(13, "Shuttle Search") { - private readonly long[] _buses; - private readonly long _earliest; - private readonly string[] _fullSchedule; + _earliest = long.Parse(Input.First()); + _fullSchedule = Input.Last().Split(','); + _buses = _fullSchedule.Where(c => c != "x").Select(long.Parse).ToArray(); + } - public Day13() : base(13, "Shuttle Search") - { - _earliest = long.Parse(Input.First()); - _fullSchedule = Input.Last().Split(','); - _buses = _fullSchedule.Where(c => c != "x").Select(long.Parse).ToArray(); - } - - public override string Part1() - { - for (var i = _earliest;; i++) - if (_buses.Any(b => i % b == 0)) - { - var bus = _buses.First(b => i % b == 0); - return $"{bus * (i - _earliest)}"; - } - } - - public override string Part2() - { - var i = 0; - long result = 1, multiplier = 1; - - foreach (var id in _fullSchedule) + public override string Part1() + { + for (var i = _earliest; ; i++) + if (_buses.Any(b => i % b == 0)) { - if (id != "x") - { - var increment = long.Parse(id); - while (((result += multiplier) + i) % increment != 0) - { - } + var bus = _buses.First(b => i % b == 0); + return $"{bus * (i - _earliest)}"; + } + } - multiplier *= increment; + public override string Part2() + { + var i = 0; + long result = 1, multiplier = 1; + + foreach (var id in _fullSchedule) + { + if (id != "x") + { + var increment = long.Parse(id); + while (((result += multiplier) + i) % increment != 0) + { } - i++; + multiplier *= increment; } - return $"{result}"; + i++; } + + return $"{result}"; } -} \ No newline at end of file +} diff --git a/aoc2020/Day14.cs b/aoc2020/Day14.cs index 3252cc9..156aa16 100644 --- a/aoc2020/Day14.cs +++ b/aoc2020/Day14.cs @@ -1,111 +1,106 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 14: +/// +public sealed class Day14 : Day { - /// - /// Day 14: - /// - public sealed class Day14 : Day + public Day14() : base(14, "Docking Data") { - public Day14() : base(14, "Docking Data") - { - } + } - public override string Part1() - { - var writes = new Dictionary(); - ulong mask = 0, bits = 0; + public override string Part1() + { + var writes = new Dictionary(); + ulong mask = 0, bits = 0; - foreach (var line in Input) - if (line.StartsWith("mask = ")) - { - var str = line.Split("mask = ", 2)[1]; - mask = bits = 0; - for (var i = 35; i >= 0; --i) - switch (str[35 - i]) - { - case 'X': - mask |= 1UL << i; - break; - case '1': - bits |= 1UL << i; - break; - } - } - else - { - var spl = line.Split(new[] {'[', ']', '='}, - StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - .Skip(1) - .Select(ulong.Parse) - .ToArray(); - - writes[spl[0]] = (spl[1] & mask) | bits; - } - - return $"{writes.Aggregate, ulong>(0, (current, w) => current + w.Value)}"; - } - - public override string Part2() - { - var memory = new Dictionary(); - var mask = ""; - - foreach (var line in Input) + foreach (var line in Input) + if (line.StartsWith("mask = ")) { - var spl = line.Split(' ', 3, StringSplitOptions.TrimEntries); - - if (spl[0] == "mask") - { - mask = spl[2]; - } - else - { - var value = ulong.Parse(spl[2]); - var addr = ulong.Parse(spl[0].Split(new[] {'[', ']'}, - StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)[1]); - - var floats = new List(); - for (var i = 0; i < mask.Length; i++) - switch (mask[i]) - { - case 'X': - floats.Add(i); - break; - case '1': - addr |= 1UL << (35 - i); - break; - } - - if (floats.Any()) + var str = line.Split("mask = ", 2)[1]; + mask = bits = 0; + for (var i = 35; i >= 0; --i) + switch (str[35 - i]) { - var combos = new List {addr}; - - foreach (var i in floats) - { - var newCombos = new List(); - foreach (var c in combos) - { - newCombos.Add(c | (1UL << (35 - i))); - newCombos.Add(c & ~(1UL << (35 - i))); - } - - combos = newCombos; - } - - foreach (var c in combos) - memory[c] = value; + case 'X': + mask |= 1UL << i; + break; + case '1': + bits |= 1UL << i; + break; } - else - { - memory[addr] = value; - } - } + } + else + { + var spl = line.Split(new[] { '[', ']', '=' }, + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Skip(1) + .Select(ulong.Parse) + .ToArray(); + + writes[spl[0]] = (spl[1] & mask) | bits; } - return $"{memory.Aggregate, ulong>(0, (current, w) => current + w.Value)}"; - } + return $"{writes.Aggregate, ulong>(0, (current, w) => current + w.Value)}"; } -} \ No newline at end of file + + public override string Part2() + { + var memory = new Dictionary(); + var mask = ""; + + foreach (var line in Input) + { + var spl = line.Split(' ', 3, StringSplitOptions.TrimEntries); + + if (spl[0] == "mask") + { + mask = spl[2]; + } + else + { + var value = ulong.Parse(spl[2]); + var addr = ulong.Parse(spl[0].Split(new[] { '[', ']' }, + StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)[1]); + + var floats = new List(); + for (var i = 0; i < mask.Length; i++) + switch (mask[i]) + { + case 'X': + floats.Add(i); + break; + case '1': + addr |= 1UL << (35 - i); + break; + } + + if (floats.Any()) + { + var combos = new List { addr }; + + foreach (var i in floats) + { + var newCombos = new List(); + foreach (var c in combos) + { + newCombos.Add(c | (1UL << (35 - i))); + newCombos.Add(c & ~(1UL << (35 - i))); + } + + combos = newCombos; + } + + foreach (var c in combos) + memory[c] = value; + } + else + { + memory[addr] = value; + } + } + } + + return $"{memory.Aggregate, ulong>(0, (current, w) => current + w.Value)}"; + } +} diff --git a/aoc2020/Day15.cs b/aoc2020/Day15.cs index 2d69a6c..6eb0bd9 100644 --- a/aoc2020/Day15.cs +++ b/aoc2020/Day15.cs @@ -1,48 +1,45 @@ -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 15: +/// +public sealed class Day15 : Day { - /// - /// Day 15: - /// - public sealed class Day15 : Day + private readonly int[] _turns; + private int _current; + private int _i; + + public Day15() : base(15, "Rambunctious Recitation") { - private readonly int[] _turns; - private int _current; - private int _i; + var initial = Input.First().Split(',').Select(int.Parse).ToArray(); + _turns = new int[30_000_000]; - public Day15() : base(15, "Rambunctious Recitation") - { - var initial = Input.First().Split(',').Select(int.Parse).ToArray(); - _turns = new int[30_000_000]; - - // seed array with initial values - for (_i = 1; _i < initial.Length + 1; _i++) - _turns[initial[_i - 1]] = _i; - } - - public override string Part1() - { - for (; _i != 2020; _i++) - { - var next = _turns[_current] > 0 ? _i - _turns[_current] : 0; - _turns[_current] = _i; - _current = next; - } - - return $"{_current}"; - } - - public override string Part2() - { - for (; _i != 30_000_000; _i++) - { - var next = _turns[_current] > 0 ? _i - _turns[_current] : 0; - _turns[_current] = _i; - _current = next; - } - - return $"{_current}"; - } + // seed array with initial values + for (_i = 1; _i < initial.Length + 1; _i++) + _turns[initial[_i - 1]] = _i; } -} \ No newline at end of file + + public override string Part1() + { + for (; _i != 2020; _i++) + { + var next = _turns[_current] > 0 ? _i - _turns[_current] : 0; + _turns[_current] = _i; + _current = next; + } + + return $"{_current}"; + } + + public override string Part2() + { + for (; _i != 30_000_000; _i++) + { + var next = _turns[_current] > 0 ? _i - _turns[_current] : 0; + _turns[_current] = _i; + _current = next; + } + + return $"{_current}"; + } +} diff --git a/aoc2020/Day16.cs b/aoc2020/Day16.cs index 8057cf7..63fa5c9 100644 --- a/aoc2020/Day16.cs +++ b/aoc2020/Day16.cs @@ -1,91 +1,86 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 16: +/// +public sealed class Day16 : Day { - /// - /// Day 16: - /// - public sealed class Day16 : Day + private readonly Dictionary> _rules; + private readonly List> _tickets; + + public Day16() : base(16, "Ticket Translation") { - private readonly Dictionary> _rules; - private readonly List> _tickets; + _tickets = new(); + _rules = new(); - public Day16() : base(16, "Ticket Translation") + foreach (var line in Input) { - _tickets = new List>(); - _rules = new Dictionary>(); - - foreach (var line in Input) + var spl = line.Split(": ", 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + if (spl.Length == 2) { - var spl = line.Split(": ", 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); - if (spl.Length == 2) + var rule = spl[1].Split(" or ").Select(s => { - var rule = spl[1].Split(" or ").Select(s => - { - var r = s.Split('-').Select(int.Parse).ToList(); - return new Range(r[0], r[1]); - }).ToList(); + var r = s.Split('-').Select(int.Parse).ToList(); + return new Range(r[0], r[1]); + }).ToList(); - _rules.Add(spl[0], rule); - } - else - { - spl = line.Split(','); - if (spl.Length > 1) - _tickets.Add(spl.Select(int.Parse).ToList()); - } + _rules.Add(spl[0], rule); + } + else + { + spl = line.Split(','); + if (spl.Length > 1) + _tickets.Add(spl.Select(int.Parse).ToList()); } } - - public override string Part1() - { - var allValues = _tickets.Skip(1).SelectMany(t => t); - var allRules = _rules.Values.SelectMany(r => r); - return - $"{allValues.Where(t => !allRules.Any(r => r.Contains(t))).Sum()}"; - } - - public override string Part2() - { - var ticketFields = _tickets - // valid tickets - .Where(ticket => ticket - .All(t => _rules.Values - .SelectMany(r => r) - .Any(r => r.Contains(t)))) - // group by index - .SelectMany(inner => inner.Select((item, index) => new {item, index})) - .GroupBy(i => i.index, i => i.item) - .Select(g => g.ToList()) - .Select((val, i) => new {Value = val, Index = i}) - .ToList(); - - var matchedRules = _rules - // find matching rules and indices - .SelectMany(x => ticketFields - .Where(y => y.Value.All(z => x.Value.Any(r => r.Contains(z)))) - .Select(y => (x.Key, y.Index)) - .ToList()) - .ToList(); - - matchedRules.Sort((a, b) => - matchedRules.Count(x => x.Key == a.Key) - matchedRules.Count(x => x.Key == b.Key)); - - while (matchedRules.Any(x => matchedRules.Count(y => y.Key == x.Key) > 1)) - foreach (var (key, index) in matchedRules.Where(y => - matchedRules.Count(z => z.Key == y.Key) == 1 && - matchedRules.Count(z => z.Index == y.Index) > 1)) - // filter matches by index - matchedRules = matchedRules - .Where(x => x.Index != index || x.Key == key) - .ToList(); - - var departureFields = matchedRules.Where(r => r.Key.StartsWith("departure")); - - return - $"{departureFields.Aggregate(1L, (l, match) => l * _tickets.First()[match.Index])}"; - } } -} \ No newline at end of file + + public override string Part1() + { + var allValues = _tickets.Skip(1).SelectMany(t => t); + var allRules = _rules.Values.SelectMany(r => r); + return + $"{allValues.Where(t => !allRules.Any(r => r.Contains(t))).Sum()}"; + } + + public override string Part2() + { + var ticketFields = _tickets + // valid tickets + .Where(ticket => ticket + .All(t => _rules.Values + .SelectMany(r => r) + .Any(r => r.Contains(t)))) + // group by index + .SelectMany(inner => inner.Select((item, index) => new { item, index })) + .GroupBy(i => i.index, i => i.item) + .Select(g => g.ToList()) + .Select((val, i) => new { Value = val, Index = i }) + .ToList(); + + var matchedRules = _rules + // find matching rules and indices + .SelectMany(x => ticketFields + .Where(y => y.Value.All(z => x.Value.Any(r => r.Contains(z)))) + .Select(y => (x.Key, y.Index)) + .ToList()) + .ToList(); + + matchedRules.Sort((a, b) => + matchedRules.Count(x => x.Key == a.Key) - matchedRules.Count(x => x.Key == b.Key)); + + while (matchedRules.Any(x => matchedRules.Count(y => y.Key == x.Key) > 1)) + foreach (var (key, index) in matchedRules.Where(y => + matchedRules.Count(z => z.Key == y.Key) == 1 && + matchedRules.Count(z => z.Index == y.Index) > 1)) + // filter matches by index + matchedRules = matchedRules + .Where(x => x.Index != index || x.Key == key) + .ToList(); + + var departureFields = matchedRules.Where(r => r.Key.StartsWith("departure")); + + return + $"{departureFields.Aggregate(1L, (l, match) => l * _tickets.First()[match.Index])}"; + } +} diff --git a/aoc2020/Day17.cs b/aoc2020/Day17.cs index cee6c3f..504d231 100644 --- a/aoc2020/Day17.cs +++ b/aoc2020/Day17.cs @@ -1,130 +1,126 @@ -using System.Collections.Generic; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 17: +/// +public sealed class Day17 : Day { - /// - /// Day 17: - /// - public sealed class Day17 : Day + private readonly Dictionary<(int x, int y, int z), char> _plane; + private readonly Dictionary<(int x, int y, int z, int w), char> _plane4; + + + public Day17() : base(17, "Conway Cubes") { - private readonly Dictionary<(int x, int y, int z), char> _plane; - private readonly Dictionary<(int x, int y, int z, int w), char> _plane4; + _plane = new Dictionary<(int x, int y, int z), char>(); + _plane4 = new Dictionary<(int x, int y, int z, int w), char>(); + var input = Input.ToList(); - - public Day17() : base(17, "Conway Cubes") - { - _plane = new Dictionary<(int x, int y, int z), char>(); - _plane4 = new Dictionary<(int x, int y, int z, int w), char>(); - var input = Input.ToList(); - - for (var x = 0; x < 32; x++) + for (var x = 0; x < 32; x++) for (var y = 0; y < 32; y++) - for (var z = 0; z < 32; z++) - _plane[(x, y, z)] = '.'; + for (var z = 0; z < 32; z++) + _plane[(x, y, z)] = '.'; - for (var x = 0; x < 32; x++) + for (var x = 0; x < 32; x++) for (var y = 0; y < 32; y++) - for (var z = 0; z < 32; z++) - for (var w = 0; w < 32; w++) - _plane4[(x, y, z, w)] = '.'; + for (var z = 0; z < 32; z++) + for (var w = 0; w < 32; w++) + _plane4[(x, y, z, w)] = '.'; - for (var y = 0; y < input.Count; y++) + for (var y = 0; y < input.Count; y++) for (var x = 0; x < input[y].Length; x++) _plane[(x, y, 0)] = input[y][x]; - for (var y = 0; y < input.Count; y++) + for (var y = 0; y < input.Count; y++) for (var x = 0; x < input[y].Length; x++) _plane4[(x, y, 0, 0)] = input[y][x]; - } - - private static int Neighbors(IReadOnlyDictionary<(int x, int y, int z), char> plane, int x, int y, int z) - { - var neighbors = 0; - - foreach (var i in new[] {-1, 0, 1}) - foreach (var j in new[] {-1, 0, 1}) - foreach (var k in new[] {-1, 0, 1}) - { - if (i == 0 && j == 0 && k == 0) continue; - if (plane[((x + i) & 31, (y + j) & 31, (z + k) & 31)] == '#') neighbors++; - } - - return neighbors; - } - - private static Dictionary<(int x, int y, int z), char> Iterate( - IReadOnlyDictionary<(int x, int y, int z), char> prev) - { - var next = new Dictionary<(int x, int y, int z), char>(); - - for (var z = 0; z < 32; z++) - for (var y = 0; y < 32; y++) - for (var x = 0; x < 32; x++) - { - var active = Neighbors(prev, x, y, z); - if (prev[(x, y, z)] == '#') - next[(x, y, z)] = active == 2 || active == 3 ? '#' : '.'; - else - next[(x, y, z)] = active == 3 ? '#' : '.'; - } - - return next; - } - - private static int Neighbors4(IReadOnlyDictionary<(int x, int y, int z, int w), char> plane, int x, int y, - int z, int w) - { - var neighbors = 0; - - foreach (var i in new[] {-1, 0, 1}) - foreach (var j in new[] {-1, 0, 1}) - foreach (var k in new[] {-1, 0, 1}) - foreach (var l in new[] {-1, 0, 1}) - { - if (i == 0 && j == 0 && k == 0 && l == 0) continue; - if (plane[((x + i) & 31, (y + j) & 31, (z + k) & 31, (w + l) & 31)] == '#') neighbors++; - } - - return neighbors; - } - - private static Dictionary<(int x, int y, int z, int w), char> Iterate4( - IReadOnlyDictionary<(int x, int y, int z, int w), char> prev) - { - var next = new Dictionary<(int x, int y, int z, int w), char>(); - - for (var z = 0; z < 32; z++) - for (var y = 0; y < 32; y++) - for (var x = 0; x < 32; x++) - for (var w = 0; w < 32; w++) - { - var active = Neighbors4(prev, x, y, z, w); - if (prev[(x, y, z, w)] == '#') - next[(x, y, z, w)] = active == 2 || active == 3 ? '#' : '.'; - else - next[(x, y, z, w)] = active == 3 ? '#' : '.'; - } - - return next; - } - - public override string Part1() - { - var plane = _plane; - foreach (var _ in Enumerable.Range(0, 6)) - plane = Iterate(plane); - - return $"{plane.Values.Count(v => v == '#')}"; - } - - public override string Part2() - { - var plane = _plane4; - foreach (var _ in Enumerable.Range(0, 6)) - plane = Iterate4(plane); - - return $"{plane.Values.Count(v => v == '#')}"; - } } -} \ No newline at end of file + + private static int Neighbors(IReadOnlyDictionary<(int x, int y, int z), char> plane, int x, int y, int z) + { + var neighbors = 0; + + foreach (var i in new[] { -1, 0, 1 }) + foreach (var j in new[] { -1, 0, 1 }) + foreach (var k in new[] { -1, 0, 1 }) + { + if (i == 0 && j == 0 && k == 0) continue; + if (plane[((x + i) & 31, (y + j) & 31, (z + k) & 31)] == '#') neighbors++; + } + + return neighbors; + } + + private static Dictionary<(int x, int y, int z), char> Iterate( + IReadOnlyDictionary<(int x, int y, int z), char> prev) + { + var next = new Dictionary<(int x, int y, int z), char>(); + + for (var z = 0; z < 32; z++) + for (var y = 0; y < 32; y++) + for (var x = 0; x < 32; x++) + { + var active = Neighbors(prev, x, y, z); + if (prev[(x, y, z)] == '#') + next[(x, y, z)] = active == 2 || active == 3 ? '#' : '.'; + else + next[(x, y, z)] = active == 3 ? '#' : '.'; + } + + return next; + } + + private static int Neighbors4(IReadOnlyDictionary<(int x, int y, int z, int w), char> plane, int x, int y, + int z, int w) + { + var neighbors = 0; + + foreach (var i in new[] { -1, 0, 1 }) + foreach (var j in new[] { -1, 0, 1 }) + foreach (var k in new[] { -1, 0, 1 }) + foreach (var l in new[] { -1, 0, 1 }) + { + if (i == 0 && j == 0 && k == 0 && l == 0) continue; + if (plane[((x + i) & 31, (y + j) & 31, (z + k) & 31, (w + l) & 31)] == '#') neighbors++; + } + + return neighbors; + } + + private static Dictionary<(int x, int y, int z, int w), char> Iterate4( + IReadOnlyDictionary<(int x, int y, int z, int w), char> prev) + { + var next = new Dictionary<(int x, int y, int z, int w), char>(); + + for (var z = 0; z < 32; z++) + for (var y = 0; y < 32; y++) + for (var x = 0; x < 32; x++) + for (var w = 0; w < 32; w++) + { + var active = Neighbors4(prev, x, y, z, w); + if (prev[(x, y, z, w)] == '#') + next[(x, y, z, w)] = active == 2 || active == 3 ? '#' : '.'; + else + next[(x, y, z, w)] = active == 3 ? '#' : '.'; + } + + return next; + } + + public override string Part1() + { + var plane = _plane; + foreach (var _ in Enumerable.Range(0, 6)) + plane = Iterate(plane); + + return $"{plane.Values.Count(v => v == '#')}"; + } + + public override string Part2() + { + var plane = _plane4; + foreach (var _ in Enumerable.Range(0, 6)) + plane = Iterate4(plane); + + return $"{plane.Values.Count(v => v == '#')}"; + } +} diff --git a/aoc2020/Day18.cs b/aoc2020/Day18.cs index 08821d3..68c4498 100644 --- a/aoc2020/Day18.cs +++ b/aoc2020/Day18.cs @@ -1,81 +1,75 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 18: +/// +public sealed class Day18 : Day { - /// - /// Day 18: - /// - public sealed class Day18 : Day + private readonly List _expressions; + + public Day18() : base(18, "Operation Order") { - private readonly List _expressions; - - public Day18() : base(18, "Operation Order") - { - _expressions = Input.Select(line => line.Replace(" ", "")).ToList(); - } - - private static long Calculate(string expr, Func precedence) - { - var postfixNotation = new StringBuilder(); - var postfixStack = new Stack(); - - foreach (var c in expr) - if (char.IsDigit(c)) - { - postfixNotation.Append(c); - } - else if (c == '(') - { - postfixStack.Push(c); - } - else if (c == ')') - { - while (postfixStack.Count > 0 && postfixStack.Peek() != '(') - postfixNotation.Append(postfixStack.Pop()); - - postfixStack.TryPop(out _); - } - else - { - while (postfixStack.Count > 0 && precedence(c) <= precedence(postfixStack.Peek())) - postfixNotation.Append(postfixStack.Pop()); - - postfixStack.Push(c); - } - - while (postfixStack.Count > 0) - postfixNotation.Append(postfixStack.Pop()); - - var expressionStack = new Stack(); - - foreach (var c in postfixNotation.ToString()) - if (char.IsDigit(c)) - { - expressionStack.Push((long) char.GetNumericValue(c)); - } - else - { - var a = expressionStack.Pop(); - var b = expressionStack.Pop(); - - if (c == '+') expressionStack.Push(a + b); - if (c == '*') expressionStack.Push(a * b); - } - - return expressionStack.Pop(); - } - - public override string Part1() - { - return $"{_expressions.Sum(expr => Calculate(expr, c => c == '+' || c == '*' ? 1 : 0))}"; - } - - public override string Part2() - { - return $"{_expressions.Sum(expr => Calculate(expr, c => c switch {'+' => 2, '*' => 1, _ => 0}))}"; - } + _expressions = Input.Select(line => line.Replace(" ", "")).ToList(); } -} \ No newline at end of file + + private static long Calculate(string expr, Func precedence) + { + var postfixNotation = new StringBuilder(); + var postfixStack = new Stack(); + + foreach (var c in expr) + if (char.IsDigit(c)) + { + postfixNotation.Append(c); + } + else if (c == '(') + { + postfixStack.Push(c); + } + else if (c == ')') + { + while (postfixStack.Count > 0 && postfixStack.Peek() != '(') + postfixNotation.Append(postfixStack.Pop()); + + postfixStack.TryPop(out _); + } + else + { + while (postfixStack.Count > 0 && precedence(c) <= precedence(postfixStack.Peek())) + postfixNotation.Append(postfixStack.Pop()); + + postfixStack.Push(c); + } + + while (postfixStack.Count > 0) + postfixNotation.Append(postfixStack.Pop()); + + var expressionStack = new Stack(); + + foreach (var c in postfixNotation.ToString()) + if (char.IsDigit(c)) + { + expressionStack.Push((long)char.GetNumericValue(c)); + } + else + { + var a = expressionStack.Pop(); + var b = expressionStack.Pop(); + + if (c == '+') expressionStack.Push(a + b); + if (c == '*') expressionStack.Push(a * b); + } + + return expressionStack.Pop(); + } + + public override string Part1() + { + return $"{_expressions.Sum(expr => Calculate(expr, c => c == '+' || c == '*' ? 1 : 0))}"; + } + + public override string Part2() + { + return $"{_expressions.Sum(expr => Calculate(expr, c => c switch { '+' => 2, '*' => 1, _ => 0 }))}"; + } +} diff --git a/aoc2020/Day19.cs b/aoc2020/Day19.cs index 009bf45..375a36f 100644 --- a/aoc2020/Day19.cs +++ b/aoc2020/Day19.cs @@ -1,57 +1,51 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 19: +/// +public sealed class Day19 : Day { - /// - /// Day 19: - /// - public sealed class Day19 : Day + private readonly string[] _messages; + private readonly Dictionary _rules; + private readonly Stack _stack; + + public Day19() : base(19, "Monster Messages") { - private readonly string[] _messages; - private readonly Dictionary _rules; - private readonly Stack _stack; - - public Day19() : base(19, "Monster Messages") - { - _rules = Input.TakeWhile(l => !string.IsNullOrWhiteSpace(l)) - .Select(l => l.Split(':')) - .Select(a => (key: a[0], - val: a[1].Split('|', StringSplitOptions.RemoveEmptyEntries) - .Select(s => s.Split(' ', StringSplitOptions.RemoveEmptyEntries)).ToArray())) - .ToDictionary(a => a.key, a => a.val); - _messages = Input.Skip(_rules.Count + 1).ToArray(); - _stack = new Stack(); - } - - private string MakeRegexExpression(string key) - { - if (_stack.Count(s => s == key) > 10) return "x"; - _stack.Push(key); - var sub = string.Join("|", _rules[key].Select(test => test.Length switch - { - 1 => test[0][0] == '"' ? test[0].Trim('"') : MakeRegexExpression(test[0]), - _ => string.Join(string.Empty, test.Select(MakeRegexExpression)) - })); - _stack.Pop(); - return _rules[key].Length > 1 ? $"({sub})" : sub; - } - - public override string Part1() - { - var exp = new Regex($"^{MakeRegexExpression("0")}$"); - return $"{_messages.Count(m => exp.IsMatch(m))}"; - } - - public override string Part2() - { - // fix rules 8 and 11 - _rules["8"] = new[] {new[] {"42"}, new[] {"42", "8"}}; - _rules["11"] = new[] {new[] {"42", "31"}, new[] {"42", "11", "31"}}; - var exp = new Regex($"^{MakeRegexExpression("0")}$"); - return $"{_messages.Count(m => exp.IsMatch(m))}"; - } + _rules = Input.TakeWhile(l => !string.IsNullOrWhiteSpace(l)) + .Select(l => l.Split(':')) + .Select(a => (key: a[0], + val: a[1].Split('|', StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Split(' ', StringSplitOptions.RemoveEmptyEntries)).ToArray())) + .ToDictionary(a => a.key, a => a.val); + _messages = Input.Skip(_rules.Count + 1).ToArray(); + _stack = new Stack(); } -} \ No newline at end of file + + private string MakeRegexExpression(string key) + { + if (_stack.Count(s => s == key) > 10) return "x"; + _stack.Push(key); + var sub = string.Join("|", _rules[key].Select(test => test.Length switch + { + 1 => test[0][0] == '"' ? test[0].Trim('"') : MakeRegexExpression(test[0]), + _ => string.Join(string.Empty, test.Select(MakeRegexExpression)) + })); + _stack.Pop(); + return _rules[key].Length > 1 ? $"({sub})" : sub; + } + + public override string Part1() + { + var exp = new Regex($"^{MakeRegexExpression("0")}$"); + return $"{_messages.Count(m => exp.IsMatch(m))}"; + } + + public override string Part2() + { + // fix rules 8 and 11 + _rules["8"] = new[] { new[] { "42" }, new[] { "42", "8" } }; + _rules["11"] = new[] { new[] { "42", "31" }, new[] { "42", "11", "31" } }; + var exp = new Regex($"^{MakeRegexExpression("0")}$"); + return $"{_messages.Count(m => exp.IsMatch(m))}"; + } +} diff --git a/aoc2020/Day20.cs b/aoc2020/Day20.cs index f2eadc9..bf03eb8 100644 --- a/aoc2020/Day20.cs +++ b/aoc2020/Day20.cs @@ -1,183 +1,178 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace aoc2020; -namespace aoc2020 +/// +/// Day 20: +/// +public sealed class Day20 : Day { - /// - /// Day 20: - /// - public sealed class Day20 : Day + private readonly List _allPermutations; + private readonly List _topLefts; + + public Day20() : base(20, "Jurassic Jigsaw") { - private readonly List _allPermutations; - private readonly List _topLefts; + var lines = new List(); + var tiles = new List(); + var currentTileId = 0; - public Day20() : base(20, "Jurassic Jigsaw") - { - var lines = new List(); - var tiles = new List(); - var currentTileId = 0; - - foreach (var line in Input) - if (line.StartsWith("Tile ")) - { - currentTileId = int.Parse(line.Split(' ', ':')[1]); - } - else if (line == "") - { - tiles.Add(new(currentTileId, lines.Select(l => l.ToCharArray()).ToArray())); - lines.Clear(); - } - else - { - lines.Add(line); - } - - if (lines.Any()) tiles.Add(new(currentTileId, lines.Select(l => l.ToCharArray()).ToArray())); - - _allPermutations = tiles.SelectMany(t => t.AllPermutations()).ToList(); - _topLefts = _allPermutations - .Where(t => !_allPermutations.Any(t2 => t.TileId != t2.TileId && t.LeftId == t2.RightId) && - !_allPermutations.Any(t2 => t.TileId != t2.TileId && t.TopId == t2.BottomId)) - .ToList(); - } - - private int Roughness(Tile arg) - { - var seaMonster = new[] + foreach (var line in Input) + if (line.StartsWith("Tile ")) { + currentTileId = int.Parse(line.Split(' ', ':')[1]); + } + else if (line == "") + { + tiles.Add(new(currentTileId, lines.Select(l => l.ToCharArray()).ToArray())); + lines.Clear(); + } + else + { + lines.Add(line); + } + + if (lines.Any()) tiles.Add(new(currentTileId, lines.Select(l => l.ToCharArray()).ToArray())); + + _allPermutations = tiles.SelectMany(t => t.AllPermutations()).ToList(); + _topLefts = _allPermutations + .Where(t => !_allPermutations.Any(t2 => t.TileId != t2.TileId && t.LeftId == t2.RightId) && + !_allPermutations.Any(t2 => t.TileId != t2.TileId && t.TopId == t2.BottomId)) + .ToList(); + } + + private int Roughness(Tile arg) + { + var seaMonster = new[] + { " # ", "# ## ## ###", " # # # # # # " }.Select(s => s.ToArray()).ToArray(); - - const int seaMonsterWidth = 20; - const int seaMonsterHeight = 3; - const int seaMonsterTiles = 15; - var placedTiles = new Dictionary<(int x, int y), Tile>(); - bool NotPlaced(Tile tile) => placedTiles!.Values.All(t => t.TileId != tile.TileId); - char Grid(int i, int j) => placedTiles![(i / 8, j / 8)].Pixels[j % 8 + 1][i % 8 + 1]; + const int seaMonsterWidth = 20; + const int seaMonsterHeight = 3; + const int seaMonsterTiles = 15; - bool HasSeaMonster((int x, int y) location) - { - for (var j = 0; j < seaMonsterHeight; j++) + var placedTiles = new Dictionary<(int x, int y), Tile>(); + bool NotPlaced(Tile tile) => placedTiles!.Values.All(t => t.TileId != tile.TileId); + char Grid(int i, int j) => placedTiles![(i / 8, j / 8)].Pixels[j % 8 + 1][i % 8 + 1]; + + bool HasSeaMonster((int x, int y) location) + { + for (var j = 0; j < seaMonsterHeight; j++) for (var i = 0; i < seaMonsterWidth; i++) { if (seaMonster![j][i] == ' ') continue; if (Grid(location.x + i, location.y + j) != '#') return false; } - return true; - } + return true; + } - placedTiles[(0, 0)] = arg; - int x = 1, y = 0; - while (true) + placedTiles[(0, 0)] = arg; + int x = 1, y = 0; + while (true) + { + placedTiles.TryGetValue((x - 1, y), out var left); + placedTiles.TryGetValue((x, y - 1), out var top); + if (left == null && top == null) break; + + var firstMatch = _allPermutations + .Where(t => (left is null || t.LeftId == left.RightId) && + (top is null || t.TopId == top.BottomId)) + .Where(NotPlaced) + .FirstOrDefault(); + + if (firstMatch is not null) { - placedTiles.TryGetValue((x - 1, y), out var left); - placedTiles.TryGetValue((x, y - 1), out var top); - if (left == null && top == null) break; - - var firstMatch = _allPermutations - .Where(t => (left is null || t.LeftId == left.RightId) && - (top is null || t.TopId == top.BottomId)) - .Where(NotPlaced) - .FirstOrDefault(); - - if (firstMatch is not null) - { - placedTiles[(x, y)] = firstMatch; - x++; - } - else - { - x = 0; - y++; - } + placedTiles[(x, y)] = firstMatch; + x++; } + else + { + x = 0; + y++; + } + } - var gridWidth = placedTiles.Keys.Max(t => t.x) + 1; - var gridHeight = placedTiles.Keys.Max(t => t.y) + 1; + var gridWidth = placedTiles.Keys.Max(t => t.x) + 1; + var gridHeight = placedTiles.Keys.Max(t => t.y) + 1; - var seaMonsterCount = Enumerable.Range(0, gridWidth * 8 - seaMonsterWidth) - .SelectMany(_ => Enumerable.Range(0, gridHeight * 8 - seaMonsterHeight), (i, j) => (i, j)) - .Count(HasSeaMonster); - - if (seaMonsterCount == 0) return 0; + var seaMonsterCount = Enumerable.Range(0, gridWidth * 8 - seaMonsterWidth) + .SelectMany(_ => Enumerable.Range(0, gridHeight * 8 - seaMonsterHeight), (i, j) => (i, j)) + .Count(HasSeaMonster); - var roughness = 0; - for (var j = 0; j < gridHeight; j++) + if (seaMonsterCount == 0) return 0; + + var roughness = 0; + for (var j = 0; j < gridHeight; j++) for (var i = 0; i < gridWidth; i++) if (Grid(x, y) == '#') roughness++; - return roughness - seaMonsterCount * seaMonsterTiles; + return roughness - seaMonsterCount * seaMonsterTiles; + } + + public override string Part1() + { + return $"{_topLefts.Select(t => t.TileId).Distinct().Aggregate(1L, (acc, next) => acc * next)}"; + } + + public override string Part2() + { + return $"{_topLefts.Select(Roughness).First(r => r > 0)}"; + } + + private record Tile(int TileId, char[][] Pixels) + { + private const int Size = 10; + internal int TopId => GetId(z => (z, 0)); + internal int BottomId => GetId(z => (z, Size - 1)); + internal int LeftId => GetId(z => (0, z)); + internal int RightId => GetId(z => (Size - 1, z)); + + private int GetId(Func selector) + { + return Enumerable.Range(0, Size) + .Select(selector) + .Select((c, i) => (Pixels[c.x][c.y] == '#' ? 1 : 0) << i) + .Aggregate(0, (acc, next) => acc | next); } - public override string Part1() + private Tile RotateClockwise() { - return $"{_topLefts.Select(t => t.TileId).Distinct().Aggregate(1L, (acc, next) => acc * next)}"; + return Transform((x, y, newPixels) => newPixels[x][Size - 1 - y] = Pixels[y][x]); } - public override string Part2() + private Tile Flip() { - return $"{_topLefts.Select(Roughness).First(r => r > 0)}"; + return Transform((x, y, newPixels) => newPixels[y][Size - 1 - x] = Pixels[y][x]); } - private record Tile(int TileId, char[][] Pixels) + private Tile Transform(Action transformFunc) { - private const int Size = 10; - internal int TopId => GetId(z => (z, 0)); - internal int BottomId => GetId(z => (z, Size - 1)); - internal int LeftId => GetId(z => (0, z)); - internal int RightId => GetId(z => (Size - 1, z)); + var newPixels = Enumerable.Repeat(false, Size).Select(_ => new char[Size]).ToArray(); - private int GetId(Func selector) - { - return Enumerable.Range(0, Size) - .Select(selector) - .Select((c, i) => (Pixels[c.x][c.y] == '#' ? 1 : 0) << i) - .Aggregate(0, (acc, next) => acc | next); - } - - private Tile RotateClockwise() - { - return Transform((x, y, newPixels) => newPixels[x][Size - 1 - y] = Pixels[y][x]); - } - - private Tile Flip() - { - return Transform((x, y, newPixels) => newPixels[y][Size - 1 - x] = Pixels[y][x]); - } - - private Tile Transform(Action transformFunc) - { - var newPixels = Enumerable.Repeat(false, Size).Select(_ => new char[Size]).ToArray(); - - for (var y = 0; y < Size; y++) + for (var y = 0; y < Size; y++) for (var x = 0; x < Size; x++) transformFunc(x, y, newPixels); - return new(TileId, newPixels); - } + return new(TileId, newPixels); + } - internal IEnumerable AllPermutations() + internal IEnumerable AllPermutations() + { + var tile = this; + for (var i = 1; i <= 8; i++) { - var tile = this; - for (var i = 1; i <= 8; i++) - { - yield return tile; - if (i == 4) tile = Flip(); - else if (i == 8) yield break; - tile = tile.RotateClockwise(); - } - } - - public string Format() - { - return $"Tile {TileId}:\n{string.Join("\n", Pixels.Select(p => new string(p)))}"; + yield return tile; + if (i == 4) tile = Flip(); + else if (i == 8) yield break; + tile = tile.RotateClockwise(); } } + + public string Format() + { + return $"Tile {TileId}:\n{string.Join("\n", Pixels.Select(p => new string(p)))}"; + } } -} \ No newline at end of file +} diff --git a/aoc2020/Day21.cs b/aoc2020/Day21.cs index 366cc20..ec3ea15 100644 --- a/aoc2020/Day21.cs +++ b/aoc2020/Day21.cs @@ -1,22 +1,21 @@ -namespace aoc2020 +namespace aoc2020; + +/// +/// Day 21: +/// +public sealed class Day21 : Day { - /// - /// Day 21: - /// - public sealed class Day21 : Day + public Day21() : base(21, "Allergen Assessment") { - public Day21() : base(21, "Allergen Assessment") - { - } - - public override string Part1() - { - return ""; - } - - public override string Part2() - { - return ""; - } } -} \ No newline at end of file + + public override string Part1() + { + return ""; + } + + public override string Part2() + { + return ""; + } +} diff --git a/aoc2020/Day22.cs b/aoc2020/Day22.cs index 6b48c3b..041f023 100644 --- a/aoc2020/Day22.cs +++ b/aoc2020/Day22.cs @@ -1,22 +1,21 @@ -namespace aoc2020 +namespace aoc2020; + +/// +/// Day 22: +/// +public sealed class Day22 : Day { - /// - /// Day 22: - /// - public sealed class Day22 : Day + public Day22() : base(22, "Crab Combat") { - public Day22() : base(22, "Crab Combat") - { - } - - public override string Part1() - { - return ""; - } - - public override string Part2() - { - return ""; - } } -} \ No newline at end of file + + public override string Part1() + { + return ""; + } + + public override string Part2() + { + return ""; + } +} diff --git a/aoc2020/Day23.cs b/aoc2020/Day23.cs index 3a4f365..1523962 100644 --- a/aoc2020/Day23.cs +++ b/aoc2020/Day23.cs @@ -1,22 +1,21 @@ -namespace aoc2020 +namespace aoc2020; + +/// +/// Day 23: +/// +public sealed class Day23 : Day { - /// - /// Day 23: - /// - public sealed class Day23 : Day + public Day23() : base(23, "Crab Cups") { - public Day23() : base(23, "Crab Cups") - { - } - - public override string Part1() - { - return ""; - } - - public override string Part2() - { - return ""; - } } -} \ No newline at end of file + + public override string Part1() + { + return ""; + } + + public override string Part2() + { + return ""; + } +} diff --git a/aoc2020/Day24.cs b/aoc2020/Day24.cs index 8432c98..8b6468f 100644 --- a/aoc2020/Day24.cs +++ b/aoc2020/Day24.cs @@ -1,22 +1,21 @@ -namespace aoc2020 +namespace aoc2020; + +/// +/// Day 24: +/// +public sealed class Day24 : Day { - /// - /// Day 24: - /// - public sealed class Day24 : Day + public Day24() : base(24, "Lobby Layout") { - public Day24() : base(24, "Lobby Layout") - { - } - - public override string Part1() - { - return ""; - } - - public override string Part2() - { - return ""; - } } -} \ No newline at end of file + + public override string Part1() + { + return ""; + } + + public override string Part2() + { + return ""; + } +} diff --git a/aoc2020/Day25.cs b/aoc2020/Day25.cs index 85a35c3..9096897 100644 --- a/aoc2020/Day25.cs +++ b/aoc2020/Day25.cs @@ -1,22 +1,21 @@ -namespace aoc2020 +namespace aoc2020; + +/// +/// Day 25: +/// +public sealed class Day25 : Day { - /// - /// Day 25: - /// - public sealed class Day25 : Day + public Day25() : base(25, "Combo Breaker") { - public Day25() : base(25, "Combo Breaker") - { - } - - public override string Part1() - { - return ""; - } - - public override string Part2() - { - return ""; - } } -} \ No newline at end of file + + public override string Part1() + { + return ""; + } + + public override string Part2() + { + return ""; + } +} diff --git a/aoc2020/DayX.cs.txt b/aoc2020/DayX.cs.txt index f5b45c2..89ea477 100644 --- a/aoc2020/DayX.cs.txt +++ b/aoc2020/DayX.cs.txt @@ -1,22 +1,21 @@ -namespace aoc2020 +namespace aoc2020; + +/// +/// Day X: +/// +public sealed class DayX : Day { - /// - /// Day X: - /// - public sealed class DayX : Day + public DayX() : base(X, "") { - public DayX() : base(X, "") - { - } - - public override string Part1() - { - return ""; - } - - public override string Part2() - { - return ""; - } } -} \ No newline at end of file + + public override string Part1() + { + return ""; + } + + public override string Part2() + { + return ""; + } +} diff --git a/aoc2020/Extensions.cs b/aoc2020/Extensions.cs index 4c75d5e..057e8f2 100644 --- a/aoc2020/Extensions.cs +++ b/aoc2020/Extensions.cs @@ -1,28 +1,26 @@ -using System; using System.Diagnostics; -namespace aoc2020 -{ - public static class Extensions - { - /// - /// increased accuracy for stopwatch based on frequency. - /// - /// blog - /// details here - /// - /// - /// - /// - public static double ScaleMilliseconds(this Stopwatch stopwatch) - { - return 1_000 * stopwatch.ElapsedTicks / (double) Stopwatch.Frequency; - } +namespace aoc2020; - public static bool Contains(this Range range, int i) - { - return i >= range.Start.Value && i <= range.End.Value; - } +public static class Extensions +{ + /// + /// increased accuracy for stopwatch based on frequency. + /// + /// blog + /// details here + /// + /// + /// + /// + public static double ScaleMilliseconds(this Stopwatch stopwatch) + { + return 1_000 * stopwatch.ElapsedTicks / (double)Stopwatch.Frequency; } -} \ No newline at end of file + + public static bool Contains(this Range range, int i) + { + return i >= range.Start.Value && i <= range.End.Value; + } +} diff --git a/aoc2020/Program.cs b/aoc2020/Program.cs index b11dddf..0fbfff2 100644 --- a/aoc2020/Program.cs +++ b/aoc2020/Program.cs @@ -1,30 +1,27 @@ -using System; -using System.Linq; -using System.Reflection; +using System.Reflection; -namespace aoc2020 +namespace aoc2020; + +internal static class Program { - internal static class Program + private static void Main(string[] args) { - private static void Main(string[] args) - { - var days = Assembly.GetExecutingAssembly().GetTypes() - .Where(t => t.BaseType == typeof(Day)) - .Select(t => (Day) Activator.CreateInstance(t)) - .OrderBy(d => d.DayNumber); + var days = Assembly.GetExecutingAssembly().GetTypes() + .Where(t => t.BaseType == typeof(Day)) + .Select(t => Activator.CreateInstance(t) as Day) + .OrderBy(d => d?.DayNumber); - if (args.Length == 1 && int.TryParse(args[0], out var dayNum)) - { - var day = days.FirstOrDefault(d => d.DayNumber == dayNum); - if (day != null) - day.AllParts(); - else - Console.WriteLine($"Day {dayNum} invalid or not yet implemented"); - } + if (args.Length == 1 && int.TryParse(args[0], out var dayNum)) + { + var day = days.FirstOrDefault(d => d?.DayNumber == dayNum); + if (day != null) + day.AllParts(); else - { - foreach (var d in days) d.AllParts(); - } + Console.WriteLine($"Day {dayNum} invalid or not yet implemented"); + } + else + { + foreach (var d in days) d?.AllParts(); } } -} \ No newline at end of file +} diff --git a/aoc2020/aoc2020.csproj b/aoc2020/aoc2020.csproj index 339aa6b..2af3036 100644 --- a/aoc2020/aoc2020.csproj +++ b/aoc2020/aoc2020.csproj @@ -1,8 +1,10 @@ - + Exe - net5.0 + net6.0 + enable + enable @@ -11,4 +13,11 @@ + + + + + + +