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';
|
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
|
bool String::operator==(StringView str) const
|
||||||
{
|
{
|
||||||
if (size() != str.size())
|
if (size() != str.size())
|
||||||
|
|
|
@ -145,6 +145,22 @@ namespace BAN
|
||||||
return m_data[0];
|
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
|
bool StringView::contains(char ch) const
|
||||||
{
|
{
|
||||||
for (size_type i = 0; i < m_size; i++)
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
|
|
@ -130,9 +130,7 @@ namespace BAN
|
||||||
ASSERT(!contains(key));
|
ASSERT(!contains(key));
|
||||||
TRY(rebucket(m_size + 1));
|
TRY(rebucket(m_size + 1));
|
||||||
auto& bucket = get_bucket(key);
|
auto& bucket = get_bucket(key);
|
||||||
auto result = bucket.emplace_back(key, forward<Args>(args)...);
|
TRY(bucket.emplace_back(key, forward<Args>(args)...));
|
||||||
if (result.is_error())
|
|
||||||
return Error::from_errno(ENOMEM);
|
|
||||||
m_size++;
|
m_size++;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -220,23 +218,17 @@ namespace BAN
|
||||||
|
|
||||||
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||||
Vector<LinkedList<Entry>> new_buckets;
|
Vector<LinkedList<Entry>> new_buckets;
|
||||||
if (new_buckets.resize(new_bucket_count).is_error())
|
TRY(new_buckets.resize(new_bucket_count));
|
||||||
return Error::from_errno(ENOMEM);
|
|
||||||
|
|
||||||
if constexpr(STABLE)
|
for (auto& bucket : m_buckets)
|
||||||
{
|
{
|
||||||
// NOTE: We have to copy the old entries to the new entries and not move
|
for (Entry& entry : bucket)
|
||||||
// since we might run out of memory half way through.
|
|
||||||
for (auto& bucket : m_buckets)
|
|
||||||
{
|
{
|
||||||
for (Entry& entry : bucket)
|
size_type bucket_index = HASH()(entry.key) % new_buckets.size();
|
||||||
{
|
if constexpr(STABLE)
|
||||||
size_type bucket_index = HASH()(entry.key) % new_buckets.size();
|
TRY(new_buckets[bucket_index].push_back(entry));
|
||||||
if constexpr(STABLE)
|
else
|
||||||
TRY(new_buckets[bucket_index].push_back(entry));
|
TRY(new_buckets[bucket_index].push_back(move(entry)));
|
||||||
else
|
|
||||||
TRY(new_buckets[bucket_index].push_back(BAN::move(entry)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Hash.h>
|
#include <BAN/Hash.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/LinkedList.h>
|
||||||
#include <BAN/Math.h>
|
#include <BAN/Math.h>
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
#include <BAN/Vector.h>
|
#include <BAN/Vector.h>
|
||||||
|
@ -9,24 +11,22 @@
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH = hash<T>, bool STABLE = true>
|
||||||
class HashSetIterator;
|
|
||||||
|
|
||||||
template<typename T, typename HASH = hash<T>>
|
|
||||||
class HashSet
|
class HashSet
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using size_type = hash_t;
|
using size_type = size_t;
|
||||||
using const_iterator = HashSetIterator<T, HASH>;
|
using iterator = IteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||||
|
using const_iterator = ConstIteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HashSet() = default;
|
HashSet() = default;
|
||||||
HashSet(const HashSet<T, HASH>&);
|
HashSet(const HashSet&);
|
||||||
HashSet(HashSet<T, HASH>&&);
|
HashSet(HashSet&&);
|
||||||
|
|
||||||
HashSet<T, HASH>& operator=(const HashSet<T, HASH>&);
|
HashSet& operator=(const HashSet&);
|
||||||
HashSet<T, HASH>& operator=(HashSet<T, HASH>&&);
|
HashSet& operator=(HashSet&&);
|
||||||
|
|
||||||
ErrorOr<void> insert(const T&);
|
ErrorOr<void> insert(const T&);
|
||||||
ErrorOr<void> insert(T&&);
|
ErrorOr<void> insert(T&&);
|
||||||
|
@ -35,8 +35,10 @@ namespace BAN
|
||||||
|
|
||||||
ErrorOr<void> reserve(size_type);
|
ErrorOr<void> reserve(size_type);
|
||||||
|
|
||||||
const_iterator begin() const { return const_iterator(this, m_buckets.begin()); }
|
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
|
||||||
const_iterator end() const { return const_iterator(this, m_buckets.end()); }
|
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;
|
bool contains(const T&) const;
|
||||||
|
|
||||||
|
@ -45,63 +47,31 @@ namespace BAN
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorOr<void> rebucket(size_type);
|
ErrorOr<void> rebucket(size_type);
|
||||||
Vector<T>& get_bucket(const T&);
|
LinkedList<T>& get_bucket(const T&);
|
||||||
const Vector<T>& get_bucket(const T&) const;
|
const LinkedList<T>& get_bucket(const T&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector<Vector<T>> m_buckets;
|
Vector<LinkedList<T>> m_buckets;
|
||||||
size_type m_size = 0;
|
size_type m_size = 0;
|
||||||
|
|
||||||
friend class HashSetIterator<T, HASH>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
class HashSetIterator
|
HashSet<T, HASH, STABLE>::HashSet(const HashSet& other)
|
||||||
{
|
|
||||||
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)
|
|
||||||
: m_buckets(other.m_buckets)
|
: m_buckets(other.m_buckets)
|
||||||
, m_size(other.m_size)
|
, m_size(other.m_size)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
HashSet<T, HASH>::HashSet(HashSet<T, HASH>&& other)
|
HashSet<T, HASH, STABLE>::HashSet(HashSet&& other)
|
||||||
: m_buckets(move(other.m_buckets))
|
: m_buckets(move(other.m_buckets))
|
||||||
, m_size(other.m_size)
|
, m_size(other.m_size)
|
||||||
{
|
{
|
||||||
other.clear();
|
other.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet<T, HASH>& other)
|
HashSet<T, HASH, STABLE>& HashSet<T, HASH, STABLE>::operator=(const HashSet& other)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
m_buckets = other.m_buckets;
|
m_buckets = other.m_buckets;
|
||||||
|
@ -109,8 +79,8 @@ namespace BAN
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet<T, HASH>&& other)
|
HashSet<T, HASH, STABLE>& HashSet<T, HASH, STABLE>::operator=(HashSet&& other)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
m_buckets = move(other.m_buckets);
|
m_buckets = move(other.m_buckets);
|
||||||
|
@ -119,14 +89,14 @@ namespace BAN
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
ErrorOr<void> HashSet<T, HASH>::insert(const T& key)
|
ErrorOr<void> HashSet<T, HASH, STABLE>::insert(const T& key)
|
||||||
{
|
{
|
||||||
return insert(move(T(key)));
|
return insert(move(T(key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
ErrorOr<void> HashSet<T, HASH>::insert(T&& key)
|
ErrorOr<void> HashSet<T, HASH, STABLE>::insert(T&& key)
|
||||||
{
|
{
|
||||||
if (!empty() && get_bucket(key).contains(key))
|
if (!empty() && get_bucket(key).contains(key))
|
||||||
return {};
|
return {};
|
||||||
|
@ -137,75 +107,75 @@ namespace BAN
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
void HashSet<T, HASH>::remove(const T& key)
|
void HashSet<T, HASH, STABLE>::remove(const T& key)
|
||||||
{
|
{
|
||||||
if (empty()) return;
|
if (empty()) return;
|
||||||
Vector<T>& bucket = get_bucket(key);
|
auto& bucket = get_bucket(key);
|
||||||
for (size_type i = 0; i < bucket.size(); i++)
|
for (auto it = bucket.begin(); it != bucket.end(); it++)
|
||||||
{
|
{
|
||||||
if (bucket[i] == key)
|
if (*it == key)
|
||||||
{
|
{
|
||||||
bucket.remove(i);
|
bucket.remove(it);
|
||||||
m_size--;
|
m_size--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
void HashSet<T, HASH>::clear()
|
void HashSet<T, HASH, STABLE>::clear()
|
||||||
{
|
{
|
||||||
m_buckets.clear();
|
m_buckets.clear();
|
||||||
m_size = 0;
|
m_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
ErrorOr<void> HashSet<T, HASH>::reserve(size_type size)
|
ErrorOr<void> HashSet<T, HASH, STABLE>::reserve(size_type size)
|
||||||
{
|
{
|
||||||
TRY(rebucket(size));
|
TRY(rebucket(size));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
bool HashSet<T, HASH>::contains(const T& key) const
|
bool HashSet<T, HASH, STABLE>::contains(const T& key) const
|
||||||
{
|
{
|
||||||
if (empty()) return false;
|
if (empty()) return false;
|
||||||
return get_bucket(key).contains(key);
|
return get_bucket(key).contains(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
typename HashSet<T, HASH>::size_type HashSet<T, HASH>::size() const
|
typename HashSet<T, HASH, STABLE>::size_type HashSet<T, HASH, STABLE>::size() const
|
||||||
{
|
{
|
||||||
return m_size;
|
return m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
bool HashSet<T, HASH>::empty() const
|
bool HashSet<T, HASH, STABLE>::empty() const
|
||||||
{
|
{
|
||||||
return m_size == 0;
|
return m_size == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
ErrorOr<void> HashSet<T, HASH>::rebucket(size_type bucket_count)
|
ErrorOr<void> HashSet<T, HASH, STABLE>::rebucket(size_type bucket_count)
|
||||||
{
|
{
|
||||||
if (m_buckets.size() >= bucket_count)
|
if (m_buckets.size() >= bucket_count)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
size_type new_bucket_count = Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||||
Vector<Vector<T>> new_buckets;
|
Vector<LinkedList<T>> 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 keys to the new keys and not move
|
for (auto& bucket : m_buckets)
|
||||||
// since we might run out of memory half way through.
|
|
||||||
for (Vector<T>& bucket : m_buckets)
|
|
||||||
{
|
{
|
||||||
for (T& key : bucket)
|
for (T& key : bucket)
|
||||||
{
|
{
|
||||||
size_type bucket_index = HASH()(key) % new_buckets.size();
|
size_type bucket_index = HASH()(key) % new_buckets.size();
|
||||||
if (new_buckets[bucket_index].push_back(key).is_error())
|
if constexpr(STABLE)
|
||||||
return Error::from_errno(ENOMEM);
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
Vector<T>& HashSet<T, HASH>::get_bucket(const T& key)
|
LinkedList<T>& HashSet<T, HASH, STABLE>::get_bucket(const T& key)
|
||||||
{
|
{
|
||||||
ASSERT(!m_buckets.empty());
|
ASSERT(!m_buckets.empty());
|
||||||
size_type index = HASH()(key) % m_buckets.size();
|
size_type index = HASH()(key) % m_buckets.size();
|
||||||
return m_buckets[index];
|
return m_buckets[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH, bool STABLE>
|
||||||
const Vector<T>& HashSet<T, HASH>::get_bucket(const T& key) const
|
const LinkedList<T>& HashSet<T, HASH, STABLE>::get_bucket(const T& key) const
|
||||||
{
|
{
|
||||||
ASSERT(!m_buckets.empty());
|
ASSERT(!m_buckets.empty());
|
||||||
size_type index = HASH()(key) % m_buckets.size();
|
size_type index = HASH()(key) % m_buckets.size();
|
||||||
return m_buckets[index];
|
return m_buckets[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
// Unstable hash set moves values between container during rebucketing.
|
||||||
HashSetIterator<T, HASH>& HashSetIterator<T, HASH>::operator++()
|
// 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
|
||||||
ASSERT(*this);
|
// going to stop using the hash set after insertion fails.
|
||||||
if (m_current_key == m_current_bucket->end())
|
template<typename T, typename HASH = hash<T>>
|
||||||
m_current_bucket++;
|
using HashSetUnstable = HashSet<T, HASH, false>;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -21,11 +21,11 @@ namespace BAN
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LinkedList() = default;
|
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(LinkedList<T>&& other) { *this = move(other); }
|
||||||
~LinkedList() { clear(); }
|
~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>&&);
|
LinkedList<T>& operator=(LinkedList<T>&&);
|
||||||
|
|
||||||
ErrorOr<void> push_back(const T&);
|
ErrorOr<void> push_back(const T&);
|
||||||
|
@ -115,7 +115,7 @@ namespace BAN
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
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();
|
clear();
|
||||||
for (const T& elem : other)
|
for (const T& elem : other)
|
||||||
|
|
|
@ -8,6 +8,12 @@
|
||||||
namespace BAN::Math
|
namespace BAN::Math
|
||||||
{
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr T abs(T val)
|
||||||
|
{
|
||||||
|
return val < 0 ? -val : val;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline constexpr T min(T a, T b)
|
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) const { ASSERT(index < m_size); return data()[index]; }
|
||||||
char& operator[](size_type index) { 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==(StringView) const;
|
||||||
bool operator==(const char*) const;
|
bool operator==(const char*) const;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/ForwardList.h>
|
|
||||||
#include <BAN/Formatter.h>
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/ForwardList.h>
|
||||||
#include <BAN/Iterators.h>
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/Optional.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
@ -35,6 +36,9 @@ namespace BAN
|
||||||
char back() const;
|
char back() const;
|
||||||
char front() const;
|
char front() const;
|
||||||
|
|
||||||
|
BAN::Optional<size_type> find(char) const;
|
||||||
|
BAN::Optional<size_type> find(bool(*comp)(char)) const;
|
||||||
|
|
||||||
bool contains(char) const;
|
bool contains(char) const;
|
||||||
size_type count(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> 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> 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> 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> inline constexpr bool is_integral_v = is_integral<T>::value;
|
||||||
template<typename T> concept integral = is_integral_v<T>;
|
template<typename T> concept integral = is_integral_v<T>;
|
||||||
|
|
|
@ -64,7 +64,8 @@ namespace BAN
|
||||||
const T& front() const;
|
const T& front() const;
|
||||||
T& front();
|
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> reserve(size_type);
|
||||||
ErrorOr<void> shrink_to_fit();
|
ErrorOr<void> shrink_to_fit();
|
||||||
|
|
||||||
|
@ -297,7 +298,21 @@ namespace BAN
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
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));
|
TRY(ensure_capacity(size));
|
||||||
if (size < m_size)
|
if (size < m_size)
|
||||||
|
|
|
@ -19,6 +19,13 @@ set(AOC2023_PROJECTS
|
||||||
day14
|
day14
|
||||||
day15
|
day15
|
||||||
day16
|
day16
|
||||||
|
day17
|
||||||
|
day18
|
||||||
|
day19
|
||||||
|
day20
|
||||||
|
day21
|
||||||
|
day23
|
||||||
|
full
|
||||||
)
|
)
|
||||||
|
|
||||||
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_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