Best of days, worst of days!

Success

My solution ended up totally generic between part 1 and part 2, I just needed to add a fact expansionFactor is N, where N is 2 for part 1 and 1000000 for part 2, as well as a fact charAt X Y is "#" or charAt X Y is "." depending on what the character is.

Dusa’s default reasoning is great for saying which rows and columns have stars or not:

**hasStar y Y is { false? } :- charAt _ Y is _.
hasStar y Y is true :- charAt _ Y is "#".
hasStar x X is { false? } :- charAt X _ is _.
hasStar x X is true :- charAt X _ is "#".**

Then we remap the old X and Y coordinates to new X and Y coordinates based on whether rows are empty and full.

**needsRemap x 0 0.
needsRemap y 0 0.
remap Axis Old is (minus (plus New expansionFactor) 1) :- 
    needsRemap Axis Old New,
    hasStar Axis Old is false.
remap Axis Old is New :-
    needsRemap Axis Old New,
    hasStar Axis Old is true.
needsRemap Axis (s Old) (s New) :-
    remap Axis Old is New.**

Stars are just remapped chars.

**starAt (remap x X) (remap y Y) :-
    charAt X Y is "#".**

Failure: the impractical solution

Initially I calculated the distance between pairs of stars in Dusa and then added them up in Dusa, using a lexicographic ordering to avoid double-counting stars in both directions

Here’s the program on val.town

This was the first time I got OOM errors from Dusa! Remembering all the different ways of comparing all n^2 star pairs was too much.

Just fine: the practical solution

This is probably the most logic I’ve put in JavaScript yet: there’s no need to keep all the pairs of stars in memory, so we can just run the accumulator in JavaScript:

function bigabs(x) {
  return x < 0 ? -x : x;
}

for (const expansionFactor of [2, 10, 100, 1000000]) {
  const dusa = new Dusa(PROGRAM);

  dusa.load({ map: INPUT, width: INPUT[0].length, expansionFactor }, "field");
  const solution = dusa.sample();
  const stars = [...solution.lookup("starAt")];
  let accum = 0n;
  for (const [index, [x1, y1]] of stars.entries()) {
    for (const [x2, y2] of stars.slice(index + 1)) {
      accum += bigabs(x1 - x2) + bigabs(y1 - y2);
    }
  }

  console.log(accum);
}

Here’s the program on val.town

Notes

Definitely the first day I got OOM errors!