181 lines
5.6 KiB
C++
181 lines
5.6 KiB
C++
|
#include <fstream>
|
||
|
#include <iostream>
|
||
|
#include <sstream>
|
||
|
#include <vector>
|
||
|
#include <tuple>
|
||
|
#include <set>
|
||
|
|
||
|
using namespace std;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
#define DBG(str) do { std::cout << str << std::endl; } while( false )
|
||
|
#else
|
||
|
#define DBG(str) do { } while ( false )
|
||
|
#endif
|
||
|
|
||
|
vector<vector<char>> parseGrid(const char *path) {
|
||
|
vector<vector<char>> grid;
|
||
|
|
||
|
ifstream input(path);
|
||
|
string line;
|
||
|
while (getline(input, line)) {
|
||
|
char c;
|
||
|
vector<char> row;
|
||
|
stringstream ss;
|
||
|
ss << line;
|
||
|
while (ss >> c) {
|
||
|
row.push_back(c);
|
||
|
}
|
||
|
grid.push_back(row);
|
||
|
}
|
||
|
return grid;
|
||
|
}
|
||
|
|
||
|
void printGrid(vector<vector<char>> grid) {
|
||
|
for (int y = 0; y < grid.size(); y++) {
|
||
|
for (int x = 0; x < grid[0].size(); x++) {
|
||
|
cout << grid[y][x];
|
||
|
}
|
||
|
cout << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void plotScore(vector<vector<char>> grid, set<tuple<int,int>> &visited, int x, int y, char c, int &perimeter, int &size) {
|
||
|
if (visited.count(make_tuple(x,y))) return;
|
||
|
visited.insert(make_tuple(x,y));
|
||
|
size++;
|
||
|
|
||
|
// above
|
||
|
if (y == 0 || grid[y - 1][x] != c) {
|
||
|
perimeter++;
|
||
|
} else if (!visited.count(make_tuple(x,y - 1))) {
|
||
|
plotScore(grid, visited, x, y - 1, c, perimeter, size);
|
||
|
}
|
||
|
// below
|
||
|
if (y == grid.size() - 1 || grid[y + 1][x] != c) {
|
||
|
perimeter++;
|
||
|
} else if (!visited.count(make_tuple(x,y + 1))) {
|
||
|
plotScore(grid, visited, x, y + 1, c, perimeter, size);
|
||
|
}
|
||
|
|
||
|
// left
|
||
|
if (x == 0 || grid[y][x - 1] != c) {
|
||
|
perimeter++;
|
||
|
} else if (!visited.count(make_tuple(x - 1,y))) {
|
||
|
plotScore(grid, visited, x - 1, y, c, perimeter, size);
|
||
|
}
|
||
|
// right
|
||
|
if (x == grid[0].size() - 1 || grid[y][x + 1] != c) {
|
||
|
perimeter++;
|
||
|
} else if (!visited.count(make_tuple(x + 1,y))) {
|
||
|
plotScore(grid, visited, x + 1, y, c, perimeter, size);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
enum position {
|
||
|
TL,
|
||
|
TR,
|
||
|
BL,
|
||
|
BR
|
||
|
};
|
||
|
|
||
|
void plotScoreCheaper(vector<vector<char>> grid, set<tuple<int,int>> &visited, set<tuple<int,int,position>> &visited_corners, int x, int y, char c, int &sides, int &size) {
|
||
|
if (visited.count(make_tuple(x,y))) return;
|
||
|
visited.insert(make_tuple(x,y));
|
||
|
size++;
|
||
|
|
||
|
// check if grid[y][x] is a corner, since #corners == #sides
|
||
|
/// top left
|
||
|
if (!visited_corners.count(make_tuple(x, y, TL)) &&
|
||
|
((x == 0 || grid[y][x - 1] != c) && (y == 0 || grid[y - 1][x] != c) // outer corner
|
||
|
|| (x > 0 && y > 0 && grid[y][x - 1] == c && grid[y - 1][x] == c && grid[y - 1][x - 1] != c))) // inner corner
|
||
|
{
|
||
|
sides++;
|
||
|
visited_corners.insert(make_tuple(x, y, TL));
|
||
|
}
|
||
|
/// bottom left
|
||
|
if (!visited_corners.count(make_tuple(x, y, BL)) &&
|
||
|
((x == 0 || grid[y][x - 1] != c) && (y == grid.size() - 1 || grid[y + 1][x] != c)) // outer corner
|
||
|
|| (x > 0 && y < grid.size() - 1 && grid[y][x - 1] == c && grid[y + 1][x] == c && grid[y + 1][x - 1] != c)) // inner corner
|
||
|
{
|
||
|
sides++;
|
||
|
visited_corners.insert(make_tuple(x, y, BL));
|
||
|
}
|
||
|
/// top right
|
||
|
if (!visited_corners.count(make_tuple(x, y, TR)) &&
|
||
|
((x == grid[y].size() - 1 || grid[y][x + 1] != c) && (y == 0 || grid[y - 1][x] != c)) // outer corner
|
||
|
|| (x < grid[y].size() - 1 && y > 0 && grid[y][x + 1] == c && grid[y - 1][x] == c && grid[y - 1][x + 1] != c)) // inner corner
|
||
|
{
|
||
|
sides++;
|
||
|
visited_corners.insert(make_tuple(x, y, TR));
|
||
|
}
|
||
|
/// bottom right
|
||
|
if (!visited_corners.count(make_tuple(x, y, BR)) &&
|
||
|
((x == grid[y].size() - 1 || grid[y][x + 1] != c) && (y == grid.size() - 1 || grid[y + 1][x] != c)) // outer corner
|
||
|
|| (x < grid[y].size() - 1 && y < grid.size() - 1 && grid[y][x + 1] == c && grid[y + 1][x] == c && grid[y + 1][x + 1] != c)) // inner corner
|
||
|
{
|
||
|
sides++;
|
||
|
visited_corners.insert(make_tuple(x, y, BR));
|
||
|
}
|
||
|
|
||
|
// now recurse on all 4 sides
|
||
|
if (y > 0 && !visited.count(make_tuple(x, y - 1)) && grid[y - 1][x] == c) {
|
||
|
plotScoreCheaper(grid, visited, visited_corners, x, y - 1, c, sides, size);
|
||
|
}
|
||
|
if (y < grid.size() - 1 && !visited.count(make_tuple(x, y + 1)) && grid[y + 1][x] == c) {
|
||
|
plotScoreCheaper(grid, visited, visited_corners, x, y + 1, c, sides, size);
|
||
|
}
|
||
|
if (x > 0 && !visited.count(make_tuple(x - 1, y)) && grid[y][x - 1] == c) {
|
||
|
plotScoreCheaper(grid, visited, visited_corners, x - 1, y, c, sides, size);
|
||
|
}
|
||
|
if (x < grid[y].size() - 1 && !visited.count(make_tuple(x + 1, y)) && grid[y][x + 1] == c) {
|
||
|
plotScoreCheaper(grid, visited, visited_corners, x + 1, y, c, sides, size);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int part1(const char *path) {
|
||
|
vector<vector<char>> grid = parseGrid(path);
|
||
|
|
||
|
int ret = 0;
|
||
|
|
||
|
set<tuple<int,int>> visited;
|
||
|
for (int y = 0; y < grid.size(); y++) {
|
||
|
for (int x = 0; x < grid[y].size(); x++) {
|
||
|
int perimeter = 0;
|
||
|
int size = 0;
|
||
|
plotScore(grid, visited, x, y, grid[y][x], perimeter, size);
|
||
|
ret += perimeter * size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int part2(const char *path) {
|
||
|
vector<vector<char>> grid = parseGrid(path);
|
||
|
set<tuple<int,int,position>> visited_corners;
|
||
|
|
||
|
int ret = 0;
|
||
|
|
||
|
set<tuple<int,int>> visited;
|
||
|
for (int y = 0; y < grid.size(); y++) {
|
||
|
for (int x = 0; x < grid[y].size(); x++) {
|
||
|
int sides = 0;
|
||
|
int size = 0;
|
||
|
plotScoreCheaper(grid, visited, visited_corners, x, y, grid[y][x], sides, size);
|
||
|
if (sides != 0) DBG("visited " << grid[y][x] << " got sides=" << sides << " and size=" << size << " result=" << sides * size << endl);
|
||
|
ret += sides * size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[]) {
|
||
|
cout << "example1: " << part1("example") << endl;
|
||
|
cout << "input1: " << part1("input") << endl;
|
||
|
cout << "example2: " << part2("example") << endl;
|
||
|
cout << "input2: " << part2("input") << endl;
|
||
|
return 0;
|
||
|
}
|