From 2ed6284bcd94491dfd099c64636920f941ab03f2 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Sat, 18 Mar 2023 17:50:59 +0100 Subject: [PATCH] 2022 day 15 init --- 2022/day15/Makefile | 111 ++++++++++++++++++++ 2022/day15/README.org | 139 +++++++++++++++++++++++++ 2022/day15/aoc-c.c | 194 +++++++++++++++++++++++++++++++++++ 2022/day15/aoc.h | 17 +++ 2022/day15/common.bash | 68 ++++++++++++ 2022/day15/common.c | 49 +++++++++ 2022/day15/input/example.txt | 14 +++ 2022/day15/input/input.txt | 26 +++++ README.md | 1 + 9 files changed, 619 insertions(+) create mode 100644 2022/day15/Makefile create mode 100644 2022/day15/README.org create mode 100644 2022/day15/aoc-c.c create mode 100644 2022/day15/aoc.h create mode 100644 2022/day15/common.bash create mode 100644 2022/day15/common.c create mode 100644 2022/day15/input/example.txt create mode 100644 2022/day15/input/input.txt diff --git a/2022/day15/Makefile b/2022/day15/Makefile new file mode 100644 index 0000000..9091528 --- /dev/null +++ b/2022/day15/Makefile @@ -0,0 +1,111 @@ +# AOC daily Makefile - GNU make only. +# +# 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 +# + +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 diff --git a/2022/day15/README.org b/2022/day15/README.org new file mode 100644 index 0000000..36a3171 --- /dev/null +++ b/2022/day15/README.org @@ -0,0 +1,139 @@ +** --- Day 15: Beacon Exclusion Zone --- +You feel the ground rumble again as the distress signal leads you to a +large network of subterranean tunnels. You don't have time to search +them all, but you don't need to: your pack contains a set of deployable +/sensors/ that you imagine were originally built to locate lost Elves. + +The sensors aren't very powerful, but that's okay; your handheld device +indicates that you're close enough to the source of the distress signal +to use them. You pull the emergency sensor system out of your pack, hit +the big button on top, and the sensors zoom off down the tunnels. + +Once a sensor finds a spot it thinks will give it a good reading, it +attaches itself to a hard surface and begins monitoring for the nearest +signal source /beacon/. Sensors and beacons always exist at integer +coordinates. Each sensor knows its own position and can /determine the +position of a beacon precisely/; however, sensors can only lock on to +the one beacon /closest to the sensor/ as measured by the +[[https://en.wikipedia.org/wiki/Taxicab_geometry][Manhattan distance]]. +(There is never a tie where two beacons are the same distance to a +sensor.) + +It doesn't take long for the sensors to report back their positions and +closest beacons (your puzzle input). For example: + +#+begin_example +Sensor at x=2, y=18: closest beacon is at x=-2, y=15 +Sensor at x=9, y=16: closest beacon is at x=10, y=16 +Sensor at x=13, y=2: closest beacon is at x=15, y=3 +Sensor at x=12, y=14: closest beacon is at x=10, y=16 +Sensor at x=10, y=20: closest beacon is at x=10, y=16 +Sensor at x=14, y=17: closest beacon is at x=10, y=16 +Sensor at x=8, y=7: closest beacon is at x=2, y=10 +Sensor at x=2, y=0: closest beacon is at x=2, y=10 +Sensor at x=0, y=11: closest beacon is at x=2, y=10 +Sensor at x=20, y=14: closest beacon is at x=25, y=17 +Sensor at x=17, y=20: closest beacon is at x=21, y=22 +Sensor at x=16, y=7: closest beacon is at x=15, y=3 +Sensor at x=14, y=3: closest beacon is at x=15, y=3 +Sensor at x=20, y=1: closest beacon is at x=15, y=3 +#+end_example + +So, consider the sensor at =2,18=; the closest beacon to it is at +=-2,15=. For the sensor at =9,16=, the closest beacon to it is at +=10,16=. + +Drawing sensors as =S= and beacons as =B=, the above arrangement of +sensors and beacons looks like this: + +#+begin_example + 1 1 2 2 + 0 5 0 5 0 5 + 0 ....S....................... + 1 ......................S..... + 2 ...............S............ + 3 ................SB.......... + 4 ............................ + 5 ............................ + 6 ............................ + 7 ..........S.......S......... + 8 ............................ + 9 ............................ +10 ....B....................... +11 ..S......................... +12 ............................ +13 ............................ +14 ..............S.......S..... +15 B........................... +16 ...........SB............... +17 ................S..........B +18 ....S....................... +19 ............................ +20 ............S......S........ +21 ............................ +22 .......................B.... +#+end_example + +This isn't necessarily a comprehensive map of all beacons in the area, +though. Because each sensor only identifies its closest beacon, if a +sensor detects a beacon, you know there are no other beacons that close +or closer to that sensor. There could still be beacons that just happen +to not be the closest beacon to any sensor. Consider the sensor at +=8,7=: + +#+begin_example + 1 1 2 2 + 0 5 0 5 0 5 +-2 ..........#................. +-1 .........###................ + 0 ....S...#####............... + 1 .......#######........S..... + 2 ......#########S............ + 3 .....###########SB.......... + 4 ....#############........... + 5 ...###############.......... + 6 ..#################......... + 7 .#########S#######S#........ + 8 ..#################......... + 9 ...###############.......... +10 ....B############........... +11 ..S..###########............ +12 ......#########............. +13 .......#######.............. +14 ........#####.S.......S..... +15 B........###................ +16 ..........#SB............... +17 ................S..........B +18 ....S....................... +19 ............................ +20 ............S......S........ +21 ............................ +22 .......................B.... +#+end_example + +This sensor's closest beacon is at =2,10=, and so you know there are no +beacons that close or closer (in any positions marked =#=). + +None of the detected beacons seem to be producing the distress signal, +so you'll need to work out where the distress beacon is by working out +where it /isn't/. For now, keep things simple by counting the positions +where a beacon cannot possibly be along just a single row. + +So, suppose you have an arrangement of beacons and sensors like in the +example above and, just in the row where =y=10=, you'd like to count the +number of positions a beacon cannot possibly exist. The coverage from +all sensors near that row looks like this: + +#+begin_example + 1 1 2 2 + 0 5 0 5 0 5 + 9 ...#########################... +10 ..####B######################.. +11 .###S#############.###########. +#+end_example + +In this example, in the row where =y=10=, there are =26= positions where +a beacon cannot be present. + +Consult the report from the sensors you just deployed. /In the row where +=y=2000000=, how many positions cannot contain a beacon?/ diff --git a/2022/day15/aoc-c.c b/2022/day15/aoc-c.c new file mode 100644 index 0000000..b610f96 --- /dev/null +++ b/2022/day15/aoc-c.c @@ -0,0 +1,194 @@ +/* aoc-c.c: Advent of Code 2022, day 15 + * + * Copyright (C) 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 + */ + +#include +#include +#include +#include +#include +#include + +#include "br.h" +#include "list.h" +#include "pool.h" +#include "debug.h" + +#include "aoc.h" + +pool_t *pool_segment; + +#define INF (-1) /* found infinite fall position */ + +typedef enum { /* map cells */ + EMPTY = '.', + STONE = '#', + SAND = 'o', +} type_t; + +typedef struct segment { + int x1, y1, x2, y2; + struct list_head list; +} segment_t; + +LIST_HEAD(segments); + +typedef struct map { + int xmin, xmax, ymin, ymax; + int size_x, size_y; + int deltax, deltay; + int dropcol; /* drop sand here */ + int dropped; /* number of sand units */ + char *m; +} map_t; + +#define XY(m, x, y) ((y) * m->size_x + (x)) +#define P(m, x, y) (m->m[XY(m, (x), (y))]) + +static int drop_sand(struct map *m, int x, int y) +{ + int ret = 0, tmp; + + if (y >= m->ymax) /* part 1: nothing left under */ + return INF; + + if (P(m, x, y+1) == EMPTY) { /* down */ + if ((tmp = drop_sand(m, x, y+1)) < 0) + return INF; + ret += tmp; + } + if (P(m, x-1, y+1) == EMPTY) { /* left */ + if ((tmp = drop_sand(m, x-1, y+1)) < 0) + return INF; + ret += tmp; + } + if (P(m, x+1, y+1) == EMPTY) { /* right */ + if ((tmp = drop_sand(m, x+1, y+1)) < 0) + return INF; + ret += tmp; + } + /* the 3 lower adjacent cells are filled */ + P(m, x, y) = SAND; + m->dropped++; + if (y == 0) /* part 2 */ + return INF; + return ret; +} + +static struct map *gen_map(struct map *m, int part) +{ + segment_t *cur; + + if (part == 1) { + m->size_x = (m->xmax - m->xmin) + 3; + m->size_y = m->ymax + 1; + m->deltax = m->xmin - 1; + } else { + m->ymax += 2; + m->xmin = 500 - m->ymax - 1; + m->xmax = 500 + m->ymax + 1; + m->deltax = m->xmin; + m->size_x = (m->xmax - m->xmin); + m->size_y = m->ymax + 3; + } + m->dropcol = 500 - m->deltax; + m->dropped = 0; + + m->m = malloc(m->size_x * m->size_y); + memset(m->m, '.', m->size_x * m->size_y); + list_for_each_entry(cur, &segments, list) { + int x1 = cur->x1 - m->deltax, y1 = cur->y1 - m->deltay, + x2 = cur->x2 - m->deltax, y2 = cur->y2 - m->deltay; + int dx = 0, dy = 0; + if (x1 == x2) + dy = y2 - y1 > 0 ? 1 : -1; + else + dx = x2 - x1 > 0 ? 1 : -1; + do { + P(m, x1, y1) = '#'; + x1 += dx, y1 += dy; + } while (x1 != x2 || y1 != y2); + P(m, x2, y2) = '#'; + } + if (part == 2) + for (int i = 0; i < m->size_x; ++i) + P(m, m->xmin - m->deltax + i, m->ymax) = STONE; + return m; +} + +static struct map *parse(map_t *m) +{ + size_t alloc = 0; + ssize_t buflen; + char *buf = NULL, *cur; + int i, scanned; + int x1, y1, x2, y2, x, y, n; + segment_t *segment; + + while ((buflen = getline(&buf, &alloc, stdin)) > 0) { + i = 0; + buf[--buflen] = 0; + cur = buf; + while (1) { + scanned = sscanf(cur, "%d,%d ->%n", &x, &y, &n); + if (scanned != 2) + break; + m->xmin = min(x, m->xmin); + m->xmax = max(x, m->xmax); + m->ymin = min(y, m->ymin); + m->ymax = max(y, m->ymax); + if (i) { + x1 = x2; + y1 = y2; + } + x2 = x; + y2 = y; + if (!i) /* first point */ + goto next; + segment = pool_get(pool_segment); + segment->x1 = x1; + segment->y1 = y1; + segment->x2 = x2; + segment->y2 = y2; + list_add_tail(&segment->list, &segments); + next: + i++; + cur += n; + if (cur - buf >= buflen) + break; + } + } + free(buf); + return m; +} + +static int doit(map_t *m, int part) +{ + m = gen_map(parse(m), part); + drop_sand(m, m->dropcol, 0); + return m->dropped; +} + + +int main(int ac, char **av) +{ + int part = parseargs(ac, av); + static map_t m; + m.xmin = INT_MAX; m.xmax = INT_MIN; + m.ymin = INT_MAX, m.ymax = INT_MIN; + + pool_segment = pool_create("segment", 512, sizeof(segment_t)); + + printf("%s: res=%d\n", *av, doit(&m, part)); + free(m.m); + pool_destroy(pool_segment); + exit(0); +} diff --git a/2022/day15/aoc.h b/2022/day15/aoc.h new file mode 100644 index 0000000..2ef8361 --- /dev/null +++ b/2022/day15/aoc.h @@ -0,0 +1,17 @@ +/* aoc.c: Advent of Code 2022 + * + * Copyright (C) 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 + */ +#ifndef _AOC_H_ +#define _AOC_H_ + +extern int parseargs(int ac, char**av); + +#endif /* _AOC_H_ */ diff --git a/2022/day15/common.bash b/2022/day15/common.bash new file mode 100644 index 0000000..5af1e54 --- /dev/null +++ b/2022/day15/common.bash @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# common.bash: Advent of Code 2022, common bash functions +# +# Copyright (C) 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 + +# 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" +} diff --git a/2022/day15/common.c b/2022/day15/common.c new file mode 100644 index 0000000..a3827b6 --- /dev/null +++ b/2022/day15/common.c @@ -0,0 +1,49 @@ +/* common.c: Advent of Code 2022, common functions + * + * Copyright (C) 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 + */ + +#include +#include +#include + +#include "aoc.h" +#include "debug.h" + +static int usage(char *prg) +{ + fprintf(stderr, "Usage: %s [-d debug_level] [-p part] [-i input]\n", prg); + return 1; +} + +int parseargs(int ac, char **av) +{ + int opt, part = 1; + + 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; + case 'i': + + default: + return usage(*av); + } + } + if (optind < ac) + return usage(*av); + return part; +} diff --git a/2022/day15/input/example.txt b/2022/day15/input/example.txt new file mode 100644 index 0000000..a612424 --- /dev/null +++ b/2022/day15/input/example.txt @@ -0,0 +1,14 @@ +Sensor at x=2, y=18: closest beacon is at x=-2, y=15 +Sensor at x=9, y=16: closest beacon is at x=10, y=16 +Sensor at x=13, y=2: closest beacon is at x=15, y=3 +Sensor at x=12, y=14: closest beacon is at x=10, y=16 +Sensor at x=10, y=20: closest beacon is at x=10, y=16 +Sensor at x=14, y=17: closest beacon is at x=10, y=16 +Sensor at x=8, y=7: closest beacon is at x=2, y=10 +Sensor at x=2, y=0: closest beacon is at x=2, y=10 +Sensor at x=0, y=11: closest beacon is at x=2, y=10 +Sensor at x=20, y=14: closest beacon is at x=25, y=17 +Sensor at x=17, y=20: closest beacon is at x=21, y=22 +Sensor at x=16, y=7: closest beacon is at x=15, y=3 +Sensor at x=14, y=3: closest beacon is at x=15, y=3 +Sensor at x=20, y=1: closest beacon is at x=15, y=3 diff --git a/2022/day15/input/input.txt b/2022/day15/input/input.txt new file mode 100644 index 0000000..2079d2e --- /dev/null +++ b/2022/day15/input/input.txt @@ -0,0 +1,26 @@ +Sensor at x=3999724, y=2000469: closest beacon is at x=4281123, y=2282046 +Sensor at x=3995530, y=8733: closest beacon is at x=3321979, y=-692911 +Sensor at x=3016889, y=2550239: closest beacon is at x=2408038, y=2645605 +Sensor at x=3443945, y=3604888: closest beacon is at x=3610223, y=3768674 +Sensor at x=168575, y=491461: closest beacon is at x=1053731, y=-142061 +Sensor at x=2820722, y=3865596: closest beacon is at x=3191440, y=3801895 +Sensor at x=2329102, y=2456329: closest beacon is at x=2408038, y=2645605 +Sensor at x=3889469, y=3781572: closest beacon is at x=3610223, y=3768674 +Sensor at x=3256726, y=3882107: closest beacon is at x=3191440, y=3801895 +Sensor at x=3729564, y=3214899: closest beacon is at x=3610223, y=3768674 +Sensor at x=206718, y=2732608: closest beacon is at x=-152842, y=3117903 +Sensor at x=2178192, y=2132103: closest beacon is at x=2175035, y=2000000 +Sensor at x=1884402, y=214904: closest beacon is at x=1053731, y=-142061 +Sensor at x=3060435, y=980430: closest beacon is at x=2175035, y=2000000 +Sensor at x=3998355, y=3965954: closest beacon is at x=3610223, y=3768674 +Sensor at x=3704399, y=3973731: closest beacon is at x=3610223, y=3768674 +Sensor at x=1421672, y=3446889: closest beacon is at x=2408038, y=2645605 +Sensor at x=3415633, y=3916020: closest beacon is at x=3191440, y=3801895 +Sensor at x=2408019, y=2263990: closest beacon is at x=2408038, y=2645605 +Sensor at x=3735247, y=2533767: closest beacon is at x=4281123, y=2282046 +Sensor at x=1756494, y=1928662: closest beacon is at x=2175035, y=2000000 +Sensor at x=780161, y=1907142: closest beacon is at x=2175035, y=2000000 +Sensor at x=3036853, y=3294727: closest beacon is at x=3191440, y=3801895 +Sensor at x=53246, y=3908582: closest beacon is at x=-152842, y=3117903 +Sensor at x=2110517, y=2243287: closest beacon is at x=2175035, y=2000000 +Sensor at x=3149491, y=3998374: closest beacon is at x=3191440, y=3801895 diff --git a/README.md b/README.md index ed850d1..fd34a29 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,5 @@ - `C`: All (days 1-25) #### Advent of Code 2022 + - `Bash`: All (days 1-12) - `C`: All (days 1-14)