From 828d13f9678b51ab522d59b80893b8db1d161bc0 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Mon, 19 Sep 2022 19:35:37 +0200 Subject: [PATCH] 2019 day 3 + add br.h for some useful stuff (min, max... macros) --- 2019/day03/EXAMPLE.txt | 4 +- 2019/day03/EXAMPLE2.txt | 4 +- 2019/day03/EXAMPLE3.txt | 2 + 2019/day03/Makefile | 20 +++- 2019/day03/README.org | 59 ++++++++++- 2019/day03/aoc-c.c | 224 ++++++++++++++++++++++++++++++++++++++++ 2019/include/br.h | 203 ++++++++++++++++++++++++++++++++++++ 7 files changed, 502 insertions(+), 14 deletions(-) create mode 100644 2019/day03/EXAMPLE3.txt create mode 100644 2019/day03/aoc-c.c create mode 100644 2019/include/br.h diff --git a/2019/day03/EXAMPLE.txt b/2019/day03/EXAMPLE.txt index 620a05e..73b95a1 100644 --- a/2019/day03/EXAMPLE.txt +++ b/2019/day03/EXAMPLE.txt @@ -1,2 +1,2 @@ -R75,D30,R83,U83,L12,D49,R71,U7,L72 -U62,R66,U55,R34,D71,R55,D58,R83 +R8,U5,L5,D3 +U7,R6,D4,L4 diff --git a/2019/day03/EXAMPLE2.txt b/2019/day03/EXAMPLE2.txt index 4f3a2a4..620a05e 100644 --- a/2019/day03/EXAMPLE2.txt +++ b/2019/day03/EXAMPLE2.txt @@ -1,2 +1,2 @@ -R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51 -U98,R91,D20,R16,D67,R40,U7,R15,U6,R7 +R75,D30,R83,U83,L12,D49,R71,U7,L72 +U62,R66,U55,R34,D71,R55,D58,R83 diff --git a/2019/day03/EXAMPLE3.txt b/2019/day03/EXAMPLE3.txt new file mode 100644 index 0000000..4f3a2a4 --- /dev/null +++ b/2019/day03/EXAMPLE3.txt @@ -0,0 +1,2 @@ +R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51 +U98,R91,D20,R16,D67,R40,U7,R15,U6,R7 diff --git a/2019/day03/Makefile b/2019/day03/Makefile index 04f5b05..de8b34b 100644 --- a/2019/day03/Makefile +++ b/2019/day03/Makefile @@ -40,6 +40,10 @@ CFLAGS += -Wno-unused-result CFLAGS += -DDEBUG_DEBUG # activate general debug (debug.c) CFLAGS += -DDEBUG_POOL # memory pools management +VALGRIND := valgrind +VALGRINDFLAGS := -q -s --leak-check=full --show-leak-kinds=all --track-origins=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) @@ -49,11 +53,12 @@ all: ex1 ex2 memcheck: memcheck1 -memcheck1: - @valgrind -q -s --track-origins=yes aoc-c -p 1 < $(INPUT) +memcheck1: aoc-c + @$(VALGRIND) $(VALGRINDFLAGS) aoc-c -p 1 < $(INPUT) -memcheck2: - @valgrind -q -s --track-origins=yes aoc-c -p 2 < $(INPUT) +memcheck2: aoc-c + @$(VALGRIND) $(VALGRINDFLAGS) aoc-c -p 2 < $(INPUT) + @#@valgrind -s --track-origins=yes aoc-c -p 2 < $(INPUT) compile: aoc-c @@ -72,6 +77,11 @@ clean: @echo compiling $< @$(CC) $(CFLAGS) $(LDFLAGS) -I $(INCDIR) $< $(LDLIB) -o $@ -.c.s: +# 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 $@ diff --git a/2019/day03/README.org b/2019/day03/README.org index 37a10ca..3457ad2 100644 --- a/2019/day03/README.org +++ b/2019/day03/README.org @@ -55,12 +55,61 @@ is closer to the central port: its distance is =3 + 3 = 6=. Here are a few more examples: -- =R75,D30,R83,U83,L12,D49,R71,U7,L72= - =U62,R66,U55,R34,D71,R55,D58,R83= =distance =159= -- =R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51= - =U98,R91,D20,R16,D67,R40,U7,R15,U6,R7= =distance =135= +- =R75,D30,R83,U83,L12,D49,R71,U7,L72U62,R66,U55,R34,D71,R55,D58,R83= = + distance =159= +- =R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51U98,R91,D20,R16,D67,R40,U7,R15,U6,R7= + = distance =135= /What is the Manhattan distance/ from the central port to the closest intersection? -To begin, [[file:3/input][get your puzzle input]]. +Your puzzle answer was =860=. + +** --- Part Two --- +It turns out that this circuit is very timing-sensitive; you actually +need to /minimize the signal delay/. + +To do this, calculate the /number of steps/ each wire takes to reach +each intersection; choose the intersection where the /sum of both wires' +steps/ is lowest. If a wire visits a position on the grid multiple +times, use the steps value from the /first/ time it visits that position +when calculating the total value of a specific intersection. + +The number of steps a wire takes is the total number of grid squares the +wire has entered to get to that location, including the intersection +being considered. Again consider the example from above: + +#+BEGIN_EXAMPLE + ........... + .+-----+... + .|.....|... + .|..+--X-+. + .|..|..|.|. + .|.-X--+.|. + .|..|....|. + .|.......|. + .o-------+. + ........... +#+END_EXAMPLE + +In the above example, the intersection closest to the central port is +reached after =8+5+5+2 = 20= steps by the first wire and =7+6+4+3 = 20= +steps by the second wire for a total of =20+20 = 40= steps. + +However, the top-right intersection is better: the first wire takes only +=8+5+2 = 15= and the second wire takes only =7+6+2 = 15=, a total of +=15+15 = 30= steps. + +Here are the best steps for the extra examples from above: + +- =R75,D30,R83,U83,L12,D49,R71,U7,L72U62,R66,U55,R34,D71,R55,D58,R83= = + =610= steps +- =R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51U98,R91,D20,R16,D67,R40,U7,R15,U6,R7= + = =410= steps + +/What is the fewest combined steps the wires must take to reach an +intersection?/ + +Your puzzle answer was =9238=. + +Both parts of this puzzle are complete! They provide two gold stars: ** diff --git a/2019/day03/aoc-c.c b/2019/day03/aoc-c.c new file mode 100644 index 0000000..cb4bb97 --- /dev/null +++ b/2019/day03/aoc-c.c @@ -0,0 +1,224 @@ +/* aoc-c.c: Advent of Code 2019, day 3 parts 1 & 2 + * + * Copyright (C) 2021 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include +#include +#include + +#include "br.h" +#include "bits.h" +#include "debug.h" +#include "list.h" +#include "pool.h" +#include "debug.h" + +typedef enum { DOWN = 'D', LEFT = 'L', RIGHT = 'R', UP = 'U' } dir_t; + +struct point { + int x, y; +}; + +struct wire { + int dist; /* total distance before this wire */ + struct point delta; /* current delta */ + struct point p0, p1; + struct list_head list; /* wire list */ +}; + +static struct wires { + int nwires[2]; + struct list_head head[2]; +} wires[2]; + +static pool_t *wire_pool; + +static struct wires *parse() +{ + size_t alloc = 0; + ssize_t buflen; + char *buf = NULL, *token; + + /* initialize wires lists */ + INIT_LIST_HEAD(&wires->head[0]); + INIT_LIST_HEAD(&wires->head[1]); + + for (int line = 0; line < 2; ++line) { + int x0 = 0, y0 = 0; + int x1 = 0, y1 = 0; + int totdist = 0; + + if ((buflen = getline(&buf, &alloc, stdin)) <= 0) { + fprintf(stderr, "error %d reading file.\n", errno); + goto end; + } + + for (token = strtok(buf, ","); token; token = strtok(NULL, ",")) { + dir_t dir = *token; + int dist = atoi(token + 1); + struct wire *new = pool_get(wire_pool); + INIT_LIST_HEAD(&new->list); + new->dist = totdist; + new->delta.x = 0; + new->delta.y = 0; + totdist += abs(dist); + switch(dir) { + case DOWN: + y1 = y0 - dist; + new->delta.y = -dist; + break; + case UP: + y1 = y0 + dist; + new->delta.y = dist; + break; + case LEFT: + x1 = x0 - dist; + new->delta.x = -dist; + break; + case RIGHT: + x1 = x0 + dist; + new->delta.x = dist; + } + new->p0.x = min(x0, x1); + new->p0.y = min(y0, y1); + new->p1.x = max(x0, x1); + new->p1.y = max(y0, y1); + list_add_tail(&new->list, &wires->head[line]); + x0 = x1; + y0 = y1; + wires->nwires[line]++; + } + } +end: + free(buf); + return wires; +} + +#define manhattan(x, y) (abs(x) + abs(y)) + +static struct point *intersect(struct wire *w1, struct wire *w2, struct point *ret) +{ + log_f(3, "(%d,%d)-(%d,%d) (%d,%d)-(%d,%d): ", + w1->p0.x, w1->p0.y, w1->p1.x, w1->p1.y, + w2->p0.x, w2->p0.y, w2->p1.x, w2->p1.y); + if (w1->p0.x == w1->p1.x) { /* w1 vertical */ + /* TODO: overlapping wires (multiple intersections) */ + if (w1->p0.x >= w2->p0.x && w1->p1.x <= w2->p1.x && + w1->p0.y <= w2->p0.y && w1->p1.y >= w2->p1.y) { + log(3, "intersect 1 at (%d, %d)\n", w1->p0.x, w2->p0.y); + ret->x = w1->p0.x; + ret->y = w2->p0.y; + return ret; + //return manhatan(w1->p0.x, w2->p0.y); + } + log(3, "no intersection\n"); + return NULL; + } else { /* w1 horizontal */ + if (w1->p0.x <= w2->p0.x && w1->p1.x >= w2->p1.x && + w1->p0.y <= w2->p1.y && w1->p1.y >= w2->p0.y) { + log(3, "intersect 2 at (%d, %d)\n", w2->p0.x, w1->p0.y); + ret->x = w2->p0.x; + ret->y = w1->p0.y; + return ret; + //return manhatan(w2->p0.x, w1->p0.y); + } + log(3, "no intersection\n"); + return NULL; + } +} + +static s32 part1(struct wires *w) +{ + struct wire *w0, *w1; + struct point cross; + s32 res = INT32_MAX; + + list_for_each_entry(w0, &w->head[0], list) { + list_for_each_entry(w1, &w->head[1], list) { + if (intersect(w0, w1, &cross)) { + int tmp = manhattan(cross.x, cross.y); + log(3, "new manhattan: %d\n", tmp); + res = min(tmp, res); + } + } + } + return res; +} + +static int part2(struct wires *w) +{ + struct wire *w0, *w1; + struct point cross; + s32 res = INT32_MAX; + + list_for_each_entry(w0, &w->head[0], list) { + list_for_each_entry(w1, &w->head[1], list) { + if (intersect(w0, w1, &cross)) { + if (cross.x || cross.y) { + int tmp = w0->dist + w1->dist; + + /* as w->p0 contains the lowest value of (x, y), we lost the + * starting point information. We adjust remaining steps with + * the delta sign, which indicates if we swapped p0 and p1. + */ + tmp += w0->delta.x + w0->delta.y > 0 ? + abs(cross.x - w0->p0.x) + abs(cross.y - w0->p0.y) : + abs(cross.x - w0->p1.x) + abs(cross.y - w0->p1.y); + tmp += w1->delta.x + w1->delta.y > 0 ? + abs(cross.x - w1->p0.x) + abs(cross.y - w1->p0.y) : + abs(cross.x - w1->p1.x) + abs(cross.y - w1->p1.y); + /* new best */ + res = min(tmp, res); + } + } + } + } + return res; +} + +static int usage(char *prg) +{ + fprintf(stderr, "Usage: %s [-d debug_level] [-p part]\n", prg); + return 1; +} + +int main(int ac, char **av) +{ + int opt, part = 1; + struct wires *wires; + + while ((opt = getopt(ac, av, "d:p:")) != -1) { + switch (opt) { + 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; + default: + return usage(*av); + } + } + + if (optind < ac) + return usage(*av); + wire_pool = pool_create("wire", 256, sizeof(struct wire)); + + wires = parse(); + printf("%s : res=%d\n", *av, part == 1? part1(wires): part2(wires)); + pool_destroy(wire_pool); + exit (0); +} diff --git a/2019/include/br.h b/2019/include/br.h new file mode 100644 index 0000000..ea346f9 --- /dev/null +++ b/2019/include/br.h @@ -0,0 +1,203 @@ +/* bits.h - bits functions. + * + * Copyright (C) 2021-2022 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Some parts are taken from Linux's kernel and others, and are : + * SPDX-License-Identifier: GPL-2.0 + * + * This header contains generic stuff. + */ + +#ifndef _BR_H +#define _BR_H + +/* generate a (maybe) unique id. + */ +#define ___PASTE(x, y) x##y +#define __PASTE(x, y) ___PASTE(x, y) +#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) +//__##prefix##__COUNTER__ + +/* see https://lkml.org/lkml/2018/3/20/845 for explanation of this monster + */ +#define __is_constexpr(x) \ + (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) + +/* + * min()/max()/clamp() macros must accomplish three things: + * + * - avoid multiple evaluations of the arguments (so side-effects like + * "x++" happen only once) when non-constant. + * - perform strict type-checking (to generate warnings instead of + * nasty runtime surprises). See the "unnecessary" pointer comparison + * in __typecheck(). + * - retain result as a constant expressions when called with only + * constant expressions (to avoid tripping VLA warnings in stack + * allocation usage). + */ +#define __typecheck(x, y) \ + (!!(sizeof((typeof(x) *)1 == (typeof(y) *)1))) + +#define __no_side_effects(x, y) \ + (__is_constexpr(x) && __is_constexpr(y)) + +#define __safe_cmp(x, y) \ + (__typecheck(x, y) && __no_side_effects(x, y)) + +#define __cmp(x, y, op) ((x) op (y) ? (x) : (y)) + +#define __cmp_once(x, y, unique_x, unique_y, op) ({ \ + typeof(x) unique_x = (x); \ + typeof(y) unique_y = (y); \ + __cmp(unique_x, unique_y, op); }) + +#define __careful_cmp(x, y, op) \ + __builtin_choose_expr(__safe_cmp(x, y), \ + __cmp(x, y, op), \ + __cmp_once(x, y, __UNIQUE_ID(__x), __UNIQUE_ID(__y), op)) + +/** + * min - return minimum of two values of the same or compatible types + * @x: first value + * @y: second value + */ +#define min(x, y) __careful_cmp(x, y, <) + +/** + * max - return maximum of two values of the same or compatible types + * @x: first value + * @y: second value + */ +#define max(x, y) __careful_cmp(x, y, >) + +/** + * min3 - return minimum of three values + * @x: first value + * @y: second value + * @z: third value + */ +#define min3(x, y, z) min((typeof(x))min(x, y), z) + +/** + * max3 - return maximum of three values + * @x: first value + * @y: second value + * @z: third value + */ +#define max3(x, y, z) max((typeof(x))max(x, y), z) + +/** + * min_not_zero - return the minimum that is _not_ zero, unless both are zero + * @x: value1 + * @y: value2 + */ +#define min_not_zero(x, y) ({ \ + typeof(x) __x = (x); \ + typeof(y) __y = (y); \ + __x == 0 ? __y : ((__y == 0) ? __x : min(__x, __y)); }) + +/** + * clamp - return a value clamped to a given range with strict typechecking + * @val: current value + * @lo: lowest allowable value + * @hi: highest allowable value + * + * This macro does strict typechecking of @lo/@hi to make sure they are of the + * same type as @val. See the unnecessary pointer comparisons. + */ +#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi) + +/* + * ..and if you can't take the strict + * types, you can specify one yourself. + * + * Or not use min/max/clamp at all, of course. + */ + +/** + * min_t - return minimum of two values, using the specified type + * @type: data type to use + * @x: first value + * @y: second value + */ +#define min_t(type, x, y) __careful_cmp((type)(x), (type)(y), <) + +/** + * max_t - return maximum of two values, using the specified type + * @type: data type to use + * @x: first value + * @y: second value + */ +#define max_t(type, x, y) __careful_cmp((type)(x), (type)(y), >) + +/** + * clamp_t - return a value clamped to a given range using a given type + * @type: the type of variable to use + * @val: current value + * @lo: minimum allowable value + * @hi: maximum allowable value + * + * This macro does no typechecking and uses temporary variables of type + * @type to make all the comparisons. + */ +#define clamp_t(type, val, lo, hi) min_t(type, max_t(type, val, lo), hi) + +/** + * clamp_val - return a value clamped to a given range using val's type + * @val: current value + * @lo: minimum allowable value + * @hi: maximum allowable value + * + * This macro does no typechecking and uses temporary variables of whatever + * type the input argument @val is. This is useful when @val is an unsigned + * type and @lo and @hi are literals that will otherwise be assigned a signed + * integer type. + */ +#define clamp_val(val, lo, hi) clamp_t(typeof(val), val, lo, hi) + +/** + * swap - swap values of @a and @b + * @a: first value + * @b: second value + */ +#define swap(a, b) \ + do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) + +/** + * ARRAY_SIZE - get the number of elements in array @arr + * @arr: array to be sized + */ +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +/** + * abs - return absolute value of an argument + * @x: the value. If it is unsigned type, it is converted to signed type first. + * char is treated as if it was signed (regardless of whether it really is) + * but the macro's return type is preserved as char. + * + * Return: an absolute value of x. + */ +#define abs(x) __abs_choose_expr(x, long long, \ + __abs_choose_expr(x, long, \ + __abs_choose_expr(x, int, \ + __abs_choose_expr(x, short, \ + __abs_choose_expr(x, char, \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(x), char), \ + (char)({ signed char __x = (x); __x<0?-__x:__x; }), \ + ((void)0))))))) + +#define __abs_choose_expr(x, type, other) __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(x), signed type) || \ + __builtin_types_compatible_p(typeof(x), unsigned type), \ + ({ signed type __x = (x); __x < 0 ? -__x : __x; }), other) + + +#endif /* _BR_H */