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