-
Notifications
You must be signed in to change notification settings - Fork 10
/
Day11.cs
134 lines (104 loc) · 4.85 KB
/
Day11.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
using AdventOfCode.CSharp.Common;
using System;
using System.Numerics;
namespace AdventOfCode.CSharp.Y2021.Solvers;
public class Day11 : ISolver
{
public static void Solve(ReadOnlySpan<byte> input, Solution solution)
{
// Each row of 10 octopi is represented by a 64-bit value. Each octopus is stored using 6 bits, totalling to 60
// bits and leaving 4 unused bits. The top bit is 1 if the octopus has not flashed in the current step. The
// next 5 bits are the energy level + 6. The reason 6 is added is because it means that once the energy level
// hits 10, the second highest bit will be 1, making it easy to tell if an octopus can be flashed.
Span<long> rows = stackalloc long[10];
int cursor = 0;
for (int i = 0; i < 10; i++)
{
long row = 0;
for (int j = 0; j < 10; j++)
{
row <<= 6; // Shift the current row up by 6 bits, leaving the bottom 6 bits for the new octopus.
row |= 1 << 5; // set flag indicating that the octopus has not flashed yet.
row += input[cursor++] - '0' + 6; // add the energy level + 6.
}
rows[i] = row;
cursor++;
}
int totalFlashes = 0;
for (int step = 0; step < 100; step++)
totalFlashes += Step(rows);
solution.SubmitPart1(totalFlashes);
int step2 = 100;
while (Step(rows) != 100)
step2++;
solution.SubmitPart2(step2 + 1);
}
private static int Step(Span<long> rows)
{
const long octopusLowestBitMask = 0b000001_000001_000001_000001_000001_000001_000001_000001_000001_000001;
const long flashResetMask = octopusLowestBitMask * 0b100111; // Set energy level to 1 (stored as 7) and set the unflashed flag.
const long canBeFlashedFlagMask = octopusLowestBitMask << 4;
const long isUnflashedFlagMask = octopusLowestBitMask << 5;
const long bottomNineOctopiMask = (1L << 54) - 1;
for (int i = 0; i < 10; i++)
{
long row = rows[i];
// Mask for octopi that still have the unflashed flag set
long unflashedOctopiMask = ((row & isUnflashedFlagMask) >> 5) * 0b111111;
long flashedOctopiMask = ~unflashedOctopiMask;
// For each unflashed octopus, add 1 to their energy level
long unflashedOctopiWithLevelIncrease = unflashedOctopiMask & (row + octopusLowestBitMask);
// For each flashed octopus, set the energy level to 1 (7 after offset by 6) and with the unflashed bit flag set back to 1.
long flashedOctopiAfterReest = flashedOctopiMask & flashResetMask;
rows[i] = unflashedOctopiWithLevelIncrease | flashedOctopiAfterReest;
}
int stepFlashes = 0;
int flashes;
do
{
flashes = 0;
// Handle first row
long firstRow = rows[0];
// Get a 1 in the lowest bit for each octopus that can be flashed
long firstRowCanBeFlashed = (firstRow & canBeFlashedFlagMask) >> 4;
flashes += BitOperations.PopCount((ulong)firstRowCanBeFlashed);
// Set flashed octopi to 0
firstRow &= ~(firstRowCanBeFlashed * 0b111111);
// Increment to the left and right
firstRow += firstRowCanBeFlashed >> 6;
firstRow += (firstRowCanBeFlashed & bottomNineOctopiMask) << 6;
long prevRow = firstRow;
long prevRowFlashed = firstRowCanBeFlashed;
for (int i = 1; i < 10; i++)
{
long row = rows[i];
// Handle flashes from previous row
row += prevRowFlashed;
row += prevRowFlashed >> 6;
row += (prevRowFlashed & bottomNineOctopiMask) << 6;
// Get a 1 in the lowest bit for each octopus that can be flashed
long canBeFlashed = (row & canBeFlashedFlagMask) >> 4;
if (canBeFlashed > 0)
{
flashes += BitOperations.PopCount((ulong)canBeFlashed);
// Set flashed octopi to 0
row &= ~(canBeFlashed * 0b111111);
// Increment the previous row
prevRow += canBeFlashed;
prevRow += canBeFlashed >> 6;
prevRow += (canBeFlashed & bottomNineOctopiMask) << 6;
// Increment to the left and right
row += canBeFlashed >> 6;
row += (canBeFlashed & bottomNineOctopiMask) << 6;
}
rows[i - 1] = prevRow;
prevRow = row;
prevRowFlashed = canBeFlashed;
}
rows[9] = prevRow;
stepFlashes += flashes;
}
while (flashes > 0);
return stepFlashes;
}
}