Compare commits

...

19 Commits

Author SHA1 Message Date
Bananymous 910a57089b AOC2023: Implement day23 part1
My day22 implementation is off by couple of bricks...
2023-12-26 00:56:35 +02:00
Bananymous 861bf27e96 BAN: Implement abs() 2023-12-26 00:54:06 +02:00
Bananymous 36590fb5c7 AOC2023: Implement day21 part1 2023-12-24 14:58:21 +02:00
Bananymous ce990c3026 AOC2023: Implement day20 part1 2023-12-24 13:39:26 +02:00
Bananymous b833239a82 BAN: Make hashmap work with non-copy constructable values 2023-12-24 13:38:47 +02:00
Bananymous 6fec142760 BAN: Add requires clauses for vector 2023-12-24 13:38:20 +02:00
Bananymous 84b2438b3d BAN: Add requires for copy constructor for linked list 2023-12-24 13:37:49 +02:00
Bananymous 0e714d5eb4 BAN: Implement String::operator==(const String&) 2023-12-24 13:36:46 +02:00
Bananymous 9b8e6e6629 BAN: Implement is_*constructable 2023-12-24 13:36:12 +02:00
Bananymous 4146f2777b AOC2023: Implement 10 hour solution to day19 part2 2023-12-23 20:53:50 +02:00
Bananymous 64323c51e6 AOC2023: Implement day19 part 1 2023-12-23 18:47:44 +02:00
Bananymous a0200a7b10 AOC2023: Implement day18 part 1 2023-12-23 18:47:21 +02:00
Bananymous 8add759b5d AOC2023: Implement day17 part1 2023-12-23 18:46:43 +02:00
Bananymous 2faf90bc2b AOC2023: Add script to create day template and download input 2023-12-23 18:46:14 +02:00
Bananymous 762d575d70 AOC2023: Add program to run all days 2023-12-23 18:45:40 +02:00
Bananymous 2e77718f07 BAN: Implement find() for StringView 2023-12-23 18:43:52 +02:00
Bananymous f371fabe35 BAN: HashSet is now internally Vector<LinkedList<T>>
It used to be Vector<Vector<T>> but this feels more proper
2023-12-23 16:37:21 +02:00
Bananymous 79a15132da BAN: Cleanup HashSet
I now use BAN::Iterator as the base iterator. Also unstable version
was added.
2023-12-23 16:32:06 +02:00
Bananymous bacc0db778 BAN: Fix unstable hash map rebucket 2023-12-23 16:31:42 +02:00
30 changed files with 1666 additions and 187 deletions

View File

@ -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())

View File

@ -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++)

View File

@ -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,13 +218,8 @@ 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)
{
// 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)
@ -235,8 +228,7 @@ namespace BAN
if constexpr(STABLE)
TRY(new_buckets[bucket_index].push_back(entry));
else
TRY(new_buckets[bucket_index].push_back(BAN::move(entry)));
}
TRY(new_buckets[bucket_index].push_back(move(entry)));
}
}

View File

@ -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>;
}

View File

@ -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)

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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>;

View File

@ -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)

View File

@ -19,6 +19,13 @@ set(AOC2023_PROJECTS
day14
day15
day16
day17
day18
day19
day20
day21
day23
full
)
set(BANAN_AOC2023_BIN ${BANAN_BIN}/aoc2023)

View File

@ -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)

View File

@ -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);
}

View File

@ -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)

BIN
userspace/aoc2023/day17/a.out Executable file

Binary file not shown.

View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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)

View File

@ -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;
}

15
userspace/aoc2023/init-day.sh Executable file
View File

@ -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

View File

@ -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