├── .editorconfig ├── .gitattributes ├── .gitignore ├── AdventOfCode.sln ├── Directory.Build.props ├── README.MD ├── azure-pipelines.yml ├── csharp ├── 2015 │ ├── AdventOfCode.CSharp.2015.csproj │ └── Solvers │ │ ├── Day01.cs │ │ ├── Day02.cs │ │ ├── Day03.cs │ │ ├── Day04.cs │ │ ├── Day05.cs │ │ ├── Day06.cs │ │ ├── Day07.cs │ │ ├── Day08.cs │ │ ├── Day09.cs │ │ ├── Day10.cs │ │ ├── Day11.cs │ │ ├── Day12.cs │ │ ├── Day13.cs │ │ ├── Day14.cs │ │ ├── Day15.cs │ │ ├── Day16.cs │ │ ├── Day17.cs │ │ ├── Day18.cs │ │ ├── Day19.cs │ │ ├── Day20.cs │ │ ├── Day21.cs │ │ ├── Day22.cs │ │ ├── Day23.cs │ │ ├── Day24.cs │ │ └── Day25.cs ├── 2016 │ ├── AdventOfCode.CSharp.2016.csproj │ └── Solvers │ │ ├── Day01.cs │ │ ├── Day02.cs │ │ ├── Day03.cs │ │ ├── Day04.cs │ │ ├── Day05.cs │ │ ├── Day06.cs │ │ ├── Day07.cs │ │ ├── Day08.cs │ │ ├── Day09.cs │ │ ├── Day10.cs │ │ ├── Day11.cs │ │ ├── Day12.cs │ │ └── Day13.cs ├── 2017 │ ├── AdventOfCode.CSharp.2017.csproj │ └── Solvers │ │ ├── Day01.cs │ │ └── Day02.cs ├── 2018 │ ├── AdventOfCode.CSharp.2018.csproj │ └── Solvers │ │ ├── Day01.cs │ │ └── Day02.cs ├── 2019 │ ├── AdventOfCode.CSharp.2019.csproj │ ├── Common │ │ └── IntCode.cs │ └── Solvers │ │ ├── Day01.cs │ │ └── Day02.cs ├── 2020 │ ├── AdventOfCode.CSharp.2020.csproj │ └── Solvers │ │ ├── Day01.cs │ │ ├── Day02.cs │ │ ├── Day03.cs │ │ ├── Day04.cs │ │ ├── Day05.cs │ │ ├── Day06.cs │ │ ├── Day07.cs │ │ ├── Day08.cs │ │ ├── Day09.cs │ │ ├── Day10.cs │ │ ├── Day11.cs │ │ ├── Day12.cs │ │ ├── Day13.cs │ │ ├── Day14.cs │ │ ├── Day15.cs │ │ ├── Day16.cs │ │ ├── Day17.cs │ │ ├── Day18.cs │ │ ├── Day19.cs │ │ ├── Day20.cs │ │ ├── Day21.cs │ │ ├── Day22.cs │ │ ├── Day23.cs │ │ ├── Day24.cs │ │ └── Day25.cs ├── 2021 │ ├── AdventOfCode.CSharp.2021.csproj │ └── Solvers │ │ ├── Day01.cs │ │ ├── Day02.cs │ │ ├── Day03.cs │ │ ├── Day04.cs │ │ ├── Day05.cs │ │ ├── Day06.cs │ │ ├── Day07.cs │ │ ├── Day08.cs │ │ ├── Day09.cs │ │ ├── Day10.cs │ │ ├── Day11.cs │ │ ├── Day12.cs │ │ ├── Day13.cs │ │ ├── Day14.cs │ │ ├── Day15.cs │ │ ├── Day16.cs │ │ ├── Day17.cs │ │ ├── Day18.cs │ │ ├── Day19.cs │ │ ├── Day20.cs │ │ ├── Day21.cs │ │ ├── Day22.cs │ │ ├── Day23.cs │ │ ├── Day24.cs │ │ └── Day25.cs ├── 2022 │ ├── AdventOfCode.CSharp.2022.csproj │ └── Solvers │ │ ├── Day01.cs │ │ ├── Day02.cs │ │ ├── Day03.cs │ │ ├── Day04.cs │ │ ├── Day05.cs │ │ ├── Day06.cs │ │ ├── Day07.cs │ │ ├── Day19.cs │ │ └── Day20.cs ├── 2023 │ ├── AdventOfCode.CSharp.2023.csproj │ └── Solvers │ │ ├── Day01.cs │ │ ├── Day02.cs │ │ ├── Day03.cs │ │ ├── Day04.cs │ │ ├── Day05.cs │ │ ├── Day06.cs │ │ ├── Day07.cs │ │ ├── Day08.cs │ │ ├── Day09.cs │ │ ├── Day10.cs │ │ ├── Day11.cs │ │ ├── Day12.cs │ │ ├── Day13.cs │ │ ├── Day14.cs │ │ ├── Day15.cs │ │ ├── Day16.cs │ │ ├── Day17.cs │ │ ├── Day18.cs │ │ ├── Day19.cs │ │ ├── Day20.cs │ │ ├── Day21.cs │ │ ├── Day22.cs │ │ ├── Day23.cs │ │ ├── Day24.cs │ │ └── Day25.cs ├── 2024 │ ├── AdventOfCode.CSharp.2024.csproj │ └── Solvers │ │ ├── Day01.cs │ │ ├── Day02.cs │ │ ├── Day03.cs │ │ ├── Day04.cs │ │ ├── Day05.cs │ │ └── Day07.cs ├── Benchmarks │ ├── AdventOfCode.CSharp.Benchmarks.csproj │ ├── BenchmarkBase.cs │ ├── ManualConfigExtensions.cs │ ├── Program.cs │ ├── Y2015Solver.cs │ ├── Y2016Solver.cs │ ├── Y2017Solver.cs │ ├── Y2018Solver.cs │ ├── Y2019Solver.cs │ ├── Y2020Solver.cs │ ├── Y2021Solver.cs │ ├── Y2022Solver.cs │ ├── Y2023Solver.cs │ └── Y2024Solver.cs ├── Common │ ├── AdventOfCode.CSharp.Common.csproj │ ├── ISolver.cs │ ├── OCR.cs │ ├── PermutationIterator.cs │ ├── PrioritySet.cs │ ├── SIMDMD5.cs │ ├── Solution.cs │ ├── SolutionWriter.cs │ ├── SolverUtils.cs │ ├── SpanExtensions.cs │ ├── SpanReader.cs │ └── ThrowHelper.cs ├── Runner │ ├── AdventOfCode.CSharp.Runner.csproj │ ├── AdventRunner.cs │ └── Program.cs └── Tests │ ├── AdventOfCode.CSharp.Tests.csproj │ ├── TestHelpers.cs │ ├── Y2015Tests.cs │ ├── Y2016Tests.cs │ ├── Y2017Tests.cs │ ├── Y2018Tests.cs │ ├── Y2019Tests.cs │ ├── Y2020Tests.cs │ ├── Y2021Tests.cs │ ├── Y2022Tests.cs │ ├── Y2023Tests.cs │ └── Y2024Tests.cs ├── fsharp ├── 2015 │ ├── AdventOfCode.FSharp.2015.fsproj │ ├── Benchmarks.md │ ├── Program.fs │ ├── Properties │ │ └── launchSettings.json │ └── Solutions │ │ ├── Day01.fs │ │ ├── Day02.fs │ │ ├── Day03.fs │ │ ├── Day04.fs │ │ ├── Day05.fs │ │ ├── Day06.fs │ │ ├── Day07.fs │ │ ├── Day08.fs │ │ ├── Day09.fs │ │ ├── Day10.fs │ │ ├── Day11.fs │ │ ├── Day12.fs │ │ ├── Day13.fs │ │ ├── Day14.fs │ │ ├── Day15.fs │ │ ├── Day16.fs │ │ ├── Day17.fs │ │ ├── Day18.fs │ │ ├── Day19.fs │ │ ├── Day20.fs │ │ ├── Day21.fs │ │ ├── Day22.fs │ │ ├── Day23.fs │ │ ├── Day24.fs │ │ └── Day25.fs ├── 2016 │ ├── AdventOfCode.FSharp.2016.fsproj │ ├── Benchmarks.md │ ├── Program.fs │ ├── Properties │ │ └── launchSettings.json │ └── Solutions │ │ ├── Day01.fs │ │ ├── Day02.fs │ │ ├── Day03.fs │ │ ├── Day04.fs │ │ ├── Day05.fs │ │ ├── Day06.fs │ │ ├── Day07.fs │ │ ├── Day08.fs │ │ ├── Day09.fs │ │ ├── Day10.fs │ │ ├── Day11.fs │ │ ├── Day12.fs │ │ ├── Day13.fs │ │ ├── Day14.fs │ │ ├── Day15.fs │ │ ├── Day16.fs │ │ ├── Day17.fs │ │ ├── Day18.fs │ │ ├── Day19.fs │ │ ├── Day20.fs │ │ ├── Day21.fs │ │ ├── Day22.fs │ │ ├── Day23.fs │ │ ├── Day24.fs │ │ └── Day25.fs ├── 2017 │ ├── AdventOfCode.FSharp.2017.fsproj │ ├── Benchmarks.md │ ├── Program.fs │ ├── Properties │ │ └── launchSettings.json │ └── Solutions │ │ ├── Day01.fs │ │ ├── Day02.fs │ │ ├── Day03.fs │ │ ├── Day04.fs │ │ ├── Day05.fs │ │ ├── Day06.fs │ │ ├── Day07.fs │ │ ├── Day08.fs │ │ ├── Day09.fs │ │ ├── Day10.fs │ │ ├── Day11.fs │ │ ├── Day12.fs │ │ ├── Day13.fs │ │ ├── Day14.fs │ │ ├── Day15.fs │ │ ├── Day16.fs │ │ ├── Day17.fs │ │ ├── Day18.fs │ │ ├── Day19.fs │ │ ├── Day20.fs │ │ ├── Day21.fs │ │ ├── Day22.fs │ │ ├── Day23.fs │ │ ├── Day24.fs │ │ └── Day25.fs ├── 2018 │ ├── AdventOfCode.FSharp.2018.fsproj │ ├── Benchmarks.md │ ├── Program.fs │ ├── Properties │ │ └── launchSettings.json │ └── Solutions │ │ ├── Day01.fs │ │ ├── Day02.fs │ │ ├── Day03.fs │ │ ├── Day04.fs │ │ ├── Day05.fs │ │ ├── Day06.fs │ │ ├── Day07.fs │ │ ├── Day08.fs │ │ ├── Day09.fs │ │ ├── Day10.fs │ │ ├── Day11.fs │ │ ├── Day12.fs │ │ ├── Day13.fs │ │ ├── Day14.fs │ │ ├── Day15.fs │ │ ├── Day16.fs │ │ ├── Day17.fs │ │ ├── Day18.fs │ │ ├── Day19.fs │ │ ├── Day20.fs │ │ ├── Day21.fs │ │ ├── Day22.fs │ │ ├── Day23.fs │ │ ├── Day24.fs │ │ └── Day25.fs ├── 2019 │ ├── AdventOfCode.FSharp.2019.fsproj │ ├── Benchmarks.md │ ├── Common │ │ └── Intcode.fs │ ├── Program.fs │ ├── Properties │ │ └── launchSettings.json │ └── Solutions │ │ ├── Day01.fs │ │ ├── Day02.fs │ │ ├── Day03.fs │ │ ├── Day04.fs │ │ ├── Day05.fs │ │ ├── Day06.fs │ │ ├── Day07.fs │ │ ├── Day08.fs │ │ ├── Day09.fs │ │ ├── Day10.fs │ │ ├── Day11.fs │ │ ├── Day12.fs │ │ ├── Day13.fs │ │ ├── Day14.fs │ │ ├── Day15.fs │ │ ├── Day16.fs │ │ ├── Day17.fs │ │ ├── Day18.fs │ │ ├── Day19.fs │ │ ├── Day20.fs │ │ ├── Day21.fs │ │ ├── Day22.fs │ │ ├── Day23.fs │ │ ├── Day24.fs │ │ ├── Day25.fs │ │ └── DayTemplate.fs └── Common │ ├── AdventOfCode.FSharp.Common.fsproj │ ├── Benchmarking.fs │ ├── Library.fs │ └── Runner.fs └── global.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | bin 3 | obj 4 | packages 5 | BenchmarkDotNet.Artifacts 6 | *.csproj.user 7 | .ionide 8 | .fake 9 | .vscode 10 | settings.local.json 11 | input 12 | Benchmarks.md 13 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | preview 5 | strict 6 | enable 7 | true 8 | True 9 | true 10 | true 11 | pdbonly 12 | true 13 | 14 | 15 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | 4 | pool: 5 | vmImage: 'ubuntu-latest' 6 | 7 | steps: 8 | - task: UseDotNet@2 9 | displayName: 'Use .NET 8' 10 | inputs: 11 | version: 8.0.100 12 | installationPath: $(Agent.ToolsDirectory)/dotnet 13 | 14 | - task: DotNetCoreCLI@2 15 | inputs: 16 | command: 'build' 17 | projects: './AdventOfCode.sln' 18 | arguments: -c Release -o $(Build.ArtifactStagingDirectory) 19 | displayName: .NET Build 20 | -------------------------------------------------------------------------------- /csharp/2015/AdventOfCode.CSharp.2015.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AdventOfCode.CSharp.Y2015 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /csharp/2015/Solvers/Day01.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | 4 | namespace AdventOfCode.CSharp.Y2015.Solvers; 5 | 6 | public class Day01 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int currentFloor = 0; 11 | int firstBasementFloor = 0; 12 | for (int i = 0; i < input.Length; i++) 13 | { 14 | if (input[i] == '(') 15 | { 16 | currentFloor++; 17 | } 18 | else 19 | { 20 | currentFloor--; 21 | if (currentFloor == -1 && firstBasementFloor == 0) 22 | { 23 | firstBasementFloor = i + 1; 24 | } 25 | } 26 | } 27 | 28 | solution.SubmitPart1(currentFloor); 29 | solution.SubmitPart2(firstBasementFloor); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /csharp/2015/Solvers/Day03.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace AdventOfCode.CSharp.Y2015.Solvers; 6 | 7 | public class Day03 : ISolver 8 | { 9 | public static void Solve(ReadOnlySpan input, Solution solution) 10 | { 11 | var seen = new HashSet(); 12 | 13 | RunSanta(input, seen, start: 0, step: 1); 14 | int part1 = seen.Count; 15 | 16 | seen.Clear(); 17 | RunSanta(input, seen, start: 0, step: 2); 18 | RunSanta(input, seen, start: 1, step: 2); 19 | int part2 = seen.Count; 20 | 21 | solution.SubmitPart1(part1); 22 | solution.SubmitPart2(part2); 23 | } 24 | 25 | private static void RunSanta(ReadOnlySpan moves, HashSet seen, int start, int step) 26 | { 27 | const int unitX = 1 << 16; 28 | const int unitY = 1; 29 | 30 | // get starting coordinates 31 | uint x = ushort.MaxValue / 2; 32 | uint y = ushort.MaxValue / 2; 33 | 34 | // pack them into a single uint 35 | uint encodedPos = x << 16 | y; 36 | seen.Add(encodedPos); 37 | 38 | for (int i = start; i < moves.Length; i += step) 39 | { 40 | switch (moves[i]) 41 | { 42 | case (byte)'^': 43 | encodedPos += unitY; 44 | break; 45 | case (byte)'v': 46 | encodedPos -= unitY; 47 | break; 48 | case (byte)'>': 49 | encodedPos += unitX; 50 | break; 51 | case (byte)'<': 52 | encodedPos -= unitX; 53 | break; 54 | } 55 | 56 | seen.Add(encodedPos); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /csharp/2015/Solvers/Day08.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | 4 | namespace AdventOfCode.CSharp.Y2015.Solvers; 5 | 6 | public class Day08 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int part1 = 0; 11 | int part2 = 0; 12 | 13 | foreach (Range lineRange in input.SplitLines()) 14 | { 15 | ReadOnlySpan line = input[lineRange]; 16 | part1 += GetPart1Length(line); 17 | part2 += GetPart2Length(line); 18 | } 19 | 20 | solution.SubmitPart1(part1); 21 | solution.SubmitPart2(part2); 22 | } 23 | 24 | private static int GetPart1Length(ReadOnlySpan line) 25 | { 26 | int charCount = 0; 27 | 28 | int i = 1; 29 | while (i < line.Length - 1) 30 | { 31 | charCount++; 32 | if (line[i] == '\\') 33 | { 34 | switch (line[i + 1]) 35 | { 36 | case (byte)'\\': 37 | case (byte)'\"': 38 | i += 2; 39 | break; 40 | case (byte)'x': 41 | i += 4; 42 | break; 43 | default: 44 | i++; 45 | break; 46 | } 47 | } 48 | else 49 | { 50 | i++; 51 | } 52 | } 53 | 54 | return line.Length - charCount; 55 | } 56 | 57 | private static int GetPart2Length(ReadOnlySpan line) 58 | { 59 | int charCount = 2 + line.Length; 60 | foreach (byte c in line) 61 | { 62 | if (c is (byte)'\\' or (byte)'\"') 63 | { 64 | charCount++; 65 | } 66 | } 67 | 68 | return charCount - line.Length; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /csharp/2015/Solvers/Day10.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | 4 | namespace AdventOfCode.CSharp.Y2015.Solvers; 5 | 6 | public class Day10 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | input = input.TrimEnd((byte)'\n'); 11 | byte[] currentSequence = new byte[input.Length]; 12 | int curLength = input.Length; 13 | for (int i = 0; i < input.Length; i++) 14 | { 15 | currentSequence[i] = (byte)(input[i] - '0'); 16 | } 17 | 18 | int part1 = 0; 19 | for (int i = 0; i < 50; i++) 20 | { 21 | if (i == 40) 22 | { 23 | part1 = curLength; 24 | } 25 | 26 | // maximum size 27 | byte[] nextSequence = new byte[curLength * 2]; 28 | int nextLength = 0; 29 | 30 | int curIndex = 0; 31 | while (curIndex < curLength) 32 | { 33 | byte curVal = currentSequence[curIndex]; 34 | byte amount = 1; 35 | for (int j = 1; j <= 2; j++) 36 | { 37 | if (curIndex + j >= curLength || currentSequence[curIndex + j] != curVal) 38 | { 39 | break; 40 | } 41 | 42 | amount++; 43 | } 44 | 45 | nextSequence[nextLength++] = amount; 46 | nextSequence[nextLength++] = curVal; 47 | curIndex += amount; 48 | } 49 | 50 | currentSequence = nextSequence; 51 | curLength = nextLength; 52 | } 53 | 54 | solution.SubmitPart1(part1); 55 | solution.SubmitPart2(curLength); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /csharp/2015/Solvers/Day25.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | using System.Numerics; 4 | 5 | namespace AdventOfCode.CSharp.Y2015.Solvers; 6 | 7 | public class Day25 : ISolver 8 | { 9 | public static void Solve(ReadOnlySpan input, Solution solution) 10 | { 11 | var reader = new SpanReader(input); 12 | reader.SkipLength("To continue, please consult the code grid in the manual. Enter the code at row ".Length); 13 | int row = reader.ReadPosIntUntil(','); 14 | reader.SkipLength(" column ".Length); 15 | int column = reader.ReadPosIntUntil('.'); 16 | 17 | int n = row + column - 1; 18 | int diagEnd = n * (n + 1) / 2; 19 | int repetitions = diagEnd - row; 20 | 21 | BigInteger part1 = (BigInteger.ModPow(252533, repetitions, 33554393) * 20151125) % 33554393; 22 | solution.SubmitPart1(part1); 23 | solution.SubmitPart2(string.Empty); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /csharp/2016/AdventOfCode.CSharp.2016.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AdventOfCode.CSharp.Y2016 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /csharp/2016/Solvers/Day02.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | 4 | namespace AdventOfCode.CSharp.Y2016.Solvers; 5 | 6 | public class Day02 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | const string part1Code = "123456789"; 11 | const string part2Code = "0010002340567890ABC000E00"; 12 | 13 | int part1x = 1, part1y = 1; 14 | int part2x = 2, part2y = 2; 15 | 16 | SolutionWriter part1Writer = solution.GetPart1Writer(); 17 | SolutionWriter part2Writer = solution.GetPart2Writer(); 18 | 19 | foreach (Range lineRange in input.SplitLines()) 20 | { 21 | ReadOnlySpan line = input[lineRange]; 22 | foreach (byte dir in line) 23 | { 24 | switch (dir) 25 | { 26 | case (byte)'L': 27 | part1x = Math.Max(part1x - 1, 0); 28 | part2x = Math.Max(part2x - 1, Math.Abs(2 - part2y)); 29 | break; 30 | case (byte)'R': 31 | part1x = Math.Min(part1x + 1, 2); 32 | part2x = Math.Min(part2x + 1, 4 - Math.Abs(2 - part2y)); 33 | break; 34 | case (byte)'U': 35 | part1y = Math.Max(part1y - 1, 0); 36 | part2y = Math.Max(part2y - 1, Math.Abs(2 - part2x)); 37 | break; 38 | case (byte)'D': 39 | part1y = Math.Min(part1y + 1, 2); 40 | part2y = Math.Min(part2y + 1, 4 - Math.Abs(2 - part2x)); 41 | break; 42 | } 43 | } 44 | 45 | part1Writer.Write(part1Code[part1x + 3 * part1y]); 46 | part2Writer.Write(part2Code[part2x + 5 * part2y]); 47 | } 48 | 49 | // Put a newline at the end to signify end of output 50 | part1Writer.Complete(); 51 | part2Writer.Complete(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /csharp/2016/Solvers/Day06.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AdventOfCode.CSharp.Common; 3 | 4 | namespace AdventOfCode.CSharp.Y2016.Solvers; 5 | 6 | public class Day06 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int width = input.IndexOf((byte)'\n'); 11 | int[,] counts = new int[width + 1, 26]; 12 | 13 | int i = 0; 14 | while (i < input.Length) 15 | { 16 | for (int c = 0; c < width; c++) 17 | { 18 | int letter = input[i++] - 'a'; 19 | counts[c, letter]++; 20 | } 21 | 22 | i++; // skip newline character 23 | } 24 | 25 | char[] part1 = new char[width]; 26 | char[] part2 = new char[width]; 27 | 28 | for (int c = 0; c < width; c++) 29 | { 30 | int minLetter = -1; 31 | int minCount = int.MaxValue; 32 | int maxLetter = -1; 33 | int maxCount = 0; 34 | 35 | for (int letter = 0; letter < 26; letter++) 36 | { 37 | int count = counts[c, letter]; 38 | if (count > 0) 39 | { 40 | if (count < minCount) 41 | { 42 | minCount = count; 43 | minLetter = letter; 44 | } 45 | 46 | if (count > maxCount) 47 | { 48 | maxCount = count; 49 | maxLetter = letter; 50 | } 51 | } 52 | } 53 | 54 | part1[c] = (char)('a' + maxLetter); 55 | part2[c] = (char)('a' + minLetter); 56 | } 57 | 58 | solution.SubmitPart1(part1); 59 | solution.SubmitPart2(part2); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /csharp/2016/Solvers/Day09.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AdventOfCode.CSharp.Common; 3 | 4 | namespace AdventOfCode.CSharp.Y2016.Solvers; 5 | 6 | public class Day09 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | input = input.TrimEnd((byte)'\n'); 11 | int part1 = SolvePart1(input); 12 | long part2 = SolvePart2(input); 13 | 14 | solution.SubmitPart1(part1); 15 | solution.SubmitPart2(part2); 16 | } 17 | 18 | private static int SolvePart1(ReadOnlySpan input) 19 | { 20 | int length = 0; 21 | var reader = new SpanReader(input); 22 | while (!reader.Done) 23 | { 24 | if (reader.Read() == '(') 25 | { 26 | int repLength = reader.ReadPosIntUntil('x'); 27 | int repCount = reader.ReadPosIntUntil(')'); 28 | 29 | length += repLength * repCount; 30 | reader.SkipLength(repLength); 31 | } 32 | else 33 | { 34 | length += 1; 35 | } 36 | } 37 | 38 | return length; 39 | } 40 | 41 | private static long SolvePart2(ReadOnlySpan input) 42 | { 43 | long length = 0; 44 | var reader = new SpanReader(input); 45 | while (!reader.Done) 46 | { 47 | if (reader.Read() == '(') 48 | { 49 | int repLength = reader.ReadPosIntUntil('x'); 50 | int repCount = reader.ReadPosIntUntil(')'); 51 | ReadOnlySpan rep = reader.ReadBytes(repLength); 52 | length += SolvePart2(rep) * repCount; 53 | } 54 | else 55 | { 56 | length += 1; 57 | } 58 | } 59 | 60 | return length; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /csharp/2016/Solvers/Day13.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Numerics; 4 | using AdventOfCode.CSharp.Common; 5 | 6 | namespace AdventOfCode.CSharp.Y2016.Solvers; 7 | 8 | public class Day13 : ISolver 9 | { 10 | public static void Solve(ReadOnlySpan input, Solution solution) 11 | { 12 | int favouriteNumber = new SpanReader(input).ReadPosIntUntil('\n'); 13 | 14 | var seen = new HashSet<(int, int)>(); 15 | 16 | int steps = 0; 17 | var frontier = new HashSet<(int, int)> { (1, 1) }; 18 | 19 | int? part1 = null; 20 | int? part2 = null; 21 | while (part1 == null || part2 == null) 22 | { 23 | var newFrontier = new HashSet<(int, int)>(); 24 | foreach ((int x, int y) in frontier) 25 | { 26 | if (x == 31 && y == 39) 27 | part1 = steps; 28 | 29 | if (IsWall(x, y) || seen.Contains((x, y))) 30 | continue; 31 | 32 | seen.Add((x, y)); 33 | 34 | newFrontier.Add((x - 1, y)); 35 | newFrontier.Add((x + 1, y)); 36 | newFrontier.Add((x, y - 1)); 37 | newFrontier.Add((x, y + 1)); 38 | } 39 | 40 | if (steps == 50) 41 | part2 = seen.Count; 42 | 43 | frontier = newFrontier; 44 | steps++; 45 | } 46 | 47 | solution.SubmitPart1(part1.Value); 48 | solution.SubmitPart2(part2.Value); 49 | 50 | bool IsWall(int x, int y) 51 | { 52 | if (x < 0 || y < 0) 53 | return true; 54 | 55 | int sum = x * x + 3 * x + 2 * x * y + y + y * y + favouriteNumber; 56 | return BitOperations.PopCount((uint)sum) % 2 == 1; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /csharp/2017/AdventOfCode.CSharp.2017.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AdventOfCode.CSharp.Y2017 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /csharp/2017/Solvers/Day01.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | 4 | namespace AdventOfCode.CSharp.Y2017.Solvers; 5 | 6 | public class Day01 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int len = input.TrimEnd((byte)'\n').Length; 11 | int mid = len / 2; 12 | int part1 = 0; 13 | int part2 = 0; 14 | for (int i = 0; i < len; i++) 15 | { 16 | byte cur = input[i]; 17 | if (cur == input[(i + 1) % len]) 18 | { 19 | part1 += cur - '0'; 20 | } 21 | 22 | if (cur == input[(i + mid) % len]) 23 | { 24 | part2 += cur - '0'; 25 | } 26 | } 27 | 28 | solution.SubmitPart1(part1); 29 | solution.SubmitPart2(part2); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /csharp/2017/Solvers/Day02.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace AdventOfCode.CSharp.Y2017.Solvers; 6 | 7 | public class Day02 : ISolver 8 | { 9 | public static void Solve(ReadOnlySpan input, Solution solution) 10 | { 11 | int part1 = 0; 12 | int part2 = 0; 13 | 14 | var nums = new List(); 15 | foreach (Range rowRange in input.SplitLines()) 16 | { 17 | ReadOnlySpan row = input[rowRange]; 18 | int minValue = int.MaxValue; 19 | int maxValue = int.MinValue; 20 | int quotient = 0; 21 | 22 | nums.Clear(); 23 | var rowReader = new SpanReader(row); 24 | while (!rowReader.Done) 25 | { 26 | int cell = rowReader.ReadPosIntUntil('\t'); 27 | minValue = Math.Min(minValue, cell); 28 | maxValue = Math.Max(maxValue, cell); 29 | 30 | if (quotient == 0) 31 | { 32 | foreach (int num in nums) 33 | { 34 | if (cell % num == 0) 35 | { 36 | quotient = cell / num; 37 | break; 38 | } 39 | else if (num % cell == 0) 40 | { 41 | quotient = num / cell; 42 | break; 43 | } 44 | } 45 | 46 | nums.Add(cell); 47 | } 48 | } 49 | 50 | part1 += maxValue - minValue; 51 | part2 += quotient; 52 | } 53 | 54 | solution.SubmitPart1(part1); 55 | solution.SubmitPart2(part2); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /csharp/2018/AdventOfCode.CSharp.2018.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AdventOfCode.CSharp.Y2018 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /csharp/2018/Solvers/Day01.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace AdventOfCode.CSharp.Y2018.Solvers; 6 | 7 | public class Day01 : ISolver 8 | { 9 | public class Frequency 10 | { 11 | public int Value { get; set; } 12 | 13 | public int Index { get; set; } 14 | 15 | public int ModTotal { get; set; } 16 | } 17 | 18 | public static void Solve(ReadOnlySpan input, Solution solution) 19 | { 20 | int freqIndex = 0; 21 | int freqTotal = 0; 22 | var freqs = new List(); 23 | var reader = new SpanReader(input); 24 | while (!reader.Done) 25 | { 26 | freqs.Add(new Frequency { Value = freqTotal, Index = freqIndex }); 27 | freqIndex++; 28 | 29 | int mul = reader.Peek() == '-' ? -1 : 1; 30 | reader.SkipLength(1); 31 | 32 | freqTotal += mul * reader.ReadPosIntUntil('\n'); 33 | } 34 | 35 | foreach (Frequency freq in freqs) 36 | { 37 | int mod = freq.Value % freqTotal; 38 | freq.ModTotal = mod < 0 ? mod + freqTotal : mod; 39 | } 40 | 41 | // sort by mods first, then by value 42 | freqs.Sort((a, b) => a.ModTotal != b.ModTotal 43 | ? a.ModTotal.CompareTo(b.ModTotal) 44 | : a.Value.CompareTo(b.Value)); 45 | 46 | var prev = new Frequency { ModTotal = -1 }; 47 | int minDiff = int.MaxValue; 48 | int minIndex = int.MaxValue; 49 | int minFreq = 0; 50 | foreach (Frequency freq in freqs) 51 | { 52 | if (freq.ModTotal == prev.ModTotal) 53 | { 54 | int diff = freq.Value - prev.Value; 55 | if (diff < minDiff || (diff == minDiff && prev.Index < minIndex)) 56 | { 57 | minDiff = diff; 58 | minIndex = prev.Index; 59 | minFreq = freq.Value; 60 | } 61 | } 62 | 63 | prev = freq; 64 | } 65 | 66 | solution.SubmitPart1(freqTotal); 67 | solution.SubmitPart2(minFreq); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /csharp/2019/AdventOfCode.CSharp.2019.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AdventOfCode.CSharp.Y2019 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /csharp/2019/Common/IntCode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AdventOfCode.CSharp.Y2019.Common; 4 | 5 | public static class IntCode 6 | { 7 | public static ReadOnlySpan ParseFromInput(ReadOnlySpan program) 8 | { 9 | // initialise an int array which the intcode will be read into. 10 | int[] code = new int[program.Length / 2 + 1]; 11 | int size = 0; 12 | bool isNegative = false; 13 | int n = 0; 14 | foreach (byte c in program) 15 | { 16 | if (c == ',') 17 | { 18 | if (isNegative) 19 | { 20 | n = -n; 21 | isNegative = false; 22 | } 23 | code[size++] = n; 24 | n = 0; 25 | } 26 | else if (c == '-') 27 | { 28 | isNegative = true; 29 | } 30 | else 31 | { 32 | int digit = c - '0'; 33 | n = n * 10 + digit; 34 | } 35 | } 36 | 37 | return new ReadOnlySpan(code, 0, size); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /csharp/2019/Solvers/Day01.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | 4 | namespace AdventOfCode.CSharp.Y2019.Solvers; 5 | 6 | public class Day01 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | var reader = new SpanReader(input); 11 | 12 | int part1 = 0; 13 | int part2 = 0; 14 | while (!reader.Done) 15 | { 16 | int mass = reader.ReadPosIntUntil('\n'); 17 | int fuel = mass / 3 - 2; 18 | part1 += fuel; 19 | while (fuel > 0) 20 | { 21 | part2 += fuel; 22 | fuel = fuel / 3 - 2; 23 | } 24 | } 25 | 26 | solution.SubmitPart1(part1); 27 | solution.SubmitPart2(part2); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /csharp/2019/Solvers/Day02.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using AdventOfCode.CSharp.Y2019.Common; 3 | using System; 4 | 5 | namespace AdventOfCode.CSharp.Y2019.Solvers; 6 | 7 | public class Day02 : ISolver 8 | { 9 | public static void Solve(ReadOnlySpan input, Solution solution) 10 | { 11 | ReadOnlySpan intCode = IntCode.ParseFromInput(input); 12 | int[] memory = intCode.ToArray(); 13 | 14 | int part1 = Run(memory, 12, 2); 15 | solution.SubmitPart1(part1); 16 | 17 | for (int noun = 0; noun < 100; noun++) 18 | { 19 | for (int verb = 0; verb < 100; verb++) 20 | { 21 | // reinitialise memory to intcode 22 | intCode.CopyTo(memory); 23 | 24 | if (Run(memory, noun, verb) == 19690720) 25 | { 26 | int part2 = 100 * noun + verb; 27 | solution.SubmitPart2(part2); 28 | return; 29 | } 30 | } 31 | } 32 | 33 | ThrowHelper.ThrowException("Unable to find solution for part 2"); 34 | } 35 | 36 | private static int Run(int[] memory, int noun, int verb) 37 | { 38 | memory[1] = noun; 39 | memory[2] = verb; 40 | 41 | int ip = 0; 42 | while (memory[ip] != 99) 43 | { 44 | int a = memory[ip + 1]; 45 | int b = memory[ip + 2]; 46 | int c = memory[ip + 3]; 47 | switch (memory[ip]) 48 | { 49 | case 1: 50 | memory[c] = memory[b] + memory[a]; 51 | break; 52 | case 2: 53 | memory[c] = memory[b] * memory[a]; 54 | break; 55 | } 56 | 57 | ip += 4; 58 | } 59 | 60 | return memory[0]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /csharp/2020/AdventOfCode.CSharp.2020.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AdventOfCode.CSharp.Y2020 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /csharp/2020/Solvers/Day01.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | 4 | namespace AdventOfCode.CSharp.Y2020.Solvers; 5 | 6 | public class Day01 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int length = 0; 11 | byte[]? numberSet = new byte[2048]; 12 | int[]? numbers = new int[512]; 13 | foreach (Range lineRange in input.SplitLines()) 14 | { 15 | ReadOnlySpan line = input[lineRange]; 16 | int num = line[0] - '0'; 17 | for (int i = 1; i < line.Length; i++) 18 | { 19 | num = num * 10 + (line[i] - '0'); 20 | } 21 | 22 | numberSet[num] = 1; 23 | numbers[length++] = num; 24 | } 25 | 26 | Array.Sort(numbers, 0, length); 27 | 28 | int part1 = -1; 29 | int part2 = -1; 30 | for (int i = 0; i < length; i++) 31 | { 32 | int a = numbers[i]; 33 | int part1B = 2020 - a; 34 | for (int j = i + 1; j < length; j++) 35 | { 36 | int b = numbers[j]; 37 | if (b < part1B) 38 | { 39 | int c = part1B - b; 40 | if (numberSet[c] == 1) 41 | { 42 | part2 = a * b * c; 43 | solution.SubmitPart2(part2); 44 | if (part1 >= 0) 45 | return; 46 | } 47 | } 48 | else if (b == part1B) 49 | { 50 | part1 = a * b; 51 | solution.SubmitPart1(part1); 52 | if (part2 >= 0) 53 | return; 54 | } 55 | else 56 | { 57 | break; 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /csharp/2020/Solvers/Day02.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using AdventOfCode.CSharp.Common; 4 | 5 | namespace AdventOfCode.CSharp.Y2020.Solvers; 6 | 7 | public class Day02 : ISolver 8 | { 9 | public static void Solve(ReadOnlySpan input, Solution solution) 10 | { 11 | int part1 = 0; 12 | int part2 = 0; 13 | 14 | foreach (Range lineRange in input.SplitLines()) 15 | { 16 | ReadOnlySpan line = input[lineRange]; 17 | int i = 0; 18 | int left = ParsePosInt(line, until: '-', ref i); 19 | int right = ParsePosInt(line, until: ' ', ref i); 20 | byte letter = line[i]; 21 | ReadOnlySpan password = line.Slice(i + 3); 22 | 23 | int letterCount = password.Count(letter); 24 | if (left <= letterCount && letterCount <= right) 25 | { 26 | part1++; 27 | } 28 | 29 | if (password[left - 1] == letter ^ password[right - 1] == letter) 30 | { 31 | part2++; 32 | } 33 | } 34 | 35 | solution.SubmitPart1(part1); 36 | solution.SubmitPart2(part2); 37 | } 38 | 39 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 40 | static int ParsePosInt(ReadOnlySpan str, char until, ref int i) 41 | { 42 | byte c = str[i++]; 43 | int num = c - '0'; 44 | while ((c = str[i++]) != until) 45 | { 46 | num = num * 10 + (c - '0'); 47 | } 48 | 49 | return num; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /csharp/2020/Solvers/Day03.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using AdventOfCode.CSharp.Common; 4 | 5 | namespace AdventOfCode.CSharp.Y2020.Solvers; 6 | public class Day03 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | 10 | { 11 | int w = input.IndexOf((byte)'\n'); 12 | int h = (input.Length + 1) / (w + 1); 13 | 14 | long slope_1_1 = CountTrees(input, w, h, 1, 1); 15 | long slope_3_1 = CountTrees(input, w, h, 3, 1); 16 | long slope_5_1 = CountTrees(input, w, h, 5, 1); 17 | long slope_7_1 = CountTrees(input, w, h, 7, 1); 18 | long slope_1_2 = CountTrees(input, w, h, 1, 2); 19 | 20 | long part1 = slope_3_1; 21 | long part2 = slope_1_1 * slope_3_1 * slope_5_1 * slope_7_1 * slope_1_2; 22 | 23 | solution.SubmitPart1(part1); 24 | solution.SubmitPart2(part2); 25 | } 26 | 27 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 28 | static long CountTrees(ReadOnlySpan input, int w, int h, int dx, int dy) 29 | { 30 | int stride = w + 1; 31 | int x = 0; 32 | long total = 0; 33 | for (int y = 0; y < h; y += dy) 34 | { 35 | if (input[y * stride + x] == '#') 36 | { 37 | total += 1; 38 | } 39 | 40 | x += dx; 41 | if (x >= w) 42 | x -= w; 43 | } 44 | 45 | return total; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /csharp/2020/Solvers/Day05.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using AdventOfCode.CSharp.Common; 4 | 5 | namespace AdventOfCode.CSharp.Y2020.Solvers; 6 | 7 | public class Day05 : ISolver 8 | { 9 | public static void Solve(ReadOnlySpan input, Solution solution) 10 | { 11 | uint[] seen = new uint[32]; 12 | int numSeats = input.Length / 11; 13 | 14 | int maxSeat = 0; 15 | for (int i = 0; i < input.Length; i += 11) 16 | { 17 | // unrolling the loop to save precious microseconds 18 | int seatId = 0; 19 | if (input[i + 0] == 'B') seatId |= 1 << 9; 20 | if (input[i + 1] == 'B') seatId |= 1 << 8; 21 | if (input[i + 2] == 'B') seatId |= 1 << 7; 22 | if (input[i + 3] == 'B') seatId |= 1 << 6; 23 | if (input[i + 4] == 'B') seatId |= 1 << 5; 24 | if (input[i + 5] == 'B') seatId |= 1 << 4; 25 | if (input[i + 6] == 'B') seatId |= 1 << 3; 26 | if (input[i + 7] == 'R') seatId |= 1 << 2; 27 | if (input[i + 8] == 'R') seatId |= 1 << 1; 28 | if (input[i + 9] == 'R') seatId |= 1 << 0; 29 | 30 | if (seatId > maxSeat) 31 | maxSeat = seatId; 32 | 33 | seen[seatId / 32] |= 1u << (seatId & 31); 34 | } 35 | 36 | solution.SubmitPart1(maxSeat); 37 | 38 | int minSeat = maxSeat - numSeats - 1; 39 | for (int i = minSeat / 32 + 1; i < seen.Length; i++) 40 | { 41 | uint elem = seen[i]; 42 | if (elem != 0xFFFFFFFFu) 43 | { 44 | int part2 = 32 * i + (31 - BitOperations.LeadingZeroCount(~elem)); 45 | solution.SubmitPart2(part2); 46 | return; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /csharp/2020/Solvers/Day06.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using AdventOfCode.CSharp.Common; 4 | 5 | namespace AdventOfCode.CSharp.Y2020.Solvers; 6 | 7 | public class Day06 : ISolver 8 | { 9 | public static void Solve(ReadOnlySpan input, Solution solution) 10 | { 11 | int part1 = 0; 12 | int part2 = 0; 13 | 14 | uint personAnswers = 0; 15 | uint groupAnswers = 0; 16 | uint groupAllAnswers = uint.MaxValue; 17 | foreach (byte c in input) 18 | { 19 | if (c == '\n') 20 | { 21 | if (personAnswers == 0) 22 | { 23 | part1 += BitOperations.PopCount(groupAnswers); 24 | part2 += BitOperations.PopCount(groupAllAnswers); 25 | 26 | groupAnswers = 0; 27 | groupAllAnswers = uint.MaxValue; 28 | } 29 | else 30 | { 31 | groupAnswers |= personAnswers; 32 | groupAllAnswers &= personAnswers; 33 | personAnswers = 0; 34 | } 35 | } 36 | else 37 | { 38 | personAnswers |= 1u << (c - 'a'); 39 | } 40 | } 41 | 42 | // process the last group 43 | part1 += BitOperations.PopCount(groupAnswers); 44 | part2 += BitOperations.PopCount(groupAllAnswers); 45 | 46 | solution.SubmitPart1(part1); 47 | solution.SubmitPart2(part2); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /csharp/2020/Solvers/Day10.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AdventOfCode.CSharp.Common; 3 | 4 | namespace AdventOfCode.CSharp.Y2020.Solvers; 5 | 6 | public class Day10 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int len = input.Count((byte)'\n'); 11 | int[] nums = new int[len]; 12 | 13 | var reader = new SpanReader(input); 14 | for (int i = 0; i < len; i++) 15 | { 16 | nums[i] = reader.ReadPosIntUntil('\n'); 17 | } 18 | 19 | Array.Sort(nums); 20 | 21 | int oneDiffs = 0; 22 | int threeDiffs = 1; // there will always be a diff of three at the end, so start at 1 23 | 24 | long ways0 = 0; 25 | long ways1 = 0; 26 | long ways2 = 1; 27 | 28 | int prev0 = int.MinValue; 29 | int prev1 = int.MinValue; 30 | int prev2 = 0; 31 | 32 | foreach (int num in nums) 33 | { 34 | // part 1 35 | int diff = num - prev2; 36 | if (diff == 1) 37 | { 38 | oneDiffs++; 39 | } 40 | else if (diff == 3) 41 | { 42 | threeDiffs++; 43 | } 44 | 45 | // part 2 46 | long ways = ways2; 47 | if (num - prev1 <= 3) 48 | { 49 | ways += ways1; 50 | if (num - prev0 <= 3) 51 | { 52 | ways += ways0; 53 | } 54 | } 55 | 56 | prev0 = prev1; 57 | prev1 = prev2; 58 | prev2 = num; 59 | 60 | ways0 = ways1; 61 | ways1 = ways2; 62 | ways2 = ways; 63 | } 64 | 65 | int part1 = oneDiffs * threeDiffs; 66 | long part2 = ways2; 67 | 68 | solution.SubmitPart1(part1); 69 | solution.SubmitPart2(part2); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /csharp/2020/Solvers/Day13.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Numerics; 4 | using AdventOfCode.CSharp.Common; 5 | 6 | namespace AdventOfCode.CSharp.Y2020.Solvers; 7 | 8 | public class Day13 : ISolver 9 | { 10 | public static void Solve(ReadOnlySpan input, Solution solution) 11 | { 12 | var reader = new SpanReader(input.TrimEnd((byte)'\n')); 13 | int earliestTime = reader.ReadPosIntUntil('\n'); 14 | 15 | int part1Id = -1; 16 | int part1Time = int.MaxValue; 17 | 18 | BigInteger product = 1; 19 | var buses = new List<(int n, int a)>(); 20 | for (int i = 0; !reader.Done; i++) 21 | { 22 | if (reader.Peek() == 'x') 23 | { 24 | reader.SkipLength("x,".Length); 25 | continue; 26 | } 27 | 28 | int busId = reader.ReadPosIntUntil(','); 29 | 30 | // Part 1 31 | int waitTime = busId - (earliestTime % busId); 32 | if (waitTime < part1Time) 33 | { 34 | part1Id = busId; 35 | part1Time = waitTime; 36 | } 37 | 38 | // Part 2 39 | product *= busId; 40 | 41 | int remainder = (busId - i) % busId; 42 | if (remainder < 0) 43 | { 44 | remainder += busId; 45 | } 46 | 47 | buses.Add((busId, remainder)); 48 | } 49 | 50 | int part1 = part1Id * part1Time; 51 | 52 | BigInteger part2 = 0; 53 | foreach ((int n, int a) in buses) 54 | { 55 | BigInteger p = product / n; 56 | part2 += a * BigInteger.ModPow(p, n - 2, n) * p; 57 | } 58 | 59 | part2 %= product; 60 | 61 | solution.SubmitPart1(part1); 62 | solution.SubmitPart2(part2); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /csharp/2020/Solvers/Day25.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using AdventOfCode.CSharp.Common; 4 | 5 | namespace AdventOfCode.CSharp.Y2020.Solvers; 6 | 7 | public class Day25 : ISolver 8 | { 9 | public static void Solve(ReadOnlySpan input, Solution solution) 10 | { 11 | var reader = new SpanReader(input); 12 | int key1 = reader.ReadPosIntUntil('\n'); 13 | int key2 = reader.ReadPosIntUntil('\n'); 14 | 15 | int n = 1; 16 | int loopSize = 0; 17 | while (n != key1) 18 | { 19 | n = (7 * n) % 20201227; 20 | loopSize++; 21 | } 22 | 23 | BigInteger part1 = BigInteger.ModPow(key2, loopSize, 20201227); 24 | 25 | solution.SubmitPart1(part1); 26 | solution.SubmitPart2(string.Empty); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /csharp/2021/AdventOfCode.CSharp.2021.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AdventOfCode.CSharp.Y2021 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /csharp/2021/Solvers/Day02.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | 4 | namespace AdventOfCode.CSharp.Y2021.Solvers; 5 | 6 | public class Day02 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int horizontal = 0; 11 | int part1DepthPart2Aim = 0; // depth for part 1, aim for part 2 12 | int part2Depth = 0; 13 | 14 | int i = 0; 15 | while (i < input.Length) 16 | { 17 | switch (input[i]) 18 | { 19 | case (byte)'f': // forward 20 | int amount = CharToValue(input[i + "forward ".Length]); 21 | horizontal += amount; 22 | part2Depth += part1DepthPart2Aim * amount; 23 | i += "forward x\n".Length; 24 | break; 25 | case (byte)'d': // down 26 | part1DepthPart2Aim += CharToValue(input[i + "down ".Length]); 27 | i += "down x\n".Length; 28 | break; 29 | default: // up 30 | part1DepthPart2Aim -= CharToValue(input[i + "up ".Length]); 31 | i += "up x\n".Length; 32 | break; 33 | } 34 | } 35 | 36 | solution.SubmitPart1(horizontal * part1DepthPart2Aim); 37 | solution.SubmitPart2(horizontal * part2Depth); 38 | } 39 | 40 | private static int CharToValue(byte c) => c - '0'; 41 | } 42 | -------------------------------------------------------------------------------- /csharp/2021/Solvers/Day06.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace AdventOfCode.CSharp.Y2021.Solvers; 6 | 7 | public class Day06 : ISolver 8 | { 9 | public static void Solve(ReadOnlySpan input, Solution solution) 10 | { 11 | Span counts = stackalloc long[9]; 12 | counts.Clear(); 13 | 14 | int cursor = 0; 15 | while (cursor < input.Length) 16 | { 17 | counts[input[cursor] - '0']++; 18 | cursor += 2; 19 | } 20 | 21 | Iterate(counts, 80); 22 | 23 | long part1 = 0; 24 | foreach (long count in counts) 25 | part1 += count; 26 | 27 | solution.SubmitPart1(part1); 28 | 29 | Iterate(counts, 256 - 80); 30 | 31 | long part2 = 0; 32 | foreach (long count in counts) 33 | part2 += count; 34 | 35 | solution.SubmitPart2(part2); 36 | } 37 | 38 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 39 | private static void Iterate(Span counts, int n) 40 | { 41 | int i = 0; 42 | while (i + 7 < n) 43 | { 44 | long sevens = counts[7]; 45 | counts[7] = counts[5]; 46 | counts[5] += counts[3]; 47 | counts[3] += counts[1]; 48 | counts[1] += counts[8]; 49 | counts[8] = counts[6]; 50 | counts[6] += counts[4]; 51 | counts[4] += counts[2]; 52 | counts[2] += counts[0]; 53 | counts[0] += sevens; 54 | 55 | i += 7; 56 | } 57 | 58 | while (i < n) 59 | { 60 | long zeroes = counts[0]; 61 | for (int j = 0; j < 8; j++) 62 | counts[j] = counts[j + 1]; 63 | 64 | counts[6] += zeroes; 65 | counts[8] = zeroes; 66 | 67 | i++; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /csharp/2022/AdventOfCode.CSharp.2022.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AdventOfCode.CSharp.Y2022 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /csharp/2022/Solvers/Day01.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AdventOfCode.CSharp.Common; 3 | 4 | namespace AdventOfCode.CSharp.Y2022.Solvers; 5 | 6 | public class Day01 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int inputCursor = 0; 11 | 12 | int elf1 = 0; 13 | int elf2 = 0; 14 | int elf3 = 0; 15 | while (inputCursor < input.Length) 16 | { 17 | int elfTotal = 0; 18 | while (inputCursor < input.Length && input[inputCursor] != '\n') 19 | { 20 | elfTotal += ReadLineAsInteger(input, ref inputCursor); 21 | } 22 | 23 | if (elfTotal > elf3) 24 | { 25 | if (elfTotal > elf2) 26 | { 27 | elf3 = elf2; 28 | if (elfTotal > elf1) 29 | { 30 | elf2 = elf1; 31 | elf1 = elfTotal; 32 | } 33 | else 34 | { 35 | elf2 = elfTotal; 36 | } 37 | } 38 | else 39 | { 40 | elf3 = elfTotal; 41 | } 42 | } 43 | 44 | inputCursor++; 45 | } 46 | 47 | solution.SubmitPart1(elf1); 48 | solution.SubmitPart2(elf1 + elf2 + elf3); 49 | } 50 | 51 | private static int ReadLineAsInteger(ReadOnlySpan input, ref int i) 52 | { 53 | // Assume that the first character is always a digit 54 | int ret = input[i++] - '0'; 55 | 56 | byte cur; 57 | while ((cur = input[i++]) != '\n') 58 | ret = ret * 10 + cur - '0'; 59 | 60 | return ret; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /csharp/2022/Solvers/Day02.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AdventOfCode.CSharp.Common; 3 | 4 | namespace AdventOfCode.CSharp.Y2022.Solvers; 5 | 6 | public class Day02 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int part1 = 0; 11 | int part2 = 0; 12 | for (int i = 0; i < input.Length; i += "A Z\n".Length) 13 | { 14 | byte l = input[i]; 15 | byte r = input[i + 2]; 16 | 17 | part1 += r - 'W'; // score for choice 18 | part1 += 3 * ((r - l + 2) % 3); // score for outcome 19 | 20 | 21 | part2 += (l + r + 2) % 3 + 1; // score for choice 22 | part2 += 3 * (r - 'X'); // score for outcome 23 | } 24 | 25 | solution.SubmitPart1(part1); 26 | solution.SubmitPart2(part2); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /csharp/2022/Solvers/Day03.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using AdventOfCode.CSharp.Common; 4 | 5 | namespace AdventOfCode.CSharp.Y2022.Solvers; 6 | 7 | public class Day03 : ISolver 8 | { 9 | public static void Solve(ReadOnlySpan input, Solution solution) 10 | { 11 | int part1 = 0; 12 | int part2 = 0; 13 | 14 | while (input.Length > 0) 15 | { 16 | ReadOnlySpan sack1 = ReadSack(ref input); 17 | ReadOnlySpan sack2 = ReadSack(ref input); 18 | ReadOnlySpan sack3 = ReadSack(ref input); 19 | 20 | part1 += GetPriority(sack1, out ulong items1) + GetPriority(sack2, out ulong items2) + GetPriority(sack3, out ulong items3); 21 | part2 += ExtractPriorityFromBitSet(items1 & items2 & items3); 22 | } 23 | 24 | solution.SubmitPart1(part1); 25 | solution.SubmitPart2(part2); 26 | } 27 | 28 | private static ReadOnlySpan ReadSack(ref ReadOnlySpan input) 29 | { 30 | int sackEndIndex = input.IndexOf((byte)'\n'); 31 | ReadOnlySpan sack = input[..sackEndIndex]; 32 | input = input[(sackEndIndex + 1)..]; 33 | return sack; 34 | } 35 | 36 | private static int GetPriority(ReadOnlySpan rucksack, out ulong sackItems) 37 | { 38 | int halfSize = rucksack.Length / 2; 39 | ulong half1 = 0; 40 | for (int i = 0; i < halfSize; i++) 41 | half1 |= 1UL << (rucksack[i] - 'A'); 42 | 43 | ulong half2 = 0; 44 | for (int i = halfSize; i < 2 * halfSize; i++) 45 | half2 |= 1UL << (rucksack[i] - 'A'); 46 | 47 | ulong common = half1 & half2; 48 | sackItems = half1 | half2; 49 | 50 | return ExtractPriorityFromBitSet(common); 51 | } 52 | 53 | private static int ExtractPriorityFromBitSet(ulong bits) 54 | { 55 | int commonIndex = BitOperations.TrailingZeroCount(bits); 56 | if (commonIndex < 26) 57 | return commonIndex + 27; 58 | else 59 | return commonIndex - 31; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /csharp/2022/Solvers/Day04.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AdventOfCode.CSharp.Common; 3 | 4 | namespace AdventOfCode.CSharp.Y2022.Solvers; 5 | 6 | public class Day04 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int inputCursor = 0; 11 | 12 | int part1 = 0; 13 | int part2 = 0; 14 | 15 | while (inputCursor < input.Length) 16 | { 17 | int start1 = ReadIntegerUntil(input, '-', ref inputCursor); 18 | int end1 = ReadIntegerUntil(input, ',', ref inputCursor); 19 | int start2 = ReadIntegerUntil(input, '-', ref inputCursor); 20 | int end2 = ReadIntegerUntil(input, '\n', ref inputCursor); 21 | 22 | if (start2 <= end1 && start1 <= end2) 23 | { 24 | part2++; 25 | 26 | if ((start1 <= start2 && end2 <= end1) || (start2 <= start1 && end1 <= end2)) 27 | { 28 | part1++; 29 | } 30 | } 31 | } 32 | 33 | solution.SubmitPart1(part1); 34 | solution.SubmitPart2(part2); 35 | } 36 | 37 | public static int ReadIntegerUntil(ReadOnlySpan span, char c, ref int i) 38 | { 39 | // Assume that the first character is always a digit 40 | int ret = span[i++] - '0'; 41 | 42 | byte cur; 43 | while ((cur = span[i++]) != c) 44 | ret = ret * 10 + (cur - '0'); 45 | 46 | return ret; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /csharp/2023/AdventOfCode.CSharp.2023.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AdventOfCode.CSharp.Y2023 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /csharp/2023/Solvers/Day02.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AdventOfCode.CSharp.Common; 3 | 4 | namespace AdventOfCode.CSharp.Y2023.Solvers; 5 | 6 | public class Day02 : ISolver 7 | { 8 | public static void Solve(ReadOnlySpan input, Solution solution) 9 | { 10 | int part1 = 0; 11 | int part2 = 0; 12 | 13 | int gameId = 1; 14 | while (input.Length > 1) 15 | { 16 | ParseLine(ref input, gameId, out int maxR, out int maxB, out int maxG); 17 | 18 | if (maxR <= 12 && maxG <= 13 && maxB <= 14) 19 | part1 += gameId; 20 | 21 | part2 += maxR * maxB * maxG; 22 | gameId++; 23 | } 24 | 25 | solution.SubmitPart1(part1); 26 | solution.SubmitPart2(part2); 27 | } 28 | 29 | private static ReadOnlySpan ParseLine(ref ReadOnlySpan input, int gameId, out int maxR, out int maxB, out int maxG) 30 | { 31 | maxR = 0; 32 | maxB = 0; 33 | maxG = 0; 34 | 35 | // skip the "Game 1" part 36 | input = input.Slice("Game ".Length + (gameId < 10 ? 1 : (gameId < 100 ? 2 : 3))); 37 | 38 | while (input[0] != '\n') 39 | { 40 | // Parse integer 41 | byte c; 42 | int amt = input[2] - '0'; // look at input[2] to skip ": " or ", " or "; " 43 | int i = 3; 44 | while ((c = input[i++]) != ' ') 45 | amt = 10 * amt + (c - '0'); 46 | 47 | switch (input[i]) 48 | { 49 | case (byte)'r': 50 | maxR = Math.Max(maxR, amt); 51 | input = input.Slice(i + "red".Length); 52 | break; 53 | case (byte)'g': 54 | maxG = Math.Max(maxG, amt); 55 | input = input.Slice(i + "green".Length); 56 | break; 57 | case (byte)'b': 58 | maxB = Math.Max(maxB, amt); 59 | input = input.Slice(i + "blue".Length); 60 | break; 61 | } 62 | } 63 | 64 | input = input.Slice(1); 65 | return input; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /csharp/2024/AdventOfCode.CSharp.2024.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AdventOfCode.CSharp.Y2024 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /csharp/Benchmarks/AdventOfCode.CSharp.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /csharp/Benchmarks/ManualConfigExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using BenchmarkDotNet.Columns; 4 | using BenchmarkDotNet.Configs; 5 | 6 | namespace AdventOfCode.CSharp.Benchmarks; 7 | 8 | public static class ManualConfigExtensions 9 | { 10 | public static ManualConfig ClearColumns(this ManualConfig config) 11 | { 12 | var columnProviders = 13 | typeof(ManualConfig)! 14 | .GetField("columnProviders", BindingFlags.Instance | BindingFlags.NonPublic)! 15 | .GetValue(config) as List; 16 | 17 | columnProviders!.Clear(); 18 | 19 | return config; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /csharp/Benchmarks/Y2015Solver.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using AdventOfCode.CSharp.Y2015.Solvers; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace AdventOfCode.CSharp.Benchmarks; 6 | 7 | [GenericTypeArguments(typeof(Day01))] 8 | [GenericTypeArguments(typeof(Day02))] 9 | [GenericTypeArguments(typeof(Day03))] 10 | [GenericTypeArguments(typeof(Day04))] 11 | [GenericTypeArguments(typeof(Day05))] 12 | [GenericTypeArguments(typeof(Day06))] 13 | [GenericTypeArguments(typeof(Day07))] 14 | [GenericTypeArguments(typeof(Day08))] 15 | [GenericTypeArguments(typeof(Day09))] 16 | [GenericTypeArguments(typeof(Day10))] 17 | [GenericTypeArguments(typeof(Day11))] 18 | [GenericTypeArguments(typeof(Day12))] 19 | [GenericTypeArguments(typeof(Day13))] 20 | [GenericTypeArguments(typeof(Day14))] 21 | [GenericTypeArguments(typeof(Day15))] 22 | [GenericTypeArguments(typeof(Day16))] 23 | [GenericTypeArguments(typeof(Day17))] 24 | [GenericTypeArguments(typeof(Day18))] 25 | [GenericTypeArguments(typeof(Day19))] 26 | [GenericTypeArguments(typeof(Day20))] 27 | [GenericTypeArguments(typeof(Day21))] 28 | [GenericTypeArguments(typeof(Day22))] 29 | [GenericTypeArguments(typeof(Day23))] 30 | [GenericTypeArguments(typeof(Day24))] 31 | [GenericTypeArguments(typeof(Day25))] 32 | public class Y2015Solver : SolverBenchmarkBase where TSolver : ISolver 33 | { 34 | } 35 | -------------------------------------------------------------------------------- /csharp/Benchmarks/Y2016Solver.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using AdventOfCode.CSharp.Y2016.Solvers; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace AdventOfCode.CSharp.Benchmarks; 6 | 7 | [GenericTypeArguments(typeof(Day01))] 8 | [GenericTypeArguments(typeof(Day02))] 9 | [GenericTypeArguments(typeof(Day03))] 10 | [GenericTypeArguments(typeof(Day04))] 11 | [GenericTypeArguments(typeof(Day05))] 12 | [GenericTypeArguments(typeof(Day06))] 13 | [GenericTypeArguments(typeof(Day07))] 14 | [GenericTypeArguments(typeof(Day08))] 15 | [GenericTypeArguments(typeof(Day09))] 16 | [GenericTypeArguments(typeof(Day10))] 17 | [GenericTypeArguments(typeof(Day11))] 18 | [GenericTypeArguments(typeof(Day12))] 19 | [GenericTypeArguments(typeof(Day13))] 20 | public class Y2016Solver : SolverBenchmarkBase where TSolver : ISolver 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /csharp/Benchmarks/Y2017Solver.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using AdventOfCode.CSharp.Y2017.Solvers; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace AdventOfCode.CSharp.Benchmarks; 6 | 7 | [GenericTypeArguments(typeof(Day01))] 8 | [GenericTypeArguments(typeof(Day02))] 9 | public class Y2017Solver : SolverBenchmarkBase where TSolver : ISolver 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /csharp/Benchmarks/Y2018Solver.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using AdventOfCode.CSharp.Y2018.Solvers; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace AdventOfCode.CSharp.Benchmarks; 6 | 7 | [GenericTypeArguments(typeof(Day01))] 8 | [GenericTypeArguments(typeof(Day02))] 9 | public class Y2018Solver : SolverBenchmarkBase where TSolver : ISolver 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /csharp/Benchmarks/Y2019Solver.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using AdventOfCode.CSharp.Y2019.Solvers; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace AdventOfCode.CSharp.Benchmarks; 6 | 7 | [GenericTypeArguments(typeof(Day01))] 8 | [GenericTypeArguments(typeof(Day02))] 9 | public class Y2019Solver : SolverBenchmarkBase where TSolver : ISolver 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /csharp/Benchmarks/Y2020Solver.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using AdventOfCode.CSharp.Y2020.Solvers; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace AdventOfCode.CSharp.Benchmarks; 6 | 7 | [GenericTypeArguments(typeof(Day01))] 8 | [GenericTypeArguments(typeof(Day02))] 9 | [GenericTypeArguments(typeof(Day03))] 10 | [GenericTypeArguments(typeof(Day04))] 11 | [GenericTypeArguments(typeof(Day05))] 12 | [GenericTypeArguments(typeof(Day06))] 13 | [GenericTypeArguments(typeof(Day07))] 14 | [GenericTypeArguments(typeof(Day08))] 15 | [GenericTypeArguments(typeof(Day09))] 16 | [GenericTypeArguments(typeof(Day10))] 17 | [GenericTypeArguments(typeof(Day11))] 18 | [GenericTypeArguments(typeof(Day12))] 19 | [GenericTypeArguments(typeof(Day13))] 20 | [GenericTypeArguments(typeof(Day14))] 21 | [GenericTypeArguments(typeof(Day15))] 22 | [GenericTypeArguments(typeof(Day16))] 23 | [GenericTypeArguments(typeof(Day17))] 24 | [GenericTypeArguments(typeof(Day18))] 25 | [GenericTypeArguments(typeof(Day19))] 26 | [GenericTypeArguments(typeof(Day20))] 27 | [GenericTypeArguments(typeof(Day21))] 28 | [GenericTypeArguments(typeof(Day22))] 29 | [GenericTypeArguments(typeof(Day23))] 30 | [GenericTypeArguments(typeof(Day24))] 31 | [GenericTypeArguments(typeof(Day25))] 32 | public class Y2020Solver : SolverBenchmarkBase where TSolver : ISolver 33 | { 34 | } 35 | -------------------------------------------------------------------------------- /csharp/Benchmarks/Y2021Solver.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using AdventOfCode.CSharp.Y2021.Solvers; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace AdventOfCode.CSharp.Benchmarks; 6 | 7 | [GenericTypeArguments(typeof(Day01))] 8 | [GenericTypeArguments(typeof(Day02))] 9 | [GenericTypeArguments(typeof(Day03))] 10 | [GenericTypeArguments(typeof(Day04))] 11 | [GenericTypeArguments(typeof(Day05))] 12 | [GenericTypeArguments(typeof(Day06))] 13 | [GenericTypeArguments(typeof(Day07))] 14 | [GenericTypeArguments(typeof(Day08))] 15 | [GenericTypeArguments(typeof(Day09))] 16 | [GenericTypeArguments(typeof(Day10))] 17 | [GenericTypeArguments(typeof(Day11))] 18 | [GenericTypeArguments(typeof(Day12))] 19 | [GenericTypeArguments(typeof(Day13))] 20 | [GenericTypeArguments(typeof(Day14))] 21 | [GenericTypeArguments(typeof(Day15))] 22 | [GenericTypeArguments(typeof(Day16))] 23 | [GenericTypeArguments(typeof(Day17))] 24 | [GenericTypeArguments(typeof(Day18))] 25 | [GenericTypeArguments(typeof(Day19))] 26 | [GenericTypeArguments(typeof(Day20))] 27 | [GenericTypeArguments(typeof(Day21))] 28 | [GenericTypeArguments(typeof(Day22))] 29 | [GenericTypeArguments(typeof(Day23))] 30 | [GenericTypeArguments(typeof(Day24))] 31 | [GenericTypeArguments(typeof(Day25))] 32 | public class Y2021Solver : SolverBenchmarkBase where TSolver : ISolver 33 | { 34 | } 35 | -------------------------------------------------------------------------------- /csharp/Benchmarks/Y2022Solver.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Common; 2 | using AdventOfCode.CSharp.Y2022.Solvers; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace AdventOfCode.CSharp.Benchmarks; 6 | 7 | [GenericTypeArguments(typeof(Day01))] 8 | [GenericTypeArguments(typeof(Day02))] 9 | [GenericTypeArguments(typeof(Day03))] 10 | [GenericTypeArguments(typeof(Day04))] 11 | [GenericTypeArguments(typeof(Day05))] 12 | [GenericTypeArguments(typeof(Day06))] 13 | [GenericTypeArguments(typeof(Day07))] 14 | [GenericTypeArguments(typeof(Day19))] 15 | [GenericTypeArguments(typeof(Day20))] 16 | public class Y2022Solver : SolverBenchmarkBase where TSolver : ISolver 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /csharp/Benchmarks/Y2024Solver.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using AdventOfCode.CSharp.Common; 3 | using AdventOfCode.CSharp.Y2024.Solvers; 4 | using BenchmarkDotNet.Attributes; 5 | 6 | namespace AdventOfCode.CSharp.Benchmarks; 7 | 8 | [GenericTypeArguments(typeof(Day01))] 9 | [GenericTypeArguments(typeof(Day02))] 10 | [GenericTypeArguments(typeof(Day03))] 11 | [GenericTypeArguments(typeof(Day04))] 12 | [GenericTypeArguments(typeof(Day05))] 13 | [GenericTypeArguments(typeof(Day07))] 14 | public class Y2024Solver : MultiInputSolverBenchmarkBase where TSolver : ISolver 15 | { 16 | } 17 | 18 | [MemoryDiagnoser(displayGenColumns: false)] 19 | public class AllDays2024 20 | { 21 | private readonly char[] _part1Buffer = new char[128]; 22 | private readonly char[] _part2Buffer = new char[128]; 23 | private readonly byte[][][] _inputs = new byte[25][][]; 24 | 25 | public AllDays2024() 26 | { 27 | for (int i = 0; i < 25; i++) 28 | { 29 | _inputs[i] = new byte[5][]; 30 | string inputFolder = $"input/2024/extra/day{i + 1:D2}"; 31 | if (Directory.Exists(inputFolder)) 32 | { 33 | int j = 0; 34 | foreach (string file in Directory.EnumerateFiles(inputFolder)) 35 | _inputs[i][j++] = File.ReadAllBytes(file); 36 | } 37 | } 38 | } 39 | 40 | [Benchmark(OperationsPerInvoke = 5)] 41 | public void SolveAllDays() 42 | { 43 | for (int i = 0; i < 5; i++) 44 | { 45 | Day01.Solve(_inputs[0][i], new(_part1Buffer, _part2Buffer)); 46 | Day02.Solve(_inputs[1][i], new(_part1Buffer, _part2Buffer)); 47 | Day03.Solve(_inputs[2][i], new(_part1Buffer, _part2Buffer)); 48 | Day04.Solve(_inputs[3][i], new(_part1Buffer, _part2Buffer)); 49 | Day05.Solve(_inputs[4][i], new(_part1Buffer, _part2Buffer)); 50 | Day07.Solve(_inputs[6][i], new(_part1Buffer, _part2Buffer)); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /csharp/Common/AdventOfCode.CSharp.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | 6 | -------------------------------------------------------------------------------- /csharp/Common/ISolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AdventOfCode.CSharp.Common; 4 | 5 | public interface ISolver 6 | { 7 | static abstract void Solve(ReadOnlySpan input, Solution solution); 8 | } 9 | -------------------------------------------------------------------------------- /csharp/Common/OCR.cs: -------------------------------------------------------------------------------- 1 | namespace AdventOfCode.CSharp.Common; 2 | 3 | public static class OCR 4 | { 5 | // borrowing OCR table from https://github.com/willkill07/AdventOfCode2016/blob/master/src/Day08.cpp 6 | public static char MaskToLetter(int letterPixels) => letterPixels switch 7 | { 8 | 0x19297A52 => 'A', 9 | 0x392E4A5C => 'B', 10 | 0x1928424C => 'C', 11 | 0x39294A5C => 'D', 12 | 0x3D0E421E => 'E', 13 | 0x3D0E4210 => 'F', 14 | 0x19285A4E => 'G', 15 | 0x252F4A52 => 'H', 16 | 0x1C42108E => 'I', 17 | 0x0C210A4C => 'J', 18 | 0x254C5292 => 'K', 19 | 0x2108421E => 'L', 20 | 0x23BAC631 => 'M', 21 | 0x252D5A52 => 'N', 22 | 0x19294A4C => 'O', 23 | 0x39297210 => 'P', 24 | 0x19295A4D => 'Q', 25 | 0x39297292 => 'R', 26 | 0x1D08305C => 'S', 27 | 0x1C421084 => 'T', 28 | 0x25294A4C => 'U', 29 | 0x2318A944 => 'V', 30 | 0x231AD6AA => 'W', 31 | 0x22A22951 => 'X', 32 | 0x23151084 => 'Y', 33 | 0x3C22221E => 'Z', 34 | 0x00000000 => ' ', 35 | _ => '?' 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /csharp/Common/PermutationIterator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AdventOfCode.CSharp.Common; 4 | 5 | public ref struct PermutationIterator 6 | { 7 | private readonly Span _span; 8 | private readonly int[] _stack; 9 | private int _stackPointer = 0; 10 | private bool _hasMadeFirstMove = false; 11 | 12 | public PermutationIterator(Span span) 13 | { 14 | _span = span; 15 | _stack = new int[span.Length]; 16 | } 17 | 18 | public readonly PermutationIterator GetEnumerator() => this; 19 | 20 | public bool MoveNext() 21 | { 22 | // The first permutation is the input 23 | if (!_hasMadeFirstMove) 24 | { 25 | _hasMadeFirstMove = true; 26 | return true; 27 | } 28 | 29 | // iterate through all permutations using Heap's algorithm 30 | // https://en.wikipedia.org/wiki/Heap%27s_algorithm 31 | if (_stackPointer >= _stack.Length) 32 | { 33 | return false; 34 | } 35 | 36 | while (_stack[_stackPointer] >= _stackPointer) 37 | { 38 | _stack[_stackPointer++] = 0; 39 | if (_stackPointer >= _stack.Length) 40 | { 41 | return false; 42 | } 43 | } 44 | 45 | int swapIndex = _stackPointer % 2 == 0 ? 0 : _stack[_stackPointer]; 46 | 47 | (_span[swapIndex], _span[_stackPointer]) = (_span[_stackPointer], _span[swapIndex]); 48 | _stack[_stackPointer]++; 49 | _stackPointer = 0; 50 | 51 | return true; 52 | } 53 | 54 | public readonly ReadOnlySpan Current => _span; 55 | } 56 | -------------------------------------------------------------------------------- /csharp/Common/Solution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace AdventOfCode.CSharp.Common; 5 | 6 | public readonly ref struct Solution(Span part1Buffer, Span part2Buffer) 7 | { 8 | private readonly Span _part1Buffer = part1Buffer; 9 | private readonly Span _part2Buffer = part2Buffer; 10 | 11 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 12 | public void SubmitPart1(T val) where T : ISpanFormattable 13 | { 14 | val.TryFormat(_part1Buffer, out int charsWritten, default, default); 15 | _part1Buffer[charsWritten] = '\n'; 16 | } 17 | 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void SubmitPart2(T val) where T : ISpanFormattable 20 | { 21 | val.TryFormat(_part2Buffer, out int charsWritten, default, default); 22 | _part2Buffer[charsWritten] = '\n'; 23 | } 24 | 25 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 26 | public void SubmitPart1(ReadOnlySpan val) 27 | { 28 | val.CopyTo(_part1Buffer); 29 | _part1Buffer[val.Length] = '\n'; 30 | } 31 | 32 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 33 | public void SubmitPart2(ReadOnlySpan val) 34 | { 35 | val.CopyTo(_part2Buffer); 36 | _part2Buffer[val.Length] = '\n'; 37 | } 38 | 39 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 40 | public SolutionWriter GetPart1Writer() => new(_part1Buffer); 41 | 42 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 43 | public SolutionWriter GetPart2Writer() => new(_part2Buffer); 44 | } 45 | -------------------------------------------------------------------------------- /csharp/Common/SolutionWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace AdventOfCode.CSharp.Common; 5 | 6 | public ref struct SolutionWriter(Span buffer) 7 | { 8 | private readonly Span _buffer = buffer; 9 | private int _i = 0; 10 | 11 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 12 | public void Write(T val) where T : ISpanFormattable 13 | { 14 | val.TryFormat(_buffer.Slice(_i), out int charsWritten, default, default); 15 | _i += charsWritten; 16 | } 17 | 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void Write(ReadOnlySpan val) 20 | { 21 | val.CopyTo(_buffer.Slice(_i)); 22 | _i += val.Length; 23 | } 24 | 25 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 26 | public void Write(char val) 27 | { 28 | _buffer[_i++] = val; 29 | } 30 | 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | public readonly void Complete() 33 | { 34 | _buffer[_i] = '\n'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /csharp/Common/SolverUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AdventOfCode.CSharp.Common; 4 | 5 | public static class SolverUtils 6 | { 7 | public static (int Year, int Day) GetYearAndDay() where TSolver : ISolver 8 | { 9 | return GetYearAndDay(typeof(TSolver)); 10 | } 11 | 12 | public static (int Year, int Day) GetYearAndDay(Type solverType) 13 | { 14 | int year = int.Parse(solverType.Namespace!.Split('.')[2][1..]); 15 | int dayNumber = int.Parse(solverType.Name[3..]); 16 | return (year, dayNumber); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /csharp/Common/SpanExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AdventOfCode.CSharp.Common; 4 | 5 | public static partial class SpanExtensions 6 | { 7 | public static PermutationIterator GetPermutations(this Span span) => new(span); 8 | 9 | public static MemoryExtensions.SpanSplitEnumerator SplitLines(this ReadOnlySpan str) => str.TrimEnd((byte)'\n').Split((byte)'\n'); 10 | } 11 | -------------------------------------------------------------------------------- /csharp/Common/ThrowHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AdventOfCode.CSharp.Common; 4 | 5 | /// 6 | /// A helper class for throwing exceptions which helps make the call site more inline-able 7 | /// 8 | public static class ThrowHelper 9 | { 10 | public static void ThrowException(string message) => throw new Exception(message); 11 | 12 | public static void ThrowArgumentException(string message, string arg) => throw new ArgumentException(message, arg); 13 | 14 | public static void ThrowArgumentOutOfRangeException(string arg) => throw new ArgumentOutOfRangeException(arg); 15 | 16 | public static void ThrowInvalidOperationException(string message) => throw new InvalidOperationException(message); 17 | } 18 | -------------------------------------------------------------------------------- /csharp/Runner/AdventOfCode.CSharp.Runner.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | PreserveNewest 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /csharp/Runner/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Text; 3 | using System.Text.RegularExpressions; 4 | using AdventOfCode.CSharp.Common; 5 | using AdventOfCode.CSharp.Runner; 6 | 7 | int year = 2024; 8 | int day = 1; 9 | 10 | byte[] inputBytes = await AdventRunner.GetInputAsync(year, day, fetchIfMissing: true); 11 | string input = Encoding.ASCII.GetString(inputBytes); 12 | 13 | var lines = input 14 | .TrimEnd('\n') 15 | .Split("\n") 16 | //.Select(Extensions.ExtractNumbers) 17 | //.Select(LineData.FromString) 18 | .ToList(); 19 | 20 | var ans = 0; 21 | foreach ((var i, var line) in lines.Enumerate()) 22 | { 23 | 24 | } 25 | 26 | Console.WriteLine(ans); 27 | 28 | // Record for storing parsed line data 29 | record LineData() 30 | { 31 | public static LineData FromString(string line) 32 | { 33 | var parts = line.Split(' '); 34 | return new LineData(); 35 | } 36 | } 37 | 38 | static class Extensions 39 | { 40 | public static List ExtractNumbers(this string s) where T : IBinaryInteger 41 | { 42 | var numbers = new List(); 43 | foreach (Match? match in Regex.Matches(s, @"(?:(? ExtractIntegers(this string s) => s.ExtractNumbers(); 54 | public static List ExtractLongs(this string s) => s.ExtractNumbers(); 55 | 56 | public static IEnumerable<(int Index, T Item)> Enumerate(this IEnumerable enumerable) 57 | { 58 | int i = 0; 59 | foreach (var item in enumerable) 60 | yield return (i++, item); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /csharp/Tests/AdventOfCode.CSharp.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /csharp/Tests/Y2016Tests.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Y2016.Solvers; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace AdventOfCode.CSharp.Tests; 5 | 6 | [TestClass] 7 | public class Y2016Tests 8 | { 9 | [TestMethod] public void Day01() => TestHelpers.AssertDay("146", "131"); 10 | [TestMethod] public void Day02() => TestHelpers.AssertDay("45973", "27CA4"); 11 | [TestMethod] public void Day03() => TestHelpers.AssertDay("982", "1826"); 12 | [TestMethod] public void Day04() => TestHelpers.AssertDay("278221", "267"); 13 | [TestMethod] public void Day05() => TestHelpers.AssertDay("2414bc77", "437e60fc"); 14 | [TestMethod] public void Day06() => TestHelpers.AssertDay("wkbvmikb", "evakwaga"); 15 | [TestMethod] public void Day07() => TestHelpers.AssertDay("110", "242"); 16 | [TestMethod] public void Day08() => TestHelpers.AssertDay("128", "EOARGPHYAO"); 17 | [TestMethod] public void Day09() => TestHelpers.AssertDay("112830", "10931789799"); 18 | [TestMethod] public void Day10() => TestHelpers.AssertDay("86", "22847"); 19 | [TestMethod] public void Day11() => TestHelpers.AssertDay("33", "57"); 20 | [TestMethod] public void Day12() => TestHelpers.AssertDay("318003", "9227657"); 21 | [TestMethod] public void Day13() => TestHelpers.AssertDay("82", "138"); 22 | } 23 | -------------------------------------------------------------------------------- /csharp/Tests/Y2017Tests.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Y2017.Solvers; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace AdventOfCode.CSharp.Tests; 5 | 6 | [TestClass] 7 | public class Y2017Tests 8 | { 9 | [TestMethod] public void Day01() => TestHelpers.AssertDay("1047", "982"); 10 | 11 | [TestMethod] public void Day02() => TestHelpers.AssertDay("53460", "282"); 12 | } 13 | -------------------------------------------------------------------------------- /csharp/Tests/Y2018Tests.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Y2018.Solvers; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace AdventOfCode.CSharp.Tests; 5 | 6 | [TestClass] 7 | public class Y2018Tests 8 | { 9 | [TestMethod] public void Day01() => TestHelpers.AssertDay("516", "71892"); 10 | 11 | [TestMethod] public void Day02() => TestHelpers.AssertDay("7470", "kqzxdenujwcstybmgvyiofrrd"); 12 | } 13 | -------------------------------------------------------------------------------- /csharp/Tests/Y2019Tests.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Y2019.Solvers; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace AdventOfCode.CSharp.Tests; 5 | 6 | [TestClass] 7 | public class Y2019Tests 8 | { 9 | [TestMethod] public void Day01() => TestHelpers.AssertDay("3226488", "4836845"); 10 | 11 | [TestMethod] public void Day02() => TestHelpers.AssertDay("3306701", "7621"); 12 | } 13 | -------------------------------------------------------------------------------- /csharp/Tests/Y2022Tests.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Y2022.Solvers; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace AdventOfCode.CSharp.Tests; 5 | 6 | [TestClass] 7 | public class Y2022Tests 8 | { 9 | [TestMethod] public void Day01() => TestHelpers.AssertDay("70720", "207148"); 10 | [TestMethod] public void Day02() => TestHelpers.AssertDay("12535", "15457"); 11 | [TestMethod] public void Day03() => TestHelpers.AssertDay("8515", "2434"); 12 | [TestMethod] public void Day04() => TestHelpers.AssertDay("534", "841"); 13 | [TestMethod] public void Day05() => TestHelpers.AssertDay("JRVNHHCSJ", "GNFBSBJLH"); 14 | [TestMethod] public void Day06() => TestHelpers.AssertDay("1912", "2122"); 15 | [TestMethod] public void Day07() => TestHelpers.AssertDay("1141028", "8278005"); 16 | [TestMethod] public void Day19() => TestHelpers.AssertDay("1480", "3168"); 17 | [TestMethod] public void Day20() => TestHelpers.AssertDay("5498", "3390007892081"); 18 | } 19 | -------------------------------------------------------------------------------- /csharp/Tests/Y2024Tests.cs: -------------------------------------------------------------------------------- 1 | using AdventOfCode.CSharp.Y2024.Solvers; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace AdventOfCode.CSharp.Tests; 5 | 6 | [TestClass] 7 | public class Y2024Tests 8 | { 9 | [TestMethod][MultiInputData(2024, 1)] public void Day01(string filePath, string expectedPart1, string expectedPart2) => TestHelpers.AssertDay(filePath, expectedPart1, expectedPart2); 10 | [TestMethod][MultiInputData(2024, 2)] public void Day02(string filePath, string expectedPart1, string expectedPart2) => TestHelpers.AssertDay(filePath, expectedPart1, expectedPart2); 11 | [TestMethod][MultiInputData(2024, 3)] public void Day03(string filePath, string expectedPart1, string expectedPart2) => TestHelpers.AssertDay(filePath, expectedPart1, expectedPart2); 12 | [TestMethod][MultiInputData(2024, 4)] public void Day04(string filePath, string expectedPart1, string expectedPart2) => TestHelpers.AssertDay(filePath, expectedPart1, expectedPart2); 13 | [TestMethod][MultiInputData(2024, 5)] public void Day05(string filePath, string expectedPart1, string expectedPart2) => TestHelpers.AssertDay(filePath, expectedPart1, expectedPart2); 14 | [TestMethod][MultiInputData(2024, 7)] public void Day07(string filePath, string expectedPart1, string expectedPart2) => TestHelpers.AssertDay(filePath, expectedPart1, expectedPart2); 15 | } 16 | -------------------------------------------------------------------------------- /fsharp/2015/AdventOfCode.FSharp.2015.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /fsharp/2015/Program.fs: -------------------------------------------------------------------------------- 1 | namespace AdventOfCode.FSharp.Y2015 2 | 3 | open AdventOfCode.FSharp 4 | open AdventOfCode.FSharp.Common 5 | open AdventOfCode.FSharp.Benchmarking 6 | 7 | module Program = 8 | let getSolver day part printAnswer = 9 | let run (solver : Day<_, _, _>) = 10 | Runner.run printAnswer 2015 day part solver 11 | match day with 12 | | 1 -> run Year2015Day01.solver | 2 -> run Year2015Day02.solver | 3 -> run Year2015Day03.solver 13 | | 4 -> run Year2015Day04.solver | 5 -> run Year2015Day05.solver | 6 -> run Year2015Day06.solver 14 | | 7 -> run Year2015Day07.solver | 8 -> run Year2015Day08.solver | 9 -> run Year2015Day09.solver 15 | | 10 -> run Year2015Day10.solver | 11 -> run Year2015Day11.solver | 12 -> run Year2015Day12.solver 16 | | 13 -> run Year2015Day13.solver | 14 -> run Year2015Day14.solver | 15 -> run Year2015Day15.solver 17 | | 16 -> run Year2015Day16.solver | 17 -> run Year2015Day17.solver | 18 -> run Year2015Day18.solver 18 | | 19 -> run Year2015Day19.solver | 20 -> run Year2015Day20.solver | 21 -> run Year2015Day21.solver 19 | | 22 -> run Year2015Day22.solver | 23 -> run Year2015Day23.solver | 24 -> run Year2015Day24.solver 20 | | 25 -> run Year2015Day25.solver 21 | | day -> fun _ -> printfn "Invalid Day: %i (Year %i)" day 2015 22 | 23 | type Bench2015() = 24 | inherit Bench() with 25 | override _.GetSolverFunc day part () = 26 | getSolver day part false () 27 | 28 | [] 29 | let main argv = 30 | let runPart day part = getSolver day part true () 31 | let runDay day = for part in 1..2 do runPart day part 32 | match argv.[0] with 33 | | "BENCH" -> Benchmarking.runBenchmarks() 34 | | "ALL" -> for day in 1..25 do runDay day 35 | | x -> 36 | let parts = x.Split('.') |> Array.map int 37 | match parts.Length with 38 | | 1 -> runDay parts.[0] 39 | | 2 -> runPart parts.[0] parts.[1] 40 | | _ -> () 41 | 0 42 | -------------------------------------------------------------------------------- /fsharp/2015/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "AdventOfCode.2015": { 4 | "commandName": "Project", 5 | "commandLineArgs": "ALL" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day01.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day01 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let charToLevelDiff = 6 | function 7 | | '(' -> 1 8 | | ')' -> -1 9 | | c -> failwithf "Invalid character: %c" c 10 | 11 | let solvePart1 input = 12 | input 13 | |> Seq.sumBy charToLevelDiff 14 | 15 | let solvePart2 input = 16 | input 17 | |> Seq.map charToLevelDiff 18 | |> Seq.scan (+) 0 19 | |> Seq.findIndex (fun l -> l < 0) 20 | 21 | let solver = { parse = parseFirstLine asString; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day02.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day02 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type Prism = { W : int; H : int; L : int; } 6 | let asPrism (arr : string []) = 7 | { W = int arr.[0]; H = int arr.[1]; L = int arr.[2]} 8 | 9 | let parse = parseEachLine (splitBy "x" asPrism) 10 | 11 | let solvePart1 input = 12 | input 13 | |> Seq.sumBy (fun p -> 14 | let sides = [| p.L * p.W; p.W * p.H; p.H * p.L |] 15 | let area = 2 * (Seq.sum sides) 16 | area + (Seq.min sides)) 17 | 18 | let solvePart2 input = 19 | input 20 | |> Seq.sumBy (fun p -> 21 | let dims = [| p.L; p.W; p.H |] 22 | let wrap = 2 * (Seq.sum dims - Seq.max dims) 23 | let bow = p.L * p.W * p.H 24 | wrap + bow) 25 | 26 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day03.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day03 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type Dir = N | S | E | W 6 | let parseDir = function | '^' -> N | 'v' -> S | '>' -> E | '<' -> W | c -> failwithf "Invalid: %A" c 7 | 8 | let parse = parseFirstLine asString >> Seq.map parseDir 9 | 10 | let solvePart1 input = 11 | input 12 | |> Seq.mapFold (fun (x, y) dir -> 13 | let newPos = 14 | match dir with 15 | | N -> (x, y - 1) 16 | | S -> (x, y + 1) 17 | | E -> (x + 1, y) 18 | | W -> (x - 1, y) 19 | newPos, newPos) (0, 0) 20 | |> fst 21 | |> Seq.append [(0, 0)] 22 | |> Seq.groupBy id 23 | |> Seq.length 24 | 25 | let solvePart2 input = 26 | input 27 | |> Seq.mapFold (fun ((x1, y1), (x2, y2), isFirstRobot) dir -> 28 | let (x, y) = if isFirstRobot then (x1, y1) else (x2, y2) 29 | let newPos = 30 | match dir with 31 | | N -> (x, y - 1) 32 | | S -> (x, y + 1) 33 | | E -> (x + 1, y) 34 | | W -> (x - 1, y) 35 | let (x1, y1) = if isFirstRobot then newPos else (x1, y1) 36 | let (x2, y2) = if not isFirstRobot then newPos else (x2, y2) 37 | [(x1, y1); (x2, y2)], ((x1, y1), (x2, y2), not isFirstRobot)) ((0, 0), (0, 0), true) 38 | |> fst 39 | |> Seq.concat 40 | |> Seq.append [(0, 0)] 41 | |> Seq.groupBy id 42 | |> Seq.length 43 | 44 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day04.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day04 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System.Security.Cryptography 5 | open System.Text 6 | 7 | let parse = parseFirstLine asString 8 | 9 | let getHash (input : string) = 10 | let md5Obj = MD5.Create() 11 | let inputAsBytes = Encoding.ASCII.GetBytes input 12 | let getHash' index = 13 | let indexBytes = Encoding.ASCII.GetBytes (string index) 14 | let byteArr = Array.append inputAsBytes indexBytes 15 | md5Obj.ComputeHash byteArr 16 | getHash' 17 | 18 | let solvePart1 input = 19 | Seq.initInfinite (getHash input) 20 | |> Seq.indexed 21 | |> Seq.find (fun (i, h) -> h.[0] = 0uy && h.[1] = 0uy && h.[2] &&& 0xF0uy = 0uy) 22 | |> fst 23 | 24 | let solvePart2 input = 25 | Seq.initInfinite (getHash input) 26 | |> Seq.indexed 27 | |> Seq.find (fun (i, h) -> h.[0] = 0uy && h.[1] = 0uy && h.[2] = 0uy) 28 | |> fst 29 | 30 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day05.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day05 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseEachLine asString 6 | 7 | let isNice1 str = 8 | let counts = str |> Seq.groupBy id |> Map.ofSeq |> Map.map (fun k v -> Seq.length v) 9 | let vowels = "aeiou" |> Seq.sumBy (fun c -> Map.tryFind c counts |> Option.defaultValue 0) 10 | let doubles = str |> Seq.pairwise |> Seq.filter (fun (i, j) -> i = j) 11 | let hasDouble = doubles |> Seq.isEmpty |> not 12 | let consecPairs = str |> Seq.pairwise |> Seq.filter (fun (i, j) -> int j - int i = 1 && "acpx".Contains(i)) 13 | let hasConsec = consecPairs |> Seq.isEmpty |> not 14 | vowels >= 3 && hasDouble && (not hasConsec) 15 | 16 | let solvePart1 input = 17 | input 18 | |> Seq.where isNice1 19 | |> Seq.length 20 | 21 | let isNice2 (str : string) = 22 | let triples = str |> Seq.windowed 3 |> Seq.where (fun cs -> cs.[0] = cs.[2]) 23 | let pairsNoOverlap = 24 | str 25 | |> Seq.pairwise 26 | |> Seq.indexed 27 | |> Seq.groupBy snd 28 | |> Seq.filter (fun (_, pairs) -> 29 | let inds = pairs |> Seq.map fst |> Seq.toArray 30 | inds.Length > 2 || (inds.Length = 2 && abs (inds.[0] - inds.[1]) <> 1)) 31 | 32 | let hasTriple = triples |> Seq.isEmpty |> not 33 | let hasPairs = pairsNoOverlap |> Seq.isEmpty |> not 34 | hasTriple && hasPairs 35 | 36 | let solvePart2 input = 37 | input 38 | |> Seq.where isNice2 39 | |> Seq.length 40 | 41 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day08.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day08 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseEachLine asString 6 | 7 | let getStrLength str = 8 | let l = String.length str 9 | let rec parsePosition (count : int) i = 10 | if i >= l then count 11 | else 12 | let isLast = i = l - 1 13 | match str.[i] with 14 | | '\\' -> 15 | if isLast then count + 1 16 | else 17 | match str.[i + 1] with 18 | | '\\' -> parsePosition (count + 1) (i + 2) 19 | | 'x' when i <= l - 4 -> parsePosition (count + 1) (i + 4) 20 | | '"' -> parsePosition (count + 1) (i + 2) 21 | | _ -> parsePosition (count + 1) (i + 1) 22 | | '"' -> parsePosition count (i + 1) 23 | | _ -> parsePosition (count + 1) (i + 1) 24 | 25 | parsePosition 0 0 26 | 27 | let encodeString str = 28 | let l = String.length str 29 | let rec parsePosition (count : int) i = 30 | if i >= l then count 31 | else 32 | let isLast = i = l - 1 33 | match str.[i] with 34 | | '\\' -> 35 | if isLast then count + 1 36 | else 37 | match str.[i + 1] with 38 | | '\\' -> parsePosition (count + 4) (i + 2) 39 | | 'x' when i <= l - 4 -> parsePosition (count + 5) (i + 4) 40 | | '"' -> parsePosition (count + 4) (i + 2) 41 | | _ -> parsePosition (count + 1) (i + 1) 42 | | '"' -> parsePosition (count + 2) (i + 1) 43 | | _ -> parsePosition (count + 1) (i + 1) 44 | 45 | parsePosition 2 0 46 | 47 | let solvePart1 lines = 48 | lines 49 | |> Seq.sumBy (fun str -> String.length str - getStrLength str) 50 | 51 | let solvePart2 lines = 52 | lines 53 | |> Seq.sumBy (fun str -> encodeString str - String.length str) 54 | 55 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day09.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day09 2 | 3 | open AdventOfCode.FSharp.Common 4 | open GraphFS.Graph 5 | 6 | type Edge = 7 | { P1 : string; P2 : string; Dist : int } 8 | 9 | let parseEdge line = 10 | let toks = splitBy " " id line 11 | { P1 = toks.[0]; P2 = toks.[2]; Dist = int toks.[4]} 12 | 13 | let parse = parseEachLine parseEdge 14 | 15 | let solvePart1 input = 16 | let G = 17 | input 18 | |> Seq.map (fun e -> [(e.P1, e.P2), e.Dist; (e.P2, e.P1), e.Dist]) 19 | |> Seq.collect id 20 | |> Graph.fromEdgesWithData 21 | 22 | let locations = Graph.verts G |> Set.ofSeq 23 | 24 | let rec getShortest current remainingLocs = 25 | if Set.isEmpty remainingLocs then 0 26 | else 27 | remainingLocs 28 | |> Seq.map (fun l -> 29 | let dist = Graph.getEdgeData (current, l) G 30 | let remPath = getShortest l (Set.remove l remainingLocs) 31 | dist + remPath) 32 | |> Seq.min 33 | 34 | locations 35 | |> Seq.map (fun l -> getShortest l (Set.remove l locations)) 36 | |> Seq.min 37 | 38 | let solvePart2 input = 39 | let G = 40 | input 41 | |> Seq.map (fun e -> [(e.P1, e.P2), e.Dist; (e.P2, e.P1), e.Dist]) 42 | |> Seq.collect id 43 | |> Graph.fromEdgesWithData 44 | 45 | let locations = Graph.verts G |> Set.ofSeq 46 | 47 | let rec getShortest current remainingLocs = 48 | if Set.isEmpty remainingLocs then 0 49 | else 50 | remainingLocs 51 | |> Seq.map (fun l -> 52 | let dist = Graph.getEdgeData (current, l) G 53 | let remPath = getShortest l (Set.remove l remainingLocs) 54 | dist + remPath) 55 | |> Seq.max 56 | 57 | locations 58 | |> Seq.map (fun l -> getShortest l (Set.remove l locations)) 59 | |> Seq.max 60 | 61 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day10.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day10 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let toCounts str = 6 | str |> Seq.map (fun c -> (1, int c - int '0')) |> Seq.toList 7 | 8 | let rec compress acc counts = 9 | match counts with 10 | | [] -> acc [] 11 | | (n1, c1) :: (n2, c2) :: xs when c1 = c2 -> compress acc ((n1 + n2, c1) :: xs) 12 | | x :: xs -> compress (fun a -> acc (x :: a)) xs 13 | 14 | let iterate counts = 15 | counts 16 | |> compress id 17 | |> List.map (fun (n, c) -> [ (1, n); (1, c) ]) 18 | |> List.collect id 19 | 20 | let parse = parseFirstLine toCounts 21 | 22 | let solvePart1 input = 23 | let rec iterN n counts = 24 | if n = 0 then counts |> List.sumBy fst 25 | else iterate counts |> iterN (n - 1) 26 | iterN 40 input 27 | 28 | let solvePart2 input = 29 | let rec iterN n counts = 30 | if n = 0 then counts |> List.sumBy fst 31 | else iterate counts |> iterN (n - 1) 32 | iterN 50 input 33 | 34 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day11.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day11 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let bumpInvalidLetters (str : string) = 6 | str.Replace('i', 'j').Replace('o', 'p').Replace('l', 'm') 7 | 8 | let parse = parseFirstLine (bumpInvalidLetters) 9 | 10 | let nextChar c = 11 | match c with 12 | | 'z' -> 'a' 13 | | 'h' -> 'j' 14 | | 'n' -> 'p' 15 | | 'k' -> 'm' 16 | | c -> (int c) + 1 |> char 17 | 18 | let rec nextStr (str : string) = 19 | let len = String.length str 20 | if len = 1 then string (nextChar str.[0]) 21 | else 22 | match str.[len - 1] with 23 | | 'z' -> nextStr (str.Substring(0, len - 1)) + "a" 24 | | c -> str.Substring(0, len - 1) + (nextChar c |> string) 25 | 26 | let isValid str = 27 | let hasTriple = str |> Seq.map int |> Seq.windowed 3 |> Seq.exists (fun w -> w.[1] = w.[0] + 1 && w.[2] = w.[1] + 1) 28 | let doubleIndexes = 29 | str 30 | |> Seq.pairwise 31 | |> Seq.indexed 32 | |> Seq.filter (fun (i, (a, b)) -> a = b) 33 | |> Seq.map fst 34 | |> Seq.sort 35 | |> Seq.toArray 36 | 37 | let hasDouble = 38 | match doubleIndexes.Length with 39 | | 0 | 1 -> false 40 | | 2 -> doubleIndexes.[1] - doubleIndexes.[0] > 1 41 | | _ -> true 42 | 43 | hasTriple && hasDouble 44 | 45 | let solvePart1 input = 46 | let rec findNextMatch str = 47 | let n = nextStr str 48 | if isValid n then n 49 | else findNextMatch n 50 | 51 | findNextMatch (bumpInvalidLetters input) 52 | 53 | let solvePart2 input = 54 | let rec findNextMatch str = 55 | let n = nextStr str 56 | if isValid n then n 57 | else findNextMatch n 58 | 59 | findNextMatch (bumpInvalidLetters input) 60 | |> findNextMatch 61 | 62 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day12.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day12 2 | 3 | open AdventOfCode.FSharp.Common 4 | open Newtonsoft.Json.Linq 5 | 6 | let parse = parseFirstLine asString 7 | 8 | let solvePart1 input = extractInts input |> Seq.sum 9 | 10 | let solvePart2 input = 11 | let rec getTotals (obj : JToken) = 12 | match obj.Type with 13 | | JTokenType.Integer -> obj.Value() 14 | | JTokenType.Array -> (obj :?> JArray) |> Seq.sumBy getTotals 15 | | JTokenType.Object -> 16 | let values : JToken seq = (obj :?> JObject) |> Seq.map (fun kvp -> (kvp :?> JProperty).Value) 17 | let hasRed = values |> Seq.filter (fun t -> t.Type = JTokenType.String) |> Seq.exists (fun t -> (t.Value()) = "red") 18 | if hasRed then 0 19 | else values |> Seq.sumBy getTotals 20 | | _ -> 0 21 | 22 | let asJson = JObject.Parse ("{\"data\": " + input + "}") 23 | getTotals asJson 24 | 25 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day15.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day15 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type Ingredient = { Capacity : int; Durability : int; Flavor : int; Texture : int; Calories : int } 6 | 7 | let props ingr = [| ingr.Capacity; ingr.Durability; ingr.Flavor; ingr.Texture |] 8 | 9 | let parseIngredient line = 10 | let ints = extractInts line 11 | { Capacity = ints.[0]; Durability = ints.[1]; Flavor = ints.[2]; Texture = ints.[3]; Calories = ints.[4] } 12 | 13 | let parse = parseEachLine parseIngredient 14 | 15 | let solvePart1 input = 16 | let ings = input |> Seq.toArray 17 | seq { 18 | for i = 0 to 100 do 19 | for j = 0 to (100 - i) do 20 | for k = 0 to (100 - i - j) do 21 | for l = 0 to (100 - i - j - k) do 22 | let mults = [|i; j; k; l|] 23 | [ 0 .. 3 ] 24 | |> Seq.map (fun prop -> 25 | [0 .. 3] 26 | |> Seq.sumBy (fun ingI -> (props ings.[ingI]).[prop] * mults.[ingI]) 27 | |> max 0) 28 | |> Seq.reduce (*) } |> Seq.max 29 | 30 | let solvePart2 input = 31 | let ings = input |> Seq.toArray 32 | seq { 33 | for i = 0 to 100 do 34 | for j = 0 to (100 - i) do 35 | for k = 0 to (100 - i - j) do 36 | let l = 100 - i - j - k 37 | let mults = [|i; j; k; l|] 38 | let calories = [0 .. 3] |> Seq.sumBy (fun ingI -> ings.[ingI].Calories * mults.[ingI]) 39 | if calories = 500 then 40 | [ 0 .. 3 ] 41 | |> Seq.map (fun prop -> 42 | [0 .. 3] 43 | |> Seq.sumBy (fun ingI -> (props ings.[ingI]).[prop] * mults.[ingI]) 44 | |> max 0) 45 | |> Seq.reduce (*) } |> Seq.max 46 | 47 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day16.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day16 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type Sue = { Number : int; Items : Map } 6 | 7 | let parseSue line = 8 | let toks = splitBy " " id line 9 | let num = toks.[1].TrimEnd(':') |> int 10 | 11 | let items = 12 | toks 13 | |> Seq.skip 2 14 | |> Seq.chunkBySize 2 15 | |> Seq.map (fun (entry : string []) -> 16 | let item = entry.[0].TrimEnd(':') 17 | let amount = int (entry.[1].TrimEnd(',')) 18 | item, amount) 19 | |> Map.ofSeq 20 | 21 | { Number = num; Items = items } 22 | 23 | let parse = parseEachLine parseSue 24 | 25 | let amounts = 26 | [ 27 | 3, "children" 28 | 7, "cats" 29 | 2, "samoyeds" 30 | 3, "pomeranians" 31 | 0, "akitas" 32 | 0, "vizslas" 33 | 5, "goldfish" 34 | 3, "trees" 35 | 2, "cars" 36 | 1, "perfumes" 37 | ] 38 | 39 | let scoreSue1 sue = 40 | let tryGet item = 41 | if Map.containsKey item sue.Items then Some sue.Items.[item] 42 | else None 43 | 44 | amounts 45 | |> Seq.exists (fun (a, i) -> 46 | match tryGet i with 47 | | Some x -> a <> x 48 | | None -> false) 49 | |> not 50 | 51 | let solvePart1 sues = 52 | sues 53 | |> Seq.filter scoreSue1 54 | |> Seq.head 55 | |> (fun s -> s.Number) 56 | 57 | let scoreSue2 sue = 58 | let tryGet item = 59 | if Map.containsKey item sue.Items then Some sue.Items.[item] 60 | else None 61 | 62 | amounts 63 | |> Seq.exists (fun (a, i) -> 64 | match tryGet i with 65 | | Some x -> 66 | match i with 67 | | "cats" | "trees" -> x <= a 68 | | "pomeranians" | "goldfish" -> x >= a 69 | | _ -> x <> a 70 | | None -> false) 71 | |> not 72 | 73 | let solvePart2 sues = 74 | sues 75 | |> Seq.filter scoreSue2 76 | |> Seq.head 77 | |> (fun s -> s.Number) 78 | 79 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day17.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day17 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseEachLine asInt 6 | 7 | let solvePart1 input = 8 | let sizes = input |> Seq.sortByDescending id |> Seq.toArray 9 | let firstRow = Array.init 151 (fun i -> if i = 0 then 1 else 0) 10 | let nextRow (size : int) (row : int []) = 11 | let nextCell cur_sum = 12 | if cur_sum < size then row.[cur_sum] 13 | else row.[cur_sum] + row.[cur_sum - size] 14 | [| 0 .. 150 |] |> Array.map nextCell 15 | let arr = Array.foldBack nextRow sizes firstRow 16 | arr.[150] 17 | 18 | let solvePart2 input = 19 | let sizes = input |> Seq.sortByDescending id |> Seq.toArray 20 | let firstRow = Array.init 151 (fun i -> if i = 0 then (0, 1) else (sizes.Length + 1, 0)) 21 | let nextRow (size : int) (row : (int * int) []) = 22 | let nextCell cur_sum = 23 | if cur_sum < size then row.[cur_sum] 24 | else 25 | let c1, w1 = row.[cur_sum] 26 | let c2, w2 = row.[cur_sum - size] 27 | if c1 < c2 + 1 then 28 | c1, w1 29 | elif c1 > c2 + 1 then 30 | c2 + 1, w2 31 | else 32 | c1, w1 + w2 33 | 34 | [| 0 .. 150 |] |> Array.map nextCell 35 | let arr = Array.foldBack nextRow sizes firstRow 36 | arr.[150] |> snd 37 | 38 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day19.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day19 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parseReplacements lines = 6 | let lineArr = lines |> Seq.toArray 7 | let replacements = 8 | lineArr 9 | |> Array.take (lineArr.Length - 2) 10 | |> Array.map (splitBy " => " (fun t -> t.[0], t.[1])) 11 | |> Array.groupBy fst 12 | |> Map.ofArray 13 | |> Map.map (fun _ v -> v |> Array.map snd) 14 | let molecule = Array.last lineArr 15 | 16 | replacements, molecule 17 | 18 | let parse = parseEachLine asString >> parseReplacements 19 | 20 | let solvePart1 (replacements, (molecule : string)) = 21 | [0 .. molecule.Length - 1] 22 | |> Seq.collect (fun i -> 23 | if i = molecule.Length - 1 then [i, 1] 24 | else [i, 1; i, 2]) 25 | |> Seq.collect (fun (i, l) -> 26 | let k = molecule.Substring(i, l) 27 | if Map.containsKey k replacements then 28 | replacements.[k] 29 | |> Array.map (fun r -> molecule.Substring(0, i) + r + molecule.Substring(min (molecule.Length - 1) (l + i))) 30 | else [||]) 31 | |> Seq.distinct 32 | |> Seq.length 33 | 34 | let solvePart2 (_, molecule) = 35 | let totalElements = molecule |> Seq.filter (fun c -> System.Char.IsUpper c) |> Seq.length 36 | let totalRn = molecule |> Seq.pairwise |> Seq.filter (fun (a, b) -> a = 'R' && b = 'n') |> Seq.length 37 | let totalAr = molecule |> Seq.pairwise |> Seq.filter (fun (a, b) -> a = 'A' && b = 'r') |> Seq.length 38 | let totalY = molecule |> Seq.filter (fun a -> a = 'Y') |> Seq.length 39 | totalElements - totalRn - totalAr - 2 * totalY - 1 40 | 41 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day20.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day20 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseFirstLine asInt 6 | 7 | let rec getPrimeFactors n = 8 | let fac = 9 | [2 .. int (ceil (sqrt (float n)))] 10 | |> List.tryFind (fun i -> n % i = 0 && i <> n) 11 | 12 | match fac with 13 | | Some f -> f :: (getPrimeFactors (n / f)) 14 | | None -> [n] 15 | 16 | let rec allMultiples factors = 17 | match factors with 18 | | x :: xs -> 19 | let ms = allMultiples xs 20 | let ms2 = ms |> Set.map (fun y -> y * x) 21 | Set.union ms ms2 22 | | [] -> Set.empty |> Set.add 1 23 | 24 | let allFactors n = getPrimeFactors n |> allMultiples |> Set.add n 25 | 26 | let solvePart1 input = 27 | Seq.initInfinite id 28 | |> Seq.find (fun i -> 29 | let mults = allFactors i 30 | let s = Seq.sum mults 31 | s * 10 >= input) 32 | 33 | let solvePart2 input = 34 | Seq.initInfinite id 35 | |> Seq.find (fun i -> 36 | let mults = allFactors i 37 | let s = mults |> Seq.filter (fun m -> m = 0 || i / m <= 50) |> Seq.sum 38 | s * 11 >= input) 39 | 40 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day23.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day23 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type Instruction = 6 | | Half of string 7 | | Triple of string 8 | | Increment of string 9 | | Jump of int 10 | | JumpIfEven of string * int 11 | | JumpIfOne of string * int 12 | 13 | let asInstr line = 14 | let toks = splitBy " " id line 15 | match toks.[0] with 16 | | "hlf" -> Half toks.[1] 17 | | "tpl" -> Triple toks.[1] 18 | | "inc" -> Increment toks.[1] 19 | | "jmp" -> Jump (int toks.[1]) 20 | | "jie" -> JumpIfEven (toks.[1].TrimEnd(','), int toks.[2]) 21 | | "jio" -> JumpIfOne (toks.[1].TrimEnd(','), int toks.[2]) 22 | | _ -> failwithf "Invalid Instruction: %s" line 23 | 24 | let parse = parseEachLine asInstr >> Seq.toArray 25 | 26 | type State = { PC : int; Regs : Map; Code : Instruction [] } 27 | let setReg reg value state = { state with Regs = Map.add reg value state.Regs } 28 | let getReg reg state = Map.tryFind reg state.Regs |> Option.defaultValue 0 29 | let mapReg f reg state = state |> setReg reg (getReg reg state |> f) 30 | let jmp offset state = { state with PC = state.PC + offset } 31 | 32 | let step state = 33 | match state.Code.[state.PC] with 34 | | Half r -> state |> mapReg (fun r -> r / 2) r |> jmp 1 35 | | Triple r -> state |> mapReg (fun r -> r * 3) r |> jmp 1 36 | | Increment r -> state |> mapReg ((+) 1) r |> jmp 1 37 | | Jump o -> state |> jmp o 38 | | JumpIfEven (r, o) -> state |> jmp (if getReg r state % 2 = 0 then o else 1) 39 | | JumpIfOne (r, o) -> state |> jmp (if getReg r state = 1 then o else 1) 40 | 41 | let runProgram state = 42 | let rec run' state = 43 | if state.PC < 0 || state.PC >= state.Code.Length then state 44 | else step state |> run' 45 | run' state 46 | 47 | let solvePart1 input = 48 | { PC = 0; Regs = Map.empty; Code = input } 49 | |> runProgram 50 | |> getReg "b" 51 | 52 | let solvePart2 input = 53 | { PC = 0; Regs = Map.empty; Code = input } 54 | |> setReg "a" 1 55 | |> runProgram 56 | |> getReg "b" 57 | 58 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day24.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day24 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseEachLine asInt >> Seq.map int64 >> Seq.toArray 6 | 7 | let getCombinations (target : int64) (weights : int64 []) = 8 | let rec getCombinations' target (weightIndex : int) = 9 | match Array.tryItem weightIndex weights with 10 | | None -> Seq.empty 11 | | Some w -> 12 | if target = w then seq { (2 <<< int32 w), 1, w } 13 | elif target < w then Seq.empty 14 | else seq { 15 | yield! getCombinations' (target - w) (weightIndex + 1) |> Seq.map (fun (c, ct, qe) -> (c ||| (2 <<< int32 w)), ct + 1, w * qe) 16 | yield! getCombinations' target (weightIndex + 1) } 17 | getCombinations' target 0 18 | 19 | let solvePart1 input = 20 | let totalWeight = input |> Array.sum 21 | let target = totalWeight / 3L 22 | let allCombinations = getCombinations target (Array.sort input) |> Seq.toArray 23 | let _, _, qe = 24 | allCombinations 25 | // The below two lines are necessary if the minimal value does not divide evenly. 26 | //|> Array.sortBy (fun (c, ct, qe) -> ct, qe) 27 | //|> Array.find (fun (c1, _, _) -> Array.exists (fun (c2, _, _) -> c1 &&& c2 = 0) allCombinations) 28 | |> Array.minBy (fun (_, ct, qe) -> ct, qe) 29 | qe 30 | 31 | let solvePart2 input = 32 | let totalWeight = input |> Array.sum 33 | let target = totalWeight / 4L 34 | let allCombinations = getCombinations target (Array.sort input) |> Seq.toArray 35 | // The commented out lines are necessary if the minimal value does not divide evenly. 36 | //let allCombinationPairs = getCombinations (target * 2L) (Array.sort input) |> Seq.toArray 37 | let _, _, qe = 38 | allCombinations 39 | //|> Array.sortBy (fun (_, ct, qe) -> ct, qe) 40 | //|> Array.find (fun (c1, _, _) -> Array.exists (fun (c2, _, _) -> c1 &&& c2 = 0) allCombinationPairs) 41 | |> Array.minBy (fun (_, ct, qe) -> ct, qe) 42 | qe 43 | 44 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2015/Solutions/Day25.fs: -------------------------------------------------------------------------------- 1 | module Year2015Day25 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseFirstLine extractInts 6 | 7 | let getCode row column = 8 | let n = row + column - 1 9 | let diagEnd = n * (n + 1) / 2 10 | let repetitions = diagEnd - row 11 | let rec genNext c n = 12 | if n = 0 then c 13 | else genNext ((c * 252533L) % 33554393L) (n - 1) 14 | genNext 20151125L repetitions 15 | 16 | let solvePart1 (input : int []) = 17 | let row, column = input.[0], input.[1] 18 | getCode row column 19 | 20 | let solver = { parse = parse; part1 = solvePart1; part2 = (fun _ -> "Advent of Code Finished!") } -------------------------------------------------------------------------------- /fsharp/2016/AdventOfCode.FSharp.2016.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /fsharp/2016/Program.fs: -------------------------------------------------------------------------------- 1 | namespace AdventOfCode.FSharp.Y2016 2 | 3 | open AdventOfCode.FSharp 4 | open AdventOfCode.FSharp.Common 5 | open AdventOfCode.FSharp.Benchmarking 6 | 7 | module Program = 8 | let getSolver day part printAnswer = 9 | let run (solver : Day<_, _, _>) = 10 | Runner.run printAnswer 2016 day part solver 11 | match day with 12 | | 1 -> run Year2016Day01.solver | 2 -> run Year2016Day02.solver | 3 -> run Year2016Day03.solver 13 | | 4 -> run Year2016Day04.solver | 5 -> run Year2016Day05.solver | 6 -> run Year2016Day06.solver 14 | | 7 -> run Year2016Day07.solver | 8 -> run Year2016Day08.solver | 9 -> run Year2016Day09.solver 15 | | 10 -> run Year2016Day10.solver | 11 -> run Year2016Day11.solver | 12 -> run Year2016Day12.solver 16 | | 13 -> run Year2016Day13.solver | 14 -> run Year2016Day14.solver | 15 -> run Year2016Day15.solver 17 | | 16 -> run Year2016Day16.solver | 17 -> run Year2016Day17.solver | 18 -> run Year2016Day18.solver 18 | | 19 -> run Year2016Day19.solver | 20 -> run Year2016Day20.solver | 21 -> run Year2016Day21.solver 19 | | 22 -> run Year2016Day22.solver | 23 -> run Year2016Day23.solver | 24 -> run Year2016Day24.solver 20 | | 25 -> run Year2016Day25.solver 21 | | day -> fun _ -> printfn "Invalid Day: %i (Year %i)" day 2016 22 | 23 | type Bench2016() = 24 | inherit Bench() with 25 | override _.GetSolverFunc day part () = 26 | getSolver day part false () 27 | 28 | [] 29 | let main argv = 30 | let runPart day part = getSolver day part true () 31 | let runDay day = for part in 1..2 do runPart day part 32 | match argv.[0] with 33 | | "BENCH" -> Benchmarking.runBenchmarks() 34 | | "ALL" -> for day in 1..25 do runDay day 35 | | x -> 36 | let parts = x.Split('.') |> Array.map int 37 | match parts.Length with 38 | | 1 -> runDay parts.[0] 39 | | 2 -> runPart parts.[0] parts.[1] 40 | | _ -> () 41 | 0 42 | -------------------------------------------------------------------------------- /fsharp/2016/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "AdventOfCode.2016": { 4 | "commandName": "Project", 5 | "commandLineArgs": "ALL" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day01.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day01 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type Dir = Left | Right 6 | 7 | let parseDir (dir : string) = 8 | if dir.[0] = 'L' then 9 | Left, int dir.[1..] 10 | else 11 | Right, int dir.[1..] 12 | 13 | let parse = parseFirstLine (splitBy ", " (Array.map parseDir)) 14 | 15 | let changeDir dir (dx, dy, px, py) = 16 | let ndx, ndy = 17 | if dir = Left then 18 | (dy, -dx) 19 | else 20 | (-dy, dx) 21 | (ndx, ndy, px, py) 22 | 23 | let move dist (dx, dy, px, py) = 24 | (dx, dy, px+dx*dist, py+dy*dist) 25 | 26 | let goDir cur (dir, dist) = 27 | cur |> changeDir dir |> move dist 28 | 29 | let solvePart1 data = 30 | let _, _, x, y = Seq.fold goDir (0, -1, 0, 0) data 31 | abs x + abs y 32 | 33 | let solvePart2 data = 34 | let rec goDir' cur seen steps data = 35 | let _, _, px, py = cur 36 | if Set.contains (px, py) seen then 37 | abs px + abs py 38 | else 39 | if steps > 0 then 40 | goDir' (move 1 cur) (Set.add (px, py) seen) (steps - 1) data 41 | else 42 | match data with 43 | | (dir, dist) :: ds -> 44 | let newPos = changeDir dir cur 45 | goDir' newPos seen (dist) ds 46 | | [] -> abs px + abs py 47 | goDir' (0, -1, 0, 0) Set.empty 0 (data |> Array.toList) 48 | 49 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day02.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day02 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type Move = Up | Left | Down | Right 6 | let parseMove = function 7 | | 'L' -> Left 8 | | 'R' -> Right 9 | | 'U' -> Up 10 | | _ -> Down 11 | 12 | let asInstructions = Seq.map parseMove >> Seq.toArray 13 | 14 | let parse = parseEachLine asInstructions 15 | 16 | let changeIndex (x, y) move = 17 | match move with 18 | | Left -> (max 0 (x-1), y) 19 | | Right -> (min 2 (x + 1), y) 20 | | Down -> (x, (min 2 (y+1))) 21 | | Up -> (x, (max 0 (y-1))) 22 | 23 | let changeIndex2 (x, y) move = 24 | match move with 25 | | Left -> ((max (abs (2-y)) (x-1)), y) 26 | | Right -> (min (4-(abs (2-y))) (x + 1), y) 27 | | Down -> (x, (min (4-(abs (2-x))) (y+1))) 28 | | Up -> (x, (max (abs (2-x)) (y-1))) 29 | 30 | let code1 = [| 31 | [|'1'; '2'; '3'|] 32 | [|'4'; '5'; '6'|] 33 | [|'7'; '8'; '9'|] 34 | |] 35 | 36 | let code2 = [| 37 | [|'0'; '0'; '1'; '0'; '0' |] 38 | [|'0'; '2'; '3'; '4'; '0' |] 39 | [|'5'; '6'; '7'; '8'; '9' |] 40 | [|'0'; 'A'; 'B'; 'C'; '0' |] 41 | [|'0'; '0'; 'E'; '0'; '0' |] 42 | |] 43 | 44 | let solve changeI (code : char [] []) start data = 45 | let applyMoves sequence start = 46 | Array.fold changeI start sequence 47 | let apply (state, pass) seq = 48 | let x, y = applyMoves seq state 49 | (x, y), (pass + string code.[y].[x]) 50 | Seq.fold apply (start, "") data |> snd 51 | 52 | let solvePart1 = solve changeIndex code1 (1, 1) 53 | let solvePart2 = solve changeIndex2 code2 (0, 2) 54 | 55 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day03.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day03 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseEachLine extractInts 6 | 7 | let isValid sides = 8 | let sorted = sides |> Array.sort 9 | sorted.[0] + sorted.[1] > sorted.[2] 10 | 11 | let solvePart1 lines = 12 | lines |> Seq.filter (isValid) |> Seq.length 13 | 14 | let transpose (s : int [] seq) = 15 | let r = s |> Seq.toArray 16 | Seq.init 3 (fun i -> [| r.[0].[i]; r.[1].[i]; r.[2].[i] |]) 17 | 18 | let solvePart2 lines = 19 | lines 20 | |> Seq.chunkBySize 3 21 | |> Seq.map transpose 22 | |> Seq.concat 23 | |> Seq.filter (isValid) 24 | |> Seq.length 25 | 26 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day04.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day04 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type Room = {name: string; sector: int; check: string} 6 | 7 | let parseRoom (groups : string[]) = 8 | let name = groups.[0] |> Seq.filter (fun l -> l <> '-') |> Seq.map string |> String.concat "" 9 | let sector = int groups.[1] 10 | let check = groups.[2] 11 | {name=name; sector=sector; check=check} 12 | 13 | let parse = parseEachLine (withRegex "(.*)-(\d+)\[(.*)\]") >> Seq.map parseRoom 14 | 15 | let rot n str = 16 | let toInt (c : char) = (int c - int 'a') 17 | let toChar (i : int) = char (i + int 'a') 18 | str |> Seq.map (toInt >> (fun i -> (i + n) % 26) >> toChar >> string) |> String.concat "" 19 | 20 | let getCheck name = 21 | let getCheck' = 22 | Seq.countBy id 23 | >> Seq.sortBy (fun (k, b) -> (-b, k)) 24 | >> Seq.take 5 25 | >> Seq.map (fun (k, _) -> string k) 26 | >> String.concat "" 27 | getCheck' name 28 | 29 | let solvePart1 lines = 30 | lines 31 | |> Seq.filter (fun s -> s.check = (getCheck s.name)) 32 | |> Seq.sumBy (fun s -> s.sector) 33 | 34 | let solvePart2 lines = 35 | let line = lines |> Seq.find (fun line -> (rot line.sector line.name).Contains("north")) 36 | line.sector 37 | 38 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day05.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day05 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System.Security.Cryptography 5 | 6 | let parse = parseFirstLine asString 7 | let md5Obj = MD5.Create() 8 | 9 | let asHexDigit num = 10 | match num with 11 | | num when num < 10uy -> '0' + char num 12 | | _ -> 'a' + char (num - 10uy) 13 | 14 | let md5StartsWith5Zeroes (msg : string) : bool * char * char = 15 | let hash = 16 | msg 17 | |> System.Text.Encoding.ASCII.GetBytes 18 | |> md5Obj.ComputeHash 19 | let isValid = hash.[0] = 0uy && hash.[1] = 0uy && (hash.[2] &&& 0xF0uy) = 0uy 20 | isValid, asHexDigit (hash.[2] &&& 0x0Fuy), asHexDigit (hash.[3] >>> 4) 21 | 22 | let hashStartsWith5Zeroes salt number = salt + (string number) |> md5StartsWith5Zeroes 23 | 24 | let solvePart1 doorId = 25 | Seq.initInfinite (hashStartsWith5Zeroes doorId) 26 | |> Seq.filter (fun (isValid, _, _) -> isValid) 27 | |> Seq.take 8 28 | |> Seq.map (fun (_, d, _) -> d) 29 | |> charsToStr 30 | 31 | let isValidIndex idx = '0' <= idx && idx < '8' 32 | 33 | let solvePart2 doorId = 34 | let rec searchNext count seen mapping = 35 | let isValidHash, index, c = hashStartsWith5Zeroes doorId count 36 | if isValidHash && isValidIndex index && not (Set.contains index seen) then 37 | let newSeen = Set.add index seen 38 | let newMap = Map.add index c mapping 39 | if Set.count newSeen = 8 then 40 | newMap 41 | else 42 | searchNext (count + 1) newSeen newMap 43 | else 44 | searchNext (count + 1) seen mapping 45 | let positions = searchNext 0 Set.empty Map.empty |> Map.toArray 46 | let values = positions |> Array.map snd 47 | let inds = positions |> Array.map (fun (i, _) -> i |> string |> int) 48 | Array.permute (fun i -> inds.[i]) values |> charsToStr 49 | 50 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day06.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day06 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseEachLine asString 6 | 7 | let toInds = Seq.mapi (fun i v -> (i, v)) 8 | 9 | let getMostCommon isDescending = 10 | let sortFn = if isDescending then Seq.sortByDescending else Seq.sortBy 11 | Seq.groupBy id 12 | >> sortFn (fun (k1, v1) -> Seq.length v1) 13 | >> Seq.head 14 | >> fst 15 | 16 | let solve isDescending = 17 | Seq.map toInds 18 | >> Seq.concat 19 | >> Seq.groupBy fst 20 | >> Seq.map (snd >> getMostCommon isDescending) 21 | >> Seq.sortBy fst 22 | >> Seq.map snd 23 | >> charsToStr 24 | 25 | let solvePart1 = solve true 26 | let solvePart2 = solve false 27 | 28 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day08.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day08 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let mo a b = ((a % b) + b) % b 6 | 7 | type RC = Row | Column 8 | type SeqType = Rect of int * int | Rotate of RC * int * int 9 | 10 | let parseRect (parts : string[]) = 11 | let dims = splitBy "x" asIntArray parts.[1] 12 | let w = dims.[0] 13 | let h = dims.[1] 14 | Rect (w, h) 15 | 16 | let parseRotate (parts : string[]) = 17 | let t = if parts.[1] = "row" then Row else Column 18 | let v = (extractInts parts.[2]).[0] 19 | let by = int parts.[4] |> string |> int 20 | Rotate (t, v, by) 21 | 22 | let parseDir line = 23 | let parts = splitBy " " asStringArray line 24 | if parts.[0] = "rect" then 25 | parseRect parts 26 | else 27 | parseRotate parts 28 | 29 | let parse = parseEachLine parseDir 30 | 31 | let strip = Array2D.zeroCreate 6 50 32 | 33 | let applyDir grid = 34 | let W = Array2D.length2 grid 35 | let H = Array2D.length1 grid 36 | function 37 | | Rect (w, h) -> 38 | Array2D.mapi (fun y x v -> if y < h && x < w then 1 else v) grid 39 | | Rotate (typ, idx, by) -> 40 | let getValue y x v = 41 | if typ = Column && x = idx then 42 | grid.[mo (y - by) 6, x] 43 | elif typ = Row && y = idx then 44 | grid.[y, mo (x - by) 50] 45 | else 46 | v 47 | Array2D.mapi getValue grid 48 | 49 | let solvePart1 lines = 50 | let grid = Seq.fold applyDir strip lines 51 | grid |> Seq.cast |> Seq.sum 52 | 53 | let solvePart2 lines = 54 | let grid = Seq.fold applyDir strip lines 55 | seq { 56 | for row in 0..5 do 57 | '\n' 58 | for col in 0..49 do 59 | (if grid.[row, col] = 1 then '#' else ' ') } 60 | |> charsToStr 61 | 62 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day09.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day09 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System 5 | 6 | let parse = parseFirstLine asString 7 | 8 | let parseGroup (line : char []) i = 9 | let rb = Array.IndexOf(line.[i..], ')') + i 10 | let sub = new String(line.[i+1..rb-1]) 11 | let dims = splitBy "x" asIntArray sub 12 | let w, h = dims.[0], dims.[1] 13 | let offset = w * h 14 | rb + (w + 1), offset 15 | 16 | let solvePart1 (lines : string) = 17 | let charArr = lines |> Seq.toArray 18 | let rec parseLine i count = 19 | if i >= charArr.Length then 20 | count 21 | else 22 | let l = charArr.[i] 23 | if l = '(' then 24 | let newI, newCount = parseGroup charArr i 25 | parseLine newI (count + newCount) 26 | else 27 | parseLine (i + 1) (count + 1) 28 | parseLine 0 0 29 | 30 | let rec parseGroup2 (line : char []) i = 31 | let rb = Array.IndexOf(line.[i..], ')') + i 32 | let sub = new String(line.[i+1..rb-1]) 33 | let dims = splitBy "x" asIntArray sub 34 | let w, h = dims.[0], int64 dims.[1] 35 | 36 | let rec getCoveredArea ni total = 37 | if ni >= (rb + w + 1) then 38 | total 39 | else 40 | if line.[ni] = '(' then 41 | let newI, subCount, _ = parseGroup2 line ni 42 | getCoveredArea newI (total + subCount) 43 | else 44 | getCoveredArea (ni + 1) (total + 1L) 45 | let totalArea = getCoveredArea (rb + 1) 0L 46 | rb + (w + 1), totalArea * h, false 47 | 48 | let solvePart2 lines = 49 | let charArr = lines |> Seq.toArray 50 | let rec parseLine i count isData = 51 | if i >= charArr.Length then 52 | count 53 | else 54 | let l = charArr.[i] 55 | if l = '(' && not isData then 56 | let newI, newCount, isData = parseGroup2 charArr i 57 | parseLine newI (count + newCount) isData 58 | else 59 | parseLine (i + 1) (count + 1L) false 60 | parseLine 0 0L false 61 | 62 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day13.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day13 2 | 3 | open AdventOfCode.FSharp.Common 4 | open GraphFS.Core.VertexSet 5 | open GraphFS.Core.EdgeSet 6 | open System.Numerics 7 | open GraphFS.Graph 8 | open GraphFS.Algorithms.ShortestPath 9 | 10 | let parse = parseFirstLine asInt >> uint64 11 | 12 | let isOpenSpace input (x, y) = 13 | let z = x*x + 3UL*x + 2UL*x*y + y + y*y 14 | let withInput = z + input 15 | let bits = BitOperations.PopCount withInput 16 | bits % 2 = 0 17 | 18 | let manhattan (p1 : uint64 * uint64) (p2 : uint64 * uint64) = 19 | let (x1, y1), (x2, y2) = p1, p2 20 | let xDiff, yDiff = int64 x2 - int64 x1, int64 y2 - int64 y1 21 | abs xDiff + abs yDiff |> int 22 | 23 | let getGraph input = 24 | let vertexSet = { new IVertexSet with member __.HasVert v = isOpenSpace input v } 25 | 26 | let edgeSet = 27 | { new IEdgeSet with 28 | member __.HasEdge edge = 29 | let p1, p2 = edge 30 | manhattan p1 p2 = 1 31 | member __.Neighbours p = 32 | let x, y = p 33 | seq { 34 | if x <> 0UL then (x-1UL, y) 35 | if y <> 0UL then (x, y-1UL) 36 | (x+1UL, y) 37 | (x, y+1UL) } 38 | |> Seq.filter (isOpenSpace input) } 39 | 40 | Graph.fromSets vertexSet edgeSet 41 | 42 | let solvePart1 input = 43 | let graph = getGraph input 44 | let shortestPath = astar (1UL, 1UL) (31UL, 39UL) manhattan graph 45 | shortestPath.Value 46 | 47 | let solvePart2 input = 48 | let graph = getGraph input 49 | let source = (1UL, 1UL) 50 | 51 | let rec bfs steps seen fringe = 52 | if Set.isEmpty fringe || steps < 0 then seen 53 | else 54 | let seen = Set.union seen fringe 55 | let fringe' = 56 | fringe 57 | |> Seq.collect (fun vertex -> Graph.neighbours vertex graph) 58 | |> Set.ofSeq 59 | Set.difference fringe' seen 60 | |> bfs (steps - 1) seen 61 | 62 | (set [source] |> bfs 50 Set.empty).Count 63 | 64 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day15.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day15 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System 5 | 6 | type Disc = { Number: int; Positions: int; Time: int; Position: int } 7 | 8 | let asDisc line = 9 | let ints = extractInts line 10 | { Number = ints.[0]; Positions = ints.[1]; Time = ints.[2]; Position = ints.[3] } 11 | 12 | let parse = parseEachLine asDisc >> Seq.toArray 13 | 14 | let toRemainders discs = 15 | discs 16 | |> Array.map (fun d -> {d with Position = (d.Position + d.Number) % d.Positions}) 17 | |> Array.map (fun d -> ((d.Positions - d.Position) % d.Positions, d.Positions)) 18 | 19 | let chineseRemainderTheorem remainders = 20 | let sorted = remainders |> Array.sortByDescending snd |> Array.toList 21 | let rec sieve remainders s = 22 | match remainders with 23 | | [] -> s |> Seq.tryHead 24 | | (a, n) :: xs -> s |> Seq.filter (fun x -> a = x % n) |> sieve xs 25 | 26 | let maxValue = Seq.map snd sorted |> Seq.reduce (*) 27 | sieve sorted [0 .. (maxValue - 1)] 28 | 29 | let solvePart1 discs = 30 | let remainders = toRemainders discs 31 | (remainders |> chineseRemainderTheorem).Value 32 | 33 | let solvePart2 discs = 34 | let newDiscs = Array.append discs [| {Number = discs.Length + 1; Positions = 11; Time = 0; Position = 0 }|] 35 | solvePart1 newDiscs 36 | 37 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day16.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day16 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseFirstLine asString 6 | 7 | let expand (str : string) = 8 | let a = str |> Seq.toArray 9 | let b = a |> Array.rev |> Array.map (function | '0' -> '1' | _ -> '0') 10 | let newBytes = Array.concat [a; [| '0' |]; b] 11 | newBytes |> charsToStr 12 | 13 | let rec expandFully length (str : string) = 14 | if str.Length >= length then str.Substring(0, length) 15 | else expandFully length (expand str) 16 | 17 | let rec checksum (str : string) = 18 | if str.Length % 2 = 0 then 19 | str 20 | |> Seq.chunkBySize 2 21 | |> Seq.map (fun i -> if i.[0] = i.[1] then '1' else '0') 22 | |> charsToStr 23 | |> checksum 24 | else str 25 | 26 | let solve length input = 27 | expandFully length input 28 | |> checksum 29 | 30 | let solvePart1 input = solve 272 input 31 | 32 | let solvePart2 input = solve 35651584 input 33 | 34 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day18.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day18 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseFirstLine asString >> Seq.map (function | '^' -> true | _ -> false) >> Seq.toArray 6 | 7 | let isTrap l c r = 8 | (l && c && (not r)) || 9 | (c && r && (not l)) || 10 | (l && (not c) && (not r)) || 11 | (r && (not c) && (not l)) 12 | 13 | let nextState (row : bool []) = 14 | let nextRow = 15 | row 16 | |> Array.windowed 3 17 | |> Array.map (fun w -> isTrap w.[0] w.[1] w.[2]) 18 | Array.concat [| [| row.[1] |]; nextRow; [| row.[row.Length - 2] |] |] 19 | 20 | let solve input rows = 21 | Seq.init rows id 22 | |> Seq.mapFold (fun row _ -> row, nextState row) input 23 | |> fst 24 | |> Seq.collect id 25 | |> Seq.where not 26 | |> Seq.length 27 | 28 | let solvePart1 input = solve input 40 29 | let solvePart2 input = solve input 400000 30 | 31 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day19.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day19 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseFirstLine asInt 6 | 7 | let josephus n = 8 | let rec josephus' n i acc = 9 | if (i > n) then acc + 1 10 | else josephus' n (i + 1) ((acc + 2) % i) 11 | josephus' n 1 0 12 | 13 | let solvePart1 elves = josephus elves 14 | 15 | let solvePart2 elves = 16 | // round down to nearest power of 3 17 | let p = pown 3 (System.Math.Log(float elves, 3.) |> int) 18 | elves - p + (max (elves - 2 * p) 0) 19 | 20 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day20.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day20 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseEachLine (splitBy "-" (fun i -> int64 i.[0], int64 i.[1])) >> Seq.toArray 6 | 7 | let getValidIpRanges lines = 8 | let joins = lines |> Array.map (fun (s, _) -> (s, 1)) 9 | let leaves = lines |> Array.map (fun (_, e) -> (e, -1)) 10 | 11 | let together = 12 | Array.append joins leaves 13 | |> Array.sort 14 | 15 | let step count (value, diff) = 16 | let newCount = count + diff 17 | (value, newCount), newCount 18 | 19 | let temps, _ = Array.mapFold step 0 together 20 | 21 | temps 22 | |> Seq.pairwise 23 | |> Seq.where (fun ((v1, c1), (v2, c2)) -> c1 = 0 && c2 = 1 && v2 - v1 > 1L) 24 | |> Seq.map (fun ((v1, _), (v2, _)) -> (v1 + 1L, v2 - 1L)) 25 | 26 | let solvePart1 lines = getValidIpRanges lines |> Seq.head |> fst 27 | let solvePart2 lines = getValidIpRanges lines |> Seq.sumBy (fun (s, e) -> e - s + 1L) 28 | 29 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2016/Solutions/Day25.fs: -------------------------------------------------------------------------------- 1 | module Year2016Day25 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseEachLine asString >> Seq.toArray 6 | 7 | let solvePart1 (instrs : string []) = 8 | let val1 = (instrs.[1].Split " ").[1] |> int 9 | let val2 = (instrs.[2].Split " ").[1] |> int 10 | let minVal = val1 * val2 11 | let rec findVal val' = 12 | if val' > minVal then val' - minVal 13 | else findVal ((val' <<< 2) + 2) 14 | findVal 2 15 | 16 | let solver = { parse = parse; part1 = solvePart1; part2 = (fun _ -> "Advent of Code Finished!") } -------------------------------------------------------------------------------- /fsharp/2017/AdventOfCode.FSharp.2017.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /fsharp/2017/Program.fs: -------------------------------------------------------------------------------- 1 | namespace AdventOfCode.FSharp.Y2017 2 | 3 | open AdventOfCode.FSharp 4 | open AdventOfCode.FSharp.Common 5 | open AdventOfCode.FSharp.Benchmarking 6 | 7 | module Program = 8 | let getSolver day part printAnswer = 9 | let run (solver : Day<_, _, _>) = 10 | Runner.run printAnswer 2017 day part solver 11 | match day with 12 | | 1 -> run Year2017Day01.solver | 2 -> run Year2017Day02.solver | 3 -> run Year2017Day03.solver 13 | | 4 -> run Year2017Day04.solver | 5 -> run Year2017Day05.solver | 6 -> run Year2017Day06.solver 14 | | 7 -> run Year2017Day07.solver | 8 -> run Year2017Day08.solver | 9 -> run Year2017Day09.solver 15 | | 10 -> run Year2017Day10.solver | 11 -> run Year2017Day11.solver | 12 -> run Year2017Day12.solver 16 | | 13 -> run Year2017Day13.solver | 14 -> run Year2017Day14.solver | 15 -> run Year2017Day15.solver 17 | | 16 -> run Year2017Day16.solver | 17 -> run Year2017Day17.solver | 18 -> run Year2017Day18.solver 18 | | 19 -> run Year2017Day19.solver | 20 -> run Year2017Day20.solver | 21 -> run Year2017Day21.solver 19 | | 22 -> run Year2017Day22.solver | 23 -> run Year2017Day23.solver | 24 -> run Year2017Day24.solver 20 | | 25 -> run Year2017Day25.solver 21 | | day -> fun _ -> printfn "Invalid Day: %i (Year %i)" day 2017 22 | 23 | type Bench2017() = 24 | inherit Bench() with 25 | override _.GetSolverFunc day part () = 26 | getSolver day part false () 27 | 28 | [] 29 | let main argv = 30 | let runPart day part = getSolver day part true () 31 | let runDay day = for part in 1..2 do runPart day part 32 | match argv.[0] with 33 | | "BENCH" -> Benchmarking.runBenchmarks() 34 | | "ALL" -> for day in 1..25 do runDay day 35 | | x -> 36 | let parts = x.Split('.') |> Array.map int 37 | match parts.Length with 38 | | 1 -> runDay parts.[0] 39 | | 2 -> runPart parts.[0] parts.[1] 40 | | _ -> () 41 | 0 42 | -------------------------------------------------------------------------------- /fsharp/2017/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "AdventOfCode.2017": { 4 | "commandName": "Project", 5 | "commandLineArgs": "ALL" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day01.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day01 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let charToDigit c = int c - int '0' 6 | let solve windowSize captcha = 7 | Seq.append captcha captcha 8 | |> Seq.windowed windowSize 9 | |> Seq.take (Seq.length captcha) 10 | |> Seq.filter (fun w -> Seq.head w = Seq.last w) 11 | |> Seq.sumBy (Seq.head >> charToDigit) 12 | 13 | let part2 captcha = solve ((Seq.length captcha / 2) + 1) captcha 14 | let solver = { parse = parseFirstLine asString; part1 = solve 2; part2 = part2 } -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day02.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day02 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System.Linq 5 | 6 | let getLargestDiff ints = (Seq.max ints) - (Seq.min ints) 7 | let isValidDivisor ints i = (Seq.map ((*) i) ints).Intersect(ints).Any() 8 | let getDivisor ints = [2 .. (Seq.max ints)] |> Seq.find (isValidDivisor ints) 9 | 10 | let part1 = Seq.sumBy getLargestDiff 11 | let part2 = Seq.sumBy getDivisor 12 | let solver = {parse = parseEachLine (splitBy "\t" asIntArray); part1 = part1; part2 = part2} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day03.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day03 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let manhattanDistance target = 6 | let ringNumber = target |> float |> sqrt |> ceil |> int |> (fun r -> r / 2) 7 | let ringEnd = ringNumber * 2 |> (fun r -> pown r 2) |> (+) 1 8 | [1 ; 3 ; 5 ; 7] 9 | |> Seq.map (fun i -> abs (target - (ringEnd - i * ringNumber)) + ringNumber) 10 | |> Seq.min 11 | 12 | let getNextValue grid posMap (newX, newY) = 13 | if Map.isEmpty posMap then 1 14 | else 15 | [(-1, -1); (-1, 0); (-1, 1); (0, -1); (0, 1); (1, -1); (1, 0); (1, 1)] 16 | |> Seq.map (fun (x, y) -> (newX + x, newY + y)) 17 | |> Seq.filter (fun pos -> Map.containsKey pos posMap) 18 | |> Seq.sumBy ((fun pos -> Map.find pos posMap) >> (fun pos -> List.item pos grid)) 19 | 20 | // this somehow manages to calculate the next x and y position given the current position in the spiral. I forget how it works. 21 | let getNextPos (x, y) = 22 | if (y <= 0) && (x <= -y) && (y <= x) then (x + 1, y) 23 | elif (x > 0) && (y < x) then (x, y + 1) 24 | elif (y > 0) && (-x < y) then (x - 1, y) 25 | else (x, y - 1) 26 | 27 | let part2 target = 28 | let rec solve grid posMap newPos = 29 | let newValue = getNextValue grid posMap newPos 30 | if newValue > target then newValue 31 | else solve (grid @ [ newValue ]) (Map.add newPos grid.Length posMap) (getNextPos newPos) 32 | solve List.empty Map.empty (0, 0) 33 | 34 | let solver = {parse = parseFirstLine asInt; part1 = manhattanDistance; part2 = part2} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day04.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day04 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System 5 | 6 | let isUnique lst = 7 | let rec isUnique' seen = function 8 | | [] -> true 9 | | x :: xs -> if Set.contains x seen then false else isUnique' (Set.add x seen) xs 10 | isUnique' Set.empty lst 11 | let sortedString (str : string) = str |> Seq.sort |> String.Concat 12 | let solve mapper = Seq.map mapper >> Seq.filter isUnique >> Seq.length 13 | let solver = {parse = parseEachLine (splitBy " " asStringArray >> Array.toList); part1 = solve id; part2 = solve (List.map sortedString)} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day05.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day05 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let solve modifyOffset offsets = 6 | // I took inspiration from https://github.com/mstksg/advent-of-code-2017/blob/master/src/AOC2017/Day05.hs and used a zipper 7 | let rec solve' total ls x rs n = 8 | if n = 0 then solve' (total + 1) ls (modifyOffset x) rs x 9 | elif n < 0 then match ls with | l :: ls' -> solve' total ls' l (x :: rs) (n + 1) | [] -> total 10 | else match rs with | r :: rs' -> solve' total (x :: ls) r rs' (n - 1) | [] -> total 11 | solve' 0 [] (Seq.head offsets) (Seq.tail offsets |> Seq.toList) 0 12 | 13 | let modifyPart2 x = if x >= 3 then (x - 1) else (x + 1) 14 | let solver = {parse = parseEachLine asInt; part1 = solve ((+) 1); part2 = solve modifyPart2} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day06.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day06 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let banksToStr = Array.map (fun i -> i.ToString()) >> String.concat "," 6 | 7 | let distribute numBanks maxV maxI i v = 8 | let doesOverlap = ((if i <= maxI then numBanks else 0) + i) <= (maxI + (maxV % numBanks)) 9 | (if i = maxI then 0 else v) + (maxV / numBanks) + (if doesOverlap then 1 else 0) 10 | 11 | let solve = 12 | let rec solve' seen c banks = 13 | if (Map.containsKey (banksToStr banks) seen) then (seen, banks) 14 | else 15 | let maxV = Array.max banks 16 | let maxI = Array.findIndex ((=) maxV) banks 17 | solve' (Map.add (banksToStr banks) c seen) (c + 1) (Array.mapi (distribute (Seq.length banks) maxV maxI) banks) 18 | solve' Map.empty 0 19 | 20 | let part1 = solve >> fst >> Map.count 21 | let part2 = solve >> (fun (seen, banks) -> (Map.count seen) - (Map.find (banksToStr banks) seen)) 22 | let solver = {parse = parseFirstLine (splitBy "\t" asIntArray); part1 = part1; part2 = part2} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day07.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day07 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let getSubTowers (tokens : string array) = if Array.length tokens = 2 then Array.empty else tokens.[3..] |> Array.map (fun c -> c.TrimEnd(',')) 6 | let asProgram = splitBy " " (fun tokens -> (tokens.[0], (tokens.[1].Trim('(',')') |> int, getSubTowers tokens))) 7 | 8 | let rec findRoot tower currentProgram = 9 | match Map.tryFindKey (fun _ (_, children) -> Array.contains currentProgram children) tower with 10 | | None -> currentProgram 11 | | Some parent -> findRoot tower parent 12 | 13 | let rec getWeight tower node = 14 | let weight, children = Map.find node tower 15 | weight + (Array.sumBy (getWeight tower) children) 16 | 17 | let getChildrenWeights tower = 18 | Seq.map (fun c -> (getWeight tower c, Map.find c tower |> fst)) 19 | >> Seq.groupBy fst 20 | >> Seq.sortByDescending (fun (k, g) -> Seq.length g) 21 | >> Seq.toArray 22 | 23 | let getMissingWeight tower = 24 | tower 25 | |> Map.map (fun _ (_, children) -> getChildrenWeights tower children) 26 | |> Map.toSeq 27 | |> Seq.filter (fun (_, v) -> (Array.length v) = 2) 28 | |> Seq.map (fun (_, v) -> (snd v.[1] |> Seq.head |> snd) + (fst v.[0]) - (fst v.[1])) 29 | |> Seq.min 30 | 31 | let part1 tower = 32 | let head = Seq.head tower 33 | findRoot tower head.Key 34 | 35 | let solver = {parse = parseEachLine asProgram >> Map.ofSeq; part1 = part1; part2 = getMissingWeight} 36 | -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day08.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day08 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parseIncOrDec = function 6 | | "inc" -> (+) 7 | | "dec" -> (-) 8 | | _ -> (fun _ x -> x) 9 | 10 | let parseOperator = function 11 | | ">" -> (>) 12 | | "<" -> (<) 13 | | ">=" -> (>=) 14 | | "<=" -> (<=) 15 | | "==" -> (=) 16 | | "!=" -> (<>) 17 | | _ -> (fun _ _ -> false) 18 | 19 | let asInstruction line = 20 | let parts = splitBy " " asStringArray line 21 | let incOrDecFunc = fun x -> (parseIncOrDec parts.[1]) x (int parts.[2]) 22 | let operatorFunc = fun x -> (parseOperator parts.[5]) x (int parts.[6]) 23 | parts.[0], incOrDecFunc, parts.[4], operatorFunc 24 | 25 | let getOrDefault key map ``default``= 26 | match Map.tryFind key map with 27 | | Some v -> v 28 | | None -> ``default`` 29 | 30 | let simulate (var1, incOrDec, var2, passesComparisonCheck) vars = 31 | let val1, val2 = getOrDefault var1 vars 0, getOrDefault var2 vars 0 32 | if passesComparisonCheck val2 then Map.add var1 (incOrDec val1) vars else vars 33 | let maxVar = Map.toSeq >> Seq.map snd >> Seq.max 34 | 35 | let solve = Seq.mapFold (fun vars insn -> simulate insn vars |> (fun v -> (maxVar v, v))) Map.empty >> fst 36 | let solver = {parse = parseEachLine asInstruction; part1 = solve >> Seq.last; part2 = solve >> Seq.max} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day09.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day09 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type GarbageState = NotGarbage | Garbage | Cancelled 6 | type State = {level: int; state: GarbageState; score: int; garbage: int } 7 | 8 | let step current nextChar = 9 | match (current.state, nextChar) with 10 | | (Garbage, '!') -> {current with state = Cancelled} 11 | | (Garbage, '>') -> {current with state = NotGarbage} 12 | | (Garbage, _) -> {current with garbage = current.garbage + 1} 13 | | (Cancelled, _) -> {current with state = Garbage} 14 | | (NotGarbage, '{') -> {current with level = current.level + 1} 15 | | (NotGarbage, '}') -> {current with level = current.level - 1; score = current.score + current.level} 16 | | (NotGarbage, '<') -> {current with state = Garbage} 17 | | _ -> current; 18 | 19 | let solve = Seq.fold step {level=0; state=NotGarbage; score=0; garbage=0} 20 | let solver = {parse = parseFirstLine asString; part1 = solve >> (fun state -> state.score); part2 = solve >> (fun state -> state.garbage)} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day10.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day10 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let repeat n folder init = Seq.fold (fun s _ -> folder s) init [0..(n-1)] 6 | 7 | let (%!) a b = (a % b + b) % b 8 | 9 | type State = {hash: int array; skip: int; start: int} 10 | let revN n s = Array.append (Array.take n s |> Array.rev) (Array.skip n s) 11 | let shift n s = Array.append (Array.skip n s) (Array.take n s) 12 | let step listLen state length = 13 | let shiftLen = ((length + state.skip) % listLen) 14 | let newHash = state.hash |> revN length |> shift shiftLen 15 | {state with hash = newHash; skip = state.skip + 1; start = (state.start - shiftLen) %! listLen} 16 | 17 | let solveRound listLen init = Array.fold (step listLen) init 18 | let sparseHash listLen n lengths = 19 | repeat n (fun i -> solveRound listLen i lengths) {hash=[|0..(listLen-1)|]; skip=0; start=0} 20 | |> (fun s -> shift s.start s.hash) 21 | let denseHash = Array.chunkBySize 16 >> Array.map (Array.fold (^^^) 0) 22 | 23 | let strToDenseHash = 24 | Seq.map int 25 | >> Seq.toArray 26 | >> (fun hash -> Array.append hash [|17;31;73;47;23|]) 27 | >> sparseHash 256 64 28 | >> denseHash 29 | 30 | let part1 = splitBy "," asIntArray >> sparseHash 256 1 >> (fun s -> s.[0] * s.[1]) 31 | let toHexStr = Array.fold (fun h i -> h + sprintf "%02x" i) "" 32 | let solver = {parse = parseFirstLine asString; part1 = part1; part2 = strToDenseHash >> toHexStr} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day11.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day11 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let dist (x, y) = (abs(x) + abs(y)) / 2 6 | let getDir = function | "n" -> (0, 2) | "s" -> (0, -2) | "ne" -> (1, 1) | "nw" -> (-1, 1) | "se" -> (1, -1) | "sw" -> (-1, -1) | _ -> (0, 0) 7 | let addDir (x1,y1) (x2,y2) = (x1+x2,y1+y2) 8 | let step coords = getDir >> addDir coords >> (fun c -> (dist c, c)) 9 | let solve = Array.mapFold step (0, 0) >> fst 10 | let solver = {parse = parseFirstLine (splitBy "," asStringArray); part1 = solve >> Array.last; part2 = solve >> Array.max} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day12.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day12 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let getConnectedComponent getVerts rootNode = 6 | let rec getConnectedComponent' comp = function 7 | | [] -> comp 8 | | x :: xs when Set.contains x comp -> getConnectedComponent' comp xs 9 | | x :: xs -> getConnectedComponent' (Set.add x comp) (getVerts x @ xs) 10 | getConnectedComponent' Set.empty [rootNode] 11 | 12 | let getConnectedComponents getVerts nodes = 13 | let rec getConnectedComponents' seen unseen components = 14 | if Set.isEmpty unseen then components 15 | else 16 | let newComp = getConnectedComponent getVerts (Seq.head unseen) 17 | getConnectedComponents' (Set.union seen newComp) (Set.difference unseen newComp) (newComp :: components) 18 | getConnectedComponents' Set.empty nodes List.empty 19 | 20 | let asConnections = splitBy ", " asIntArray >> Array.toList 21 | let asPipe = splitBy " <-> " (Array.item 1 >> asConnections) 22 | let part1 graph = getConnectedComponent (fun v -> List.item v graph) 0 |> Set.count 23 | let part2 graph = getConnectedComponents (fun v -> List.item v graph) (set [0..(List.length graph - 1)]) |> List.length 24 | let solver = {parse = parseEachLine asPipe >> Seq.toList; part1 = part1; part2 = part2} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day13.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day13 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let collides delay (layer, length) = 6 | (delay + layer) % (2 * (length - 1)) = 0 7 | 8 | let getScore = 9 | List.filter (collides 0) 10 | >> (List.sumBy (fun l -> fst l * snd l)) 11 | 12 | let rec findValid delay layers = 13 | if List.exists (collides delay) layers then 14 | findValid (delay + 1) layers 15 | else 16 | delay 17 | 18 | let asLayer = splitBy ": " (fun l -> (int l.[0], int l.[1])) 19 | 20 | let solver = { parse = parseEachLine asLayer >> Seq.toList; part1 = getScore; part2 = findValid 0 } -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day14.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day14 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System 5 | 6 | let toBinStr (i : int) = 7 | Convert.ToString(i, 2).PadLeft(8, '0') 8 | 9 | let getHash key i = 10 | Year2017Day10.strToDenseHash (sprintf "%s-%i" key i) 11 | |> Array.fold (fun h i -> h + toBinStr i) "" 12 | 13 | let hashToCoords i = 14 | Seq.mapi (fun j h -> ((i, j), h)) 15 | >> Seq.filter (snd >> ((=) '1')) 16 | >> Seq.map fst 17 | >> Set.ofSeq 18 | 19 | let getActiveCoords key = 20 | Seq.map (getHash key) [0..127] 21 | |> Seq.mapi hashToCoords 22 | |> Set.unionMany 23 | 24 | let getSurroundingNodes activeCoords (i, j) = 25 | [(i-1, j); (i+1, j); (i, j-1); (i, j+1)] 26 | |> List.filter (fun node -> Set.contains node activeCoords) 27 | 28 | let getConnectedComponent getVerts rootNode = 29 | let rec getConnectedComponent' comp = function 30 | | [] -> comp 31 | | x :: xs when Set.contains x comp -> getConnectedComponent' comp xs 32 | | x :: xs -> getConnectedComponent' (Set.add x comp) (getVerts x @ xs) 33 | getConnectedComponent' Set.empty [rootNode] 34 | 35 | let getConnectedComponents getVerts nodes = 36 | let rec getConnectedComponents' seen unseen components = 37 | if Set.isEmpty unseen then components 38 | else 39 | let newComp = getConnectedComponent getVerts (Seq.head unseen) 40 | getConnectedComponents' (Set.union seen newComp) (Set.difference unseen newComp) (newComp :: components) 41 | getConnectedComponents' Set.empty nodes List.empty 42 | 43 | let part2 = 44 | getActiveCoords 45 | >> (fun coords -> getConnectedComponents (getSurroundingNodes coords) coords) 46 | 47 | let solver = {parse = parseFirstLine asString; part1 = getActiveCoords >> Set.count; part2 = part2 >> List.length} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day15.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day15 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let lcg g x = g * x % 2147483647UL 6 | let rec lcg2 mask g x = match lcg g x with | next when (next &&& mask = 0UL) -> next | next -> lcg2 mask g next 7 | 8 | let asSeed = splitBy "with " (Array.item 1 >> uint64) 9 | let solve genA genB iterations seeds = 10 | let seedA, seedB = Seq.head seeds, Seq.tail seeds |> Seq.head 11 | let rec solve' a b count = function 12 | | 0 -> count 13 | | n -> 14 | let a, b = genA 16807UL a, genB 48271UL b 15 | let i = if (a &&& 0xFFFFUL) = (b &&& 0xFFFFUL) then 1 else 0 16 | solve' a b (count + i) (n - 1) 17 | solve' seedA seedB 0 iterations 18 | let solver = {parse = parseEachLine asSeed; part1 = solve lcg lcg 40_000_000; part2 = solve (lcg2 3UL) (lcg2 7UL) 5_000_000} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day16.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day16 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System 5 | 6 | type DanceMove = Spin of int | Exchange of int * int | Partner of int * int 7 | 8 | let asMove (move : string) = 9 | match move.[0] with 10 | | 's' -> Spin (move.[1..] |> int) 11 | | 'x' -> Exchange (move.[1..] |> splitBy "/" (fun x -> (int x.[0], int x.[1]))) 12 | | 'p' -> Partner (int move.[1] - int 'a', int move.[3] - int 'a') 13 | | _ -> Spin 0 14 | 15 | let swap (i, j) list = list |> List.mapi (fun k h -> if k = i then list.[j] elif k = j then list.[i] else h) 16 | let performMove order = function 17 | | Spin i -> (List.skip (16 - i) order) @ (List.take (16-i) order) 18 | | Exchange (a, b) -> swap (a, b) order 19 | | Partner (a, b) -> swap ((List.findIndex ((=) a) order), (List.findIndex ((=)b) order)) order 20 | 21 | let orderToStr = List.map ((+) (int 'a') >> char) >> String.Concat 22 | let performNDances n moves = 23 | let performDance order = Array.fold performMove order moves 24 | let rec performNDances' dances order = function 25 | | 0 -> orderToStr order 26 | | x when List.contains (orderToStr order) dances -> List.item (n % (n - x)) dances 27 | | x -> performNDances' (dances @ [orderToStr order]) (performDance order) (x - 1) 28 | performNDances' List.empty [0..15] n 29 | 30 | let solver = {parse = parseFirstLine (splitBy "," (Array.map asMove)); part1 = performNDances 1; part2 = performNDances 1_000_000_000} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day17.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day17 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let getInsertPositions i skip = List.fold (fun l n -> (((List.head l) + skip) % n + 1) :: l) [0] (List.init i ((+)1)) 6 | let rec findTarget target = function 7 | | [] -> 0 8 | | x :: xs when x = target -> List.length xs 9 | | x :: xs -> findTarget (target + if x < target then - 1 else 0) xs 10 | let part1 = getInsertPositions 2017 >> (fun pos -> findTarget ((List.head pos) + 1) pos) 11 | 12 | let rec part2 afterZero i n skip = 13 | if n = 50000001 then afterZero 14 | else (i + skip) % n |> (fun next -> part2 (if next = 0 then n else afterZero) (next + 1) (n + 1) skip) 15 | let solver = {parse = parseFirstLine asInt; part1 = part1; part2 = part2 0 0 1} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day19.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day19 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type State = {x: int; y: int; dx: int; dy: int; steps: int; letters: string} 6 | let step state = {state with x=state.x+state.dx; y=state.y+state.dy; steps=state.steps+1} 7 | let solve (diagram : string list) = 8 | let rec move state = 9 | match diagram.[state.y].[state.x], state.dx with 10 | | ' ', _ -> state 11 | | '+', 0 -> move ({state with dx = (if diagram.[state.y].[state.x-1] = ' ' then 1 else -1); dy = 0} |> step) 12 | | '+', _ -> move ({state with dy = (if diagram.[state.y-1].[state.x] = ' ' then 1 else -1); dx = 0} |> step) 13 | | '-', _ | '|', _ -> move (step state) 14 | | x, _ -> move ({state with letters = state.letters + x.ToString()} |> step) 15 | move {x=diagram.[0].IndexOf('|'); y=0; dx=0; dy=1; steps=0; letters=""} 16 | let solver = {parse = parseEachLine asString >> Seq.toList; part1 = solve >> (fun s -> s.letters); part2 = solve >> (fun s -> s.steps)} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day20.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day20 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let asVector (s : string) = splitBy "," (Array.map int64) s.[3..(s.Length-2)] |> (fun v -> (v.[0],v.[1],v.[2])) 6 | let asParticle = splitBy ", " (Array.map asVector >> (fun vecs -> (vecs.[0],vecs.[1],vecs.[2]))) 7 | 8 | let addVec (x1,y1,z1) (x2,y2,z2) = (x1 + x2, y1 + y2, z1 + z2) 9 | let tickParticle = (fun (p, v, a) -> (p, addVec v a, a)) >> (fun (p, v, a) -> (addVec p v, v, a)) 10 | let dist (x, y, z) = abs x + abs y + abs z 11 | 12 | let ticksToSimulate = 200 13 | let solve transformList = 14 | let tickAll = List.map (fun (pos, p) -> (pos, tickParticle p)) >> transformList 15 | let rec tick t particles = if t = ticksToSimulate then particles else tick (t + 1) (tickAll particles) 16 | List.mapi (fun i v -> i, v) >> tick 0 17 | 18 | let filterColliding = List.groupBy (fun (_, (p, _, _)) -> p) >> List.filter (fun (_, l) -> List.tail l = []) >> List.collect snd 19 | let part1 = solve id >> List.minBy (fun (_, (p, v, a)) -> (dist a, dist v, dist p)) >> fst 20 | let part2 = solve filterColliding >> List.length 21 | 22 | let solver = {parse = parseEachLine asParticle >> Seq.toList; part1 = part1; part2 = part2} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day21.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day21 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let maxIndex p = Array2D.length1 p - 1 6 | let flatten grid = seq {for x in [0..maxIndex grid] do 7 | for y in [0..maxIndex grid] do 8 | yield grid.[x, y]} 9 | let asRule = splitBy " => " (Array.map (splitBy "/" array2D) >> (fun arr -> (arr.[0], arr.[1]))) 10 | let gridToStr = flatten >> Seq.fold (fun str x -> str + x.ToString()) "" 11 | let genPerms (pattern, out) = 12 | let flip (p : 'a [,]) = Array2D.mapi (fun x y _ -> p.[x, maxIndex p - y]) p 13 | let rotate (p : 'a [,]) = Array2D.mapi (fun x y _ -> p.[maxIndex p - y, x]) p 14 | let rec gen p = seq { yield p; yield (flip p); yield! gen (rotate p)} 15 | gen pattern |> Seq.take 8 |> Seq.map (fun p -> (gridToStr p, out)) 16 | 17 | let iterate grid (rules : Map) = 18 | let currentGridSize = maxIndex grid + 1 19 | let subSize = if (currentGridSize % 2) = 0 then 2 else 3 20 | let size = currentGridSize / subSize 21 | let subgrids = seq {for x in [0..size-1] do 22 | for y in [0..size-1] do 23 | yield grid.[subSize*x .. subSize*(x+1)-1, subSize*y .. subSize*(y+1)-1]} 24 | let enhancedSubgrids = 25 | subgrids 26 | |> Seq.map gridToStr 27 | |> Seq.map (fun subGridStr -> Map.find subGridStr rules) 28 | |> Seq.toArray 29 | let subSize' = subSize + 1 30 | Array2D.init (subSize' * size) (subSize' * size) (fun x y -> enhancedSubgrids.[(x/subSize' * size) + (y/subSize')].[x%subSize',y%subSize']) 31 | 32 | let getActiveCount = flatten >> Seq.filter (fun c -> c = '#') >> Seq.length 33 | let solve iterations rules = 34 | let rec getIterations grid = seq { yield grid; yield! getIterations (iterate grid rules)} 35 | getIterations (array2D [".#.";"..#";"###"]) |> Seq.item iterations |> getActiveCount 36 | 37 | let solver = {parse = parseEachLine asRule >> Seq.collect genPerms >> Map.ofSeq; part1 = solve 5; part2 = solve 18} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day22.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day22 2 | 3 | open System.Collections.Generic 4 | open AdventOfCode.FSharp.Common 5 | 6 | // tuples are slow when used as a map/dictionary key for some reason, convert 7 | // the coord to a long instead. Value determined by n^2+n=Int64.MaxValue 8 | let toHash (x, y) = x + 3037000500L * y 9 | let toGridMap lines = 10 | // Map was too slow, we use a mutable Dictionary instead 11 | let grid = new Dictionary(); 12 | let center = (String.length (Seq.head lines)) / 2 13 | let defaultEntries = 14 | lines 15 | |> Seq.mapi (fun i r -> Seq.mapi (fun j c -> ((j-center, i-center), if c = '#' then 2 else 0)) r) 16 | |> Seq.collect id 17 | for (x, y), v in defaultEntries do 18 | grid.[toHash (int64 x, int64 y)] <- v 19 | grid 20 | 21 | let move (x, y) = function 0 -> (x, y - 1L) | 1 -> (x + 1L, y) | 2 -> (x, y + 1L) | 3 -> (x - 1L, y) | _ -> (x, y) 22 | let solve jump iterations (grid : Dictionary) = 23 | let rec step pos dir infected = function 24 | | 0 -> infected 25 | | n -> 26 | let node = grid.GetValueOrDefault(toHash pos, 0) 27 | let dir', node' = (dir + node + 3) % 4, (node + jump) % 4 28 | grid.[toHash pos] <- node' 29 | step (move pos dir') dir' (infected + if node' = 2 then 1 else 0) (n - 1) 30 | step (0L, 0L) 0 0 iterations 31 | 32 | let solver = {parse = parseEachLine asString >> toGridMap; part1 = solve 2 10000; part2 = solve 1 10000000} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day23.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day23 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let rec checkPrimes limit n d = if d = limit then true elif n % d = 0 then false else checkPrimes limit n (d + 1) 6 | let isPrime n = if n < 2 then false else checkPrimes (float n |> sqrt |> ceil |> int) n 2 7 | let parse = parseFirstLine (splitBy " " asStringArray >> Array.item 2 >> int) 8 | let part2 x = 9 | (x + 1000) * 100 10 | |> (fun n -> [n..17..n+17000]) 11 | |> List.filter (isPrime >> not) 12 | |> List.length 13 | let solver = {parse = parse; part1 = (fun n -> (n - 2) * (n - 2)); part2 = part2} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day24.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day24 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let asComponent = splitBy "/" asIntArray >> (fun a -> a.[0], a.[1]) 6 | let strength = List.sumBy (fun c -> fst c + snd c) 7 | let rec build bridge next components = 8 | seq { 9 | yield bridge 10 | // if we have a component which bridges to itself that can connect, then always use this. This cuts down a lot of branches in the DFS 11 | if Set.contains (next, next) components then yield! build ((next, next) :: bridge) next (Set.remove (next, next) components) 12 | else 13 | let bridgeable = Set.filter (fun c -> fst c = next || snd c = next) components 14 | for comp in bridgeable do 15 | let next' = if snd comp = next then fst comp else snd comp 16 | yield! build (comp :: bridge) next' (Set.remove comp components) } 17 | let solve maximiser = set >> build [] 0 >> Seq.maxBy maximiser >> strength 18 | let solver = {parse = parseEachLine asComponent; part1 = solve strength; part2 = solve (fun c -> (List.length c, strength c))} -------------------------------------------------------------------------------- /fsharp/2017/Solutions/Day25.fs: -------------------------------------------------------------------------------- 1 | module Year2017Day25 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System.IO 5 | 6 | // I frequently needed the last word without the last letter (unnecessary punctuation) 7 | let lastWord line = splitBy " " Array.last line |> (fun word -> word.Remove(word.Length - 1)) 8 | let parseInstruction (lines : string list) = 9 | let getAction i = (lastWord lines.[i] |> int, lastWord lines.[i + 1] = "left", lastWord lines.[i + 2]) 10 | (lastWord lines.[0], (getAction 2, getAction 6)) 11 | let parseInstructions lines = lines |> Seq.skip 3 |> Seq.chunkBySize 10 |> Seq.map (Seq.toList >> parseInstruction) |> Map.ofSeq 12 | let parseBlueprint lines = 13 | let initState = lastWord (Seq.head lines) 14 | let steps = Seq.item 1 lines |> splitBy " " (fun words -> int words.[5]) 15 | (initState, steps, parseInstructions lines) 16 | 17 | // more zipper stuff, for weird reasons I could not figure out how to abstract out the zipper logic without taking a performance hit 18 | let solve (initState, steps, instructions) = 19 | let rec step ls x rs state = function 20 | | 0 -> Seq.sum ls + x + Seq.sum rs 21 | | n -> 22 | let instruction = Map.find state instructions 23 | let (newValue, isLeft, newState) = if x = 0 then fst instruction else snd instruction 24 | let newLs, newX, newRs = 25 | if isLeft then match ls with l :: ls' -> (ls', l, newValue :: rs) | [] -> ([], 0, newValue :: rs) 26 | else match rs with r :: rs' -> (newValue :: ls, r, rs') | [] -> (newValue :: ls, 0, []) 27 | step newLs newX newRs newState (n - 1) 28 | step [] 0 [] initState steps 29 | 30 | let solver = {parse = id; part1 = File.ReadLines >> parseBlueprint >> solve; part2 = (fun _ -> "Advent of Code Finished!")} -------------------------------------------------------------------------------- /fsharp/2018/AdventOfCode.FSharp.2018.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /fsharp/2018/Program.fs: -------------------------------------------------------------------------------- 1 | namespace AdventOfCode.FSharp.Y2018 2 | 3 | open AdventOfCode.FSharp 4 | open AdventOfCode.FSharp.Common 5 | open AdventOfCode.FSharp.Benchmarking 6 | 7 | module Program = 8 | let getSolver day part printAnswer = 9 | let run (solver : Day<_, _, _>) = 10 | Runner.run printAnswer 2018 day part solver 11 | match day with 12 | | 1 -> run Year2018Day01.solver | 2 -> run Year2018Day02.solver | 3 -> run Year2018Day03.solver 13 | | 4 -> run Year2018Day04.solver | 5 -> run Year2018Day05.solver | 6 -> run Year2018Day06.solver 14 | | 7 -> run Year2018Day07.solver | 8 -> run Year2018Day08.solver | 9 -> run Year2018Day09.solver 15 | | 10 -> run Year2018Day10.solver | 11 -> run Year2018Day11.solver | 12 -> run Year2018Day12.solver 16 | | 13 -> run Year2018Day13.solver | 14 -> run Year2018Day14.solver | 15 -> run Year2018Day15.solver 17 | | 16 -> run Year2018Day16.solver | 17 -> run Year2018Day17.solver | 18 -> run Year2018Day18.solver 18 | | 19 -> run Year2018Day19.solver | 20 -> run Year2018Day20.solver | 21 -> run Year2018Day21.solver 19 | | 22 -> run Year2018Day22.solver | 23 -> run Year2018Day23.solver | 24 -> run Year2018Day24.solver 20 | | 25 -> run Year2018Day25.solver 21 | | day -> fun _ -> printfn "Invalid Day: %i (Year %i)" day 2018 22 | 23 | type Bench2018() = 24 | inherit Bench() with 25 | override _.GetSolverFunc day part () = 26 | getSolver day part false () 27 | 28 | [] 29 | let main argv = 30 | let runPart day part = getSolver day part true () 31 | let runDay day = for part in 1..2 do runPart day part 32 | match argv.[0] with 33 | | "BENCH" -> Benchmarking.runBenchmarks() 34 | | "ALL" -> for day in 1..25 do runDay day 35 | | x -> 36 | let parts = x.Split('.') |> Array.map int 37 | match parts.Length with 38 | | 1 -> runDay parts.[0] 39 | | 2 -> runPart parts.[0] parts.[1] 40 | | _ -> () 41 | 0 42 | -------------------------------------------------------------------------------- /fsharp/2018/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "AdventOfCode.2018": { 4 | "commandName": "Project", 5 | "commandLineArgs": "ALL" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /fsharp/2018/Solutions/Day01.fs: -------------------------------------------------------------------------------- 1 | module Year2018Day01 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let (%!) a b = (a % b + b) % b 6 | 7 | let solvePart2 changes = 8 | let shift = Seq.sum changes 9 | let getDiff ((xi, xf), (yi, yf)) = if shift > 0 then (yf - xf, xi, yf) else (yf - xf, yi, xf) 10 | Seq.toArray changes 11 | |> Array.scan (+) 0 12 | |> Array.take (Seq.length changes) 13 | |> Array.mapi (fun i v -> (i, v)) 14 | |> Array.groupBy (fun x -> (snd x) %! shift) 15 | |> Array.map(fun g -> snd g |> Array.sort |> Array.pairwise |> Array.map getDiff) 16 | |> Array.concat 17 | |> Array.min 18 | 19 | let solver = {parse = parseEachLine asInt; part1 = Seq.sum; part2 = solvePart2 >> (fun (_, _, f) -> f)} -------------------------------------------------------------------------------- /fsharp/2018/Solutions/Day02.fs: -------------------------------------------------------------------------------- 1 | module Year2018Day02 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System 5 | 6 | let solvePart1 gifts = 7 | let containsCount i = Seq.countBy id >> Seq.map snd >> Seq.contains i 8 | let countContains i = Seq.map (containsCount i) >> Seq.filter id >> Seq.length 9 | (countContains 2 gifts) * (countContains 3 gifts) 10 | 11 | let solvePart2 gifts = 12 | let replaceNth str n = String.mapi (fun i x -> if i = n then '_' else x) str 13 | let removeUnderscore s = Seq.filter ((<>)'_') s |> String.Concat 14 | let duplicate, _ = 15 | gifts 16 | |> Seq.map (fun str -> Seq.init (String.length str) (replaceNth str)) 17 | |> Seq.concat 18 | |> Seq.countBy id 19 | |> Seq.find (fun (_, s) -> s = 2) 20 | removeUnderscore duplicate 21 | 22 | let solver = {parse = parseEachLine asString; part1 = solvePart1; part2 = solvePart2} -------------------------------------------------------------------------------- /fsharp/2018/Solutions/Day05.fs: -------------------------------------------------------------------------------- 1 | module Year2018Day05 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let remainingPolymer = 6 | let processUnit polymer ch = 7 | match polymer with 8 | | x :: xs when abs (ch - x) = 32 -> xs 9 | | xs -> ch :: xs 10 | Seq.fold processUnit [] 11 | let filterChars str ch = Seq.filter (fun c -> c <> ch && (c - 32) <> ch) str 12 | let solvePart1 = Seq.map int >> remainingPolymer >> Seq.length 13 | let solvePart2 str = 14 | let reducedStr = str |> Seq.map int |> remainingPolymer 15 | Seq.init 26 ((+)65 >> filterChars reducedStr >> remainingPolymer >> Seq.length) |> Seq.min 16 | let solver = {parse = parseFirstLine asString; part1 = solvePart1; part2 = solvePart2} -------------------------------------------------------------------------------- /fsharp/2018/Solutions/Day08.fs: -------------------------------------------------------------------------------- 1 | module Year2018Day08 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let solve getValue = 6 | let rec getTree tree = 7 | let subChildren, metadata = List.item 0 tree, List.item 1 tree 8 | let subValues, tree' = List.mapFold (fun t _ -> getTree t) (List.skip 2 tree) [1..subChildren] 9 | let meta, remainingTree = List.splitAt metadata tree' 10 | getValue meta subValues, remainingTree 11 | Array.toList >> getTree >> fst 12 | 13 | let part1Value meta subValues = (List.sum meta) + (List.sum subValues) 14 | let part2Value meta subValues = 15 | let getSubtotal i = 16 | match List.tryItem (i - 1) subValues with 17 | | Some subtotal -> subtotal 18 | | None -> 0 19 | List.sumBy (if List.isEmpty subValues then id else getSubtotal) meta 20 | 21 | let solver = {parse = parseFirstLine (splitBy " " asIntArray); part1 = solve part1Value; part2 = solve part2Value} -------------------------------------------------------------------------------- /fsharp/2018/Solutions/Day11.fs: -------------------------------------------------------------------------------- 1 | module Year2018Day11 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let getPrefixSums serial size = 6 | let indexArray = Array.init size ((+)1) 7 | let getPowerLevel x y = 8 | let rackId = x + 10 9 | (((rackId * y + serial) * rackId / 100) % 10) - 5 10 | let getRow (prevRow : int []) row = 11 | let getPrefixSum prevSum x = 12 | let ownValue = getPowerLevel x row 13 | let overlap = if x > 0 then prevRow.[x - 1] else 0 14 | prevSum + ownValue + prevRow.[x] - overlap 15 | indexArray |> Array.scan getPrefixSum 0 16 | indexArray |> Array.scan getRow (Array.zeroCreate (size + 1)) |> array2D 17 | 18 | let getBestSumForSize size s (sums : int [,]) = 19 | let subGridSum x y = 20 | sums.[y + s, x + s] - sums.[y + s, x] - sums.[y, x + s] + sums.[y, x] 21 | seq { for y in 0..size-s do 22 | for x in 0..size-s do 23 | yield subGridSum x y, (x, y) } |> Seq.max 24 | 25 | let solvePart1 size subsize serial = 26 | let _, (x, y) = 27 | getPrefixSums serial size 28 | |> getBestSumForSize size subsize 29 | sprintf "%i,%i" (x + 1) (y + 1) 30 | 31 | let solvePart2 size serial = 32 | let sums = getPrefixSums serial size 33 | let (_, (x, y)), subsize = 34 | Array.init 300 ((+)1) // for each subsize 35 | |> Array.Parallel.map (fun i -> (getBestSumForSize size i sums, i)) 36 | |> Array.max 37 | sprintf "%i,%i,%i" (x + 1) (y + 1) subsize 38 | 39 | let solver = {parse = parseFirstLine asInt; part1 = solvePart1 300 3; part2 = solvePart2 300} -------------------------------------------------------------------------------- /fsharp/2018/Solutions/Day12.fs: -------------------------------------------------------------------------------- 1 | module Year2018Day12 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System.IO 5 | 6 | let parsePlants lines = 7 | let initState = lines |> Seq.head |> splitBy ": " (fun line -> line.[1]) 8 | let parseRule = splitBy " => " (fun r -> (r.[0], r.[1])) 9 | let rules = 10 | lines 11 | |> Seq.skip 2 12 | |> Seq.map parseRule 13 | |> Map.ofSeq 14 | initState, rules 15 | 16 | let step getNextState (curState, startIndex) = 17 | let paddedState = "...." + curState + "...." 18 | let nextState = 19 | paddedState 20 | |> Seq.windowed 5 21 | |> Seq.map getNextState 22 | |> String.concat "" 23 | let firstPlant, lastPlant = nextState.IndexOf("#"), nextState.LastIndexOf("#") 24 | let newIndex = startIndex - 2 + firstPlant 25 | nextState.Substring(firstPlant, lastPlant - firstPlant + 1), newIndex 26 | 27 | let getRule rules (rule : char []) = Map.find (new System.String(rule)) rules 28 | let getScore (state, startIndex) = 29 | state 30 | |> Seq.mapi (fun i v -> (i, v)) 31 | |> Seq.fold (fun score (i, pot) -> score + (if pot = '#' then i + startIndex else 0)) 0 32 | 33 | let solvePart1 (initState, rules) = 34 | Seq.init 20 id 35 | |> Seq.fold (fun s _ -> step (getRule rules) s) (initState, 0) 36 | |> getScore 37 | 38 | let solvePart2 (initState, rules) = 39 | let rec findRepeat i state startIndex = 40 | let (nextState, nextStartIndex) = step (getRule rules) (state, startIndex) 41 | if state = nextState then 42 | let curScore = getScore (nextState, nextStartIndex) 43 | let scoreDiff = curScore - (getScore (state, startIndex)) 44 | let stepsLeft = (50000000000L - (int64 (i + 1))) 45 | stepsLeft * (int64 scoreDiff) + (int64 curScore) 46 | else 47 | findRepeat (i + 1) nextState nextStartIndex 48 | findRepeat 0 initState 0 49 | 50 | let solver = {parse = File.ReadLines >> parsePlants; part1 = solvePart1; part2 = solvePart2} -------------------------------------------------------------------------------- /fsharp/2018/Solutions/Day19.fs: -------------------------------------------------------------------------------- 1 | module Year2018Day19 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System.IO 5 | 6 | let getUniqueVals lines = 7 | let lines = lines |> Seq.toArray 8 | // after comparing many inputs, the only lines that were different were 9 | // the constants on line 23 and 25 10 | let unique1 = lines.[22] |> splitBy " " (fun p -> int p.[2]) 11 | let unique2 = lines.[24] |> splitBy " " (fun p -> int p.[2]) 12 | unique1, unique2 13 | 14 | let getPart1Target (unique1, unique2) = 15 | let target = 16 | 2 // addi target 2 target 17 | |> (fun t -> t * t) // mulr target target target 18 | |> (*) 19 // mulr ip target target 19 | |> (*) 11 // muli target 11 target 20 | let temp = 21 | unique1 // addi temp unique1 temp 22 | |> (*) 22 // mulr temp ip temp 23 | |> (+) unique2 // addi temp unique2 temp 24 | temp + target // addr target temp target 25 | 26 | let getPart2Target (unique1, unique2) = 27 | let target = getPart1Target (unique1, unique2) 28 | let temp = 29 | 27 // setr ip temp 30 | |> (*) 28 // mulr temp ip temp 31 | |> (+) 29 // addr ip temp temp 32 | |> (*) 30 // mulr ip temp temp 33 | |> (*) 14 // muli temp 14 temp 34 | |> (*) 32 // mulr temp ip temp 35 | target + temp // addr target temp target 36 | 37 | let getFactors target = 38 | seq { for i = 1 to (target |> double |> sqrt |> int) do 39 | if target % i = 0 then 40 | yield i 41 | if i * i <> target then 42 | yield target / i } 43 | 44 | let solve f = f >> getFactors >> Seq.sum 45 | 46 | let solver = {parse = File.ReadLines >> getUniqueVals; part1 = solve getPart1Target; part2 = solve getPart2Target} -------------------------------------------------------------------------------- /fsharp/2018/Solutions/Day20.fs: -------------------------------------------------------------------------------- 1 | module Year2018Day20 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | type Direction = North | South | East | West 6 | type Term = Start | End | Move of Direction | StartGroup | EndGroup | Alternative 7 | let lexTerm = function 8 | | '^' -> Start | '$' -> End 9 | | 'N' -> Move North | 'S' -> Move South | 'E' -> Move East | 'W' -> Move West 10 | | '(' -> StartGroup | ')' -> EndGroup | '|' -> Alternative 11 | | c -> failwithf "Unexpected term %c" c 12 | let parseRegex = parseFirstLine <| Seq.map lexTerm 13 | 14 | type Position = int * int 15 | let moveDir (x, y) = function 16 | | North -> (x, y - 1) 17 | | South -> (x, y + 1) 18 | | East -> (x + 1, y) 19 | | West -> (x - 1, y) 20 | 21 | type ParseState = {posWithDist: Position * int; dists: Map; posStack: (Position * int) list} 22 | let updateDists (pos, dist) dists = 23 | match Map.tryFind pos dists with 24 | | Some d when d < dist -> dists 25 | | _ -> Map.add pos dist dists 26 | 27 | let parseMove {posWithDist=(pos, dist); dists=dists; posStack=posStack} dir = 28 | let newPosWithDist = moveDir pos dir, dist + 1 29 | let dists = updateDists newPosWithDist dists 30 | {posWithDist=newPosWithDist; dists=dists; posStack=posStack} 31 | 32 | let parseTerm state = function 33 | | Start | End -> state 34 | | Move dir -> parseMove state dir 35 | | StartGroup -> {state with posStack=state.posWithDist::state.posStack} 36 | | EndGroup -> {state with posStack=List.tail state.posStack} 37 | | Alternative -> {state with posWithDist=List.head state.posStack} 38 | 39 | let solve getResult regex = 40 | let initState = {posWithDist=((0, 0), 0); dists=Map.empty; posStack=[]} 41 | let finalState = Seq.fold parseTerm initState regex 42 | finalState.dists |> Map.toArray |> Array.map snd |> getResult 43 | 44 | let part1 = Array.max 45 | let part2 = Array.filter (fun x -> x >= 1000) >> Array.length 46 | let solver = {parse = parseRegex; part1 = solve part1; part2 = solve part2} -------------------------------------------------------------------------------- /fsharp/2018/Solutions/Day21.fs: -------------------------------------------------------------------------------- 1 | module Year2018Day21 2 | 3 | open AdventOfCode.FSharp.Common 4 | open System.IO 5 | 6 | let getSeed = 7 | // after comparing many inputs, the only lines that was different was line 8 8 | Seq.item 8 >> splitBy " " (fun p -> int p.[1]) 9 | 10 | let getNextTerminatingValue seed prev = 11 | let applyByte value = 12 | (+) value 13 | >> (&&&) 0xFFFFFF 14 | >> (*) 65899 15 | >> (&&&) 0xFFFFFF 16 | 17 | let b = prev ||| 0x10000 18 | [|0; 8; 16|] 19 | |> Array.map (fun shift -> b >>> shift &&& 0xFF) 20 | |> Array.fold applyByte seed 21 | 22 | let solvePart1 seed = getNextTerminatingValue seed 0 23 | let solvePart2 seed = 24 | let rec findLastUnique prev seen = 25 | let next = getNextTerminatingValue seed prev 26 | if Set.contains next seen then 27 | prev 28 | else 29 | findLastUnique next (Set.add next seen) 30 | findLastUnique 0 Set.empty 31 | 32 | let solver = {parse = File.ReadLines >> getSeed; part1 = solvePart1; part2 = solvePart2} -------------------------------------------------------------------------------- /fsharp/2018/Solutions/Day25.fs: -------------------------------------------------------------------------------- 1 | module Year2018Day25 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let manhattan p0 p1 = Array.zip p0 p1 |> Array.sumBy (fun (c0, c1) -> abs (c0 - c1)) 6 | let solve points = 7 | let rec countComponents count unseen = 8 | let rec findComponent queue unseen = 9 | match queue with 10 | | p0 :: ps -> 11 | let toAdd, toKeep = List.partition (fun p1 -> manhattan p0 p1 <= 3) unseen 12 | let newQueue = List.foldBack (fun t q -> t :: q) toAdd ps 13 | findComponent newQueue toKeep 14 | | [] -> unseen 15 | match unseen with 16 | | p :: _ -> countComponents (count + 1) (findComponent [p] unseen) 17 | | [] -> count 18 | countComponents 0 (Seq.toList points) 19 | 20 | let solver = {parse = id; part1 = parseEachLine (splitBy "," asIntArray) >> solve; part2 = (fun _ -> "Advent of Code Finished!")} -------------------------------------------------------------------------------- /fsharp/2019/AdventOfCode.FSharp.2019.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /fsharp/2019/Program.fs: -------------------------------------------------------------------------------- 1 | namespace AdventOfCode.FSharp.Y2019 2 | 3 | open AdventOfCode.FSharp 4 | open AdventOfCode.FSharp.Common 5 | open AdventOfCode.FSharp.Benchmarking 6 | 7 | module Program = 8 | let getSolver day part printAnswer = 9 | let run (solver : Day<_, _, _>) = 10 | Runner.run printAnswer 2019 day part solver 11 | match day with 12 | | 1 -> run Year2019Day01.solver | 2 -> run Year2019Day02.solver | 3 -> run Year2019Day03.solver 13 | | 4 -> run Year2019Day04.solver | 5 -> run Year2019Day05.solver | 6 -> run Year2019Day06.solver 14 | | 7 -> run Year2019Day07.solver | 8 -> run Year2019Day08.solver | 9 -> run Year2019Day09.solver 15 | | 10 -> run Year2019Day10.solver | 11 -> run Year2019Day11.solver | 12 -> run Year2019Day12.solver 16 | | 13 -> run Year2019Day13.solver | 14 -> run Year2019Day14.solver | 15 -> run Year2019Day15.solver 17 | | 16 -> run Year2019Day16.solver | 17 -> run Year2019Day17.solver | 18 -> run Year2019Day18.solver 18 | | 19 -> run Year2019Day19.solver | 20 -> run Year2019Day20.solver | 21 -> run Year2019Day21.solver 19 | | 22 -> run Year2019Day22.solver | 23 -> run Year2019Day23.solver | 24 -> run Year2019Day24.solver 20 | | 25 -> run Year2019Day25.solver 21 | | day -> fun () -> if printAnswer then printfn "Invalid Day: %i (Year %i)" day 2019 22 | 23 | type Bench2019() = 24 | inherit Bench() with 25 | override _.GetSolverFunc day part () = 26 | getSolver day part false () 27 | 28 | [] 29 | let main argv = 30 | let runPart day part = getSolver day part true () 31 | let runDay day = for part in 1..2 do runPart day part 32 | match argv.[0] with 33 | | "BENCH" -> Benchmarking.runBenchmarks() 34 | | "ALL" -> for day in 1..25 do runDay day 35 | | x -> 36 | let parts = x.Split('.') |> Array.map int 37 | match parts.Length with 38 | | 1 -> runDay parts.[0] 39 | | 2 -> runPart parts.[0] parts.[1] 40 | | _ -> () 41 | 0 42 | -------------------------------------------------------------------------------- /fsharp/2019/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "AdventOfCode.2019": { 4 | "commandName": "Project", 5 | "commandLineArgs": "ALL" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day01.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day01 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let getFuel mass = mass / 3 - 2 6 | 7 | let solvePart1 input = input |> Seq.sumBy getFuel 8 | 9 | let getFuel2 mass = 10 | let rec getFuel2' mass = 11 | if mass <= 0 then 0 12 | else mass + getFuel2' (getFuel mass) 13 | getFuel2' (getFuel mass) 14 | 15 | let solvePart2 input = input |> Seq.sumBy getFuel2 16 | 17 | let solver = { parse = parseEachLine asInt; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day02.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day02 2 | 3 | open AdventOfCode.FSharp.Common 4 | open AdventOfCode.FSharp.Y2019.Common.Intcode 5 | 6 | let rec run comp = 7 | match runInstruction comp with 8 | | InstructionResult.Complete c -> run c 9 | | InstructionResult.Halted -> comp 10 | | _ -> failwith "Should not have entered another state for day 2" 11 | 12 | let solve noun verb = 13 | Computer.create 14 | >> Computer.set 1 noun 15 | >> Computer.set 2 verb 16 | >> run 17 | >> Computer.get 0 18 | 19 | let solvePart2 intCode = 20 | seq { 21 | for noun = 0L to 99L do 22 | for verb = 0L to 99L do 23 | if solve noun verb intCode = 19690720L then 24 | 100L * noun + verb } |> Seq.head 25 | 26 | let solver = { parse = parseIntCode; part1 = solve 12L 2L; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day04.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day04 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let asRange (ints : int []) = ints.[0], ints.[1] 6 | let parse = parseFirstLine (splitBy "-" asIntArray) >> asRange 7 | 8 | let isMonotonic str = str |> Seq.pairwise |> Seq.exists (fun (a, b) -> a > b) |> not 9 | let countChars str = str |> Seq.countBy id 10 | let hasPair str = countChars str |> Seq.exists (fun (_, c) -> c >= 2) 11 | let hasStrictPair str = countChars str |> Seq.exists (fun (_, c) -> c = 2) 12 | 13 | let solvePart1 (low, high) = 14 | [low .. high] 15 | |> Seq.map string 16 | |> Seq.filter isMonotonic 17 | |> Seq.filter hasPair 18 | |> Seq.length 19 | 20 | let solvePart2 (low, high) = 21 | [low .. high] 22 | |> Seq.map string 23 | |> Seq.filter isMonotonic 24 | |> Seq.filter hasStrictPair 25 | |> Seq.length 26 | 27 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day05.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day05 2 | 3 | open AdventOfCode.FSharp.Common 4 | open AdventOfCode.FSharp.Y2019.Common.Intcode 5 | 6 | let provideInput systemId = 7 | function 8 | | Input f -> f systemId 9 | | _ -> failwith "Expected an input" 10 | 11 | let readOutput = 12 | function 13 | | Output (o, _) -> o 14 | | _ -> failwith "Expected an output" 15 | 16 | let solve systemId = 17 | Computer.create 18 | >> run 19 | >> provideInput systemId 20 | >> readOutput 21 | >> List.last 22 | 23 | let solver = { parse = parseIntCode; part1 = solve 1L; part2 = solve 5L } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day06.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day06 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let asOrbit line = 6 | let objects = splitBy ")" id line 7 | objects.[1], objects.[0] 8 | 9 | let parse = parseEachLine asOrbit >> Map.ofSeq 10 | 11 | let pathToRoot orbits object = 12 | let rec pathToRoot' object = 13 | match Map.tryFind object orbits with 14 | | Some obj -> obj :: pathToRoot' obj 15 | | None -> [ ] 16 | pathToRoot' object 17 | 18 | let solvePart1 orbits = 19 | orbits 20 | |> Map.toSeq 21 | |> Seq.sumBy (fst >> pathToRoot orbits >> List.length) 22 | 23 | let solvePart2 orbits = 24 | let pathToYou = pathToRoot orbits "YOU" |> List.rev 25 | let pathToSanta = pathToRoot orbits "SAN" |> List.rev 26 | 27 | let distToCommonParent = Seq.zip pathToYou pathToSanta |> Seq.findIndex (fun (obj1, obj2) -> obj1 <> obj2) 28 | pathToYou.Length + pathToSanta.Length - (2 * distToCommonParent) 29 | 30 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day07.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day07 2 | 3 | open AdventOfCode.FSharp.Common 4 | open AdventOfCode.FSharp.Y2019.Common.Intcode 5 | 6 | let rec permutations items = 7 | seq { 8 | if Set.isEmpty items then [] 9 | else 10 | for x in items do 11 | for xs in permutations (Set.remove x items) do 12 | x :: xs } 13 | 14 | let provideInput systemId = 15 | function 16 | | Input f -> f systemId 17 | | s -> s 18 | 19 | let tryReadOutput = 20 | function 21 | | Output (o, c) -> Some o, c 22 | | c -> None, c 23 | 24 | let runAmpWithSignal signal = provideInput signal >> tryReadOutput 25 | 26 | type AmpState = Running of signal: int64 | Completed of signal: int64 27 | 28 | let runAmpInState state amp = 29 | match state with 30 | | Running i -> 31 | match runAmpWithSignal i amp with 32 | | Some x, c -> c, Running x.[0] 33 | | None, c -> c, Completed i 34 | | Completed _ -> amp, state 35 | 36 | let runAllAmps state amps = 37 | Array.mapFold runAmpInState state amps 38 | 39 | let processAmps1 state amps = 40 | match runAllAmps state amps with 41 | | _, Running i -> i 42 | | _, Completed i -> i 43 | 44 | let rec processAmps2 state amps = 45 | match runAllAmps state amps with 46 | | _, Completed s -> s 47 | | newAmps, state' -> processAmps2 state' newAmps 48 | 49 | let solve processAmps minId maxId intCode = 50 | let program = Computer.create intCode |> run 51 | let getThrusterSignal (ampIds : int64 []) = 52 | Array.init 5 (fun i -> program |> provideInput (ampIds.[i])) 53 | |> processAmps (Running 0L) 54 | 55 | permutations (set [minId .. maxId]) 56 | |> Seq.map (List.toArray >> getThrusterSignal) 57 | |> Seq.max 58 | 59 | let solver = { parse = parseIntCode; part1 = solve processAmps1 0L 4L; part2 = solve processAmps2 5L 9L } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day08.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day08 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let solvePart1 input = 6 | let counts = 7 | input 8 | |> Array.chunkBySize (25 * 6) 9 | |> Array.map (Array.countBy id >> Map.ofArray) 10 | |> Array.minBy (Map.find '0') 11 | 12 | counts.['1'] * counts.['2'] 13 | 14 | let solvePart2 input = 15 | let output = 16 | input 17 | |> Array.chunkBySize (25 * 6) 18 | |> Array.reduce (Array.map2 (fun p1 p2 -> if p1 = '2' then p2 else p1)) 19 | |> Array.chunkBySize 25 20 | |> Array.map (System.String.Concat >> String.map (function '1' -> '█' | _ -> ' ')) 21 | |> String.concat "\n" 22 | 23 | // just so it's nicer to read for the runner 24 | "\n" + output 25 | 26 | let solver = { parse = parseFirstLine asCharArray; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day09.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day09 2 | 3 | open AdventOfCode.FSharp.Common 4 | open AdventOfCode.FSharp.Y2019.Common.Intcode 5 | 6 | let provideInput systemId = 7 | function 8 | | Input f -> f systemId 9 | | _ -> failwith "Expected an input" 10 | 11 | let readOutput = 12 | function 13 | | Output (o, _) -> o 14 | | _ -> failwith "Expected an output" 15 | 16 | let solve boostMode = 17 | Computer.create 18 | >> run 19 | >> provideInput boostMode 20 | >> readOutput 21 | >> List.head 22 | 23 | let solver = { parse = parseIntCode; part1 = solve 1L; part2 = solve 2L } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day11.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day11 2 | 3 | open AdventOfCode.FSharp.Common 4 | open AdventOfCode.FSharp.Y2019.Common.Intcode 5 | 6 | let turn (dx, dy) dir = if dir = 0L then (dy, -dx) else (-dy, dx) 7 | let move (x, y) (dx, dy) = (x + dx, y + dy) 8 | 9 | type GameState = 10 | { Grid : Map 11 | Dir : int * int 12 | Pos : int * int } 13 | 14 | static member create = { Grid = Map.empty; Pos = (0, 0); Dir = (0, -1) } 15 | static member paintPos color s = { s with Grid = Map.add s.Pos color s.Grid } 16 | static member turn turnDir s = { s with Dir = turn s.Dir turnDir } 17 | static member move s = { s with Pos = move s.Pos s.Dir } 18 | 19 | let readOutput output = 20 | match Seq.toList output with 21 | | paint :: turnDir :: [] -> 22 | GameState.paintPos paint 23 | >> GameState.turn turnDir 24 | >> GameState.move 25 | | _ -> id 26 | 27 | let writeInput state = Map.tryFind state.Pos state.Grid |> Option.defaultValue 0L 28 | 29 | let printGrid grid = 30 | let updateBounds (x1, x2, y1, y2) (x, y) = (min x x1, max x x2, min y y1, max y y2) 31 | let minX, maxX, minY, maxY = Map.fold (fun bounds pos _ -> updateBounds bounds pos) (0, 0, 0, 0) grid 32 | 33 | seq { 34 | for y = minY to maxY do 35 | '\n' 36 | for x = minX to maxX do 37 | if (Map.tryFind (x, y) grid = Some 1L) then '█' else ' ' 38 | } |> charsToStr 39 | 40 | let rec runProgram state = 41 | function 42 | | Input f -> writeInput state |> f |> runProgram state 43 | | Output (o, s) -> runProgram (readOutput o state) s 44 | | Halted -> state 45 | 46 | let solvePart1 = 47 | Computer.create 48 | >> run 49 | >> runProgram GameState.create 50 | >> (fun s -> s.Grid.Count) 51 | 52 | let solvePart2 = 53 | Computer.create 54 | >> run 55 | >> runProgram (GameState.create |> GameState.paintPos 1L) 56 | >> (fun s -> printGrid s.Grid) 57 | 58 | let solver = { parse = parseIntCode; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day12.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day12 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let toDimensions moons = 6 | let xDim = moons |> Array.map (fun m -> Array.item 0 m, 0) 7 | let yDim = moons |> Array.map (fun m -> Array.item 1 m, 0) 8 | let zDim = moons |> Array.map (fun m -> Array.item 2 m, 0) 9 | [| xDim; yDim; zDim |] 10 | 11 | let parse = parseEachLine extractInts >> Seq.toArray >> toDimensions 12 | 13 | let getAccel x y = 14 | if x < y then 1 15 | elif x > y then -1 16 | else 0 17 | 18 | let step moons = 19 | moons 20 | |> Array.map (fun p -> Array.fold (fun (p1, v1) (p2, _) -> (p1, v1 + getAccel p1 p2)) p moons) 21 | |> Array.map (fun (p, v) -> (p + v, v)) 22 | 23 | let rec stepN n dim = 24 | if n = 0 then dim 25 | else step dim |> stepN (n - 1) 26 | 27 | let totalEnergy dims = 28 | let totalEnergyForMoon moon = 29 | let potential = dims |> Array.sumBy (Array.item moon >> fst >> abs) 30 | let kinetic = dims |> Array.sumBy (Array.item moon >> snd >> abs) 31 | potential * kinetic 32 | let m = Array.length (Array.head dims) 33 | [| 0 .. m - 1|] |> Array.sumBy totalEnergyForMoon 34 | 35 | let solvePart1 dims = 36 | dims 37 | |> Array.map (stepN 1000) 38 | |> totalEnergy 39 | 40 | let stepsUntilEqual dim = 41 | let rec stepsUntilEqual' n dim' = 42 | if dim = dim' && n > 0L then n 43 | else step dim' |> stepsUntilEqual' (n + 1L) 44 | stepsUntilEqual' 0L dim 45 | 46 | let rec gcd a b = 47 | if b = 0L then abs a 48 | else gcd b (a % b) 49 | 50 | let solvePart2 dims = 51 | dims 52 | |> Array.map stepsUntilEqual 53 | |> Array.reduce (fun a b -> a * b / (gcd a b)) 54 | 55 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day16.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day16 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parse = parseFirstLine (Seq.map (fun c -> (int c - int '0')) >> Seq.toArray) 6 | 7 | let applyFFTPhase basePos list = 8 | let len = Array.length list 9 | let preSum = Array.scan (+) 0 list 10 | let finalSum = Array.last preSum 11 | 12 | let getValAtIndex i = 13 | let pos = i + basePos 14 | let rec getVal' j pat acc = 15 | if j >= len then acc 16 | elif j + pos >= len then acc + pat * (finalSum - preSum.[j]) 17 | else getVal' (j + 2 * pos) (-pat) (acc + pat * (preSum.[j + pos] - preSum.[j])) 18 | getVal' i 1 0 19 | 20 | let toLastDigit i = abs (i % 10) 21 | 22 | Array.init len (getValAtIndex >> toLastDigit) 23 | 24 | let rec solve basePos n list = 25 | if n = 0 then list 26 | else applyFFTPhase basePos list |> solve basePos (n - 1) 27 | 28 | let getFirstNDigits n = Array.take n >> Array.reduce (fun i n -> 10 * i + n) 29 | 30 | let solvePart1 = solve 1 100 >> getFirstNDigits 8 31 | 32 | let solvePart2 input = 33 | let offset = getFirstNDigits 7 input 34 | let len = Array.length input 35 | Array.init (len * 10000 - offset) (fun i -> input.[(offset + i) % len]) 36 | |> solve (offset + 1) 100 37 | |> getFirstNDigits 8 38 | 39 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/Day21.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day21 2 | 3 | open AdventOfCode.FSharp.Common 4 | open AdventOfCode.FSharp.Y2019.Common.Intcode 5 | 6 | let debug = false 7 | 8 | let solve program = 9 | let printAscii = Seq.map char >> charsToStr 10 | let rec runProg input comp = 11 | match input, comp with 12 | | x :: xs, Input f -> f x |> runProg xs 13 | | x :: xs, Output (o, s) -> 14 | if debug then printf "%s" (printAscii o) 15 | s |> runProg input 16 | | [], Output (o, s) -> 17 | if debug then printf "%s" (printAscii o) 18 | o |> List.last 19 | | _, _ -> failwith "ERR" 20 | 21 | let inputAsStr = ((String.concat "\n" program) + "\n" |> Seq.map int64 |> Seq.toList) 22 | Computer.create >> run >> runProg inputAsStr 23 | 24 | (* 25 | If A is a hole we must jump or we fall into it 26 | If C is a hole and D is not a hole we must jump or we fail on ####.#..### 27 | This can be represented by !A || (!C && D) 28 | *) 29 | let part1 = 30 | [ "NOT C J" // J = !C 31 | "AND D J" // J = !C && D 32 | "NOT A T" // T = !A 33 | "OR T J" // J = !A || (!C && D) 34 | "WALK" ] 35 | 36 | (* 37 | Similar to part 1, except changing (!C && D) to (!(B && C) && D && (E || H)) 38 | The (E || H) part ensures that when we jump to D, that a floor exists a step or a jump after it. 39 | The !C is changed to !(B && C) since we want to jump immediately on ##.##...# 40 | *) 41 | let part2 = 42 | [ "OR H J" // J = H 43 | "OR E J" // J = E || H 44 | "AND D J" // J = D && (E || H) 45 | "OR C T" // T = C 46 | "AND B T" // T = C && B 47 | "NOT T T" // T = !(C && B) 48 | "AND T J" // J = !(B && C) && D && (E || H) 49 | "NOT A T" // T = !A 50 | "OR T J" // J = !A || (!(B && C) && D && (E || H)) 51 | "RUN" ] 52 | 53 | let solver = { parse = parseIntCode; part1 = solve part1; part2 = solve part2 } -------------------------------------------------------------------------------- /fsharp/2019/Solutions/DayTemplate.fs: -------------------------------------------------------------------------------- 1 | module Year2019Day 2 | 3 | open AdventOfCode.FSharp.Common 4 | 5 | let parseLine (line : string) = 6 | asInt line 7 | 8 | let parse = parseEachLine parseLine 9 | 10 | let solvePart1 (input) = 11 | input 12 | 13 | let solvePart2 (input) = 14 | input 15 | 16 | let solver = { parse = parse; part1 = solvePart1; part2 = solvePart2 } -------------------------------------------------------------------------------- /fsharp/Common/AdventOfCode.FSharp.Common.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /fsharp/Common/Benchmarking.fs: -------------------------------------------------------------------------------- 1 | namespace AdventOfCode.FSharp 2 | 3 | open BenchmarkDotNet.Reports 4 | open BenchmarkDotNet.Attributes 5 | open BenchmarkDotNet.Columns 6 | open BenchmarkDotNet.Jobs 7 | open BenchmarkDotNet.Configs 8 | open BenchmarkDotNet.Running 9 | open Perfolizer.Horology 10 | open System.Globalization 11 | 12 | module Benchmarking = 13 | 14 | [] 15 | type Bench() = 16 | let mutable solverFunc : unit -> unit = fun _ -> () 17 | 18 | abstract member GetSolverFunc : int -> int -> unit -> unit 19 | 20 | [] 21 | member val public Day = 0 with get, set 22 | 23 | [] 24 | member val public Part = 0 with get, set 25 | 26 | [] 27 | member self.GlobalSetupData() = 28 | solverFunc <- self.GetSolverFunc self.Day self.Part 29 | 30 | [] 31 | member __.RunPart () = solverFunc () 32 | 33 | let runBenchmarks<'T when 'T :> Bench> () = 34 | let benchmarkJob = 35 | Job.Default 36 | .WithWarmupCount(1) 37 | .WithIterationTime(TimeInterval.FromMilliseconds(250.)) 38 | .WithMinIterationCount(10) 39 | .WithMaxIterationCount(15) 40 | .DontEnforcePowerPlan() 41 | 42 | let summaryStyle = new SummaryStyle(CultureInfo.InvariantCulture, true, SizeUnit.B, TimeUnit.Nanosecond, false) 43 | 44 | let benchmarkConfig = 45 | DefaultConfig.Instance 46 | .AddJob(benchmarkJob.AsDefault()) 47 | .WithSummaryStyle(summaryStyle) 48 | 49 | BenchmarkRunner.Run<'T>(benchmarkConfig) |> ignore -------------------------------------------------------------------------------- /fsharp/Common/Library.fs: -------------------------------------------------------------------------------- 1 | namespace AdventOfCode.FSharp 2 | 3 | open System 4 | open System.IO 5 | open System.Text.RegularExpressions 6 | open System.Collections.Generic 7 | 8 | // This is a set of methods that I found myself using many times in my solutions 9 | module Common = 10 | // Every day has a corresponding Day record which defines how to parse the file 11 | // then two functions for solving each part respectively 12 | [] 13 | [] 14 | type Day<'a, 'b, 'c> = { parse: string -> 'a; part1: 'a -> 'b; part2: 'a -> 'c } 15 | 16 | // helper methods for parsing 17 | let parseFirstLine f (fileName : string) = 18 | use fs = new FileStream(fileName, FileMode.Open) 19 | use reader = new StreamReader(fs) 20 | f (reader.ReadLine()) 21 | 22 | let parseEachLine f = File.ReadLines >> Seq.map f 23 | let parseEachLineIndexed f = File.ReadLines >> Seq.mapi f 24 | 25 | let asString : string -> string = id 26 | let asInt : string -> int = int 27 | let asCharArray (s : string) = s.ToCharArray () 28 | let asStringArray : string [] -> string [] = Array.map string 29 | let asIntArray : string [] -> int [] = Array.map int 30 | let asInt64Array : string [] -> int64 [] = Array.map int64 31 | let splitBy (c : string) f (str : string) = str.Split([| c |], StringSplitOptions.None) |> f 32 | let extractInts str = [| for m in Regex.Matches(str, "(-?\d+)") -> int m.Value |] 33 | let withRegex regex str = [| for m in Regex.Match(str, regex).Groups -> m.Value|] |> Array.tail 34 | 35 | let charsToStr (chars : char seq) = chars |> Seq.map string |> String.concat "" 36 | 37 | let inline repeatN n f x = 38 | let rec aux n x = 39 | if n = 0 then x 40 | else f x |> aux (n - 1) 41 | aux n x 42 | 43 | let inline repeatUntil f pred x = 44 | let rec aux x = 45 | if pred x then x 46 | else f x |> aux 47 | aux x 48 | 49 | let inline repeatUntilDuplicate f x = 50 | let seen = new HashSet<_>() 51 | let rec aux x = 52 | if seen.Contains(x) then x 53 | else 54 | seen.Add(x) |> ignore 55 | f x |> aux 56 | aux x 57 | -------------------------------------------------------------------------------- /fsharp/Common/Runner.fs: -------------------------------------------------------------------------------- 1 | namespace AdventOfCode.FSharp 2 | 3 | open System.IO 4 | open Common 5 | 6 | module Runner = 7 | let run printResult year day part (solver : Day<_, _, _>) = 8 | let run part solve = 9 | let fileName = Path.Combine("input", (sprintf "day%02i.txt" day)) 10 | fun _ -> 11 | let result = fileName |> solver.parse |> solve 12 | if printResult then 13 | printfn "Year %i Day %02i-%i %O" year day part result 14 | match part with 15 | | 1 -> run 1 solver.part1 16 | | 2 -> run 2 solver.part2 17 | | _ -> fun _ -> () -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.100", 4 | "rollForward": "latestMinor" 5 | }, 6 | "msbuild-sdks": { 7 | "MSTest.Sdk": "3.6.3" 8 | } 9 | } 10 | --------------------------------------------------------------------------------