AIO commit
Sorry for the late commit, forgot to commit and push it on Saturday 7th. Thank god there are moodle reminders
This commit is contained in:
parent
3083dbce7f
commit
2e6b418fef
24 changed files with 5790 additions and 1 deletions
22
AdventOfCode/AdventOfCode.Test/Day1Test.cs
Normal file
22
AdventOfCode/AdventOfCode.Test/Day1Test.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
namespace AdventOfCode.Test;
|
||||
|
||||
public sealed class Day1Test
|
||||
{
|
||||
private string _input = "3 4\n4 3\n2 5\n1 3\n3 9\n3 3";
|
||||
|
||||
[Fact]
|
||||
public void Part1()
|
||||
{
|
||||
var day1 = new Day1();
|
||||
var result = day1.Part1(_input);
|
||||
Assert.Equal(11, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Part2()
|
||||
{
|
||||
var day1 = new Day1();
|
||||
var result = day1.Part2(_input);
|
||||
Assert.Equal(31, result);
|
||||
}
|
||||
}
|
||||
22
AdventOfCode/AdventOfCode.Test/Day2Test.cs
Normal file
22
AdventOfCode/AdventOfCode.Test/Day2Test.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
namespace AdventOfCode.Test;
|
||||
|
||||
public sealed class Day2Test
|
||||
{
|
||||
private string _input = "7 6 4 2 1\n1 2 7 8 9\n9 7 6 2 1\n1 3 2 4 5\n8 6 4 4 1\n1 3 6 7 9";
|
||||
|
||||
[Fact]
|
||||
public void Part1()
|
||||
{
|
||||
var day2 = new Day2();
|
||||
var result = day2.Part1(_input);
|
||||
Assert.Equal(2, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Part2()
|
||||
{
|
||||
var day2 = new Day2();
|
||||
var result = day2.Part2(_input);
|
||||
Assert.Equal(4, result);
|
||||
}
|
||||
}
|
||||
28
AdventOfCode/AdventOfCode.Test/Day3Test.cs
Normal file
28
AdventOfCode/AdventOfCode.Test/Day3Test.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
namespace AdventOfCode.Test;
|
||||
|
||||
public sealed class Day3Test
|
||||
{
|
||||
[Fact]
|
||||
public void Part1()
|
||||
{
|
||||
const string Input = "xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))";
|
||||
const int Expected = 161;
|
||||
var day3 = new Day3();
|
||||
|
||||
var result = day3.Part1(Input);
|
||||
|
||||
Assert.Equal(Expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Part2()
|
||||
{
|
||||
const string Input = "xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))";
|
||||
const int Expected = 48;
|
||||
var day3 = new Day3();
|
||||
|
||||
var result = day3.Part2(Input);
|
||||
|
||||
Assert.Equal(Expected, result);
|
||||
}
|
||||
}
|
||||
28
AdventOfCode/AdventOfCode.Test/Day4Test.cs
Normal file
28
AdventOfCode/AdventOfCode.Test/Day4Test.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
namespace AdventOfCode.Test;
|
||||
|
||||
public sealed class Day4Test
|
||||
{
|
||||
[Fact]
|
||||
public void Part1()
|
||||
{
|
||||
const string Input = "MMMSXXMASM\nMSAMXMSMSA\nAMXSXMAAMM\nMSAMASMSMX\nXMASAMXAMM\nXXAMMXXAMA\nSMSMSASXSS\nSAXAMASAAA\nMAMMMXMMMM\nMXMXAXMASX";
|
||||
const int Expected = 18;
|
||||
var day4 = new Day4();
|
||||
|
||||
var result = day4.Part1(Input);
|
||||
|
||||
Assert.Equal(Expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Part2()
|
||||
{
|
||||
const string Input = ".M.S......\n..A..MSMS.\n.M.S.MAA..\n..A.ASMSM.\n.M.S.M....\n..........\nS.S.S.S.S.\n.A.A.A.A..\nM.M.M.M.M.\n..........";
|
||||
const int Expected = 9;
|
||||
var day4 = new Day4();
|
||||
|
||||
var result = day4.Part2(Input);
|
||||
|
||||
Assert.Equal(Expected, result);
|
||||
}
|
||||
}
|
||||
29
AdventOfCode/AdventOfCode.Test/Day5Test.cs
Normal file
29
AdventOfCode/AdventOfCode.Test/Day5Test.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
namespace AdventOfCode.Test;
|
||||
|
||||
public sealed class Day5Test
|
||||
{
|
||||
private string input
|
||||
= "47|53\r\n97|13\r\n97|61\r\n97|47\r\n75|29\r\n61|13\r\n75|53\r\n29|13\r\n97|29\r\n53|29\r\n61|53\r\n97|53\r\n61|29\r\n47|13\r\n75|47\r\n97|75\r\n47|61\r\n75|61\r\n47|29\r\n75|13\r\n53|13\r\n\r\n75,47,61,53,29\r\n97,61,53,29,13\r\n75,29,13\r\n75,97,47,61,53\r\n61,13,29\r\n97,13,75,29,47";
|
||||
|
||||
[Fact]
|
||||
public void Part1()
|
||||
{
|
||||
const int Expected = 143;
|
||||
var day5 = new Day5();
|
||||
|
||||
var result = day5.Part1(input);
|
||||
|
||||
Assert.Equal(Expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Part2()
|
||||
{
|
||||
const int Expected = 123;
|
||||
var day5 = new Day5();
|
||||
|
||||
var result = day5.Part2(input);
|
||||
|
||||
Assert.Equal(Expected, result);
|
||||
}
|
||||
}
|
||||
28
AdventOfCode/AdventOfCode.Test/Day6Test.cs
Normal file
28
AdventOfCode/AdventOfCode.Test/Day6Test.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
namespace AdventOfCode.Test;
|
||||
|
||||
public sealed class Day6Test
|
||||
{
|
||||
private const string Input = "....#.....\r\n.........#\r\n..........\r\n..#.......\r\n.......#..\r\n..........\r\n.#..^.....\r\n........#.\r\n#.........\r\n......#...";
|
||||
|
||||
[Fact]
|
||||
public void Part1()
|
||||
{
|
||||
const int Expected = 41;
|
||||
var day = new Day6();
|
||||
|
||||
var result = day.Part1(Input);
|
||||
|
||||
Assert.Equal(Expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Part2()
|
||||
{
|
||||
const int Expected = 6;
|
||||
var day = new Day6();
|
||||
|
||||
var result = day.Part2(Input);
|
||||
|
||||
Assert.Equal(Expected, result);
|
||||
}
|
||||
}
|
||||
24
AdventOfCode/AdventOfCode.Test/Day7Test.cs
Normal file
24
AdventOfCode/AdventOfCode.Test/Day7Test.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
namespace AdventOfCode.Test;
|
||||
|
||||
public sealed class Day7Test
|
||||
{
|
||||
private string input = "190: 10 19\r\n3267: 81 40 27\r\n83: 17 5\r\n156: 15 6\r\n7290: 6 8 6 15\r\n161011: 16 10 13\r\n192: 17 8 14\r\n21037: 9 7 18 13\r\r\n292: 11 6 16 20";
|
||||
|
||||
[Fact]
|
||||
public void TestPart1()
|
||||
{
|
||||
const int Expected = 3749;
|
||||
|
||||
var day = new Day7();
|
||||
Assert.Equal(Expected, day.Part1(input));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestPart2()
|
||||
{
|
||||
const int Expected = 11387;
|
||||
|
||||
var day = new Day7();
|
||||
Assert.Equal(Expected, day.Part2(input));
|
||||
}
|
||||
}
|
||||
83
AdventOfCode/AdventOfCode/Day1.cs
Normal file
83
AdventOfCode/AdventOfCode/Day1.cs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
namespace AdventOfCode;
|
||||
|
||||
public sealed class Day1 : IDay
|
||||
{
|
||||
public long Part1(string input)
|
||||
{
|
||||
var data = ParseInput(input);
|
||||
var leftList = data[0];
|
||||
var rightList = data[1];
|
||||
|
||||
Array.Sort(leftList);
|
||||
Array.Sort(rightList);
|
||||
|
||||
int totalDistance = 0;
|
||||
for (int i = 0; i < Math.Min(leftList.Length, rightList.Length); i++)
|
||||
{
|
||||
totalDistance += Math.Abs(leftList[i] - rightList[i]);
|
||||
}
|
||||
|
||||
return totalDistance;
|
||||
}
|
||||
|
||||
public long Part2(string input)
|
||||
{
|
||||
var data = ParseInput(input);
|
||||
var leftList = data[0];
|
||||
var rightList = data[1];
|
||||
|
||||
var uniqueNumbers = new FlexArrayInt();
|
||||
var counts = new FlexArrayInt();
|
||||
|
||||
foreach (var num in rightList)
|
||||
{
|
||||
if (uniqueNumbers.Contains(num))
|
||||
{
|
||||
for (int i = 0; i < uniqueNumbers.Count; i++)
|
||||
{
|
||||
if (uniqueNumbers[i] == num)
|
||||
{
|
||||
counts[i] += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uniqueNumbers.Add(num);
|
||||
counts.Add(1);
|
||||
}
|
||||
}
|
||||
|
||||
int similarityScore = 0;
|
||||
foreach (var num in leftList)
|
||||
{
|
||||
for (int i = 0; i < uniqueNumbers.Count; i++)
|
||||
{
|
||||
if (uniqueNumbers[i] == num)
|
||||
{
|
||||
similarityScore += num * counts[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return similarityScore;
|
||||
}
|
||||
|
||||
private int[][] ParseInput(string input)
|
||||
{
|
||||
var lines = input.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
var leftList = new int[lines.Length];
|
||||
var rightList = new int[lines.Length];
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var numbers = lines[i].Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
leftList[i] = int.Parse(numbers[0]);
|
||||
rightList[i] = int.Parse(numbers[1]);
|
||||
}
|
||||
|
||||
return [leftList, rightList];
|
||||
}
|
||||
}
|
||||
126
AdventOfCode/AdventOfCode/Day2.cs
Normal file
126
AdventOfCode/AdventOfCode/Day2.cs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
namespace AdventOfCode;
|
||||
|
||||
public sealed class Day2 : IDay
|
||||
{
|
||||
public long Part1(string input)
|
||||
{
|
||||
var data = ParseInput(input);
|
||||
int safeCount = 0;
|
||||
|
||||
foreach (var levels in data)
|
||||
{
|
||||
if (IsSafe(levels))
|
||||
{
|
||||
safeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return safeCount;
|
||||
}
|
||||
|
||||
public long Part2(string input)
|
||||
{
|
||||
var data = ParseInput(input);
|
||||
int safeCount = 0;
|
||||
|
||||
foreach (var levels in data)
|
||||
{
|
||||
if (IsSafeWithDampener(levels))
|
||||
{
|
||||
safeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return safeCount;
|
||||
}
|
||||
|
||||
private int[][] ParseInput(string input)
|
||||
{
|
||||
var lines = input.Split('\n');
|
||||
var result = new int[lines.Length][];
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var parts = lines[i].Split(' ');
|
||||
var numbers = new int[parts.Length];
|
||||
|
||||
for (int j = 0; j < parts.Length; j++)
|
||||
{
|
||||
numbers[j] = int.Parse(parts[j]);
|
||||
}
|
||||
|
||||
result[i] = numbers;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool IsSafe(int[] levels)
|
||||
{
|
||||
int[] diffs = new int[levels.Length - 1];
|
||||
|
||||
for (int i = 0; i < levels.Length - 1; i++)
|
||||
{
|
||||
diffs[i] = levels[i + 1] - levels[i];
|
||||
}
|
||||
|
||||
bool increasing = true;
|
||||
for (int i = 0; i < diffs.Length; i++)
|
||||
{
|
||||
if (diffs[i] < 1 || diffs[i] > 3)
|
||||
{
|
||||
increasing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (increasing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool decreasing = true;
|
||||
for (int i = 0; i < diffs.Length; i++)
|
||||
{
|
||||
if (diffs[i] < -3 || diffs[i] > -1)
|
||||
{
|
||||
decreasing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (decreasing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private bool IsSafeWithDampener(int[] levels)
|
||||
{
|
||||
if (IsSafe(levels))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < levels.Length; i++)
|
||||
{
|
||||
int[] tempLevels = new int[levels.Length - 1];
|
||||
int index = 0;
|
||||
for (int j = 0; j < levels.Length; j++)
|
||||
{
|
||||
if (j != i)
|
||||
{
|
||||
tempLevels[index++] = levels[j];
|
||||
}
|
||||
}
|
||||
|
||||
if (IsSafe(tempLevels))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
65
AdventOfCode/AdventOfCode/Day3.cs
Normal file
65
AdventOfCode/AdventOfCode/Day3.cs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AdventOfCode;
|
||||
|
||||
public sealed class Day3 : IDay
|
||||
{
|
||||
public long Part1(string input)
|
||||
{
|
||||
var data = ParseInput(input);
|
||||
int total = 0;
|
||||
|
||||
for (int i = 0; i < data.Count; i += 2)
|
||||
{
|
||||
int x = data[i];
|
||||
int y = data[i + 1];
|
||||
total += x * y;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public long Part2(string input)
|
||||
{
|
||||
var tokens = Regex.Matches(input, @"do\(\)|don't\(\)|mul\(\d+,\d+\)");
|
||||
bool enabled = true;
|
||||
int total = 0;
|
||||
|
||||
foreach (Match token in tokens)
|
||||
{
|
||||
var instruction = token.Value;
|
||||
|
||||
if (instruction == "do()")
|
||||
{
|
||||
enabled = true;
|
||||
}
|
||||
else if (instruction == "don't()")
|
||||
{
|
||||
enabled = false;
|
||||
}
|
||||
else if (instruction.StartsWith("mul(") && enabled)
|
||||
{
|
||||
var numbers = Regex.Matches(instruction, @"\d+");
|
||||
int x = int.Parse(numbers[0].Value);
|
||||
int y = int.Parse(numbers[1].Value);
|
||||
total += x * y;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
private FlexArrayInt ParseInput(string input)
|
||||
{
|
||||
var matches = Regex.Matches(input, @"mul\((\d+),(\d+)\)");
|
||||
var data = new FlexArrayInt();
|
||||
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
data.Add(int.Parse(match.Groups[1].Value)); // x
|
||||
data.Add(int.Parse(match.Groups[2].Value)); // y
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
155
AdventOfCode/AdventOfCode/Day4.cs
Normal file
155
AdventOfCode/AdventOfCode/Day4.cs
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
namespace AdventOfCode;
|
||||
|
||||
public sealed class Day4 : IDay
|
||||
{
|
||||
public long Part1(string input)
|
||||
{
|
||||
var grid = ParseInput(input);
|
||||
string word = "XMAS";
|
||||
int wordLen = word.Length;
|
||||
|
||||
(int dx, int dy)[] directions =
|
||||
[
|
||||
(0, 1), // right
|
||||
(1, 0), // down
|
||||
(1, 1), // down-right
|
||||
(1, -1), // down-left
|
||||
(0, -1), // left
|
||||
(-1, 0), // up
|
||||
(-1, -1), // up-left
|
||||
(-1, 1) // up-right
|
||||
];
|
||||
|
||||
bool IsValid(int x, int y)
|
||||
{
|
||||
return x >= 0 && x < grid.Length && y >= 0 && y < grid[0].Length;
|
||||
}
|
||||
|
||||
bool SearchFrom(int x, int y, int dx, int dy)
|
||||
{
|
||||
for (int i = 0; i < wordLen; i++)
|
||||
{
|
||||
int nx = x + i * dx;
|
||||
int ny = y + i * dy;
|
||||
|
||||
if (!IsValid(nx, ny) || grid[nx][ny] != word[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < grid.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < grid[0].Length; j++)
|
||||
{
|
||||
foreach (var (dx, dy) in directions)
|
||||
{
|
||||
if (SearchFrom(i, j, dx, dy))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public long Part2(string input)
|
||||
{
|
||||
var grid = ParseInput(input);
|
||||
int count = 0;
|
||||
int rows = grid.Length;
|
||||
int cols = grid[0].Length;
|
||||
|
||||
for (int x = 0; x < rows; x++)
|
||||
{
|
||||
for (int y = 0; y < cols; y++)
|
||||
{
|
||||
if (grid[x][y] == 'A')
|
||||
{
|
||||
count += CountXMasAt(grid, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count / 2;
|
||||
}
|
||||
|
||||
private char[][] ParseInput(string input)
|
||||
{
|
||||
var lines = input.Split('\n');
|
||||
var result = new char[lines.Length][];
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
int start = 0;
|
||||
int end = line.Length - 1;
|
||||
|
||||
// Trim start
|
||||
while (start <= end && char.IsWhiteSpace(line[start]))
|
||||
{
|
||||
start++;
|
||||
}
|
||||
|
||||
// Trim end
|
||||
while (end >= start && char.IsWhiteSpace(line[end]))
|
||||
{
|
||||
end--;
|
||||
}
|
||||
|
||||
var trimmedLine = new char[end - start + 1];
|
||||
for (int j = start, k = 0; j <= end; j++, k++)
|
||||
{
|
||||
trimmedLine[k] = line[j];
|
||||
}
|
||||
|
||||
result[i] = trimmedLine;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int CountXMasAt(char[][] grid, int x, int y)
|
||||
{
|
||||
int count = 0;
|
||||
int rows = grid.Length;
|
||||
int cols = grid[0].Length;
|
||||
|
||||
(int dx, int dy)[][] diagonals = new[]
|
||||
{
|
||||
new[] { (-1, -1), (1, 1) }, // Top-left to bottom-right
|
||||
new[] { (-1, 1), (1, -1) } // Top-right to bottom-left
|
||||
};
|
||||
|
||||
foreach (var diag in diagonals)
|
||||
{
|
||||
var positions = new (int, int)[diag.Length];
|
||||
for (int i = 0; i < diag.Length; i++)
|
||||
{
|
||||
positions[i] = (x + diag[i].dx, y + diag[i].dy);
|
||||
}
|
||||
|
||||
// Check if all positions are valid
|
||||
if (positions.All(pos => pos.Item1 >= 0 && pos.Item1 < rows &&
|
||||
pos.Item2 >= 0 && pos.Item2 < cols))
|
||||
{
|
||||
char c1 = grid[positions[0].Item1][positions[0].Item2];
|
||||
char c2 = grid[positions[1].Item1][positions[1].Item2];
|
||||
|
||||
// Check for 'M-A-S' or 'S-A-M'
|
||||
if ((c1 == 'M' && c2 == 'S') || (c1 == 'S' && c2 == 'M'))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
167
AdventOfCode/AdventOfCode/Day5.cs
Normal file
167
AdventOfCode/AdventOfCode/Day5.cs
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
namespace AdventOfCode;
|
||||
|
||||
public sealed class Day5 : IDay
|
||||
{
|
||||
public long Part1(string input)
|
||||
{
|
||||
ParseInput(input, out var rulesX, out var rulesY, out var updates);
|
||||
int total = 0;
|
||||
|
||||
foreach (var update in updates)
|
||||
{
|
||||
if (IsCorrectOrder(rulesX, rulesY, update))
|
||||
{
|
||||
int middleIndex = update.Count / 2;
|
||||
total += update[middleIndex];
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public long Part2(string input)
|
||||
{
|
||||
ParseInput(input, out var rulesX, out var rulesY, out var updates);
|
||||
int total = 0;
|
||||
|
||||
foreach (var update in updates)
|
||||
{
|
||||
if (!IsCorrectOrder(rulesX, rulesY, update))
|
||||
{
|
||||
var correctOrder = OrderUpdate(rulesX, rulesY, update);
|
||||
int middleIndex = correctOrder.Count / 2;
|
||||
total += correctOrder[middleIndex];
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
private void ParseInput(string input, out FlexArrayInt rulesX, out FlexArrayInt rulesY, out FlexArrayInt[] updates)
|
||||
{
|
||||
var sections = input.Split($"{Environment.NewLine}{Environment.NewLine}");
|
||||
var rulesSection = sections[0].Split('\n');
|
||||
var updatesSection = sections[1].Split('\n');
|
||||
|
||||
rulesX = new FlexArrayInt();
|
||||
rulesY = new FlexArrayInt();
|
||||
|
||||
// Parse rules
|
||||
foreach (var line in rulesSection)
|
||||
{
|
||||
var parts = line.Split('|');
|
||||
rulesX.Add(int.Parse(parts[0]));
|
||||
rulesY.Add(int.Parse(parts[1]));
|
||||
}
|
||||
|
||||
// Parse updates
|
||||
updates = new FlexArrayInt[updatesSection.Length];
|
||||
for (int i = 0; i < updatesSection.Length; i++)
|
||||
{
|
||||
var parts = updatesSection[i].Split(',');
|
||||
var pages = new int[parts.Length];
|
||||
for (int j = 0; j < parts.Length; j++)
|
||||
{
|
||||
pages[j] = int.Parse(parts[j]);
|
||||
}
|
||||
var updateArray = new FlexArrayInt();
|
||||
foreach (var page in pages)
|
||||
{
|
||||
updateArray.Add(page);
|
||||
}
|
||||
updates[i] = updateArray;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsCorrectOrder(FlexArrayInt rulesX, FlexArrayInt rulesY, FlexArrayInt update)
|
||||
{
|
||||
var position = new int[10000];
|
||||
for (int i = 0; i < position.Length; i++)
|
||||
{
|
||||
position[i] = -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < update.Count; i++)
|
||||
{
|
||||
position[update[i]] = i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rulesX.Count; i++)
|
||||
{
|
||||
int x = rulesX[i];
|
||||
int y = rulesY[i];
|
||||
|
||||
if (position[x] != -1 && position[y] != -1)
|
||||
{
|
||||
if (position[x] > position[y])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private FlexArrayInt OrderUpdate(FlexArrayInt rulesX, FlexArrayInt rulesY, FlexArrayInt update)
|
||||
{
|
||||
var pagesSet = new HashSet<int>();
|
||||
for (int i = 0; i < update.Count; i++)
|
||||
{
|
||||
pagesSet.Add(update[i]);
|
||||
}
|
||||
|
||||
var graph = new Dictionary<int, FlexArrayInt>();
|
||||
var inDegree = new Dictionary<int, int>();
|
||||
|
||||
foreach (var page in pagesSet)
|
||||
{
|
||||
graph[page] = new FlexArrayInt();
|
||||
inDegree[page] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rulesX.Count; i++)
|
||||
{
|
||||
int x = rulesX[i];
|
||||
int y = rulesY[i];
|
||||
if (pagesSet.Contains(x) && pagesSet.Contains(y))
|
||||
{
|
||||
graph[x].Add(y);
|
||||
inDegree[y]++;
|
||||
}
|
||||
}
|
||||
|
||||
var queue = new Queue<int>();
|
||||
foreach (var page in pagesSet)
|
||||
{
|
||||
if (inDegree[page] == 0)
|
||||
{
|
||||
queue.Enqueue(page);
|
||||
}
|
||||
}
|
||||
|
||||
var orderedPages = new FlexArrayInt();
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
int page = queue.Dequeue();
|
||||
orderedPages.Add(page);
|
||||
|
||||
for (int i = 0; i < graph[page].Count; i++)
|
||||
{
|
||||
int neighbor = graph[page][i];
|
||||
inDegree[neighbor]--;
|
||||
if (inDegree[neighbor] == 0)
|
||||
{
|
||||
queue.Enqueue(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (orderedPages.Count != pagesSet.Count)
|
||||
{
|
||||
return new FlexArrayInt();
|
||||
}
|
||||
|
||||
return orderedPages;
|
||||
}
|
||||
}
|
||||
198
AdventOfCode/AdventOfCode/Day6.cs
Normal file
198
AdventOfCode/AdventOfCode/Day6.cs
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
namespace AdventOfCode;
|
||||
|
||||
public sealed class Day6 : IDay
|
||||
{
|
||||
public long Part1(string input)
|
||||
{
|
||||
var mapData = ParseInput(input);
|
||||
int height = mapData.GetLength(0);
|
||||
int width = mapData.GetLength(1);
|
||||
|
||||
// '^', '>', 'v', '<'
|
||||
int[] dx = { 0, 1, 0, -1 };
|
||||
int[] dy = { -1, 0, 1, 0 };
|
||||
|
||||
int guardX = -1, guardY = -1, direction = -1;
|
||||
char[] directions = { '^', '>', 'v', '<' };
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int d = 0; d < directions.Length; d++)
|
||||
{
|
||||
if (mapData[y, x] == directions[d])
|
||||
{
|
||||
guardX = x;
|
||||
guardY = y;
|
||||
direction = d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (direction != -1) break;
|
||||
}
|
||||
|
||||
if (direction != -1) break;
|
||||
}
|
||||
|
||||
// Track with bool array
|
||||
var visited = new bool[height, width];
|
||||
visited[guardY, guardX] = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int nextX = guardX + dx[direction];
|
||||
int nextY = guardY + dy[direction];
|
||||
|
||||
if (nextX >= 0 && nextX < width && nextY >= 0 && nextY < height)
|
||||
{
|
||||
char cell = mapData[nextY, nextX];
|
||||
|
||||
if (cell == '#') // Turn right if wall
|
||||
{
|
||||
direction = (direction + 1) % 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
guardX = nextX;
|
||||
guardY = nextY;
|
||||
visited[guardY, guardX] = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break; // Out of bounds
|
||||
}
|
||||
}
|
||||
|
||||
// Count visited cells
|
||||
int visitedCount = 0;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
if (visited[y, x]) visitedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return visitedCount;
|
||||
}
|
||||
|
||||
public long Part2(string input)
|
||||
{
|
||||
var mapData = ParseInput(input);
|
||||
|
||||
int height = mapData.GetLength(0);
|
||||
int width = mapData.GetLength(1);
|
||||
|
||||
// '^', '>', 'v', '<'
|
||||
int[] dx = { 0, 1, 0, -1 };
|
||||
int[] dy = { -1, 0, 1, 0 };
|
||||
char[] directions = { '^', '>', 'v', '<' };
|
||||
|
||||
// Find initial position and direction
|
||||
int guardStartX = -1, guardStartY = -1, startDirection = -1;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int d = 0; d < directions.Length; d++)
|
||||
{
|
||||
if (mapData[y, x] == directions[d])
|
||||
{
|
||||
guardStartX = x;
|
||||
guardStartY = y;
|
||||
startDirection = d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (startDirection != -1) break;
|
||||
}
|
||||
|
||||
if (startDirection != -1) break;
|
||||
}
|
||||
|
||||
var possiblePositions = new List<(int, int)>();
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
if (mapData[y, x] == '.' && (x != guardStartX || y != guardStartY))
|
||||
{
|
||||
// Copy the map and add an obstruction at (x, y)
|
||||
var newMap = new char[height, width];
|
||||
Array.Copy(mapData, newMap, mapData.Length);
|
||||
newMap[y, x] = '#';
|
||||
|
||||
int guardX = guardStartX;
|
||||
int guardY = guardStartY;
|
||||
int direction = startDirection;
|
||||
var visitedStates = new HashSet<(int, int, int)>();
|
||||
|
||||
bool loopDetected = false;
|
||||
int maxSteps = height * width * 4; // Arbitrary step limit
|
||||
|
||||
for (int step = 0; step < maxSteps; step++)
|
||||
{
|
||||
var state = (guardX, guardY, direction);
|
||||
if (visitedStates.Contains(state))
|
||||
{
|
||||
loopDetected = true;
|
||||
break;
|
||||
}
|
||||
visitedStates.Add(state);
|
||||
|
||||
int nextX = guardX + dx[direction];
|
||||
int nextY = guardY + dy[direction];
|
||||
|
||||
if (nextX >= 0 && nextX < width && nextY >= 0 && nextY < height)
|
||||
{
|
||||
char cell = newMap[nextY, nextX];
|
||||
if (cell == '#')
|
||||
{
|
||||
direction = (direction + 1) % 4; // Turn right
|
||||
}
|
||||
else
|
||||
{
|
||||
guardX = nextX;
|
||||
guardY = nextY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break; // Guard leaves the map
|
||||
}
|
||||
}
|
||||
|
||||
if (loopDetected)
|
||||
{
|
||||
possiblePositions.Add((x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return possiblePositions.Count;
|
||||
}
|
||||
|
||||
private char[,] ParseInput(string input)
|
||||
{
|
||||
var lines = input.Split($"{Environment.NewLine}", StringSplitOptions.RemoveEmptyEntries);
|
||||
int height = lines.Length;
|
||||
int width = lines[0].Length;
|
||||
|
||||
var map = new char[height, width];
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
map[y, x] = lines[y][x];
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
165
AdventOfCode/AdventOfCode/Day7.cs
Normal file
165
AdventOfCode/AdventOfCode/Day7.cs
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
namespace AdventOfCode;
|
||||
|
||||
public sealed class Day7 : IDay
|
||||
{
|
||||
public long Part1(string input)
|
||||
{
|
||||
string[] lines = input.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
|
||||
long totalCalibrationResult = 0;
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
string[] parts = line.Split(':');
|
||||
long testValue = long.Parse(TrimWhitespace(parts[0]));
|
||||
long[] numbers = ParseNumbers(TrimWhitespace(parts[1]));
|
||||
long n = numbers.Length;
|
||||
long numOperators = n - 1;
|
||||
|
||||
if (TryMatchTestValue(numbers, testValue, numOperators))
|
||||
{
|
||||
totalCalibrationResult += (long)testValue;
|
||||
}
|
||||
}
|
||||
|
||||
return totalCalibrationResult;
|
||||
}
|
||||
|
||||
public long Part2(string input)
|
||||
{
|
||||
string[] lines = input.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
|
||||
long totalCalibrationResult = 0;
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
string[] parts = line.Split(':');
|
||||
long testValue = long.Parse(TrimWhitespace(parts[0]));
|
||||
long[] numbers = ParseNumbers(TrimWhitespace(parts[1]));
|
||||
long n = numbers.Length;
|
||||
long numOperators = n - 1;
|
||||
|
||||
if (TryMatchTestValueWithConcatenation(numbers, testValue, numOperators))
|
||||
{
|
||||
totalCalibrationResult += testValue;
|
||||
}
|
||||
}
|
||||
|
||||
return totalCalibrationResult;
|
||||
}
|
||||
|
||||
private bool TryMatchTestValueWithConcatenation(long[] numbers, long testValue, long numOperators)
|
||||
{
|
||||
long combinations = (long)Math.Pow(3, numOperators);
|
||||
|
||||
// test all combinations of '+', '*', '||'
|
||||
for (int combination = 0; combination < combinations; combination++)
|
||||
{
|
||||
long result = numbers[0];
|
||||
long opSelector = combination;
|
||||
|
||||
for (int i = 0; i < numOperators; i++)
|
||||
{
|
||||
char op = GetOperator(opSelector % 3);
|
||||
opSelector /= 3;
|
||||
|
||||
if (op == '+')
|
||||
{
|
||||
result += numbers[i + 1];
|
||||
}
|
||||
else if (op == '*')
|
||||
{
|
||||
result *= numbers[i + 1];
|
||||
}
|
||||
else if (op == 'C') // '||' concatenation
|
||||
{
|
||||
result = long.Parse(result + numbers[i + 1].ToString());
|
||||
}
|
||||
}
|
||||
|
||||
if (result == testValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private char GetOperator(long opIndex)
|
||||
{
|
||||
return opIndex switch
|
||||
{
|
||||
0 => '+',
|
||||
1 => '*',
|
||||
2 => 'C',
|
||||
_ => ' '
|
||||
};
|
||||
}
|
||||
|
||||
private long[] ParseNumbers(string numbersStr)
|
||||
{
|
||||
string[] numberStrings = numbersStr.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
long[] numbers = new long[numberStrings.Length];
|
||||
for (int i = 0; i < numberStrings.Length; i++)
|
||||
{
|
||||
numbers[i] = long.Parse(numberStrings[i]);
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
private bool TryMatchTestValue(long[] numbers, long testValue, long numOperators)
|
||||
{
|
||||
long combinations = (long)Math.Pow(2, numOperators);
|
||||
|
||||
// Test all combinations of '+' and '*'
|
||||
for (long combination = 0; combination < combinations; combination++)
|
||||
{
|
||||
long result = numbers[0];
|
||||
long opSelector = combination;
|
||||
|
||||
for (int i = 0; i < numOperators; i++)
|
||||
{
|
||||
char op = (opSelector & 1) == 0 ? '+' : '*';
|
||||
opSelector >>= 1;
|
||||
|
||||
if (op == '+')
|
||||
{
|
||||
result += numbers[i + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
result *= numbers[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (result == testValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string TrimWhitespace(string input)
|
||||
{
|
||||
int start = 0;
|
||||
int end = input.Length - 1;
|
||||
|
||||
while (start <= end && char.IsWhiteSpace(input[start]))
|
||||
{
|
||||
start++;
|
||||
}
|
||||
while (end >= start && char.IsWhiteSpace(input[end]))
|
||||
{
|
||||
end--;
|
||||
}
|
||||
|
||||
var trimmedChars = new char[end - start + 1];
|
||||
for (int i = start, j = 0; i <= end; i++, j++)
|
||||
{
|
||||
trimmedChars[j] = input[i];
|
||||
}
|
||||
|
||||
return new string(trimmedChars);
|
||||
}
|
||||
}
|
||||
119
AdventOfCode/AdventOfCode/FlexArrayInt.cs
Normal file
119
AdventOfCode/AdventOfCode/FlexArrayInt.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
namespace AdventOfCode;
|
||||
|
||||
public sealed class FlexArrayInt
|
||||
{
|
||||
public const int DefaultStartSize = 4;
|
||||
private int[] _data;
|
||||
|
||||
public int Count { get; private set; }
|
||||
public int Capacity => _data.Length;
|
||||
|
||||
public int this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index > Count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _data[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (index < 0 || index > Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_data[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(int value, bool firstOnly)
|
||||
{
|
||||
var removedAny = false;
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
if (value != _data[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RemoveAt(i--);
|
||||
removedAny = true;
|
||||
if (firstOnly)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return removedAny;
|
||||
}
|
||||
|
||||
public bool RemoveAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index != Count - 1)
|
||||
{
|
||||
ShiftLeft(index);
|
||||
|
||||
}
|
||||
|
||||
Count--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ShiftLeft(int fromIndex)
|
||||
{
|
||||
for (var i = fromIndex; i < Count; i++)
|
||||
{
|
||||
_data[i] = _data[i + 1];
|
||||
}
|
||||
}
|
||||
public FlexArrayInt(int? initialSize = null)
|
||||
{
|
||||
var size = Math.Max(0, initialSize ?? DefaultStartSize);
|
||||
_data = new int[size];
|
||||
}
|
||||
|
||||
public void Add(int value)
|
||||
{
|
||||
if (Capacity == Count)
|
||||
{
|
||||
Grow();
|
||||
}
|
||||
|
||||
_data[Count++] = value;
|
||||
}
|
||||
|
||||
public bool Contains(int value)
|
||||
{
|
||||
if (Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
if (_data[i] == value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private void Grow()
|
||||
{
|
||||
var newData = new int[Capacity * 2];
|
||||
Array.Copy(_data, newData, Count);
|
||||
_data = newData;
|
||||
|
||||
}
|
||||
}
|
||||
7
AdventOfCode/AdventOfCode/IDay.cs
Normal file
7
AdventOfCode/AdventOfCode/IDay.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace AdventOfCode;
|
||||
|
||||
public interface IDay
|
||||
{
|
||||
long Part1(string input);
|
||||
long Part2(string input);
|
||||
}
|
||||
|
|
@ -1,5 +1,35 @@
|
|||
using System.Text;
|
||||
using AdventOfCode;
|
||||
|
||||
Console.OutputEncoding = Encoding.UTF8;
|
||||
|
||||
Console.WriteLine("*** AdventOfCode ***");
|
||||
Console.WriteLine("*** AdventOfCode ***");
|
||||
const string DataLocation = "../../../../../data"; // a very nice path
|
||||
const string DataFileName = "/Day";
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
Console.WriteLine($"Day {i+1}:");
|
||||
var data = File.ReadAllText($"{DataLocation}{DataFileName}{i+1}");
|
||||
var day = GetDay(i + 1);
|
||||
|
||||
Console.WriteLine("\tPart 1: " + day.Part1(data));
|
||||
Console.WriteLine("\tPart 2: " + day.Part2(data));
|
||||
Console.WriteLine();
|
||||
}
|
||||
// using interface, because I am too lazy to write a gigantic switch in the for loop
|
||||
// I hope you are ok if it's done this way :)
|
||||
IDay GetDay(int day)
|
||||
{
|
||||
return day switch
|
||||
{
|
||||
1 => new Day1(),
|
||||
2 => new Day2(),
|
||||
3 => new Day3(),
|
||||
4 => new Day4(),
|
||||
5 => new Day5(),
|
||||
6 => new Day6(),
|
||||
7 => new Day7(),
|
||||
_ => new Day7()
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue