aoc

My solutions for the Advent of Code
git clone https://git.tronto.net/aoc
Download | Log | Files | Refs | README

commit 30e53593bf1d336520bc046f62965cd73579a711
parent 10848ee1a0e718d6a48e6bde1cf07d4138eb4b1c
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Sun, 24 Dec 2023 20:12:34 +0100

Added solution for 24

Diffstat:
A2023/24/24a.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A2023/24/24b.c | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 281 insertions(+), 0 deletions(-)

diff --git a/2023/24/24a.c b/2023/24/24a.c @@ -0,0 +1,76 @@ +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +#define N 300 +#define XMIN 2e14 +#define XMAX 4e14 +#define eps 1e-6 + +#define ABS(x) ((x)>0?(x):-(x)) + +typedef struct { double x, y, z; } point_t; +typedef struct { point_t p, v; } line_t; + +char *buf, line[N]; +int n, s; +line_t l[N]; + +bool isnum(char c) { return (c >= '0' && c <= '9') || c == '-'; } + +line_t readl(char *buf) { + double a[6]; + for (int i = 0; i < 6; i++) { + a[i] = (double)atoll(buf); + while (isnum(*buf)) buf++; + while (*buf != '\n' && !isnum(*buf)) buf++; + } + return (line_t) { + .p = (point_t){.x = a[0], .y = a[1], .z = a[2]}, + .v = (point_t){.x = a[3], .y = a[4], .z = a[5]} + }; +} + +double det(double a, double b, double c, double d) { return a*d-b*c; } + +bool parallel2d(line_t l1, line_t l2) { + double d = det(l1.v.x, l2.v.x, l1.v.y, l2.v.y); + return ABS(d) < eps; +} + +point_t collide2d(line_t l1, line_t l2, double *t1, double *t2) { + double a1 = l1.v.x; + double b1 = -l2.v.x; + double c1 = l2.p.x - l1.p.x; + double a2 = l1.v.y; + double b2 = -l2.v.y; + double c2 = l2.p.y - l1.p.y; + + double d = det(a1, b1, a2, b2); + *t1 = det(c1, b1, c2, b2) / d; + *t2 = det(a1, c1, a2, c2) / d; + + double x = l1.p.x + l1.v.x * (*t1); + double y = l1.p.y + l1.v.y * (*t1); + + return (point_t) {.x = x, .y = y}; +} + +int main() { + for (n = 0; (buf = fgets(line, N, stdin)) != NULL; n++) + l[n] = readl(buf); + + for (int i = 0; i < n; i++) { + for (int j = i+1; j < n; j++) { + if (parallel2d(l[i], l[j])) continue; + double t1, t2; + point_t p = collide2d(l[i], l[j], &t1, &t2); + if (t1 < -eps || t2 < -eps) continue; + s += p.x > XMIN-eps && p.x < XMAX+eps && + p.y > XMIN-eps && p.y < XMAX+eps; + } + } + + printf("%d\n", s); + return 0; +} diff --git a/2023/24/24b.c b/2023/24/24b.c @@ -0,0 +1,205 @@ +/* +Due to numerical errors, the result of this program was not correct on my +machine (ahah - doesn't work on my machine (: ) +However, with slightly different values for eps I got two answers whose +difference was 4 and one was too high, the other too low. A quick binary +search gave the correct result. +I'll make a more stable version if I feel like it. +*/ + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +#define D 10 +#define N 300 + +#define ABS(x) ((x)>0?(x):-(x)) +#define eps 1e1 +#define EQ(x,y) (ABS((x)-(y))<eps) + +typedef struct { double x, y, z; } point_t; +typedef struct { point_t p, v; } line_t; + +char *buf, line[N]; +int nlines, s; +line_t l[N]; + +bool isnum(char c) { return (c >= '0' && c <= '9') || c == '-'; } + +line_t readl(char *buf) { + double a[6]; + for (int i = 0; i < 6; i++) { + a[i] = (double)atoll(buf); + while (isnum(*buf)) buf++; + while (*buf != '\n' && !isnum(*buf)) buf++; + } + return (line_t) { + .p = (point_t){.x = a[0], .y = a[1], .z = a[2]}, + .v = (point_t){.x = a[3], .y = a[4], .z = a[5]} + }; +} + +void swap(double *x, double *y) { double aux = *x; *x = *y; *y = aux; } + +void printa(double A[D][D], double C[D], int d) { + for (int i = 0; i < d; i++) { + printf("[ "); + for (int j = 0; j < d; j++) + printf("%.2lf ", A[i][j]); + printf("]\t[%.2lf]\n", C[i]); + } +} + +/* Solve the linear system AX = C with row reduction */ +/* Return true if it has a unique solution, false in any other case */ +bool solvesystem(double A[D][D], double C[D], int d, double *X) { + /* Row reduction */ + for (int i = 0; i < d; i++) { + /* Make first nonzero */ + int nz; + for (nz = i; nz < d && EQ(A[i][nz], 0.0); nz++) ; + if (nz == d) return false; + swap(&C[i], &C[nz]); + for (int j = 0; j < d; j++) + swap(&A[i][j], &A[nz][j]); + + /* Reduce rows */ + for (int ii = i+1; ii < d; ii++) { + double r = A[ii][i] / A[i][i]; + for (int k = i; k < d; k++) + A[ii][k] -= r*A[i][k]; + C[ii] -= r*C[i]; + } + } + + /* Back substitution */ + for (int i = d-1; i >= 0; i--) { + X[i] = C[i]; + for (int j = i+1; j < d; j++) + X[i] -= A[i][j]*X[j]; + X[i] /= A[i][i]; + } + + return true; +} + +bool equal(point_t p, point_t q) { + return EQ(p.x, q.x) && EQ(p.y, q.y) && EQ(p.z, q.z); +} + +point_t pos(line_t l, double t) { + return (point_t) { + .x = l.p.x + t*l.v.x, + .y = l.p.y + t*l.v.y, + .z = l.p.z + t*l.v.z + }; +} + +void testsolution(line_t sol) { + for (int i = 0; i < nlines; i++) { + double t = -1.0; + if (!EQ(sol.v.x, l[i].v.x)) + t = (sol.p.x-l[i].p.x)/(l[i].v.x-sol.v.x); + if (!EQ(sol.v.y, l[i].v.y)) + t = (sol.p.y-l[i].p.y)/(l[i].v.y-sol.v.y); + if (!EQ(sol.v.z, l[i].v.z)) + t = (sol.p.z-l[i].p.z)/(l[i].v.z-sol.v.z); + point_t p1 = pos(sol, t), p2 = pos(l[i], t); + printf("Line %d intersected at t = %.2lf " + "in (%.2lf, %.2lf, %.2lf) and (%.2lf, %.2lf, %.2lf)\n", + i, t, p1.x, p1.y, p1.z, p2.x, p2.y, p2.z); + if (t < eps || + !EQ(p1.x, p2.x) || !EQ(p1.y, p2.y) || !EQ(p1.z, p2.z)) { + printf("Error!\n"); + return; + } + } + printf("All lines intersected correctly, solution is valid\n"); +} + +int main() { + for (nlines = 0; (buf = fgets(line, N, stdin)) != NULL; nlines++) + l[nlines] = readl(buf); + + /* To figure the correct starting point + velocity, we solve some + * systems of linear equations. Write your starting point and velocity + * as unknowns x, y, z, Vx, Vy, Vz. Equating the position of the + * rock at time t1 (another unknown parameter) with the position + * of one of the hailstones at the same time t1, we get a system + * of 3 equations and 7 unknowns. Unfortunately, these equations + * have degree 2 - this is not a linear system! + * However, manipulating these equations a bit we can get a linear + * equation of the type: + * (Vy1-Vy2)x - (Vx1-Vx2)y + - (y1-y2)Vx + (x1-x2)Vy = + * y2Vx2 + x2Vy2 - y1Vx1 - x1Vy1 + * Where x1, y1, z2, Vx1, Vy1, Vz1 and x2, y2, z2, Vx2, Vy2, Vz2 + * are the starting points and velocities of two of the hailstones. + * So with 2 lines we can get a linear equation. Similarly, we can + * get equations involving the unknowns z and Vz. + * We can use the myriad of hailstones we have to generate as many + * equations as we like. The system is going to be overdetermined, + * but the problem statement seems to ensure that there is going to + * be a solution. On the other hand it can happen that we make a + * bad choice of lines and the equation we use are underdetermined. + * This last problem is not accounted for in the code - if it happens, + * one can shuffle the input file until it works. + */ + int d = 6; + double A[D][D] = { + /* First equation: lines 0 and 1, x and y only */ + {l[0].v.y-l[1].v.y, l[1].v.x-l[0].v.x, 0.0, + l[1].p.y-l[0].p.y, l[0].p.x-l[1].p.x, 0.0}, + /* Second equation: lines 0 and 2, x and y only */ + {l[0].v.y-l[2].v.y, l[2].v.x-l[0].v.x, 0.0, + l[2].p.y-l[0].p.y, l[0].p.x-l[2].p.x, 0.0}, + /* Third equation: lines 0 and 1, x and z only */ + {l[0].v.z-l[1].v.z, 0.0, l[1].v.x-l[0].v.x, + l[1].p.z-l[0].p.z, 0.0, l[0].p.x-l[1].p.x}, + /* Fourth equation: lines 0 and 2, x and z only */ + {l[0].v.z-l[2].v.z, 0.0, l[2].v.x-l[0].v.x, + l[2].p.z-l[0].p.z, 0.0, l[0].p.x-l[2].p.x}, + /* Fifth equation: lines 0 and 1, y and z only */ + {0.0, l[0].v.z-l[1].v.z, l[1].v.y-l[0].v.y, + 0.0, l[1].p.z-l[0].p.z, l[0].p.y-l[1].p.y}, + /* Sixth equation: lines 0 and 2, y and z only */ + {0.0, l[0].v.z-l[2].v.z, l[2].v.y-l[0].v.y, + 0.0, l[2].p.z-l[0].p.z, l[0].p.y-l[2].p.y}, + }; + double C[D] = { + /* First equation: lines 0 and 1, x and y only */ + l[1].p.y*l[1].v.x - l[1].p.x*l[1].v.y + - l[0].p.y*l[0].v.x + l[0].p.x*l[0].v.y, + /* Second equation: lines 0 and 2, x and y only */ + l[2].p.y*l[2].v.x - l[2].p.x*l[2].v.y + - l[0].p.y*l[0].v.x + l[0].p.x*l[0].v.y, + /* Third equation: lines 0 and 1, x and z only */ + l[1].p.z*l[1].v.x - l[1].p.x*l[1].v.z + - l[0].p.z*l[0].v.x + l[0].p.x*l[0].v.z, + /* Fourth equation: lines 0 and 2, x and z only */ + l[2].p.z*l[2].v.x - l[2].p.x*l[2].v.z + - l[0].p.z*l[0].v.x + l[0].p.x*l[0].v.z, + /* Fifth equation: lines 0 and 1, y and z only */ + l[1].p.z*l[1].v.y - l[1].p.y*l[1].v.z + - l[0].p.z*l[0].v.y + l[0].p.y*l[0].v.z, + /* Sixth equation: lines 0 and 2, y and z only */ + l[2].p.z*l[2].v.y - l[2].p.y*l[2].v.z + - l[0].p.z*l[0].v.y + l[0].p.y*l[0].v.z, + }; + double X[d]; + if (!solvesystem(A, C, d, X)) { + printf("No unique solution, shuffle input and try again.\n"); + exit(1); + } + + line_t sol = { + .p = {.x = X[0], .y = X[1], .z = X[2]}, + .v = {.x = X[3], .y = X[4], .z = X[5]} + }; + testsolution(sol); + + printf("p = (%.2lf, %.2lf, %.2lf), v = (%.2lf, %.2lf, %.2lf)\n", + sol.p.x, sol.p.y, sol.p.z, sol.v.x, sol.v.y, sol.v.z); + printf("%lf\n", sol.p.x + sol.p.y + sol.p.z); + return 0; +}