diff --git a/day17/Makefile b/day17/Makefile new file mode 100644 index 0000000..b6b95eb --- /dev/null +++ b/day17/Makefile @@ -0,0 +1,15 @@ +CC=g++ +BIN_NAME=day17 +CCFLAGS=-o $(BIN_NAME) -g -std=c++17 +LDFLAGS=-lfolly + +.PHONY: all, clean + +all: $(BIN_NAME) + +clean: + rm -f $(BIN_NAME) + +$(BIN_NAME): day17.cpp + $(CC) $(CCFLAGS) $(LDFLAGS) day17.cpp + diff --git a/day17/day17.cpp b/day17/day17.cpp new file mode 100644 index 0000000..30c1167 --- /dev/null +++ b/day17/day17.cpp @@ -0,0 +1,164 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +constexpr char ALIVE_CHAR = '#'; +constexpr char DEAD_CHAR = '.'; +constexpr int PART_1_CYCLE_COUNT = 6; + +enum CellState { ALIVE, DEAD }; + +using Board = std::map, CellState>; + +std::vector readInput(const std::string &filename) { + std::vector input; + std::string line; + std::ifstream file(filename); + while (std::getline(file, line)) { + input.push_back(line); + } + + return input; +} + +Board parseBoard(const std::vector &input) { + Board board; + int rowCursor = 0; + for (auto rowIterator = input.cbegin(); rowIterator != input.cend(); (rowCursor++, ++rowIterator)) { + auto &line = *rowIterator; + int colCursor = 0; + for (auto colIterator = line.cbegin(); colIterator != line.cend(); (colCursor++, ++colIterator)) { + CellState state = *colIterator == ALIVE_CHAR ? ALIVE : DEAD; + std::tuple position(rowCursor, colCursor, 0); + board.emplace(std::move(position), std::move(state)); + } + } + + return board; +} + +int getNumAdjacentLiveNeighbors(const Board &board, int row, int col, int depth) { + int count = 0; + for (int dRow = -1; dRow <= 1; dRow++) { + for (int dCol = -1; dCol <= 1; dCol++) { + for (int dDepth = -1; dDepth <= 1; dDepth++) { + if (dRow == 0 && dCol == 0 && dDepth == 0) { + continue; + } + + std::tuple position(row + dRow, col + dCol, depth + dDepth); + if (board.find(position) == board.end()) { + continue; + } + + count += (board.at(position) == ALIVE); + } + } + } + + return count; +} + +template +std::pair getMinMax(const Board &board) { + std::vector, CellState>> alivePositions; + std::copy_if( + board.cbegin(), + board.cend(), + std::back_inserter(alivePositions), + [](const std::pair, CellState> &item) { return item.second == ALIVE; }); + + auto elements = std::minmax_element( + alivePositions.cbegin(), + alivePositions.cend(), + [](const std::pair, CellState> &item, + const std::pair, CellState> &item2) { + return std::get(item.first) < std::get(item2.first); + }); + int minComponent = std::get(elements.first->first); + int maxComponent = std::get(elements.second->first); + + return std::pair(minComponent, maxComponent); +} + +std::pair, std::tuple> getRangeOnEachAxis(const Board &board) { + std::pair rowRange = getMinMax<0>(board); + std::pair colRange = getMinMax<1>(board); + std::pair depthRange = getMinMax<2>(board); + + return std::pair, std::tuple>( + std::tuple(rowRange.first, colRange.first, depthRange.first), + std::tuple(rowRange.second, colRange.second, depthRange.second)); +} + +template +Val getOrDefault(const std::map &map, Val defaultValue, Key key) { + if (map.find(key) == map.end()) { + return defaultValue; + } + + return map.at(key); +} + +void printBoard(const Board &board) { + auto ranges = getRangeOnEachAxis(board); + for (int depth = std::get<2>(ranges.first); depth <= std::get<2>(ranges.second); depth++) { + std::cout << "Depth z=" << depth << std::endl; + for (int row = std::get<0>(ranges.first); row <= std::get<0>(ranges.second); row++) { + for (int col = std::get<1>(ranges.first); col <= std::get<1>(ranges.second); col++) { + std::tuple position(row, col, depth); + CellState state = getOrDefault(board, DEAD, position); + std::cout << ((state == ALIVE) ? ALIVE_CHAR : DEAD_CHAR); + } + std::cout << std::endl; + } + } + std::cout << std::endl; +} + +int part1(const std::vector &input) { + Board board = parseBoard(input); + Board nextBoard = board; + for (int i = 0; i < PART_1_CYCLE_COUNT; i++) { + printBoard(board); + auto ranges = getRangeOnEachAxis(board); + for (int row = std::get<0>(ranges.first) - 1; row <= std::get<0>(ranges.second) + 1; row++) { + for (int col = std::get<1>(ranges.first) - 1; col <= std::get<1>(ranges.second) + 1; col++) { + for (int depth = std::get<2>(ranges.first) - 1; depth <= std::get<2>(ranges.second) + 1; depth++) { + int aliveNeighbors = getNumAdjacentLiveNeighbors(board, row, col, depth); + std::tuple position(row, col, depth); + CellState state = getOrDefault(board, DEAD, position); + if (aliveNeighbors != 2 && aliveNeighbors != 3) { + state = DEAD; + } else if (state == DEAD && aliveNeighbors == 3) { + state = ALIVE; + } + + nextBoard[position] = state; + } + } + } + std::swap(nextBoard, board); + } + + return std::count_if(board.cbegin(), board.cend(), [](const std::pair, CellState> &item) { + return item.second == ALIVE; + }); +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + std::cerr << argv[0] << " " << std::endl; + return 1; + } + + auto input = readInput(argv[1]); + + std::cout << part1(input) << std::endl; +}