diff --git a/2024/16/example.txt b/2024/16/example.txt new file mode 100644 index 0000000..2c21676 --- /dev/null +++ b/2024/16/example.txt @@ -0,0 +1,15 @@ +############### +#.......#....E# +#.#.###.#.###.# +#.....#.#...#.# +#.###.#####.#.# +#.#.#.......#.# +#.#.#####.###.# +#...........#.# +###.#.#####.#.# +#...#.....#.#.# +#.#.#.###.#.#.# +#.....#...#.#.# +#.###.#.#.#.#.# +#S..#.....#...# +############### diff --git a/2024/16/example2.txt b/2024/16/example2.txt new file mode 100644 index 0000000..bc61c57 --- /dev/null +++ b/2024/16/example2.txt @@ -0,0 +1,17 @@ +################# +#...#...#...#..E# +#.#.#.#.#.#.#.#.# +#.#.#.#...#...#.# +#.#.#.#.###.#.#.# +#...#.#.#.....#.# +#.#.#.#.#.#####.# +#.#...#.#.#.....# +#.#.#####.#.###.# +#.#.#.......#...# +#.#.###.#####.### +#.#.#...#.....#.# +#.#.#.#####.###.# +#.#.#.........#.# +#.#.#.#########.# +#S#.............# +################# diff --git a/2024/16/solution.js b/2024/16/solution.js new file mode 100644 index 0000000..b97ba74 --- /dev/null +++ b/2024/16/solution.js @@ -0,0 +1,39 @@ +const input = (await Bun.file(Bun.argv[2]).text()).trim() + +const map = input.split("\n") + +const rotateL = ({x,y}) => ({x: -y, y: x,}); +const rotateR = ({x,y}) => ({x: y, y: -x,}); +const add = (a, b) => ({x: a.x + b.x, y: a.y + b.y}); + +const height = map.length; +const width = height; + +const key = ({x,y}, dir) => (dir.x + (dir.y * 2)) * (width * height) + x + y*width; +const visited = new Set(); + +let end, pos; +for (let y = 0; y < map.length; y++) { + const endx = map[y].indexOf("E") + if (endx !== -1) {end = {x: endx, y}} + const startx = map[y].indexOf("S") + if (startx !== -1) {pos = {x: startx, y}} +} + +let dir = {x: 1, y: 0}; +console.log(end, pos) + +function search(pos, dir) { + if (visited.has(key(pos, dir))) { + return {area: 0, perimeter: 0} + } + if (map[y][x] === "#") { + return null + } + if (map[y][x] === "E") { + return 0 + } + search(add(pos,dir), dir) +} + + diff --git a/2024/16/solution.nix b/2024/16/solution.nix new file mode 100644 index 0000000..452760f --- /dev/null +++ b/2024/16/solution.nix @@ -0,0 +1,197 @@ +{lib, pkgs, ...}: input: 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; + + index = i: list: elemAt list i; + index2d = {x, y}: m: m |> index y |> index x; + + init = let + chart = input |> splitString "\n" |> map stringToCharacters; + width = (elemAt chart 0 |> length) + 1; + height = length chart; + startIndex = input |> stringToCharacters |> findFirstIndex (char: char == "S") null; + endIndex = input |> stringToCharacters |> findFirstIndex (char: char == "E") null; + in { + inherit chart width height; + pos = { + x = mod startIndex width; + y = startIndex / width; + }; + goal = { + x = mod endIndex width; + y = endIndex / width; + }; + } + ; + + 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 + index2d fwd init.chart == "#" + then null + else + if (isNode fwd || fwd == init.goal || fwd == init.pos) + then { + name = (key fwd); + value = score + 1; + } + else + search {pos = fwd; inherit dir; score = score + 1;} + ; + + isNode = 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)}"; + + nodes = range 1 (init.width - 3) |> map (x: + range 1 (init.height - 3) |> map (y: {inherit x y;}) + ) + |> concatLists + |> filter isNode + |> (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); + }) + |> listToAttrs; + + graph = pkgs.runCommand "graph" {} '' + mkdir -p $out + echo 'digraph { + ${nodes |> mapAttrs (from: tos: + tos |> mapAttrs (dir: edge: if isNull edge then "" else '' + "${from}" -> "${edge.name}" [label="${dir} ${toString edge.value}"] + '') |> attrValues |> concatStringsSep "\n" + ) |> attrValues |> concatStringsSep "\n"} + }' > $out/graph.dot + cat $out/graph.dot | ${pkgs.graphviz}/bin/dot -Tsvg > $out/graph.svg + ''; + + getPath = { + 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 = { + scores = mapAttrs (n: v: + if n == key init.pos then {dir = "east"; score = 0; pos = n;} + else {dir = null; score = null; pos = n;} + ) nodes; + done = []; + }; + + getScores = state: let + 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 + nodes.${top.pos} + |> mapAttrs (dir: {name, value}: let + turnCost = if top.dir == dir then 0 else 1000; + existing = state.scores.${name}; + score = top.score + value + turnCost; + isBetter = isNull existing.score || existing.score > score; + in { + inherit name; + value = if elem name state.done || !isBetter then existing else { + inherit dir score; + pos = name; + }; + }) + |> attrValues + |> listToAttrs + |> (newScores: { + scores = state.scores // newScores; + done = state.done ++ [top.pos]; + }) + ; + + 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; + in if elem (key init.goal) next.done then next else fastest next; + + part1result = (fastest initScores).scores.${key init.goal}; + +} diff --git a/2024/flake.lock b/2024/flake.lock index dd3f27c..fa45999 100644 --- a/2024/flake.lock +++ b/2024/flake.lock @@ -3,8 +3,8 @@ "aoc-inputs": { "flake": false, "locked": { - "lastModified": 1734266579, - "narHash": "sha256-fhPoaCWITp2KZtdxm+D9e6GiWf+c3/RGJeiq03/ynfY=", + "lastModified": 1734334657, + "narHash": "sha256-NjsEC/6Mu+i94YPgK4yN0Y5TxhlWwXK5Ev2UzIkfGZo=", "path": "/tmp/aoc-inputs", "type": "path" }, diff --git a/2024/flake.nix b/2024/flake.nix index ac43fc8..67dfb0b 100644 --- a/2024/flake.nix +++ b/2024/flake.nix @@ -25,16 +25,18 @@ inherit (pkgs) lib; - in (lib.range 1 15 + in (lib.range 1 25 |> map (i: let id = lib.fixedWidthNumber 2 i; in { name = "day-${id}"; value = let solution = import ./${id}/solution.nix pkgs; example = (pkgs.lib.readFile ./${id}/example.txt); + example2 = (pkgs.lib.readFile ./${id}/example2.txt); example3 = (pkgs.lib.readFile ./${id}/example3.txt); input = (pkgs.lib.readFile "${aoc-inputs}/${id}"); in { example = solution example; + example2 = solution example2; example3 = solution example3; real = solution input; test = tix.run [ diff --git a/2024/graph.jpg b/2024/graph.jpg new file mode 100644 index 0000000..86e215e Binary files /dev/null and b/2024/graph.jpg differ diff --git a/2024/result b/2024/result new file mode 120000 index 0000000..d03bff3 --- /dev/null +++ b/2024/result @@ -0,0 +1 @@ +/nix/store/3dxw74x2scq38j9m84r6m3iq320npwn4-graph \ No newline at end of file