diff --git a/2019/day05/EXAMPLE.txt b/2019/day05/EXAMPLE.txt new file mode 100644 index 0000000..8d109b0 --- /dev/null +++ b/2019/day05/EXAMPLE.txt @@ -0,0 +1 @@ +1002,4,3,4 diff --git a/2019/day05/EXAMPLE2.txt b/2019/day05/EXAMPLE2.txt new file mode 100644 index 0000000..4c34a69 --- /dev/null +++ b/2019/day05/EXAMPLE2.txt @@ -0,0 +1 @@ +1101,100,-1,4,0 diff --git a/2019/day05/INPUT.txt b/2019/day05/INPUT.txt new file mode 100644 index 0000000..ec0423c --- /dev/null +++ b/2019/day05/INPUT.txt @@ -0,0 +1 @@ +3,225,1,225,6,6,1100,1,238,225,104,0,1101,86,8,225,1101,82,69,225,101,36,65,224,1001,224,-106,224,4,224,1002,223,8,223,1001,224,5,224,1,223,224,223,102,52,148,224,101,-1144,224,224,4,224,1002,223,8,223,101,1,224,224,1,224,223,223,1102,70,45,225,1002,143,48,224,1001,224,-1344,224,4,224,102,8,223,223,101,7,224,224,1,223,224,223,1101,69,75,225,1001,18,85,224,1001,224,-154,224,4,224,102,8,223,223,101,2,224,224,1,224,223,223,1101,15,59,225,1102,67,42,224,101,-2814,224,224,4,224,1002,223,8,223,101,3,224,224,1,223,224,223,1101,28,63,225,1101,45,22,225,1101,90,16,225,2,152,92,224,1001,224,-1200,224,4,224,102,8,223,223,101,7,224,224,1,223,224,223,1101,45,28,224,1001,224,-73,224,4,224,1002,223,8,223,101,7,224,224,1,224,223,223,1,14,118,224,101,-67,224,224,4,224,1002,223,8,223,1001,224,2,224,1,223,224,223,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,7,677,677,224,102,2,223,223,1005,224,329,1001,223,1,223,1008,226,226,224,1002,223,2,223,1005,224,344,1001,223,1,223,1107,677,226,224,1002,223,2,223,1006,224,359,1001,223,1,223,107,677,677,224,102,2,223,223,1005,224,374,101,1,223,223,1108,677,226,224,102,2,223,223,1005,224,389,1001,223,1,223,1007,677,677,224,1002,223,2,223,1005,224,404,101,1,223,223,1008,677,226,224,102,2,223,223,1005,224,419,101,1,223,223,1108,226,677,224,102,2,223,223,1006,224,434,1001,223,1,223,8,677,226,224,1002,223,2,223,1005,224,449,101,1,223,223,1008,677,677,224,1002,223,2,223,1006,224,464,1001,223,1,223,1108,226,226,224,1002,223,2,223,1005,224,479,1001,223,1,223,1007,226,677,224,102,2,223,223,1005,224,494,1001,223,1,223,1007,226,226,224,102,2,223,223,1005,224,509,101,1,223,223,107,677,226,224,1002,223,2,223,1006,224,524,1001,223,1,223,108,677,677,224,102,2,223,223,1006,224,539,101,1,223,223,7,677,226,224,102,2,223,223,1006,224,554,1001,223,1,223,1107,226,677,224,102,2,223,223,1005,224,569,101,1,223,223,108,677,226,224,1002,223,2,223,1006,224,584,101,1,223,223,108,226,226,224,102,2,223,223,1006,224,599,1001,223,1,223,1107,226,226,224,102,2,223,223,1006,224,614,1001,223,1,223,8,226,677,224,102,2,223,223,1006,224,629,1001,223,1,223,107,226,226,224,102,2,223,223,1005,224,644,101,1,223,223,8,226,226,224,102,2,223,223,1006,224,659,101,1,223,223,7,226,677,224,102,2,223,223,1005,224,674,101,1,223,223,4,223,99,226 diff --git a/2019/day05/Makefile b/2019/day05/Makefile new file mode 100644 index 0000000..7fd5a7c --- /dev/null +++ b/2019/day05/Makefile @@ -0,0 +1,93 @@ +# AOC daily Makefile - GNU make only. +# +# 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 +# + +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=gnu99 +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/day05/README.org b/2019/day05/README.org new file mode 100644 index 0000000..be8dbea --- /dev/null +++ b/2019/day05/README.org @@ -0,0 +1,104 @@ +** --- Day 5: Sunny with a Chance of Asteroids --- +You're starting to sweat as the ship makes its way toward Mercury. The +Elves suggest that you get the air conditioner working by upgrading your +ship computer to support the Thermal Environment Supervision Terminal. + +The Thermal Environment Supervision Terminal (TEST) starts by running a +/diagnostic program/ (your puzzle input). The TEST diagnostic program +will run on [[file:2][your existing Intcode computer]] after a few +modifications: + +/First/, you'll need to add /two new instructions/: + +- Opcode =3= takes a single integer as /input/ and saves it to the + position given by its only parameter. For example, the instruction + =3,50= would take an input value and store it at address =50=. +- Opcode =4= /outputs/ the value of its only parameter. For example, the + instruction =4,50= would output the value at address =50=. + +Programs that use these instructions will come with documentation that +explains what should be connected to the input and output. The program +=3,0,4,0,99= outputs whatever it gets as input, then halts. + +/Second/, you'll need to add support for /parameter modes/: + +Each parameter of an instruction is handled based on its parameter mode. +Right now, your ship computer already understands parameter mode =0=, +/position mode/, which causes the parameter to be interpreted as a +/position/ - if the parameter is =50=, its value is /the value stored at +address =50= in memory/. Until now, all parameters have been in position +mode. + +Now, your ship computer will also need to handle parameters in mode =1=, +/immediate mode/. In immediate mode, a parameter is interpreted as a +/value/ - if the parameter is =50=, its value is simply /=50=/. + +Parameter modes are stored in the same value as the instruction's +opcode. The opcode is a two-digit number based only on the ones and tens +digit of the value, that is, the opcode is the rightmost two digits of +the first value in an instruction. Parameter modes are single digits, +one per parameter, read right-to-left from the opcode: the first +parameter's mode is in the hundreds digit, the second parameter's mode +is in the thousands digit, the third parameter's mode is in the +ten-thousands digit, and so on. Any missing modes are =0=. + +For example, consider the program =1002,4,3,4,33=. + +The first instruction, =1002,4,3,4=, is a /multiply/ instruction - the +rightmost two digits of the first value, =02=, indicate opcode =2=, +multiplication. Then, going right to left, the parameter modes are =0= +(hundreds digit), =1= (thousands digit), and =0= (ten-thousands digit, +not present and therefore zero): + +#+BEGIN_EXAMPLE + ABCDE + 1002 + + DE - two-digit opcode, 02 == opcode 2 + C - mode of 1st parameter, 0 == position mode + B - mode of 2nd parameter, 1 == immediate mode + A - mode of 3rd parameter, 0 == position mode, + omitted due to being a leading zero +#+END_EXAMPLE + +This instruction multiplies its first two parameters. The first +parameter, =4= in position mode, works like it did before - its value is +the value stored at address =4= (=33=). The second parameter, =3= in +immediate mode, simply has value =3=. The result of this operation, +=33 * 3 = 99=, is written according to the third parameter, =4= in +position mode, which also works like it did before - =99= is written to +address =4=. + +Parameters that an instruction writes to will /never be in immediate +mode/. + +/Finally/, some notes: + +- It is important to remember that the instruction pointer should + increase by /the number of values in the instruction/ after the + instruction finishes. Because of the new instructions, this amount is + no longer always =4=. +- Integers can be negative: =1101,100,-1,4,0= is a valid program (find + =100 + -1=, store the result in position =4=). + +The TEST diagnostic program will start by requesting from the user the +ID of the system to test by running an /input/ instruction - provide it +=1=, the ID for the ship's air conditioner unit. + +It will then perform a series of diagnostic tests confirming that +various parts of the Intcode computer, like parameter modes, function +correctly. For each test, it will run an /output/ instruction indicating +how far the result of the test was from the expected value, where =0= +means the test was successful. Non-zero outputs mean that a function is +not working correctly; check the instructions that were run before the +output instruction to see which one failed. + +Finally, the program will output a /diagnostic code/ and immediately +halt. This final output isn't an error; an output followed immediately +by a halt means the program finished. If all outputs were zero except +the diagnostic code, the diagnostic program ran successfully. + +After providing =1= to the only input instruction and passing all the +tests, /what diagnostic code does the program produce?/ + +To begin, [[file:5/input][get your puzzle input]]. diff --git a/2019/day05/aoc-c.c b/2019/day05/aoc-c.c new file mode 100644 index 0000000..7223c35 --- /dev/null +++ b/2019/day05/aoc-c.c @@ -0,0 +1,150 @@ +/* aoc-c.c: Advent of Code 2019, day 5 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 "debug.h" + +typedef enum { + ADD = 1, + MUL = 2, + RET = 99 +} opcode_t; + +#define MAXOPS 1024 +struct program { + int length; /* total program length */ + int cur; /* current position */ + int ops[MAXOPS]; /* should really be dynamic */ +}; + +#define OP(p) ((p)->ops + (p)->cur) + +#define A1(p) ((p)->ops + *((p)->ops + (p)->cur + 1)) +#define A2(p) ((p)->ops + *((p)->ops + (p)->cur + 2)) +#define A3(p) ((p)->ops + *((p)->ops + (p)->cur + 3)) + +static int run(struct program *prog) +{ + while (1) { + opcode_t opcode = *OP(prog); + switch (opcode) { + case ADD: + *A3(prog) = *A1(prog) + *A2(prog); + break; + case MUL: + *A3(prog) = *A1(prog) * *A2(prog); + break; + case RET: + return prog->ops[0]; + default: + fprintf(stderr, "wrong opcode %d at %d.\n", opcode, prog->cur); + exit (1); + } + prog->cur += 4; + } + return -1; +} + +static struct program *parse() +{ + size_t alloc = 0; + ssize_t buflen; + char *buf = NULL, *token; + int input; + struct program *prog = NULL; + + if ((buflen = getline(&buf, &alloc, stdin)) <= 0) { + fprintf(stderr, "error reading file.\n"); + goto end; + } + + if (!(prog = calloc(1, sizeof(struct program)))) { + fprintf(stderr, "cannot allocate program.\n"); + goto freebuf; + } + for (token = strtok(buf, ","); token; token = strtok(NULL, ",")) { + if (prog->length >= MAXOPS - 1) { + fprintf(stderr, "overflow !\n"); + free(prog); + prog = NULL; + goto freebuf; + } + input=atoi(token); + prog->ops[prog->length++] = input; + } +freebuf: + free(buf); +end: + return prog; +} + +static int part1(struct program *p) +{ + p->ops[1] = 12; + p->ops[2] = 2; + + return run(p); +} + +static int part2(struct program *p) +{ + struct program work; + + for (int noun = 0; noun < 100; ++noun) { + for (int verb = 0; verb < 100; ++verb) { + work = *p; + work.ops[1] = noun; + work.ops[2] = verb; + if (run(&work) == 19690720) + return noun * 100 + verb; + } + } + return -1; +} + +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 program *p; + + 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); + p = parse(); + printf("%s : res=%d\n", *av, part == 1? part1(p): part2(p)); + free(p); + exit (0); +}