[rust] Solve Day 1
This commit is contained in:
parent
e7206e5aa2
commit
48bdc183ef
8 changed files with 216 additions and 27 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
.direnv
|
.direnv
|
||||||
dist-newstyle
|
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>
|
./bash/main.sh day<n> < ./input/day<n>
|
||||||
#+END_SRC
|
#+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:
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { inherit system; };
|
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 {
|
ghcOverrides = hself: hsuper: rec {
|
||||||
aoc2023 = pkgs.haskell.lib.overrideSrc (hsuper.callPackage ./haskell/default.nix {}) {
|
aoc2023 = hsuper.callPackage ./haskell/default.nix {};
|
||||||
src = filteredSource;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
ghc94Pkgs = pkgs.haskell.packages.ghc94.override {
|
ghc94Pkgs = pkgs.haskell.packages.ghc94.override {
|
||||||
overrides = ghcOverrides;
|
overrides = ghcOverrides;
|
||||||
|
@ -44,12 +24,19 @@
|
||||||
(pkgs.haskell-language-server.override {supportedGhcVersions = ["948"];})
|
(pkgs.haskell-language-server.override {supportedGhcVersions = ["948"];})
|
||||||
pkgs.cabal2nix
|
pkgs.cabal2nix
|
||||||
pkgs.ormolu
|
pkgs.ormolu
|
||||||
|
|
||||||
pkgs.shellcheck
|
pkgs.shellcheck
|
||||||
|
|
||||||
|
pkgs.rustc
|
||||||
|
pkgs.cargo
|
||||||
|
pkgs.rustfmt
|
||||||
|
pkgs.rust-analyzer
|
||||||
|
pkgs.clippy
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
aoc2023-haskell = ghc94Pkgs.aoc2023;
|
aoc2023-haskell = ghc94Pkgs.aoc2023;
|
||||||
aoc2023-bash =
|
aoc2023-bash =
|
||||||
let bashDir = pkgs.runCommand "aoc2023-bash" {} ''
|
let bashDir = pkgs.runCommandLocal "aoc2023-bash" {} ''
|
||||||
mkdir $out
|
mkdir $out
|
||||||
cp -r ${./bash} $out/bin
|
cp -r ${./bash} $out/bin
|
||||||
chmod -R +w $out
|
chmod -R +w $out
|
||||||
|
@ -62,24 +49,38 @@
|
||||||
${bashDir}/bin/main.sh "''${@}"
|
${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 =
|
checks =
|
||||||
let mkCheck = (prefix: exec: day: parts: {
|
let mkCheck = (lang: exec: day: parts: {
|
||||||
name = "${prefix}-${day}";
|
name = "${lang}-${day}";
|
||||||
value = pkgs.testers.testEqualContents {
|
value = pkgs.testers.testEqualContents {
|
||||||
assertion = day;
|
assertion = "${lang}: ${day}";
|
||||||
expected = pkgs.writeText "expected" ''
|
expected = pkgs.writeText "expected" ''
|
||||||
Part 1 Answer: ${parts.part1}
|
Part 1 Answer: ${parts.part1}
|
||||||
Part 2 Answer: ${parts.part2}
|
Part 2 Answer: ${parts.part2}
|
||||||
'';
|
'';
|
||||||
actual = pkgs.runCommand "actual" {} ''
|
actual = pkgs.runCommandLocal "actual" {} ''
|
||||||
"${exec}" "${day}" < "${./input}/${day}" >$out
|
"${exec}" "${day}" < "${./input}/${day}" >$out
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
in pkgs.lib.attrsets.mapAttrs' (mkCheck "haskell" "${packages.aoc2023-haskell}/bin/aoc2023") answers
|
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;
|
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…
Add table
Reference in a new issue