Compare commits
7 Commits
f46240e879
...
3963afe343
Author | SHA1 | Date |
---|---|---|
Bananymous | 3963afe343 | |
Bananymous | 3b21cc90ae | |
Bananymous | 4e900804b8 | |
Bananymous | 9ec733904f | |
Bananymous | 3352640d09 | |
Bananymous | 637397dd2f | |
Bananymous | 5edbb1d5c4 |
|
@ -7,10 +7,7 @@
|
|||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename Container>
|
||||
class HashMapIterator;
|
||||
|
||||
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
||||
template<typename Key, typename T, typename HASH = BAN::hash<Key>, bool STABLE = true>
|
||||
class HashMap
|
||||
{
|
||||
public:
|
||||
|
@ -35,12 +32,12 @@ namespace BAN
|
|||
|
||||
public:
|
||||
HashMap() = default;
|
||||
HashMap(const HashMap<Key, T, HASH>&);
|
||||
HashMap(HashMap<Key, T, HASH>&&);
|
||||
HashMap(const HashMap<Key, T, HASH, STABLE>&);
|
||||
HashMap(HashMap<Key, T, HASH, STABLE>&&);
|
||||
~HashMap();
|
||||
|
||||
HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&);
|
||||
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&);
|
||||
HashMap<Key, T, HASH, STABLE>& operator=(const HashMap<Key, T, HASH, STABLE>&);
|
||||
HashMap<Key, T, HASH, STABLE>& operator=(HashMap<Key, T, HASH, STABLE>&&);
|
||||
|
||||
ErrorOr<void> insert(const Key&, const T&);
|
||||
ErrorOr<void> insert(const Key&, T&&);
|
||||
|
@ -77,26 +74,26 @@ namespace BAN
|
|||
friend iterator;
|
||||
};
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
HashMap<Key, T, HASH>::HashMap(const HashMap<Key, T, HASH>& other)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
HashMap<Key, T, HASH, STABLE>::HashMap(const HashMap<Key, T, HASH, STABLE>& other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
HashMap<Key, T, HASH>::HashMap(HashMap<Key, T, HASH>&& other)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
HashMap<Key, T, HASH, STABLE>::HashMap(HashMap<Key, T, HASH, STABLE>&& other)
|
||||
{
|
||||
*this = move(other);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
HashMap<Key, T, HASH>::~HashMap()
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
HashMap<Key, T, HASH, STABLE>::~HashMap()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(const HashMap<Key, T, HASH>& other)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
HashMap<Key, T, HASH, STABLE>& HashMap<Key, T, HASH, STABLE>::operator=(const HashMap<Key, T, HASH, STABLE>& other)
|
||||
{
|
||||
clear();
|
||||
m_buckets = other.m_buckets;
|
||||
|
@ -104,8 +101,8 @@ namespace BAN
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(HashMap<Key, T, HASH>&& other)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
HashMap<Key, T, HASH, STABLE>& HashMap<Key, T, HASH, STABLE>::operator=(HashMap<Key, T, HASH, STABLE>&& other)
|
||||
{
|
||||
clear();
|
||||
m_buckets = move(other.m_buckets);
|
||||
|
@ -114,21 +111,21 @@ namespace BAN
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, const T& value)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
ErrorOr<void> HashMap<Key, T, HASH, STABLE>::insert(const Key& key, const T& value)
|
||||
{
|
||||
return insert(key, move(T(value)));
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, T&& value)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
ErrorOr<void> HashMap<Key, T, HASH, STABLE>::insert(const Key& key, T&& value)
|
||||
{
|
||||
return emplace(key, move(value));
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::emplace(const Key& key, Args&&... args)
|
||||
ErrorOr<void> HashMap<Key, T, HASH, STABLE>::emplace(const Key& key, Args&&... args)
|
||||
{
|
||||
ASSERT(!contains(key));
|
||||
TRY(rebucket(m_size + 1));
|
||||
|
@ -140,15 +137,15 @@ namespace BAN
|
|||
return {};
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::reserve(size_type size)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
ErrorOr<void> HashMap<Key, T, HASH, STABLE>::reserve(size_type size)
|
||||
{
|
||||
TRY(rebucket(size));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
void HashMap<Key, T, HASH>::remove(const Key& key)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
void HashMap<Key, T, HASH, STABLE>::remove(const Key& key)
|
||||
{
|
||||
if (empty()) return;
|
||||
auto& bucket = get_bucket(key);
|
||||
|
@ -163,15 +160,15 @@ namespace BAN
|
|||
}
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
void HashMap<Key, T, HASH>::clear()
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
void HashMap<Key, T, HASH, STABLE>::clear()
|
||||
{
|
||||
m_buckets.clear();
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
T& HashMap<Key, T, HASH>::operator[](const Key& key)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
T& HashMap<Key, T, HASH, STABLE>::operator[](const Key& key)
|
||||
{
|
||||
ASSERT(!empty());
|
||||
auto& bucket = get_bucket(key);
|
||||
|
@ -181,8 +178,8 @@ namespace BAN
|
|||
ASSERT(false);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
const T& HashMap<Key, T, HASH>::operator[](const Key& key) const
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
const T& HashMap<Key, T, HASH, STABLE>::operator[](const Key& key) const
|
||||
{
|
||||
ASSERT(!empty());
|
||||
const auto& bucket = get_bucket(key);
|
||||
|
@ -192,8 +189,8 @@ namespace BAN
|
|||
ASSERT(false);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
bool HashMap<Key, T, HASH>::contains(const Key& key) const
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
bool HashMap<Key, T, HASH, STABLE>::contains(const Key& key) const
|
||||
{
|
||||
if (empty()) return false;
|
||||
const auto& bucket = get_bucket(key);
|
||||
|
@ -203,20 +200,20 @@ namespace BAN
|
|||
return false;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
bool HashMap<Key, T, HASH>::empty() const
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
bool HashMap<Key, T, HASH, STABLE>::empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
typename HashMap<Key, T, HASH>::size_type HashMap<Key, T, HASH>::size() const
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
typename HashMap<Key, T, HASH, STABLE>::size_type HashMap<Key, T, HASH, STABLE>::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::rebucket(size_type bucket_count)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
ErrorOr<void> HashMap<Key, T, HASH, STABLE>::rebucket(size_type bucket_count)
|
||||
{
|
||||
if (m_buckets.size() >= bucket_count)
|
||||
return {};
|
||||
|
@ -226,15 +223,20 @@ namespace BAN
|
|||
if (new_buckets.resize(new_bucket_count).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
// NOTE: We have to copy the old entries to the new entries and not move
|
||||
// since we might run out of memory half way through.
|
||||
for (auto& bucket : m_buckets)
|
||||
if constexpr(STABLE)
|
||||
{
|
||||
for (Entry& entry : bucket)
|
||||
// NOTE: We have to copy the old entries to the new entries and not move
|
||||
// since we might run out of memory half way through.
|
||||
for (auto& bucket : m_buckets)
|
||||
{
|
||||
size_type bucket_index = HASH()(entry.key) % new_buckets.size();
|
||||
if (new_buckets[bucket_index].push_back(entry).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
for (Entry& entry : bucket)
|
||||
{
|
||||
size_type bucket_index = HASH()(entry.key) % new_buckets.size();
|
||||
if constexpr(STABLE)
|
||||
TRY(new_buckets[bucket_index].push_back(entry));
|
||||
else
|
||||
TRY(new_buckets[bucket_index].push_back(BAN::move(entry)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,20 +244,27 @@ namespace BAN
|
|||
return {};
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
LinkedList<typename HashMap<Key, T, HASH, STABLE>::Entry>& HashMap<Key, T, HASH, STABLE>::get_bucket(const Key& key)
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
auto index = HASH()(key) % m_buckets.size();
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const
|
||||
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||
const LinkedList<typename HashMap<Key, T, HASH, STABLE>::Entry>& HashMap<Key, T, HASH, STABLE>::get_bucket(const Key& key) const
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
auto index = HASH()(key) % m_buckets.size();
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
// Unstable hash map moves values between container during rebucketing.
|
||||
// This means that if insertion to map fails, elements could be in invalid state
|
||||
// and that container is no longer usable. This is better if either way you are
|
||||
// going to stop using the hash map after insertion fails.
|
||||
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
||||
using HashMapUnstable = HashMap<Key, T, HASH, false>;
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ int memcmp(const void* s1, const void* s2, size_t n)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void* memcpy(void* dstp, const void* srcp, size_t n)
|
||||
__attribute__((optimize("-O0")))
|
||||
void* memcpy(void* __restrict__ dstp, const void* __restrict__ srcp, size_t n)
|
||||
{
|
||||
unsigned char* dst = static_cast<unsigned char*>(dstp);
|
||||
const unsigned char* src = static_cast<const unsigned char*>(srcp);
|
||||
|
@ -45,6 +46,7 @@ void* memmove(void* destp, const void* srcp, size_t n)
|
|||
return destp;
|
||||
}
|
||||
|
||||
__attribute__((optimize("-O0")))
|
||||
void* memset(void* s, int c, size_t n)
|
||||
{
|
||||
unsigned char* p = static_cast<unsigned char*>(s);
|
||||
|
@ -144,6 +146,7 @@ char* strndup(const char* str, size_t size)
|
|||
}
|
||||
#endif
|
||||
|
||||
__attribute__((optimize("-O0")))
|
||||
size_t strlen(const char* str)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
|
|
@ -183,7 +183,7 @@ int execve(const char* pathname, char* const argv[], char* const envp[])
|
|||
int execvp(const char* file, char* const argv[])
|
||||
{
|
||||
char buffer[1024];
|
||||
const char* pathname = file;
|
||||
const char* pathname = NULL;
|
||||
|
||||
// do path resolution if file doesn't contain /
|
||||
if (strchr(file, '/') == nullptr)
|
||||
|
@ -218,6 +218,16 @@ int execvp(const char* file, char* const argv[])
|
|||
cur++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pathname = file;
|
||||
}
|
||||
|
||||
if (!pathname)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return execve(pathname, argv, environ);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ set(AOC2023_PROJECTS
|
|||
day12
|
||||
day13
|
||||
day14
|
||||
day15
|
||||
day16
|
||||
)
|
||||
|
||||
set(BANAN_AOC2023_BIN ${BANAN_BIN}/aoc2023)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(aoc2023_day15 CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2023_day15 ${SOURCES})
|
||||
target_compile_options(aoc2023_day15 PUBLIC -O2 -g)
|
||||
target_link_libraries(aoc2023_day15 PUBLIC libc ban)
|
||||
|
||||
add_dependencies(aoc2023_day15 libc-install ban-install)
|
||||
|
||||
add_custom_target(aoc2023_day15-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day15 ${BANAN_AOC2023_BIN}/day15
|
||||
DEPENDS aoc2023_day15
|
||||
DEPENDS aoc2023_always
|
||||
)
|
||||
|
||||
add_dependencies(aoc2023 aoc2023_day15)
|
||||
add_dependencies(aoc2023-install aoc2023_day15-install)
|
|
@ -0,0 +1,150 @@
|
|||
#include <BAN/Array.h>
|
||||
#include <BAN/LinkedList.h>
|
||||
#include <BAN/String.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
using i8 = int8_t;
|
||||
using i16 = int16_t;
|
||||
using i32 = int32_t;
|
||||
using i64 = int64_t;
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
|
||||
i64 parse_i64(BAN::StringView string)
|
||||
{
|
||||
i64 result = 0;
|
||||
for (char c : string)
|
||||
result = (result * 10) + (c - '0');
|
||||
return result;
|
||||
}
|
||||
|
||||
BAN::Vector<BAN::StringView> parse_instructions(FILE* fp)
|
||||
{
|
||||
static BAN::String line;
|
||||
|
||||
char buffer[128];
|
||||
while (fgets(buffer, sizeof(buffer), fp))
|
||||
{
|
||||
BAN::StringView buffer_sv(buffer);
|
||||
MUST(line.append(buffer_sv));
|
||||
if (buffer_sv.back() == '\n')
|
||||
{
|
||||
line.pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return MUST(line.sv().split(','));
|
||||
}
|
||||
|
||||
i64 calculate_hash(BAN::StringView string)
|
||||
{
|
||||
i64 result = 0;
|
||||
for (char c : string)
|
||||
result = ((result + c) * 17) % 256;
|
||||
return result;
|
||||
}
|
||||
|
||||
i64 puzzle1(FILE* fp)
|
||||
{
|
||||
auto instructions = parse_instructions(fp);
|
||||
|
||||
i64 result = 0;
|
||||
for (auto insn : instructions)
|
||||
result += calculate_hash(insn);
|
||||
return result;
|
||||
}
|
||||
|
||||
i64 puzzle2(FILE* fp)
|
||||
{
|
||||
struct Entry
|
||||
{
|
||||
BAN::String label;
|
||||
i64 focal_length;
|
||||
};
|
||||
using Box = BAN::LinkedList<Entry>;
|
||||
|
||||
auto instructions = parse_instructions(fp);
|
||||
|
||||
BAN::Array<Box, 256> boxes;
|
||||
|
||||
for (auto insn : instructions)
|
||||
{
|
||||
if (insn.back() == '-')
|
||||
{
|
||||
auto label = insn.substring(0, insn.size() - 1);
|
||||
i64 hash = calculate_hash(label);
|
||||
|
||||
for (auto it = boxes[hash].begin(); it != boxes[hash].end(); it++)
|
||||
{
|
||||
if (it->label == label)
|
||||
{
|
||||
boxes[hash].remove(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto temp = MUST(insn.split('='));
|
||||
|
||||
auto label = temp[0];
|
||||
auto focal_length = parse_i64(temp[1]);
|
||||
i64 hash = calculate_hash(label);
|
||||
|
||||
bool found = false;
|
||||
for (auto it = boxes[hash].begin(); it != boxes[hash].end(); it++)
|
||||
{
|
||||
if (it->label == label)
|
||||
{
|
||||
it->focal_length = focal_length;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
MUST(boxes[hash].emplace_back(label, focal_length));
|
||||
}
|
||||
}
|
||||
|
||||
i64 result = 0;
|
||||
for (size_t i = 0; i < boxes.size(); i++)
|
||||
{
|
||||
size_t slot = 0;
|
||||
for (auto it = boxes[i].begin(); it != boxes[i].end(); it++, slot++)
|
||||
result += (i + 1) * (slot + 1) * it->focal_length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2023/day15_input.txt";
|
||||
|
||||
if (argc >= 2)
|
||||
file_path = argv[1];
|
||||
|
||||
FILE* fp = fopen(file_path, "r");
|
||||
if (fp == nullptr)
|
||||
{
|
||||
perror("fopen");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("puzzle1: %" PRId64 "\n", puzzle1(fp));
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
printf("puzzle2: %" PRId64 "\n", puzzle2(fp));
|
||||
|
||||
fclose(fp);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(aoc2023_day16 CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2023_day16 ${SOURCES})
|
||||
target_compile_options(aoc2023_day16 PUBLIC -O2 -g)
|
||||
target_link_libraries(aoc2023_day16 PUBLIC libc ban)
|
||||
|
||||
add_dependencies(aoc2023_day16 libc-install ban-install)
|
||||
|
||||
add_custom_target(aoc2023_day16-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day16 ${BANAN_AOC2023_BIN}/day16
|
||||
DEPENDS aoc2023_day16
|
||||
DEPENDS aoc2023_always
|
||||
)
|
||||
|
||||
add_dependencies(aoc2023 aoc2023_day16)
|
||||
add_dependencies(aoc2023-install aoc2023_day16-install)
|
|
@ -0,0 +1,232 @@
|
|||
#include <BAN/HashSet.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
using i8 = int8_t;
|
||||
using i16 = int16_t;
|
||||
using i32 = int32_t;
|
||||
using i64 = int64_t;
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
|
||||
enum class Tile { Empty, PositiveMirror, NegativeMirror, HorizontalSplitter, VerticalSplitter };
|
||||
enum class Direction { North, South, East, West };
|
||||
|
||||
using Grid = BAN::Vector<BAN::Vector<Tile>>;
|
||||
|
||||
struct Position
|
||||
{
|
||||
i64 y;
|
||||
i64 x;
|
||||
Direction dir;
|
||||
|
||||
bool operator==(const Position& other) const
|
||||
{
|
||||
return x == other.x && y == other.y && dir == other.dir;
|
||||
}
|
||||
};
|
||||
|
||||
struct PositionHash
|
||||
{
|
||||
BAN::hash_t operator()(Position position) const
|
||||
{
|
||||
return BAN::hash<i64>()((position.y << 32) | position.x) ^ BAN::hash<i8>()(static_cast<i8>(position.dir));
|
||||
}
|
||||
};
|
||||
|
||||
Grid parse_grid(FILE* fp)
|
||||
{
|
||||
Grid grid;
|
||||
|
||||
auto char_to_tile =
|
||||
[](char c)
|
||||
{
|
||||
if (c == '.')
|
||||
return Tile::Empty;
|
||||
if (c == '/')
|
||||
return Tile::PositiveMirror;
|
||||
if (c == '\\')
|
||||
return Tile::NegativeMirror;
|
||||
if (c == '-')
|
||||
return Tile::HorizontalSplitter;
|
||||
if (c == '|')
|
||||
return Tile::VerticalSplitter;
|
||||
ASSERT_NOT_REACHED();
|
||||
};
|
||||
|
||||
char buffer[128];
|
||||
while (fgets(buffer, sizeof(buffer), fp))
|
||||
{
|
||||
BAN::StringView line(buffer);
|
||||
ASSERT(line.back() == '\n');
|
||||
line = line.substring(0, line.size() - 1);
|
||||
if (line.empty())
|
||||
break;
|
||||
|
||||
MUST(grid.emplace_back(line.size()));
|
||||
for (size_t i = 0; i < line.size(); i++)
|
||||
grid.back()[i] = char_to_tile(line[i]);
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
BAN::Vector<Position> get_next_positions(Position position, const Grid& grid)
|
||||
{
|
||||
auto tile = grid[position.y][position.x];
|
||||
|
||||
BAN::Vector<Position> next_positions;
|
||||
|
||||
switch (tile)
|
||||
{
|
||||
case Tile::Empty:
|
||||
switch (position.dir)
|
||||
{
|
||||
case Direction::North: MUST(next_positions.emplace_back(position.y - 1, position.x, Direction::North)); break;
|
||||
case Direction::South: MUST(next_positions.emplace_back(position.y + 1, position.x, Direction::South)); break;
|
||||
case Direction::West: MUST(next_positions.emplace_back(position.y, position.x - 1, Direction::West)); break;
|
||||
case Direction::East: MUST(next_positions.emplace_back(position.y, position.x + 1, Direction::East)); break;
|
||||
}
|
||||
break;
|
||||
case Tile::PositiveMirror:
|
||||
switch (position.dir)
|
||||
{
|
||||
case Direction::North: MUST(next_positions.emplace_back(position.y, position.x + 1, Direction::East)); break;
|
||||
case Direction::South: MUST(next_positions.emplace_back(position.y, position.x - 1, Direction::West)); break;
|
||||
case Direction::West: MUST(next_positions.emplace_back(position.y + 1, position.x, Direction::South)); break;
|
||||
case Direction::East: MUST(next_positions.emplace_back(position.y - 1, position.x, Direction::North)); break;
|
||||
}
|
||||
break;
|
||||
case Tile::NegativeMirror:
|
||||
switch (position.dir)
|
||||
{
|
||||
case Direction::North: MUST(next_positions.emplace_back(position.y, position.x - 1, Direction::West)); break;
|
||||
case Direction::South: MUST(next_positions.emplace_back(position.y, position.x + 1, Direction::East)); break;
|
||||
case Direction::West: MUST(next_positions.emplace_back(position.y - 1, position.x, Direction::North)); break;
|
||||
case Direction::East: MUST(next_positions.emplace_back(position.y + 1, position.x, Direction::South)); break;
|
||||
}
|
||||
break;
|
||||
case Tile::HorizontalSplitter:
|
||||
switch (position.dir)
|
||||
{
|
||||
case Direction::North:
|
||||
case Direction::South:
|
||||
MUST(next_positions.emplace_back(position.y, position.x - 1, Direction::West));
|
||||
MUST(next_positions.emplace_back(position.y, position.x + 1, Direction::East));
|
||||
break;
|
||||
case Direction::West: MUST(next_positions.emplace_back(position.y, position.x - 1, Direction::West)); break;
|
||||
case Direction::East: MUST(next_positions.emplace_back(position.y, position.x + 1, Direction::East)); break;
|
||||
}
|
||||
break;
|
||||
case Tile::VerticalSplitter:
|
||||
switch (position.dir)
|
||||
{
|
||||
case Direction::North: MUST(next_positions.emplace_back(position.y - 1, position.x, Direction::North)); break;
|
||||
case Direction::South: MUST(next_positions.emplace_back(position.y + 1, position.x, Direction::South)); break;
|
||||
case Direction::West:
|
||||
case Direction::East:
|
||||
MUST(next_positions.emplace_back(position.y - 1, position.x, Direction::North));
|
||||
MUST(next_positions.emplace_back(position.y + 1, position.x, Direction::South));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < next_positions.size();)
|
||||
{
|
||||
if (next_positions[i].y < 0 || next_positions[i].y >= (i64)grid.size())
|
||||
next_positions.remove(i);
|
||||
else if (next_positions[i].x < 0 || next_positions[i].x >= (i64)grid.front().size())
|
||||
next_positions.remove(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
|
||||
return next_positions;
|
||||
}
|
||||
|
||||
i64 count_energized_tiles(const Grid& grid, Position start)
|
||||
{
|
||||
BAN::HashSet<Position, PositionHash> visited;
|
||||
BAN::HashSet<Position, PositionHash> energized;
|
||||
|
||||
BAN::Vector<Position> current_positions;
|
||||
MUST(current_positions.push_back(start));
|
||||
MUST(visited.insert(current_positions.front()));
|
||||
|
||||
while (!current_positions.empty())
|
||||
{
|
||||
auto position = current_positions.back();
|
||||
current_positions.pop_back();
|
||||
|
||||
MUST(energized.insert({ position.y, position.x, Direction::North }));
|
||||
|
||||
auto next_positions = get_next_positions(position, grid);
|
||||
for (auto next : next_positions)
|
||||
{
|
||||
if (visited.contains(next))
|
||||
continue;
|
||||
MUST(visited.insert(next));
|
||||
MUST(current_positions.push_back(next));
|
||||
}
|
||||
}
|
||||
|
||||
return energized.size();
|
||||
}
|
||||
|
||||
i64 puzzle1(FILE* fp)
|
||||
{
|
||||
auto grid = parse_grid(fp);
|
||||
return count_energized_tiles(grid, { 0, 0, Direction::East });
|
||||
}
|
||||
|
||||
i64 puzzle2(FILE* fp)
|
||||
{
|
||||
auto grid = parse_grid(fp);
|
||||
|
||||
i64 max_energized = 0;
|
||||
|
||||
for (i64 y = 0; y < (i64)grid.size(); y++)
|
||||
{
|
||||
max_energized = BAN::Math::max(max_energized, count_energized_tiles(grid, { y, 0, Direction::East }));
|
||||
max_energized = BAN::Math::max(max_energized, count_energized_tiles(grid, { y, (i64)grid.front().size() - 1, Direction::West }));
|
||||
}
|
||||
|
||||
for (i64 x = 0; x < (i64)grid.front().size(); x++)
|
||||
{
|
||||
max_energized = BAN::Math::max(max_energized, count_energized_tiles(grid, { 0, x, Direction::South }));
|
||||
max_energized = BAN::Math::max(max_energized, count_energized_tiles(grid, { (i64)grid.size() - 1, x, Direction::North }));
|
||||
}
|
||||
|
||||
return max_energized;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2023/day16_input.txt";
|
||||
|
||||
if (argc >= 2)
|
||||
file_path = argv[1];
|
||||
|
||||
FILE* fp = fopen(file_path, "r");
|
||||
if (fp == nullptr)
|
||||
{
|
||||
perror("fopen");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("puzzle1: %" PRId64 "\n", puzzle1(fp));
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
printf("puzzle2: %" PRId64 "\n", puzzle2(fp));
|
||||
|
||||
fclose(fp);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
day*_input.txt
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
wget --no-cookies --header "Cookie: session=$AOC_SESSION" https://adventofcode.com/2023/day/$1/input -O day$1_input.txt
|
Loading…
Reference in New Issue