diff --git a/2022/d9/.gitignore b/2022/d9/.gitignore new file mode 100644 index 0000000..bd32e74 --- /dev/null +++ b/2022/d9/.gitignore @@ -0,0 +1,2 @@ +.direnv +target diff --git a/2022/d9/Cargo.lock b/2022/d9/Cargo.lock new file mode 100644 index 0000000..0235225 --- /dev/null +++ b/2022/d9/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "d9" +version = "0.1.0" diff --git a/2022/d9/Cargo.toml b/2022/d9/Cargo.toml new file mode 100644 index 0000000..20ff77d --- /dev/null +++ b/2022/d9/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "d9" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/2022/d9/aoc/Cargo.lock b/2022/d9/aoc/Cargo.lock new file mode 100644 index 0000000..a5b53ef --- /dev/null +++ b/2022/d9/aoc/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aoc" +version = "0.1.0" diff --git a/2022/d9/aoc/Cargo.toml b/2022/d9/aoc/Cargo.toml new file mode 100644 index 0000000..706d2e1 --- /dev/null +++ b/2022/d9/aoc/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "aoc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/2022/d9/aoc/src/main.rs b/2022/d9/aoc/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/2022/d9/aoc/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/2022/d9/flake.lock b/2022/d9/flake.lock index a62a638..5724db4 100644 --- a/2022/d9/flake.lock +++ b/2022/d9/flake.lock @@ -15,6 +15,21 @@ "type": "github" } }, + "flake-utils_2": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1670064435, @@ -31,10 +46,46 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1665296151, + "narHash": "sha256-uOB0oxqxN9K7XGF1hcnY+PQnlQJ+3bP2vCn/+Ru/bbc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "14ccaaedd95a488dd7ae142757884d8e125b3363", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1670552927, + "narHash": "sha256-lCE51eAGrAFS4k9W5aDGFpVtOAwQQ/rFMN80PCDh0vo=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "a0fdafd18c9cf599fde17fbaf07dbb20fa57eecb", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" } } }, diff --git a/2022/d9/flake.nix b/2022/d9/flake.nix index a0f5e43..dc3876a 100644 --- a/2022/d9/flake.nix +++ b/2022/d9/flake.nix @@ -3,26 +3,40 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; + rust-overlay.url = "github:oxalica/rust-overlay"; }; - outputs = { nixpkgs, flake-utils, ... } @ inputs: + outputs = { nixpkgs, flake-utils, rust-overlay, ... } @ inputs: flake-utils.lib.eachSystem flake-utils.lib.defaultSystems (sys: let - overlays = [ ]; + overlays = [ rust-overlay.overlays.default ]; pkgs = import nixpkgs { system = sys; overlays = overlays; }; shellHookAfter = '' echo "The input files should be placed under ./data/{submission,example}.txt" echo "This problem shares one input between two parts" ''; py_pkgs = [ pkgs.python310 ]; + rs_pkgs = [ + pkgs.openssl + pkgs.pkg-config + + # Add rust-src, which rust-analyzer seems to rely upon + (pkgs.rust-bin.selectLatestNightlyWith + ( + toolchain: + toolchain.default.override { + extensions = [ "rust-src" ]; + } + )) + ]; in { # Jack of all trades devShell = pkgs.mkShell { - nativeBuildInputs = py_pkgs ++ ocaml_pkgs; + buildInputs = py_pkgs ++ rs_pkgs; shellHook = '' - echo "> Default runtime. This contains both ocaml and python3 env" - echo "Run ./run-py.sh for Python's output and ./run-oml.sh for OCaml's output" + echo "> Default runtime. This contains both rust and python3 env" + echo "Run ./run-py.sh for Python's output and ./run-rs.sh for Rust's output" '' + shellHookAfter; }; devShells = { @@ -38,6 +52,13 @@ # nix develop ./#fennel # nix develop ./#python + rust = pkgs.mkShell { + nativeBuildInputs = rs_pkgs; + shellHook = '' + echo "> Rust runtime" + echo "Run ./run-rs.sh to see output of the solution" + '' + shellHookAfter; + }; python = pkgs.mkShell { nativeBuildInputs = py_pkgs; shellHook = '' diff --git a/2022/d9/src/d9.py b/2022/d9/src/d9.py index 4d33bc0..c6de296 100644 --- a/2022/d9/src/d9.py +++ b/2022/d9/src/d9.py @@ -1,18 +1,18 @@ #!/usr/bin/env python3 import sys -from typing import Iterable, Generator -from functools import reduce -from itertools import product +from typing import Iterable def add_nd(lhs: Iterable[int], rhs: Iterable[int]): + # Add two generic n-d vector together return tuple(l + r for l, r in zip(lhs, rhs)) def negate_nd(vec: Iterable[int]): + # Negates a generic n-d vector return tuple(-e for e in vec) -def norm_1k_nd(vec: Iterable[int]): - return sum(abs(e) for e in vec) def norm_infk_nd(vec: Iterable[int]): + # Mathematical concept where I get the length of the longest vector component return max(abs(e) for e in vec) - +def sign(e: int): + return 0 if e == 0 else (1 if e > 0 else -1) def dir_to_offset(dir: str): match dir: @@ -26,8 +26,6 @@ def dir_to_offset(dir: str): return (1, 0) case d: raise RuntimeError(f"Unknown direction {d}") -def sign(e: int): - return 0 if e == 0 else (1 if e > 0 else -1) def move_knot(knot: list[tuple[int, int]], offset: Iterable[int]): knot.append(add_nd(knot[-1], offset)) @@ -35,50 +33,44 @@ def move_knot(knot: list[tuple[int, int]], offset: Iterable[int]): def part1(commands: list[tuple[str, int]]): tail_locs: list[tuple[int, int]] = [(0,0)] # keeps track of where tail moved + # NOTE: I don't need to keep track of head's history here. This is only + # for ease of debugging head_locs : list[tuple[int, int]] = [(0,0)] # keeps track of where head moved for dir, rep in commands: for _ in range(rep): move_knot(head_locs, dir_to_offset(dir)) offset = add_nd(head_locs[-1], negate_nd(tail_locs[-1])) # print(f"{offset=}") - match norm_1k_nd(offset), norm_infk_nd(offset): - case (0, _) | (1, _) | (2, 1): - pass # don't need to catchup - case (_, _): - # negate & normalize in inf_k - move_knot(tail_locs, (sign(e) for e in offset)) - case e: - raise RuntimeError(f"Unknown case {e}\n{tail_locs=}\n{head_locs=}") + if norm_infk_nd(offset) <= 1: + continue # no need to catch up + move_knot(tail_locs, (sign(e) for e in offset)) # print(f"{tail_locs=}") # print(f"{head_locs=}") - return len(set(tail_locs)) def part2(commands: list[tuple[str, int]]): KNOTS = 10 + # NOTE: I don't need to keep track of any history but tail's. This is only + # for ease of debugging locs = [[(0,0)] for _ in range(KNOTS)] # 10 knots :) for dir, rep in commands: for _ in range(rep): move_knot(locs[0], dir_to_offset(dir)) # move head for i in range(1, KNOTS): offset = add_nd(locs[i-1][-1], negate_nd(locs[i][-1])) - match norm_1k_nd(offset), norm_infk_nd(offset): - case (0, _) | (1, _) | (2, 1): - pass # don't need to catchup - case (_, _): - # negate & normalize in inf_k - move_knot(locs[i], (sign(e) for e in offset)) - case e: - raise RuntimeError(f"Unknown case {e}\n{locs[i]=}\n{locs[i-1]=}") + if norm_infk_nd(offset) <= 1: + continue # no need to catch up + move_knot(locs[i], (sign(e) for e in offset)) return len(set(locs[-1])) def main(lines: Iterable[str]): splited = (line.strip().split() for line in lines) - striped_lines = [(comp[0], int(comp[1])) for comp in splited if len(comp) > 0] - # print(striped_lines) - commands = [(comp[0], int(comp[1])) for comp in striped_lines] + # -> [" D 4 ",...] -> [["D", "4"], ...] + ## Filters out any line that has empty newline and turn repitions into int + commands = [(comp[0], int(comp[1])) for comp in splited if len(comp) > 0] + # -> [[], ["D", "4"], ...] -> [["D", 4]] print("part1", part1(commands)) print("part2", part2(commands)) pass @@ -86,3 +78,4 @@ def main(lines: Iterable[str]): if __name__=="__main__": with open(sys.argv[1], "r") as f: main(f) + diff --git a/2022/d9/src/main.rs b/2022/d9/src/main.rs new file mode 100644 index 0000000..dfafdbc --- /dev/null +++ b/2022/d9/src/main.rs @@ -0,0 +1,6 @@ +fn main() { + // let test = include_str!("../data/example.txt"); + let sub = inlcude_str!("../data/submission.txt"); + let commands = sub.lines() + .map(|line| line.split(" ")) +}