-
Notifications
You must be signed in to change notification settings - Fork 10
/
Day04.cs
147 lines (120 loc) · 5.29 KB
/
Day04.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using AdventOfCode.CSharp.Common;
using System;
using System.Runtime.CompilerServices;
namespace AdventOfCode.CSharp.Y2021.Solvers;
public class Day04 : ISolver
{
// This solver assumes that there are no bingo numbers greater than 99, which is true of all the AoC inputs and examples.
public static void Solve(ReadOnlySpan<byte> input, Solution solution)
{
// Create a lookup table where the key is the bingo number and the value is what order it is called out.
// For example, if the first number called out is 9, then orderIndexLookup[9] == 0.
Span<byte> orderIndexLookup = stackalloc byte[100];
// Represents the index into the input which we are currently parsing.
int inputCursor = 0;
// Parses the first line of the input and stores the order in the orderIndexLookup.
ParseNumberOrderLine(input, orderIndexLookup, ref inputCursor);
// Keep track of the earliest and latest bingos and what their scores were.
int earliestBingo = int.MaxValue;
int earliestBingoScore = 0;
int latestBingo = 0;
int latestBingoScore = 0;
// Stores the order of the latest called out number in each column of a given board.
Span<byte> colMaxs = stackalloc byte[5];
// Stores the 25 bingo numbers of the current board.
Span<byte> bingoNumbers = stackalloc byte[25];
// Each iteration of the loop processes a single bingo board.
while (inputCursor < input.Length)
{
// Stores the order of the earliest bingo among the rows of the board only
int earliestRowBingo = int.MaxValue;
// Skips a newline
inputCursor++;
// All boards are 5x5 as per the problem statement
for (int row = 0; row < 5; row++)
{
// Stores the order of the latest called out number in the current row.
int latestInRow = 0;
for (int col = 0; col < 5; col++)
{
// As we assume no bingo numbers are greater than 99, each number is represented using 2 characters
// with a space in the first character for single-digit numbers.
int digitOne = input[inputCursor++] switch { (byte)' ' => 0, byte c => c - '0' };
int digitTwo = input[inputCursor++] - '0';
// Skip the space or newline.
inputCursor++;
// Calculate the bingo number using the digits and the order it is called out
int value = digitOne * 10 + digitTwo;
byte order = orderIndexLookup[value];
// Update latestInRow and colMaxs[col]
if (order > latestInRow)
latestInRow = order;
if (row == 0 || order > colMaxs[col])
colMaxs[col] = order;
bingoNumbers[row * 5 + col] = (byte)value;
}
if (latestInRow < earliestRowBingo)
earliestRowBingo = latestInRow;
}
// Take the min of all the columns and the earliest row.
int bingo = Math.Min(Math.Min(Math.Min(colMaxs[0], colMaxs[1]), Math.Min(colMaxs[2], colMaxs[3])), Math.Min(colMaxs[4], earliestRowBingo));
// Update the earliest and latest bingo values
if (bingo < earliestBingo)
{
earliestBingo = bingo;
earliestBingoScore = CalculateScore(orderIndexLookup, bingoNumbers, bingo);
}
if (bingo > latestBingo)
{
latestBingo = bingo;
latestBingoScore = CalculateScore(orderIndexLookup, bingoNumbers, bingo);
}
}
solution.SubmitPart1(earliestBingoScore);
solution.SubmitPart2(latestBingoScore);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ParseNumberOrderLine(ReadOnlySpan<byte> input, Span<byte> orderIndexLookup, ref int inputCursor)
{
byte order = 0;
while (true)
{
int digitOne = input[inputCursor++] - '0';
byte charTwo = input[inputCursor++];
switch (charTwo)
{
case (byte)',':
orderIndexLookup[digitOne] = order;
break;
case (byte)'\n':
orderIndexLookup[digitOne] = order;
return;
default:
orderIndexLookup[digitOne * 10 + (charTwo - '0')] = order;
if (input[inputCursor++] == '\n')
return;
break;
}
order++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int CalculateScore(Span<byte> orderIndexLookup, Span<byte> bingoNumbers, int bingo)
{
int score = 0;
int bingoNumber = 0;
foreach (byte number in bingoNumbers)
{
byte order = orderIndexLookup[number];
if (order > bingo)
{
score += number;
}
else if (order == bingo)
{
bingoNumber = number;
}
}
return score * bingoNumber;
}
}