From 130f2a4d543d5671bbd5843b96280fbff0bc3af4 Mon Sep 17 00:00:00 2001 From: Bruno Date: Tue, 4 Oct 2022 07:34:29 +0200 Subject: [PATCH] 2019 day 9 init from day 7 --- 2019/day09/EXAMPLE.txt | 1 + 2019/day09/EXAMPLE2.txt | 1 + 2019/day09/EXAMPLE3.txt | 1 + 2019/day09/INPUT.txt | 1 + 2019/day09/Makefile | 93 +++++++++++++++ 2019/day09/README.org | 81 +++++++++++++ 2019/day09/aoc-c.c | 258 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 436 insertions(+) create mode 100644 2019/day09/EXAMPLE.txt create mode 100644 2019/day09/EXAMPLE2.txt create mode 100644 2019/day09/EXAMPLE3.txt create mode 100644 2019/day09/INPUT.txt create mode 100644 2019/day09/Makefile create mode 100644 2019/day09/README.org create mode 100644 2019/day09/aoc-c.c diff --git a/2019/day09/EXAMPLE.txt b/2019/day09/EXAMPLE.txt new file mode 100644 index 0000000..2ce40b6 --- /dev/null +++ b/2019/day09/EXAMPLE.txt @@ -0,0 +1 @@ +109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99 diff --git a/2019/day09/EXAMPLE2.txt b/2019/day09/EXAMPLE2.txt new file mode 100644 index 0000000..50a032d --- /dev/null +++ b/2019/day09/EXAMPLE2.txt @@ -0,0 +1 @@ +1102,34915192,34915192,7,4,7,99,0 diff --git a/2019/day09/EXAMPLE3.txt b/2019/day09/EXAMPLE3.txt new file mode 100644 index 0000000..f0c3e19 --- /dev/null +++ b/2019/day09/EXAMPLE3.txt @@ -0,0 +1 @@ +104,1125899906842624,99 diff --git a/2019/day09/INPUT.txt b/2019/day09/INPUT.txt new file mode 100644 index 0000000..0ad4957 --- /dev/null +++ b/2019/day09/INPUT.txt @@ -0,0 +1 @@ +1102,34463338,34463338,63,1007,63,34463338,63,1005,63,53,1102,3,1,1000,109,988,209,12,9,1000,209,6,209,3,203,0,1008,1000,1,63,1005,63,65,1008,1000,2,63,1005,63,904,1008,1000,0,63,1005,63,58,4,25,104,0,99,4,0,104,0,99,4,17,104,0,99,0,0,1101,20,0,1007,1101,0,197,1022,1102,475,1,1028,1102,30,1,1008,1101,25,0,1010,1102,1,23,1009,1101,0,22,1013,1101,470,0,1029,1102,24,1,1014,1102,1,39,1005,1101,31,0,1003,1101,807,0,1026,1101,0,26,1018,1102,1,804,1027,1101,0,0,1020,1102,1,38,1017,1101,0,27,1016,1102,443,1,1024,1101,0,36,1006,1102,21,1,1015,1101,28,0,1001,1102,33,1,1019,1102,1,37,1011,1102,1,190,1023,1101,0,434,1025,1101,34,0,1004,1102,1,1,1021,1101,0,29,1012,1102,1,32,1002,1101,35,0,1000,109,30,2105,1,-7,1001,64,1,64,1105,1,199,4,187,1002,64,2,64,109,-23,2101,0,-5,63,1008,63,32,63,1005,63,225,4,205,1001,64,1,64,1105,1,225,1002,64,2,64,109,7,2102,1,-5,63,1008,63,23,63,1005,63,251,4,231,1001,64,1,64,1106,0,251,1002,64,2,64,109,-16,2101,0,2,63,1008,63,33,63,1005,63,275,1001,64,1,64,1106,0,277,4,257,1002,64,2,64,109,10,21102,40,1,4,1008,1012,40,63,1005,63,299,4,283,1106,0,303,1001,64,1,64,1002,64,2,64,109,7,2102,1,-9,63,1008,63,33,63,1005,63,327,1001,64,1,64,1105,1,329,4,309,1002,64,2,64,109,-17,2107,34,2,63,1005,63,347,4,335,1105,1,351,1001,64,1,64,1002,64,2,64,109,1,1201,8,0,63,1008,63,23,63,1005,63,375,1001,64,1,64,1106,0,377,4,357,1002,64,2,64,109,-4,2108,31,8,63,1005,63,395,4,383,1105,1,399,1001,64,1,64,1002,64,2,64,109,3,1201,8,0,63,1008,63,36,63,1005,63,421,4,405,1105,1,425,1001,64,1,64,1002,64,2,64,109,25,2105,1,1,4,431,1001,64,1,64,1105,1,443,1002,64,2,64,109,-3,1205,0,459,1001,64,1,64,1106,0,461,4,449,1002,64,2,64,109,-2,2106,0,10,4,467,1106,0,479,1001,64,1,64,1002,64,2,64,109,12,1206,-9,495,1001,64,1,64,1106,0,497,4,485,1002,64,2,64,109,-39,1207,9,36,63,1005,63,519,4,503,1001,64,1,64,1105,1,519,1002,64,2,64,109,11,1202,-1,1,63,1008,63,28,63,1005,63,541,4,525,1105,1,545,1001,64,1,64,1002,64,2,64,109,6,2107,24,1,63,1005,63,565,1001,64,1,64,1106,0,567,4,551,1002,64,2,64,109,1,1207,-3,35,63,1005,63,583,1106,0,589,4,573,1001,64,1,64,1002,64,2,64,109,1,21102,41,1,5,1008,1015,40,63,1005,63,613,1001,64,1,64,1105,1,615,4,595,1002,64,2,64,109,-2,2108,22,1,63,1005,63,635,1001,64,1,64,1105,1,637,4,621,1002,64,2,64,109,-10,1208,4,33,63,1005,63,653,1106,0,659,4,643,1001,64,1,64,1002,64,2,64,109,16,1206,6,673,4,665,1106,0,677,1001,64,1,64,1002,64,2,64,109,-4,1202,-8,1,63,1008,63,35,63,1005,63,701,1001,64,1,64,1105,1,703,4,683,1002,64,2,64,109,13,21108,42,42,-8,1005,1015,721,4,709,1105,1,725,1001,64,1,64,1002,64,2,64,109,-18,21107,43,44,5,1005,1010,743,4,731,1106,0,747,1001,64,1,64,1002,64,2,64,109,-11,1208,8,32,63,1005,63,765,4,753,1106,0,769,1001,64,1,64,1002,64,2,64,109,15,21101,44,0,5,1008,1014,47,63,1005,63,789,1105,1,795,4,775,1001,64,1,64,1002,64,2,64,109,13,2106,0,5,1106,0,813,4,801,1001,64,1,64,1002,64,2,64,109,-12,21108,45,43,0,1005,1010,829,1106,0,835,4,819,1001,64,1,64,1002,64,2,64,109,-4,21107,46,45,10,1005,1016,855,1001,64,1,64,1106,0,857,4,841,1002,64,2,64,109,3,21101,47,0,5,1008,1014,47,63,1005,63,883,4,863,1001,64,1,64,1106,0,883,1002,64,2,64,109,10,1205,2,901,4,889,1001,64,1,64,1105,1,901,4,64,99,21102,27,1,1,21102,915,1,0,1106,0,922,21201,1,13433,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21101,0,942,0,1106,0,922,22102,1,1,-1,21201,-2,-3,1,21102,1,957,0,1105,1,922,22201,1,-1,-2,1106,0,968,21202,-2,1,-2,109,-3,2106,0,0 diff --git a/2019/day09/Makefile b/2019/day09/Makefile new file mode 100644 index 0000000..7a1c6da --- /dev/null +++ b/2019/day09/Makefile @@ -0,0 +1,93 @@ +# 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.txt +SHELL := /bin/bash + +CC := gcc + +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 ex1 ex2 + +all: README.org ex1 ex2 + +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 + +assembly: aoc-c.s + +ex1: aoc-c + @$(TIME) aoc-c -p 1 < $(INPUT) + +ex2: aoc-c + @$(TIME) aoc-c -p 2 < $(INPUT) + +clean: + @rm -f aoc-c core* vgcore* gmon.out aoc-c.s aoc-c.i README.html + +.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: %.html + @echo generating $@. Cleanup before commit ! + @pandoc $< -o $@ diff --git a/2019/day09/README.org b/2019/day09/README.org new file mode 100644 index 0000000..1d28a35 --- /dev/null +++ b/2019/day09/README.org @@ -0,0 +1,81 @@ +** --- Day 9: Sensor Boost --- +You've just said goodbye to the rebooted rover and left Mars when you +receive a faint distress signal coming from the asteroid belt. It must +be the Ceres monitoring station! + +In order to lock on to the signal, you'll need to boost your sensors. +The Elves send up the latest /BOOST/ program - Basic Operation Of System +Test. + +While BOOST (your puzzle input) is capable of boosting your sensors, for +tenuous safety reasons, it refuses to do so until the computer it runs +on passes some checks to demonstrate it is a /complete Intcode +computer/. + +[[file:5][Your existing Intcode computer]] is missing one key feature: +it needs support for parameters in /relative mode/. + +Parameters in mode =2=, /relative mode/, behave very similarly to +parameters in /position mode/: the parameter is interpreted as a +position. Like position mode, parameters in relative mode can be read +from or written to. + +The important difference is that relative mode parameters don't count +from address =0=. Instead, they count from a value called the /relative +base/. The /relative base/ starts at =0=. + +The address a relative mode parameter refers to is itself /plus/ the +current /relative base/. When the relative base is =0=, relative mode +parameters and position mode parameters with the same value refer to the +same address. + +For example, given a relative base of =50=, a relative mode parameter of +=-7= refers to memory address =50 + -7 = 43=. + +The relative base is modified with the /relative base offset/ +instruction: + +- Opcode =9= /adjusts the relative base/ by the value of its only + parameter. The relative base increases (or decreases, if the value is + negative) by the value of the parameter. + +For example, if the relative base is =2000=, then after the instruction +=109,19=, the relative base would be =2019=. If the next instruction +were =204,-34=, then the value at address =1985= would be output. + +Your Intcode computer will also need a few other capabilities: + +- The computer's available memory should be much larger than the initial + program. Memory beyond the initial program starts with the value =0= + and can be read or written like any other memory. (It is invalid to + try to access memory at a negative address, though.) +- The computer should have support for large numbers. Some instructions + near the beginning of the BOOST program will verify this capability. + +Here are some example programs that use these features: + +- =109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99= takes no + input and produces a + [[https://en.wikipedia.org/wiki/Quine_(computing)][copy of itself]] as + output. +- =1102,34915192,34915192,7,4,7,99,0= should output a 16-digit number. +- =104,1125899906842624,99= should output the large number in the + middle. + +The BOOST program will ask for a single input; run it in test mode by +providing it the value =1=. It will perform a series of checks on each +opcode, output any opcodes (and the associated parameter modes) that +seem to be functioning incorrectly, and finally output a BOOST keycode. + +Once your Intcode computer is fully functional, the BOOST program should +report no malfunctioning opcodes when run in test mode; it should only +output a single value, the BOOST keycode. /What BOOST keycode does it +produce?/ + +To begin, [[file:9/input][get your puzzle input]]. + +Answer: + +You can also [Shareon +[[https://twitter.com/intent/tweet?text=%22Sensor+Boost%22+%2D+Day+9+%2D+Advent+of+Code+2019&url=https%3A%2F%2Fadventofcode%2Ecom%2F2019%2Fday%2F9&related=ericwastl&hashtags=AdventOfCode][Twitter]] +[[javascript:void(0);][Mastodon]]] this puzzle. diff --git a/2019/day09/aoc-c.c b/2019/day09/aoc-c.c new file mode 100644 index 0000000..25a300f --- /dev/null +++ b/2019/day09/aoc-c.c @@ -0,0 +1,258 @@ +/* aoc-c.c: Advent of Code 2019, day 9 parts 1 & 2 + * + * 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 "br.h" +#include "bits.h" +#include "debug.h" +#include "list.h" +#include "pool.h" + +/* operators codes + */ +typedef enum { + ADD = 1, MUL = 2, /* CALC: add and mult */ + INP = 3, OUT = 4, /* I/O: input and output value */ + JMP_T = 5, JMP_F = 6, /* JUMPS: jump if true / if false */ + SET_LT = 7, SET_EQ = 8, /* COND SETS: set if true/false */ + ADJ_RL = 9, /* ADDRESSING: adjust relative addr */ + HLT = 99 /* HALT */ +} opcode_t; + +/** + * ops - array of op-codes, mnemo, and number of parameters + * @op: An integer, the opcode + * @length: Next instruction offset + */ +typedef struct { + int op; + u8 length; +} ops_t; + +typedef struct input { + int val; + struct list_head list; +} input_t; + +#define MAXOPS 1024 +typedef struct { + int length; /* total program length */ + int cur; /* current position */ + struct list_head input; /* process input queue */ + int mem [MAXOPS]; /* should really be dynamic */ +} program_t; + +static ops_t ops[] = { + [ADD] = { ADD, 4 }, [MUL] = { MUL, 4 }, + [INP] = { INP, 2 }, [OUT] = { OUT, 2 }, + [JMP_T] = { JMP_T, 3 }, [JMP_F] = { JMP_F, 3 }, + [SET_LT] = { SET_LT, 4 }, [SET_EQ] = { SET_EQ, 4 }, + [HLT] = { HLT, 1 } +}; + + +static int _flag_pow10[] = {1, 100, 1000, 10000}; +#define OP(p, n) ((p->mem[n]) % 100) +#define ISDIRECT(p, n, i) ((((p->mem[n]) / _flag_pow10[i]) % 10)) +#define DIRECT(p, i) ((p)->mem[i]) +#define INDIRECT(p, i) (DIRECT(p, DIRECT(p, i))) + +#define peek(p, n, i) (ISDIRECT(p, n, i)? DIRECT(p, n + i): INDIRECT(p, n + i)) +#define poke(p, n, i, val) do { \ + INDIRECT(p, n + i) = val; } \ + while (0) + + +static pool_t *pool_input; +static __always_inline int prg_add_input(program_t *prg, int in) +{ + input_t *input = pool_get(pool_input); + input->val = in; + list_add_tail(&input->list, &prg->input); + return in; +} + +static __always_inline int prg_get_input(program_t *prg, int *out) +{ + input_t *input = list_first_entry_or_null(&prg->input, input_t, list); + if (!input) + return 0; + *out = input->val; + list_del(&input->list); + pool_add(pool_input, input); + return 1; +} + +/** + * permute - get next permutation of an array of integers + * @len: length of array + * @array: address of array + * + * Algorithm: lexicographic permutations + * https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order + * Before the initial call, the array must be sorted (e.g. 0 2 3 5) + * + * Return: 1 if next permutation was found, 0 if no more permutation. + * + */ +static int permute_next(int len, int *array) +{ + int k, l; + + /* 1. Find the largest index k such that a[k] < a[k + 1] */ + for (k = len - 2; k >= 0 && array[k] >= array[k + 1]; k--) + ; + /* No more permutations */ + if (k < 0) + return 0; + /* 2. Find the largest index l greater than k such that a[k] < a[l] */ + for (l = len - 1; array[l] <= array[k]; l--) + ; + /* 3. Swap the value of a[k] with that of a[l] */ + swap(array[k], array[l]); + /* 4. Reverse sequence from a[k + 1] up to the final element */ + for (l = len - 1, k++; k < l; k++, l--) + swap(array[k], array[l]); + return 1; +} + +static int run(program_t *p, int *end) +{ + int out = -1; + while (1) { + int op = OP(p, p->cur), cur = p->cur, input; + + if (!(ops[op].op)) { + fprintf(stderr, "PANIC: illegal instruction %d at %d.\n", op, p->cur); + return -1; + } + switch (op) { + case ADD: + poke(p, p->cur, 3, peek(p, p->cur, 1) + peek(p, p->cur, 2)); + break; + case MUL: + poke(p, p->cur, 3, peek(p, p->cur, 1) * peek(p, p->cur, 2)); + break; + case INP: + if (prg_get_input(p, &input)) + poke(p, p->cur, 1, input); + else + /* we need an input which is not yet avalaible, so we need + * to put the program in "waiting mode": We stop it (and + * return output value) without setting end flag. + */ + goto sleep; + break; + case OUT: + out = peek(p, p->cur, 1); + break; + case JMP_T: + if (peek(p, p->cur, 1)) + p->cur = peek(p, p->cur, 2); + break; + case JMP_F: + if (!peek(p, p->cur, 1)) + p->cur = peek(p, p->cur, 2); + break; + case SET_LT: + poke(p, p->cur, 3, peek(p, p->cur, 1) < peek(p, p->cur, 2) ? 1: 0); + break; + case SET_EQ: + poke(p, p->cur, 3, peek(p, p->cur, 1) == peek(p, p->cur, 2) ? 1: 0); + break; + case HLT: + *end = 1; + sleep: + return out; + } + if (p->cur == cur) + p->cur += ops[op].length; + } +} + +static void parse(program_t *prog) +{ + while (scanf("%d%*c", &prog->mem[prog->length++]) > 0) + ; +} + +static int usage(char *prg) +{ + fprintf(stderr, "Usage: %s [-d debug_level] [-p part] [-i input]\n", prg); + return 1; +} + +int main(int ac, char **av) +{ + int phase1[] = {0, 1, 2, 3, 4}, phase2[] = {5, 6, 7, 8, 9}, *phase; + int opt, max = 0, part = 1; + program_t p = { 0 }, prg[5]; + + while ((opt = getopt(ac, av, "d:p:o:")) != -1) { + switch (opt) { + case 'd': + debug_level_set(atoi(optarg)); + break; + case 'o': + for (ulong i = 0; i < strlen(optarg); ++i) + phase1[i] = optarg[i] - '0'; + break; + case 'p': /* 1 or 2 */ + part = atoi(optarg); + if (part < 1 || part > 2) + return usage(*av); + break; + default: + return usage(*av); + } + } + + pool_input = pool_create("input", 128, sizeof(input_t)); + + if (optind < ac) + return usage(*av); + + phase = part == 1? phase1: phase2; + parse(&p); + + do { + int out = 0, end = 0; + /* reset programs initial state, and add phase to their input + */ + for (unsigned i = 0; i < ARRAY_SIZE(prg); ++i) { + prg[i] = p; + INIT_LIST_HEAD(&prg[i].input); + prg_add_input(&prg[i], phase[i]); + } + + /* run the 5 processes in order (0, 1, 2, 3, 4, 0, 1, etc...), + * until end flag is set by the process 4 (HLT instruction) + */ + while (!end) { + for (int i = 0; i < 5; ++i) { + /* add last process output in current process input queue + */ + prg_add_input(&prg[i], out); + out = run(&prg[i], &end); + } + } + max = max(max, out); + } while (permute_next(5, phase)); + + printf("%s : res=%d\n", *av, max); + pool_destroy(pool_input); + exit(0); +}