feat: complete day 2

This commit is contained in:
Stevan Freeborn
2024-12-05 00:40:13 -06:00
parent 4f723c8eb4
commit ad0830f789
3 changed files with 240 additions and 2 deletions
+56
View File
@@ -0,0 +1,56 @@
namespace CeresSearch.Tests;
public class GridTests
{
private readonly string[] _exampleInput = [
"MMMSXXMASM",
"MSAMXMSMSA",
"AMXSXMAAMM",
"MSAMASMSMX",
"XMASAMXAMM",
"XXAMMXXAMA",
"SMSMSASXSS",
"SAXAMASAAA",
"MAMMMXMMMM",
"MXMXAXMASX",
];
private async Task<string[]> GetPuzzleInput()
{
return await File.ReadAllLinesAsync(Path.Combine(AppContext.BaseDirectory, "INPUT.txt"));
}
[Test]
public async Task CountOccurrences_WhenCalledWithExampleInput_ItShouldReturnExpectedCount()
{
var result = new Grid(_exampleInput).CountOccurrences("XMAS");
await Assert.That(result).IsEqualTo(18);
}
[Test]
public async Task CountOccurrences_WhenCalledWithPuzzleInput_ItShouldReturnExpectedCount()
{
var input = await GetPuzzleInput();
var result = new Grid(input).CountOccurrences("XMAS");
await Assert.That(result).IsEqualTo(2644);
}
[Test]
public async Task CountXmases_WhenCalledWithExampleInput_ItShouldReturnExpectedCount()
{
var result = new Grid(_exampleInput).CountXmases();
await Assert.That(result).IsEqualTo(9);
}
[Test]
public async Task CountXmases_WhenCalledWithPuzzleInput_ItShouldReturnExpectedCount()
{
var input = await GetPuzzleInput();
var result = new Grid(input).CountXmases();
await Assert.That(result).IsEqualTo(1952);
}
}
+154
View File
@@ -0,0 +1,154 @@
using System.Xml;
namespace CeresSearch;
record Direction(int XOffset, int YOffset)
{
public static readonly Direction Up = new(0, -1);
public static readonly Direction Down = new(0, 1);
public static readonly Direction Left = new(-1, 0);
public static readonly Direction Right = new(1, 0);
public static readonly Direction DownToTheRight = new(1, 1);
public static readonly Direction DownToTheLeft = new(-1, 1);
public static readonly Direction UpToTheRight = new(1, -1);
public static readonly Direction UpToTheLeft = new(-1, -1);
}
class Grid(string[] input)
{
private readonly Direction[] _directions = [
Direction.UpToTheLeft,
Direction.UpToTheRight,
Direction.DownToTheLeft,
Direction.DownToTheRight,
Direction.Up,
Direction.Down,
Direction.Left,
Direction.Right,
];
public int CountXmases()
{
var total = 0;
WalkGrid((rowIndex, columnIndex) =>
{
if (FoundXmas(rowIndex, columnIndex))
{
total++;
}
});
return total;
}
public int CountOccurrences(string word)
{
var total = 0;
WalkGrid((rowIndex, columnIndex) =>
total += _directions.Count(direction => FoundWord(word, direction, rowIndex, columnIndex))
);
return total;
}
private void WalkGrid(Action<int, int> callback)
{
for (var rowIndex = 0; rowIndex < input.Length; rowIndex++)
{
var row = input[rowIndex];
for (var columnIndex = 0; columnIndex < row.Length; columnIndex++)
{
callback(rowIndex, columnIndex);
}
}
}
private bool FoundXmas(int rowIndex, int colIndex)
{
var currentCharacter = input[rowIndex][colIndex];
if (currentCharacter is not 'A')
{
return false;
}
var xmasMap = new Dictionary<Direction, char>();
foreach (var direction in _directions)
{
if (
direction == Direction.Up ||
direction == Direction.Down ||
direction == Direction.Left ||
direction == Direction.Right
)
{
continue;
}
var rowIndexToCheck = rowIndex + direction.XOffset;
var colIndexToCheck = colIndex + direction.YOffset;
if (IsOffGrid(rowIndexToCheck, colIndexToCheck))
{
return false;
}
var characterToCheck = input[rowIndexToCheck][colIndexToCheck];
if (characterToCheck != 'S' && characterToCheck != 'M')
{
return false;
}
xmasMap.Add(direction, characterToCheck);
}
return IsValidXmas(xmasMap);
}
private bool IsValidXmas(Dictionary<Direction, char> map)
{
var upToTheRight = map[Direction.UpToTheRight];
var downToTheLeft = map[Direction.DownToTheLeft];
var upToTheLeft = map[Direction.UpToTheLeft];
var downToTheRight = map[Direction.DownToTheRight];
return upToTheRight != downToTheLeft && upToTheLeft != downToTheRight;
}
private bool FoundWord(string word, Direction direction, int rowIndex, int columnIndex)
{
for (var currentTargetCharacterIndex = 0; currentTargetCharacterIndex < word.Length; currentTargetCharacterIndex++)
{
var currentTargetCharacter = word[currentTargetCharacterIndex];
var currentCharacterRowIndex = rowIndex + (direction.XOffset * currentTargetCharacterIndex);
var currentCharacterColumnIndex = columnIndex + (direction.YOffset * currentTargetCharacterIndex);
if (IsOffGrid(currentCharacterRowIndex, currentCharacterColumnIndex))
{
return false;
}
var currentCharacter = input[currentCharacterRowIndex][currentCharacterColumnIndex];
if (currentCharacter != currentTargetCharacter)
{
return false;
}
}
return true;
}
private bool IsOffGrid(int rowIndex, int columnIndex)
{
return rowIndex < 0 ||
rowIndex > input.Length - 1 ||
columnIndex < 0 ||
columnIndex > input[0].Length - 1;
}
}
+30 -2
View File
@@ -1,2 +1,30 @@
// See https://aka.ms/new-console-template for more information using System.Diagnostics;
Console.WriteLine("Hello, World!");
using CeresSearch;
if (args.Length is 0)
{
Console.WriteLine("Please provide a path to the input file.");
return;
}
if (File.Exists(args[0]) is false)
{
Console.WriteLine("The provided file does not exist.");
return;
}
var isPart2 = args.Length is 2 && args[1] is "part2";
var input = await File.ReadAllLinesAsync(args[0]);
var stopwatch = new Stopwatch();
stopwatch.Start();
var grid = new Grid(input);
var results = isPart2
? grid.CountXmases()
: grid.CountOccurrences("XMAS");
stopwatch.Stop();
Console.WriteLine($"The number of occurrences is {results}. ({stopwatch.ElapsedMilliseconds}ms)");