mirror of
https://github.com/jcreek/advent-of-code.git
synced 2026-07-13 03:03:46 +00:00
159 lines
5.6 KiB
C#
159 lines
5.6 KiB
C#
namespace AOC.Tests.Y2023;
|
|
|
|
[TestFixture]
|
|
[Parallelizable(ParallelScope.All)]
|
|
public class Day04
|
|
{
|
|
[SetUp]
|
|
public void Setup()
|
|
{
|
|
realData = File.ReadAllLines(Path.Combine(TestContext.CurrentContext.TestDirectory, "Y2023", "Data",
|
|
$"{GetThisClassName()}.dat"));
|
|
}
|
|
|
|
protected string GetThisClassName() { return GetType().Name; }
|
|
private string[] realData;
|
|
|
|
[TestCase(@"Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
|
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
|
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
|
|
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
|
|
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
|
|
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11", 13)]
|
|
[TestCase(null, 17782)] // The actual answer
|
|
public void Part1(string? input, int? expected)
|
|
{
|
|
string[] lines = input != null ? input.Split("\n") : realData;
|
|
|
|
int totalPoints = 0;
|
|
|
|
foreach (string line in lines)
|
|
{
|
|
// Given this string "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53" split it into two lists of numbers divided by |,
|
|
// ignoring everything before the :
|
|
|
|
// Extract substring after colon
|
|
string numbersPart = line.Substring(line.IndexOf(':') + 1).Trim();
|
|
|
|
// Split the string into two parts using '|'
|
|
string[] parts = numbersPart.Split('|');
|
|
|
|
// Convert each part to a list of integers
|
|
List<int> myNumbers = parts[0].Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(int.Parse).ToList();
|
|
List<int> winningNumbers = parts[1].Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(int.Parse).ToList();
|
|
|
|
// Get all numbers in myNumbers that appear in the winningNumbers
|
|
List<int> matchingNumbers = myNumbers.Intersect(winningNumbers).ToList();
|
|
|
|
Console.WriteLine($"Matches: {matchingNumbers.Count}");
|
|
|
|
if (matchingNumbers.Count > 0)
|
|
{
|
|
// 1 point for the first match, then doubled for each of the other matches after the first
|
|
int points = 1; // Start with 1 point for the first match
|
|
for (int i = 1; i < matchingNumbers.Count; i++)
|
|
{
|
|
points *= 2; // Double the points for each subsequent match
|
|
}
|
|
|
|
Console.WriteLine($"Points: {points}");
|
|
|
|
totalPoints += points;
|
|
}
|
|
}
|
|
|
|
int result = totalPoints;
|
|
|
|
if (expected != null)
|
|
{
|
|
Assert.That(result, Is.EqualTo(expected.Value));
|
|
}
|
|
|
|
Console.WriteLine($"Part 1: {result}");
|
|
}
|
|
|
|
private record Card
|
|
{
|
|
public int Id { get; set; }
|
|
public int Matches { get; set; }
|
|
public int Copies { get; set; }
|
|
}
|
|
|
|
[TestCase(@"Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
|
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
|
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
|
|
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
|
|
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
|
|
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11", 30)]
|
|
[TestCase(null, 8477787)] // The actual answer
|
|
public void Part2(string? input, int? expected)
|
|
{
|
|
string[] lines = input != null ? input.Split("\n") : realData;
|
|
|
|
int totalCards = 0;
|
|
|
|
List<Card> cards = new();
|
|
|
|
// Get the data for all the original cards
|
|
foreach (string line in lines)
|
|
{
|
|
// Given this string "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53" split it into two lists of numbers divided by |,
|
|
// ignoring everything before the :
|
|
|
|
// Get the card number after "Card " and before ":"
|
|
int cardNumber =
|
|
int.Parse(line.Substring(line.IndexOf(' ') + 1, line.IndexOf(':') - line.IndexOf(' ') - 1));
|
|
|
|
// Extract substring after colon
|
|
string numbersPart = line.Substring(line.IndexOf(':') + 1).Trim();
|
|
|
|
// Split the string into two parts using '|'
|
|
string[] parts = numbersPart.Split('|');
|
|
|
|
// Convert each part to a list of integers
|
|
List<int> myNumbers = parts[0].Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(int.Parse).ToList();
|
|
List<int> winningNumbers = parts[1].Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(int.Parse).ToList();
|
|
|
|
// Get all numbers in myNumbers that appear in the winningNumbers
|
|
List<int> matchingNumbers = myNumbers.Intersect(winningNumbers).ToList();
|
|
|
|
Card card = new() { Id = cardNumber, Matches = matchingNumbers.Count, Copies = 1 };
|
|
|
|
cards.Add(card);
|
|
}
|
|
|
|
// Work out any copies - you win 1 copy of each of the next 4 cards for each matching number
|
|
foreach (Card card in cards)
|
|
{
|
|
if (card.Matches > 0)
|
|
{
|
|
// Handle all copies, including the original
|
|
for (int i = 0; i < card.Copies; i++)
|
|
{
|
|
for (int j = 1; j <= card.Matches; j++)
|
|
{
|
|
// Increment the number of copies for the relevant card
|
|
Card cardToCopy = cards.First(c => c.Id == card.Id + j);
|
|
cardToCopy.Copies += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
totalCards = cards.Sum(c => c.Copies);
|
|
|
|
int result = totalCards;
|
|
|
|
if (expected != null)
|
|
{
|
|
Assert.That(result, Is.EqualTo(expected.Value));
|
|
}
|
|
|
|
Console.WriteLine($"Part 2: {result}");
|
|
}
|
|
}
|