Compare commits

..

14 Commits

22 changed files with 1933 additions and 256 deletions

View File

@@ -301,3 +301,17 @@ aoc-c: res=665
aoc-c: res=25434 aoc-c: res=25434
time: 0:00.00 real, 0.00 user, 0.00 sys time: 0:00.00 real, 0.00 user, 0.00 sys
context-switch: 0+1, page-faults: 0+121 context-switch: 0+1, page-faults: 0+121
=========================================
================= day15 =================
=========================================
+++++++++++++++++ part 1
aoc-c: res=5176944
time: 0:00.00 real, 0.00 user, 0.00 sys
context-switch: 0+1, page-faults: 0+152
+++++++++++++++++ part 2
aoc-c: res=13350458933732
time: 0:00.00 real, 0.00 user, 0.00 sys
context-switch: 0+1, page-faults: 0+88

View File

@@ -1,6 +1,6 @@
/* aoc-c.c: Advent of Code 2022, day 13 /* aoc-c.c: Advent of Code 2022, day 13
* *
* Copyright (C) 2022 Bruno Raoult ("br") * Copyright (C) 2022-2023 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING. * Some rights reserved. See COPYING.
* *

View File

@@ -1,6 +1,6 @@
/* aoc-c.c: Advent of Code 2022, day 14 /* aoc-c.c: Advent of Code 2022, day 14
* *
* Copyright (C) 2022 Bruno Raoult ("br") * Copyright (C) 2022-2023 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING. * Some rights reserved. See COPYING.
* *

View File

@@ -1,6 +1,6 @@
/* aoc-c.c: Advent of Code 2022, day 15 /* aoc-c.c: Advent of Code 2022, day 15
* *
* Copyright (C) 2022 Bruno Raoult ("br") * Copyright (C) 2022-2023 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING. * Some rights reserved. See COPYING.
* *
@@ -20,30 +20,38 @@
#include "br.h" #include "br.h"
#include "list.h" #include "list.h"
#include "pool.h" #include "pool.h"
#include "hashtable.h"
#include "pjwhash-inline.h"
#include "debug.h" #include "debug.h"
#include "aoc.h" #include "aoc.h"
static pool_t *pool_segment, *pool_row; static pool_t *pool_segment, *pool_pair;
#define HBITS 20 /* 20 bits: 1,048,576 buckets */ #define HBITS 20 /* 20 bits: 1,048,576 buckets */
static DEFINE_HASHTABLE(hasht_rows, HBITS); struct coord {
int x, y;
};
#define TOP 0
#define RIGHT 1
#define BOTTOM 2
#define LEFT 3
/** /**
* struct map - full map * struct pair - input file pair list
* @xmin, @xmax: most left/right x-coordinates. * @sensor, @beacon: struct coord sensor and beacon coordinates.
* @ymin, @ymax: most top/bottom y-coordinates. * @manhattan: manhattan distance between sensor and beacon.
* @hash: rows hash table * @parity: beacon coordinates parity (as bishop color in chess).
* @corners: coordinates of rhombus immediately out of sensor range (clockwise).
* @list: list of pairs.
*/ */
struct map { struct pair {
int xmin, xmax, ymin, ymax; struct coord sensor, beacon;
struct hlist_head *hash; int manhattan;
} map = { int parity;
//INT_MAX, INT_MIN, INT_MAX, INT_MIN, hasht_rows struct coord corners[4];
INT_MIN, INT_MAX, INT_MIN, INT_MAX, hasht_rows struct list_head list;
}; };
LIST_HEAD(pairs_head);
/** /**
* struct row - row description * struct row - row description
@@ -56,7 +64,20 @@ struct row {
int beacons[64]; int beacons[64];
int nbeacons; int nbeacons;
struct list_head segments; struct list_head segments;
struct hlist_node hlist; };
/**
* struct map - full map
* @min, @max: map's min and max coordinates.
* @hash: rows hash table
*/
static struct map {
struct coord min, max;
struct row row; /* for part 1 */
//hlist_head *hash;
} map = {
.min = { INT_MIN, INT_MIN }, .max = {INT_MAX, INT_MAX },
.row = { 0, {0}, 0, LIST_HEAD_INIT(map.row.segments) }
}; };
/** /**
@@ -81,121 +102,48 @@ struct segment {
struct list_head list; struct list_head list;
}; };
static struct row *find_row(struct hlist_head *head, int row) static struct segment *get_segment(int row, int start, int end)
{
struct row *cur;
hlist_for_each_entry(cur, head, hlist)
if (cur->row == row)
return cur;
return NULL;
}
static void print_segments()
{
struct row *prow;
struct segment *s;
//log_f(1, "xmin=%d xmax=%d\n", map.xmin, map.xmax);
if (! testmode())
return;
for (int y = -3; y <= 22; ++y) {
if ((prow = find_row(&map.hash[hash_32(y, HBITS)], y))) {
log(1, "%2d ", y);
log(5, "prow(%d->%d)=%p\n", y, hash_32(y, HBITS), prow);
int count = 0;
list_for_each_entry(s, &prow->segments, list) {
log(1, "%s (%d,%d)", count? " -> ":"", s->start, s->end);
}
log(1, "\n");
}
}
}
static void print_map() /* for test mode only */
{
struct row *prow;
struct segment *s;
//log_f(1, "xmin=%d xmax=%d\n", map.xmin, map.xmax);
if (! testmode())
return;
log(1, " - 1 1 2 2 3\n");
log(1, " 5 0 5 0 5 0 5 0\n");
for (int y = -3; y <= 22; ++y) {
if ((prow = find_row(&map.hash[hash_32(y, HBITS)], y))) {
log(1, "%2d ", y);
log(5, "prow(%d->%d)=%p\n", y, hash_32(y, HBITS), prow);
int x = -6;
list_for_each_entry(s, &prow->segments, list) {
//log_f(1, "segment start=%d end=%d\n", s->start, s->end);
for (; x <= 30 && x < s->start; ++x) {
log(1, ".");
}
for (; x <= 30 && x <= s->end; ++x) {
log(1, "#");
}
}
for (; x <= 30; ++x) {
log(1, ".");
}
log(1, "\n");
}
}
}
static struct segment *get_segment(int row, int x1, int x2)
{ {
struct segment *new = pool_get(pool_segment); struct segment *new = pool_get(pool_segment);
log_f(5, "alloc segment (%d,%d) on row (%d)\n", x1, x2, row); log_f(5, "alloc segment (%d,%d) on row (%d)\n", start, end, row);
new->row=row; new->row=row;
new->start=x1; new->start=start;
new->end=x2; new->end=end;
INIT_LIST_HEAD(&new->list); INIT_LIST_HEAD(&new->list);
return new; return new;
} }
static int merge_segment(struct row *prow, int start, int end) static void merge_segment(int start, int end)
{ {
struct segment *seg, *new; struct segment *seg, *new;
struct list_head *cur, *tmp; struct list_head *cur, *tmp;
int l; static int l = 9;
l = debug_level_get(); new = get_segment(map.row.row, start, end);
//if (prow->row != 9) if (list_empty(&map.row.segments)) {
l++; list_add(&new->list, &map.row.segments);
log_f(l, "merging segment (%d,%d) on row (%d)\n", start, end, prow->row);
new = get_segment(prow->row, start, end);
if (list_empty(&prow->segments)) {
log_f(l, " first segment\n");
list_add(&new->list, &prow->segments);
goto end; goto end;
} }
list_for_each_safe(cur, tmp, &prow->segments) { list_for_each_safe(cur, tmp, &map.row.segments) {
seg = list_entry(cur, struct segment, list); seg = list_entry(cur, struct segment, list);
log_f(l, "compare to (start=%d end=%d)\n", seg->start, seg->end);
/* 1) check for disjoint segments */ /* 1) check for disjoint segments */
if (start > seg->end + 1) { if (start > seg->end + 1) {
log_f(l, " skipping (%d,%d)\n", seg->start, seg->end);
continue; continue;
} }
if (end < seg->start - 1) { if (end < seg->start - 1) {
log_f(l, " adding before (%d,%d)\n", seg->start, seg->end);
list_add_tail(&new->list, &seg->list); list_add_tail(&new->list, &seg->list);
goto end; goto end;
} }
/* new is inside cur: do nothing */ /* 2) new is inside cur: do nothing */
if (start >= seg->start && end <= seg->end) { if (start >= seg->start && end <= seg->end) {
log_f(l, " overlap IN, do nothing\n"); log_f(l, " overlap IN, do nothing\n");
pool_add(pool_segment, new); pool_add(pool_segment, new);
goto end; goto end;
} }
/* cur inside new: remove cur */ /* 3) cur inside new: remove cur */
if (start <= seg->start && end >= seg->end) { if (start <= seg->start && end >= seg->end) {
log_f(l, " overlap OUT, remove current\n"); log_f(l, " overlap OUT, remove current\n");
// TODO: avoid this // TODO: avoid this
@@ -204,219 +152,257 @@ static int merge_segment(struct row *prow, int start, int end)
continue; continue;
} }
/* 2) adjacent block */ /* 4) new segment start is within current one */
if (start >= seg->start && start <= seg->end + 1) { if (start >= seg->start && start <= seg->end + 1) {
log_f(l, " setting new start to %d\n", seg->start);
new->start = seg->start; new->start = seg->start;
list_del(cur); list_del(cur);
pool_add(pool_segment, seg); pool_add(pool_segment, seg);
continue; continue;
} }
/* 5) new segment is left-adjacent to current */
if (end == seg->start - 1) { if (end == seg->start - 1) {
seg->start = start; seg->start = start;
pool_add(pool_segment, new); pool_add(pool_segment, new);
goto end; goto end;
} }
/* we know here there is at least one overlap or contiguous */ /* from here, we know there is an overlap */
log_f(l, " could merge new=(%d,%d) with cur=(%d,%d)\n",
start, end, seg->start, seg->end);
/* exactly one overlap */ /* 6) adjust new start to current start */
log_f(3, " exactly one overlap\n"); if (start >= seg->start)
if (start >= seg->start) {
log_f(l, " overlap left new=(%d,%d)\n", new->start, new->end);
new->start = seg->start; new->start = seg->start;
}
/* 7) remove current if covered by new */
if (end >= seg->end){ if (end >= seg->end){
log_f(l, " overlap right: delete cur\n");
list_del(cur); list_del(cur);
pool_add(pool_segment, seg); pool_add(pool_segment, seg);
continue; continue;
} }
/* we stop here */ /* 8) replace current with new - finished */
log_f(l, " stop here\n");
new->end = seg->end; new->end = seg->end;
list_add_tail(&new->list, cur); list_add_tail(&new->list, cur);
list_del(cur); list_del(cur);
pool_add(pool_segment, seg); pool_add(pool_segment, seg);
goto end; goto end;
} }
log_f(l, " adding at end of list\n"); list_add_tail(&new->list, &map.row.segments);
list_add_tail(&new->list, &prow->segments);
end: end:
//print_segments();
return 10;
}
static void add_beacon(int bx, int by)
{
uint hash = by, bucket = hash_32(hash, HBITS);
struct row *prow = find_row(&map.hash[bucket], hash);
if (!prow) {
puts("fuck)");
exit(1);
}
for (int i = 0; i < prow->nbeacons; ++i) {
if (prow->beacons[i] == bx)
return; return;
} }
prow->beacons[prow->nbeacons++] = bx;
static __always_inline void add_beacon(int bx)
{
for (int i = 0; i < map.row.nbeacons; ++i) {
if (map.row.beacons[i] == bx)
return;
}
map.row.beacons[map.row.nbeacons++] = bx;
} }
static int add_segment(int row, int center, int half) /**
{ * is_off_range() - test if a point is off range from all sensors.
int x1, x2;
uint hash = row, bucket = hash_32(hash, HBITS);
struct row *prow = find_row(&map.hash[bucket], hash);
x1 = max(center - half, map.xmin);
x2 = min(center + half, map.xmax);
if (x1 != center - half || x2 != center + half)
log(1, "adjust x: min:%d->%d max:%d->%d\n",
center - half, x1, center + half, x2);
log_f(3, "adding segment (%d,%d) on row (%d) - bucket(%u) = %u prow=%p\n",
x1, x2, row, hash, bucket, prow);
/*
* if (row < map.ymin) {
* log(5, "new ymin=%d->%d\n", map.ymin, row);
* map.ymin = row;
* }
* if (row > map.ymax) {
* log(5, "new ymax=%d->%d\n", map.ymax, row);
* map.ymax = row;
* }
* if (x1 < map.xmin) {
* log(5, "new xmin=%d->%d\n", map.xmin, x1);
* map.xmin = x1;
* }
* if (x2 > map.xmax) {
* log(5, "new xmax=%d->%d\n", map.xmax, x2);
* map.xmax = x2;
* }
*/ */
log(3, "map borders: xmin=%d xmax=%d ymin=%d ymax=%d\n", static __always_inline int is_off_range(struct coord *point)
map.xmin, map.xmax, map.ymin, map.ymax); {
if (!prow) { struct pair *pair;
prow = pool_get(pool_row);
prow->row = row; /* reverse loop, because higher manhattan means higher chances to fail */
prow->nbeacons = 0; list_for_each_entry_reverse(pair, &pairs_head, list) {
INIT_HLIST_NODE(&prow->hlist); if ((abs(point->x - pair->sensor.x) +
INIT_LIST_HEAD(&prow->segments); abs(point->y - pair->sensor.y)) <= pair->manhattan)
hlist_add_head(&prow->hlist, &map.hash[bucket]); return 0;
} }
merge_segment(prow, x1, x2);
return 1; return 1;
} }
static int add_segments(int sx, int sy, int bx, int by) static struct pair *parse()
{ {
int manhattan = abs(bx - sx) + abs(by - sy); int ret;
int ymin = max(sy - manhattan, map.ymin); struct pair *pair = NULL, *cur;
int ymax = min(sy + manhattan, map.ymax); struct coord sensor, beacon;
log_f(2, "sensor4=(%d, %d) beacon=(%d, %d) - ", sx, sy, bx, by); ret = scanf("%*[^-0-9]%d%*[^-0-9]%d%*[^-0-9]%d%*[^-0-9]%d",
log(2, "manhattan=%u ymin=%d ymax=%d\n", manhattan, ymin, ymax); &sensor.x, &sensor.y, &beacon.x, &beacon.y);
//add_segment(sy, sx, manhattan); if (ret == 4) {
pair = pool_get(pool_pair);
pair->sensor = sensor;
pair->beacon = beacon;
pair->manhattan = abs(beacon.x - sensor.x) + abs(beacon.y - sensor.y);
pair->parity = (pair->beacon.x + pair->beacon.y) % 2;
pair->corners[TOP] = (struct coord) {
sensor.x, sensor.y - pair->manhattan - 1
};
pair->corners[BOTTOM] = (struct coord) {
sensor.x, sensor.y + pair->manhattan + 1
};
pair->corners[RIGHT] = (struct coord) {
sensor.x + pair->manhattan + 1, sensor.y
};
pair->corners[LEFT] = (struct coord) {
sensor.x - pair->manhattan - 1, sensor.y
};
for (int y = ymin; y <= ymax; ++y) { /* keep list ordered by manhattan */
int half = manhattan - abs(y - sy); if (!list_empty(&pairs_head)) {
add_segment(y, sx, half); list_for_each_entry(cur, &pairs_head, list) {
if (y == by) if (cur->manhattan > pair->manhattan) {
add_beacon(bx, by); list_add_tail(&pair->list, &cur->list);
//add_segment(y, sx, half); goto end;
} }
//for (int dy = 1, half = manhattan - 1; dy <= manhattan; ++dy, half--) { }
// add_segment(sy - dy, sx, half); }
// add_segment(sy + dy, sx, half); list_add_tail(&pair->list, &pairs_head);
//} }
//add_beacon(bx, by); end:
return 1; return pair;
} }
static inline int parse(int *sx, int *sy, int *bx, int *by) /**
{ * /#\
int ret = scanf("%*[^-0-9]%d%*[^-0-9]%d%*[^-0-9]%d%*[^-0-9]%d", * /#\ /# #\
sx, sy, bx, by); * /# #\ /# #\
* /# #\O/# <--- O is a possible point
* #X#
* rhomb A /#\ rhom B
* /# #\
* /# #\
* /# #\
* /# #\
* /# rhombs #\
* A & B
* (intersection)
*/
/**
* intersect() - find intersection of two segments
*
*/
static __always_inline struct coord *intersect(struct coord *p1, struct coord *p2,
struct coord *q1, struct coord *q2,
struct coord *ret)
{
int a1, a2, b1, b2, x, y;
/* a1, b1, a2, b2 are the formulas of (p1, p2) and (q1, q2), such as:
* y = ax + b
* a = (y2 - y1) / (x2 - x1) x2 ≠ x1
* b = y - a * x We can take either p1 or p2 coordinates
*/
a1 = (p2->y - p1->y) / (p2->x - p1->x);
b1 = p1->y - p1->x * a1;
a2 = (q2->y - q1->y) / (q2->x - q1->x);
b2 = q1->y - q1->x * a2;
/* Lines intersection (x,y) is at:
* (a1 * x) + b1 = (a2 * x) + b2
* x * (a1 - a2) = b2 - b1
* x = (b2 - b1) / (a1 - a2) a2 ≠ a1
* Then we find y = ax + b
*/
x = (b2 - b1) / (a1 - a2);
y = a1 * x + b1;
/* check if intersection is:
* 1) Within p1-p2 and q1-q2 segments
* 2) Within map area
*/
if (x >= min(min(p1->x, p2->x), min(q1->x, q2->x)) &&
x <= max(max(p1->x, p2->x), max(q1->x, q2->x)) &&
y >= min(min(p1->y, p2->y), min(q1->y, q2->y)) &&
y <= max(max(p1->y, p2->y), max(q1->y, q2->y)) &&
x >= map.min.x && x <= map.max.x &&
y >= map.min.y && y <= map.max.y) {
*ret = (struct coord) {x, y};
} else {
ret = NULL;
}
return ret; return ret;
} }
static ulong part1() #define T_R(p) &p->corners[TOP], &p->corners[RIGHT]
{ #define R_B(p) &p->corners[RIGHT], &p->corners[BOTTOM]
ulong res = 0; #define B_L(p) &p->corners[BOTTOM], &p->corners[LEFT]
int row = testmode() ? 10: 2000000; #define L_T(p) &p->corners[LEFT], &p->corners[TOP]
uint bucket = hash_32(row, HBITS);
int sx, sy, bx, by;
map.ymin = row - 1; static struct coord *check_intersect(struct coord *ret)
map.ymax = row + 1; {
while (parse(&sx, &sy, &bx, &by) > 0) { struct pair *pair, *second;
int manhattan = abs(bx - sx) + abs(by - sy);
add_segments(sx, sy, bx, by); list_for_each_entry(pair, &pairs_head, list) {
log(3, "m=%d : ", manhattan); second = list_prepare_entry(pair, &pairs_head, list);
list_for_each_entry_continue(second, &pairs_head, list) {
if (second->parity == pair->parity) {
/* top right segment */
if ((intersect(T_R(pair), R_B(second), ret) && is_off_range(ret)) ||
(intersect(T_R(pair), L_T(second), ret) && is_off_range(ret)))
return ret;
/* bottom left segment */
if ((intersect(B_L(pair), R_B(second), ret) && is_off_range(ret)) ||
(intersect(B_L(pair), L_T(second), ret) && is_off_range(ret)))
return ret;
/* right bottom segment */
if ((intersect(R_B(pair), T_R(second), ret) && is_off_range(ret)) ||
(intersect(R_B(pair), B_L(second), ret) && is_off_range(ret)))
return ret;
/* left top segment */
if ((intersect(L_T(pair), T_R(second), ret) && is_off_range(ret)) ||
(intersect(L_T(pair), B_L(second), ret) && is_off_range(ret)))
return ret;
} }
struct row *prow = find_row(&map.hash[bucket], row); }
if (prow) { }
return NULL;
}
static u64 part1(void)
{
u64 res = 0;
struct pair *pair;
struct segment *cur; struct segment *cur;
print_map();
list_for_each_entry(cur, &prow->segments, list) { map.row.row = map.min.y = map.max.y = testmode() ? 10: 2000000;
printf("counting segment (%d,%d) = %d nbeac=%d\n", cur->start, cur->end,
cur->end - cur->start + 1, prow->nbeacons); while ((pair = parse())) {
if (map.row.row >= pair->sensor.y - pair->manhattan &&
map.row.row <= pair->sensor.y + pair->manhattan) {
int half = pair->manhattan - abs(map.row.row - pair->sensor.y);
int x1 = max(pair->sensor.x - half, map.min.x);
int x2 = max(pair->sensor.x + half, map.min.x);
merge_segment(x1, x2);
if (map.row.row == pair->beacon.y)
add_beacon(pair->beacon.x);
}
}
list_for_each_entry(cur, &map.row.segments, list)
res += cur->end - cur->start + 1; res += cur->end - cur->start + 1;
} return res - map.row.nbeacons;
res -= prow->nbeacons;
}
return res;
} }
static ulong part2() static u64 part2()
{ {
ulong res = 0; u64 res = 0;
int sx, sy, bx, by; struct coord result = {0, 0};
map.xmin = map.ymin = 0; map.min.x = map.min.y = 0;
map.xmax = map.ymax = testmode()? 20: 4000000; map.max.x = map.max.y = testmode()? 20: 4000000;
while ((parse(&sx, &sy, &bx, &by)) > 0) {
int manhattan = abs(bx - sx) + abs(by - sy);
add_segments(sx, sy, bx, by); while (parse())
log(3, "m=%d : ", manhattan); ;
} check_intersect(&result);
for (int row = map.ymin; row <= map.ymax; ++row) { res = ((u64)result.x) * 4000000UL + (u64)result.y;
uint bucket = hash_32(row, HBITS);
struct row *prow = find_row(&map.hash[bucket], row);
if (!prow) {
printf("fuck 1: prow(%d)=NULL\n", row);
exit(1);
}
struct segment *cur;
if (list_empty(&prow->segments)) {
puts("fuck 2\n");
continue;
}
cur = list_first_entry(&prow->segments, struct segment, list);
if (cur->end != map.xmax) {
res = ((u64)cur->end + 1UL) * 4000000UL + (u64)row;
break;
}
}
return res; return res;
} }
int main(int ac, char **av) int main(int ac, char **av)
{ {
int part = parseargs(ac, av); int part = parseargs(ac, av);
pool_row = pool_create("rows", 8192, sizeof(struct row));
pool_segment = pool_create("segments", 8192, sizeof(struct segment)); pool_segment = pool_create("segments", 8192, sizeof(struct segment));
pool_pair = pool_create("pair", 32, sizeof(struct pair));
printf("%s: res=%lu\n", *av, part == 1? part1(): part2()); printf("%s: res=%lu\n", *av, part == 1? part1(): part2());
pool_destroy(pool_row);
pool_destroy(pool_segment); pool_destroy(pool_segment);
pool_destroy(pool_pair);
exit(0); exit(0);
} }

111
2022/day16/Makefile Normal file
View File

@@ -0,0 +1,111 @@
# AOC daily Makefile - GNU make only.
#
# Copyright (C) 2021-2023 Bruno Raoult ("br")
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
#
INPUT := input/input.txt
SHELL := /bin/bash
CC := gcc
BEAR := bear
CCLSFILE:= compile_commands.json
LIB := aoc_$(shell uname -m)
INCDIR := ../include
LIBDIR := ../lib
LDFLAGS := -L$(LIBDIR)
#LDLIB := -l$(LIB) -lm
LDLIB := -l$(LIB)
export LD_LIBRARY_PATH = $(LIBDIR)
CFLAGS += -std=gnu11
CFLAGS += -O2
CFLAGS += -g
# for gprof
# CFLAGS += -pg
CFLAGS += -Wall
CFLAGS += -Wextra
CFLAGS += -march=native
# Next one may be useful for valgrind (some invalid instructions)
# CFLAGS += -mno-tbm
CFLAGS += -Wmissing-declarations
CFLAGS += -Wno-unused-result
CFLAGS += -DDEBUG_DEBUG # activate general debug (debug.c)
CFLAGS += -DDEBUG_POOL # memory pools management
VALGRIND := valgrind
VALGRINDFLAGS := --leak-check=full --show-leak-kinds=all --track-origins=yes \
--sigill-diagnostics=yes --quiet --show-error-list=yes
TIME := \time -f "\ttime: %E real, %U user, %S sys\n\tcontext-switch:\t%c+%w, page-faults: %F+%R\n"
export PATH := .:$(PATH)
.PHONY: clean all compile assembly memcheck memcheck1 memcheck2 part1 part2 ccls bear org
all: README.org ccls part1 part2
memcheck: memcheck1 memcheck2
memcheck1: aoc-c
@$(VALGRIND) $(VALGRINDFLAGS) aoc-c -p 1 < $(INPUT)
memcheck2: aoc-c
@$(VALGRIND) $(VALGRINDFLAGS) aoc-c -p 2 < $(INPUT)
@#@valgrind -s --track-origins=yes aoc-c -p 2 < $(INPUT)
compile: aoc-c
cpp: aoc-c.i
assembly: aoc-c.s
part1: aoc-c
@#$(TIME) aoc.bash -p 1 < $(INPUT) 2>&1
@$(TIME) aoc-c -p 1 < $(INPUT)
part2: aoc-c
@#$(TIME) aoc.bash -p 2 < $(INPUT) 2>&1
@$(TIME) aoc-c -p 2 < $(INPUT)
ccls: $(CCLSFILE)
clean:
@rm -f aoc-c core* vgcore* gmon.out aoc-c.s aoc-c.i README.html compile_commands.json
aoc-c: aoc-c.c common.c
@echo compiling $<
$(CC) $(CFLAGS) $(LDFLAGS) -I $(INCDIR) $^ $(LDLIB) -o $@
# generate pre-processed file (.i) and assembler (.s)
%.i: %.c
@echo generating $@
@$(CC) -E $(CFLAGS) -I $(INCDIR) $< -o $@
%.s: %.c
@echo generating $@
@$(CC) -S -fverbose-asm $(CFLAGS) -I $(INCDIR) $< -o $@
# generate README.org from README.html (must cleanup !)
org: README.org
%.org: %.html
@echo generating $@. Cleanup before commit !
@pandoc $< -o $@
# generate compile_commands.json
$(CCLSFILE): aoc-c.c Makefile
$(BEAR) -- make clean compile
bear: clean
@touch .ccls-root
@$(BEAR) -- make compile

275
2022/day16/README.org Normal file
View File

@@ -0,0 +1,275 @@
** --- Day 16: Proboscidea Volcanium ---
The sensors have led you to the origin of the distress signal: yet
another handheld device, just like the one the Elves gave you. However,
you don't see any Elves around; instead, the device is surrounded by
elephants! They must have gotten lost in these tunnels, and one of the
elephants apparently figured out how to turn on the distress signal.
The ground rumbles again, much stronger this time. What kind of cave is
this, exactly? You scan the cave with your handheld device; it reports
mostly igneous rock, some ash, pockets of pressurized gas, magma... this
isn't just a cave, it's a volcano!
You need to get the elephants out of here, quickly. Your device
estimates that you have /30 minutes/ before the volcano erupts, so you
don't have time to go back out the way you came in.
You scan the cave for other options and discover a network of pipes and
pressure-release /valves/. You aren't sure how such a system got into a
volcano, but you don't have time to complain; your device produces a
report (your puzzle input) of each valve's /flow rate/ if it were opened
(in pressure per minute) and the tunnels you could use to move between
the valves.
There's even a valve in the room you and the elephants are currently
standing in labeled =AA=. You estimate it will take you one minute to
open a single valve and one minute to follow any tunnel from one valve
to another. What is the most pressure you could release?
For example, suppose you had the following scan output:
#+begin_example
Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
Valve BB has flow rate=13; tunnels lead to valves CC, AA
Valve CC has flow rate=2; tunnels lead to valves DD, BB
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
Valve EE has flow rate=3; tunnels lead to valves FF, DD
Valve FF has flow rate=0; tunnels lead to valves EE, GG
Valve GG has flow rate=0; tunnels lead to valves FF, HH
Valve HH has flow rate=22; tunnel leads to valve GG
Valve II has flow rate=0; tunnels lead to valves AA, JJ
Valve JJ has flow rate=21; tunnel leads to valve II
#+end_example
All of the valves begin /closed/. You start at valve =AA=, but it must
be damaged or jammed or something: its flow rate is =0=, so there's no
point in opening it. However, you could spend one minute moving to valve
=BB= and another minute opening it; doing so would release pressure
during the remaining /28 minutes/ at a flow rate of =13=, a total
eventual pressure release of =28 * 13 = 364=. Then, you could spend your
third minute moving to valve =CC= and your fourth minute opening it,
providing an additional /26 minutes/ of eventual pressure release at a
flow rate of =2=, or =52= total pressure released by valve =CC=.
Making your way through the tunnels like this, you could probably open
many or all of the valves by the time 30 minutes have elapsed. However,
you need to release as much pressure as possible, so you'll need to be
methodical. Instead, consider this approach:
#+begin_example
== Minute 1 ==
No valves are open.
You move to valve DD.
== Minute 2 ==
No valves are open.
You open valve DD.
== Minute 3 ==
Valve DD is open, releasing 20 pressure.
You move to valve CC.
== Minute 4 ==
Valve DD is open, releasing 20 pressure.
You move to valve BB.
== Minute 5 ==
Valve DD is open, releasing 20 pressure.
You open valve BB.
== Minute 6 ==
Valves BB and DD are open, releasing 33 pressure.
You move to valve AA.
== Minute 7 ==
Valves BB and DD are open, releasing 33 pressure.
You move to valve II.
== Minute 8 ==
Valves BB and DD are open, releasing 33 pressure.
You move to valve JJ.
== Minute 9 ==
Valves BB and DD are open, releasing 33 pressure.
You open valve JJ.
== Minute 10 ==
Valves BB, DD, and JJ are open, releasing 54 pressure.
You move to valve II.
== Minute 11 ==
Valves BB, DD, and JJ are open, releasing 54 pressure.
You move to valve AA.
== Minute 12 ==
Valves BB, DD, and JJ are open, releasing 54 pressure.
You move to valve DD.
== Minute 13 ==
Valves BB, DD, and JJ are open, releasing 54 pressure.
You move to valve EE.
== Minute 14 ==
Valves BB, DD, and JJ are open, releasing 54 pressure.
You move to valve FF.
== Minute 15 ==
Valves BB, DD, and JJ are open, releasing 54 pressure.
You move to valve GG.
== Minute 16 ==
Valves BB, DD, and JJ are open, releasing 54 pressure.
You move to valve HH.
== Minute 17 ==
Valves BB, DD, and JJ are open, releasing 54 pressure.
You open valve HH.
== Minute 18 ==
Valves BB, DD, HH, and JJ are open, releasing 76 pressure.
You move to valve GG.
== Minute 19 ==
Valves BB, DD, HH, and JJ are open, releasing 76 pressure.
You move to valve FF.
== Minute 20 ==
Valves BB, DD, HH, and JJ are open, releasing 76 pressure.
You move to valve EE.
== Minute 21 ==
Valves BB, DD, HH, and JJ are open, releasing 76 pressure.
You open valve EE.
== Minute 22 ==
Valves BB, DD, EE, HH, and JJ are open, releasing 79 pressure.
You move to valve DD.
== Minute 23 ==
Valves BB, DD, EE, HH, and JJ are open, releasing 79 pressure.
You move to valve CC.
== Minute 24 ==
Valves BB, DD, EE, HH, and JJ are open, releasing 79 pressure.
You open valve CC.
== Minute 25 ==
Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure.
== Minute 26 ==
Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure.
== Minute 27 ==
Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure.
== Minute 28 ==
Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure.
== Minute 29 ==
Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure.
== Minute 30 ==
Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure.
#+end_example
This approach lets you release the most pressure possible in 30 minutes
with this valve layout, =1651=.
Work out the steps to release the most pressure in 30 minutes. /What is
the most pressure you can release?/
Your puzzle answer was =1737=.
** --- Part Two ---
You're worried that even with an optimal approach, the pressure released
won't be enough. What if you got one of the elephants to help you?
It would take you 4 minutes to teach an elephant how to open the right
valves in the right order, leaving you with only /26 minutes/ to
actually execute your plan. Would having two of you working together be
better, even if it means having less time? (Assume that you teach the
elephant before opening any valves yourself, giving you both the same
full 26 minutes.)
In the example above, you could teach the elephant to help you as
follows:
#+begin_example
== Minute 1 ==
No valves are open.
You move to valve II.
The elephant moves to valve DD.
== Minute 2 ==
No valves are open.
You move to valve JJ.
The elephant opens valve DD.
== Minute 3 ==
Valve DD is open, releasing 20 pressure.
You open valve JJ.
The elephant moves to valve EE.
== Minute 4 ==
Valves DD and JJ are open, releasing 41 pressure.
You move to valve II.
The elephant moves to valve FF.
== Minute 5 ==
Valves DD and JJ are open, releasing 41 pressure.
You move to valve AA.
The elephant moves to valve GG.
== Minute 6 ==
Valves DD and JJ are open, releasing 41 pressure.
You move to valve BB.
The elephant moves to valve HH.
== Minute 7 ==
Valves DD and JJ are open, releasing 41 pressure.
You open valve BB.
The elephant opens valve HH.
== Minute 8 ==
Valves BB, DD, HH, and JJ are open, releasing 76 pressure.
You move to valve CC.
The elephant moves to valve GG.
== Minute 9 ==
Valves BB, DD, HH, and JJ are open, releasing 76 pressure.
You open valve CC.
The elephant moves to valve FF.
== Minute 10 ==
Valves BB, CC, DD, HH, and JJ are open, releasing 78 pressure.
The elephant moves to valve EE.
== Minute 11 ==
Valves BB, CC, DD, HH, and JJ are open, releasing 78 pressure.
The elephant opens valve EE.
(At this point, all valves are open.)
== Minute 12 ==
Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure.
...
== Minute 20 ==
Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure.
...
== Minute 26 ==
Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure.
#+end_example
With the elephant helping, after 26 minutes, the best you could do would
release a total of =1707= pressure.
/With you and an elephant working together for 26 minutes, what is the
most pressure you could release?/
Your puzzle answer was =2216=.
Both parts of this puzzle are complete! They provide two gold stars: **

446
2022/day16/aoc-c.c Normal file
View File

@@ -0,0 +1,446 @@
/* aoc-c.c: Advent of Code 2022, day 16
*
* Copyright (C) 2023 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "br.h"
#include "list.h"
#include "pool.h"
#include "debug.h"
#include "bits.h"
#include "aoc.h"
pool_t *pool_valve;
union val {
u32 val;
char str[3];
};
enum state {
CLOSED,
OPENED
};
struct worker {
struct valve *pos;
int depth;
int time;
};
struct valve {
int index; /* -1 for zero flow rate */
union val val;
enum state state;
int rate;
int evalflow, evaltime;
int playedflow, playedtime;
struct hlist_node hlist;
struct list_head index_sorted;
struct list_head flow_sorted;
struct list_head eval;
int worker;
struct list_head played;
int ntunnels, tottunnels;
struct valve **tunnels; /* array */
};
static struct graph {
struct valve *aa; /* head ("AA") */
int npositive; /* only "AA" & working valves */
int nvalves;
u64 opened; /* bitmask of opened valves */
u64 openable; /* bitmask of openable valves */
struct list_head index_sorted; /* TO REMOVE ? */
struct list_head flow_sorted;
struct list_head eval;
struct list_head played[2];
struct valve **indexed_all;
int *dist; /* 2-D array */
} graph = {
.aa = NULL,
.npositive = 0,
.nvalves = 0,
.index_sorted = LIST_HEAD_INIT(graph.index_sorted),
.flow_sorted = LIST_HEAD_INIT(graph.flow_sorted),
.eval = LIST_HEAD_INIT(graph.eval),
.played[0] = LIST_HEAD_INIT(graph.played[0]),
.played[1] = LIST_HEAD_INIT(graph.played[1]),
.indexed_all = NULL,
.dist = NULL
};
#define POS(a, b) ((a)*graph.nvalves + (b))
#define DIST(a, b) (graph.dist[POS((a), (b))])
static void print_valves()
{
struct valve *cur;
printf("**** graph: .head=%p npositive=%d\n", graph.aa, graph.npositive);
printf("index1: ");
list_for_each_entry(cur, &graph.index_sorted, index_sorted) {
printf("%d:%s ", cur->index, cur->val.str);
}
printf("\n");
printf("index2: ");
for (int i = 0; i < graph.nvalves; ++i) {
printf("%d:%s ", graph.indexed_all[i]->index, graph.indexed_all[i]->val.str);
}
printf("\n");
if (testmode()) {
printf("distances:\n ");
for (int i = 0; i < graph.nvalves; ++i) {
printf(" %s", graph.indexed_all[i]->val.str);
}
printf("\n");
for (int i = 0; i < graph.nvalves; ++i) {
printf("%s ", graph.indexed_all[i]->val.str);
for (int j = 0; j < graph.nvalves; ++j) {
printf("%5d ", DIST(i, j));
}
printf("\n");
}
}
printf("flow_sorted: ");
list_for_each_entry(cur, &graph.flow_sorted, flow_sorted) {
printf("%s:%d ", cur->val.str, cur->rate);
}
printf("\n");
printf("openable: %#lx ", graph.openable);
int pos, tmp;
bit_for_each64_2(pos, tmp, graph.openable) {
printf("%d ", pos);
}
printf("\n");
}
#define PAD3 log(3, "%*s", _depth * 2, "")
#define PAD4 log(4, "%*s", _depth * 2, "")
/**
* eval() - eval possible moves from @flow_sorted list.
* @_depth: recursivity depth (for debug only, TODO: remove).
* @nworkers: number of workers.
* @workers: array of workers.
* @pick: max position (in @flow_sorted) to pick moves from (-1 for all).
* @pressure: total pressure per time unit so far.
*
* Find the "best" next move by evaluating only the first @pick elements
* in @flow_sorted list.
*
* @Return: the current position eval.
*/
static struct valve *eval(int _depth, int nworkers, struct worker *worker, int pick, int pressure)
{
struct valve *cur, *best = NULL, *sub;
struct list_head *list_flow, *tmp;
int _pick = pick, val = 0, val1, max = 0, bestworker = -1;
int _nworkers = nworkers;
if (nworkers == 2 && worker[0].pos->index == worker[1].pos->index)
_nworkers = 1;
PAD3; log_f(3, "EVAL _depth=%d w0={ pos=%d[%s] depth=%d time=%d } w1={ pos=%d[%s] depth=%d time=%d } pick=%d pressure=%d \n",
_depth,
worker[0].pos->index, worker[0].pos->val.str,
worker[0].depth, worker[0].time,
worker[1].pos->index, worker[1].pos->val.str,
worker[1].depth, worker[1].time,
pick, pressure);
list_for_each_safe(list_flow, tmp, &graph.flow_sorted) {
cur = list_entry(list_flow, struct valve, flow_sorted);
//int nworkers = worker[0].pos->index == worker[1].pos->index? 1: 2;
if (!--_pick) {
PAD4; log(4, "pick exhausted\n");
break;
}
for (int _w = 0; _w < _nworkers; ++_w) {
struct worker *w = worker + _w;
int d = DIST(w->pos->index, cur->index);
int remain = w->time - (d + 1);
PAD3; log(3, "worker %d/%d ", _w + 1, nworkers );
PAD3; log(3, "dist(%s,%s) = %d ", w->pos->val.str, cur->val.str, d);
if (remain < 1) {
PAD3; log(4, "time exhausted\n");
continue;
}
val = remain * cur->rate;
PAD3; log(3, "--> val=%d\n", val);
if (w->depth > 0) {
struct worker _tmp = *w;
w->pos = cur;
w->depth--;
w->time = remain;
/* do not use list_del() here, to preserve prev/next pointers */
__list_del_entry(list_flow);
sub = eval(_depth + 1, nworkers, worker, pick, pressure + w->pos->rate);
list_flow->prev->next = list_flow;
list_flow->next->prev = list_flow;
*w = _tmp;
} else {
sub = NULL;
}
val1 = sub? sub->evalflow: 0;
PAD3; log(3, "eval3(%s->%s)= %5d = %d + %d", w->pos->val.str, cur->val.str,
val+val1, val, val1);
if (val + val1 > max) {
max = val + val1;
best = cur;
bestworker = _w;
log(3, " NEW MAX !");
}
log(3, "\n");
}
}
if (best) {
best->evalflow = max;
best->worker = bestworker; /* FIXME */
PAD3; log(3, "EVAL returning best [%s] worker=%d eval=%d\n", best->val.str,
best->worker, max);
}
return best;
}
static struct valve *find_valve(union val val, int ntunnels, int rate)
{
struct valve *cur;
log_f(3, "val=%s ntunnels=%d rate=%d\n", val.str, ntunnels, rate);
list_for_each_entry(cur, &graph.index_sorted, index_sorted) {
//log(3, "\tcomparing with found, addr=%p\n", cur);
if (cur->val.val == val.val) {
log(3, "\tfound, addr=%p\n", cur);
if (ntunnels)
goto init;
goto end;
}
}
cur = pool_get(pool_valve);
cur->val.val = val.val;
cur->ntunnels = 0;
cur->state = CLOSED;
cur->worker = -1;
cur->evalflow = cur->playedflow = 0;
cur->evaltime = cur->playedtime = 30;
INIT_LIST_HEAD(&cur->index_sorted);
INIT_LIST_HEAD(&cur->flow_sorted);
INIT_LIST_HEAD(&cur->eval);
INIT_LIST_HEAD(&cur->played);
log(3, "\talloc new, addr=%p\n", cur);
init:
if (ntunnels) {
cur->rate = rate;
cur->tottunnels = ntunnels;
cur->tunnels = calloc(ntunnels, sizeof(struct valve *));
}
end:
return cur;
}
/**
* nthtok - get nth token fron string.
* @buf: buffer to parse.
* @sep: separators string.
* @n: token number (0: first token).
*
* @Return: pointer to token.
*/
static char *nthtok(char *buf, const char *sep, int n)
{
char *ret;
for (; n >= 0; n--) {
ret = strtok(buf, sep);
buf = NULL;
}
return ret;
}
#define SEP " ,;="
static struct graph *parse()
{
int index = 0, ntunnels;
size_t alloc = 0;
ssize_t buflen;
char *buf = NULL, *tok;
int rate;
struct valve *v1, *v2;
union val cur, link;
while ((buflen = getline(&buf, &alloc, stdin)) > 0) {
buf[--buflen] = 0;
/* valve name */
strncpy(cur.str, nthtok(buf, SEP, 1), sizeof(cur.str));
//printf("valve=%s ", tok);
rate = atoi(nthtok(NULL, SEP, 3));
//printf("rate=%s ", tok);
tok = nthtok(NULL, SEP, 4);
ntunnels = (buf + buflen - tok) / 4 + 1;
v1 = find_valve(cur, ntunnels, rate);
v1->index = index++;
// TODO: remove this list ?
list_add_tail(&v1->index_sorted, &graph.index_sorted);
graph.nvalves++;
//if (rate || v1->val.val == ('A' << 8 | 'A')) {
if (rate) {
struct valve *cur;
graph.npositive++;
/* keep this list sorted by flow decrasing */
list_for_each_entry(cur, &graph.flow_sorted, flow_sorted) {
if (rate > cur->rate) {
list_add_tail(&v1->flow_sorted, &cur->flow_sorted);
goto inserted;
}
}
list_add_tail(&v1->flow_sorted, &graph.flow_sorted);
inserted: ;
if (rate) {
//printf("adjust openable(%d): %#lx", v1->index, graph.openable);
graph.openable |= (1 << v1->index);
//printf("->%#lx", graph.openable);
}
}
//printf("lead=%s ntunnels=%d ", tok, ntunnels);
do {
link.val = *(u16 *)tok;
v2 = find_valve(link, 0, 0);
*(v1->tunnels + v1->ntunnels++) = v2;
//printf(",%s", tok);
} while ((tok = nthtok(NULL, SEP, 0)));
//printf("\n");
}
graph.aa = find_valve((union val) { .str="AA" }, 0, 0);
/* build array of indexed valves */
graph.indexed_all = calloc(graph.nvalves, sizeof(struct valve *));
list_for_each_entry(v1, &graph.index_sorted, index_sorted) {
graph.indexed_all[v1->index] = v1;
}
return &graph;
}
static int is_neighbour(int i, int j)
{
struct valve *v1 = graph.indexed_all[i], *v2 = graph.indexed_all[j];
for (int i = 0; i < v1->ntunnels; ++i)
if (v1->tunnels[i]->val.val == v2->val.val)
return 1;
return 0;
}
static void build_distances()
{
int i, j, k;
graph.dist = calloc(graph.nvalves * graph.nvalves, sizeof(int));
/* initialize values */
for (i = 0; i < graph.nvalves; ++i) {
for (j = 1; j < graph.nvalves; ++j) {
if (i != j) {
if (is_neighbour(i, j))
DIST(i, j) = DIST(j, i) = 1;
else
DIST(i, j) = DIST(j, i) = 10000;
}
}
}
/* get all distances using Floyd-Warshall
* see https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
*
* Add all vertices one by one to the set of intermediate vertices.
* ---> Before start of an iteration, we have shortest distances between all
* pairs of vertices such that the shortest distances consider only the
* vertices in set {0, 1, 2, .. k-1} as intermediate vertices.
* ----> After the end of an iteration, vertex no. k is added to the set of
* intermediate vertices and the set becomes {0, 1, 2, .. k}
*/
for (k = 0; k < graph.nvalves; k++) {
/* Pick all vertices as source one by one */
for (i = 0; i < graph.nvalves; i++) {
/* Pick all vertices as destination for the above picked source */
for (j = i + 1; j < graph.nvalves; j++)
DIST(i, j) = DIST(j, i) = min(DIST(i, j), DIST(i, k) + DIST(k, j));
}
}
return;
}
static void print_played(int nworkers)
{
struct valve *p;
int total = 0;
for (int w = 0; w < nworkers; ++w) {
int remain = 26, i = 1;
struct valve *prev = graph.aa;
i = 1;
printf("played by %d/%d:\n", w + 1, nworkers);
list_for_each_entry(p, &graph.played[w], played) {
printf("%2d: %s, ", i, p->val.str);
remain -= DIST(p->index, prev->index) + 1;
total += p->rate * remain;
printf("dist=%d remain=%d total=%d", DIST(p->index, prev->index), remain,
total);
printf("\n");
i++;
prev = p;
}
}
}
static int doit(int part)
{
struct worker w[2];
struct valve *best;
int res = 0;
//int topick = part == 1? 7: 12;
int topick = part == 1? 12: 12;
w[0].pos = w[1].pos = graph.aa;
//w[0].depth = w[1].depth = part == 1? 7: 4;
w[0].depth = w[1].depth = part == 1? 4: 4;
w[0].time = w[1].time = part == 1? 30: 26;
while ((best = eval(0, part, w, topick, 0))) {
list_del(&best->flow_sorted);
list_add_tail(&best->played, &graph.played[best->worker]);
w[best->worker].time -= DIST(w[best->worker].pos->index, best->index) + 1;
w[best->worker].pos = best;
res += best->rate * w[best->worker].time;
print_played(part);
}
return res;
}
static ulong part1()
{
return doit(1);
}
static ulong part2()
{
return doit(2);
}
int main(int ac, char **av)
{
int part = parseargs(ac, av);
pool_valve = pool_create("valve", 512, sizeof(struct valve));
parse();
build_distances();
print_valves();
printf("%s: res=%lu\n", *av, part == 1? part1(): part2());
exit(0);
}

17
2022/day16/aoc.h Normal file
View File

@@ -0,0 +1,17 @@
/* aoc.c: Advent of Code 2022
*
* Copyright (C) 2022-2023 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*/
#ifndef _AOC_H_
#define _AOC_H_
extern int parseargs(int ac, char**av);
extern int testmode(void);
#endif /* _AOC_H_ */

68
2022/day16/common.bash Normal file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
#
# common.bash: Advent of Code 2022, common bash functions
#
# Copyright (C) 2022-2023 Bruno Raoult ("br")
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
# shellcheck disable=2034
export cmdname=${0##*/}
export debug=0
export res
export LANG=C
shopt -s extglob
set -o noglob
usage() {
printf "usage: %s [-d DEBUG] [-p PART]\n" "$cmdname"
exit 1
}
checkargs() {
local part=1
while getopts p:d: todo; do
case "$todo" in
d)
if [[ "$OPTARG" =~ ^[[:digit:]+]$ ]]; then
debug="$OPTARG"
else
printf "%s: illegal [%s] debug level.\n" "$CMD" "$OPTARG"
exit 1
fi
;;
p)
if [[ "$OPTARG" =~ ^[12]$ ]]; then
part="$OPTARG"
else
printf "%s: illegal [%s] part.\n" "$CMD" "$OPTARG"
exit 1
fi
;;
*)
usage
;;
esac
done
# Now check remaining argument (backup directory)
shift $((OPTIND - 1))
(( $# > 1 )) && usage
return "$part"
}
main() {
local -i part
checkargs "$@"
part=$?
parse "$part"
solve "$part"
printf "%s: res=%s\n" "$cmdname" "$res"
}

59
2022/day16/common.c Normal file
View File

@@ -0,0 +1,59 @@
/* common.c: Advent of Code 2022, common functions
*
* Copyright (C) 2022-2023 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "aoc.h"
#include "debug.h"
static int _testmode = 0;
static int usage(char *prg)
{
fprintf(stderr, "Usage: %s [-t][-d debug_level] [-p part] [-i input]\n", prg);
return 1;
}
int testmode(void)
{
return _testmode;
}
int parseargs(int ac, char **av)
{
int opt, part = 1;
while ((opt = getopt(ac, av, "td:p:")) != -1) {
switch (opt) {
case 't':
_testmode = 1;
break;
case 'd':
debug_level_set(atoi(optarg));
break;
case 'p': /* 1 or 2 */
part = atoi(optarg);
if (part < 1 || part > 2)
return usage(*av);
break;
case 'i':
default:
return usage(*av);
}
}
if (optind < ac)
return usage(*av);
return part;
}

View File

@@ -0,0 +1,10 @@
Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
Valve BB has flow rate=13; tunnels lead to valves CC, AA
Valve CC has flow rate=2; tunnels lead to valves DD, BB
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
Valve EE has flow rate=3; tunnels lead to valves FF, DD
Valve FF has flow rate=0; tunnels lead to valves EE, GG
Valve GG has flow rate=0; tunnels lead to valves FF, HH
Valve HH has flow rate=22; tunnel leads to valve GG
Valve II has flow rate=0; tunnels lead to valves AA, JJ
Valve JJ has flow rate=21; tunnel leads to valve II

View File

@@ -0,0 +1,54 @@
Valve EJ has flow rate=25; tunnel leads to valve MC
Valve WC has flow rate=0; tunnels lead to valves OW, RU
Valve NP has flow rate=0; tunnels lead to valves VR, KL
Valve AA has flow rate=0; tunnels lead to valves QT, AP, EZ, AK, XV
Valve VO has flow rate=6; tunnels lead to valves KM, RF, HS, LJ, IA
Valve CB has flow rate=0; tunnels lead to valves UI, UP
Valve TE has flow rate=18; tunnel leads to valve JT
Valve CZ has flow rate=0; tunnels lead to valves UP, OW
Valve LJ has flow rate=0; tunnels lead to valves DV, VO
Valve UP has flow rate=7; tunnels lead to valves SK, CB, CZ
Valve FP has flow rate=0; tunnels lead to valves OW, RE
Valve KM has flow rate=0; tunnels lead to valves SE, VO
Valve DV has flow rate=0; tunnels lead to valves LJ, UM
Valve FL has flow rate=0; tunnels lead to valves AH, TS
Valve VR has flow rate=24; tunnels lead to valves DM, TF, NP
Valve IA has flow rate=0; tunnels lead to valves VS, VO
Valve RF has flow rate=0; tunnels lead to valves VO, JF
Valve RT has flow rate=0; tunnels lead to valves UM, SE
Valve RU has flow rate=0; tunnels lead to valves AR, WC
Valve SE has flow rate=4; tunnels lead to valves GU, KM, CX, RT
Valve MC has flow rate=0; tunnels lead to valves EJ, AR
Valve TF has flow rate=0; tunnels lead to valves AH, VR
Valve CX has flow rate=0; tunnels lead to valves SE, TO
Valve GL has flow rate=11; tunnels lead to valves UY, KL, CY
Valve GU has flow rate=0; tunnels lead to valves SE, EZ
Valve VS has flow rate=0; tunnels lead to valves XN, IA
Valve EZ has flow rate=0; tunnels lead to valves AA, GU
Valve GK has flow rate=0; tunnels lead to valves FI, HZ
Valve JT has flow rate=0; tunnels lead to valves TE, XN
Valve DM has flow rate=0; tunnels lead to valves VR, HZ
Valve AR has flow rate=16; tunnels lead to valves UI, RU, MC
Valve XN has flow rate=9; tunnels lead to valves XP, JT, VS, GT, CY
Valve CY has flow rate=0; tunnels lead to valves XN, GL
Valve QT has flow rate=0; tunnels lead to valves UM, AA
Valve KL has flow rate=0; tunnels lead to valves GL, NP
Valve SK has flow rate=0; tunnels lead to valves XV, UP
Valve OW has flow rate=12; tunnels lead to valves CZ, WC, FP
Valve AK has flow rate=0; tunnels lead to valves AA, HS
Valve XV has flow rate=0; tunnels lead to valves AA, SK
Valve GT has flow rate=0; tunnels lead to valves XN, UM
Valve FI has flow rate=0; tunnels lead to valves JF, GK
Valve UY has flow rate=0; tunnels lead to valves JF, GL
Valve UM has flow rate=5; tunnels lead to valves DV, GT, RT, QT
Valve IQ has flow rate=0; tunnels lead to valves HZ, AH
Valve JF has flow rate=10; tunnels lead to valves RF, FI, UY, RE, TS
Valve TS has flow rate=0; tunnels lead to valves JF, FL
Valve AH has flow rate=23; tunnels lead to valves IQ, FL, TF
Valve HS has flow rate=0; tunnels lead to valves AK, VO
Valve HZ has flow rate=20; tunnels lead to valves IQ, DM, GK
Valve TO has flow rate=15; tunnel leads to valve CX
Valve XP has flow rate=0; tunnels lead to valves AP, XN
Valve AP has flow rate=0; tunnels lead to valves XP, AA
Valve RE has flow rate=0; tunnels lead to valves JF, FP
Valve UI has flow rate=0; tunnels lead to valves AR, CB

111
2022/day17/Makefile Normal file
View File

@@ -0,0 +1,111 @@
# AOC daily Makefile - GNU make only.
#
# Copyright (C) 2021-2023 Bruno Raoult ("br")
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
#
INPUT := input/input.txt
SHELL := /bin/bash
CC := gcc
BEAR := bear
CCLSFILE:= compile_commands.json
LIB := aoc_$(shell uname -m)
INCDIR := ../include
LIBDIR := ../lib
LDFLAGS := -L$(LIBDIR)
#LDLIB := -l$(LIB) -lm
LDLIB := -l$(LIB)
export LD_LIBRARY_PATH = $(LIBDIR)
CFLAGS += -std=gnu11
CFLAGS += -O2
CFLAGS += -g
# for gprof
# CFLAGS += -pg
CFLAGS += -Wall
CFLAGS += -Wextra
CFLAGS += -march=native
# Next one may be useful for valgrind (some invalid instructions)
# CFLAGS += -mno-tbm
CFLAGS += -Wmissing-declarations
CFLAGS += -Wno-unused-result
CFLAGS += -DDEBUG_DEBUG # activate general debug (debug.c)
CFLAGS += -DDEBUG_POOL # memory pools management
VALGRIND := valgrind
VALGRINDFLAGS := --leak-check=full --show-leak-kinds=all --track-origins=yes \
--sigill-diagnostics=yes --quiet --show-error-list=yes
TIME := \time -f "\ttime: %E real, %U user, %S sys\n\tcontext-switch:\t%c+%w, page-faults: %F+%R\n"
export PATH := .:$(PATH)
.PHONY: clean all compile assembly memcheck memcheck1 memcheck2 part1 part2 ccls bear org
all: README.org ccls part1 part2
memcheck: memcheck1 memcheck2
memcheck1: aoc-c
@$(VALGRIND) $(VALGRINDFLAGS) aoc-c -p 1 < $(INPUT)
memcheck2: aoc-c
@$(VALGRIND) $(VALGRINDFLAGS) aoc-c -p 2 < $(INPUT)
@#@valgrind -s --track-origins=yes aoc-c -p 2 < $(INPUT)
compile: aoc-c
cpp: aoc-c.i
assembly: aoc-c.s
part1: aoc-c
@#$(TIME) aoc.bash -p 1 < $(INPUT) 2>&1
@$(TIME) aoc-c -p 1 < $(INPUT)
part2: aoc-c
@#$(TIME) aoc.bash -p 2 < $(INPUT) 2>&1
@$(TIME) aoc-c -p 2 < $(INPUT)
ccls: $(CCLSFILE)
clean:
@rm -f aoc-c core* vgcore* gmon.out aoc-c.s aoc-c.i README.html compile_commands.json
aoc-c: aoc-c.c common.c
@echo compiling $<
$(CC) $(CFLAGS) $(LDFLAGS) -I $(INCDIR) $^ $(LDLIB) -o $@
# generate pre-processed file (.i) and assembler (.s)
%.i: %.c
@echo generating $@
@$(CC) -E $(CFLAGS) -I $(INCDIR) $< -o $@
%.s: %.c
@echo generating $@
@$(CC) -S -fverbose-asm $(CFLAGS) -I $(INCDIR) $< -o $@
# generate README.org from README.html (must cleanup !)
org: README.org
%.org: %.html
@echo generating $@. Cleanup before commit !
@pandoc $< -o $@
# generate compile_commands.json
$(CCLSFILE): aoc-c.c Makefile
$(BEAR) -- make clean compile
bear: clean
@touch .ccls-root
@$(BEAR) -- make compile

374
2022/day17/README.org Normal file
View File

@@ -0,0 +1,374 @@
** --- Day 17: Pyroclastic Flow ---
Your handheld device has located an alternative exit from the cave for
you and the elephants. The ground is rumbling almost continuously now,
but the strange valves bought you some time. It's definitely getting
warmer in here, though.
The tunnels eventually open into a very tall, narrow chamber. Large,
oddly-shaped rocks are falling into the chamber from above, presumably
due to all the rumbling. If you can't work out where the rocks will fall
next, you might be crushed!
The five types of rocks have the following peculiar shapes, where =#= is
rock and =.= is empty space:
#+begin_example
####
.#.
###
.#.
..#
..#
###
#
#
#
#
##
##
#+end_example
The rocks fall in the order shown above: first the =-= shape, then the
=+= shape, and so on. Once the end of the list is reached, the same
order repeats: the =-= shape falls first, sixth, 11th, 16th, etc.
The rocks don't spin, but they do get pushed around by jets of hot gas
coming out of the walls themselves. A quick scan reveals the effect the
jets of hot gas will have on the rocks as they fall (your puzzle input).
For example, suppose this was the jet pattern in your cave:
#+begin_example
>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>
#+end_example
In jet patterns, =<= means a push to the left, while =>= means a push to
the right. The pattern above means that the jets will push a falling
rock right, then right, then right, then left, then left, then right,
and so on. If the end of the list is reached, it repeats.
The tall, vertical chamber is exactly /seven units wide/. Each rock
appears so that its left edge is two units away from the left wall and
its bottom edge is three units above the highest rock in the room (or
the floor, if there isn't one).
After a rock appears, it alternates between /being pushed by a jet of
hot gas/ one unit (in the direction indicated by the next symbol in the
jet pattern) and then /falling one unit down/. If any movement would
cause any part of the rock to move into the walls, floor, or a stopped
rock, the movement instead does not occur. If a /downward/ movement
would have caused a falling rock to move into the floor or an
already-fallen rock, the falling rock stops where it is (having landed
on something) and a new rock immediately begins falling.
Drawing falling rocks with =@= and stopped rocks with =#=, the jet
pattern in the example above manifests as follows:
#+begin_example
The first rock begins falling:
|..@@@@.|
|.......|
|.......|
|.......|
+-------+
Jet of gas pushes rock right:
|...@@@@|
|.......|
|.......|
|.......|
+-------+
Rock falls 1 unit:
|...@@@@|
|.......|
|.......|
+-------+
Jet of gas pushes rock right, but nothing happens:
|...@@@@|
|.......|
|.......|
+-------+
Rock falls 1 unit:
|...@@@@|
|.......|
+-------+
Jet of gas pushes rock right, but nothing happens:
|...@@@@|
|.......|
+-------+
Rock falls 1 unit:
|...@@@@|
+-------+
Jet of gas pushes rock left:
|..@@@@.|
+-------+
Rock falls 1 unit, causing it to come to rest:
|..####.|
+-------+
A new rock begins falling:
|...@...|
|..@@@..|
|...@...|
|.......|
|.......|
|.......|
|..####.|
+-------+
Jet of gas pushes rock left:
|..@....|
|.@@@...|
|..@....|
|.......|
|.......|
|.......|
|..####.|
+-------+
Rock falls 1 unit:
|..@....|
|.@@@...|
|..@....|
|.......|
|.......|
|..####.|
+-------+
Jet of gas pushes rock right:
|...@...|
|..@@@..|
|...@...|
|.......|
|.......|
|..####.|
+-------+
Rock falls 1 unit:
|...@...|
|..@@@..|
|...@...|
|.......|
|..####.|
+-------+
Jet of gas pushes rock left:
|..@....|
|.@@@...|
|..@....|
|.......|
|..####.|
+-------+
Rock falls 1 unit:
|..@....|
|.@@@...|
|..@....|
|..####.|
+-------+
Jet of gas pushes rock right:
|...@...|
|..@@@..|
|...@...|
|..####.|
+-------+
Rock falls 1 unit, causing it to come to rest:
|...#...|
|..###..|
|...#...|
|..####.|
+-------+
A new rock begins falling:
|....@..|
|....@..|
|..@@@..|
|.......|
|.......|
|.......|
|...#...|
|..###..|
|...#...|
|..####.|
+-------+
#+end_example
The moment each of the next few rocks begins falling, you would see
this:
#+begin_example
|..@....|
|..@....|
|..@....|
|..@....|
|.......|
|.......|
|.......|
|..#....|
|..#....|
|####...|
|..###..|
|...#...|
|..####.|
+-------+
|..@@...|
|..@@...|
|.......|
|.......|
|.......|
|....#..|
|..#.#..|
|..#.#..|
|#####..|
|..###..|
|...#...|
|..####.|
+-------+
|..@@@@.|
|.......|
|.......|
|.......|
|....##.|
|....##.|
|....#..|
|..#.#..|
|..#.#..|
|#####..|
|..###..|
|...#...|
|..####.|
+-------+
|...@...|
|..@@@..|
|...@...|
|.......|
|.......|
|.......|
|.####..|
|....##.|
|....##.|
|....#..|
|..#.#..|
|..#.#..|
|#####..|
|..###..|
|...#...|
|..####.|
+-------+
|....@..|
|....@..|
|..@@@..|
|.......|
|.......|
|.......|
|..#....|
|.###...|
|..#....|
|.####..|
|....##.|
|....##.|
|....#..|
|..#.#..|
|..#.#..|
|#####..|
|..###..|
|...#...|
|..####.|
+-------+
|..@....|
|..@....|
|..@....|
|..@....|
|.......|
|.......|
|.......|
|.....#.|
|.....#.|
|..####.|
|.###...|
|..#....|
|.####..|
|....##.|
|....##.|
|....#..|
|..#.#..|
|..#.#..|
|#####..|
|..###..|
|...#...|
|..####.|
+-------+
|..@@...|
|..@@...|
|.......|
|.......|
|.......|
|....#..|
|....#..|
|....##.|
|....##.|
|..####.|
|.###...|
|..#....|
|.####..|
|....##.|
|....##.|
|....#..|
|..#.#..|
|..#.#..|
|#####..|
|..###..|
|...#...|
|..####.|
+-------+
|..@@@@.|
|.......|
|.......|
|.......|
|....#..|
|....#..|
|....##.|
|##..##.|
|######.|
|.###...|
|..#....|
|.####..|
|....##.|
|....##.|
|....#..|
|..#.#..|
|..#.#..|
|#####..|
|..###..|
|...#...|
|..####.|
+-------+
#+end_example
To prove to the elephants your simulation is accurate, they want to know
how tall the tower will get after 2022 rocks have stopped (but before
the 2023rd rock begins falling). In this example, the tower of rocks
will be =3068= units tall.
/How many units tall will the tower of rocks be after 2022 rocks have
stopped falling?/

17
2022/day17/aoc.h Normal file
View File

@@ -0,0 +1,17 @@
/* aoc.c: Advent of Code 2022
*
* Copyright (C) 2022-2023 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*/
#ifndef _AOC_H_
#define _AOC_H_
extern int parseargs(int ac, char**av);
extern int testmode(void);
#endif /* _AOC_H_ */

68
2022/day17/common.bash Normal file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
#
# common.bash: Advent of Code 2022, common bash functions
#
# Copyright (C) 2022-2023 Bruno Raoult ("br")
# Licensed under the GNU General Public License v3.0 or later.
# Some rights reserved. See COPYING.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
#
# SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
# shellcheck disable=2034
export cmdname=${0##*/}
export debug=0
export res
export LANG=C
shopt -s extglob
set -o noglob
usage() {
printf "usage: %s [-d DEBUG] [-p PART]\n" "$cmdname"
exit 1
}
checkargs() {
local part=1
while getopts p:d: todo; do
case "$todo" in
d)
if [[ "$OPTARG" =~ ^[[:digit:]+]$ ]]; then
debug="$OPTARG"
else
printf "%s: illegal [%s] debug level.\n" "$CMD" "$OPTARG"
exit 1
fi
;;
p)
if [[ "$OPTARG" =~ ^[12]$ ]]; then
part="$OPTARG"
else
printf "%s: illegal [%s] part.\n" "$CMD" "$OPTARG"
exit 1
fi
;;
*)
usage
;;
esac
done
# Now check remaining argument (backup directory)
shift $((OPTIND - 1))
(( $# > 1 )) && usage
return "$part"
}
main() {
local -i part
checkargs "$@"
part=$?
parse "$part"
solve "$part"
printf "%s: res=%s\n" "$cmdname" "$res"
}

59
2022/day17/common.c Normal file
View File

@@ -0,0 +1,59 @@
/* common.c: Advent of Code 2022, common functions
*
* Copyright (C) 2022-2023 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "aoc.h"
#include "debug.h"
static int _testmode = 0;
static int usage(char *prg)
{
fprintf(stderr, "Usage: %s [-t][-d debug_level] [-p part] [-i input]\n", prg);
return 1;
}
int testmode(void)
{
return _testmode;
}
int parseargs(int ac, char **av)
{
int opt, part = 1;
while ((opt = getopt(ac, av, "td:p:")) != -1) {
switch (opt) {
case 't':
_testmode = 1;
break;
case 'd':
debug_level_set(atoi(optarg));
break;
case 'p': /* 1 or 2 */
part = atoi(optarg);
if (part < 1 || part > 2)
return usage(*av);
break;
case 'i':
default:
return usage(*av);
}
}
if (optind < ac)
return usage(*av);
return part;
}

View File

@@ -0,0 +1 @@
>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>

File diff suppressed because one or more lines are too long

View File

@@ -25,6 +25,7 @@
#ifdef DEBUG_DEBUG #ifdef DEBUG_DEBUG
void debug_init(u32 level); void debug_init(u32 level);
void debug_level_set(u32 level); void debug_level_set(u32 level);
u32 debug_level_get(void);
void _printf debug(u32 level, bool timestamp, void _printf debug(u32 level, bool timestamp,
u32 indent, const char *src, u32 indent, const char *src,
u32 line, const char *, ...); u32 line, const char *, ...);

View File

@@ -35,6 +35,11 @@ void debug_level_set(u32 level)
log(1, "debug level set to %u\n", level); log(1, "debug level set to %u\n", level);
} }
u32 debug_level_get()
{
return debug_level;
}
void debug_init(u32 level) void debug_init(u32 level)
{ {
struct timespec timer; struct timespec timer;