auto solve = [](grid2d const& grid) -> std::pair<int, u64> {
std::flat_map<int, u64> beam_counts, next_beam_counts;
beam_counts[flux::find(grid.data, 'S')] = 1;
int split_count = 0;
for (int row : flux::iota(1, grid.height)) {
for (auto [col, count] : beam_counts) {
if (grid[col, row] == '^') {
next_beam_counts[col - 1] += count;
next_beam_counts[col + 1] += count;
++split_count;
} else {
next_beam_counts[col] += count;
}
}
std::swap(beam_counts, next_beam_counts);
next_beam_counts.clear();
}
return {split_count, flux::sum(beam_counts.values())};
};
Nothing particularly fancy for today's #AdventOfCode, but it did give me an excuse to use C++23's std::flat_map for the first time
Github: github.com/tcbrindle/ad...
07.12.2025 12:48
👍 5
🔁 1
💬 0
📌 0
auto part1 = [](grid2d const& grid) {
return grid.coordinates()
.filter([&](coord pos) { return grid[pos] == '@'; })
.count_if([&](coord pos) { return can_access(grid, pos); });
};
auto part2 = [](grid2d grid) {
int total_removed = 0;
while (true) {
int removed = 0;
grid.coordinates()
.filter([&](coord pos) {
return grid[pos] == '@' && can_access(grid, pos);
})
.for_each([&](coord pos) {
grid[pos] = '.';
++removed;
});
total_removed += removed;
if (removed == 0) { return total_removed; }
}
};
Day 4 of #AdventOfCode was short and sweet. But I wonder if there's a neater way of doing this than using a while loop?
Github: github.com/tcbrindle/ad...
04.12.2025 20:21
👍 0
🔁 1
💬 0
📌 0
Well, I wrote Flux so I should probably use it! 🙂
03.12.2025 19:40
👍 0
🔁 0
💬 1
📌 0
// flux::find_max returns the *last* position if several are equally
// maximal, but for this problem we need the *first* position
auto find_first_max =
std::bind_back(flux::find_min, flux::cmp::reverse_compare);
template <std::size_t N>
auto calculate_joltage = [](std::string_view line) -> u64 {
std::array<std::size_t, N> indices;
std::size_t window_size = line.size() - N + 1;
indices[0] = find_first_max(flux::slice(line, 0, window_size));
u64 jolts = line[indices[0]] - '0';
for (std::size_t i = 1; i < N; i++) {
indices[i] = find_first_max(
flux::slice(line, indices[i - 1] + 1, window_size + i));
jolts = 10 * jolts + (line[indices[i]] - '0');
}
return jolts;
};
template <std::size_t N>
auto solve = [](std::string_view input) {
return flux::split_string(input, '\n')
.filter([](std::string_view line) { return !line.empty(); })
.map(calculate_joltage<N>)
.sum();
};
auto const part1 = solve<2>;
auto const part2 = solve<12>;
A really nice little problem for #AdventOfCode day 3!
(Yes, std::max_element() would be slightly less code in this case, but I'm not *not* going to use Flux...)
03.12.2025 15:23
👍 2
🔁 1
💬 0
📌 0
Update: after seeing Python folks using regex to solve today's #AdventOfCode I thought I'd give it ago in C++, and... wow. Still brute force, but now it's beautiful! 😍
02.12.2025 15:53
👍 3
🔁 1
💬 2
📌 0
auto is_invalid_id_p1 = [](u64 id) -> bool {
auto const str = to_string(id);
auto mid = str.size() / 2;
return flux::equal(flux::ref(str).take(mid), flux::ref(str).drop(mid));
};
auto is_invalid_id_p2 = [](u64 id) -> bool {
auto const str = to_string(id);
return flux::iota(1uz, 1 + str.size() / 2)
.filter([&](auto sz) { return str.size() % sz == 0; })
.map([&](auto sz) { return flux::ref(str).chunk(sz); })
.any([](flux::multipass_sequence auto chunks) {
auto first = chunks.front().value();
return std::move(chunks).drop(1).all(
[&](auto chunk) { return flux::equal(chunk, first); });
});
};
template <auto TestFn>
auto test_ranges = [](std::vector<id_range> const& input) -> u64 {
return flux::ref(input)
.map([](id_range rng) { return flux::iota(rng.from, rng.to + 1); })
.flatten()
.filter(TestFn)
.sum();
};
auto part1 = test_ranges<is_invalid_id_p1>;
auto part2 = test_ranges<is_invalid_id_p2>;
I might have overdone the sequence chaining while brute-forcing #AdventOfCode day 2, but I have no regrets
github.com/tcbrindle/ad...
02.12.2025 12:59
👍 2
🔁 2
💬 0
📌 1
Very fortunate to have the one and only Herb Sutter as a guest speaker at C++ London this evening
13.06.2025 19:12
👍 3
🔁 0
💬 0
📌 0
The Road to Flux 1.0 · tcbrindle flux · Discussion #242
Flux has been around for a couple of years now, and the feedback I get is mostly that people seem to think it's pretty good. I think it's pretty good too! But I'm also aware that the current design...
I've just posted a long old update about my plans for the Flux library, focusing on ease of use, performance and a potential future standardisation effort.
I'm keen to get feedback at this stage so please head on over to Github and leave a comment if you so choose...
github.com/tcbrindle/fl...
29.05.2025 22:13
👍 3
🔁 0
💬 0
📌 0
This is ChatGPT 4-o, which has been trained on tens of millions of books and articles on every subject under the sun at eye-watering cost. Yet apparently it can't even correctly analyse a few hundred words of english text. And this is supposed to be the future?
29.05.2025 15:52
👍 1
🔁 0
💬 0
📌 0
Only after I asked it to tell me the specific line numbers on which the alleged misspellings occurred did it "apologise for the earlier error"
29.05.2025 15:52
👍 0
🔁 0
💬 1
📌 0
I'm a bit of an AI skeptic, but I thought I'd give Copilot a go in VSCode since it's now free. After writing some documentation in Markdown format, I asked it to check for any spelling or grammar mistakes. It insisted, repeatedly, that I had misspelled words *that didn't even appear in the document*
29.05.2025 15:52
👍 4
🔁 4
💬 1
📌 0
But the 5th of April was last month?
04.05.2025 18:26
👍 4
🔁 0
💬 0
📌 0
Hey Matt, any truth to the rumour/legend that Jez San demo’d an early version (starring Yoshi) for Nintendo during Starfox 2 development, which then inspired Miyamoto to create Mario 64?
25.03.2025 19:41
👍 1
🔁 0
💬 1
📌 0
📢 Episode 225 is out! 📢 In this episode, @elbeno.com and I chat with @tristanbrindle.com about plans for @cppnorth.bsky.social, future Flux plans, the slow death of Twitter and more! adspthepodcast.com/2025/03/14/E...
14.03.2025 07:05
👍 4
🔁 2
💬 0
📌 0
Guessing: no empty structs in C (unlike C++) so 1 is not allowed, but 2 contains a “declaration” so is okay
08.03.2025 08:59
👍 4
🔁 0
💬 0
📌 0
📢 Episode 224 is out! 📢 In this episode, @elbeno.com and I chat with @tristanbrindle.com about updates in Flux, internal iteration vs external iteration and more! adspthepodcast.com/2025/03/07/E...
07.03.2025 16:13
👍 1
🔁 1
💬 0
📌 0
📢 Episode 223 is out! 📢 In this episode, @elbeno.com and I chat with @tristanbrindle.com about the recent C++ London meetup, the status of safety in C++ and the future of C++ and whether it is dying ☠️ adspthepodcast.com/2025/02/28/E...
28.02.2025 13:13
👍 8
🔁 4
💬 0
📌 0
📢 Episode 222 is out! 📢 In this episode, @elbeno.com and I chat with @tristanbrindle.com about graph algorithms resources 📊, tropical semirings 🌴, Stepanov stories 📖, FM2GP 📓, EOP 📙, TV shows & movies 📺 and more! adspthepodcast.com/2025/02/21/E...
21.02.2025 13:23
👍 8
🔁 5
💬 0
📌 0
Phil Nash, Michael Wong, Lisa Lippincott, Mungo Gill, Timur Doumler, John Lakos, Gašper Ažman and Joshua Berne
An all-star lineup discussing contracts for #cplusplus at C++ London
20.01.2025 19:05
👍 3
🔁 0
💬 0
📌 0
template <int Size>
struct grid_t {
std::bitset<Size * Size> data;
static constexpr position target{Size - 1, Size - 1};
constexpr auto operator[](this auto& self, position const& pos)
{
return self.data[pos.y * Size + pos.x];
}
};
template <int Size, std::size_t N>
auto part1(std::span<position const> bytes) -> int {
grid_t<Size> grid{};
flux::for_each(bytes.first<N>(),
[&](position const& pos) { grid[pos] = true; });
return dijkstra(graph_t{grid}, {0, 0}).at(grid.target);
}
template <int Size, std::size_t Skip>
auto part2(std::span<position const> bytes) -> position {
auto idx = *std::ranges::partition_point(
std::views::iota(Skip, bytes.size()), [&](std::size_t n) {
grid_t<Size> grid{};
flux::for_each(bytes.first(n + 1),
[&](position const& pos) { grid[pos] = true; });
return dijkstra(graph_t{grid}, {0, 0}).contains(grid.target);
});
return bytes[idx];
}
I've found the last few days pretty tricky, so #AdventOfCode day 18 was a nice change of pace. Dijkstra's algorithm came up a couple of days ago so I had an implementation ready to go, and then std::partition_point() does all the hard work for part 2. I like this one!
18.12.2024 13:00
👍 4
🔁 0
💬 0
📌 0
struct robot {
vec2 pos;
vec2 vel;
};
auto const parse_input = [](std::string_view input) -> std::vector<robot> {
constexpr auto& regex = R"(p=(-?\d+),(-?\d+) v=(-?\d+),(-?\d+)\n?)";
return flux::map(ctre::tokenize<regex>(input),
[](auto result) {
auto [_, px, py, vx, vy] = result;
return robot{.pos = {px.to_number(), py.to_number()},
.vel = {vx.to_number(), vy.to_number()}};
})
.to<std::vector>();
};
template <vec2 Bounds>
auto const part1 = [](std::vector<robot> robots) -> int64_t {
// Move robots
for (auto& [pos, vel] : robots) {
pos += 100 * (vel + Bounds);
pos.x %= Bounds.x;
pos.y %= Bounds.y;
}
// Calculate safety factor
std::array<int64_t, 4> quadrants{};
for (auto const& [pos, _] : robots) {
if (pos.x < Bounds.x / 2) {
if (pos.y < Bounds.y / 2) {
++quadrants[0];
} else if (pos.y > Bounds.y / 2) {
++quadrants[1];
}
} else if (pos.x > Bounds.x / 2) {
if (pos.y < Bounds.y / 2) {
++quadrants[2];
} else if (pos.y > Bounds.y / 2) {
++quadrants[3];
}
}
}
return flux::product(quadrants);
};
A frustratingly vague #AdventOfCode day 14, where we're asked to "find a picture of a Christmas tree" with no absolutely no further details. I solved it by the ingenious method of dumping 10,000 ASCII "pictures" to a text file and searching for "*********". I'm not proud!
At least part 1 was easy.
14.12.2024 16:55
👍 5
🔁 0
💬 0
📌 0
struct game_info {
vec2 a;
vec2 b;
vec2 prize;
};
auto solve(game_info const& game) -> std::optional<i64>
{
auto [a, b, prize] = game;
i64 i = b.y * prize.x - b.x * prize.y;
i64 j = -a.y * prize.x + a.x * prize.y;
i64 det = (a.x * b.y) - (a.y * b.x);
if (det == 0 || i % det != 0 || j % det != 0) {
return std::nullopt;
} else {
return 3 * i / det + j / det;
}
}
auto part1(std::vector<game_info> const& games) -> i64
{
return flux::ref(games).map(solve).filter_deref().sum();
}
auto part2(std::vector<game_info> const& games) -> i64
{
return flux::ref(games)
.map([](game_info g) {
g.prize += vec2{10000000000000, 10000000000000};
return solve(g);
})
.filter_deref()
.sum();
}
Ah, #AdventOfCode day 13, in which I fail to spot that we're solving a system of two simultaneous equations despite allegedly holding multiple degrees in mathematics AND WRITING THE EQUATIONS DOWN IN FRONT OF ME 🤦♂️. I got there in the end though.
github.com/tcbrindle/ad...
13.12.2024 13:06
👍 13
🔁 0
💬 0
📌 0
With the fast hash map it's about 3.5ms for part 2 on my laptop
11.12.2024 17:50
👍 1
🔁 0
💬 1
📌 0
// From https://github.com/martinus/unordered_dense
// 10x faster than std::unordered_map for this problem!
using stones_map = ankerl::unordered_dense::map<u64, u64>;
template <int N>
auto blink = [](stones_map stones) -> u64 {
stones_map next;
for (auto _ : flux::ints(0, N)) {
for (auto [val, count] : stones) {
if (val == 0) {
next[1] += count;
} else if (auto opt = split_digits(val)) {
next[opt->first] += count;
next[opt->second] += count;
} else {
next[val * 2024] += count;
}
}
std::swap(stones, next);
next.clear();
}
return flux::sum(stones | std::views::values);
};
auto const part1 = blink<25>;
auto const part2 = blink<75>;
Exponential growth was the enemy in #AdventOfCode today!
A simple vector was good enough for part 1, but more iterations in part 2 required a different approach. I went for storing the data in a (value, count) hash map, which worked very well. A nice puzzle!
github.com/tcbrindle/ad...
11.12.2024 15:11
👍 3
🔁 0
💬 1
📌 0
This is very nice! Clearly recursive lambdas were the way to go today
10.12.2024 17:03
👍 1
🔁 0
💬 1
📌 0
struct trail_info {
int score;
int rating;
};
auto walk_trail(grid2d const& grid, position start) -> trail_info
{
std::vector<position> goals;
[&](this auto const& self, position here) -> void {
char value = grid[here];
if (value == '9') {
goals.push_back(here);
} else {
get_neighbours(here)
.filter([&](position p) {
return grid.is_in_bounds(p) &&
(grid[p] == value + 1);
})
.for_each(self);
}
}(start);
flux::sort(goals);
int score = flux::ref(goals).dedup().count();
return {.score = score, .rating = int(goals.size())};
};
auto walk_all(std::string_view input) -> trail_info
{
auto grid = parse_input(input);
return grid.positions()
.filter([&](auto pos) { return grid[pos] == '0'; })
.map(std::bind_front(walk_trail, grid))
.fold([](auto sum, auto info) {
sum.score += info.score;
sum.rating += info.rating;
return sum;
},
trail_info{});
};
A strange #AdventOfCode problem today where you had to solve part 2 in order to get part 1! I'm pretty happy with my solution to this one, runs in ~100µs on my laptop and has my first use of a C++23 recursive lambda.
Github: github.com/tcbrindle/ad...
10.12.2024 16:29
👍 6
🔁 0
💬 0
📌 0
I used exactly this approach to get the stars, but my solution took a couple of seconds to run. Rewrote it as a boring DFS and it ran in 10ms. Not sure what I did wrong the first time but it made me a bit sad!
07.12.2024 22:59
👍 3
🔁 0
💬 1
📌 0
No #AdventOfCode screenshots today as my code is super messy and not worth showing off. But it was very satisfying taking the initial 5 minute(!) runtime for part 2 down to ~70ms on my laptop.
github.com/tcbrindle/ad...
06.12.2024 17:29
👍 2
🔁 0
💬 0
📌 0
FYI the "144 pages" link in that article points to your C drive
06.12.2024 11:28
👍 1
🔁 0
💬 1
📌 0