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
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Key, typename T, typename HASH = BAN::hash<Key>, bool STABLE = true>
|
||||||
class HashMapIterator;
|
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
|
||||||
class HashMap
|
class HashMap
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -35,12 +32,12 @@ namespace BAN
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HashMap() = default;
|
HashMap() = default;
|
||||||
HashMap(const HashMap<Key, T, HASH>&);
|
HashMap(const HashMap<Key, T, HASH, STABLE>&);
|
||||||
HashMap(HashMap<Key, T, HASH>&&);
|
HashMap(HashMap<Key, T, HASH, STABLE>&&);
|
||||||
~HashMap();
|
~HashMap();
|
||||||
|
|
||||||
HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&);
|
HashMap<Key, T, HASH, STABLE>& operator=(const HashMap<Key, T, HASH, STABLE>&);
|
||||||
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&);
|
HashMap<Key, T, HASH, STABLE>& operator=(HashMap<Key, T, HASH, STABLE>&&);
|
||||||
|
|
||||||
ErrorOr<void> insert(const Key&, const T&);
|
ErrorOr<void> insert(const Key&, const T&);
|
||||||
ErrorOr<void> insert(const Key&, T&&);
|
ErrorOr<void> insert(const Key&, T&&);
|
||||||
|
@ -77,26 +74,26 @@ namespace BAN
|
||||||
friend iterator;
|
friend iterator;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
HashMap<Key, T, HASH>::HashMap(const HashMap<Key, T, HASH>& other)
|
HashMap<Key, T, HASH, STABLE>::HashMap(const HashMap<Key, T, HASH, STABLE>& other)
|
||||||
{
|
{
|
||||||
*this = other;
|
*this = other;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
HashMap<Key, T, HASH>::HashMap(HashMap<Key, T, HASH>&& other)
|
HashMap<Key, T, HASH, STABLE>::HashMap(HashMap<Key, T, HASH, STABLE>&& other)
|
||||||
{
|
{
|
||||||
*this = move(other);
|
*this = move(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
HashMap<Key, T, HASH>::~HashMap()
|
HashMap<Key, T, HASH, STABLE>::~HashMap()
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(const HashMap<Key, T, HASH>& other)
|
HashMap<Key, T, HASH, STABLE>& HashMap<Key, T, HASH, STABLE>::operator=(const HashMap<Key, T, HASH, STABLE>& other)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
m_buckets = other.m_buckets;
|
m_buckets = other.m_buckets;
|
||||||
|
@ -104,8 +101,8 @@ namespace BAN
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(HashMap<Key, T, HASH>&& other)
|
HashMap<Key, T, HASH, STABLE>& HashMap<Key, T, HASH, STABLE>::operator=(HashMap<Key, T, HASH, STABLE>&& other)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
m_buckets = move(other.m_buckets);
|
m_buckets = move(other.m_buckets);
|
||||||
|
@ -114,21 +111,21 @@ namespace BAN
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, const T& value)
|
ErrorOr<void> HashMap<Key, T, HASH, STABLE>::insert(const Key& key, const T& value)
|
||||||
{
|
{
|
||||||
return insert(key, move(T(value)));
|
return insert(key, move(T(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, T&& value)
|
ErrorOr<void> HashMap<Key, T, HASH, STABLE>::insert(const Key& key, T&& value)
|
||||||
{
|
{
|
||||||
return emplace(key, move(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>
|
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));
|
ASSERT(!contains(key));
|
||||||
TRY(rebucket(m_size + 1));
|
TRY(rebucket(m_size + 1));
|
||||||
|
@ -140,15 +137,15 @@ namespace BAN
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
ErrorOr<void> HashMap<Key, T, HASH>::reserve(size_type size)
|
ErrorOr<void> HashMap<Key, T, HASH, STABLE>::reserve(size_type size)
|
||||||
{
|
{
|
||||||
TRY(rebucket(size));
|
TRY(rebucket(size));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
void HashMap<Key, T, HASH>::remove(const Key& key)
|
void HashMap<Key, T, HASH, STABLE>::remove(const Key& key)
|
||||||
{
|
{
|
||||||
if (empty()) return;
|
if (empty()) return;
|
||||||
auto& bucket = get_bucket(key);
|
auto& bucket = get_bucket(key);
|
||||||
|
@ -163,15 +160,15 @@ namespace BAN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
void HashMap<Key, T, HASH>::clear()
|
void HashMap<Key, T, HASH, STABLE>::clear()
|
||||||
{
|
{
|
||||||
m_buckets.clear();
|
m_buckets.clear();
|
||||||
m_size = 0;
|
m_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
T& HashMap<Key, T, HASH>::operator[](const Key& key)
|
T& HashMap<Key, T, HASH, STABLE>::operator[](const Key& key)
|
||||||
{
|
{
|
||||||
ASSERT(!empty());
|
ASSERT(!empty());
|
||||||
auto& bucket = get_bucket(key);
|
auto& bucket = get_bucket(key);
|
||||||
|
@ -181,8 +178,8 @@ namespace BAN
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
const T& HashMap<Key, T, HASH>::operator[](const Key& key) const
|
const T& HashMap<Key, T, HASH, STABLE>::operator[](const Key& key) const
|
||||||
{
|
{
|
||||||
ASSERT(!empty());
|
ASSERT(!empty());
|
||||||
const auto& bucket = get_bucket(key);
|
const auto& bucket = get_bucket(key);
|
||||||
|
@ -192,8 +189,8 @@ namespace BAN
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
bool HashMap<Key, T, HASH>::contains(const Key& key) const
|
bool HashMap<Key, T, HASH, STABLE>::contains(const Key& key) const
|
||||||
{
|
{
|
||||||
if (empty()) return false;
|
if (empty()) return false;
|
||||||
const auto& bucket = get_bucket(key);
|
const auto& bucket = get_bucket(key);
|
||||||
|
@ -203,20 +200,20 @@ namespace BAN
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
bool HashMap<Key, T, HASH>::empty() const
|
bool HashMap<Key, T, HASH, STABLE>::empty() const
|
||||||
{
|
{
|
||||||
return m_size == 0;
|
return m_size == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
typename HashMap<Key, T, HASH>::size_type HashMap<Key, T, HASH>::size() const
|
typename HashMap<Key, T, HASH, STABLE>::size_type HashMap<Key, T, HASH, STABLE>::size() const
|
||||||
{
|
{
|
||||||
return m_size;
|
return m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
ErrorOr<void> HashMap<Key, T, HASH>::rebucket(size_type bucket_count)
|
ErrorOr<void> HashMap<Key, T, HASH, STABLE>::rebucket(size_type bucket_count)
|
||||||
{
|
{
|
||||||
if (m_buckets.size() >= bucket_count)
|
if (m_buckets.size() >= bucket_count)
|
||||||
return {};
|
return {};
|
||||||
|
@ -225,16 +222,21 @@ namespace BAN
|
||||||
Vector<LinkedList<Entry>> new_buckets;
|
Vector<LinkedList<Entry>> new_buckets;
|
||||||
if (new_buckets.resize(new_bucket_count).is_error())
|
if (new_buckets.resize(new_bucket_count).is_error())
|
||||||
return Error::from_errno(ENOMEM);
|
return Error::from_errno(ENOMEM);
|
||||||
|
|
||||||
// NOTE: We have to copy the old entries to the new entries and not move
|
if constexpr(STABLE)
|
||||||
// since we might run out of memory half way through.
|
|
||||||
for (auto& bucket : m_buckets)
|
|
||||||
{
|
{
|
||||||
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();
|
for (Entry& entry : bucket)
|
||||||
if (new_buckets[bucket_index].push_back(entry).is_error())
|
{
|
||||||
return Error::from_errno(ENOMEM);
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
|
LinkedList<typename HashMap<Key, T, HASH, STABLE>::Entry>& HashMap<Key, T, HASH, STABLE>::get_bucket(const Key& key)
|
||||||
{
|
{
|
||||||
ASSERT(!m_buckets.empty());
|
ASSERT(!m_buckets.empty());
|
||||||
auto index = HASH()(key) % m_buckets.size();
|
auto index = HASH()(key) % m_buckets.size();
|
||||||
return m_buckets[index];
|
return m_buckets[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH, bool STABLE>
|
||||||
const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const
|
const LinkedList<typename HashMap<Key, T, HASH, STABLE>::Entry>& HashMap<Key, T, HASH, STABLE>::get_bucket(const Key& key) const
|
||||||
{
|
{
|
||||||
ASSERT(!m_buckets.empty());
|
ASSERT(!m_buckets.empty());
|
||||||
auto index = HASH()(key) % m_buckets.size();
|
auto index = HASH()(key) % m_buckets.size();
|
||||||
return m_buckets[index];
|
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;
|
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);
|
unsigned char* dst = static_cast<unsigned char*>(dstp);
|
||||||
const unsigned char* src = static_cast<const unsigned char*>(srcp);
|
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;
|
return destp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__attribute__((optimize("-O0")))
|
||||||
void* memset(void* s, int c, size_t n)
|
void* memset(void* s, int c, size_t n)
|
||||||
{
|
{
|
||||||
unsigned char* p = static_cast<unsigned char*>(s);
|
unsigned char* p = static_cast<unsigned char*>(s);
|
||||||
|
@ -144,6 +146,7 @@ char* strndup(const char* str, size_t size)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
__attribute__((optimize("-O0")))
|
||||||
size_t strlen(const char* str)
|
size_t strlen(const char* str)
|
||||||
{
|
{
|
||||||
size_t len = 0;
|
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[])
|
int execvp(const char* file, char* const argv[])
|
||||||
{
|
{
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
const char* pathname = file;
|
const char* pathname = NULL;
|
||||||
|
|
||||||
// do path resolution if file doesn't contain /
|
// do path resolution if file doesn't contain /
|
||||||
if (strchr(file, '/') == nullptr)
|
if (strchr(file, '/') == nullptr)
|
||||||
|
@ -218,6 +218,16 @@ int execvp(const char* file, char* const argv[])
|
||||||
cur++;
|
cur++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pathname = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pathname)
|
||||||
|
{
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return execve(pathname, argv, environ);
|
return execve(pathname, argv, environ);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ set(AOC2023_PROJECTS
|
||||||
day12
|
day12
|
||||||
day13
|
day13
|
||||||
day14
|
day14
|
||||||
|
day15
|
||||||
|
day16
|
||||||
)
|
)
|
||||||
|
|
||||||
set(BANAN_AOC2023_BIN ${BANAN_BIN}/aoc2023)
|
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