Day 3 of Advent of Code, lets go.
Today, we're doing something called "binary diagnostic". Using a binary input, we need to calculate the gamma and epsilon rates, and multiply these to get the power consumption, which is our answer for Part 1.
Our input will look something like this:
00100
11110
10110
10111
10101
01111
00111
11100
10000
11001
00010
01010
The gamma rate is calculated by taking the most common bit in each position of the input, and the epsilon rate is calculated by taking the least common bit in each position. So, using the example above, the gamma rate would be 10110
and the epsilon rate is 01001
. As usual, we need to read the input:
var input = await File.ReadAllLinesAsync("input.txt");
This time, I decided to create an extension method that will help us in both Part 1 and Part 2 as it turns out. This method will give us an IEnumerable with the bits and their count in the order we specify.
public static class Extensions
{
public static IEnumerable<(char Bit, int Count)> GetBitCounts(this string[] arr, int position, string order = "asc")
{
var temp = arr.Select(x => x[position]).GroupBy(x => x).Select(x => (Bit: x.Key, Count: x.Count()));
temp = order == "desc" ? temp.OrderByDescending(grp => grp.Count) : temp.OrderBy(grp => grp.Count);
return temp;
}
}
For Part 1, it's quite simple. We go through the input, not line by line but rather char by char on all lines. We use the extension method to get the most common and least common bit for each position. Finally we convert it to integers with base 2, and multiply the values to get our answer.
private static void Part1(string[] input)
{
var gammaAnswer = string.Empty;
var epsilonAnswer = string.Empty;
for (var i = 0; i < input[0].Length; i++)
{
var bitCounts = input.GetBitCounts(i, "desc").ToArray();
gammaAnswer += bitCounts.First().Bit;
epsilonAnswer += bitCounts.Last().Bit;
}
var gamma = Convert.ToInt32(gammaAnswer, 2);
var epsilon = Convert.ToInt32(epsilonAnswer, 2);
Console.WriteLine($"Part1: {gamma * epsilon}");
}
Of course, it would have been enough to just calculate the gamma rate, since the epsilon rate is going to be the exact opposite of that. But I didn't do that :)
For Part 2, we are calculating Oxygen and CO2 ratings. This is more complicated. Now, to calculate the oxygen rating we want to look at the inputs position by position as before. We take the most common value in the position, and only keep the input lines that has that value in that position. If the values are equally common, keep lines that has a 1 in that position. For the CO2 rating, we do the same but we keep the lines with the least common value in each position, and if equal we keep the lines that has 0 in that position. For both values, we do this until there is only one line left, we then convert those lines to integers and multiply them to get the final answer.
private static void Part2(string[] input)
{
var oxygenArray = input;
var co2Array = input;
for (var i = 0; i < input[0].Length; i++)
{
if (co2Array.Length > 1)
{
var co2Temp = co2Array.GetBitCounts(i, "desc").ToArray();
var co2Bit = (co2Temp.First().Bit == '1' || co2Temp.First().Count == co2Temp.Last().Count) ? '1' : '0';
co2Array = co2Array.Where(x => x[i] == co2Bit).ToArray();
}
if (oxygenArray.Length > 1)
{
var oxygenTemp = oxygenArray.GetBitCounts(i).ToArray();
var oxygenBit = (oxygenTemp.First().Bit == '0' || oxygenTemp.First().Count == oxygenTemp.Last().Count) ? '0' : '1';
oxygenArray = oxygenArray.Where(x => x[i] == oxygenBit).ToArray();
}
if (co2Array.Length == 1 && oxygenArray.Length == 1)
{
break;
}
}
var oxygen = Convert.ToInt32(oxygenArray.First(), 2);
var co2 = Convert.ToInt32(co2Array.First(), 2);
Console.WriteLine($"Part2: {oxygen * co2}");
}