Compare commits
No commits in common. "5467ea9393aad6d2345b34a1ceb23d7ac34366c2" and "0b105c6396c832a6f6ea017218eb291cbc3c79dd" have entirely different histories.
5467ea9393
...
0b105c6396
|
@ -3,7 +3,7 @@
|
||||||
inherit (lib) splitString mod range;
|
inherit (lib) splitString mod range;
|
||||||
inherit (lib.lists) findFirstIndex imap0;
|
inherit (lib.lists) findFirstIndex imap0;
|
||||||
inherit (lib.strings) stringToCharacters;
|
inherit (lib.strings) stringToCharacters;
|
||||||
inherit (builtins) elemAt concatStringsSep length elem filter foldl' concatLists floor deepSeq listToAttrs attrValues attrNames mapAttrs hasAttr sort;
|
inherit (builtins) elemAt concatStringsSep length elem filter foldl' concatLists floor deepSeq listToAttrs attrValues attrNames mapAttrs hasAttr;
|
||||||
|
|
||||||
index = i: list: elemAt list i;
|
index = i: list: elemAt list i;
|
||||||
index2d = {x, y}: m: m |> index y |> index x;
|
index2d = {x, y}: m: m |> index y |> index x;
|
||||||
|
@ -52,18 +52,18 @@
|
||||||
in
|
in
|
||||||
if
|
if
|
||||||
index2d fwd init.chart == "#"
|
index2d fwd init.chart == "#"
|
||||||
then {pos = null; dist = null;}
|
then null
|
||||||
else
|
else
|
||||||
if (isCorner fwd || fwd == init.goal || fwd == init.pos)
|
if (isNode fwd || fwd == init.goal || fwd == init.pos)
|
||||||
then {
|
then {
|
||||||
pos = (key fwd);
|
name = (key fwd);
|
||||||
dist = score + 1;
|
value = score + 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
search {pos = fwd; inherit dir; score = score + 1;}
|
search {pos = fwd; inherit dir; score = score + 1;}
|
||||||
;
|
;
|
||||||
|
|
||||||
isCorner = pos: index2d pos init.chart == "." &&
|
isNode = pos: index2d pos init.chart == "." &&
|
||||||
(dirs
|
(dirs
|
||||||
|> mapAttrs (name: dir: let n = addVec pos dir; in index2d n init.chart == ".")
|
|> mapAttrs (name: dir: let n = addVec pos dir; in index2d n init.chart == ".")
|
||||||
|> ({north, east, south, west}:
|
|> ({north, east, south, west}:
|
||||||
|
@ -72,156 +72,126 @@
|
||||||
|
|
||||||
key = {x, y}: "${toString (y + 1)},${toString (x + 1)}";
|
key = {x, y}: "${toString (y + 1)},${toString (x + 1)}";
|
||||||
|
|
||||||
corners = range 1 (init.width - 3) |> map (x:
|
nodes = range 1 (init.width - 3) |> map (x:
|
||||||
range 1 (init.height - 3) |> map (y: {inherit x y;})
|
range 1 (init.height - 3) |> map (y: {inherit x y;})
|
||||||
)
|
)
|
||||||
|> concatLists
|
|> concatLists
|
||||||
|> filter isCorner
|
|> filter isNode
|
||||||
|> (nodes: nodes ++ [init.pos init.goal])
|
|> (nodes: nodes ++ [init.pos init.goal])
|
||||||
|> map (pos: {
|
|> map (pos: {
|
||||||
name = key pos;
|
name = key pos;
|
||||||
value = ({
|
value = {
|
||||||
north = search {inherit pos; dir = dirs.north;};
|
north = search {inherit pos; dir = dirs.north;};
|
||||||
east = search {inherit pos; dir = dirs.east;};
|
east = search {inherit pos; dir = dirs.east;};
|
||||||
south = search {inherit pos; dir = dirs.south;};
|
south = search {inherit pos; dir = dirs.south;};
|
||||||
west = search {inherit pos; dir = dirs.west;};
|
west = search {inherit pos; dir = dirs.west;};
|
||||||
} |> lib.filterAttrs (n: v: !isNull v.pos));
|
} |> lib.filterAttrs (n: v: !isNull v);
|
||||||
})
|
})
|
||||||
|> listToAttrs;
|
|> listToAttrs;
|
||||||
|
|
||||||
notNull = value: !isNull value;
|
graph = pkgs.runCommand "graph" {} ''
|
||||||
|
|
||||||
nodeGraph = graph: pkgs.runCommand "graph" {} ''
|
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
echo 'digraph {
|
echo 'digraph {
|
||||||
${graph |> mapAttrs (from: edges:
|
${nodes |> mapAttrs (from: tos:
|
||||||
edges |> mapAttrs (dir: edge: if isNull edge.pos or null then "" else ''
|
tos |> mapAttrs (dir: edge: if isNull edge then "" else ''
|
||||||
"${from}" -> "${edge.pos}" [label="${dir} ${toString edge.dist}"]
|
"${from}" -> "${edge.name}" [label="${dir} ${toString edge.value}"]
|
||||||
'') |> attrValues |> concatStringsSep "\n"
|
'') |> attrValues |> concatStringsSep "\n"
|
||||||
) |> attrValues |> concatStringsSep "\n"}
|
) |> attrValues |> concatStringsSep "\n"}
|
||||||
}' > $out/graph.dot
|
}' > $out/graph.dot
|
||||||
cat $out/graph.dot | ${pkgs.graphviz}/bin/dot -Tsvg > $out/graph.svg
|
cat $out/graph.dot | ${pkgs.graphviz}/bin/dot -Tsvg > $out/graph.svg
|
||||||
'';
|
'';
|
||||||
|
|
||||||
graphs = {
|
getPath = {
|
||||||
corners = nodeGraph corners;
|
pos ? key init.pos
|
||||||
};
|
, goal
|
||||||
|
, dir ? "east"
|
||||||
|
, score ? 0
|
||||||
|
, hist ? []
|
||||||
|
, acc ? {}
|
||||||
|
}: let
|
||||||
|
node = nodes.${pos};
|
||||||
|
in attrNames node |> foldl' (best: edgedir:
|
||||||
|
lib.traceSeq acc
|
||||||
|
(let
|
||||||
|
newHist = hist ++ [edge.name value];
|
||||||
|
edge = node.${edgedir};
|
||||||
|
turnCost = if edgedir == dir then 0 else 1000;
|
||||||
|
value = edge.value + turnCost;
|
||||||
|
newScore = score + value;
|
||||||
|
in
|
||||||
|
if isNull edge || elem edge.name hist || (hasAttr "score" best && newScore > best.score) then best else
|
||||||
|
if edge.name == goal && (hasAttr "score" best -> newScore < best.score) then
|
||||||
|
({score = newScore;})
|
||||||
|
else
|
||||||
|
getPath {
|
||||||
|
pos = edge.name;
|
||||||
|
dir = edgedir;
|
||||||
|
score = newScore;
|
||||||
|
hist = newHist;
|
||||||
|
acc = best;
|
||||||
|
inherit goal;
|
||||||
|
}
|
||||||
|
)) acc;
|
||||||
|
|
||||||
initScores = {
|
initScores = {
|
||||||
${key init.pos} = {
|
scores = mapAttrs (n: v:
|
||||||
dir = "east";
|
if n == key init.pos then {dir = "east"; score = 0; pos = n;}
|
||||||
steps = 0;
|
else {dir = null; score = null; pos = n;}
|
||||||
dist = 0;
|
) nodes;
|
||||||
turns = 0;
|
done = [];
|
||||||
pos = key init.pos;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
getLowest = state: let
|
getScores = state: let
|
||||||
unvisited = removeAttrs state.scores state.done;
|
unvisited = removeAttrs state.scores state.done;
|
||||||
|
top = attrValues unvisited |> foldl' (acc: node:
|
||||||
|
if isNull acc.score then node else
|
||||||
|
if isNull node.score then acc else
|
||||||
|
if acc.score < node.score then acc else node
|
||||||
|
) {score = null;};
|
||||||
in
|
in
|
||||||
attrValues unvisited |> foldl' (acc: node:
|
nodes.${top.pos}
|
||||||
if isNull acc.turns then node else
|
|> mapAttrs (dir: {name, value}: let
|
||||||
if isNull node.turns then acc else
|
turnCost = if top.dir == dir then 0 else 1000;
|
||||||
if acc.turns < node.turns then acc else node
|
existing = state.scores.${name};
|
||||||
) {turns = null; steps = null;}
|
score = top.score + value + turnCost;
|
||||||
;
|
isBetter = isNull existing.score || existing.score > score;
|
||||||
|
|
||||||
getScores = {done ? [], scores ? initScores}: let
|
|
||||||
prev = getLowest {inherit done scores;};
|
|
||||||
in
|
|
||||||
corners.${prev.pos}
|
|
||||||
|> mapAttrs (dir: edge: let
|
|
||||||
turns = if prev.dir == dir then prev.turns else prev.turns + 1;
|
|
||||||
existing = scores.${edge.pos} or null;
|
|
||||||
steps = prev.steps + edge.dist;
|
|
||||||
# can't evaluate same turns
|
|
||||||
# because we don't know where we will turn next.
|
|
||||||
sameSteps = notNull existing
|
|
||||||
&& existing.steps == steps
|
|
||||||
;
|
|
||||||
isBetter = isNull existing ||
|
|
||||||
(existing.steps > steps);
|
|
||||||
newScore = {
|
|
||||||
inherit steps dir turns;
|
|
||||||
inherit (edge) pos dist;
|
|
||||||
prev = prev.pos;
|
|
||||||
};
|
|
||||||
in {
|
in {
|
||||||
name = edge.pos;
|
inherit name;
|
||||||
value = if sameSteps then
|
value = if elem name state.done || !isBetter then existing else {
|
||||||
existing // {alt = newScore;}
|
inherit dir score;
|
||||||
else if isBetter
|
pos = name;
|
||||||
then newScore
|
};
|
||||||
else existing
|
|
||||||
;
|
|
||||||
})
|
})
|
||||||
|> attrValues
|
|> attrValues
|
||||||
|> listToAttrs
|
|> listToAttrs
|
||||||
|> (newScores: {
|
|> (newScores: {
|
||||||
scores = scores // newScores;
|
scores = state.scores // newScores;
|
||||||
done = done ++ [prev.pos];
|
done = state.done ++ [top.pos];
|
||||||
})
|
})
|
||||||
;
|
;
|
||||||
|
|
||||||
fastest = goal: s: let
|
getPriority = {scores, done}: scores
|
||||||
|
|> lib.attrsToList
|
||||||
|
|> filter ({name, ...}: !elem name done)
|
||||||
|
|> filter ({value, ...}: !isNull value.score)
|
||||||
|
|> sortQueue
|
||||||
|
;
|
||||||
|
|
||||||
|
lessThan = a: b: isNull a || isNull b || a < b;
|
||||||
|
|
||||||
|
sortQueue = q: q
|
||||||
|
|> builtins.sort (a: b: lessThan a.value.score b.value.score)
|
||||||
|
# |> (q: lib.traceSeq (map (v: v.value.score) q) q)
|
||||||
|
|> map ({name, value}: {
|
||||||
|
pos = name;
|
||||||
|
inherit (value) dir score;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
fastest = s: let
|
||||||
next = getScores s;
|
next = getScores s;
|
||||||
in if elem (key goal) (next.done) then next else fastest goal next;
|
in if elem (key init.goal) next.done then next else fastest next;
|
||||||
|
|
||||||
pathScore = steps: turns: steps + turns * 1000;
|
|
||||||
|
|
||||||
toGoal = (fastest init.goal {}).scores.${key init.goal};
|
|
||||||
|
|
||||||
part1result = pathScore toGoal.steps toGoal.turns;
|
|
||||||
|
|
||||||
# PART 2
|
|
||||||
|
|
||||||
# WIP:
|
|
||||||
# go through the path, add each pos to visited
|
|
||||||
# if point has alt,
|
|
||||||
# TODO: and the alt turns add up to the correct path.
|
|
||||||
# find points on that path.
|
|
||||||
# add length of filtered alt, (-1 for the overlap at end?).
|
|
||||||
# add alt to visited.
|
|
||||||
pathToList = scores: {pos ? key init.goal, visited ? []}:
|
|
||||||
let
|
|
||||||
res = scores.${pos};
|
|
||||||
prevPath = if res ? prev
|
|
||||||
then pathToList scores {pos = res.prev; visited = visited;}
|
|
||||||
else {path = []; visited = [pos];};
|
|
||||||
a = pathToList scores {pos = res.alt.prev; visited = visited ++ prevPath.visited;};
|
|
||||||
altPath = if res ? alt
|
|
||||||
then {path = [thisAltSpot] ++ a.path; inherit (a) visited;}
|
|
||||||
else {path = []; visited = [];}
|
|
||||||
;
|
|
||||||
|
|
||||||
thisSpot = {inherit (res) pos steps turns dir dist;
|
|
||||||
alt = altPath.path;
|
|
||||||
};
|
|
||||||
thisAltSpot = {inherit (res.alt) pos steps turns dir dist;
|
|
||||||
alt = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
in {
|
|
||||||
visited = [pos] ++ prevPath.visited ++ altPath.visited;
|
|
||||||
path =
|
|
||||||
if elem pos visited then [] else
|
|
||||||
[thisSpot]
|
|
||||||
++
|
|
||||||
prevPath.path
|
|
||||||
;
|
|
||||||
};
|
|
||||||
|
|
||||||
cornerScores = fastest init.goal {};
|
|
||||||
|
|
||||||
addDist = foldl' (acc: {dist,alt,dir,...}:
|
|
||||||
acc + dist + (if addDist alt == 1 then 0 else (addDist alt) - 2)
|
|
||||||
) 1;
|
|
||||||
|
|
||||||
part1path = (pathToList cornerScores.scores {});
|
|
||||||
|
|
||||||
# not 520, too high
|
|
||||||
part2resultWrong = addDist part1path.path;
|
|
||||||
|
|
||||||
|
part1result = (fastest initScores).scores.${key init.goal};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
5,4
|
|
||||||
4,2
|
|
||||||
4,5
|
|
||||||
3,0
|
|
||||||
2,1
|
|
||||||
6,3
|
|
||||||
2,4
|
|
||||||
1,5
|
|
||||||
0,6
|
|
||||||
3,3
|
|
||||||
2,6
|
|
||||||
5,1
|
|
||||||
1,2
|
|
||||||
5,5
|
|
||||||
2,5
|
|
||||||
6,5
|
|
||||||
1,4
|
|
||||||
0,4
|
|
||||||
6,4
|
|
||||||
1,1
|
|
||||||
6,1
|
|
||||||
1,0
|
|
||||||
0,5
|
|
||||||
1,6
|
|
||||||
2,0
|
|
|
@ -1,52 +0,0 @@
|
||||||
{lib, input ? "", my-lib, ...}@pkgs: rec {
|
|
||||||
inherit pkgs;
|
|
||||||
inherit lib;
|
|
||||||
inherit (builtins) elemAt genList elem concatStringsSep;
|
|
||||||
inherit (lib) splitString;
|
|
||||||
inherit (lib.strings) toIntBase10;
|
|
||||||
|
|
||||||
init = input: input |> lib.trim
|
|
||||||
|> splitString "\n"
|
|
||||||
|> map (line: let c = splitString "," line; in {
|
|
||||||
x = elemAt c 0 |> toIntBase10;
|
|
||||||
y = elemAt c 1 |> toIntBase10;
|
|
||||||
})
|
|
||||||
;
|
|
||||||
|
|
||||||
genChart = size: coords: genList (y: genList (x:
|
|
||||||
if x == 0 && y == 0 then "S" else
|
|
||||||
if x == size - 1 && y == size - 1 then "E" else
|
|
||||||
if elem {inherit x y;} coords then "#" else "."
|
|
||||||
) size) size;
|
|
||||||
|
|
||||||
chartToStr = chart: chart |> (map (concatStringsSep "")) |> concatStringsSep "\n";
|
|
||||||
|
|
||||||
mkSolution = {size, bytes, input}: rec {
|
|
||||||
inherit size bytes input;
|
|
||||||
walls = lib.sublist 0 bytes (init input);
|
|
||||||
chart = genChart size walls;
|
|
||||||
part1result = dijkstra.toGoal.steps;
|
|
||||||
dijkstra = my-lib.dijkstra {
|
|
||||||
pos = {x = 0; y = 0;};
|
|
||||||
goal = {x = size - 1; y = size - 1;};
|
|
||||||
chart = chart;
|
|
||||||
width = size;
|
|
||||||
height = size;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
example = mkSolution {
|
|
||||||
size = 7;
|
|
||||||
bytes = 12;
|
|
||||||
input = lib.readFile ./example.txt;
|
|
||||||
};
|
|
||||||
|
|
||||||
real = mkSolution {
|
|
||||||
size = 71;
|
|
||||||
bytes = 1024;
|
|
||||||
input = input;
|
|
||||||
};
|
|
||||||
|
|
||||||
# not 272, too high
|
|
||||||
|
|
||||||
}
|
|
|
@ -3,8 +3,8 @@
|
||||||
"aoc-inputs": {
|
"aoc-inputs": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1734543082,
|
"lastModified": 1734334657,
|
||||||
"narHash": "sha256-hU8vRkPOKfQqp34Ffx4INiz3l6HdwGcbfyHgqO4qheM=",
|
"narHash": "sha256-NjsEC/6Mu+i94YPgK4yN0Y5TxhlWwXK5Ev2UzIkfGZo=",
|
||||||
"path": "/tmp/aoc-inputs",
|
"path": "/tmp/aoc-inputs",
|
||||||
"type": "path"
|
"type": "path"
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,16 +29,12 @@
|
||||||
|> map (i: let id = lib.fixedWidthNumber 2 i; in {
|
|> map (i: let id = lib.fixedWidthNumber 2 i; in {
|
||||||
name = "day-${id}";
|
name = "day-${id}";
|
||||||
value = let
|
value = let
|
||||||
solution = import ./${id}/solution.nix ( pkgs // {
|
solution = import ./${id}/solution.nix pkgs;
|
||||||
my-lib = import ./lib/. pkgs;
|
|
||||||
inherit input;
|
|
||||||
} );
|
|
||||||
example = (pkgs.lib.readFile ./${id}/example.txt);
|
example = (pkgs.lib.readFile ./${id}/example.txt);
|
||||||
example2 = (pkgs.lib.readFile ./${id}/example2.txt);
|
example2 = (pkgs.lib.readFile ./${id}/example2.txt);
|
||||||
example3 = (pkgs.lib.readFile ./${id}/example3.txt);
|
example3 = (pkgs.lib.readFile ./${id}/example3.txt);
|
||||||
input = (pkgs.lib.readFile "${aoc-inputs}/${id}");
|
input = (pkgs.lib.readFile "${aoc-inputs}/${id}");
|
||||||
in {
|
in {
|
||||||
inherit solution;
|
|
||||||
example = solution example;
|
example = solution example;
|
||||||
example2 = solution example2;
|
example2 = solution example2;
|
||||||
example3 = solution example3;
|
example3 = solution example3;
|
||||||
|
|
BIN
2024/graph.jpg
Normal file
BIN
2024/graph.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 245 KiB |
|
@ -1,3 +0,0 @@
|
||||||
pkgs: {
|
|
||||||
dijkstra = import ./dijkstra.nix pkgs;
|
|
||||||
}
|
|
|
@ -1,212 +0,0 @@
|
||||||
{lib, pkgs, ...}: {pos, goal, chart, width, height}@init: rec {
|
|
||||||
inherit lib;
|
|
||||||
inherit (lib) splitString mod range;
|
|
||||||
inherit (lib.lists) findFirstIndex imap0;
|
|
||||||
inherit (lib.strings) stringToCharacters;
|
|
||||||
inherit (builtins) elemAt concatStringsSep length elem filter foldl' concatLists floor deepSeq listToAttrs attrValues attrNames mapAttrs hasAttr sort genList;
|
|
||||||
|
|
||||||
index = i: list: if isNull list || i < 0 || i >= length list then null else elemAt list i;
|
|
||||||
index2d = {x, y}: m: m |> index y |> index x;
|
|
||||||
|
|
||||||
chartToStr = chart: chart |> (map (concatStringsSep "")) |> concatStringsSep "\n";
|
|
||||||
|
|
||||||
rotate' = {x, y}: {y = -x; x = y;};
|
|
||||||
rotate = {x, y}: {y = x; x = -y;};
|
|
||||||
|
|
||||||
addVec = a: b: {x = a.x or 0 + b.x or 0; y = a.y or 0 + b.y or 0;};
|
|
||||||
multVec = a: m: {x = a.x or 0 * m; y = a.y or 0 * m;};
|
|
||||||
|
|
||||||
|
|
||||||
dirs = {
|
|
||||||
north = {x = 0; y = -1;};
|
|
||||||
east = {x = 1; y = 0;};
|
|
||||||
south = {x = 0; y = 1;};
|
|
||||||
west = {x = -1; y = 0;};
|
|
||||||
};
|
|
||||||
|
|
||||||
search = {
|
|
||||||
pos,
|
|
||||||
dir,
|
|
||||||
score ? 0,
|
|
||||||
}: let
|
|
||||||
fwd = addVec pos dir;
|
|
||||||
in
|
|
||||||
if
|
|
||||||
elem (index2d fwd init.chart) [null "#"]
|
|
||||||
then {pos = null; dist = null;}
|
|
||||||
else
|
|
||||||
if (isCorner fwd || fwd == init.goal || fwd == init.pos)
|
|
||||||
then {
|
|
||||||
pos = (key fwd);
|
|
||||||
dist = score + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
search {pos = fwd; inherit dir; score = score + 1;}
|
|
||||||
;
|
|
||||||
|
|
||||||
isCorner = pos: index2d pos init.chart == "." &&
|
|
||||||
(dirs
|
|
||||||
|> mapAttrs (name: dir: let n = addVec pos dir; in index2d n init.chart == ".")
|
|
||||||
|> ({north, east, south, west}:
|
|
||||||
north && east || east && south || south && west || west && north))
|
|
||||||
;
|
|
||||||
|
|
||||||
key = {x, y}: "${toString (y + 1)},${toString (x + 1)}";
|
|
||||||
|
|
||||||
corners = range 0 (init.width - 1) |> map (x:
|
|
||||||
range 0 (init.height - 1) |> map (y: {inherit x y;})
|
|
||||||
)
|
|
||||||
|> concatLists
|
|
||||||
|> filter isCorner
|
|
||||||
|> (nodes: nodes ++ [init.pos init.goal])
|
|
||||||
|> map (pos: {
|
|
||||||
name = key pos;
|
|
||||||
value = ({
|
|
||||||
north = search {inherit pos; dir = dirs.north;};
|
|
||||||
east = search {inherit pos; dir = dirs.east;};
|
|
||||||
south = search {inherit pos; dir = dirs.south;};
|
|
||||||
west = search {inherit pos; dir = dirs.west;};
|
|
||||||
} |> lib.filterAttrs (n: v: !isNull v.pos));
|
|
||||||
})
|
|
||||||
|> listToAttrs;
|
|
||||||
|
|
||||||
notNull = value: !isNull value;
|
|
||||||
|
|
||||||
nodeGraph = graph: pkgs.runCommand "graph" {} ''
|
|
||||||
mkdir -p $out
|
|
||||||
echo 'digraph {
|
|
||||||
${graph |> mapAttrs (from: edges:
|
|
||||||
edges |> mapAttrs (dir: edge: if isNull edge.pos or null then "" else ''
|
|
||||||
"${from}" -> "${edge.pos}" [label="${dir} ${toString edge.dist}"]
|
|
||||||
'') |> attrValues |> concatStringsSep "\n"
|
|
||||||
) |> attrValues |> concatStringsSep "\n"}
|
|
||||||
}' > $out/graph.dot
|
|
||||||
cat $out/graph.dot | ${pkgs.graphviz}/bin/dot -Tsvg > $out/graph.svg
|
|
||||||
'';
|
|
||||||
|
|
||||||
graphs = {
|
|
||||||
corners = nodeGraph corners;
|
|
||||||
};
|
|
||||||
|
|
||||||
initScores = {
|
|
||||||
${key init.pos} = {
|
|
||||||
dir = "east";
|
|
||||||
steps = 0;
|
|
||||||
dist = 0;
|
|
||||||
turns = 0;
|
|
||||||
pos = key init.pos;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
getLowest = state: let
|
|
||||||
unvisited = removeAttrs state.scores state.done;
|
|
||||||
in
|
|
||||||
attrValues unvisited |> foldl' (acc: node:
|
|
||||||
if isNull acc.steps then node else
|
|
||||||
if isNull node.steps then acc else
|
|
||||||
if acc.steps < node.steps then acc else node
|
|
||||||
) {turns = null; steps = null;}
|
|
||||||
;
|
|
||||||
|
|
||||||
getScores = {done ? [], scores ? initScores}: let
|
|
||||||
prev = getLowest {inherit done scores;};
|
|
||||||
in
|
|
||||||
corners.${prev.pos}
|
|
||||||
|> mapAttrs (dir: edge: let
|
|
||||||
turns = if prev.dir == dir then prev.turns else prev.turns + 1;
|
|
||||||
existing = scores.${edge.pos} or null;
|
|
||||||
steps = prev.steps + edge.dist;
|
|
||||||
# can't evaluate same turns
|
|
||||||
# because we don't know where we will turn next.
|
|
||||||
sameSteps = notNull existing
|
|
||||||
&& existing.steps == steps
|
|
||||||
;
|
|
||||||
isBetter = isNull existing ||
|
|
||||||
(existing.steps > steps);
|
|
||||||
newScore = {
|
|
||||||
inherit steps dir turns;
|
|
||||||
inherit (edge) pos dist;
|
|
||||||
prev = prev.pos;
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
name = edge.pos;
|
|
||||||
value = if sameSteps then
|
|
||||||
existing // {alt = newScore;}
|
|
||||||
else if isBetter
|
|
||||||
then newScore
|
|
||||||
else existing
|
|
||||||
;
|
|
||||||
})
|
|
||||||
|> attrValues
|
|
||||||
|> listToAttrs
|
|
||||||
|> (newScores: {
|
|
||||||
scores = scores // newScores;
|
|
||||||
done = done ++ [prev.pos];
|
|
||||||
})
|
|
||||||
;
|
|
||||||
|
|
||||||
fastest = goal: s: let
|
|
||||||
next = getScores s;
|
|
||||||
in if elem (key goal) (next.done) then next else fastest goal next;
|
|
||||||
|
|
||||||
pathScore = steps: turns: steps + turns * 1000;
|
|
||||||
|
|
||||||
toGoal = (fastest init.goal {}).scores.${key init.goal};
|
|
||||||
|
|
||||||
part1result = pathScore toGoal.steps toGoal.turns;
|
|
||||||
|
|
||||||
# PART 2
|
|
||||||
|
|
||||||
# WIP:
|
|
||||||
# go through the path, add each pos to visited
|
|
||||||
# if point has alt,
|
|
||||||
# TODO: and the alt turns add up to the correct path.
|
|
||||||
# find points on that path.
|
|
||||||
# add length of filtered alt, (-1 for the overlap at end?).
|
|
||||||
# add alt to visited.
|
|
||||||
pathToList = scores: {pos ? key init.goal, visited ? []}:
|
|
||||||
let
|
|
||||||
res = scores.${pos};
|
|
||||||
prevPath = if res ? prev
|
|
||||||
then pathToList scores {pos = res.prev; visited = visited;}
|
|
||||||
else {path = []; visited = [pos];};
|
|
||||||
a = pathToList scores {pos = res.alt.prev; visited = visited ++ prevPath.visited;};
|
|
||||||
altPath = if res ? alt
|
|
||||||
then {path = [thisAltSpot] ++ a.path; inherit (a) visited;}
|
|
||||||
else {path = []; visited = [];}
|
|
||||||
;
|
|
||||||
|
|
||||||
thisSpot = {inherit (res) pos steps turns dir dist;
|
|
||||||
# alt = altPath.path;
|
|
||||||
};
|
|
||||||
thisAltSpot = {inherit (res.alt) pos steps turns dir dist;
|
|
||||||
alt = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
in {
|
|
||||||
visited = [pos] ++ prevPath.visited ++ altPath.visited;
|
|
||||||
path =
|
|
||||||
if elem pos visited then [] else
|
|
||||||
[thisSpot]
|
|
||||||
++
|
|
||||||
prevPath.path
|
|
||||||
;
|
|
||||||
};
|
|
||||||
|
|
||||||
cornerScores = fastest init.goal {};
|
|
||||||
|
|
||||||
addDist = foldl' (acc: {dist,alt,dir,...}:
|
|
||||||
acc + dist + (if addDist alt == 1 then 0 else (addDist alt) - 2)
|
|
||||||
) 1;
|
|
||||||
|
|
||||||
part1path = (pathToList cornerScores.scores {});
|
|
||||||
|
|
||||||
# not 520, too high
|
|
||||||
part2resultWrong = addDist part1path.path;
|
|
||||||
|
|
||||||
genChart = size: walls: path: genList (y: genList (x:
|
|
||||||
if elem {inherit x y;} walls then "#"
|
|
||||||
else if elem (key {inherit x y;}) (map ({pos,...}: pos) path) then "O"
|
|
||||||
else "."
|
|
||||||
) size) size;
|
|
||||||
}
|
|
1
2024/result
Symbolic link
1
2024/result
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
/nix/store/3dxw74x2scq38j9m84r6m3iq320npwn4-graph
|
Loading…
Reference in a new issue