[rust] Solve Day 1
This commit is contained in:
parent
e7206e5aa2
commit
48bdc183ef
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
.direnv
|
||||
dist-newstyle
|
||||
target
|
||||
|
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal 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
3
Cargo.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[workspace]
|
||||
members = ["rust"]
|
||||
resolver = "2"
|
|
@ -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
|
||||
|
|
55
flake.nix
55
flake.nix
|
@ -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
8
rust/Cargo.toml
Normal 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
146
rust/src/day1.rs
Normal 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
16
rust/src/main.rs
Normal 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>"),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue