From f1395e5bc226059c39994c9da0fb6a82d056b102 Mon Sep 17 00:00:00 2001 From: TornOne Date: Tue, 9 Feb 2021 18:33:10 +0200 Subject: [PATCH] Add solutions for all days --- Advent of Code 2020.csproj | 68 +++++++++++ Day1.cs | 44 ++++++++ Day10.cs | 46 ++++++++ Day11.cs | 192 +++++++++++++++++++++++++++++++ Day12.cs | 84 ++++++++++++++ Day13.cs | 53 +++++++++ Day14.cs | 82 ++++++++++++++ Day15.cs | 27 +++++ Day16.cs | 97 ++++++++++++++++ Day17.cs | 147 ++++++++++++++++++++++++ Day18.cs | 98 ++++++++++++++++ Day19.cs | 81 +++++++++++++ Day2.cs | 43 +++++++ Day20.cs | 225 +++++++++++++++++++++++++++++++++++++ Day21.cs | 91 +++++++++++++++ Day22.cs | 95 ++++++++++++++++ Day23.cs | 86 ++++++++++++++ Day24.cs | 81 +++++++++++++ Day25.cs | 32 ++++++ Day3.cs | 37 ++++++ Day4.cs | 45 ++++++++ Day5.cs | 53 +++++++++ Day6.cs | 23 ++++ Day7.cs | 73 ++++++++++++ Day8.cs | 88 +++++++++++++++ Day9.cs | 76 +++++++++++++ Picker.cs | 28 +++++ 27 files changed, 2095 insertions(+) create mode 100644 Advent of Code 2020.csproj create mode 100644 Day1.cs create mode 100644 Day10.cs create mode 100644 Day11.cs create mode 100644 Day12.cs create mode 100644 Day13.cs create mode 100644 Day14.cs create mode 100644 Day15.cs create mode 100644 Day16.cs create mode 100644 Day17.cs create mode 100644 Day18.cs create mode 100644 Day19.cs create mode 100644 Day2.cs create mode 100644 Day20.cs create mode 100644 Day21.cs create mode 100644 Day22.cs create mode 100644 Day23.cs create mode 100644 Day24.cs create mode 100644 Day25.cs create mode 100644 Day3.cs create mode 100644 Day4.cs create mode 100644 Day5.cs create mode 100644 Day6.cs create mode 100644 Day7.cs create mode 100644 Day8.cs create mode 100644 Day9.cs create mode 100644 Picker.cs diff --git a/Advent of Code 2020.csproj b/Advent of Code 2020.csproj new file mode 100644 index 0000000..ffb5507 --- /dev/null +++ b/Advent of Code 2020.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {D017CB45-4695-4C39-9312-9A1599E5EF7D} + Exe + Advent_of_Code_2020 + Advent of Code 2020 + v4.8 + 512 + true + true + + + true + bin\Debug\ + DEBUG;TRACE + full + x64 + none + + + bin\ + + + true + none + x64 + none + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Day1.cs b/Day1.cs new file mode 100644 index 0000000..5764934 --- /dev/null +++ b/Day1.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day1 { + public static int Solution1() { + HashSet solutions = new HashSet(); + + foreach (int number in Array.ConvertAll(File.ReadAllLines("input1.txt"), int.Parse)) { + int other = 2020 - number; + + if (solutions.Contains(number)) { + return other * number; + } + solutions.Add(other); + } + + return -1; + } + + public static int Solution2() { + Dictionary solutions = new Dictionary(); + int[] numbers = Array.ConvertAll(File.ReadAllLines("input1.txt"), int.Parse); + + foreach (int number1 in numbers) { + if (solutions.TryGetValue(number1, out int product)) { + return product * number1; + } + + foreach (int number2 in numbers) { + if (number1 == number2) { + continue; + } + + int sum = number1 + number2; + if (sum < 2020) { + solutions[2020 - sum] = number1 * number2; + } + } + } + + return -1; + } +} diff --git a/Day10.cs b/Day10.cs new file mode 100644 index 0000000..8d58fd7 --- /dev/null +++ b/Day10.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; + +class Day10 { + public static int Solution1() { + int[] adapters = Array.ConvertAll(File.ReadAllLines("input10.txt"), int.Parse); + Array.Sort(adapters); + + int diff1 = adapters[0] == 1 ? 1 : 0; + int diff3 = adapters[0] == 3 ? 2 : 1; + for (int i = adapters.Length - 1; i > 0;) { + int diff = adapters[i--] - adapters[i]; + if (diff == 1) { + diff1++; + } else if (diff == 3) { + diff3++; + } + } + + return diff1 * diff3; + } + + public static int Solution2() { + int[] adapters = Array.ConvertAll(File.ReadAllLines("input10.txt"), int.Parse); + Array.Sort(adapters); + long[] arrangements = new long[adapters.Length]; + + //Count the ways from each adapter to your phone + arrangements[arrangements.Length - 1] = 1; + arrangements[arrangements.Length - 2] = 1; + arrangements[arrangements.Length - 3] = 2; + for (int i = adapters.Length - 4; i >= 0; i--) { + arrangements[i] = arrangements[i + 1]; + + if (adapters[i + 2] - adapters[i] <= 3) { + arrangements[i] += arrangements[i + 2]; + if (adapters[i + 3] - adapters[i] <= 3) { + arrangements[i] += arrangements[i + 3]; + } + } + } + + Console.WriteLine(arrangements[0] + arrangements[1] + arrangements[2]); + return 0; + } +} diff --git a/Day11.cs b/Day11.cs new file mode 100644 index 0000000..ff0cd74 --- /dev/null +++ b/Day11.cs @@ -0,0 +1,192 @@ +using System.Collections.Generic; +using System.IO; + +class Day11 { + public static int Solution1() { + string[] input = File.ReadAllLines("input11.txt"); + int width = input[0].Length + 2; + int height = input.Length + 2; + int[] seats1 = new int[width * height]; + for (int y = 0; y < input.Length; y++) { + int i = (y + 1) * width + 1; + for (int x = 0; x < input[0].Length; x++) { + if (input[y][x] == 'L') { + seats1[i + x] = 1; + } + } + } + int[] seats2 = (int[])seats1.Clone(); + + int[] offsets = new int[8] { -width - 1, -width, -width + 1, -1, 1, width - 1, width, width + 1 }; + for (int round = 1;; round++) { + bool change = false; + int[] from = round % 2 == 0 ? seats1 : seats2; + int[] to = round % 2 == 0 ? seats2 : seats1; + + for (int i = 0; i < seats1.Length; i++) { + if (from[i] == 1) { + int result = 2; + foreach (int offset in offsets) { + if (from[i + offset] == 2) { + result = 1; + break; + } + } + change = change || result == 2; + to[i] = result; + } else if (from[i] == 2) { + int count = 0; + foreach (int offset in offsets) { + if (from[i + offset] == 2) { + count++; + } + } + change = change || count >= 4; + to[i] = count >= 4 ? 1 : 2; + } + } + + if (!change) { + int count = 0; + foreach (int seat in to) { + if (seat == 2) { + count++; + } + } + return count; + } + } + } + + public static int Solution2() { + string[] map = File.ReadAllLines("input11.txt"); + int width = map[0].Length; + int height = map.Length; + + int count = 0; + int[,] indexMap = new int[map.Length, map[0].Length]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + indexMap[y, x] = map[y][x] == 'L' ? count++ : -1; + } + } + + int[][] adjacencyMap = new int[count][]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (indexMap[y, x] == -1) { + continue; + } + List adjacent = new List(8); + //Top left + for (int y2 = y - 1, x2 = x - 1; y2 >= 0 && x2 >= 0; y2--, x2--) { + int i = indexMap[y2, x2]; + if (i != -1) { + adjacent.Add(i); + break; + } + } + //Top + for (int y2 = y - 1; y2 >= 0; y2--) { + int i = indexMap[y2, x]; + if (i != -1) { + adjacent.Add(i); + break; + } + } + //Top right + for (int y2 = y - 1, x2 = x + 1; y2 >= 0 && x2 < width; y2--, x2++) { + int i = indexMap[y2, x2]; + if (i != -1) { + adjacent.Add(i); + break; + } + } + //Left + for (int x2 = x - 1; x2 >= 0; x2--) { + int i = indexMap[y, x2]; + if (i != -1) { + adjacent.Add(i); + break; + } + } + //Right + for (int x2 = x + 1; x2 < width; x2++) { + int i = indexMap[y, x2]; + if (i != -1) { + adjacent.Add(i); + break; + } + } + //Bottom left + for (int y2 = y + 1, x2 = x - 1; y2 < height && x2 >= 0; y2++, x2--) { + int i = indexMap[y2, x2]; + if (i != -1) { + adjacent.Add(i); + break; + } + } + //Bottom + for (int y2 = y + 1; y2 < height; y2++) { + int i = indexMap[y2, x]; + if (i != -1) { + adjacent.Add(i); + break; + } + } + //Bottom right + for (int y2 = y + 1, x2 = x + 1; y2 < height && x2 < width; y2++, x2++) { + int i = indexMap[y2, x2]; + if (i != -1) { + adjacent.Add(i); + break; + } + } + + adjacencyMap[indexMap[y, x]] = adjacent.ToArray(); + } + } + + bool[] seats1 = new bool[count]; + bool[] seats2 = new bool[count]; + + for (int round = 1; ; round++) { + bool change = false; + bool[] from = round % 2 == 0 ? seats1 : seats2; + bool[] to = round % 2 == 0 ? seats2 : seats1; + + for (int i = 0; i < seats1.Length; i++) { + if (from[i]) { + int seen = 0; + foreach (int adjacent in adjacencyMap[i]) { + if (from[adjacent]) { + seen++; + } + } + change = change || seen >= 5; + to[i] = seen < 5; + } else { + bool result = true; + foreach (int adjacent in adjacencyMap[i]) { + if (from[adjacent]) { + result = false; + break; + } + } + change = change || result; + to[i] = result; + } + } + + if (!change) { + int total = 0; + foreach (bool seat in to) { + if (seat) { + total++; + } + } + return total; + } + } + } +} diff --git a/Day12.cs b/Day12.cs new file mode 100644 index 0000000..7f167ac --- /dev/null +++ b/Day12.cs @@ -0,0 +1,84 @@ +using System; +using System.IO; + +class Day12 { + public static int Solution1() { + int x = 0; + int y = 0; + int rot = 0; + + foreach (string line in File.ReadLines("input12.txt")) { + char action = line[0]; + int amount = int.Parse(line.Substring(1)); + + if (action == 'N') { + y += amount; + } else if (action == 'S') { + y -= amount; + } else if (action == 'E') { + x += amount; + } else if (action == 'W') { + x -= amount; + } else if (action == 'L') { + rot = (rot - amount / 90 + 4) % 4; + } else if (action == 'R') { + rot = (rot + amount / 90) % 4; + } else if (action == 'F') { + if (rot == 0) { + x += amount; + } else if (rot == 1) { + y -= amount; + } else if (rot == 2) { + x -= amount; + } else { + y += amount; + } + } + } + + return Math.Abs(x) + Math.Abs(y); + } + + public static int Solution2() { + int x = 10; + int y = 1; + int xPos = 0; + int yPos = 0; + + foreach (string line in File.ReadLines("input12.txt")) { + char action = line[0]; + int amount = int.Parse(line.Substring(1)); + + if (action == 'N') { + y += amount; + } else if (action == 'S') { + y -= amount; + } else if (action == 'E') { + x += amount; + } else if (action == 'W') { + x -= amount; + } else if (action == 'L') { + action = 'R'; + amount = 360 - amount; + } if (action == 'R') { + if (amount == 90) { + int t = x; + x = y; + y = -t; + } else if (amount == 180) { + x = -x; + y = -y; + } else if (amount == 270) { + int t = x; + x = -y; + y = t; + } + } else if (action == 'F') { + xPos += x * amount; + yPos += y * amount; + } + } + + return Math.Abs(xPos) + Math.Abs(yPos); + } +} diff --git a/Day13.cs b/Day13.cs new file mode 100644 index 0000000..6581538 --- /dev/null +++ b/Day13.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day13 { + public static int Solution1() { + string[] input = File.ReadAllLines("input13.txt"); + int target = int.Parse(input[0]); + int earliest = int.MaxValue; + int earliestId = -1; + + foreach (string str in input[1].Split(',')) { + if (int.TryParse(str, out int id)) { + int time = (target / id + (target % id == 0 ? 0 : 1)) * id; + if (time < earliest) { + earliest = time; + earliestId = id; + } + } + } + + return (earliest - target) * earliestId; + } + + public static int Solution2() { + string[] input = File.ReadAllLines("input13.txt")[1].Split(','); + Dictionary ids = new Dictionary(); + for (int i = 0; i < input.Length; i++) { + if (int.TryParse(input[i], out int id)) { + ids[id] = i % id; + } + } + + long answer = 0; + long mult = 1; + foreach (KeyValuePair i in ids) { + int id = i.Key; + int offset = i.Value; + + for (long x = answer + offset + mult; ; x += mult) { + if (x % id == 0) { + answer = x - offset; + break; + } + } + + mult *= id; + } + + Console.WriteLine(answer); + return 0; + } +} diff --git a/Day14.cs b/Day14.cs new file mode 100644 index 0000000..196d41f --- /dev/null +++ b/Day14.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day14 { + public static int Solution1() { + Dictionary memory = new Dictionary(); + string mask = ""; + + foreach (string line in File.ReadLines("input14.txt")) { + string[] instruction = line.Split(new[] { " = " }, StringSplitOptions.None); + if (instruction[0] == "mask") { + mask = instruction[1]; + } else { + long value = long.Parse(instruction[1]); + for (int i = 0; i < 36; i++) { + char bit = mask[i]; + if (bit == '0') { + value &= 262144L * 262144L - 1 - (1L << (35 - i)); + } else if (bit == '1') { + value |= 1L << (35 - i); + } + } + memory[int.Parse(instruction[0].Substring(4, instruction[0].Length - 5))] = value; + } + } + + long sum = 0; + foreach (long value in memory.Values) { + sum += value; + } + + Console.WriteLine(sum); + return 0; + } + + public static int Solution2() { + Dictionary memory = new Dictionary(); + string mask = ""; + + foreach (string line in File.ReadLines("input14.txt")) { + string[] instruction = line.Split(new[] { " = " }, StringSplitOptions.None); + if (instruction[0] == "mask") { + mask = instruction[1]; + } else { + int value = int.Parse(instruction[1]); + int baseAddress = int.Parse(instruction[0].Substring(4, instruction[0].Length - 5)); + + void WriteToMemory(char[] address, int i) { + if (i == 36) { + memory[Convert.ToInt64(new string(address), 2)] = value; + return; + } + + if (address[i] == 'X') { + address[i] = '0'; + WriteToMemory((char[])address.Clone(), i + 1); + address[i] = '1'; + } + WriteToMemory(address, i + 1); + } + + char[] masterAddress = mask.ToCharArray(); + for (int i = 35; i >= 20; i--) { + if (masterAddress[i] == '0') { + masterAddress[i] = (char)(((baseAddress >> (35 - i)) & 1) + '0'); + } + } + + WriteToMemory(masterAddress, 0); + } + } + + long sum = 0; + foreach (int value in memory.Values) { + sum += value; + } + + Console.WriteLine(sum); + return 0; + } +} diff --git a/Day15.cs b/Day15.cs new file mode 100644 index 0000000..744273c --- /dev/null +++ b/Day15.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +class Day15 { + static readonly int[] input = new[] { 20, 9, 11, 0, 1, 2 }; + + public static int Solution1() => Solution(2020); + + public static int Solution2() => Solution(30000000); + + static int Solution(int n) { + Dictionary numbers = new Dictionary(); + for (int turn = 0; turn < input.Length;) { + numbers[input[turn++]] = (turn, turn); + } + int last = input[input.Length - 1]; + (int penultimate, int ultimate) = numbers[last]; + + for (int turn = input.Length + 1; turn <= n; turn++) { + last = ultimate - penultimate; + penultimate = numbers.TryGetValue(last, out (int _, int value) ult) ? ult.value : turn; + ultimate = turn; + numbers[last] = (penultimate, ultimate); + } + + return last; + } +} diff --git a/Day16.cs b/Day16.cs new file mode 100644 index 0000000..b984112 --- /dev/null +++ b/Day16.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day16 { + public static int Solution1() { + string[] input = File.ReadAllText("input16.txt").Split(new[] { "\n\n" }, StringSplitOptions.None); + + bool[] rules = new bool[1000]; + foreach (string[] ranges in Array.ConvertAll(input[0].Split('\n'), rule => rule.Split(new[] { ": " }, StringSplitOptions.None)[1].Split(new[] { " or " }, StringSplitOptions.None))) { + foreach (string[] range in Array.ConvertAll(ranges, range => range.Split('-'))) { + int start = int.Parse(range[0]); + int end = int.Parse(range[1]); + for (int i = start; i <= end; i++) { + rules[i] = true; + } + } + } + + int sum = 0; + foreach (int rule in Array.ConvertAll(input[2].Split(new[] { '\n' }, 2)[1].Split(new[] { '\n', ',' }, StringSplitOptions.RemoveEmptyEntries), int.Parse)) { + if (!rules[rule]) { + sum += rule; + } + } + + return sum; + } + + public static int Solution2() { + string[] input = File.ReadAllText("input16.txt").Split(new[] { "\n\n" }, StringSplitOptions.None); + + Dictionary rules = new Dictionary(); + foreach (string[] rule in Array.ConvertAll(input[0].Split('\n'), rule => rule.Split(new[] { ": " }, StringSplitOptions.None))) { + bool[] validValues = new bool[1000]; + rules[rule[0]] = validValues; + + foreach (string[] range in Array.ConvertAll(rule[1].Split(new[] { " or " }, StringSplitOptions.None), range => range.Split('-'))) { + int start = int.Parse(range[0]); + int end = int.Parse(range[1]); + for (int i = start; i <= end; i++) { + validValues[i] = true; + } + } + } + + int[] myTicket = Array.ConvertAll(input[1].Split('\n')[1].Split(','), int.Parse); + List validTickets = new List { myTicket }; + foreach (int[] ticket in Array.ConvertAll(input[2].Split(new[] { '\n' }, 2)[1].Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries), line => Array.ConvertAll(line.Split(','), int.Parse))) { + if (Array.TrueForAll(ticket, value => { + foreach (bool[] validValues in rules.Values) { + if (validValues[value]) { + return true; + } + } + return false; + })) { + validTickets.Add(ticket); + } + } + + HashSet[] possibilities = new HashSet[validTickets[0].Length]; + for (int i = 0; i < possibilities.Length; i++) { + possibilities[i] = new HashSet(rules.Keys); + } + foreach (int[] ticket in validTickets) { + for (int i = 0; i < ticket.Length; i++) { + foreach (KeyValuePair rule in rules) { + if (!rule.Value[ticket[i]]) { + possibilities[i].Remove(rule.Key); + } + } + } + } + + Dictionary fieldNames = new Dictionary(20); + int[] order = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + Array.Sort(possibilities, order, Comparer>.Create((a, b) => a.Count - b.Count)); + for (int i = 0; i < possibilities.Length; i++) { + HashSet.Enumerator enumerator = possibilities[i].GetEnumerator(); + enumerator.MoveNext(); + string name = enumerator.Current; + fieldNames[name] = order[i]; + for (int j = i + 1; j < possibilities.Length; j++) { + possibilities[j].Remove(name); + } + } + + long product = 1; + foreach (string field in new[] { "departure location", "departure station", "departure platform", "departure track", "departure date", "departure time" }) { + product *= myTicket[fieldNames[field]]; + } + + Console.WriteLine(product); + return 0; + } +} diff --git a/Day17.cs b/Day17.cs new file mode 100644 index 0000000..5697416 --- /dev/null +++ b/Day17.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day17 { + //Speedup: Dictionary for getting new active squares - each square increments their neighbors (and decrements themselves) + + public static int Solution1() { + HashSet active = new HashSet(); + char[][] input = Array.ConvertAll(File.ReadAllLines("input17.txt"), line => line.ToCharArray()); + for (int x = 0; x < input[0].Length; x++) { + for (int y = 0; y < input.Length; y++) { + if (input[y][x] == '#') { + active.Add(new Vector3(x, y, 0)); + } + } + } + + for (int i = 0; i < 6; i++) { + HashSet toCheck = new HashSet(); + foreach (Vector3 coord in active) { + foreach (Vector3 neighbor in coord.GetAllNeighbors()) { + toCheck.Add(neighbor); + } + } + + HashSet newActive = new HashSet(); + foreach (Vector3 coord in toCheck) { + int count = 0; + + foreach (Vector3 neighbor in coord.GetAllNeighbors()) { + if (active.Contains(neighbor)) { + count++; + if (count > 4) { + break; + } + } + } + + if (count == 3 || count == 4 && active.Contains(coord)) { + newActive.Add(coord); + } + } + + active = newActive; + } + + return active.Count; + } + + public static int Solution2() { + HashSet active = new HashSet(); + char[][] input = Array.ConvertAll(File.ReadAllLines("input17.txt"), line => line.ToCharArray()); + for (int x = 0; x < input[0].Length; x++) { + for (int y = 0; y < input.Length; y++) { + if (input[y][x] == '#') { + active.Add(new Vector4(x, y, 0, 0)); + } + } + } + + for (int i = 0; i < 6; i++) { + HashSet toCheck = new HashSet(); + foreach (Vector4 coord in active) { + foreach (Vector4 neighbor in coord.GetAllNeighbors()) { + toCheck.Add(neighbor); + } + } + + HashSet newActive = new HashSet(); + foreach (Vector4 coord in toCheck) { + int count = 0; + + foreach (Vector4 neighbor in coord.GetAllNeighbors()) { + if (active.Contains(neighbor)) { + count++; + if (count > 4) { + break; + } + } + } + + if (count == 3 || count == 4 && active.Contains(coord)) { + newActive.Add(coord); + } + } + + active = newActive; + } + + return active.Count; + } + + struct Vector3 { + public readonly int x, y, z; + + public Vector3(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public IEnumerable GetAllNeighbors() { + int xTo = x + 1; + int yTo = y + 1; + int zTo = z + 1; + for (int x = this.x - 1; x <= xTo; x++) { + for (int y = this.y - 1; y <= yTo; y++) { + for (int z = this.z - 1; z <= zTo; z++) { + yield return new Vector3(x, y, z); + } + } + } + } + + public override int GetHashCode() => (x << 20) ^ (y << 10) ^ z; + } + + struct Vector4 { + public readonly int x, y, z, w; + + public Vector4(int x, int y, int z, int w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public IEnumerable GetAllNeighbors() { + int xTo = x + 1; + int yTo = y + 1; + int zTo = z + 1; + int wTo = w + 1; + for (int x = this.x - 1; x <= xTo; x++) { + for (int y = this.y - 1; y <= yTo; y++) { + for (int z = this.z - 1; z <= zTo; z++) { + for (int w = this.w - 1; w <= wTo; w++) { + yield return new Vector4(x, y, z, w); + } + } + } + } + } + + public override int GetHashCode() => (x << 24) ^ (y << 16) ^ (z << 8) ^ w; + } +} diff --git a/Day18.cs b/Day18.cs new file mode 100644 index 0000000..830f4c6 --- /dev/null +++ b/Day18.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day18 { + public static int Solution1() { + long sum = 0; + + foreach (string line in File.ReadLines("input18.txt")) { + Stack values = new Stack(); + Stack operators = new Stack(); + + foreach (char c in line) { + switch (c) { + case ' ': + break; + case '(': + operators.Push(c); + break; + case ')': + char op; + while ((op = operators.Pop()) != '(') { + values.Push(Calc(values.Pop(), values.Pop(), op)); + } + break; + case '+': + case '*': + if (operators.Count > 0 && operators.Peek() != '(') { + values.Push(Calc(values.Pop(), values.Pop(), operators.Pop())); + } + operators.Push(c); + break; + default: + values.Push(c - '0'); + break; + } + } + + while (operators.Count > 0) { + values.Push(Calc(values.Pop(), values.Pop(), operators.Pop())); + } + sum += values.Pop(); + } + + Console.WriteLine(sum); + return 0; + } + + public static int Solution2() { + long sum = 0; + + foreach (string line in File.ReadLines("input18.txt")) { + Stack values = new Stack(); + Stack operators = new Stack(); + + foreach (char c in line) { + switch (c) { + case ' ': + break; + case '(': + operators.Push(c); + break; + case ')': + char op; + while ((op = operators.Pop()) != '(') { + values.Push(Calc(values.Pop(), values.Pop(), op)); + } + break; + case '+': + if (operators.Count > 0 && operators.Peek() == '+') { + values.Push(Calc(values.Pop(), values.Pop(), operators.Pop())); + } + operators.Push(c); + break; + case '*': + while (operators.Count > 0 && operators.Peek() != '(') { + values.Push(Calc(values.Pop(), values.Pop(), operators.Pop())); + } + operators.Push(c); + break; + default: + values.Push(c - '0'); + break; + } + } + + while (operators.Count > 0) { + values.Push(Calc(values.Pop(), values.Pop(), operators.Pop())); + } + sum += values.Pop(); + } + + Console.WriteLine(sum); + return 0; + } + + static long Calc(long a, long b, char op) => op == '+' ? a + b : a * b; +} diff --git a/Day19.cs b/Day19.cs new file mode 100644 index 0000000..4d727e1 --- /dev/null +++ b/Day19.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +class Day19 { + public static int Solution1() => Solution((rules, line) => rules[0].Contains(line)); + + public static int Solution2() => Solution((rules, line) => { + int Count(int rule) { + int count = 0; + + while (true) { + bool matched = false; + foreach (string start in rules[rule]) { + if (line.StartsWith(start)) { + line = line.Substring(start.Length); + count++; + matched = true; + break; + } + } + if (!matched) { + return count; + } + } + } + + int count42 = Count(42); + if (count42 < 2) { + return false; + } + + int count31 = Count(31); + return count31 >= 1 && count42 > count31 && line.Length == 0; + }); + + static int Solution(Func>, string, bool> counter) { + string[] input = File.ReadAllText("input19.txt").Split(new[] { "\n\n" }, StringSplitOptions.None); + + Dictionary rules = new Dictionary(); + foreach (string[] rule in Array.ConvertAll(input[0].Split('\n'), rule => rule.Split(new[] { ": " }, StringSplitOptions.None))) { + rules[int.Parse(rule[0])] = rule[1] == "\"a\"" ? new[] { new int[] { -'a' } } : rule[1] == "\"b\"" ? new[] { new int[] { -'b' } } : Array.ConvertAll(rule[1].Split(new[] { " | " }, StringSplitOptions.None), result => Array.ConvertAll(result.Split(' '), int.Parse)); + } + + Dictionary> ruleResults = new Dictionary>(); + HashSet GetResult(int rule) { + if (!ruleResults.TryGetValue(rule, out HashSet result)) { + result = new HashSet(); + foreach (int[] option in rules[rule]) { + result.UnionWith(Array.ConvertAll(option, x => x < 0 ? new HashSet { new string((char)-x, 1) } : GetResult(x)).Aggregate(Combine)); + } + ruleResults[rule] = result; + } + return result; + } + + HashSet Combine(HashSet first, HashSet second) { + HashSet cartesian = new HashSet(); + + foreach (string a in first) { + foreach (string b in second) { + cartesian.Add(a + b); + } + } + + return cartesian; + } + + HashSet validWords = GetResult(0); + + int count = 0; + foreach (string line in input[1].Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)) { + if (counter(ruleResults, line)) { + count++; + } + } + + return count; + } +} diff --git a/Day2.cs b/Day2.cs new file mode 100644 index 0000000..d40f933 --- /dev/null +++ b/Day2.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.IO; + +class Day2 { + public static int Solution1() { + int answer = 0; + foreach ((int lowBound, int highBound, char c, string pass) in ReadInput()) { + int count = 0; + foreach (char letter in pass) { + if (letter == c) { + count++; + } + } + + if (lowBound <= count && highBound >= count) { + answer++; + } + } + + return answer; + } + + public static int Solution2() { + int answer = 0; + foreach ((int index1, int index2, char c, string pass) in ReadInput()) { + if (pass[index1 - 1] == c != (pass[index2 - 1] == c)) { + answer++; + } + } + + return answer; + } + + static IEnumerable<(int, int, char, string)> ReadInput() { + foreach (string line in File.ReadLines("input2.txt")) { + string[] parts = line.Split(' '); + string[] bounds = parts[0].Split('-'); + char c = parts[1][0]; + + yield return (int.Parse(bounds[0]), int.Parse(bounds[1]), c, parts[2]); + } + } +} diff --git a/Day20.cs b/Day20.cs new file mode 100644 index 0000000..ee48833 --- /dev/null +++ b/Day20.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day20 { + public static int Solution1() { + (int id, int orientation)[,] tiles = FitTiles(); + Console.WriteLine((long)tiles[0, 0].id * tiles[0, 11].id * tiles[11, 0].id * tiles[11, 11].id); + return 0; + } + + public static int Solution2() { + bool[,] Rotate(bool[,] image, int amount) { + int height = image.GetLength(0); + int width = image.GetLength(1); + bool[,] newImage = new bool[amount == 2 ? height : width, amount == 2 ? width : height]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (amount == 1) { + newImage[x, height - y - 1] = image[y, x]; + } else if (amount == 2) { + newImage[height - y - 1, width - x - 1] = image[y, x]; + } else if (amount == 3) { + newImage[width - x - 1, y] = image[y, x]; + } else { + throw new ArgumentException("Wrong rotation", nameof(amount)); + } + } + } + + return newImage; + } + + bool[,] Mirror(bool[,] image) { + int height = image.GetLength(0); + int width = image.GetLength(1); + bool[,] newImage = new bool[height, width]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + newImage[y, width - x - 1] = image[y, x]; + } + } + + return newImage; + } + + (int id, int orientation)[,] tileIDs = FitTiles(); + + Dictionary tiles = new Dictionary(); + foreach (string[] lines in Array.ConvertAll(File.ReadAllText("input20.txt").Split(new[] { "\n\n" }, StringSplitOptions.RemoveEmptyEntries), tile => tile.Split('\n'))) { + bool[,] tile = new bool[8, 8]; + + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) { + tile[y, x] = lines[y + 2][x + 1] == '#'; + } + } + + tiles[int.Parse(lines[0].Substring(5, 4))] = tile; + } + + bool[,] photo = new bool[8 * 12, 8 * 12]; + for (int tileY = 0; tileY < 12; tileY++) { + for (int tileX = 0; tileX < 12; tileX++) { + (int id, int orientation) = tileIDs[tileY, tileX]; + bool[,] tile = tiles[id]; + if (orientation > 3) { + tile = Mirror(tile); + } + if (orientation % 4 != 0) { + tile = Rotate(tile, orientation % 4); + } + + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) { + photo[tileY * 8 + y, tileX * 8 + x] = tile[y, x]; + } + } + } + } + + int FindSubImage(bool[,] subImage) { + bool[,] newImage = (bool[,])photo.Clone(); + int height = subImage.GetLength(0); + int width = subImage.GetLength(1); + + for (int y = 0; y < 8 * 12 - height; y++) { + for (int x = 0; x < 8 * 12 - width; x++) { + bool isMonster = true; + for (int yOffset = 0; yOffset < height; yOffset++) { + for (int xOffset = 0; xOffset < width; xOffset++) { + if (subImage[yOffset, xOffset] && !photo[y + yOffset, x + xOffset]) { + isMonster = false; + break; + } + } + if (!isMonster) { + break; + } + } + + if (isMonster) { + for (int yOffset = 0; yOffset < height; yOffset++) { + for (int xOffset = 0; xOffset < width; xOffset++) { + if (subImage[yOffset, xOffset]) { + newImage[y + yOffset, x + xOffset] = false; + } + } + } + } + } + } + + int count = 0; + foreach (bool square in newImage) { + if (square) { + count++; + } + } + + return count; + } + + bool[,] monster = new bool[3, 20] { + { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false }, + { true, false, false, false, false, true, true, false, false, false, false, true, true, false, false, false, false, true, true, true }, + { false, true, false, false, true, false, false, true, false, false, true, false, false, true, false, false, true, false, false, false } + }; + + return Math.Min(FindSubImage(monster), + Math.Min(FindSubImage(Rotate(monster, 1)), + Math.Min(FindSubImage(Rotate(monster, 2)), + Math.Min(FindSubImage(Rotate(monster, 3)), + Math.Min(FindSubImage(Mirror(monster)), + Math.Min(FindSubImage(Rotate(Mirror(monster), 1)), + Math.Min(FindSubImage(Rotate(Mirror(monster), 2)), + FindSubImage(Rotate(Mirror(monster), 3))))))))); + } + + static (int, int)[,] FitTiles() { + int Mirror(int x) => + ((x & 1) << 9) + | ((x & 2) << 7) + | ((x & 4) << 5) + | ((x & 8) << 3) + | ((x & 16) << 1) + | ((x & 32) >> 1) + | ((x & 64) >> 3) + | ((x & 128) >> 5) + | ((x & 256) >> 7) + | ((x & 512) >> 9); + + List> tiles = new List>(); + foreach (string[] lines in Array.ConvertAll(File.ReadAllText("input20.txt").Split(new[] { "\n\n" }, StringSplitOptions.RemoveEmptyEntries), tile => tile.Split('\n'))) { + int[][] orientations = new int[8][]; + + int[] sides = new int[4]; + for (int i = 0; i < 10; i++) { + if (lines[1][i] == '#') { + sides[0] |= 1 << i; + } + if (lines[i + 1][9] == '#') { + sides[1] |= 1 << i; + } + if (lines[10][i] == '#') { + sides[2] |= 512 >> i; + } + if (lines[i + 1][0] == '#') { + sides[3] |= 512 >> i; + } + } + orientations[0] = sides; + orientations[1] = new[] { sides[3], sides[0], sides[1], sides[2] }; + orientations[2] = new[] { sides[2], sides[3], sides[0], sides[1] }; + orientations[3] = new[] { sides[1], sides[2], sides[3], sides[0] }; + + sides = new[] { Mirror(sides[0]), Mirror(sides[3]), Mirror(sides[2]), Mirror(sides[1]) }; + orientations[4] = sides; + orientations[5] = new[] { sides[3], sides[0], sides[1], sides[2] }; + orientations[6] = new[] { sides[2], sides[3], sides[0], sides[1] }; + orientations[7] = new[] { sides[1], sides[2], sides[3], sides[0] }; + + int[] sides2 = Array.ConvertAll(sides, Mirror); + + tiles.Add(new KeyValuePair(int.Parse(lines[0].Substring(5, 4)), orientations)); + } + + HashSet placedTiles = new HashSet(); + int[,][] image = new int[12, 12][]; + (int, int)[,] imageTiles = new (int, int)[12, 12]; + bool PlaceTile(int row, int col) { + if (row == 12) { + return true; + } + + foreach (KeyValuePair tile in tiles) { + if (placedTiles.Contains(tile.Key)) { + continue; + } + + for (int i = 0; i < 8; i++) { + int[] orientation = tile.Value[i]; + if (row > 0 && image[row - 1, col][2] != Mirror(orientation[0]) || col > 0 && image[row, col - 1][1] != Mirror(orientation[3])) { + continue; + } + + placedTiles.Add(tile.Key); + image[row, col] = orientation; + imageTiles[row, col] = (tile.Key, i); + if (PlaceTile(row + (col == 11 ? 1 : 0), (col + 1) % 12)) { + return true; + } + placedTiles.Remove(tile.Key); + } + } + + return false; + } + + PlaceTile(0, 0); + return imageTiles; + } +} diff --git a/Day21.cs b/Day21.cs new file mode 100644 index 0000000..18d14be --- /dev/null +++ b/Day21.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day21 { + public static int Solution1() { + Dictionary allIngredients = new Dictionary(); + Dictionary> allergenMap = new Dictionary>(); + + foreach (string line in File.ReadLines("input21.txt")) { + int allergenIndex = line.IndexOf('('); + string[] allergens = line.Substring(allergenIndex + 10, line.Length - allergenIndex - 11).Split(new[] { ", " }, StringSplitOptions.None); + string[] ingredients = line.Substring(0, allergenIndex - 1).Split(' '); + + foreach (string ingredient in ingredients) { + allIngredients[ingredient] = allIngredients.TryGetValue(ingredient, out int count) ? count + 1 : 1; + } + + foreach (string allergen in allergens) { + if (allergenMap.TryGetValue(allergen, out HashSet possibilities)) { + possibilities.IntersectWith(ingredients); + } else { + allergenMap[allergen] = new HashSet(ingredients); + } + } + } + + foreach (HashSet possibilities in allergenMap.Values) { + foreach (string possibility in possibilities) { + allIngredients.Remove(possibility); + } + } + + int total = 0; + foreach (int amount in allIngredients.Values) { + total += amount; + } + + return total; + } + + public static int Solution2() { + Dictionary> allergenMap = new Dictionary>(); + + foreach (string line in File.ReadLines("input21.txt")) { + int allergenIndex = line.IndexOf('('); + string[] allergens = line.Substring(allergenIndex + 10, line.Length - allergenIndex - 11).Split(new[] { ", " }, StringSplitOptions.None); + string[] ingredients = line.Substring(0, allergenIndex - 1).Split(' '); + + foreach (string allergen in allergens) { + if (allergenMap.TryGetValue(allergen, out HashSet possibilities)) { + possibilities.IntersectWith(ingredients); + } else { + allergenMap[allergen] = new HashSet(ingredients); + } + } + } + + while (true) { + bool changed = false; + foreach (HashSet possibilities in allergenMap.Values) { + if (possibilities.Count == 1) { + foreach (HashSet other in allergenMap.Values) { + if (other.Count != 1) { + other.ExceptWith(possibilities); + changed = true; + } + } + } + } + + if (!changed) { + break; + } + } + + string[] finalAllergens = new string[allergenMap.Count]; + allergenMap.Keys.CopyTo(finalAllergens, 0); + string[] finalIngredients = new string[allergenMap.Count]; + for (int i = 0; i < finalAllergens.Length; i++) { + HashSet.Enumerator ingredient = allergenMap[finalAllergens[i]].GetEnumerator(); + ingredient.MoveNext(); + finalIngredients[i] = ingredient.Current; + } + + Array.Sort(finalAllergens, finalIngredients); + Console.WriteLine(string.Join(",", finalIngredients)); + + return 0; + } +} diff --git a/Day22.cs b/Day22.cs new file mode 100644 index 0000000..9bcaaff --- /dev/null +++ b/Day22.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +class Day22 { + public static int Solution1() { + GetDecks(out Queue deck1, out Queue deck2); + + while (deck1.Count > 0 && deck2.Count > 0) { + int card1 = deck1.Dequeue(); + int card2 = deck2.Dequeue(); + + if (card1 > card2) { + deck1.Enqueue(card1); + deck1.Enqueue(card2); + } else { + deck2.Enqueue(card2); + deck2.Enqueue(card1); + } + } + + int sum = 0; + Queue winningDeck = deck1.Count > 0 ? deck1 : deck2; + while (winningDeck.Count > 0) { + sum += winningDeck.Count * winningDeck.Dequeue(); + } + + return sum; + } + + public static int Solution2() { + GetDecks(out Queue deck1, out Queue deck2); + PlayGame(deck1, deck2); + + int sum = 0; + Queue winningDeck = deck1.Count > 0 ? deck1 : deck2; + while (winningDeck.Count > 0) { + sum += winningDeck.Count * winningDeck.Dequeue(); + } + + return sum; + } + + static void GetDecks(out Queue deck1, out Queue deck2) { + string[][] playerInputs = Array.ConvertAll(File.ReadAllText("input22.txt").Split(new[] { "\n\n" }, StringSplitOptions.None), input => input.Substring(10).Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)); + deck1 = new Queue(Array.ConvertAll(playerInputs[0], int.Parse)); + deck2 = new Queue(Array.ConvertAll(playerInputs[1], int.Parse)); + } + + static bool PlayGame(Queue deck1, Queue deck2) { + HashSet stateMemory = new HashSet(); + + while (deck1.Count > 0 && deck2.Count > 0) { + if (!stateMemory.Add(new GameState(deck1.ToArray(), deck2.ToArray()))) { + return true; + } + + int card1 = deck1.Dequeue(); + int card2 = deck2.Dequeue(); + + bool winnerIs1 = deck1.Count >= card1 && deck2.Count >= card2 ? PlayGame(new Queue(deck1.Take(card1)), new Queue(deck2.Take(card2))) : card1 > card2; + + if (winnerIs1) { + deck1.Enqueue(card1); + deck1.Enqueue(card2); + } else { + deck2.Enqueue(card2); + deck2.Enqueue(card1); + } + } + + return deck1.Count > 0; + } + + class GameState : IEquatable { + readonly int[] deck1, deck2; + + public GameState(int[] deck1, int[] deck2) { + this.deck1 = deck1; + this.deck2 = deck2; + } + + public override bool Equals(object other) => other is GameState state && Equals(state); + public bool Equals(GameState other) => deck1.Length == other.deck1.Length && deck1.SequenceEqual(other.deck1) && deck2.SequenceEqual(other.deck2); + + public override int GetHashCode() { + int hash = deck1.Length; + for (int i = 0; i < deck1.Length; i++) { + hash ^= deck1[i] << (i % 30); + } + return hash; + } + } +} diff --git a/Day23.cs b/Day23.cs new file mode 100644 index 0000000..5861ccc --- /dev/null +++ b/Day23.cs @@ -0,0 +1,86 @@ +using System; + +class Day23 { + const string input = "198753462"; + + public static int Solution1() { + int[] cups = Array.ConvertAll(input.ToCharArray(), c => c - '0'); + int[] carry = new int[4]; + + for (int i = 0; i < 100; i++) { + Array.Copy(cups, carry, 4); + int search = cups[0]; + do { + search -= 1; + if (search == 0) { + search = 9; + } + } while (Array.Exists(carry, c => c == search)); + int dst = Array.IndexOf(cups, search); + int count = dst - 3; + Array.Copy(cups, 4, cups, 0, count); + Array.Copy(carry, 1, cups, count, 3); + Array.Copy(cups, dst + 1, cups, dst, 8 - dst); + cups[8] = carry[0]; + } + + int answer = 0; + int start = Array.IndexOf(cups, 1); + for (int i = 1; i <= 8; i++) { + answer = answer * 10 + cups[(start + i) % 9]; + } + return answer; + } + + public static int Solution2() { + Node[] nodes = new Node[1000001]; + for (int i = 1; i <= 1000000; i++) { + nodes[i] = new Node(i); + } + + Node first = nodes[input[0] - '0']; + Node last = first; + for (int i = 1; i < 9; i++) { + last.next = nodes[input[i] - '0']; + last = last.next; + } + for (int i = 10; i <= 1000000; i++) { + last.next = nodes[i]; + last = last.next; + } + last.next = first; + + for (int i = 0; i < 10000000; i++) { + Node carryStart = first.next; + Node carryEnd = carryStart.next.next; + + int dstVal = first.value; + do { + dstVal -= 1; + if (dstVal == 0) { + dstVal = 1000000; + } + } while (carryStart.value == dstVal || carryStart.next.value == dstVal || carryEnd.value == dstVal); + Node dst = nodes[dstVal]; + + Node dstNext = dst.next; + dst.next = carryStart; + first.next = carryEnd.next; + carryEnd.next = dstNext; + + first = first.next; + } + + Console.WriteLine((long)nodes[1].next.value * nodes[1].next.next.value); + return 0; + } + + class Node { + public readonly T value; + public Node next; + + public Node(T value) { + this.value = value; + } + } +} diff --git a/Day24.cs b/Day24.cs new file mode 100644 index 0000000..542dfcd --- /dev/null +++ b/Day24.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.IO; + +class Day24 { + public static int Solution1() => GetTiles().Count; + + public static int Solution2() { + IEnumerable<(int, int)> AdjacentTiles(int x, int y) { + yield return (x - 1, y); + yield return (x + 1, y); + yield return (x, y - 1); + yield return (x + 1, y - 1); + yield return (x - 1, y + 1); + yield return (x, y + 1); + } + + HashSet<(int, int)> tiles = GetTiles(); + + for (int i = 0; i < 100; i++) { + HashSet<(int, int)> tilesToCheck = new HashSet<(int, int)>(); + foreach ((int x, int y) in tiles) { + foreach ((int, int) tile in AdjacentTiles(x, y)) { + tilesToCheck.Add(tile); + } + } + + HashSet<(int, int)> newTiles = new HashSet<(int, int)>(); + foreach ((int x, int y) tile in tilesToCheck) { + int adjacentBlacks = 0; + foreach ((int, int) adjacent in AdjacentTiles(tile.x, tile.y)) { + if (tiles.Contains(adjacent)) { + adjacentBlacks++; + } + } + if (adjacentBlacks == 2 || tiles.Contains(tile) && adjacentBlacks == 1) { + newTiles.Add(tile); + } + } + tiles = newTiles; + } + + return tiles.Count; + } + + static HashSet<(int, int)> GetTiles() { + Dictionary<(int, int), bool> tiles = new Dictionary<(int, int), bool>(); + + foreach (string line in File.ReadLines("input24.txt")) { + int x = 0; + int y = 0; + for (int i = 0; i < line.Length; i++) { + if (line[i] == 'e') { + x++; + } else if (line[i] == 'w') { + x--; + } else if (line[i] == 'n') { + y++; + if (line[++i] == 'w') { + x--; + } + } else { + y--; + if (line[++i] == 'e') { + x++; + } + } + } + + tiles[(x, y)] = !(tiles.TryGetValue((x, y), out bool isBlack) && isBlack); + } + + HashSet<(int, int)> blackTiles = new HashSet<(int, int)>(); + foreach (KeyValuePair<(int, int), bool> tile in tiles) { + if (tile.Value) { + blackTiles.Add(tile.Key); + } + } + + return blackTiles; + } +} diff --git a/Day25.cs b/Day25.cs new file mode 100644 index 0000000..8e510c1 --- /dev/null +++ b/Day25.cs @@ -0,0 +1,32 @@ +using System; +using System.IO; + +class Day25 { + public static int Solution1() { + string[] keys = File.ReadAllLines("input25.txt"); + int key1 = int.Parse(keys[0]); + int key2 = int.Parse(keys[1]); + int loopSize; + + int value = 1; + int loops = 0; + while (true) { + loops++; + value = value * 7 % 20201227; + if (value == key2) { + loopSize = loops; + break; + } + } + + long keyEnc = 1; + for (int i = 0; i < loopSize; i++) { + keyEnc = keyEnc * key1 % 20201227; + } + + Console.WriteLine(keyEnc); + return 0; + } + + public static int Solution2() => -1; +} diff --git a/Day3.cs b/Day3.cs new file mode 100644 index 0000000..bdcca2b --- /dev/null +++ b/Day3.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.IO; + +class Day3 { + public static int Solution1() => Solution(File.ReadAllLines("input3.txt"), 3); + + public static int Solution2() { + string[] map = File.ReadAllLines("input3.txt"); + + int answer = Solution(map, 1, 1); + foreach (int increment in new[] { 1, 3, 5, 7 }) { + answer *= Solution(map, increment); + } + + return answer; + } + + static int Solution(IEnumerable map, int increment, int alternate = 0) { + int pos = 0; + int answer = 0; + + foreach (string line in map) { + if (alternate > 0) { + if (alternate++ % 2 == 0) { + continue; + } + } + + if (line[pos] == '#') { + answer++; + } + pos = (pos + increment) % line.Length; + } + + return answer; + } +} diff --git a/Day4.cs b/Day4.cs new file mode 100644 index 0000000..4e3e797 --- /dev/null +++ b/Day4.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day4 { + static readonly Dictionary> requiredFields = new Dictionary> { + { "byr", value => int.TryParse(value, out int year) && year >= 1920 && year <= 2002 }, + { "iyr", value => int.TryParse(value, out int year) && year >= 2010 && year <= 2020 }, + { "eyr", value => int.TryParse(value, out int year) && year >= 2020 && year <= 2030 }, + { "hgt", height => height.EndsWith("cm") && int.TryParse(height.Substring(0, height.Length - 2), out int value) && value >= 150 && value <= 193 || height.EndsWith("in") && int.TryParse(height.Substring(0, height.Length - 2), out value) && value >= 59 && value <= 76 }, + { "hcl", color => color.Length == 7 && color.TrimEnd('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') == "#" }, + { "ecl", color => color == "amb" || color == "blu" || color == "brn" || color == "gry" || color == "grn" || color == "hzl" || color == "oth" }, + { "pid", id => id.Length == 9 && int.TryParse(id, out _) } + }; + + public static int Solution1() => Solution((passport, fieldName) => passport.ContainsKey(fieldName)); + + public static int Solution2() => Solution((passport, fieldName) => passport.ContainsKey(fieldName) && requiredFields[fieldName](passport[fieldName])); + + static int Solution(Func, string, bool> validate) { + int answer = 0; + + foreach (string passport in File.ReadAllText("input4.txt").Split(new[] { "\n\n" }, StringSplitOptions.None)) { + Dictionary fields = new Dictionary(); + foreach (string field in passport.Split(new[] { ' ', '\n' }, StringSplitOptions.RemoveEmptyEntries)) { + string[] keyValue = field.Split(':'); + fields[keyValue[0]] = keyValue[1]; + } + + bool isValid = true; + foreach (string fieldName in requiredFields.Keys) { + if (!validate(fields, fieldName)) { + isValid = false; + break; + } + } + + if (isValid) { + answer++; + } + } + + return answer; + } +} diff --git a/Day5.cs b/Day5.cs new file mode 100644 index 0000000..a1a5ddc --- /dev/null +++ b/Day5.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.IO; + +class Day5 { + public static int Solution1() { + int max = 0; + + foreach (int id in GetIDs()) { + if (id > max) { + max = id; + } + } + + return max; + } + + public static int Solution2() { + int min = int.MaxValue; + int max = 0; + int sum = 0; + + foreach (int id in GetIDs()) { + if (id > max) { + max = id; + } else if (id < min) { + min = id; + } + sum += id; + } + + return (max + min) * (max - min + 1) / 2 - sum; + } + + static IEnumerable GetIDs() { + foreach (string seat in File.ReadLines("input5.txt")) { + int id = 0; + + for (int i = 0; i < 7; i++) { + if (seat[i] == 'B') { + id |= 1 << (9 - i); + } + } + + for (int i = 7; i < 10; i++) { + if (seat[i] == 'R') { + id |= 1 << (9 - i); + } + } + + yield return id; + } + } +} diff --git a/Day6.cs b/Day6.cs new file mode 100644 index 0000000..de4b5f9 --- /dev/null +++ b/Day6.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day6 { + public static int Solution1() => Solution(new HashSet(), (answers, person) => answers.UnionWith(person)); + + public static int Solution2() => Solution(new HashSet("abcdefghijklmnopqrstuvwxyz"), (answers, person) => answers.IntersectWith(person)); + + static int Solution(HashSet startSet, Action, string> setOp) { + int answer = 0; + + foreach (string group in File.ReadAllText("input6.txt").Split(new[] { "\n\n" }, StringSplitOptions.None)) { + HashSet answers = new HashSet(startSet); + foreach (string person in group.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)) { + setOp(answers, person); + } + answer += answers.Count; + } + + return answer; + } +} diff --git a/Day7.cs b/Day7.cs new file mode 100644 index 0000000..59a1d55 --- /dev/null +++ b/Day7.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.IO; + +class Day7 { + public static int Solution1() { + Dictionary rules = GetRules(); + Dictionary> reverseRules = new Dictionary>(); + + foreach (KeyValuePair rule in rules) { + foreach ((string content, _) in rule.Value) { + if (reverseRules.TryGetValue(content, out List containers)) { + containers.Add(rule.Key); + } else { + reverseRules[content] = new List { rule.Key }; + } + } + } + + HashSet potentialContainers = new HashSet(); + Stack checkStack = new Stack(); + checkStack.Push("shiny gold"); + + while (checkStack.Count > 0) { + string bag = checkStack.Pop(); + foreach (string container in reverseRules.TryGetValue(bag, out List containers) ? containers : (IEnumerable)new string[0]) { + if (potentialContainers.Add(container)) { + checkStack.Push(container); + } + } + } + + return potentialContainers.Count; + } + + public static int Solution2() { + Dictionary rules = GetRules(); + + int answer = 0; + + Dictionary toCheck = new Dictionary { { "shiny gold", 1 } }; + while (toCheck.Count > 0) { + Dictionary.Enumerator enumerator = toCheck.GetEnumerator(); + enumerator.MoveNext(); + string container = enumerator.Current.Key; + int containerCount = enumerator.Current.Value; + toCheck.Remove(container); + + foreach ((string bag, int count) in rules[container]) { + int additionalCount = count * containerCount; + toCheck[bag] = additionalCount + (toCheck.TryGetValue(bag, out int currentCount) ? currentCount : 0); + answer += additionalCount; + } + } + + return answer; + } + + static Dictionary GetRules() { + Dictionary rules = new Dictionary(); + + foreach (string line in File.ReadLines("input7.txt")) { + string[] rule = line.Split(new[] { " bags contain " }, StringSplitOptions.None); + + rules[rule[0]] = rule[1] == "no other bags." ? new (string, int)[0] : Array.ConvertAll(rule[1].Split(new[] { ", " }, StringSplitOptions.None), bag => { + string[] pieces = bag.Split(' '); + return ($"{pieces[1]} {pieces[2]}", int.Parse(pieces[0])); + }); + } + + return rules; + } +} diff --git a/Day8.cs b/Day8.cs new file mode 100644 index 0000000..8d49a79 --- /dev/null +++ b/Day8.cs @@ -0,0 +1,88 @@ +using System; +using System.IO; + +class Day8 { + enum OpCode { + nop, + acc, + jmp + } + + public static int Solution1() { + LoadProgram(out OpCode[] opCodes, out int[] opValues); + return -Run(0, 0, opCodes, opValues, new bool[opCodes.Length], -1); + } + + public static int Solution2() { + int acc = 0; + int ptr = 0; + LoadProgram(out OpCode[] opCodes, out int[] opValues); + bool[] visited = new bool[opCodes.Length]; + + while (true) { + if (opCodes[ptr] == OpCode.nop || opCodes[ptr] == OpCode.jmp) { + int result = Run(acc, ptr, opCodes, opValues, (bool[])visited.Clone(), ptr); + if (result >= 0) { + return result; + } + } + + visited[ptr] = true; + switch (opCodes[ptr]) { + case OpCode.nop: + ptr++; + break; + case OpCode.acc: + acc += opValues[ptr]; + ptr++; + break; + case OpCode.jmp: + ptr += opValues[ptr]; + break; + } + } + } + + static void LoadProgram(out OpCode[] opCodes, out int[] opValues) { + string[] input = File.ReadAllLines("input8.txt"); + opCodes = new OpCode[input.Length]; + opValues = new int[input.Length]; + + for (int i = 0; i < input.Length; i++) { + string[] command = input[i].Split(' '); + Enum.TryParse(command[0], out opCodes[i]); + opValues[i] = int.Parse(command[1]); + } + } + + static int Run(int acc, int ptr, OpCode[] opCodes, int[] opValues, bool[] visited, int opSwap) { + if (opSwap >= 0) { + opCodes[opSwap] = 2 - opCodes[opSwap]; + } + + while (ptr < opCodes.Length) { + if (visited[ptr]) { + if (opSwap >= 0) { + opCodes[opSwap] = 2 - opCodes[opSwap]; + } + return -acc; + } + visited[ptr] = true; + + switch (opCodes[ptr]) { + case OpCode.nop: + ptr++; + break; + case OpCode.acc: + acc += opValues[ptr]; + ptr++; + break; + case OpCode.jmp: + ptr += opValues[ptr]; + break; + } + } + + return acc; + } +} diff --git a/Day9.cs b/Day9.cs new file mode 100644 index 0000000..d2dc965 --- /dev/null +++ b/Day9.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using System.IO; + +class Day9 { + public static int Solution1() { + StreamReader input = File.OpenText("input9.txt"); + Queue previous = new Queue(25); + Dictionary sums = new Dictionary(); + + for (int i = 0; i < 25; i++) { + int next = int.Parse(input.ReadLine()); + + foreach (int existing in previous) { + int sum = existing + next; + sums[sum] = sums.TryGetValue(sum, out int count) ? count + 1 : 1; + } + + previous.Enqueue(next); + } + + while (true) { + int next = int.Parse(input.ReadLine()); + if (!sums.ContainsKey(next)) { + input.Close(); + return next; + } + + int first = previous.Dequeue(); + foreach (int existing in previous) { + int sum = existing + next; + sums[sum] = sums.TryGetValue(sum, out int count) ? count + 1 : 1; + + sum = first + existing; + count = sums[sum]; + if (count > 1) { + sums[sum] = count - 1; + } else { + sums.Remove(sum); + } + } + previous.Enqueue(next); + } + } + + public static int Solution2() { + const int target = 258585477; + Queue window = new Queue(); + int sum = 0; + + foreach (string line in File.ReadLines("input9.txt")) { + int n = int.Parse(line); + window.Enqueue(n); + sum += n; + + while (sum > target) { + n = window.Dequeue(); + sum -= n; + } + + if (sum == target) { + int smallest = int.MaxValue; + int largest = int.MinValue; + foreach (int num in window) { + if (num > largest) { + largest = num; + } else if (num < smallest) { + smallest = num; + } + } + return smallest + largest; + } + } + + return -1; + } +} diff --git a/Picker.cs b/Picker.cs new file mode 100644 index 0000000..a276e34 --- /dev/null +++ b/Picker.cs @@ -0,0 +1,28 @@ +using System; + +class Picker { + static readonly Func[] solutions = new Func[] { Day1.Solution1, Day1.Solution2, Day2.Solution1, Day2.Solution2, Day3.Solution1, Day3.Solution2, Day4.Solution1, Day4.Solution2, Day5.Solution1, Day5.Solution2, Day6.Solution1, Day6.Solution2, Day7.Solution1, Day7.Solution2, Day8.Solution1, Day8.Solution2, Day9.Solution1, Day9.Solution2, Day10.Solution1, Day10.Solution2, Day11.Solution1, Day11.Solution2, Day12.Solution1, Day12.Solution2, Day13.Solution1, Day13.Solution2, Day14.Solution1, Day14.Solution2, Day15.Solution1, Day15.Solution2, Day16.Solution1, Day16.Solution2, Day17.Solution1, Day17.Solution2, Day18.Solution1, Day18.Solution2, Day19.Solution1, Day19.Solution2, Day20.Solution1, Day20.Solution2, Day21.Solution1, Day21.Solution2, Day22.Solution1, Day22.Solution2, Day23.Solution1, Day23.Solution2, Day24.Solution1, Day24.Solution2, Day25.Solution1, Day25.Solution2 }; + + static void Main(string[] args) { + int i; + if (args.Length > 0) { + i = int.Parse(args[0]); + } else { + Console.WriteLine("Enter day:"); + i = int.Parse(Console.ReadLine()); + } + i = (i - 1) * 2; + + if (i < 0) { + foreach (Func solution in solutions) { + Console.WriteLine(solution()); + } + return; + } + + Console.WriteLine(solutions[i++]()); + if (solutions.Length > i) { + Console.WriteLine(solutions[i]()); + } + } +} \ No newline at end of file