[rust] Solve Day 1

main
Akshay Mankar 2023-12-01 21:14:54 +01:00
parent e7206e5aa2
commit 48bdc183ef
Signed by: axeman
GPG Key ID: CA08F3AB62369B89
8 changed files with 216 additions and 27 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.direnv
dist-newstyle
target

7
Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aoc2023"
version = "0.1.0"

3
Cargo.toml Normal file
View File

@ -0,0 +1,3 @@
[workspace]
members = ["rust"]
resolver = "2"

View File

@ -24,3 +24,10 @@ Run it like this:
./bash/main.sh day<n> < ./input/day<n>
#+END_SRC
* Rust
Run it like this:
#+BEGIN_SRC bash
cargo run -- day<n> < ./input/day<n>
#+END_SRC

View File

@ -8,28 +8,8 @@
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
# Avoids unnecessary recompiles
filteredSource = pkgs.lib.cleanSourceWith {
src = ./haskell;
filter = path: type:
let baseName = baseNameOf (toString path);
in pkgs.lib.cleanSourceFilter path type && !(
baseName == "flake.nix" ||
baseName == "flake.lock" ||
baseName == "dist-newstyle" ||
builtins.match "^cabal\.project\..*$" baseName != null ||
baseName == "hls.sh" ||
baseName == ".envrc" ||
baseName == "hie.yaml" ||
baseName == ".hlint.yaml" ||
baseName == ".hspec" ||
baseName == "ci"
);
};
ghcOverrides = hself: hsuper: rec {
aoc2023 = pkgs.haskell.lib.overrideSrc (hsuper.callPackage ./haskell/default.nix {}) {
src = filteredSource;
};
aoc2023 = hsuper.callPackage ./haskell/default.nix {};
};
ghc94Pkgs = pkgs.haskell.packages.ghc94.override {
overrides = ghcOverrides;
@ -44,12 +24,19 @@
(pkgs.haskell-language-server.override {supportedGhcVersions = ["948"];})
pkgs.cabal2nix
pkgs.ormolu
pkgs.shellcheck
pkgs.rustc
pkgs.cargo
pkgs.rustfmt
pkgs.rust-analyzer
pkgs.clippy
];
};
aoc2023-haskell = ghc94Pkgs.aoc2023;
aoc2023-bash =
let bashDir = pkgs.runCommand "aoc2023-bash" {} ''
let bashDir = pkgs.runCommandLocal "aoc2023-bash" {} ''
mkdir $out
cp -r ${./bash} $out/bin
chmod -R +w $out
@ -62,24 +49,38 @@
${bashDir}/bin/main.sh "''${@}"
'';
};
aoc2023-rust = pkgs.rustPlatform.buildRustPackage {
name = "aoc2023";
src = pkgs.lib.cleanSourceWith {
src = ./.;
filter = path: type:
let baseName = baseNameOf (toString path);
in pkgs.lib.cleanSourceFilter path type &&
(pkgs.lib.strings.hasInfix "rust" path ||
baseName == "Cargo.toml" ||
baseName == "Cargo.lock");
};
cargoSha256 = "sha256-e27WE9K2yby+6vzu0cDojtG0+aTdED7vt7wFgEd2dAw=";
};
};
checks =
let mkCheck = (prefix: exec: day: parts: {
name = "${prefix}-${day}";
let mkCheck = (lang: exec: day: parts: {
name = "${lang}-${day}";
value = pkgs.testers.testEqualContents {
assertion = day;
assertion = "${lang}: ${day}";
expected = pkgs.writeText "expected" ''
Part 1 Answer: ${parts.part1}
Part 2 Answer: ${parts.part2}
'';
actual = pkgs.runCommand "actual" {} ''
actual = pkgs.runCommandLocal "actual" {} ''
"${exec}" "${day}" < "${./input}/${day}" >$out
'';
};
});
in pkgs.lib.attrsets.mapAttrs' (mkCheck "haskell" "${packages.aoc2023-haskell}/bin/aoc2023") answers
// pkgs.lib.attrsets.mapAttrs' (mkCheck "bash" "${packages.aoc2023-bash}/bin/aoc2023") answers;
// pkgs.lib.attrsets.mapAttrs' (mkCheck "bash" "${packages.aoc2023-bash}/bin/aoc2023") answers
// pkgs.lib.attrsets.mapAttrs' (mkCheck "rust" "${packages.aoc2023-rust}/bin/aoc2023") answers;
defaultPackage = packages.dev-env;
});
}

8
rust/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "aoc2023"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

146
rust/src/day1.rs Normal file
View File

@ -0,0 +1,146 @@
use std::io;
use std::io::Read;
use std::cmp::Ordering;
pub fn run() {
let mut lines : Vec<u8> = Vec::new();
let read_result = io::stdin().read_to_end(&mut lines);
match read_result {
Err(e) => println!("Error while reading input: {}", e),
Ok(_) => {
match part1(lines.clone()) {
Some(s) => println!("Part 1 Answer: {}", s),
None => println!("Part 1 Answer: failed to compute")
};
match part2(lines) {
Some(s) => println!("Part 2 Answer: {}", s),
None => println!("Part 2 Answer: failed to compute")
}
}
}
}
fn part1(lines: Vec<u8>) -> Option<u32> {
general_solution(lines, first_digit, last_digit)
}
fn part2(lines: Vec<u8>) -> Option<u32> {
general_solution(lines, first_digit_with_creativity, last_digit_with_creativity)
}
fn general_solution(data: Vec<u8>, parse_tens: fn(&str) -> Option<u32>,parse_ones: fn(&str) -> Option<u32>) -> Option<u32> {
// Too hard to carry around the Result. Using `ok()` to convert it to
// `Option` doesn't work for some reason.
let text = String::from_utf8(data).unwrap();
let lines = text.lines();
let calibration_values : Option<Vec<u32>> =
lines.map(|line| {
let tens = parse_tens(line);
let ones = parse_ones(line);
tens.and_then(
|t| ones.and_then(
|o| Some(t * 10 + o)
)
)
}).collect();
calibration_values.and_then(|x| Some(x.into_iter().sum()))
}
fn first_digit(s: &str) -> Option<u32> {
for c in s.chars() {
if c.is_digit(10) {
return c.to_digit(10);
}
}
None
}
fn last_digit(s: &str) -> Option<u32> {
for c in s.chars().rev() {
if c.is_digit(10) {
return c.to_digit(10);
}
}
None
}
struct SearchData {
value : u32,
position : Option<usize>,
}
fn lower_search_data(d1: &SearchData, d2: &SearchData) -> Ordering {
match (d1.position, d2.position) {
(None, None) => Ordering::Equal,
(Some(_), None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater,
(Some(x), Some(y)) => x.cmp(&y)
}
}
fn greater_search_data(d1: &SearchData, d2: &SearchData) -> Ordering {
match (d1.position, d2.position) {
(None, None) => Ordering::Equal,
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(Some(x), Some(y)) => x.cmp(&y)
}
}
fn first_digit_with_creativity(s: &str) -> Option<u32> {
let search_results = [ SearchData{ value: 0, position: s.find("0")},
SearchData{ value: 1, position: s.find("1")},
SearchData{ value: 2, position: s.find("2")},
SearchData{ value: 3, position: s.find("3")},
SearchData{ value: 4, position: s.find("4")},
SearchData{ value: 5, position: s.find("5")},
SearchData{ value: 6, position: s.find("6")},
SearchData{ value: 7, position: s.find("7")},
SearchData{ value: 8, position: s.find("8")},
SearchData{ value: 9, position: s.find("9")},
SearchData{ value: 0, position: s.find("zero")},
SearchData{ value: 1, position: s.find("one")},
SearchData{ value: 2, position: s.find("two")},
SearchData{ value: 3, position: s.find("three")},
SearchData{ value: 4, position: s.find("four")},
SearchData{ value: 5, position: s.find("five")},
SearchData{ value: 6, position: s.find("six")},
SearchData{ value: 7, position: s.find("seven")},
SearchData{ value: 8, position: s.find("eight")},
SearchData{ value: 9, position: s.find("nine")}
];
search_results
.into_iter()
.min_by(lower_search_data)
.and_then(|x| x.position.and_then(|_| Some(x.value)))
}
fn last_digit_with_creativity(s: &str) -> Option<u32> {
let search_results = [ SearchData{ value: 0, position: s.rfind("0")},
SearchData{ value: 1, position: s.rfind("1")},
SearchData{ value: 2, position: s.rfind("2")},
SearchData{ value: 3, position: s.rfind("3")},
SearchData{ value: 4, position: s.rfind("4")},
SearchData{ value: 5, position: s.rfind("5")},
SearchData{ value: 6, position: s.rfind("6")},
SearchData{ value: 7, position: s.rfind("7")},
SearchData{ value: 8, position: s.rfind("8")},
SearchData{ value: 9, position: s.rfind("9")},
SearchData{ value: 0, position: s.rfind("zero")},
SearchData{ value: 1, position: s.rfind("one")},
SearchData{ value: 2, position: s.rfind("two")},
SearchData{ value: 3, position: s.rfind("three")},
SearchData{ value: 4, position: s.rfind("four")},
SearchData{ value: 5, position: s.rfind("five")},
SearchData{ value: 6, position: s.rfind("six")},
SearchData{ value: 7, position: s.rfind("seven")},
SearchData{ value: 8, position: s.rfind("eight")},
SearchData{ value: 9, position: s.rfind("nine")}
];
search_results
.into_iter()
.max_by(greater_search_data)
.and_then(|x| x.position.and_then(|_| Some(x.value)))
}

16
rust/src/main.rs Normal file
View File

@ -0,0 +1,16 @@
use std::env;
mod day1;
fn main() {
let mut args = env::args();
let _prog_name = args.next();
let day = args.next();
match day {
Some(x) => match x.as_str() {
"day1" => day1::run(),
_ => todo!()
},
_ => println!("Please provide the day as day<n>"),
}
}