Skip to content

Commit 7ff3332

Browse files
committed
Solution for Day 14.
As it turns out I should have updated Day 12 to use disjoint sets in the first place... Added new utility header for functions/classes used across solutions. For now it contains disjoint sets and the knot hash function. Also updated solutions of Day 10 and Day 12 to make use of this header.
1 parent a5b5da5 commit 7ff3332

File tree

8 files changed

+275
-86
lines changed

8 files changed

+275
-86
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
1414
include_directories(inc)
1515

1616
add_executable(aoc17
17+
inc/aoc_utility.hpp
1718
inc/default_includes.hpp
1819
inc/solution.hpp
20+
src/aoc_utility.cpp
1921
src/day01.cpp
2022
src/day02.cpp
2123
src/day03.cpp

inc/aoc_utility.hpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#ifndef AOC_UTILITY_H_
2+
#define AOC_UTILITY_H_
3+
4+
#include <array>
5+
#include <string>
6+
#include <unordered_map>
7+
#include <vector>
8+
9+
namespace aoc
10+
{
11+
/**********************************************************************
12+
* KNOT HASH *
13+
**********************************************************************/
14+
15+
std::array<unsigned int, 16> knot_hash(const std::string& input);
16+
17+
void knot_hash_round(
18+
std::array<unsigned int, 256>& array,
19+
const std::vector<unsigned int>& inputs,
20+
unsigned int& pos,
21+
unsigned int& skip);
22+
23+
/**********************************************************************
24+
* DISJOINT SETS / UNION FIND *
25+
**********************************************************************/
26+
27+
template<typename T>
28+
class disjoint_sets
29+
{
30+
struct set { T id; size_t size{0u}; };
31+
32+
std::unordered_map<T, set> table;
33+
size_t num_sets{0u};
34+
35+
public:
36+
T find(const T& t) { return find_rec(t).id; }
37+
38+
void make_union(const T& t1, const T& t2)
39+
{
40+
auto& s1 = find_rec(t1);
41+
auto& s2 = find_rec(t2);
42+
43+
if (s1.id != s2.id)
44+
{
45+
--num_sets;
46+
s2.id = s1.id;
47+
s1.size += s2.size;
48+
}
49+
}
50+
51+
bool contains(const T& t) const { return table.find(t) != table.end(); }
52+
53+
size_t size_of_set(const T& t) { return find_rec(t).size; }
54+
55+
size_t set_count() const { return num_sets; }
56+
57+
private:
58+
set& find_rec(const T& t)
59+
{
60+
auto& s = table[t];
61+
if (s.size == 0u)
62+
{
63+
++num_sets;
64+
s.id = t;
65+
s.size = 1u;
66+
return s;
67+
}
68+
else if (s.id == t) { return s; }
69+
else
70+
{
71+
auto& p = find_rec(s.id);
72+
s.id = p.id;
73+
return p;
74+
}
75+
}
76+
};
77+
78+
}
79+
#endif // AOC_UTILITY_H_
80+

inputs/Day14.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ffayrhll

instructions/Day14.txt

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
--- Day 14: Disk Defragmentation ---
2+
3+
Suddenly, a scheduled job activates the system's disk defragmenter. Were
4+
the situation different, you might sit and watch it for a while, but
5+
today, you just don't have that kind of time. It's soaking up valuable
6+
system resources that are needed elsewhere, and so the only option is to
7+
help it finish its task as soon as possible.
8+
9+
The disk in question consists of a 128x128 grid; each square of the grid
10+
is either free or used. On this disk, the state of the grid is tracked
11+
by the bits in a sequence of knot hashes.
12+
13+
A total of 128 knot hashes are calculated, each corresponding to a
14+
single row in the grid; each hash contains 128 bits which correspond to
15+
individual grid squares. Each bit of a hash indicates whether that
16+
square is free (0) or used (1).
17+
18+
The hash inputs are a key string (your puzzle input), a dash, and a
19+
number from 0 to 127 corresponding to the row. For example, if your key
20+
string were flqrgnkx, then the first row would be given by the bits of
21+
the knot hash of flqrgnkx-0, the second row from the bits of the knot
22+
hash of flqrgnkx-1, and so on until the last row, flqrgnkx-127.
23+
24+
The output of a knot hash is traditionally represented by 32 hexadecimal
25+
digits; each of these digits correspond to 4 bits, for a total of 4 * 32
26+
= 128 bits. To convert to bits, turn each hexadecimal digit to its
27+
equivalent binary value, high-bit first: 0 becomes 0000, 1 becomes 0001,
28+
e becomes 1110, f becomes 1111, and so on; a hash that begins with
29+
a0c2017... in hexadecimal would begin with
30+
10100000110000100000000101110000... in binary.
31+
32+
Continuing this process, the first 8 rows and columns for key flqrgnkx
33+
appear as follows, using # to denote used squares, and . to denote free
34+
ones:
35+
36+
##.#.#..-->
37+
.#.#.#.#
38+
....#.#.
39+
#.#.##.#
40+
.##.#...
41+
##..#..#
42+
.#...#..
43+
##.#.##.-->
44+
| |
45+
V V
46+
47+
In this example, 8108 squares are used across the entire 128x128 grid.
48+
49+
Given your actual key string, how many squares are used?
50+
51+
Your puzzle answer was 8190.
52+
53+
--- Part Two ---
54+
55+
Now, all the defragmenter needs to know is the number of regions. A
56+
region is a group of used squares that are all adjacent, not including
57+
diagonals. Every used square is in exactly one region: lone used squares
58+
form their own isolated regions, while several adjacent squares all
59+
count as a single region.
60+
61+
In the example above, the following nine regions are visible, each
62+
marked with a distinct digit:
63+
64+
11.2.3..-->
65+
.1.2.3.4
66+
....5.6.
67+
7.8.55.9
68+
.88.5...
69+
88..5..8
70+
.8...8..
71+
88.8.88.-->
72+
| |
73+
V V
74+
75+
Of particular interest is the region marked 8; while it does not appear
76+
contiguous in this small view, all of the squares marked 8 are connected
77+
when considering the whole 128x128 grid. In total, in this example, 1242
78+
regions are present.
79+
80+
How many regions are present given your key string?
81+
82+
Your puzzle answer was 1134.
83+

src/aoc_utility.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include "aoc_utility.hpp"
2+
#include "default_includes.hpp"
3+
4+
/**********************************************************************
5+
* KNOT HASH *
6+
**********************************************************************/
7+
8+
std::array<unsigned int, 16> aoc::knot_hash(const std::string& input)
9+
{
10+
static std::array<unsigned int, 256> array;
11+
std::iota(array.begin(), array.end(), 0u);
12+
13+
std::vector<unsigned int> inputs;
14+
std::for_each(input.begin(), input.end(),
15+
[&](auto c) {
16+
inputs.push_back(static_cast<unsigned int>(c));
17+
});
18+
for (auto i : {17u, 31u, 73u, 47u, 23u}) { inputs.push_back(i); }
19+
20+
unsigned int pos{0u}, skip{0u};
21+
for (auto i{0}; i < 64; ++i) { knot_hash_round(array, inputs, pos, skip); }
22+
23+
std::array<unsigned int, 16> hash;
24+
auto hh = hash.begin();
25+
for (auto it = array.begin(); it != array.end(); std::advance(it, 16))
26+
{
27+
*(hh++) = std::accumulate(it, (it + 16), 0u, std::bit_xor<void>());
28+
}
29+
30+
return hash;
31+
}
32+
33+
void aoc::knot_hash_round(
34+
std::array<unsigned int, 256>& array,
35+
const std::vector<unsigned int>& inputs,
36+
unsigned int& pos,
37+
unsigned int& skip)
38+
{
39+
for (auto len : inputs)
40+
{
41+
unsigned int start_pos{pos};
42+
unsigned int end_pos = ((pos + len) - 1u) % array.size();
43+
44+
for (auto i{0u}; i < (len / 2u); ++i)
45+
{
46+
std::swap(array[start_pos], array[end_pos]);
47+
start_pos = (start_pos + 1u) % array.size();
48+
end_pos = (end_pos > 0u) ? (end_pos - 1u) : (array.size() - 1u);
49+
}
50+
51+
pos = (pos + (len + skip++)) % array.size();
52+
}
53+
}
54+

src/day10.cpp

Lines changed: 9 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,35 @@
1+
#include "aoc_utility.hpp"
12
#include "default_includes.hpp"
23
#include "solution.hpp"
34

4-
static inline void hash_round(
5-
std::array<int,256>& list,
6-
const std::vector<int>& inputs,
7-
int& pos,
8-
int& skip_size)
9-
{
10-
for (int len : inputs)
11-
{
12-
int start_pos = pos;
13-
int end_pos = ((pos + len) - 1) % list.size();
14-
15-
for (int i = 0; i < (len / 2); ++i)
16-
{
17-
std::swap(list[start_pos], list[end_pos]);
18-
start_pos = (start_pos + 1) % list.size();
19-
end_pos = (end_pos > 0) ? (end_pos - 1) : (list.size() - 1);
20-
}
21-
22-
pos = (pos + (len + skip_size++)) % list.size();
23-
}
24-
}
25-
265
template<>
276
void solve<Day10>(std::istream& ins, std::ostream& outs)
287
{
29-
std::array<int,256> list;
30-
std::iota(list.begin(), list.end(), 0);
8+
std::array<unsigned int, 256> list;
9+
std::iota(list.begin(), list.end(), 0u);
3110

3211
std::string input;
3312
ins >> input;
3413

3514
// Part 1
36-
std::istringstream iss{input};
37-
std::vector<int> inputs_p1;
15+
std::istringstream iss{input};
16+
std::vector<unsigned int> inputs;
3817
for (std::string s; std::getline(iss, s, ',');)
3918
{
40-
inputs_p1.push_back(std::stoi(s));
19+
inputs.push_back(std::stoi(s));
4120
}
4221

43-
int pos{0}, skip_size{0};
44-
hash_round(list, inputs_p1, pos, skip_size);
22+
unsigned int pos{0u}, skip_size{0u};
23+
aoc::knot_hash_round(list, inputs, pos, skip_size);
4524
outs << "(Part 1) Result = " << (list[0] * list[1]) << std::endl;
4625

4726
// Part 2
48-
std::iota(list.begin(), list.end(), 0);
49-
std::vector<int> inputs_p2;
50-
std::for_each(
51-
input.begin(),
52-
input.end(),
53-
[&](auto c) { inputs_p2.push_back(static_cast<int>(c)); });
54-
for (int i : {17, 31, 73, 47, 23}) { inputs_p2.push_back(i); }
55-
56-
pos = 0;
57-
skip_size = 0;
58-
for (auto i = 0; i < 64; ++i)
59-
{
60-
hash_round(list, inputs_p2, pos, skip_size);
61-
}
62-
6327
// setup hex output formatting
6428
auto format = outs.flags();
6529
outs << std::setfill('0') << std::hex;
6630

6731
outs << "(Part 2) Hash = ";
68-
for (auto it = list.begin(); it < list.end(); std::advance(it, 16))
69-
{
70-
outs << std::setw(2)
71-
<< std::accumulate(it, (it + 16), 0, std::bit_xor<void>());
72-
}
73-
32+
for (auto b : aoc::knot_hash(input)) { outs << std::setw(2) << b; }
7433
outs << std::endl;
7534

7635
// reset output formatting

src/day12.cpp

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,24 @@
1+
#include "aoc_utility.hpp"
12
#include "default_includes.hpp"
23
#include "solution.hpp"
34

45
template<>
56
void solve<Day12>(std::istream& ins, std::ostream& outs)
67
{
7-
std::unordered_map<int, int> group_map;
8-
std::unordered_map<int, std::unordered_set<int>> groups;
9-
10-
int group_id_counter{0};
8+
aoc::disjoint_sets<int> groups;
119
for (std::string line; std::getline(ins, line);)
1210
{
1311
std::istringstream iss{line};
1412
std::string word;
1513
std::vector<int> group;
1614

1715
iss >> word;
18-
group.emplace_back(std::stoi(word));
16+
auto set = groups.find(stoi(word));
1917
iss >> word; // skip the <->
20-
while (iss >> word) { group.emplace_back(std::stoi(word)); }
21-
22-
std::unordered_set<int> to_merge;
23-
for (int elem : group)
24-
{
25-
auto it = group_map.find(elem);
26-
if (it != group_map.end()) { to_merge.insert(it->second); }
27-
}
28-
29-
int group_id = (to_merge.size() > 0)
30-
? *(to_merge.begin())
31-
: group_id_counter++;
32-
if (to_merge.size() > 1)
33-
{
34-
std::for_each(
35-
std::next(to_merge.begin()),
36-
to_merge.end(),
37-
[&](int gid)
38-
{
39-
groups[group_id].insert(
40-
groups[gid].begin(),
41-
groups[gid].end());
42-
groups.erase(gid);
43-
});
44-
}
45-
46-
groups[group_id].insert(group.begin(), group.end());
47-
for (int elem : groups[group_id]) { group_map[elem] = group_id; }
18+
while (iss >> word) { groups.make_union(set, stoi(word)); }
4819
}
4920

50-
outs << "Group size of '0' = " << groups[group_map[0]].size() << std::endl
51-
<< "Number of groups = " << groups.size() << std::endl;
21+
outs << "Group size of '0' = " << groups.size_of_set(0) << std::endl
22+
<< "Number of groups = " << groups.set_count() << std::endl;
5223
}
5324

0 commit comments

Comments
 (0)