├── .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 |
--------------------------------------------------------------------------------