Skip to content

Instantly share code, notes, and snippets.

@Garciat
Last active August 29, 2015 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Garciat/c8317f565975e4774864 to your computer and use it in GitHub Desktop.
Save Garciat/c8317f565975e4774864 to your computer and use it in GitHub Desktop.
#include <tuple>
#include <vector>
#include <random>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <boost/dynamic_bitset.hpp>
struct Matrix
{
int m, n;
int *data;
Matrix(int m, int n)
: m{m}, n{n}, data{new int[m * n]}
{ }
Matrix(const Matrix&) = delete;
~Matrix()
{
delete[] data;
}
auto size() const {
return m * n;
}
bool in_bounds(int i) const
{
return i >= 0 && i < m * n;
}
bool in_bounds(int i, int j) const
{
return i >= 0 && j >= 0 && i < m && j < n;
}
int& operator[] (int i)
{
return data[i];
}
int operator[] (int i) const
{
return data[i];
}
int& at(int i, int j)
{
return data[i * n + j];
}
int at(int i, int j) const
{
return data[i * n + j];
}
};
struct Board
{
int colors;
Matrix pixels;
Board(int width, int height, int colors)
: colors{colors}, pixels{height, width}
{ }
void randomize()
{
std::random_device rd{};
std::mt19937 gen{rd()};
std::uniform_int_distribution<> dis{0, colors - 1};
for (int i = 0; i < pixels.m; ++i)
{
for (int j = 0; j < pixels.n; ++j)
{
pixels.at(i, j) = dis(gen);
}
}
}
void inflate(std::string filename)
{
std::ifstream ifs{filename};
std::string line;
for (int i = 0; i < pixels.m; ++i)
{
std::getline(ifs, line);
for (int j = 0; j < pixels.n; ++j)
{
pixels.at(i, j) = static_cast<int>(line[j] - '0');
}
}
}
std::string stringify() const
{
std::ostringstream oss;
for (int i = 0; i < pixels.m; ++i)
{
for (int j = 0; j < pixels.n; ++j)
{
oss << pixels.at(i, j);
}
oss << '\n';
}
auto str = oss.str();
str.erase(str.end() - 1);
return str;
}
template <typename Func>
void foreach_neighbor_of(int i, Func f) const
{
int idiv = i / pixels.n;
int imod = i % pixels.n;
// up
if (idiv != 0) {
f(i - pixels.n);
}
// left
if (imod != 0) {
f(i - 1);
}
// right
if (imod != pixels.n - 1) {
f(i + 1);
}
// down
if (idiv != pixels.m - 1) {
f(i + pixels.n);
}
}
};
std::ostream& operator<< (std::ostream& os, const Board& board)
{
return os << board.stringify();
}
struct Island
{
int color;
std::vector<int> elements;
Island(int color)
: color{color}
{ }
Island(Island&& rhs)
: color{rhs.color}, elements{std::move(rhs.elements)}
{ }
};
template <typename Container>
void uniquify(Container& cont)
{
std::sort(std::begin(cont), std::end(cont));
auto last_unique = std::unique(std::begin(cont), std::end(cont));
cont.erase(last_unique, std::end(cont));
}
auto find_islands(const Board& board)
{
std::vector<bool> visited(board.pixels.size(), false);
std::vector<Island> islands{};
std::vector<int> visit_queue{};
for (int i = 0; i < board.pixels.size(); ++i)
{
if (visited[i]) continue;
visit_queue.clear();
visit_queue.push_back(i);
auto color = board.pixels[i];
Island island { color };
int visit_head = 0;
while (visit_head < visit_queue.size())
{
auto j = visit_queue[visit_head];
visit_head += 1;
if (visited[j]) continue;
if (color == board.pixels[j])
{
visited[j] = true;
island.elements.push_back(j);
board.foreach_neighbor_of(j, [&] (int x) { visit_queue.push_back(x); });
}
}
islands.emplace_back(std::move(island));
}
return islands;
}
auto islands_adjacency(const Board& board, const std::vector<Island>& islands)
{
std::vector<std::vector<int>> adjacency{};
std::vector<int> elem2island{};
adjacency.reserve(islands.size());
elem2island.reserve(board.pixels.size());
for (int i = 0; i < islands.size(); ++i)
{
for (int elem : islands[i].elements)
{
elem2island[elem] = i;
}
}
for (auto&& island : islands)
{
std::vector<int> friends{};
for (int elem : island.elements)
{
board.foreach_neighbor_of(elem, [&] (int neighbor) {
if (island.color == board.pixels[neighbor]) return;
friends.push_back(elem2island[neighbor]);
});
}
uniquify(friends);
adjacency.emplace_back(std::move(friends));
}
return adjacency;
}
struct NatSet
{
int count;
boost::dynamic_bitset<> table;
NatSet(const NatSet& rhs)
: count{rhs.count}, table{rhs.table}
{ }
NatSet& operator= (const NatSet& rhs)
{
count = rhs.count;
table = rhs.table;
return *this;
}
NatSet(NatSet&& rhs)
: count{rhs.count}, table{std::move(rhs.table)}
{ }
NatSet& operator= (NatSet&& rhs)
{
count = rhs.count;
table = std::move(rhs.table);
return *this;
}
NatSet(int max, std::initializer_list<int> elems)
: count{0}, table(max, false)
{
insert(elems);
}
void insert(int e)
{
if (!table[e])
{
count += 1;
}
table[e] = true;
}
template <typename Container>
void insert(const Container& cont)
{
for (auto&& x : cont)
{
table[x] = true;
}
recount();
}
bool contains(int e) const
{
return table[e];
}
bool is_subset_of(const NatSet& rhs) const
{
return table.is_subset_of(rhs.table);
}
private:
void recount()
{
count = table.count();
}
};
struct SolveState
{
NatSet domain;
std::vector<int> border;
std::vector<int> steps;
SolveState(const SolveState& rhs)
: SolveState{rhs.domain, rhs.border, rhs.steps}
{ }
SolveState& operator= (const SolveState& rhs)
{
domain = rhs.domain;
border = rhs.border;
steps = rhs.steps;
return *this;
}
SolveState(SolveState&& rhs)
: domain{std::move(rhs.domain)}
, border{std::move(rhs.border)}
, steps {std::move(rhs.steps)}
{ }
SolveState& operator= (SolveState&& rhs)
{
domain = std::move(rhs.domain);
border = std::move(rhs.border);
steps = std::move(rhs.steps);
return *this;
}
SolveState(NatSet ns)
: SolveState{std::move(ns), {}, {}}
{ }
SolveState(NatSet ns, std::vector<int> border)
: SolveState{std::move(ns), std::move(border), {}}
{ }
SolveState(NatSet ns, std::vector<int> border, std::vector<int> steps)
: domain{std::move(ns)}, border{std::move(border)}, steps{std::move(steps)}
{ }
};
auto solve(const Board& board)
{
auto islands = find_islands(board);
auto adjacency = islands_adjacency(board, islands);
std::vector<SolveState> level{};
auto init = NatSet{board.pixels.size(), {0}};
level.emplace_back(init, adjacency[0]);
std::vector<SolveState> next{};
while (true)
{
next.clear();
for (auto&& state : level)
{
for (int color = 0; color < board.colors; ++color)
{
NatSet domain{state.domain};
std::vector<int> border{};
for (int friend_ : state.border)
{
if (islands[friend_].color == color)
{
domain.insert(friend_);
for (int poop : adjacency[friend_])
{
if (!domain.contains(poop))
{
border.push_back(poop);
}
}
}
else
{
border.push_back(friend_);
}
}
if (state.domain.count == domain.count)
{
continue;
}
std::vector<int> steps{state.steps};
steps.push_back(color);
if (domain.count == islands.size())
{
return steps;
}
uniquify(border);
next.emplace_back(std::move(domain), std::move(border), std::move(steps));
}
}
std::sort(std::begin(next), std::end(next), [] (const SolveState& a, const SolveState& b) {
return a.domain.count < b.domain.count;
});
level.clear();
for (int i = 0; i < next.size(); ++i)
{
bool skip = false;
for (int j = i + 1; j < next.size(); ++j)
{
if (next[i].domain.is_subset_of(next[j].domain))
{
skip = true;
break;
}
}
if (!skip)
{
level.emplace_back(std::move(next[i]));
}
}
}
}
template <typename Container>
void print_container(std::ostream& os, const Container& cont)
{
os << '[';
auto it = std::begin(cont);
auto ot = std::end(cont);
if (it != ot) {
os << *it;
}
++it;
for ( ; it != ot; ++it) {
os << ", " << *it;
}
os << "]\n";
}
int main()
{
Board board{16, 9, 6};
//board.randomize();
board.inflate("board.txt");
std::cout << board << std::endl;
/*
auto islands = find_islands(board);
for (auto&& island : islands)
{
for (auto&& x : island.elements)
{
std::cout << x << ' ';
}
std::cout << '\n';
}
auto adjacency = islands_adjacency(board, islands);
for (auto&& row : adjacency)
{
for (auto&& x : row)
{
std::cout << x << ' ';
}
std::cout << '\n';
}
*/
auto answer = solve(board);
print_container(std::cout, answer);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment