This is a solution to Advent of Code 2016, Day 8. The tricky part of this problem is that you have to be able to rotate both the rows and columns of a two-dimensional array. Rotating a row is easy using std::rotate
, but rotating a column seems much harder.
My basic approach was to write a "getter function" such that get(i)
returns the i
th element of the (row/column) currently being rotated; and then I wrote a very straightforward "rotate by 1 in a loop" function in terms of get(i)
. That implementation is marked // DUMB
below. (Although, given this particular problem's range of input, "it's not dumb if it works.")
But I'd really like to use std::rotate
if possible, both for speed ("rotate by 1 in a loop" is O(by
xn
), when it should be just O(n
)) and for elegance. So I wrote a wrapper that takes the "getter" function and turns it into an iterator. This is marked // CLEVER
below.
- Does the STL or Boost already provide some way to turn an indexed "getter" function into a random-access iterator?
- I turn my array into a "getter" function and then turn the getter function into an iterator; seems a bit roundabout. Does the STL or Boost already provide a straightforward way to construct a "strided" iterator on an array?
- I see that
std::iterator
is deprecated in C++17. In C++17, would my code Just Work if I stopped inheriting fromstd::iterator
? or what's the best practice for writing iterators these days?
#include <assert.h>
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <regex>
#include <string>
const int HEIGHT = 6;
const int WIDTH = 50;
bool grid[HEIGHT][WIDTH];
#if 1
template<typename Getter>
auto make_iterator(const Getter& get, int i) // CLEVER
{
using T = std::remove_reference_t<decltype(get(i))>;
struct It : std::iterator<std::forward_iterator_tag, T> {
const Getter *get_;
int i_;
It(const Getter& g, int i) : get_(&g), i_(i) {}
It& operator++() { ++i_; return *this; }
It operator++(int) { It temp = *this; ++i_; return temp; }
decltype(auto) operator*() const { return (*get_)(i_); }
bool operator==(const It& rhs) const { return i_ == rhs.i_; }
bool operator!=(const It& rhs) const { return i_ != rhs.i_; }
};
return It(get, i);
}
template<typename Getter>
void rotate_right(int by, int n, const Getter& get)
{
std::rotate(make_iterator(get, 0), make_iterator(get, (n - by) % n), make_iterator(get, n));
}
#else
template<typename Getter>
void rotate_right(int by, int n, const Getter& get)
{
while (by != 0) { // DUMB
bool temp = get(n-1);
for (int i = n-1; i >= 0; --i) {
get(i+1) = get(i);
}
get(0) = temp;
--by;
}
}
#endif
void process_input(std::string line)
{
std::smatch m;
if (std::regex_match(line, m, std::regex("rect (.*)x(.*)"))) {
int a_wide = std::stoi(m.str(1));
int b_tall = std::stoi(m.str(2));
for (int i=0; i < a_wide; ++i) {
for (int j=0; j < b_tall; ++j) {
grid[j][i] = true;
}
}
} else if (std::regex_match(line, m, std::regex("rotate row y=(.*) by (.*)"))) {
int row = std::stoi(m.str(1));
int by = std::stoi(m.str(2)) % WIDTH;
rotate_right(by, WIDTH, [&](int i) -> decltype(auto) { return grid[row][i]; });
} else if (std::regex_match(line, m, std::regex("rotate column x=(.*) by (.*)"))) {
int column = std::stoi(m.str(1));
int by = std::stoi(m.str(2)) % HEIGHT;
rotate_right(by, HEIGHT, [&](int i) -> decltype(auto) { return grid[i][column]; });
} else {
assert(false);
}
}
int main()
{
std::string buf;
while (std::getline(std::cin, buf)) {
process_input(buf);
}
for (int i=0; i < HEIGHT; ++i) {
for (int j=0; j < WIDTH; ++j) {
printf("%c", grid[i][j] ? '#' : '.');
}
printf("\n");
}
}
std::begin(a)
andstd::size(a)
work for core language arraysT[N]
just the same as they do forstd::array<T,N>
. Anyway, I'm not sure what specific change you're proposing: is it just to change the one linebool grid[HEIGHT][WIDTH];
tostd::array<std::array<bool, WIDTH>, HEIGHT> grid;
and leave the rest untouched? \$\endgroup\$