solve day 19
This commit is contained in:
parent
7af3baa2e0
commit
1a031bf371
2
Makefile
2
Makefile
@ -1,5 +1,5 @@
|
||||
main: main.c day*.c vec.h aoc.h vec2.h types.h
|
||||
gcc -o main main.c -lcurl
|
||||
gcc -o main main.c -lcurl -O3
|
||||
|
||||
run: main
|
||||
./main $(day)
|
||||
|
336
day19.c
Normal file
336
day19.c
Normal file
@ -0,0 +1,336 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "aoc.h"
|
||||
|
||||
typedef enum {
|
||||
RESOURCE_ORE,
|
||||
RESOURCE_CLAY,
|
||||
RESOURCE_OBSIDIAN,
|
||||
RESOURCE_GEODE,
|
||||
__RESOURCE_COUNT
|
||||
} day19_resource;
|
||||
char *g_day19_resource_str[] = {
|
||||
[RESOURCE_ORE] = "ore",
|
||||
[RESOURCE_CLAY] = "clay",
|
||||
[RESOURCE_OBSIDIAN] = "obsidian",
|
||||
[RESOURCE_GEODE] = "geode"
|
||||
};
|
||||
|
||||
typedef u8 day19_robot_cost[__RESOURCE_COUNT];
|
||||
|
||||
typedef struct {
|
||||
day19_robot_cost robot_costs[__RESOURCE_COUNT];
|
||||
} day19_blueprint;
|
||||
|
||||
typedef struct {
|
||||
day19_blueprint *blueprints;
|
||||
size_t count;
|
||||
} day19_data;
|
||||
|
||||
static void day19_parse_count_with_resource(char *str, u8 *robot_cost)
|
||||
{
|
||||
char *after_count;
|
||||
u8 count = strtol(str, &after_count, 10);
|
||||
char *resource_str = after_count+1;
|
||||
|
||||
day19_resource resource = -1;
|
||||
for (int i = 0; i < ARRAY_LEN(g_day19_resource_str); i++) {
|
||||
if (strncmp(resource_str, g_day19_resource_str[i], strlen(g_day19_resource_str[i])) == 0) {
|
||||
resource = i;
|
||||
}
|
||||
}
|
||||
assert(resource != -1);
|
||||
|
||||
robot_cost[resource] = count;
|
||||
}
|
||||
|
||||
static void day19_parse_robot_cost(char* sentence, u8 *robot_cost)
|
||||
{
|
||||
char *costs_start = strstr(sentence, "costs")+6;
|
||||
char *and = strstr(costs_start, "and");
|
||||
if (and) {
|
||||
day19_parse_count_with_resource(costs_start, robot_cost);
|
||||
day19_parse_count_with_resource(and+4, robot_cost);
|
||||
} else {
|
||||
day19_parse_count_with_resource(costs_start, robot_cost);
|
||||
}
|
||||
}
|
||||
|
||||
static void* day19_parse(char** lines, int line_count)
|
||||
{
|
||||
day19_data *data = malloc(sizeof(day19_data));
|
||||
data->blueprints = calloc(line_count, sizeof(day19_blueprint));
|
||||
data->count = line_count;
|
||||
|
||||
for (int i = 0; i < line_count; i++) {
|
||||
day19_blueprint *bp = &data->blueprints[i];
|
||||
char *line = strchr(lines[i], ':')+2; // Skip `Blueprint %d:` part at start
|
||||
|
||||
day19_resource resource = RESOURCE_ORE;
|
||||
char *tok_save = NULL;
|
||||
for (char *tok = strtok_r(line, ".", &tok_save); tok; tok = strtok_r(NULL, ".", &tok_save)) {
|
||||
day19_parse_robot_cost(tok, bp->robot_costs[resource]);
|
||||
resource++;
|
||||
if (resource == __RESOURCE_COUNT) break;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void day19_print_blueprint(day19_blueprint *bp)
|
||||
{
|
||||
printf("Blueprint:\n");
|
||||
for (int i = 0; i < __RESOURCE_COUNT; i++) {
|
||||
printf(" Each %s robot costs", g_day19_resource_str[i]);
|
||||
bool resource_printed = false;
|
||||
for (int j = 0; j < __RESOURCE_COUNT; j++) {
|
||||
u8 count = bp->robot_costs[i][j];
|
||||
if (count == 0) continue;
|
||||
|
||||
if (resource_printed) printf(" and");
|
||||
printf(" %d %s", bp->robot_costs[i][j], g_day19_resource_str[j]);
|
||||
resource_printed = true;
|
||||
}
|
||||
printf(".\n");
|
||||
}
|
||||
}
|
||||
|
||||
static bool day19_can_buy(day19_robot_cost cost, u32 storage[__RESOURCE_COUNT])
|
||||
{
|
||||
for (int i = 0; i < __RESOURCE_COUNT; i++) {
|
||||
if (cost[i] > storage[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void day19_subtract_cost(day19_robot_cost cost, u32 storage[__RESOURCE_COUNT])
|
||||
{
|
||||
for (int i = 0; i < __RESOURCE_COUNT; i++) {
|
||||
storage[i] -= cost[i];
|
||||
}
|
||||
}
|
||||
|
||||
static u32 div_ceil(u32 a, u32 b)
|
||||
{
|
||||
return a / b + (a % b > 0);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
u16 *bucket_values;
|
||||
u32 *bucket_keys;
|
||||
u32 capacity;
|
||||
u32 count;
|
||||
} day19_cache;
|
||||
|
||||
static u32 day19_mix_and_mul(u32 key)
|
||||
{
|
||||
// Robert Jenkins' 32 bit Mix Function
|
||||
key += (key << 12);
|
||||
key ^= (key >> 22);
|
||||
key += (key << 4);
|
||||
key ^= (key >> 9);
|
||||
key += (key << 10);
|
||||
key ^= (key >> 2);
|
||||
key += (key << 7);
|
||||
key ^= (key >> 12);
|
||||
|
||||
// Knuth's Multiplicative Method
|
||||
key = (key >> 3) * 2654435761;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static u32 day19_hash(
|
||||
u32 time_left,
|
||||
u32 robots[__RESOURCE_COUNT],
|
||||
u32 storage[__RESOURCE_COUNT])
|
||||
{
|
||||
u32 key = day19_mix_and_mul(time_left);
|
||||
for (int i = 0; i < __RESOURCE_COUNT; i++) {
|
||||
key ^= day19_mix_and_mul(robots[i] << 16 | storage[i]);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
static i16 day19_cache_get(
|
||||
day19_cache *cache,
|
||||
u32 time_left,
|
||||
u32 robots[__RESOURCE_COUNT],
|
||||
u32 storage[__RESOURCE_COUNT])
|
||||
{
|
||||
u32 key = day19_hash(time_left, robots, storage);
|
||||
assert(key != 0 && "Cache key can't be zero");
|
||||
for (int i = 0; i < cache->count; i++) {
|
||||
u32 idx = (key + i) % cache->capacity;
|
||||
if (cache->bucket_keys[idx] == key) {
|
||||
return cache->bucket_values[idx];
|
||||
} else if (cache->bucket_keys[idx] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void day19_cache_put_with_key(day19_cache *cache, u32 key, u16 value)
|
||||
{
|
||||
assert(cache->count < cache->capacity && "Cache reached capacity");
|
||||
|
||||
for (int i = 0; i < cache->capacity; i++) {
|
||||
u32 idx = (key + i) % cache->capacity;
|
||||
if (cache->bucket_keys[idx] == key) {
|
||||
assert(false && "Already exists");
|
||||
} else if (cache->bucket_keys[idx] == 0) {
|
||||
cache->bucket_values[idx] = value;
|
||||
cache->bucket_keys[idx] = key;
|
||||
cache->count++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(false && "Failed to put to cache");
|
||||
}
|
||||
|
||||
static void day19_cache_put(
|
||||
day19_cache *cache,
|
||||
u32 time_left,
|
||||
u32 robots[__RESOURCE_COUNT],
|
||||
u32 storage[__RESOURCE_COUNT],
|
||||
u16 max_geodes)
|
||||
{
|
||||
u32 key = day19_hash(time_left, robots, storage);
|
||||
assert(key != 0 && "Cache key can't be zero");
|
||||
day19_cache_put_with_key(cache, key, max_geodes);
|
||||
}
|
||||
|
||||
static void day19_cache_init(day19_cache *cache, u32 initial_capacity)
|
||||
{
|
||||
cache->count = 0;
|
||||
cache->capacity = initial_capacity;
|
||||
cache->bucket_keys = calloc(initial_capacity, sizeof(u32));
|
||||
cache->bucket_values = calloc(initial_capacity, sizeof(u32));
|
||||
}
|
||||
|
||||
static void day19_cache_free(day19_cache *cache)
|
||||
{
|
||||
free(cache->bucket_values);
|
||||
free(cache->bucket_keys);
|
||||
cache->bucket_keys = NULL;
|
||||
cache->bucket_values = NULL;
|
||||
}
|
||||
|
||||
static u16 day19_max_geodes_dfs(
|
||||
day19_blueprint *bp,
|
||||
u32 time_left,
|
||||
u32 max_spend[__RESOURCE_COUNT],
|
||||
u32 robots[__RESOURCE_COUNT],
|
||||
u32 storage[__RESOURCE_COUNT],
|
||||
day19_cache *cache)
|
||||
{
|
||||
if (time_left == 0) return storage[RESOURCE_GEODE];
|
||||
|
||||
i32 cached_value = day19_cache_get(cache, time_left, robots, storage);
|
||||
if (cached_value != -1) return cached_value;
|
||||
|
||||
u16 max_geodes = storage[RESOURCE_GEODE] + robots[RESOURCE_GEODE] * time_left;
|
||||
|
||||
u32 storage_copy[__RESOURCE_COUNT];
|
||||
|
||||
for (int type = __RESOURCE_COUNT-1; type >= 0; type--) {
|
||||
if (robots[type] >= max_spend[type] && type != RESOURCE_GEODE) continue;
|
||||
|
||||
bool can_be_built = true;
|
||||
u32 time_until_robot = 0;
|
||||
for (int cost_type = 0; cost_type < __RESOURCE_COUNT; cost_type++) {
|
||||
u8 cost = bp->robot_costs[type][cost_type];
|
||||
if (cost == 0) continue;
|
||||
|
||||
if (robots[cost_type] == 0 && storage[cost_type] < cost) {
|
||||
can_be_built = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (storage[cost_type] <= cost) {
|
||||
u8 missing = cost - storage[cost_type];
|
||||
u32 time_to_resource = div_ceil(missing, robots[cost_type]);
|
||||
time_until_robot = MAX(time_until_robot, time_to_resource);
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_be_built) continue;
|
||||
|
||||
time_until_robot++;
|
||||
if (time_until_robot > time_left) continue;
|
||||
|
||||
for (int i = 0; i < __RESOURCE_COUNT; i++) {
|
||||
storage_copy[i] = storage[i];
|
||||
storage_copy[i] += robots[i] * time_until_robot;
|
||||
storage_copy[i] -= bp->robot_costs[type][i];
|
||||
}
|
||||
for (int i = 0; i < __RESOURCE_COUNT-1; i++) {
|
||||
storage_copy[i] = MIN(storage_copy[i], max_spend[i] * time_left);
|
||||
}
|
||||
robots[type]++;
|
||||
max_geodes = MAX(max_geodes, day19_max_geodes_dfs(bp, time_left - time_until_robot, max_spend, robots, storage_copy, cache));
|
||||
robots[type]--;
|
||||
}
|
||||
|
||||
day19_cache_put(cache, time_left, robots, storage, max_geodes);
|
||||
|
||||
return max_geodes;
|
||||
}
|
||||
|
||||
static u32 day19_max_geodes(day19_blueprint *bp, u32 time_limit)
|
||||
{
|
||||
u32 max_spend[__RESOURCE_COUNT] = { 0 };
|
||||
for (int resource = 0; resource < __RESOURCE_COUNT; resource++) {
|
||||
for (int robot = 0; robot < __RESOURCE_COUNT; robot++) {
|
||||
max_spend[resource] = MAX(max_spend[resource], bp->robot_costs[robot][resource]);
|
||||
}
|
||||
}
|
||||
u32 robots[__RESOURCE_COUNT] = { 0 };
|
||||
robots[RESOURCE_ORE] = 1;
|
||||
|
||||
u32 storage[__RESOURCE_COUNT] = { 0 };
|
||||
|
||||
day19_cache cache = { 0 };
|
||||
day19_cache_init(&cache, 1024*1024*4); // 4 MiB
|
||||
u32 answer = day19_max_geodes_dfs(bp, time_limit, max_spend, robots, storage, &cache);
|
||||
day19_cache_free(&cache);
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
static void day19_part1(void *p)
|
||||
{
|
||||
day19_data *data = (day19_data*)p;
|
||||
|
||||
u32 time_limit = 24;
|
||||
|
||||
u32 answer = 0;
|
||||
for (int i = 0; i < data->count; i++) {
|
||||
u32 max_geodes = day19_max_geodes(&data->blueprints[i], time_limit);
|
||||
answer += (i+1) * max_geodes;
|
||||
}
|
||||
printf("%d\n", answer);
|
||||
}
|
||||
|
||||
static void day19_part2(void *p)
|
||||
{
|
||||
day19_data *data = (day19_data*)p;
|
||||
|
||||
u32 time_limit = 32;
|
||||
|
||||
u32 answer = 1;
|
||||
answer *= day19_max_geodes(&data->blueprints[0], time_limit);
|
||||
answer *= day19_max_geodes(&data->blueprints[1], time_limit);
|
||||
answer *= day19_max_geodes(&data->blueprints[2], time_limit);
|
||||
printf("%d\n", answer);
|
||||
}
|
||||
|
||||
ADD_SOLUTION(19, day19_parse, day19_part1, day19_part2);
|
1
main.c
1
main.c
@ -27,6 +27,7 @@
|
||||
#include "day16.c"
|
||||
#include "day17.c"
|
||||
#include "day18.c"
|
||||
#include "day19.c"
|
||||
|
||||
Solution *find_solution(int day)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user