> Is anyone else doing AoC in postgres this year?
> https://adventofcode.com/2025/day/8
I am doing it, or at least chipping away a little but on the weekends. This
last weekend I got up to day 9. Most days I can solve with a single SQL
statement. Day 8 was not one of those, so I fell back to plpgsql.
> part 1 and 2 with a brute force loop, but there must be better ways.
What's so wrong with brute force? :) Day 8 seemed pretty straightforward:
split into x,y,z coordinates, calculate distances, then walk through in
distance order and create / merge groups (circuits) as you go.
In case it helps, here is my solution:
/* Greg Sabino Mullane for AOC 2025 Day 8 "Playground" */
/* Assumes data file is in /tmp/aoc_2026_day8.input */
\pset footer off
\set ON_ERROR_STOP on
\set QUIET on
SET client_min_messages = ERROR;
CREATE EXTENSION IF NOT EXISTS file_fdw SCHEMA public;
CREATE SERVER IF NOT EXISTS aoc2025 FOREIGN DATA WRAPPER file_fdw;
DROP SCHEMA IF EXISTS aoc_2025_8 CASCADE;
CREATE SCHEMA aoc_2025_8;
SET search_path = aoc_2025_8, public;
CREATE FOREIGN TABLE aoc_2025_day8 (line TEXT)
SERVER aoc2025 OPTIONS (filename '/tmp/aoc_2025_day8.input');
---------------------------
-- AOC 2025 DAY 8 PART 1 --
---------------------------
\timing on
create unlogged table t (box1 smallint, box2 smallint, d bigint, xs bigint);
WITH
aoc AS (select row_number() over() AS row,
split_part(line,',',1)::int AS x,
split_part(line,',',2)::int AS y,
split_part(line,',',3)::int AS z
from aoc_2025_day8)
, dist as (select a1.row as box1, a2.row as box2, a1.x::bigint *
a2.x::bigint AS xs,
(pow(a2.x-a1.x,2) + pow(a2.y-a1.y,2) + pow(a2.z-a1.z,2)) as d
from aoc a1 join aoc a2 on (a1.row < a2.row)
)
insert into t select box1, box2, d, xs from dist;
-- Best time: 289ms
CREATE FUNCTION connect_em(target int) returns text
LANGUAGE plpgsql
AS $$
DECLARE
k record;
cid int = 0;
loops int = 0;
circuit int[] = '{}';
ccount int[];
c1 int; c2 int;
solution1 text = '?'; solution2 text = '?';
BEGIN
/* Walk through each pair of boxes, closest ones first */
FOR k IN select * from t order by d asc LOOP
loops = loops + 1;
/* For the first part, sum up the three largest circuits */
IF loops = target then
SELECT INTO solution1 exp(sum(ln(q)))::int AS a FROM (select q FROM
(SELECT unnest(ccount) q) order by q desc limit 3);
END IF;
c1 = circuit[k.box1]; c2 = circuit[k.box2];
/* If neither box is part of an existing circuit, assign them to a new
one */
IF c1 IS NULL and c2 IS NULL THEN
cid = cid + 1;
circuit[k.box1] = cid;
circuit[k.box2] = cid;
ccount[cid] = 2;
raise debug ' Created circuit #% with boxes % and %', cid, k.box1,
k.box2;
continue;
END IF;
/* If only box1 is part of an existing circuit, add box2 */
IF c1 IS NOT NULL and c2 IS NULL THEN
circuit[k.box2] = c1;
ccount[c1] = ccount[c1] + 1;
raise debug ' Moved second box % to circuit #%, used by box %',
k.box2, c1, k.box1;
END IF;
/* If only box2 is part of an existing circuit, add box1 */
IF c1 IS NULL and c2 IS NOT NULL THEN
circuit[k.box1] = c2;
ccount[c2] = ccount[c2] + 1;
raise debug ' Moved first box % to circuit #% , used by box %',
k.box1, c2, k.box2;
END IF;
/* Both boxes are already part of a circuit, so merge or ignore */
IF c1 IS NOT NULL and c2 IS NOT NULL THEN
IF c1 = c2 THEN
raise debug ' Skip, as both boxes already belong to the same
circuit';
continue;
END IF;
/* Move anything in the old circuit to the new one */
for x in array_lower(circuit,1) .. array_upper(circuit,1) loop
if circuit[x] = c2 then
circuit[x] = c1;
ccount[c2] = ccount[c2] - 1;
ccount[c1] = ccount[c1] + 1;
end if;
end loop;
raise debug ' Merge box % circuit #% (now at %) into box % circuit
#% (now at %)',
k.box2, c2, ccount[c2], k.box1, c1, ccount[c1];
END IF;
/* We avoided using CONTINUE above just to make this check */
if c1 is null then c1 = c2; end if;
IF ccount[c1] = target THEN
solution2 = k.xs;
exit;
END IF;
END LOOP;
RETURN format('Solution 1: %s Solution 2: %s', solution1, solution2);
END
$$;
-- SET client_min_messages = DEBUG1;
SELECT connect_em(1000);
-- Best time: 126 ms
---------------------------
-- AOC 2025 DAY 8 PART 2 --
---------------------------
-- Same function as above
-- Best overall time: 451ms
On Mon, Dec 8, 2025 at 10:36 AM Bernice Southey <[email protected]>
wrote:
> Hi,
>
> Is anyone else doing AoC in postgres this year? I've solved today's
> part 1 and 2 with a brute force loop, but there must be better ways.
> If anyone found something clever in postgres, please give me a big
> hint.
> https://adventofcode.com/2025/day/8
>
> Thanks, Bernice
>
>
>
Cheers,
Greg
--
Crunchy Data - https://www.crunchydata.com
Enterprise Postgres Software Products & Tech Support