Compare commits
19 Commits
3963afe343
...
910a57089b
Author | SHA1 | Date |
---|---|---|
Bananymous | 910a57089b | |
Bananymous | 861bf27e96 | |
Bananymous | 36590fb5c7 | |
Bananymous | ce990c3026 | |
Bananymous | b833239a82 | |
Bananymous | 6fec142760 | |
Bananymous | 84b2438b3d | |
Bananymous | 0e714d5eb4 | |
Bananymous | 9b8e6e6629 | |
Bananymous | 4146f2777b | |
Bananymous | 64323c51e6 | |
Bananymous | a0200a7b10 | |
Bananymous | 8add759b5d | |
Bananymous | 2faf90bc2b | |
Bananymous | 762d575d70 | |
Bananymous | 2e77718f07 | |
Bananymous | f371fabe35 | |
Bananymous | 79a15132da | |
Bananymous | bacc0db778 |
|
@ -134,6 +134,16 @@ namespace BAN
|
|||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
bool String::operator==(const String& str) const
|
||||
{
|
||||
if (size() != str.size())
|
||||
return false;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (data()[i] != str.data()[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool String::operator==(StringView str) const
|
||||
{
|
||||
if (size() != str.size())
|
||||
|
|
|
@ -145,6 +145,22 @@ namespace BAN
|
|||
return m_data[0];
|
||||
}
|
||||
|
||||
BAN::Optional<StringView::size_type> StringView::find(char ch) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
return i;
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::Optional<StringView::size_type> StringView::find(bool(*comp)(char)) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (comp(m_data[i]))
|
||||
return i;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool StringView::contains(char ch) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
|
|
|
@ -130,9 +130,7 @@ namespace BAN
|
|||
ASSERT(!contains(key));
|
||||
TRY(rebucket(m_size + 1));
|
||||
auto& bucket = get_bucket(key);
|
||||
auto result = bucket.emplace_back(key, forward<Args>(args)...);
|
||||
if (result.is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
TRY(bucket.emplace_back(key, forward<Args>(args)...));
|
||||
m_size++;
|
||||
return {};
|
||||
}
|
||||
|
@ -220,23 +218,17 @@ namespace BAN
|
|||
|
||||
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||
Vector<LinkedList<Entry>> new_buckets;
|
||||
if (new_buckets.resize(new_bucket_count).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
TRY(new_buckets.resize(new_bucket_count));
|
||||
|
||||
if constexpr(STABLE)
|
||||
for (auto& bucket : m_buckets)
|
||||
{
|
||||
// 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)
|
||||
for (Entry& entry : bucket)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
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(move(entry)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Hash.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/LinkedList.h>
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
@ -9,24 +11,22 @@
|
|||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T, typename HASH>
|
||||
class HashSetIterator;
|
||||
|
||||
template<typename T, typename HASH = hash<T>>
|
||||
template<typename T, typename HASH = hash<T>, bool STABLE = true>
|
||||
class HashSet
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = hash_t;
|
||||
using const_iterator = HashSetIterator<T, HASH>;
|
||||
using size_type = size_t;
|
||||
using iterator = IteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||
using const_iterator = ConstIteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||
|
||||
public:
|
||||
HashSet() = default;
|
||||
HashSet(const HashSet<T, HASH>&);
|
||||
HashSet(HashSet<T, HASH>&&);
|
||||
HashSet(const HashSet&);
|
||||
HashSet(HashSet&&);
|
||||
|
||||
HashSet<T, HASH>& operator=(const HashSet<T, HASH>&);
|
||||
HashSet<T, HASH>& operator=(HashSet<T, HASH>&&);
|
||||
HashSet& operator=(const HashSet&);
|
||||
HashSet& operator=(HashSet&&);
|
||||
|
||||
ErrorOr<void> insert(const T&);
|
||||
ErrorOr<void> insert(T&&);
|
||||
|
@ -35,8 +35,10 @@ namespace BAN
|
|||
|
||||
ErrorOr<void> reserve(size_type);
|
||||
|
||||
const_iterator begin() const { return const_iterator(this, m_buckets.begin()); }
|
||||
const_iterator end() const { return const_iterator(this, m_buckets.end()); }
|
||||
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
|
||||
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
|
||||
const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
|
||||
const_iterator end() const { return const_iterator(m_buckets.end(), m_buckets.end()); }
|
||||
|
||||
bool contains(const T&) const;
|
||||
|
||||
|
@ -45,63 +47,31 @@ namespace BAN
|
|||
|
||||
private:
|
||||
ErrorOr<void> rebucket(size_type);
|
||||
Vector<T>& get_bucket(const T&);
|
||||
const Vector<T>& get_bucket(const T&) const;
|
||||
LinkedList<T>& get_bucket(const T&);
|
||||
const LinkedList<T>& get_bucket(const T&) const;
|
||||
|
||||
private:
|
||||
Vector<Vector<T>> m_buckets;
|
||||
Vector<LinkedList<T>> m_buckets;
|
||||
size_type m_size = 0;
|
||||
|
||||
friend class HashSetIterator<T, HASH>;
|
||||
};
|
||||
|
||||
template<typename T, typename HASH>
|
||||
class HashSetIterator
|
||||
{
|
||||
public:
|
||||
HashSetIterator() = default;
|
||||
HashSetIterator(const HashSetIterator<T, HASH>&);
|
||||
|
||||
HashSetIterator<T, HASH>& operator++();
|
||||
HashSetIterator<T, HASH> operator++(int);
|
||||
|
||||
const T& operator*() const;
|
||||
const T* operator->() const;
|
||||
|
||||
bool operator==(const HashSetIterator<T, HASH>&) const;
|
||||
bool operator!=(const HashSetIterator<T, HASH>&) const;
|
||||
|
||||
operator bool() const { return m_owner && m_current_bucket; }
|
||||
|
||||
private:
|
||||
HashSetIterator(const HashSet<T, HASH>* owner, Vector<Vector<T>>::const_iterator bucket);
|
||||
void find_next();
|
||||
|
||||
private:
|
||||
const HashSet<T, HASH>* m_owner = nullptr;
|
||||
Vector<Vector<T>>::const_iterator m_current_bucket;
|
||||
Vector<T>::const_iterator m_current_key;
|
||||
|
||||
friend class HashSet<T, HASH>;
|
||||
};
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>::HashSet(const HashSet<T, HASH>& other)
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
HashSet<T, HASH, STABLE>::HashSet(const HashSet& other)
|
||||
: m_buckets(other.m_buckets)
|
||||
, m_size(other.m_size)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>::HashSet(HashSet<T, HASH>&& other)
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
HashSet<T, HASH, STABLE>::HashSet(HashSet&& other)
|
||||
: m_buckets(move(other.m_buckets))
|
||||
, m_size(other.m_size)
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet<T, HASH>& other)
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
HashSet<T, HASH, STABLE>& HashSet<T, HASH, STABLE>::operator=(const HashSet& other)
|
||||
{
|
||||
clear();
|
||||
m_buckets = other.m_buckets;
|
||||
|
@ -109,8 +79,8 @@ namespace BAN
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet<T, HASH>&& other)
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
HashSet<T, HASH, STABLE>& HashSet<T, HASH, STABLE>::operator=(HashSet&& other)
|
||||
{
|
||||
clear();
|
||||
m_buckets = move(other.m_buckets);
|
||||
|
@ -119,14 +89,14 @@ namespace BAN
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
ErrorOr<void> HashSet<T, HASH>::insert(const T& key)
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
ErrorOr<void> HashSet<T, HASH, STABLE>::insert(const T& key)
|
||||
{
|
||||
return insert(move(T(key)));
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
ErrorOr<void> HashSet<T, HASH>::insert(T&& key)
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
ErrorOr<void> HashSet<T, HASH, STABLE>::insert(T&& key)
|
||||
{
|
||||
if (!empty() && get_bucket(key).contains(key))
|
||||
return {};
|
||||
|
@ -137,75 +107,75 @@ namespace BAN
|
|||
return {};
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
void HashSet<T, HASH>::remove(const T& key)
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
void HashSet<T, HASH, STABLE>::remove(const T& key)
|
||||
{
|
||||
if (empty()) return;
|
||||
Vector<T>& bucket = get_bucket(key);
|
||||
for (size_type i = 0; i < bucket.size(); i++)
|
||||
auto& bucket = get_bucket(key);
|
||||
for (auto it = bucket.begin(); it != bucket.end(); it++)
|
||||
{
|
||||
if (bucket[i] == key)
|
||||
if (*it == key)
|
||||
{
|
||||
bucket.remove(i);
|
||||
bucket.remove(it);
|
||||
m_size--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
void HashSet<T, HASH>::clear()
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
void HashSet<T, HASH, STABLE>::clear()
|
||||
{
|
||||
m_buckets.clear();
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
ErrorOr<void> HashSet<T, HASH>::reserve(size_type size)
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
ErrorOr<void> HashSet<T, HASH, STABLE>::reserve(size_type size)
|
||||
{
|
||||
TRY(rebucket(size));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
bool HashSet<T, HASH>::contains(const T& key) const
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
bool HashSet<T, HASH, STABLE>::contains(const T& key) const
|
||||
{
|
||||
if (empty()) return false;
|
||||
return get_bucket(key).contains(key);
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
typename HashSet<T, HASH>::size_type HashSet<T, HASH>::size() const
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
typename HashSet<T, HASH, STABLE>::size_type HashSet<T, HASH, STABLE>::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
bool HashSet<T, HASH>::empty() const
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
bool HashSet<T, HASH, STABLE>::empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
ErrorOr<void> HashSet<T, HASH>::rebucket(size_type bucket_count)
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
ErrorOr<void> HashSet<T, HASH, STABLE>::rebucket(size_type bucket_count)
|
||||
{
|
||||
if (m_buckets.size() >= bucket_count)
|
||||
return {};
|
||||
|
||||
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||
Vector<Vector<T>> new_buckets;
|
||||
size_type new_bucket_count = Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||
Vector<LinkedList<T>> new_buckets;
|
||||
if (new_buckets.resize(new_bucket_count).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
// NOTE: We have to copy the old keys to the new keys and not move
|
||||
// since we might run out of memory half way through.
|
||||
for (Vector<T>& bucket : m_buckets)
|
||||
for (auto& bucket : m_buckets)
|
||||
{
|
||||
for (T& key : bucket)
|
||||
{
|
||||
size_type bucket_index = HASH()(key) % new_buckets.size();
|
||||
if (new_buckets[bucket_index].push_back(key).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
if constexpr(STABLE)
|
||||
TRY(new_buckets[bucket_index].push_back(key));
|
||||
else
|
||||
TRY(new_buckets[bucket_index].push_back(move(key)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,92 +183,27 @@ namespace BAN
|
|||
return {};
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
Vector<T>& HashSet<T, HASH>::get_bucket(const T& key)
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
LinkedList<T>& HashSet<T, HASH, STABLE>::get_bucket(const T& key)
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
size_type index = HASH()(key) % m_buckets.size();
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
const Vector<T>& HashSet<T, HASH>::get_bucket(const T& key) const
|
||||
template<typename T, typename HASH, bool STABLE>
|
||||
const LinkedList<T>& HashSet<T, HASH, STABLE>::get_bucket(const T& key) const
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
size_type index = HASH()(key) % m_buckets.size();
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSetIterator<T, HASH>& HashSetIterator<T, HASH>::operator++()
|
||||
{
|
||||
ASSERT(*this);
|
||||
if (m_current_key == m_current_bucket->end())
|
||||
m_current_bucket++;
|
||||
else
|
||||
m_current_key++;
|
||||
find_next();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSetIterator<T, HASH> HashSetIterator<T, HASH>::operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
const T& HashSetIterator<T, HASH>::operator*() const
|
||||
{
|
||||
ASSERT(m_owner && m_current_bucket && m_current_key);
|
||||
return *m_current_key;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
const T* HashSetIterator<T, HASH>::operator->() const
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
bool HashSetIterator<T, HASH>::operator==(const HashSetIterator<T, HASH>& other) const
|
||||
{
|
||||
if (!m_owner || m_owner != other.m_owner)
|
||||
return false;
|
||||
return m_current_bucket == other.m_current_bucket
|
||||
&& m_current_key == other.m_current_key;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
bool HashSetIterator<T, HASH>::operator!=(const HashSetIterator<T, HASH>& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSetIterator<T, HASH>::HashSetIterator(const HashSet<T, HASH>* owner, Vector<Vector<T>>::const_iterator bucket)
|
||||
: m_owner(owner)
|
||||
, m_current_bucket(bucket)
|
||||
{
|
||||
if (m_current_bucket != m_owner->m_buckets.end())
|
||||
m_current_key = m_current_bucket->begin();
|
||||
find_next();
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
void HashSetIterator<T, HASH>::find_next()
|
||||
{
|
||||
ASSERT(m_owner && m_current_bucket);
|
||||
while (m_current_bucket != m_owner->m_buckets.end())
|
||||
{
|
||||
if (m_current_key && m_current_key != m_current_bucket->end())
|
||||
return;
|
||||
m_current_bucket++;
|
||||
m_current_key = m_current_bucket->begin();
|
||||
}
|
||||
m_current_key = typename Vector<T>::const_iterator();
|
||||
}
|
||||
// Unstable hash set moves values between container during rebucketing.
|
||||
// This means that if insertion to set 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 set after insertion fails.
|
||||
template<typename T, typename HASH = hash<T>>
|
||||
using HashSetUnstable = HashSet<T, HASH, false>;
|
||||
|
||||
}
|
|
@ -21,11 +21,11 @@ namespace BAN
|
|||
|
||||
public:
|
||||
LinkedList() = default;
|
||||
LinkedList(const LinkedList<T>& other) { *this = other; }
|
||||
LinkedList(const LinkedList<T>& other) requires is_copy_constructible_v<T> { *this = other; }
|
||||
LinkedList(LinkedList<T>&& other) { *this = move(other); }
|
||||
~LinkedList() { clear(); }
|
||||
|
||||
LinkedList<T>& operator=(const LinkedList<T>&);
|
||||
LinkedList<T>& operator=(const LinkedList<T>&) requires is_copy_constructible_v<T>;
|
||||
LinkedList<T>& operator=(LinkedList<T>&&);
|
||||
|
||||
ErrorOr<void> push_back(const T&);
|
||||
|
@ -115,7 +115,7 @@ namespace BAN
|
|||
};
|
||||
|
||||
template<typename T>
|
||||
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other)
|
||||
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other) requires is_copy_constructible_v<T>
|
||||
{
|
||||
clear();
|
||||
for (const T& elem : other)
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
namespace BAN::Math
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline constexpr T abs(T val)
|
||||
{
|
||||
return val < 0 ? -val : val;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline constexpr T min(T a, T b)
|
||||
{
|
||||
|
|
|
@ -55,6 +55,7 @@ namespace BAN
|
|||
char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; }
|
||||
char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; }
|
||||
|
||||
bool operator==(const String&) const;
|
||||
bool operator==(StringView) const;
|
||||
bool operator==(const char*) const;
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/ForwardList.h>
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/ForwardList.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/Optional.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
@ -35,6 +36,9 @@ namespace BAN
|
|||
char back() const;
|
||||
char front() const;
|
||||
|
||||
BAN::Optional<size_type> find(char) const;
|
||||
BAN::Optional<size_type> find(bool(*comp)(char)) const;
|
||||
|
||||
bool contains(char) const;
|
||||
size_type count(char) const;
|
||||
|
||||
|
|
|
@ -46,6 +46,18 @@ namespace BAN
|
|||
template<typename T> inline constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value;
|
||||
template<typename T> concept lvalue_reference = is_lvalue_reference_v<T>;
|
||||
|
||||
template<typename T, typename... Args> struct is_constructible { static constexpr bool value = __is_constructible(T, Args...); };
|
||||
template<typename T, typename... Args> inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
|
||||
|
||||
template<typename T> struct is_default_constructible { static constexpr bool value = is_constructible_v<T>; };
|
||||
template<typename T> inline constexpr bool is_default_constructible_v = is_default_constructible<T>::value;
|
||||
|
||||
template<typename T> struct is_copy_constructible { static constexpr bool value = is_constructible_v<T, const T&>; };
|
||||
template<typename T> inline constexpr bool is_copy_constructible_v = is_copy_constructible<T>::value;
|
||||
|
||||
template<typename T> struct is_move_constructible { static constexpr bool value = is_constructible_v<T, T&&>; };
|
||||
template<typename T> inline constexpr bool is_move_constructible_v = is_move_constructible<T>::value;
|
||||
|
||||
template<typename T> struct is_integral { static constexpr bool value = requires (T t, T* p, void (*f)(T)) { reinterpret_cast<T>(t); f(0); p + t; }; };
|
||||
template<typename T> inline constexpr bool is_integral_v = is_integral<T>::value;
|
||||
template<typename T> concept integral = is_integral_v<T>;
|
||||
|
|
|
@ -64,7 +64,8 @@ namespace BAN
|
|||
const T& front() const;
|
||||
T& front();
|
||||
|
||||
ErrorOr<void> resize(size_type, const T& = T());
|
||||
ErrorOr<void> resize(size_type) requires is_default_constructible_v<T>;
|
||||
ErrorOr<void> resize(size_type, const T&) requires is_copy_constructible_v<T>;
|
||||
ErrorOr<void> reserve(size_type);
|
||||
ErrorOr<void> shrink_to_fit();
|
||||
|
||||
|
@ -297,7 +298,21 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> Vector<T>::resize(size_type size, const T& value)
|
||||
ErrorOr<void> Vector<T>::resize(size_type size) requires is_default_constructible_v<T>
|
||||
{
|
||||
TRY(ensure_capacity(size));
|
||||
if (size < m_size)
|
||||
for (size_type i = size; i < m_size; i++)
|
||||
m_data[i].~T();
|
||||
if (size > m_size)
|
||||
for (size_type i = m_size; i < size; i++)
|
||||
new (m_data + i) T();
|
||||
m_size = size;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> Vector<T>::resize(size_type size, const T& value) requires is_copy_constructible_v<T>
|
||||
{
|
||||
TRY(ensure_capacity(size));
|
||||
if (size < m_size)
|
||||
|
|
|
@ -19,6 +19,13 @@ set(AOC2023_PROJECTS
|
|||
day14
|
||||
day15
|
||||
day16
|
||||
day17
|
||||
day18
|
||||
day19
|
||||
day20
|
||||
day21
|
||||
day23
|
||||
full
|
||||
)
|
||||
|
||||
set(BANAN_AOC2023_BIN ${BANAN_BIN}/aoc2023)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(aoc2023_day-template CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2023_day-template ${SOURCES})
|
||||
target_compile_options(aoc2023_day-template PUBLIC -O2 -g)
|
||||
target_link_libraries(aoc2023_day-template PUBLIC libc ban)
|
||||
|
||||
add_dependencies(aoc2023_day-template libc-install ban-install)
|
||||
|
||||
add_custom_target(aoc2023_day-template-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day-template ${BANAN_AOC2023_BIN}/day-template
|
||||
DEPENDS aoc2023_day-template
|
||||
DEPENDS aoc2023_always
|
||||
)
|
||||
|
||||
add_dependencies(aoc2023 aoc2023_day-template)
|
||||
add_dependencies(aoc2023-install aoc2023_day-template-install)
|
|
@ -0,0 +1,48 @@
|
|||
#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 puzzle1(FILE* fp)
|
||||
{
|
||||
(void)fp;
|
||||
return -1;
|
||||
}
|
||||
|
||||
i64 puzzle2(FILE* fp)
|
||||
{
|
||||
(void)fp;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2023/day-template_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_day17 CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2023_day17 ${SOURCES})
|
||||
target_compile_options(aoc2023_day17 PUBLIC -O2 -g)
|
||||
target_link_libraries(aoc2023_day17 PUBLIC libc ban)
|
||||
|
||||
add_dependencies(aoc2023_day17 libc-install ban-install)
|
||||
|
||||
add_custom_target(aoc2023_day17-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day17 ${BANAN_AOC2023_BIN}/day17
|
||||
DEPENDS aoc2023_day17
|
||||
DEPENDS aoc2023_always
|
||||
)
|
||||
|
||||
add_dependencies(aoc2023 aoc2023_day17)
|
||||
add_dependencies(aoc2023-install aoc2023_day17-install)
|
Binary file not shown.
|
@ -0,0 +1,202 @@
|
|||
#include <BAN/HashSet.h>
|
||||
#include <BAN/Vector.h>
|
||||
#include <BAN/Hash.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;
|
||||
|
||||
struct Position
|
||||
{
|
||||
i64 x;
|
||||
i64 y;
|
||||
|
||||
bool operator==(const Position& other) const
|
||||
{
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
template<>
|
||||
struct hash<Position>
|
||||
{
|
||||
hash_t operator()(const Position& position) const
|
||||
{
|
||||
return hash<u64>()(((u64)position.x << 32) | (u64)position.y);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
BAN::Vector<BAN::Vector<i64>> parse_grid(FILE* fp)
|
||||
{
|
||||
BAN::Vector<BAN::Vector<i64>> grid;
|
||||
|
||||
char buffer[256];
|
||||
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] = line[i] - '0';
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
//static constexpr i64 MIN_STEPS = 4;
|
||||
//static constexpr i64 MAX_STEPS = 10;
|
||||
template<i64 MIN_STEPS, i64 MAX_STEPS>
|
||||
i64 solve_general(FILE* fp)
|
||||
{
|
||||
struct Block
|
||||
{
|
||||
// heatloss[x][y]:
|
||||
// x: direction
|
||||
// y: steps left in that direction
|
||||
i64 heatloss[4][MAX_STEPS + 1];
|
||||
i8 entered_from = -1;
|
||||
};
|
||||
|
||||
const auto grid = parse_grid(fp);
|
||||
|
||||
// initially mark everything very large, except (0, 0)
|
||||
BAN::Vector<BAN::Vector<Block>> heatloss_map;
|
||||
MUST(heatloss_map.resize(grid.size()));
|
||||
for (size_t y = 0; y < grid.size(); y++)
|
||||
{
|
||||
MUST(heatloss_map[y].resize(grid[y].size()));
|
||||
for (size_t x = 0; x < grid[y].size(); x++)
|
||||
for (size_t dir = 0; dir < 4; dir++)
|
||||
for (size_t step = 0; step <= MAX_STEPS; step++)
|
||||
heatloss_map[y][x].heatloss[dir][step] = 1'000'000'000;
|
||||
}
|
||||
for (size_t dir = 0; dir < 4; dir++)
|
||||
for (size_t step = 0; step <= MAX_STEPS; step++)
|
||||
heatloss_map[0][0].heatloss[dir][step] = 0;
|
||||
|
||||
BAN::HashSet<Position> visited;
|
||||
BAN::HashSet<Position> pending;
|
||||
MUST(pending.insert({ 0, 0 }));
|
||||
|
||||
while (!pending.empty())
|
||||
{
|
||||
auto position = *pending.begin();
|
||||
pending.remove(position);
|
||||
|
||||
Position offsets[4] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
|
||||
|
||||
for (i8 dir = 0; dir < 4; dir++)
|
||||
{
|
||||
auto target = position;
|
||||
|
||||
i64 path_heatloss = 0;
|
||||
|
||||
auto is_target_in_bounds =
|
||||
[&](const Position& target)
|
||||
{
|
||||
if (target.y < 0 || target.y >= (i64)grid.size())
|
||||
return false;
|
||||
if (target.x < 0 || target.x >= (i64)grid.front().size())
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
i64 target_distance = (heatloss_map[position.y][position.x].entered_from == dir) ? 1 : MIN_STEPS;
|
||||
for (i64 i = 0; i < target_distance; i++)
|
||||
{
|
||||
target.x += offsets[dir].x;
|
||||
target.y += offsets[dir].y;
|
||||
if (!is_target_in_bounds(target))
|
||||
break;
|
||||
path_heatloss += grid[target.y][target.x];
|
||||
}
|
||||
if (!is_target_in_bounds(target))
|
||||
continue;
|
||||
|
||||
auto& target_heatloss = heatloss_map[target.y][target.x];
|
||||
|
||||
bool target_updated = false;
|
||||
for (i8 new_dir = 0; new_dir < 4; new_dir++)
|
||||
{
|
||||
// Don't allow going backwards
|
||||
if (new_dir == dir + 2 || dir == new_dir + 2)
|
||||
continue;
|
||||
|
||||
for (i64 step = target_distance; step <= MAX_STEPS; step++)
|
||||
{
|
||||
i64 possible_heatloss = heatloss_map[position.y][position.x].heatloss[dir][step] + path_heatloss;
|
||||
i64 new_dir_max_step = (new_dir == dir) ? step - target_distance : MAX_STEPS;
|
||||
for (i64 i = 0; i <= new_dir_max_step; i++)
|
||||
{
|
||||
if (possible_heatloss >= target_heatloss.heatloss[new_dir][i])
|
||||
continue;
|
||||
target_heatloss.heatloss[new_dir][i] = possible_heatloss;
|
||||
target_heatloss.entered_from = dir;
|
||||
target_updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target_updated || !visited.contains(target))
|
||||
MUST(pending.insert(target));
|
||||
}
|
||||
|
||||
MUST(visited.insert(position));
|
||||
}
|
||||
|
||||
i64 result = INT64_MAX;
|
||||
for (size_t dir = 0; dir < 4; dir++)
|
||||
for (size_t step = 0; step <= MAX_STEPS; step++)
|
||||
result = BAN::Math::min(result, heatloss_map.back().back().heatloss[dir][step]);
|
||||
return result;
|
||||
}
|
||||
|
||||
i64 puzzle1(FILE* fp)
|
||||
{
|
||||
return solve_general<1, 3>(fp);
|
||||
}
|
||||
|
||||
i64 puzzle2(FILE* fp)
|
||||
{
|
||||
return solve_general<4, 10>(fp);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2023/day17_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_day18 CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2023_day18 ${SOURCES})
|
||||
target_compile_options(aoc2023_day18 PUBLIC -O2 -g)
|
||||
target_link_libraries(aoc2023_day18 PUBLIC libc ban)
|
||||
|
||||
add_dependencies(aoc2023_day18 libc-install ban-install)
|
||||
|
||||
add_custom_target(aoc2023_day18-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day18 ${BANAN_AOC2023_BIN}/day18
|
||||
DEPENDS aoc2023_day18
|
||||
DEPENDS aoc2023_always
|
||||
)
|
||||
|
||||
add_dependencies(aoc2023 aoc2023_day18)
|
||||
add_dependencies(aoc2023-install aoc2023_day18-install)
|
|
@ -0,0 +1,265 @@
|
|||
#include <BAN/HashSet.h>
|
||||
#include <BAN/StringView.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;
|
||||
|
||||
struct Position
|
||||
{
|
||||
i32 x;
|
||||
i32 y;
|
||||
|
||||
bool operator==(const Position& other) const
|
||||
{
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
|
||||
Position& operator+=(const Position& other)
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Position operator+(const Position& other) const
|
||||
{
|
||||
Position temp = *this;
|
||||
temp += other;
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
struct PositionHash
|
||||
{
|
||||
BAN::hash_t operator()(const Position& position) const
|
||||
{
|
||||
return BAN::u64_hash(((u64)position.x << 32) | (u64)position.y);
|
||||
}
|
||||
};
|
||||
|
||||
enum Direction { North, West, South, East };
|
||||
|
||||
static constexpr Direction char_to_dir(char c)
|
||||
{
|
||||
if (c == 'U')
|
||||
return Direction::North;
|
||||
if (c == 'D')
|
||||
return Direction::South;
|
||||
if (c == 'L')
|
||||
return Direction::West;
|
||||
if (c == 'R')
|
||||
return Direction::East;
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
static constexpr Position s_dir_offset[] {
|
||||
{ .x = 0, .y = -1 },
|
||||
{ .x = -1, .y = 0 },
|
||||
{ .x = 0, .y = 1 },
|
||||
{ .x = 1, .y = 0 },
|
||||
};
|
||||
|
||||
i64 solve_general(FILE* fp, auto parse_dir, auto parse_count)
|
||||
{
|
||||
BAN::HashSetUnstable<Position, PositionHash> path;
|
||||
BAN::HashSetUnstable<Position, PositionHash> lpath;
|
||||
BAN::HashSetUnstable<Position, PositionHash> rpath;
|
||||
|
||||
Position current_pos { 0, 0 };
|
||||
MUST(path.insert(current_pos));
|
||||
|
||||
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;
|
||||
|
||||
auto dir = parse_dir(line);
|
||||
i64 count = parse_count(line);
|
||||
|
||||
Position loff, roff;
|
||||
switch (dir)
|
||||
{
|
||||
case Direction::North:
|
||||
loff = s_dir_offset[Direction::West];
|
||||
roff = s_dir_offset[Direction::East];
|
||||
break;
|
||||
case Direction::South:
|
||||
loff = s_dir_offset[Direction::East];
|
||||
roff = s_dir_offset[Direction::West];
|
||||
break;
|
||||
case Direction::East:
|
||||
loff = s_dir_offset[Direction::North];
|
||||
roff = s_dir_offset[Direction::South];
|
||||
break;
|
||||
case Direction::West:
|
||||
loff = s_dir_offset[Direction::South];
|
||||
roff = s_dir_offset[Direction::North];
|
||||
break;
|
||||
}
|
||||
|
||||
for (i64 i = 0; i < count; i++)
|
||||
{
|
||||
current_pos += s_dir_offset[dir];
|
||||
MUST(path.insert(current_pos));
|
||||
MUST(lpath.insert(current_pos + loff));
|
||||
MUST(rpath.insert(current_pos + roff));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto position : path)
|
||||
{
|
||||
if (lpath.contains(position))
|
||||
lpath.remove(position);
|
||||
if (rpath.contains(position))
|
||||
rpath.remove(position);
|
||||
}
|
||||
|
||||
auto find_min_and_remove_duplicates =
|
||||
[](auto& source, auto& destination, i32& minimum)
|
||||
{
|
||||
for (auto it = source.begin(); it != source.end();)
|
||||
{
|
||||
if (!destination.contains(*it))
|
||||
{
|
||||
minimum = BAN::Math::min(minimum, it->x);
|
||||
it++;
|
||||
}
|
||||
else
|
||||
{
|
||||
source.remove(*it);
|
||||
destination.remove(*it);
|
||||
it = source.begin();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
i32 lmin_x = INT32_MAX;
|
||||
find_min_and_remove_duplicates(lpath, rpath, lmin_x);
|
||||
|
||||
i32 rmin_x = INT32_MAX;
|
||||
find_min_and_remove_duplicates(rpath, lpath, rmin_x);
|
||||
|
||||
ASSERT(lmin_x != rmin_x);
|
||||
auto& expand = (lmin_x < rmin_x) ? rpath : lpath;
|
||||
|
||||
BAN::HashSetUnstable<Position, PositionHash> visited;
|
||||
BAN::HashSetUnstable<Position, PositionHash> inner_area;
|
||||
|
||||
while (!expand.empty())
|
||||
{
|
||||
auto position = *expand.begin();
|
||||
expand.remove(position);
|
||||
|
||||
MUST(inner_area.insert(position));
|
||||
MUST(visited.insert(position));
|
||||
|
||||
for (i8 dir = 0; dir < 4; dir++)
|
||||
{
|
||||
auto next = position + s_dir_offset[dir];
|
||||
if (visited.contains(next) || path.contains(next))
|
||||
continue;
|
||||
MUST(expand.insert(next));
|
||||
MUST(visited.insert(next));
|
||||
}
|
||||
}
|
||||
|
||||
return path.size() + inner_area.size();
|
||||
}
|
||||
|
||||
i64 puzzle1(FILE* fp)
|
||||
{
|
||||
auto parse_dir =
|
||||
[](auto line)
|
||||
{
|
||||
if (line[0] == 'U')
|
||||
return Direction::North;
|
||||
if (line[0] == 'D')
|
||||
return Direction::South;
|
||||
if (line[0] == 'L')
|
||||
return Direction::West;
|
||||
if (line[0] == 'R')
|
||||
return Direction::East;
|
||||
ASSERT_NOT_REACHED();
|
||||
};
|
||||
|
||||
auto parse_count =
|
||||
[](auto line)
|
||||
{
|
||||
i64 result = 0;
|
||||
for (size_t i = 2; i < line.size() && isdigit(line[i]); i++)
|
||||
result = (result * 10) + (line[i] - '0');
|
||||
return result;
|
||||
};
|
||||
|
||||
return solve_general(fp, parse_dir, parse_count);
|
||||
}
|
||||
|
||||
i64 puzzle2(FILE* fp)
|
||||
{
|
||||
//(#7a21e3)
|
||||
|
||||
auto parse_dir =
|
||||
[](auto line)
|
||||
{
|
||||
line = line.substring(*line.find('('));
|
||||
if (line[7] == '0')
|
||||
return Direction::East;
|
||||
if (line[7] == '1')
|
||||
return Direction::South;
|
||||
if (line[7] == '2')
|
||||
return Direction::West;
|
||||
if (line[7] == '3')
|
||||
return Direction::North;
|
||||
ASSERT_NOT_REACHED();
|
||||
};
|
||||
|
||||
auto parse_count =
|
||||
[](auto line)
|
||||
{
|
||||
line = line.substring(*line.find('('));
|
||||
i64 result = 0;
|
||||
for (size_t i = 2; i < 7; i++)
|
||||
result = (result * 16) + ((isdigit(line[i])) ? (line[i] - '0') : (tolower(line[i] - 'a' + 10)));
|
||||
return result;
|
||||
};
|
||||
|
||||
return solve_general(fp, parse_dir, parse_count);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2023/day18_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_day19 CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2023_day19 ${SOURCES})
|
||||
target_compile_options(aoc2023_day19 PUBLIC -O2 -g)
|
||||
target_link_libraries(aoc2023_day19 PUBLIC libc ban)
|
||||
|
||||
add_dependencies(aoc2023_day19 libc-install ban-install)
|
||||
|
||||
add_custom_target(aoc2023_day19-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day19 ${BANAN_AOC2023_BIN}/day19
|
||||
DEPENDS aoc2023_day19
|
||||
DEPENDS aoc2023_always
|
||||
)
|
||||
|
||||
add_dependencies(aoc2023 aoc2023_day19)
|
||||
add_dependencies(aoc2023-install aoc2023_day19-install)
|
|
@ -0,0 +1,290 @@
|
|||
#include <BAN/Array.h>
|
||||
#include <BAN/HashMap.h>
|
||||
#include <BAN/HashSet.h>
|
||||
#include <BAN/Sort.h>
|
||||
#include <BAN/String.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <sys/time.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 Variable { x, m, a, s };
|
||||
enum class Comparison { Less, Greater, Always };
|
||||
|
||||
struct Rule
|
||||
{
|
||||
Variable operand1;
|
||||
i64 operand2;
|
||||
Comparison comparison;
|
||||
|
||||
BAN::String target;
|
||||
};
|
||||
|
||||
using Workflows = BAN::HashMapUnstable<BAN::String, BAN::Vector<Rule>>;
|
||||
|
||||
struct Item
|
||||
{
|
||||
i64 values[4];
|
||||
|
||||
bool operator==(const Item& other) const
|
||||
{
|
||||
return memcmp(values, other.values, sizeof(values)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct ItemHash
|
||||
{
|
||||
BAN::hash_t operator()(const Item& item) const
|
||||
{
|
||||
return BAN::hash<u64>()(item.values[0]) ^
|
||||
BAN::hash<u64>()(item.values[1]) ^
|
||||
BAN::hash<u64>()(item.values[2]) ^
|
||||
BAN::hash<u64>()(item.values[3]);
|
||||
};
|
||||
};
|
||||
|
||||
i64 parse_i64(BAN::StringView str)
|
||||
{
|
||||
i64 result = 0;
|
||||
for (size_t i = 0; i < str.size() && isdigit(str[i]); i++)
|
||||
result = (result * 10) + (str[i] - '0');
|
||||
return result;
|
||||
}
|
||||
|
||||
bool get_line(FILE* fp, BAN::String& out)
|
||||
{
|
||||
out.clear();
|
||||
|
||||
bool success = false;
|
||||
|
||||
char buffer[128];
|
||||
while (fgets(buffer, sizeof(buffer), fp))
|
||||
{
|
||||
success = true;
|
||||
|
||||
MUST(out.append(buffer));
|
||||
if (out.back() == '\n')
|
||||
{
|
||||
out.pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
Workflows parse_workflows(FILE* fp)
|
||||
{
|
||||
Workflows workflows;
|
||||
|
||||
BAN::String line;
|
||||
while (get_line(fp, line) && !line.empty())
|
||||
{
|
||||
auto name = line.sv().substring(0, *line.sv().find('{'));
|
||||
MUST(workflows.emplace(name));
|
||||
|
||||
auto rule_str = line.sv().substring(*line.sv().find('{') + 1);
|
||||
rule_str = rule_str.substring(0, rule_str.size() - 1);
|
||||
|
||||
auto rules_str = MUST(rule_str.split(','));
|
||||
|
||||
for (size_t i = 0; i < rules_str.size() - 1; i++)
|
||||
{
|
||||
Rule rule;
|
||||
rule.operand1 = (rules_str[i][0] == 'x') ? Variable::x :
|
||||
(rules_str[i][0] == 'm') ? Variable::m :
|
||||
(rules_str[i][0] == 'a') ? Variable::a :
|
||||
Variable::s;
|
||||
rule.operand2 = parse_i64(rules_str[i].substring(2));
|
||||
rule.comparison = (rules_str[i][1] == '<') ? Comparison::Less : Comparison::Greater;
|
||||
rule.target = rules_str[i].substring(*rules_str[i].find(':') + 1);
|
||||
|
||||
MUST(workflows[name].push_back(BAN::move(rule)));
|
||||
}
|
||||
|
||||
MUST(workflows[name].emplace_back(Variable::x, 0, Comparison::Always, rules_str.back()));
|
||||
}
|
||||
|
||||
return workflows;
|
||||
}
|
||||
|
||||
BAN::Vector<Item> parse_items(FILE* fp)
|
||||
{
|
||||
BAN::Vector<Item> items;
|
||||
|
||||
BAN::String line;
|
||||
while (get_line(fp, line) && !line.empty())
|
||||
{
|
||||
auto values = MUST(line.sv().substring(1, line.size() - 2).split(','));
|
||||
ASSERT(values.size() == 4);
|
||||
ASSERT(values[0][0] == 'x');
|
||||
ASSERT(values[1][0] == 'm');
|
||||
ASSERT(values[2][0] == 'a');
|
||||
ASSERT(values[3][0] == 's');
|
||||
|
||||
Item item;
|
||||
item.values[0] = parse_i64(values[0].substring(2));
|
||||
item.values[1] = parse_i64(values[1].substring(2));
|
||||
item.values[2] = parse_i64(values[2].substring(2));
|
||||
item.values[3] = parse_i64(values[3].substring(2));
|
||||
|
||||
MUST(items.push_back(item));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
bool satifies_rule(const Item& item, const Rule& rule)
|
||||
{
|
||||
switch (rule.comparison)
|
||||
{
|
||||
case Comparison::Always:
|
||||
return true;
|
||||
case Comparison::Less:
|
||||
return item.values[rule.operand1] < rule.operand2;
|
||||
case Comparison::Greater:
|
||||
return item.values[rule.operand1] > rule.operand2;
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
bool is_accepted(const Item& item, const BAN::String& name, const Workflows& workflows)
|
||||
{
|
||||
const auto& workflow = workflows[name];
|
||||
for (const auto& rule : workflow)
|
||||
{
|
||||
if (!satifies_rule(item, rule))
|
||||
continue;
|
||||
if (rule.target == "A"sv)
|
||||
return true;
|
||||
if (rule.target == "R"sv)
|
||||
return false;
|
||||
return is_accepted(item, rule.target, workflows);
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
i64 puzzle1(FILE* fp)
|
||||
{
|
||||
auto workflows = parse_workflows(fp);
|
||||
auto items = parse_items(fp);
|
||||
|
||||
BAN::Vector<Item> accepted;
|
||||
|
||||
for (const auto& item : items)
|
||||
if (is_accepted(item, "in"sv, workflows))
|
||||
MUST(accepted.push_back(item));
|
||||
|
||||
i64 result = 0;
|
||||
for (const auto& item : accepted)
|
||||
result += item.values[0] + item.values[1] + item.values[2] + item.values[3];
|
||||
return result;
|
||||
}
|
||||
|
||||
i64 puzzle2(FILE* fp)
|
||||
{
|
||||
auto workflows = parse_workflows(fp);
|
||||
|
||||
BAN::Array<BAN::HashSet<i64>, 4> values;
|
||||
for (const auto& workflow : workflows)
|
||||
{
|
||||
for (const auto& rule : workflow.value)
|
||||
{
|
||||
if (rule.comparison != Comparison::Always)
|
||||
{
|
||||
MUST(values[rule.operand1].insert(rule.operand2));
|
||||
MUST(values[rule.operand1].insert(rule.operand2 + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i64 i = 0; i < 4; i++)
|
||||
{
|
||||
MUST(values[i].insert(1));
|
||||
MUST(values[i].insert(4001));
|
||||
}
|
||||
|
||||
BAN::Array<BAN::Vector<i64>, 4> values_sorted;
|
||||
for (i64 i = 0; i < 4; i++)
|
||||
{
|
||||
MUST(values_sorted[i].reserve(values[i].size()));
|
||||
for (i64 value : values[i])
|
||||
MUST(values_sorted[i].push_back(value));
|
||||
BAN::sort::sort(values_sorted[i].begin(), values_sorted[i].end());
|
||||
}
|
||||
|
||||
i64 result = 0;
|
||||
for (u64 xi = 0; xi < values_sorted[0].size() - 1; xi++)
|
||||
{
|
||||
timespec time_start;
|
||||
clock_gettime(CLOCK_MONOTONIC, &time_start);
|
||||
|
||||
for (u64 mi = 0; mi < values_sorted[1].size() - 1; mi++)
|
||||
{
|
||||
for (u64 ai = 0; ai < values_sorted[2].size() - 1; ai++)
|
||||
{
|
||||
for (u64 si = 0; si < values_sorted[3].size() - 1; si++)
|
||||
{
|
||||
Item item {{
|
||||
values_sorted[0][xi],
|
||||
values_sorted[1][mi],
|
||||
values_sorted[2][ai],
|
||||
values_sorted[3][si]
|
||||
}};
|
||||
if (!is_accepted(item, "in"sv, workflows))
|
||||
continue;
|
||||
|
||||
i64 x_count = values_sorted[0][xi + 1] - values_sorted[0][xi];
|
||||
i64 m_count = values_sorted[1][mi + 1] - values_sorted[1][mi];
|
||||
i64 a_count = values_sorted[2][ai + 1] - values_sorted[2][ai];
|
||||
i64 s_count = values_sorted[3][si + 1] - values_sorted[3][si];
|
||||
|
||||
result += x_count * m_count * a_count * s_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timespec time_stop;
|
||||
clock_gettime(CLOCK_MONOTONIC, &time_stop);
|
||||
|
||||
u64 duration_us = (time_stop.tv_sec * 1'000'000 + time_stop.tv_nsec / 1'000) - (time_start.tv_sec * 1'000'000 + time_start.tv_nsec / 1'000);
|
||||
printf("took %lu.%03lu ms, estimate %lu s\n", duration_us / 1000, duration_us % 1000, (values_sorted[0].size() - xi - 2) * duration_us / 1'000'000);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2023/day19_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_day20 CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2023_day20 ${SOURCES})
|
||||
target_compile_options(aoc2023_day20 PUBLIC -O2 -g)
|
||||
target_link_libraries(aoc2023_day20 PUBLIC libc ban)
|
||||
|
||||
add_dependencies(aoc2023_day20 libc-install ban-install)
|
||||
|
||||
add_custom_target(aoc2023_day20-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day20 ${BANAN_AOC2023_BIN}/day20
|
||||
DEPENDS aoc2023_day20
|
||||
DEPENDS aoc2023_always
|
||||
)
|
||||
|
||||
add_dependencies(aoc2023 aoc2023_day20)
|
||||
add_dependencies(aoc2023-install aoc2023_day20-install)
|
|
@ -0,0 +1,189 @@
|
|||
#include <BAN/HashMap.h>
|
||||
#include <BAN/Queue.h>
|
||||
#include <BAN/String.h>
|
||||
#include <BAN/Vector.h>
|
||||
#include <BAN/UniqPtr.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;
|
||||
|
||||
struct Signal
|
||||
{
|
||||
BAN::String target;
|
||||
BAN::String sender;
|
||||
bool high;
|
||||
};
|
||||
|
||||
struct Module
|
||||
{
|
||||
BAN::String name;
|
||||
BAN::Vector<BAN::String> targets;
|
||||
|
||||
virtual void handle_signal(const Signal& signal, BAN::Queue<Signal>& signal_queue) = 0;
|
||||
};
|
||||
|
||||
struct BroadcasterModule : public Module
|
||||
{
|
||||
void handle_signal(const Signal& signal, BAN::Queue<Signal>& signal_queue) override
|
||||
{
|
||||
for (const auto& target : targets)
|
||||
MUST(signal_queue.push({ target, name, signal.high }));
|
||||
}
|
||||
};
|
||||
|
||||
struct FlipFlopModule : public Module
|
||||
{
|
||||
bool is_on { false };
|
||||
|
||||
void handle_signal(const Signal& signal, BAN::Queue<Signal>& signal_queue) override
|
||||
{
|
||||
if (signal.high)
|
||||
return;
|
||||
is_on = !is_on;
|
||||
for (const auto& target : targets)
|
||||
MUST(signal_queue.push({ target, name, is_on }));
|
||||
}
|
||||
};
|
||||
|
||||
struct ConjunctionModule : public Module
|
||||
{
|
||||
BAN::HashMap<BAN::String, bool> inputs;
|
||||
|
||||
void handle_signal(const Signal& signal, BAN::Queue<Signal>& signal_queue) override
|
||||
{
|
||||
inputs[signal.sender] = signal.high;
|
||||
bool send_value = false;
|
||||
for (const auto& input : inputs)
|
||||
if (!input.value)
|
||||
send_value = true;
|
||||
for (const auto& target : targets)
|
||||
MUST(signal_queue.push({ target, name, send_value }));
|
||||
}
|
||||
};
|
||||
|
||||
BAN::HashMapUnstable<BAN::String, BAN::UniqPtr<Module>> parse_modules(FILE* fp)
|
||||
{
|
||||
BAN::HashMapUnstable<BAN::String, BAN::UniqPtr<Module>> modules;
|
||||
|
||||
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;
|
||||
|
||||
BAN::UniqPtr<Module> module;
|
||||
if (line.front() == '%')
|
||||
{
|
||||
module = MUST(BAN::UniqPtr<FlipFlopModule>::create());
|
||||
line = line.substring(1);
|
||||
}
|
||||
else if (line.front() == '&')
|
||||
{
|
||||
module = MUST(BAN::UniqPtr<ConjunctionModule>::create());
|
||||
line = line.substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
module = MUST(BAN::UniqPtr<BroadcasterModule>::create());
|
||||
}
|
||||
|
||||
auto name_targets = MUST(line.split('>'));
|
||||
auto name = name_targets[0].substring(0, name_targets[0].size() - 2);
|
||||
auto target_strs = MUST(name_targets[1].split(','));
|
||||
|
||||
for (auto target : target_strs)
|
||||
MUST(module->targets.emplace_back(target.substring(1)));
|
||||
|
||||
module->name = BAN::String(name);
|
||||
MUST(modules.insert(module->name, BAN::move(module)));
|
||||
}
|
||||
|
||||
for (auto& [name, module] : modules)
|
||||
{
|
||||
for (auto& target : module->targets)
|
||||
{
|
||||
if (!modules.contains(target))
|
||||
continue;
|
||||
if (auto* ptr = dynamic_cast<ConjunctionModule*>(modules[target].ptr()))
|
||||
MUST(ptr->inputs.insert(name, false));
|
||||
}
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
i64 puzzle1(FILE* fp)
|
||||
{
|
||||
auto modules = parse_modules(fp);
|
||||
|
||||
BAN::Queue<Signal> signal_queue;
|
||||
|
||||
i64 sent_hi = 0;
|
||||
i64 sent_lo = 0;
|
||||
|
||||
for (size_t i = 0; i < 1000; i++)
|
||||
{
|
||||
MUST(signal_queue.push({ "broadcaster"sv, ""sv, false }));
|
||||
while (!signal_queue.empty())
|
||||
{
|
||||
auto signal = signal_queue.front();
|
||||
signal_queue.pop();
|
||||
|
||||
if (signal.high)
|
||||
sent_hi++;
|
||||
else
|
||||
sent_lo++;
|
||||
|
||||
if (!modules.contains(signal.target))
|
||||
continue;
|
||||
|
||||
auto& module = modules[signal.target];
|
||||
module->handle_signal(signal, signal_queue);
|
||||
}
|
||||
}
|
||||
|
||||
return sent_hi * sent_lo;
|
||||
}
|
||||
|
||||
i64 puzzle2(FILE* fp)
|
||||
{
|
||||
(void)fp;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2023/day20_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_day21 CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2023_day21 ${SOURCES})
|
||||
target_compile_options(aoc2023_day21 PUBLIC -O2 -g)
|
||||
target_link_libraries(aoc2023_day21 PUBLIC libc ban)
|
||||
|
||||
add_dependencies(aoc2023_day21 libc-install ban-install)
|
||||
|
||||
add_custom_target(aoc2023_day21-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day21 ${BANAN_AOC2023_BIN}/day21
|
||||
DEPENDS aoc2023_day21
|
||||
DEPENDS aoc2023_always
|
||||
)
|
||||
|
||||
add_dependencies(aoc2023 aoc2023_day21)
|
||||
add_dependencies(aoc2023-install aoc2023_day21-install)
|
|
@ -0,0 +1,155 @@
|
|||
#include <BAN/Vector.h>
|
||||
#include <BAN/HashSet.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 { Free, Rock };
|
||||
|
||||
struct Position
|
||||
{
|
||||
i64 x;
|
||||
i64 y;
|
||||
|
||||
Position operator+(const Position& other) const
|
||||
{
|
||||
return {
|
||||
.x = x + other.x,
|
||||
.y = y + other.y
|
||||
};
|
||||
}
|
||||
|
||||
bool operator==(const Position& other) const
|
||||
{
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
};
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
template<>
|
||||
struct hash<Position>
|
||||
{
|
||||
hash_t operator()(const Position& position) const
|
||||
{
|
||||
return hash<u64>()(((u64)position.x << 32) | (u64)position.y);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct Garden
|
||||
{
|
||||
Position start;
|
||||
BAN::Vector<BAN::Vector<Tile>> tiles;
|
||||
};
|
||||
|
||||
Garden parse_garden(FILE* fp)
|
||||
{
|
||||
Garden garden;
|
||||
|
||||
i64 row = 0;
|
||||
|
||||
char buffer[256];
|
||||
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(garden.tiles.emplace_back(line.size(), Tile::Free));
|
||||
for (size_t i = 0; i < line.size(); i++)
|
||||
{
|
||||
if (line[i] == '#')
|
||||
garden.tiles.back()[i] = Tile::Rock;
|
||||
if (line[i] == 'S')
|
||||
garden.start = { .x = (i64)i, .y = row };
|
||||
}
|
||||
|
||||
row++;
|
||||
}
|
||||
|
||||
return garden;
|
||||
}
|
||||
|
||||
i64 puzzle1(FILE* fp)
|
||||
{
|
||||
auto garden = parse_garden(fp);
|
||||
|
||||
BAN::HashSetUnstable<Position> visited, reachable, pending;
|
||||
MUST(pending.insert(garden.start));
|
||||
|
||||
for (i32 i = 0; i <= 64; i++)
|
||||
{
|
||||
auto temp = BAN::move(pending);
|
||||
pending = BAN::HashSetUnstable<Position>();
|
||||
|
||||
while (!temp.empty())
|
||||
{
|
||||
auto position = *temp.begin();
|
||||
temp.remove(position);
|
||||
|
||||
MUST(visited.insert(position));
|
||||
if (i % 2 == 0)
|
||||
MUST(reachable.insert(position));
|
||||
|
||||
Position offsets[4] { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
|
||||
for (i32 j = 0; j < 4; j++)
|
||||
{
|
||||
auto next = position + offsets[j];
|
||||
if (next.y < 0 || next.y >= (i64)garden.tiles.size())
|
||||
continue;
|
||||
if (next.x < 0 || next.x >= (i64)garden.tiles[0].size())
|
||||
continue;
|
||||
if (garden.tiles[next.y][next.x] == Tile::Rock)
|
||||
continue;
|
||||
if (visited.contains(next))
|
||||
continue;
|
||||
MUST(pending.insert(next));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reachable.size();
|
||||
}
|
||||
|
||||
i64 puzzle2(FILE* fp)
|
||||
{
|
||||
(void)fp;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2023/day21_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_day23 CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2023_day23 ${SOURCES})
|
||||
target_compile_options(aoc2023_day23 PUBLIC -O2 -g)
|
||||
target_link_libraries(aoc2023_day23 PUBLIC libc ban)
|
||||
|
||||
add_dependencies(aoc2023_day23 libc-install ban-install)
|
||||
|
||||
add_custom_target(aoc2023_day23-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day23 ${BANAN_AOC2023_BIN}/day23
|
||||
DEPENDS aoc2023_day23
|
||||
DEPENDS aoc2023_always
|
||||
)
|
||||
|
||||
add_dependencies(aoc2023 aoc2023_day23)
|
||||
add_dependencies(aoc2023-install aoc2023_day23-install)
|
|
@ -0,0 +1,156 @@
|
|||
#include <BAN/Array.h>
|
||||
#include <BAN/ScopeGuard.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;
|
||||
|
||||
BAN::Vector<BAN::Vector<char>> parse_grid(FILE* fp)
|
||||
{
|
||||
BAN::Vector<BAN::Vector<char>> grid;
|
||||
|
||||
char buffer[256];
|
||||
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(), '\0'));
|
||||
for (size_t i = 0; i < line.size(); i++)
|
||||
grid.back()[i] = line[i];
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
struct Position
|
||||
{
|
||||
i64 x, y;
|
||||
|
||||
Position operator+(const Position& other) const
|
||||
{
|
||||
return { .x = x + other.x, .y = y + other.y };
|
||||
}
|
||||
|
||||
bool operator==(const Position& other) const
|
||||
{
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
};
|
||||
|
||||
i64 recurse_grid(BAN::Vector<Position>& path, const BAN::Vector<BAN::Vector<char>>& grid)
|
||||
{
|
||||
const auto entry = path.back();
|
||||
BAN::ScopeGuard _([&] { while (path.back() != entry) path.pop_back(); });
|
||||
|
||||
while (path.back().y < (i64)grid.size() - 1)
|
||||
{
|
||||
BAN::Array<Position, 4> valid_next;
|
||||
size_t valid_next_count = 0;
|
||||
|
||||
constexpr Position offsets[4] { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
{
|
||||
auto next = path.back() + offsets[i];
|
||||
if (next.y < 0 || next.y >= (i64)grid.size())
|
||||
continue;
|
||||
if (next.x < 0 || next.x >= (i64)grid.front().size())
|
||||
continue;
|
||||
switch (grid[next.y][next.x])
|
||||
{
|
||||
case '^': next.y--; break;
|
||||
case 'v': next.y++; break;
|
||||
case '<': next.x--; break;
|
||||
case '>': next.x++; break;
|
||||
case '#': continue;
|
||||
}
|
||||
if (path.contains(next))
|
||||
continue;
|
||||
valid_next[valid_next_count++] = next;
|
||||
}
|
||||
|
||||
if (valid_next_count == 0)
|
||||
return 0;
|
||||
|
||||
if (valid_next_count == 1)
|
||||
{
|
||||
MUST(path.push_back(valid_next.front()));
|
||||
continue;
|
||||
}
|
||||
|
||||
i64 result = 0;
|
||||
for (size_t i = 0; i < valid_next_count; i++)
|
||||
{
|
||||
MUST(path.push_back(valid_next[i]));
|
||||
result = BAN::Math::max(result, recurse_grid(path, grid));
|
||||
path.pop_back();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
i64 result = 0;
|
||||
for (size_t i = 1; i < path.size(); i++)
|
||||
result += BAN::Math::abs(path[i - 1].x - path[i].x) + BAN::Math::abs(path[i - 1].y - path[i].y);
|
||||
return result;
|
||||
}
|
||||
|
||||
i64 puzzle1(FILE* fp)
|
||||
{
|
||||
auto grid = parse_grid(fp);
|
||||
|
||||
BAN::Vector<Position> path;
|
||||
for (i64 x = 0; x < (i64)grid.front().size(); x++)
|
||||
{
|
||||
if (grid.front()[x] == '.')
|
||||
{
|
||||
MUST(path.push_back({ .x = x, .y = 0 }));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return recurse_grid(path, grid);
|
||||
}
|
||||
|
||||
i64 puzzle2(FILE* fp)
|
||||
{
|
||||
(void)fp;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2023/day23_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,20 @@
|
|||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(aoc2023-full CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2023-full ${SOURCES})
|
||||
target_compile_options(aoc2023-full PUBLIC -O2 -g)
|
||||
target_link_libraries(aoc2023-full PUBLIC libc)
|
||||
|
||||
add_custom_target(aoc2023-full-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023-full ${BANAN_AOC2023_BIN}/full
|
||||
DEPENDS aoc2023-full
|
||||
DEPENDS aoc2023_always
|
||||
)
|
||||
|
||||
add_dependencies(aoc2023 aoc2023-full)
|
||||
add_dependencies(aoc2023-install aoc2023-full-install)
|
|
@ -0,0 +1,20 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
for (int i = 1; i <= 23; i++)
|
||||
{
|
||||
if (i == 22)
|
||||
continue;
|
||||
printf("day %d:\n", i);
|
||||
|
||||
char command[128];
|
||||
sprintf(command, "/bin/aoc2023/day%d", i);
|
||||
|
||||
system(command);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
echo Please specify day number >& 2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d day$1 ]]; then
|
||||
cp -r day-template day$1
|
||||
sed -i "s/day-template/day$1/g" day$1/*
|
||||
fi
|
||||
|
||||
if [[ ! -f input/day$1_input.txt ]]; then
|
||||
wget --no-cookies --header "Cookie: session=$AOC_SESSION" https://adventofcode.com/2023/day/$1/input -O input/day$1_input.txt
|
||||
fi
|
|
@ -1,3 +0,0 @@
|
|||
#!/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